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.
Related
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.
I am creating class library file. In this I embedded stored procedure script file. so I need to take sp data as a string and I have to create in c#. so for this GetManifestResourceStream method need full-name of assemble and script file. so I did . But I did not figure out why my stream object getting null value.
string strNameSpace = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
using (Stream stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(strNameSpace + "GP_SOP_AdjustTax.sql"))
{
// Here stream is null.
using (StreamReader reader = new StreamReader(stream))
{
string result = reader.ReadToEnd();
}
}
It is sort of strange to get constant string value by getting assembly name... But you are missing "." in the way to construct the name:
Assembly.GetExecutingAssembly()
.GetManifestResourceStream(strNameSpace + ".GP_SOP_AdjustTax.sql"))
It will likely be safe and easier to simply hardcode the name:
Assembly.GetExecutingAssembly()
.GetManifestResourceStream("WhateverYoourNamespaceIs.GP_SOP_AdjustTax.sql"))
Note "How to embed and access resources" is available on Micorsoft support site and covers this topic.
Is there anyway in .Net (C#) to extract data from a zip file without decompressing the complete file?
I possibly want to extract data (file) from the start of a zip file if the compression algorithm compress the file used was in a deterministic order.
With .Net Framework 4.5 (using ZipArchive):
using (ZipArchive zip = ZipFile.Open(zipfile, ZipArchiveMode.Read))
foreach (ZipArchiveEntry entry in zip.Entries)
if(entry.Name == "myfile")
entry.ExtractToFile("myfile");
Find "myfile" in zipfile and extract it.
DotNetZip is your friend here.
As easy as:
using (ZipFile zip = ZipFile.Read(ExistingZipFile))
{
ZipEntry e = zip["MyReport.doc"];
e.Extract(OutputStream);
}
(you can also extract to a file or other destinations).
Reading the zip file's table of contents is as easy as:
using (ZipFile zip = ZipFile.Read(ExistingZipFile))
{
foreach (ZipEntry e in zip)
{
if (header)
{
System.Console.WriteLine("Zipfile: {0}", zip.Name);
if ((zip.Comment != null) && (zip.Comment != ""))
System.Console.WriteLine("Comment: {0}", zip.Comment);
System.Console.WriteLine("\n{1,-22} {2,8} {3,5} {4,8} {5,3} {0}",
"Filename", "Modified", "Size", "Ratio", "Packed", "pw?");
System.Console.WriteLine(new System.String('-', 72));
header = false;
}
System.Console.WriteLine("{1,-22} {2,8} {3,5:F0}% {4,8} {5,3} {0}",
e.FileName,
e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"),
e.UncompressedSize,
e.CompressionRatio,
e.CompressedSize,
(e.UsesEncryption) ? "Y" : "N");
}
}
Edited To Note: DotNetZip used to live at Codeplex. Codeplex has been shut down. The old archive is still available at Codeplex. It looks like the code has migrated to Github:
https://github.com/DinoChiesa/DotNetZip. Looks to be the original author's repo.
https://github.com/haf/DotNetZip.Semverd. This looks to be the currently maintained version. It's also packaged up an available via Nuget at https://www.nuget.org/packages/DotNetZip/
Something like this will list and extract the files one by one, if you want to use SharpZipLib:
var zip = new ZipInputStream(File.OpenRead(#"C:\Users\Javi\Desktop\myzip.zip"));
var filestream = new FileStream(#"C:\Users\Javi\Desktop\myzip.zip", FileMode.Open, FileAccess.Read);
ZipFile zipfile = new ZipFile(filestream);
ZipEntry item;
while ((item = zip.GetNextEntry()) != null)
{
Console.WriteLine(item.Name);
using (StreamReader s = new StreamReader(zipfile.GetInputStream(item)))
{
// stream with the file
Console.WriteLine(s.ReadToEnd());
}
}
Based on this example: content inside zip file
Here is how a UTF8 text file can be read from a zip archive into a string variable (.NET Framework 4.5 and up):
string zipFileFullPath = "{{TypeYourZipFileFullPathHere}}";
string targetFileName = "{{TypeYourTargetFileNameHere}}";
string text = new string(
(new System.IO.StreamReader(
System.IO.Compression.ZipFile.OpenRead(zipFileFullPath)
.Entries.Where(x => x.Name.Equals(targetFileName,
StringComparison.InvariantCulture))
.FirstOrDefault()
.Open(), Encoding.UTF8)
.ReadToEnd())
.ToArray());
the following code can read specific file as byte array :
using ZipArchive zipArchive = ZipFile.OpenRead(zipFilePath);
foreach(ZipArchiveEntry zipArchiveEntry in zipArchive.Entries)
{
if(zipArchiveEntry.Name.Equals(fileName,StringComparison.OrdinalIgnoreCase))
{
Stream stream = zipArchiveEntry.Open();
using MemoryStream memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
}
Zip files have a table of contents. Every zip utility should have the ability to query just the TOC. Or you can use a command line program like 7zip -t to print the table of contents and redirect it to a text file.
In such case you will need to parse zip local header entries. Each file, stored in zip file, has preceding Local File Header entry, which (normally) contains enough information for decompression, Generally, you can make simple parsing of such entries in stream, select needed file, copy header + compressed file data to other file, and call unzip on that part (if you don't want to deal with the whole Zip decompression code or library).
I am currently working with SharpZipLib under .NET 2.0 and via this I need to compress a single file to a single compressed archive. In order to do this I am currently using the following:
string tempFilePath = #"C:\Users\Username\AppData\Local\Temp\tmp9AE0.tmp.xml";
string archiveFilePath = #"C:\Archive\Archive_[UTC TIMESTAMP].zip";
FileInfo inFileInfo = new FileInfo(tempFilePath);
ICSharpCode.SharpZipLib.Zip.FastZip fZip = new ICSharpCode.SharpZipLib.Zip.FastZip();
fZip.CreateZip(archiveFilePath, inFileInfo.Directory.FullName, false, inFileInfo.Name);
This works exactly (ish) as it should, however while testing I have encountered a minor gotcha. Lets say that my temp directory (i.e. the directory that contains the uncompressed input file) contains the following files:
tmp9AE0.tmp.xml //The input file I want to compress
xxx_tmp9AE0.tmp.xml // Some other file
yyy_tmp9AE0.tmp.xml // Some other file
wibble.dat // Some other file
When I run the compression all the .xml files are included in the compressed archive. The reason for this is because of the final fileFilter parameter passed to the CreateZip method. Under the hood SharpZipLib is performing a pattern match and this also picks up the files prefixed with xxx_ and yyy_. I assume it would also pick up anything postfixed as well.
So the question is, how can I compress a single file with SharpZipLib? Then again maybe the question is how can I format that fileFilter so that the match can only ever pick up the file I want to compress and nothing else.
As an aside, is there any reason as to why System.IO.Compression not include a ZipStream class? (It only supports GZipStream)
EDIT : Solution (Derived from accepted answer from Hans Passant)
This is the compression method I implemented:
private static void CompressFile(string inputPath, string outputPath)
{
FileInfo outFileInfo = new FileInfo(outputPath);
FileInfo inFileInfo = new FileInfo(inputPath);
// Create the output directory if it does not exist
if (!Directory.Exists(outFileInfo.Directory.FullName))
{
Directory.CreateDirectory(outFileInfo.Directory.FullName);
}
// Compress
using (FileStream fsOut = File.Create(outputPath))
{
using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipStream = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(fsOut))
{
zipStream.SetLevel(3);
ICSharpCode.SharpZipLib.Zip.ZipEntry newEntry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(inFileInfo.Name);
newEntry.DateTime = DateTime.UtcNow;
zipStream.PutNextEntry(newEntry);
byte[] buffer = new byte[4096];
using (FileStream streamReader = File.OpenRead(inputPath))
{
ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(streamReader, zipStream, buffer);
}
zipStream.CloseEntry();
zipStream.IsStreamOwner = true;
zipStream.Close();
}
}
}
This is an XY problem, just don't use FastZip. Follow the first example on this web page to avoid accidents.
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.