How to check if a WPF resource exists? - c#

I have an application, that reads a specific type of XML file. Those XML files can reference each other, e.g.:
<MyXml>
<Reference Path="pack://application:,,,/MyOtherXML.xml"/>
<!--More data-->
</MyXml>
This is mainly because they are quite long, and you don't want to repeat yourself with 180+ lines of XML.
However, I'm not sure how to check if the files exist if they are resources. I know that if they are normal files I can just use File.Exists, but I don't think you can do that for resources. I also found this, but the answer seems to be wrong. So how do you check if a resource exists on WPF?

You need to use GetManifestResourceStream to get resources and read collection of keys from the dictionary something like this -
public static string[] GetResourceNames()
{
var assembly = Assembly.GetExecutingAssembly();
string resName = assembly.GetName().Name + ".g.resources";
using (var stream = assembly.GetManifestResourceStream(resName))
{
using (var reader = new System.Resources.ResourceReader(stream))
{
return reader.Cast<DictionaryEntry>().Select(entry =>
(string)entry.Key).ToArray();
}
}
}

you can call Assembly.GetExecutingAssembly().GetManifestResourceNames() get all the resource names and check on the results for the resource you want
var names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
if(names.Contains(resourceNameTosearch))
{
// exist
}

Related

C# - How can I have a dynamic Image / BitmapSource accessible from XAML?

In my C# project I have a list of images which are resources compiled in the exe:
/Pics/Image1.png
/Pics/Image2.png
/Pics/Image3.png
...
In my code I process the images to match them to the theme of the application. The issue I am having is that I am trying to figure out an easy way to access these processed images in the XAML syntax.
This is how I typically access a resource image (pre-processed):
<Image Source="/Pics/Image1.png" />
So I would really like to access these processed images a similar way.
I tried a static dictionary like this:
<Image Source="{x:Static local:Theme.Images.ImageDictionary[/Pics/Image1.png]}" />
But this threw an error because it doesn't like the ".png", I haven't been able to get this working with dictionary keys. Not to mention this looks really ugly.
Ideally I would love to be able to "replace" the resource references, or create a resource at runtime (e.g. PicsProcessed/Image1.png) but haven't been able to figure a way to add or modify resources in a running C# application programmatically.
Any suggestions are really appreciated - thank you!
Have you tried creating a Bitmap then setting it as the image source? I think this should be easy. Give your image a name, say theImage. You can't possibly reference the image without giving it a name.
Try the following:
string path="/Pics/Image3.png";//path to the image
var bitmapImage=new Bitmap(new Uri(path));
theImage.source=bitmapImage;//set the bitmap as the source.
There are other ways you can achieve this though. Hope this helps?
It took a couple of days but I figured out a solution!
I moved all my images into another C# Project in the same solution, set to compile as a Class Libary DLL file called DynamicResources.dll (assembly name in the Project settings is "DynamicResources"). This project is added as a reference to the main project. As such I can access images in the XAML - clean and tidy:
<Image Source="/DynamicResources;component/pics/image1.png" />
Then in the post-build event for the main project, I rename the compiled .dll so it doesn't get loaded by the main .exe binary at launch:
copy "$(TargetDir)DynamicResources.dll" "$(TargetDir)DynamicResources.temp"
del "$(TargetDir)DynamicResources.dll"
Then I used a third-party library called Mono.Cecil to load the DynamicResources.temp file (DLL format), replace the resources, write it back to memory, then tell the application to load it:
public static void UpdateAssembly()
{
string dllFile = "DynamicResources.temp";
string dllNamespace = "DynamicResources";
var asm = AssemblyDefinition.ReadAssembly(dllFile);
var module = asm.Modules.FirstOrDefault();
var resources = module.Resources;
var dllResource = (EmbeddedResource)(resources.FirstOrDefault());
var dllResourceReader = new ResourceReader(dllResource.GetResourceStream());
var newResourceOutputStream = new MemoryStream();
var newResourceWriter = new ResourceWriter(newResourceOutputStream);
foreach (DictionaryEntry dllResourceEntry in dllResourceReader)
{
var image = (BitmapSource)new ImageSourceConverter().ConvertFrom(dllResourceEntry.Value);
Color foreground = (Color)ColorConverter.ConvertFromString("#FFFFFF");
var processed = (WriteableBitmap)ColorizeImage(image, foreground); // Your image processing function ?
newResourceWriter.AddResource(dllResourceEntry.Key.ToString(), BitmapToByte(processed));
}
newResourceWriter.Close();
var newResource = new EmbeddedResource(dllResource.Name, dllResource.Attributes, newResourceOutputStream.ToArray());
module.Resources.Remove(dllResource);
module.Resources.Add(newResource);
var woutput = new MemoryStream();
asm.Write(woutput);
var doutput = woutput.ToArray();
Assembly assembly = Assembly.Load(doutput);
}
public static MemoryStream BitmapToByte(BitmapSource bitmapSource)
{
var encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
var frame = System.Windows.Media.Imaging.BitmapFrame.Create(bitmapSource);
encoder.Frames.Add(frame);
var stream = new MemoryStream();
encoder.Save(stream);
return stream;
}
public static void AttachAssembly(string myAsmFileName)
{
Assembly assembly = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + myAsmFileName); // LoadFrom
AppDomain.CurrentDomain.Load(assembly.GetName());
}
Important note: When iterating through resources, they become lowercase, so you must use lowercase file and folder names.

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
}

Save assembly resource data to file

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.

Get file name of file from full path?

I have DragDrop enabled in my WinForms application, I'm getting the list of items dropped and storing them in a string array called files, then in the DragDrop event I can do something like:
foreach (string file in files)
{
MessageBox.Show(file);
}
Which would return something like:
C:\Users\MyName\document.txt
Is it possible to get just the file name + extension (e.g. document.txt)? I'm not asking for a complete solution, but could you hint me in that direction?
Use Path static function calls such as:
Path.GetFileName(someFullPath);
See msdn here.
You could also use FileInfo class ..
FileInfo fileInfo = new FileInfo(fileFullPath);
var name = fileInfo.Name;
var extension = fileInfo.Extension;

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