dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.04k stars 4.68k forks source link

Make System.Diagnostics.FileVersionInfo non-RID specific #17041

Open weshaggard opened 8 years ago

weshaggard commented 8 years ago

Today we use PInvokes on Windows to implement System.Diagnostics.FileVersionInfo and we use MetadataReader for Unix. If we can do it compatibly we should make this a pure portable library and use the MetadataReader for all platforms and eliminate the need for the RID fork.

stephentoub commented 8 years ago

@weshaggard, the MetadataReader solution we have on Unix only allows us to inspect managed assemblies. I don't see how that could be used to make a compatible implementation on Windows, which needs to be able to inspect native Windows binaries.

weshaggard commented 8 years ago

I was under the impression from @nguerrera that the MetadaReader might also enable us to read the PE headers with the version info for non-managed as well but I'm not sure of the details. This work item can still be used to investigate the feasibility.

stephentoub commented 8 years ago

If that's feasible, great :)

nguerrera commented 8 years ago

It should be feasible though the info you need is not in the PE headers but in a Win32 resource of the PE file. The same resource is used for managed and native PE files. You can get to the PE section blob from the PEReader but then you'll have to parse the version resource yourself. See Roslyn's reader/writer for this format here: https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/CvtRes.cs

wtgodbe commented 6 years ago

@weshaggard does this issue still have any traction, or should I close as abandoned?

weshaggard commented 6 years ago

I think this is still worth investigating.

nguerrera commented 6 years ago

Another benefit of doing it this way is that you aren't forced to have a file on disk, it becomes trivial to add an overload that takes a Stream. cc @jaredpar

At some point, somebody was working on this and I think there was an email thread with more details on how to proceed. I will try to dig it up.

nguerrera commented 6 years ago

Here's the old message that I sent to someone who was going to take a stab at this:

I think the following should get you the win32 resource blob:

using (FileStream stream = File.OpenRead(pathToAssembly))
using (var peReader = new PEReader(stream))
{
    DirectoryEntry resourceDirectory = peReader.PEHeaders.PEHeader.ResourceTableDirectory;
    PEMemoryBlock block = peReader.GetSectionData(resourceDirectory.RelativeVirtualAddress);

    if (block.Length == 0)
    {
         // no win32 resources
    }

    if (resourceDirectory.Size > block.Length)
    {
         // corrupt file (bad directory entry)
    }

    var blobReader = new BlobReader(block.Pointer, resourceDirectory.Size);

From there you’ll want to reference some other code that reads it. https://github.com/Microsoft/cci/blob/c9cdc0e4852ff6a38a6e5057c90c41cc138b70a8/PEReaderAndWriter/PEReader/PEFileToObjectModel.cs#L675 is enumerating all of the win32 resources and Win32ResourceMemoryReader there is the equivalent to blobReader above.

You’ll want to do the same enumeration and look for the version resource, which has TypeId == 0x10.

From the RVAToData and Size of that resource, make another BlobReader just as we did from the resourceDirectory.RelativeVirtualAddress and resourceDirectory.Size.

Use the second blob reader to read the actual version resource as written by https://github.com/dotnet/roslyn/blob/c48bbec4f6b1ff3704c341b4e1eb928c19f288c1/src/Compilers/Core/Portable/CvtRes.cs

nguerrera commented 6 years ago

Related: https://github.com/dotnet/roslyn/issues/11386

We could put reading and writing API for this resource from a higher level than just blob into System.Reflection.Metadata and then use that API from FileVersionInfo and the compiler.