Loop through Embedded Resources and copy to local path - c#

I have a simple WinForms application, but it has some Embedded Resources (in a subfolder under "Resources") that I would like to copy out to a folder on the computer. Currently, I have the latter working (with a explicit method naming the Embedded Resource and where it should go):
string path = #"C:\Users\derek.antrican\";
using (Stream input = Assembly.GetExecutingAssembly().GetManifestResourceStream("WINFORMSAPP.Resources.SUBFOLDER.FILE.txt"))
using (Stream output = File.Create(path + "FILE.txt"))
{
input.CopyTo(output);
}
But I'm still trying to figure out how to get the former working: looping through all the resources in the "WINFORMSAPP.Resources.SUBFOLDER" folder and moving them. I've done quite a bit of Googling, but I'm still not sure how to get a list of each Embedded Resource in this subfolder.
Any help would be GREATLY appreciated!

Start by getting all resources embedded in your assembly:
Assembly.GetExecutingAssembly().GetManifestResourceNames()
You can check these names against the name of your desired subfolder to see if they are inside or outside it with a simple call to StartsWith.
Now loop through the names, and get the corresponding resource stream:
const string subfolder = "WINFORMSAPP.Resources.SUBFOLDER.";
var assembly = Assembly.GetExecutingAssembly();
foreach (var name in assembly.GetManifestResourceNames()) {
// Skip names outside of your desired subfolder
if (!name.StartsWith(subfolder)) {
continue;
}
using (Stream input = assembly.GetManifestResourceStream(name))
using (Stream output = File.Create(path + name.Substring(subfolder.Length))) {
input.CopyTo(output);
}
}

Related

Stream using behavior

I got simple code, which basically copies file from one dir to another.
string fileName = "kur.csv";
var path = #"D:\" + fileName;
Stream stream = File.OpenRead(fileName);
using Stream s = File.Create(path); // remove "using" from this line
stream.CopyTo(s);
But if I remove "using" (from 4th line start) - code doesn't work on *.csv or *.txt files. But works on *.jpg, *.docx files.
Why it works on some and why it doesn't work on others?
If I had to write a method that copies a file, I'd do something like this:
static void CopyFile(string fileName)
{
var scourceFilePath = Path.Combine(#"D:", fileName);
var destinationFilePath = Path.Combine(#"D:", "destination", fileName);
File.Copy(scourceFilePath, destinationFilePath);
}
This works for all filetypes. You could also check if the directory exists (Directory.Exists(directoryPath)) before referencing it which prevents the program from crashing if the directory does in fact not exist.
I use Path.Combine since by using it I don't have to keep track of the backslashes used.
Every method I mentioned and used is contained in the System.IO namespace.
I hope, that's what you were looking for.

Attach file to Winform and copy the file to local while running exe c#

Is it possible to attach a text file resource to my Winform exe. So when I run the "Form.exe" in another computer then it copy the text file to a specified folder. Please suggest a method to achieve the same. Thanks
If the resource name is a string:
var assembly = Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(resourceName))
using (var reader = new StreamReader(stream))
{
string text = reader.ReadToEnd();
File.WriteAllText(fileName, text);
}
else:
File.WriteAllText(fileName, Properties.Resources.TextFile1);
And also make sure that you have set the Build Action of the resource file to "Embedded Resource".
First you need to add your file as a resource in your project.
This explains what to do
Then select your file and in the properties change the "Build Action" to "Embedded Resource". This will now embed your file in your output (.exe).
To extract the file you need to do the following;
String myProject = "Name of your project";
String file = "Name of your file to extract";
String outputPath = #"c:\path\to\your\output";
using (System.IO.Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(myProject + ".Resources." + file))
{
using (System.IO.FileStream fileStream = new System.IO.FileStream(outputPath + "\\" + file, System.IO.FileMode.Create))
{
for (int i = 0; i < stream.Length; i++)
{
fileStream.WriteByte((byte)stream.ReadByte());
}
fileStream.Close();
}
}
Ideally you should check that the file does not already exist before you do this. Don't forget also to catch exceptions. Which can be very common when dealing with the file system.
Add the text file to your project resources
Properties -> Resources -> Add Resource
Read the data from the resource using
var text = Properties.Resources.textFile;
Write to the file with
File.WriteAllText(#"C:\test\testOut.txt", text);

Write from Resource to file where resource could be text or image

I'm having a problem trying to write my resource files to disk (all resource files part of the same project and assembly).
If I add
var temp = Assembly.GetExecutingAssembly().GetManifestResourceNames();
This returns a string[] in the following format
Gener.OptionsDialogForm.resources
Gener.ProgressDialog.resources
Gener.Properties.Resources.resources
Gener.g.resources
Gener.Resources.reusable.css
Gener.Resources.other.jpg
The last 2 of the array are the only 2 files I want but I assume it's not a guarantee that this will always be the case. The array could come through in another order as code is changed so I cannot explicity call the item at a given index (temp[4])
So, I could do
foreach (string item in Assembly
.GetExecutingAssembly()
.GetManifestResourceNames())
{
if (!item.Contains("Gener.Resources."))
continue;
//Do whatever I need to do
}
But this is just horrible! I face another problem with this approach; This doesn't return the file name with the extension, just the Name and as such, I have no idea what the extension is.
This is the code as it currently is
public void CopyAllFiles()
{
var files = Resources.ResourceManager.GetResourceSet(System.Globalization.CultureInfo.CurrentUICulture, true, true);
//var temp = Assembly.GetExecutingAssembly().GetManifestResourceNames();
foreach (DictionaryEntry item in files)
{
using (var resourceFileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Gener.Resources." + item.Key.ToString() + ".css")) // this won't work, I can't hard code .css as the extension could be different
{
Stream stream = new FileStream(this.DirPath, FileMode.Create, FileAccess.Write);
resourceFileStream.CopyTo(stream);
stream.Dispose();
}
}
files.Dispose();
}
But this seems... wrong... Is this how any one else would do this, I'm sure I'm missing something and such a task is common that there is a better solution?
The resource names are predictable, you could just pass the name to the Assembly.GetManifestResourceStream() method.
More productively, Visual Studio supports a designer for this so you don't have to guess at the string you need to pass. Use Project + Properties, Resources tab. Click on the dropdown arrow of the Add Resource button and select your file. You can now refer to the resource in your code with a variable name. Like:
File.WriteAllText(path, Properties.Resources.reusable);
Do consider the so-so wisdom of copying resources to files at runtime. You get the exact same outcome by just using an installer or XCopy to copy the files just once. With the significant advantage is that those resources won't eat memory address space anymore and that you won't get in trouble when you don't have write access to the directory. Which is common with UAC enabled.
This was what I used! Hopefully it will help others. It feels some what hacking, but it works!
/// <summary>
/// Copies all the files from the Resource Manifest to the relevant folders.
/// </summary>
internal void CopyAllFiles()
{
var resourceFiles = Assembly.GetExecutingAssembly().GetManifestResourceNames();
foreach (var item in resourceFiles)
{
string basePath = Resources.ResourceManager.BaseName.Replace("Properties.", "");
if (!item.Contains(basePath))
continue;
var destination = this._rootFolder + "\\" + this._javaScriptFolder + "\\" + item.Replace(basePath + ".", "");
using (Stream resouceFile = Assembly.GetExecutingAssembly().GetManifestResourceStream(item))
using (Stream output = File.Create(destination))
{
resouceFile.CopyTo(output);
}
}
}

Use Image without adding it in WPF Project/Solution Resources

I am building a Gallery for Floors in WPF application, I am reading Image Names from csv file and User will directly copy Images into Resources folder.
StreamReader streamReader = new StreamReader(
(Application.Current as PlayOnSurface.App).ProjectAmenitiesCSVPath);
// browse the csv file line by line until the end of the file
while (!streamReader.EndOfStream)
{
// for each line, split it with the split caractere (that may no be ';')
var splitLine = streamReader.ReadLine().Split('~');
if (int.Parse(splitLine[0].Trim())
== (Application.Current as PlayOnSurface.App).SelectedFloorID)
{
for (int i = 2; i < splitLine.Length; i++)
{
ImageList.Add(splitLine[i].Trim());
}
}
// map the splitted line with an entity
}
streamReader.Close();
btnPrev.IsEnabled = false;
if (ImageList.Count <= 1)
btnNext.IsEnabled = false;
imgGallery.Source = Utilities.LoadBitmapFromResource(
"Resources/Amenities/" + ImageList[0],
null);
Utilities code
public static BitmapImage LoadBitmapFromResource(string pathInApplication, Assembly assembly = null)
{
if (assembly == null)
{
assembly = Assembly.GetCallingAssembly();
}
if (pathInApplication[0] == '/')
{
pathInApplication = pathInApplication.Substring(1);
}
return new BitmapImage(new Uri(#"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute));
}
I can't add it in Resources folder in project and I can't embed resources because I want user to copy their file in Resources folder and update csv file after deployment.
My code will read it and display gallery but in that case WPF throws "Cannot locate resource" exception when loading the image on imgGallery.Source line in above code.
my csv file format:
1~FirstFloor~img1.png~img2.png~img3.png
2~SecondFloor~img1.png~img2.png~img3.png
3~ThirdFloor~img1.png~img2.png~img3.png
4~FourthFloor~img1.png~img2.png~img3.png
Maybe use the file absolute path, simply change the last line of the LoadBitmapFromResource function to:
return new BitmapImage(new Uri(assembly.GetName().CodeBase + pathInApplication, UriKind.Absolute));
this should do the fix.
Also you can try using the UriKind.Relative.
Good day
You're using an incorrect authority in your URI scheme. The application authority is to be used for data files which are known at compile time (aka static resources). You have to replace it with the siteoforigin authority.
I'd also recommend replacing your home-rolled text file format with a standard CSV file. Many programs such as Excel support generating comma-separated fields by default. While custom separators can be put, they usually require additional effort. Reading and parsing comma-separated files is also easier because many libraries are available to do these tasks.

Reading embedded XML file c#

How can I read from an embedded XML file - an XML file that is part of a c# project?
I've added a XML file to my project and I want to read from it. I want the XML file to compile with the project because I don't want that it will be a resource which the user can see.
Any idea?
Make sure the XML file is part of your .csproj project. (If you can see it in the solution explorer, you're good.)
Set the "Build Action" property for the XML file to "Embedded Resource".
Use the following code to retrieve the file contents at runtime:
public string GetResourceTextFile(string filename)
{
string result = string.Empty;
using (Stream stream = this.GetType().Assembly.
GetManifestResourceStream("assembly.folder."+filename))
{
using (StreamReader sr = new StreamReader(stream))
{
result = sr.ReadToEnd();
}
}
return result;
}
Whenever you want to read the file contents, just use
string fileContents = GetResourceTextFile("myXmlDoc.xml");
Note that "assembly.folder" should be replaced with the project name and folder containing the resource file.
Update
Actually, assembly.folder should be replaced by the namespace in which a class created in the same folder as the XML file would have by default. This is typically defaultNamespace.folder0.folder1.folder2......
You can also add the XML file as a Resource and then address its contents with Resources.YourXMLFilesResourceName (as a string, i.e. using a StringReader).
Set the Build Action to Embedded Resource, then write the following:
using (Stream stream = typeof(MyClass).Assembly.GetManifestResourceStream("MyNameSpace.Something.xml")) {
//Read the stream
}
You can use Reflector (free from http://www.red-gate.com/products/reflector/) to find the path to the embedded XML file.
Then, it's just a matter of
Assembly a = typeof(Assembly.Namespace.Class).Assembly;
Stream s = a.GetManifestResourceStream("Assembly.Namespace.Path.To.File.xml");
XmlDocument mappingFile = new XmlDocument();
mappingFile.Load(s);
s.Close();
Add the file to the project.
Set the "Build Action" property to "Embedded Resource".
Access it this way:
GetType().Module.Assembly.GetManifestResourceStream("namespace.folder.file.ext")
Notice that the resource name string is the name of the file,
including extension, preceded by the default namespace of the project.
If the resource is inside a folder, you also have to include it in the
string.
(from http://www.dotnet247.com/247reference/msgs/1/5704.aspx, but I used it pesonally)
#3Dave really helped (up vote given), however my resource helper was in a different assembly so I did the below
public string GetResourceFileText(string filename, string assemblyName)
{
string result = string.Empty;
using (Stream stream =
System.Reflection.Assembly.Load(assemblyName).GetManifestResourceStream($"{assemblyName}.{filename}"))
{
using (StreamReader sr = new StreamReader(stream))
{
result = sr.ReadToEnd();
}
}
return result;
}
Called by
GetResourceFileText("YourFileNameHere.ext", Assembly.GetExecutingAssembly().GetName().Name);

Categories