Save assembly resource data to file - c#

I have a c# program generating some WebPages. In my project I have added some JavaScript files to the project via the ResourceManager.
Now I want to get all the ResourceNames and save them to my Destination path.
I know this question have been asked a million times in here but I can not get it to work.
Here I try to list all my resources
foreach (var res in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
....
}
But I do not get the Resource names in res but
"WindowsFormsApplication3.Form1.resources" the first time in the loop
and "WindowsFormsApplication3.Properties.Resources.resources" second time
and "WindowsFormsApplication3.Properties.Resources.Designer.cs" third time
What am I doing wrong?

You are just getting the names of the manifest resources, which is not the same thing as Resource file (resx) resources.
To get the resource file resources from a manifest resource file name like "WindowsFormsApplication3.Properties.Resources.resources" you would have to do:
foreach (var manifestResourceName in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(manifestResourceName))
{
if (stream != null)
{
using (var rr = new ResourceReader(stream))
{
foreach (DictionaryEntry resource in rr)
{
var name = resource.Key.ToString();
string resourceType;
byte[] dataBytes;
rr.GetResourceData(name, out resourceType, out dataBytes);
}
}
}
}
}
And then you can save the bytes wherever you want.

Related

How to work with a ZipArchive in C#

I have a ZipArchive and am looking to access a file inside. I am not sure how I would do this, but I have a list
List<ZipContents> importList = new List<ZipContents>();
Which has two parameters:
ZipArchive which is called ZipFile
String which is called FileName
Inside the ZipArchive which is importList.ZipFile I need to find an XML file that has the same name as the Zip file name.
Currently I have this:
foreach (var import in importList)
{
var fn = import.FileName; // This is the actual file name of the zip file
// that was added into the ZipArchive.
// I will need to access the specific XML file need in the Zip by associating with
// fn
// ToDo: Extract XML file needed
// ToDo: Begin to access its contents...
}
So for example the code is looking into the ZipArchive with the name test.zip. there will be a file called test.xml that I will then need to be able to access its contents.
Like I said above I need to be able to access the contents of that file. I am sorry I have no code to support how to do this, but I have not been able to find anything else...
I have looked through a lot of the ZIpArchive documentation (including: http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive%28v=vs.110%29.aspx) and other posts on SO on how to do this, but I have come up empty. Would anyone have an idea on how to do this? Any help would be much appreciated. Thanks!
You need to extract the archive to a directory (may as well use temp since I assume you don't want to keep these):
archive.ExtractToDirectory("path string");
//Get the directory info for the directory you just extracted to
DirectoryInfo di = new DirectoryInfo("path string");
//find the xml file you want
FileInfo fi = di.GetFiles(string.Format("{0}.xml", archiveName)).FirstOrDefault();
//if the file was found, do your thing
if(fi != null)
{
//Do your file stuff here.
}
//delete the extracted directory
di.Delete();
Edit: To do the same thing just unpacking the file you care about:
//find your file
ZipArchiveEntry entry = archive
.Entries
.FirstOrDefault(e =>
e.Name == string.Format("{0}.xml", archiveName));
if(entry != null)
{
//unpack your file
entry.ExtractToFile("path to extract to");
//do your file stuff here
}
//delete file if you want
The MSDN you linked does a rather good job explaining how to access the files. Here it is applied to your example.
// iterate over the list items
foreach (var import in importList)
{
var fn = import.FileName;
// iterate over the actual archives
foreach (ZipArchiveEntry entry in import.ZipFile.Entries)
{
// only grab files that end in .xml
if (entry.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
// this extracts the file
entry.ExtractToFile(Path.Combine(#"C:\extract", entry.FullName));
// this opens the file as a stream
using(var stream = new StreamReader(entry.Open())){
// this opens file as xml stream
using(var xml = XmlReader.Create(stream){
// do some xml access on an arbitrary node
xml.MoveToContent();
xml.ReadToDescendant("my-node");
var item = xml.ReadElementContentAsString();
}
}
}
}
}
The following will extract a single xml file called file.xml and read it to an XDocument object:
var xmlEntry = importList.SelectMany(y => y.Entries)
.FirstOrDefault(entry => entry.Name.Equals("file.xml",
StringComparison.OrdinalIgnoreCase));
if (xmlEntry == null)
{
return;
}
// Open a stream to the underlying ZipArchiveEntry
using (XDocument xml = XDocument.Load(xmlEntry.Open()))
{
// Do stuff with XML
}

Reading contents from several files and writing to one file

In my application there is a situation like this.Before creating a file, my application search for files in a directory under a particular filename. If any file/files found, then it should read each files contents and write these contents(of each file) to a new file. I have googled many and tried some like this:
string temp_file_format = "ScriptLog_" + DateTime.Now.ToString("dd_MM_yyyy_HH");
string[] files = Directory.GetFiles(path,temp_file_format);
foreach (FileAccess finfo in files)
{
string text = File.ReadAllText(finfo);
}
and
System.IO.DirectoryInfo dir = new DirectoryInfo(path);
System.IO.FileInfo[] files = dir.GetFiles(temp_file_format);
foreach (FileInfo finfo in files)
{
finfo.OpenRead();
}
But all these failed..Can anyone show me an alternative for this?
Is there anything wrong in my temp_file_format string?
It will be nice if I could prepend these contents to the new file. Else also, no worries..
any help would be really appreciated..
This is a compete working implementation that does all of that
without reading everything in memory at one time (which doesn't work for large files)
without keeping any files open for more than the required time
using System.IO;
using System.Linq;
public static class Program {
public static void Main()
{
var all = Directory.GetFiles("/tmp", "*.cpp")
.SelectMany(File.ReadAllLines);
using (var w = new StreamWriter("/tmp/output.txt"))
foreach(var line in all)
w.WriteLine(line);
}
}
I tested it on mono 2.10, and it should work on any .NET 4.0+ (for File.ReadAllLines which is a lazy linewise enumerable)
Here's a short snippet that reads all the files and out puts them to the path outputPath
var lines = from file in Directory.GetFiles(path,temp_file_format)
from line in File.ReadAllLines(file)
select line;
File.WriteAllLines(outputPath, content);
The problem you are having with your code is not really related to reading files but simply trying to use an object as a type it's not. Directory.GetFiles returns an array of string and File.ReadXXX and File.OpenRead expects the path as a string. So you simply need to pass each of the strings returned as the path argument to the appropriate method. The above is one such example. Hope it helps both solve your problem and explain the actually issue with your code
try this:
foreach (FileInfo finfo in files)
{
try
{
using (StreamReader sr = new StreamReader("finfo "))
{
String line = sr.ReadToEnd();
Console.WriteLine(line);
}
}
catch (Exception e)
{
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
using (var output = File.Create(outputPath))
{
foreach (var file in Directory.GetFiles(InputPath,temp_file_format))
{
using (var input = File.OpenRead(file))
{
input.CopyTo(output);
}
}
}

Copying file from resources

Well In my C# project I add a .xml file to the resources, I want it to be extracted/copied from it to the application path, I was trying to doing this:
string appPath = Path.GetDirectoryName(Application.ExecutablePath);//Declaration of the apppath
File.Copy(appPath, Properties.Resources.config);//process for copy
But is not working :/, how can I do what I want?
Make sure the build action on your resource is set to "embed resource".
var assembly = Assembly.GetExecutingAssembly();
// See what resources are in the assembly (remove from the final code)
foreach (var name in assembly.GetManifestResourceNames()) {
Console.Writeline("'{0}'", name);
}
using (var inputStream = assembly.GetManifestResourceStream(resourcePath)) {
using( var outStream = File.OpenWrite(copyToPath)) {
input.CopyTo(outStream);
}
}

Why can't this file be deleted after using C1ZipFile?

The following code gives me a System.IO.IOException with the message 'The process cannot access the file'.
private void UnPackLegacyStats()
{
DirectoryInfo oDirectory;
XmlDocument oStatsXml;
//Get the directory
oDirectory = new DirectoryInfo(msLegacyStatZipsPath);
//Check if the directory exists
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oFile.FullName))
{
//Open the zip file
using (C1ZipFile oZipFile = new C1ZipFile(oFile.FullName, false))
{
//Check if the zip contains the stats
if (oZipFile.Entries.Contains("Stats.xml"))
{
//Get the stats as a stream
using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
{
//Load the stats as xml
oStatsXml = new XmlDocument();
oStatsXml.Load(oStatsStream);
//Close the stream
oStatsStream.Close();
}
//Loop hit elements
foreach (XmlElement oHitElement in oStatsXml.SelectNodes("/*/hits"))
{
//Do stuff
}
}
//Close the file
oZipFile.Close();
}
}
//Delete the file
oFile.Delete();
}
}
}
I am struggling to see where the file could still be locked. All objects that could be holding onto a handle to the file are in using blocks and are explicitly closed.
Is it something to do with using FileInfo objects rather than the strings returned by the static GetFiles method?
Any ideas?
I do not see problems in your code, everything look ok. To check is the problem lies in C1ZipFile I suggest you initialize zip from stream, instead of initialization from file, so you close stream explicitly:
//Open the zip file
using (Stream ZipStream = oFile.OpenRead())
using (C1ZipFile oZipFile = new C1ZipFile(ZipStream, false))
{
// ...
Several other suggestions:
You do not need to call Close() method, with using (...), remove them.
Move xml processing (Loop hit elements) outsize zip processing, i.e. after zip file closeing, so you keep file opened as least as possible.
I assume you're getting the error on the oFile.Delete call. I was able to reproduce this error. Interestingly, the error only occurs when the file is not a zip file. Is this the behavior you are seeing?
It appears that the C1ZipFile.IsZipFile call is not releasing the file when it's not a zip file. I was able to avoid this problem by using a FileStream instead of passing the file path as a string (the IsZipFile function accepts either).
So the following modification to your code seems to work:
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
using (FileStream oStream = new FileStream(oFile.FullName, FileMode.Open))
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oStream))
{
// ...
}
}
//Delete the file
oFile.Delete();
}
}
In response to the original question in the subject: I don't know if it's possible to know if a file can be deleted without attempting to delete it. You could always write a function that attempts to delete the file and catches the error if it can't and then returns a boolean indicating whether the delete was successful.
I'm just guessing: are you sure that oZipFile.Close() is enough? Perhaps you have to call oZipFile.Dispose() or oZipFile.Finalize() to be sure it has actually released the resources.
More then Likely it's not being disposed, anytime you access something outside of managed code(streams, files, etc.) you MUST dispose of them. I learned the hard way with Asp.NET and Image files, it will fill up your memory, crash your server, etc.
In the interest of completeness I am posing my working code as the changes came from more than one source.
private void UnPackLegacyStats()
{
DirectoryInfo oDirectory;
XmlDocument oStatsXml;
//Get the directory
oDirectory = new DirectoryInfo(msLegacyStatZipsPath);
//Check if the directory exists
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
//Set empty xml
oStatsXml = null;
//Load file into a stream
using (Stream oFileStream = oFile.OpenRead())
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oFileStream))
{
//Open the zip file
using (C1ZipFile oZipFile = new C1ZipFile(oFileStream, false))
{
//Check if the zip contains the stats
if (oZipFile.Entries.Contains("Stats.xml"))
{
//Get the stats as a stream
using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
{
//Load the stats as xml
oStatsXml = new XmlDocument();
oStatsXml.Load(oStatsStream);
}
}
}
}
}
//Check if we have stats
if (oStatsXml != null)
{
//Process XML here
}
//Delete the file
oFile.Delete();
}
}
}
The main lesson I learned from this is to manage file access in one place in the calling code rather than letting other components manage their own file access. This is most apropriate when you want to use the file again after the other component has finished it's task.
Although this takes a little more code you can clearly see where the stream is disposed (at the end of the using), compared to having to trust that a component has correctly disposed of the stream.

Embedding an external executable inside a C# program

How do I embed an external executable inside my C# Windows Forms application?
Edit: I need to embed it because it's an external free console application (made in C++) from which I read the output values to use in my program. It would be nice and more professional to have it embedded.
Second reason is a requirement to embed a Flash projector file inside a .NET application.
Simplest way, leading on from what Will said:
Add the .exe using Resources.resx
Code this:
string path = Path.Combine(Path.GetTempPath(), "tempfile.exe");
File.WriteAllBytes(path, MyNamespace.Properties.Resources.MyExecutable);
Process.Start(path);
Here is some sample code that would roughly accomplish this, minus error checking of any sort. Also, please make sure that the license of the program to be embedded allows this sort of use.
// extracts [resource] into the the file specified by [path]
void ExtractResource( string resource, string path )
{
Stream stream = GetType().Assembly.GetManifestResourceStream( resource );
byte[] bytes = new byte[(int)stream.Length];
stream.Read( bytes, 0, bytes.Length );
File.WriteAllBytes( path, bytes );
}
string exePath = "c:\temp\embedded.exe";
ExtractResource( "myProj.embedded.exe", exePath );
// run the exe...
File.Delete( exePath );
The only tricky part is getting the right value for the first argument to ExtractResource. It should have the form "namespace.name", where namespace is the default namespace for your project (find this under Project | Properties | Application | Default namespace). The second part is the name of the file, which you'll need to include in your project (make sure to set the build option to "Embedded Resource"). If you put the file under a directory, e.g. Resources, then that name becomes part of the resource name (e.g. "myProj.Resources.Embedded.exe"). If you're having trouble, try opening your compiled binary in Reflector and look in the Resources folder. The names listed here are the names that you would pass to GetManifestResourceStream.
Just add it to your project and set the build option to "Embedded Resource"
This is probably the simplest:
byte[] exeBytes = Properties.Resources.myApp;
string exeToRun = Path.Combine(Path.GetTempPath(), "myApp.exe");
using (FileStream exeFile = new FileStream(exeToRun, FileMode.CreateNew))
exeFile.Write(exeBytes, 0, exeBytes.Length);
Process.Start(exeToRun);
Is the executable a managed assembly? If so you can use ILMerge to merge that assembly with yours.
Here's my version:
Add the file to the project as an existing item, change the properties on the file to "Embedded resource"
To dynamically extract the file to a given location: (this example doesn't test location for write permissions etc)
/// <summary>
/// Extract Embedded resource files to a given path
/// </summary>
/// <param name="embeddedFileName">Name of the embedded resource file</param>
/// <param name="destinationPath">Path and file to export resource to</param>
public static void extractResource(String embeddedFileName, String destinationPath)
{
Assembly currentAssembly = Assembly.GetExecutingAssembly();
string[] arrResources = currentAssembly.GetManifestResourceNames();
foreach (string resourceName in arrResources)
if (resourceName.ToUpper().EndsWith(embeddedFileName.ToUpper()))
{
Stream resourceToSave = currentAssembly.GetManifestResourceStream(resourceName);
var output = File.OpenWrite(destinationPath);
resourceToSave.CopyTo(output);
resourceToSave.Close();
}
}
Add File to VS Project
Mark as "Embedded Resource" -> File properties
Use name to resolve: [Assembly Name].[Name of embedded resource] like "MyFunkyNTServcice.SelfDelete.bat"
Your code has resource bug (file handle not freed!), please correct to:
public static void extractResource(String embeddedFileName, String destinationPath)
{
var currentAssembly = Assembly.GetExecutingAssembly();
var arrResources = currentAssembly.GetManifestResourceNames();
foreach (var resourceName in arrResources)
{
if (resourceName.ToUpper().EndsWith(embeddedFileName.ToUpper()))
{
using (var resourceToSave = currentAssembly.GetManifestResourceStream(resourceName))
{
using (var output = File.OpenWrite(destinationPath))
resourceToSave.CopyTo(output);
resourceToSave.Close();
}
}
}
}
Extract something as string, if needed:
public static string ExtractResourceAsString(String embeddedFileName)
{
var currentAssembly = Assembly.GetExecutingAssembly();
var arrResources = currentAssembly.GetManifestResourceNames();
foreach (var resourceName in arrResources)
{
if (resourceName.ToUpper().EndsWith(embeddedFileName.ToUpper()))
{
using (var resourceToSave = currentAssembly.GetManifestResourceStream(resourceName))
{
using (var output = new MemoryStream())
{
resourceToSave.CopyTo(output);
return Encoding.ASCII.GetString(output.ToArray());
}
}
}
}
return string.Empty;
}

Categories

Resources