Get assembly file version from an archive without unpacking - c#

I use the following code to get a C# assembly file version for files stored on a harddrive.
var vInfo = FileVersionInfo.GetVersionInfo("assemblyPath").FileVersion;
How could I get an assembly file version for a file stored in an archive without unpacking it? Imagine, you don't have a permission to write to a harddrive. You would probably use some in-memory library for opening the archive and checking what you need to know.

Sorry but you can't without having a phisical file.
The only way to read the FileVersion is to use FileVersionInfo.GetVersionInfo which accept only a path.
And if you use reflector to see how it read the fileversion then you will see some unsafe native internal methods you cannot use...
private static string GetFileVersionString(IntPtr memPtr, string name)
{
int num;
string str = "";
IntPtr zero = IntPtr.Zero;
if (UnsafeNativeMethods.VerQueryValue(new HandleRef(null, memPtr), name, ref zero, out num) && (zero != IntPtr.Zero))
{
str = Marshal.PtrToStringAuto(zero);
}
return str;
}
Maybe you could get it with some DllImport. But this is not in my knowledge.
If you settle for AssemblyVersion you can use DotNetZip library:
Assembly assembly;
using (var data = new MemoryStream())
{
using (ZipFile zip = ZipFile.Read(LocalCatalogZip))
{
zip["assembly.dll"].Extract(data);
}
data.Seek(0, SeekOrigin.Begin);
assembly = Assembly.ReflectionOnlyLoad(data.ToArray());
}
var version = assembly.GetName().Version;
----------------UPDATE-----------------
Last thought: maybe you have permission to write a file in the temp folder: Path.GetTempPath

You can use Nuget package Mono.Cecil
ModuleDefinition.ReadModule(stream)
You'll get a ModuleDefinition, containing all desired assembly info.

Related

Get image file path from resources

I have an image file day.jpg in Resources folder and I want to access it in the code as string path not as byte[] img
Here's what I have tried.
string dayWallpaper = Assembly.GetExecutingAssembly().Location + #"..\..\Resources\day.jpg";
// Didn't found it
string dayWallpaper = Resource.day;
// Outputs byte[] and gives me an error
Then I tried to convert the byte[] to String didn't work as well
static byte[] SliceMe(byte[]? source, int pos)
{
byte[]? destfoo = new byte[source.Length - pos];
Array.Copy(source, pos, destfoo, 0, destfoo.Length);
return destfoo;
}
static string ByteToPath(path)
{
String file = Encoding.Unicode.GetString(SliceMe(path, 24)).TrimEnd("\0".ToCharArray());
return file
}
Outputs black screen
Later I search for the file
if (File.Exists(dayWallpaper))
{
do stuff
}
else
{
Console.WriteLine("File does not exists");
}
And gives me the else statement.
In the answer you posted to your question, the fact that your relative path works is an "accident" that would fail on any other device deploying your app because without the existence of the source code project the path doesn't exist. One good option is to mark the day.jpg file as Copy to Output Directory at which point most installer bundlers will pick it up and deploy it in your setup.exe, msi etc. If you are specifically using the Visual Studio IDE, you would do it like this:
Now, at runtime, to acquire the path to the copied file:
var srce = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "day.jpg");
However, there is more work to be done, because you state that you "want to store the image in a folder in the executable and the user could add more images later on." The present location of the file is not suitable for that purpose, so I would recommend the additional step of creating an AppData entry for the user to store their created content.
// Obtain a folder that "the user could add to later on".
var appData =
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
typeof(Program).Assembly.GetName().Name
);
Directory.CreateDirectory(appData);
Since you mention wanting to store the day.jpg image in that folder, go ahead and copy it to the AppData location (if not already there from a previous run of your app).
var dest = Path.Combine(appData, "day.jpg");
// Copy the image (if it's not there already) into folder that the user can add to.
if (!File.Exists(dest))
{
File.Copy(
sourceFileName: srce,
destFileName: dest
);
}
Alternatively, you could set the BuildAction to EmbeddedResource and manipulate the file as a byte stream and achieve the same end result.
I managed to do it this way
string resourcePath = Path.GetFullPath(Assembly.GetExecutingAssembly().Location + #"\..\..\..\..\Resources");
string dayWallpaper = resourcePath + #"\day.jpg";

StreamReader cannot read FileStream of code from referenced assembly

I need to analyze Properites and Methods inside of classes in a custom framework that is being referenced by my active solution with a nuget package.
I can read the files on my active solution successfully because I can use the local path of the file.
I can also pull the FileStream of the file from the referenced framework assembly, but StreamReader is only reading "MZ�", and since the files are from a Nuget package, I do not have a local path.
Here are the three solutions I've tried and the problems I'm running in to in comments:
//attempt at using FileStream
PortableExecutableReference location =
MetadataReference.CreateFromFile(Assembly.Load(assemblyName).Location);
var assembly = Assembly.LoadFrom(location.FilePath);
FileStream f = assembly.GetFiles()
.Where(t => type.Name == name).FirstOrDefault();
if (f != null)
{
using (StreamReader sr = new StreamReader(f))
{
while (!sr.EndOfStream)
{
fileString = sr.ReadLine();
}
}
}
//StreamReader does not read the FileStream, fileString value = "MZ�"
//attempt at geting the path thru f.Name
PortableExecutableReference location =
MetadataReference.CreateFromFile(Assembly.Load(assemblyName).Location);
var assembly = Assembly.LoadFrom(location.FilePath);
FileStream f = assembly.GetFiles()
.Where(t => type.Name == name).FirstOrDefault();
return f.Name;
//only returns the framework path. StreamReader can't read the file with this and since its metadata I can't find the file in framework solution.
//attempt to at least get the base class information
Assembly a = Assembly.GetAssembly(typeof(type));
using (FileStream fs = a.GetFile(name))
{
using (StreamReader sr = new StreamReader(fs))
{
return fileString = sr.ReadLine();
}
}
//fs = null
fileString should contain my class file in the form of a string.
The source code isn't included when you build a .NET program; it is compiled to IL (in a dense binary form, not as text IL source), and the IL is sent. The fact that you can do this locally is purely an accident of having the source files on your machine. This will not apply in general.
Basically, you're going to need a different option. .NET has a full reflection API, allowing you to inspect an awful lot of metadata at runtime (including fields, methods, properties, attributes, etc) - but it does not include the actual source code, because: you don't (usually) ship that.
If you can be more specific about what you need to do with what is currently fileString, we can probably guide you on the best approaches, and what is/isn't possible, etc.

How to bind/merge an exe into my program?

I'm using System.Diagnostics.Process.Start("my.exe"); to call an exe
Now that I can call my .exe, I want to bind/merge it into my c# application so that when I build my application, I can get the exe built inside the projectName\Debug\builtProgram.exe or any other way to finally get a single exe file with my desired exe file inside it.
For example, consider I create a program A and I want it to encase it inside another program B which contains only one button 'Launch Program A'. And let's say program B is portable - with a single exe file.
Question is - How to create program B?
You can include the .exe as an embedded resource in your .NET assembly, and then dump it to disk to a temporary file on startup:
var thisAssembly = Assembly.GetExecutingAssembly();
var executableFileName = Path.GetTempFileName();
using(resourceStream = thisAssembly.GetManifestResourceStream("name.of.resource.exe"))
using(fileStream = File.Create(executableFileName))
{
resourceStream.CopyTo(fileStream);
}
Then you call it just like you would normally.
Process.Start(executableFileName);
Since it's hard for me to extract embedded resource.
Here's my answer:
public static void getbytez(string file, string outp)
{
byte[] buffer = File.ReadAllBytes(file);
string base64Encoded = Convert.ToBase64String(buffer);
File.WriteAllText(outp+ ".txt", base64Encoded);
//copy the base64encoded text.
//Code by CursedGmod. credit me please :D
}
public static void extract2idk(string txtfile, string outp, string exten)
{
byte[] gu = Convert.FromBase64String(txtfile);
// use it like this: byte[] gu = Convert.FromBase64String(your base64 converted text that you copied from the txt file);
// or use File.ReadAllText if you're making a stub builder.
File.WriteAllBytes(outp + exten, gu);
Process.Start(Environment.ExpandEnvironmentVariables("%TEMP%") + Path.GetFileName(txtfile));
}

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;
}

Extracting files from a Zip archive programmatically using C# and System.IO.Packaging

I have a bunch of ZIP files that are in desperate need of some hierarchical reorganization and extraction. What I can do, currently, is create the directory structure and move the zip files to the proper location. The mystic cheese that I am missing is the part that extracts the files from the ZIP archive.
I have seen the MSDN articles on the ZipArchive class and understand them reasonable well. I have also seen the VBScript ways to extract. This is not a complex class so extracting stuff should be pretty simple. In fact, it works "mostly". I have included my current code below for reference.
using (ZipPackage package = (ZipPackage)Package.Open(#"..\..\test.zip", FileMode.Open, FileAccess.Read))
{
PackagePartCollection packageParts = package.GetParts();
foreach (PackageRelationship relation in packageParts)
{
//Do Stuff but never gets here since packageParts is empty.
}
}
The problem seems to be somewhere in the GetParts (or GetAnything for that matter). It seems that the package, while open, is empty. Digging deeper the debugger shows that the private member _zipArchive shows that it actually has parts. Parts with the right names and everything. Why won't the GetParts function retrieve them? I'ver tried casting the open to a ZipArchive and that didn't help. Grrr.
If you are manipulating ZIP files, you may want to look into a 3rd-party library to help you.
For example, DotNetZip, which has been recently updated. The current version is now v1.8. Here's an example to create a zip:
using (ZipFile zip = new ZipFile())
{
zip.AddFile("c:\\photos\\personal\\7440-N49th.png");
zip.AddFile("c:\\Desktop\\2005_Annual_Report.pdf");
zip.AddFile("ReadMe.txt");
zip.Save("Archive.zip");
}
Here's an example to update an existing zip; you don't need to extract the files to do it:
using (ZipFile zip = ZipFile.Read("ExistingArchive.zip"))
{
// 1. remove an entry, given the name
zip.RemoveEntry("README.txt");
// 2. Update an existing entry, with content from the filesystem
zip.UpdateItem("Portfolio.doc");
// 3. modify the filename of an existing entry
// (rename it and move it to a sub directory)
ZipEntry e = zip["Table1.jpg"];
e.FileName = "images/Figure1.jpg";
// 4. insert or modify the comment on the zip archive
zip.Comment = "This zip archive was updated " + System.DateTime.ToString("G");
// 5. finally, save the modified archive
zip.Save();
}
here's an example that extracts entries:
using (ZipFile zip = ZipFile.Read("ExistingZipFile.zip"))
{
foreach (ZipEntry e in zip)
{
e.Extract(TargetDirectory, true); // true => overwrite existing files
}
}
DotNetZip supports multi-byte chars in filenames, Zip encryption, AES encryption, streams, Unicode, self-extracting archives.
Also does ZIP64, for file lengths greater than 0xFFFFFFFF, or for archives with more than 65535 entries.
free. open source
get it at
codeplex or direct download from windows.net - CodePlex has been discontinued and archived
From MSDN,
In this sample, the Package class is used (as opposed to the ZipPackage.) Having worked with both, I've only seen flakiness happen when there's corruption in the zip file. Not necessarily corruption that throws the Windows extractor or Winzip, but something that the Packaging components have trouble handling.
Hope this helps, maybe it can provide you an alternative to debugging the issue.
using System;
using System.IO;
using System.IO.Packaging;
using System.Text;
class ExtractPackagedImages
{
static void Main(string[] paths)
{
foreach (string path in paths)
{
using (Package package = Package.Open(
path, FileMode.Open, FileAccess.Read))
{
DirectoryInfo dir = Directory.CreateDirectory(path + " Images");
foreach (PackagePart part in package.GetParts())
{
if (part.ContentType.ToLowerInvariant().StartsWith("image/"))
{
string target = Path.Combine(
dir.FullName, CreateFilenameFromUri(part.Uri));
using (Stream source = part.GetStream(
FileMode.Open, FileAccess.Read))
using (Stream destination = File.OpenWrite(target))
{
byte[] buffer = new byte[0x1000];
int read;
while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
{
destination.Write(buffer, 0, read);
}
}
Console.WriteLine("Extracted {0}", target);
}
}
}
}
Console.WriteLine("Done");
}
private static string CreateFilenameFromUri(Uri uri)
{
char [] invalidChars = Path.GetInvalidFileNameChars();
StringBuilder sb = new StringBuilder(uri.OriginalString.Length);
foreach (char c in uri.OriginalString)
{
sb.Append(Array.IndexOf(invalidChars, c) < 0 ? c : '_');
}
return sb.ToString();
}
}
From "ZipPackage Class" (MSDN):
While Packages are stored as Zip files* through the ZipPackage class, all Zip files are not ZipPackages. A ZipPackage has special requirements such as URI-compliant file (part) names and a "[Content_Types].xml" file that defines the MIME types for all the files contained in the Package. The ZipPackage class cannot be used to open arbitary Zip files that do not conform to the Open Packaging Conventions standard.
For further details see Section 9.2 "Mapping to a ZIP Archive" of the ECMA International "Open Packaging Conventions" standard, http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(DOCX).zip (342Kb) or http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(PDF).zip (1.3Mb)
*You can simply add ".zip" to the extension of any ZipPackage-based file (.docx, .xlsx, .pptx, etc.) to open it in your favorite Zip utility.
I was having the exact same problem! To get the GetParts() method to return something, I had to add the [Content_Types].xml file to the root of the archive with a "Default" node for every file extension included. Once I added this (just using Windows Explorer), my code was able to read and extract the archived contents.
More information on the [Content_Types].xml file can be found here:
http://msdn.microsoft.com/en-us/magazine/cc163372.aspx - There is an example file below Figure 13 of the article.
var zipFilePath = "c:\\myfile.zip";
var tempFolderPath = "c:\\unzipped";
using (Package package = ZipPackage.Open(zipFilePath, FileMode.Open, FileAccess.Read))
{
foreach (PackagePart part in package.GetParts())
{
var target = Path.GetFullPath(Path.Combine(tempFolderPath, part.Uri.OriginalString.TrimStart('/')));
var targetDir = target.Remove(target.LastIndexOf('\\'));
if (!Directory.Exists(targetDir))
Directory.CreateDirectory(targetDir);
using (Stream source = part.GetStream(FileMode.Open, FileAccess.Read))
{
FileStream targetFile = File.OpenWrite(target);
source.CopyTo(targetFile);
targetFile.Close();
}
}
}
Note: this code uses the Stream.CopyTo method in .NET 4.0
I agree withe Cheeso. System.IO.Packaging is awkward when handling generic zip files, seeing as it was designed for Office Open XML documents. I'd suggest using DotNetZip or SharpZipLib
(This is basically a rephrasing of this answer)
Turns out that System.IO.Packaging.ZipPackage doesn't support PKZIP, that's why when you open a "generic" ZIP file no "parts" are returned. This class only supports some specific flavor of ZIP files (see comments at the bottom of MSDN description) used among other as Windows Azure service packages up to SDK 1.6 - that's why if you unpack a service package and then repack it using say Info-ZIP packer it will become invalid.

Categories

Resources