24

Is there any way that I can access the values that were used for TargetFrameworkVersion and/or TargetFrameworkProfile when a .Net assembly was compiled?

The values I'm talking about are the ones contained the project file

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <OtherStuff />
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
    <OtherStuff />
  </PropertyGroup>
  <OtherStuff>
  </OtherStuff>
</Project>

Basically I'd like to find out what the Target Version of the Framework was when the assembly was compiled and if possible the Target Framework Profile as well.

And I'm not talking about the currently loaded version of the CLR, Environment.Version isn't what I'm after.

Ideally the solution would use System.Reflection but if I have to resort to other methods I will.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
Scott
  • 4,113
  • 6
  • 37
  • 49
  • I believe the `TargetFrameworkProfile` only affects which references Visual Studio will allow you to make from the project. I don't think anything gets compiled into the output assembly. – Damien_The_Unbeliever Jul 28 '11 at 06:21
  • Damien I think you are correct. I don't see it listed as an available [project level property](http://msdn.microsoft.com/en-us/library/bb629394.aspx) that can be passed to MSBuild. If it's not going to MSBuild then it's certainly not going to be embedded in the Assembly. I can still live in hope that TargetFrameworkVersion is embedded in there somewhere though. – Scott Jul 28 '11 at 06:58
  • `public string TargetFrameworkProfile { get; set; }` Microsoft.Build.Tasks – Mark Schultheiss May 23 '18 at 16:44

6 Answers6

15

If you would be satisfied with the version of the CLR that compiled the assembly, you can use the Assembly.ImageRuntimeVersion property. According to MSDN, that property:

representing the version of the common language runtime (CLR) saved in the file containing the manifest.

and

By default, ImageRuntimeVersion is set to the version of the CLR used to build the assembly. However, it might have been set to another value at compile time.

Of course, that doesn't give you the specific version of the .NET Framework (for example: .NET Frameworks 2, 3.0 and 3.5 are all on the 2.0 CLR).

If the CLR version isn't sufficient, you could to try to 'estimate' (guess intelligently) what version it must be based on the assemblies it references. For .NET 1 and 4, the CLR version should be enough. However, if the CLR version was 2.0, you wouldn't know if that meant 2.0, 3.0, or 3.5 so you could try some more logic. For example, if you saw that the Assembly referenced System.Core (using Assembly.GetReferencedAssemblies()) then you would know that the version is 3.5 since System.Core was new in 3.5. That's not exactly rock-solid since the assembly in question might not use any types from the Assembly so you wouldn't be able to catch that. To try to catch more cases, you could loop through all the referenced assemblies and check their version numbers - maybe filtering to just assemblies that start with System to avoid false positives with other libraries. If you see any System.* assemblies referenced that have a version of 3.5.x.x, then you can also be pretty sure it was built for 3.5.


As you've noticed, I don't believe the TargetFrameworkProfile escapes past Visual Studio. However, if there happens to be an app.config file for the application, Visual Studio may have put the target framework in there. For example, if you set project to use the 4.0 Client Profile, Visual Studio creates an app.config like this:

<?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
  </startup>
</configuration>
Stephen McDaniel
  • 2,938
  • 2
  • 24
  • 53
  • I have R1.dll with Target Framework 4.5, and R2.dll with Target Framework 4.0. CLR 4.0 for both dll. then, I have P1.dll compiled with Target Framework 4.0. Can P1.dll references R1.dll in execution time ? – Kiquenet Mar 28 '14 at 12:47
  • 1
    @Kiquenet This seems like a somewhat unrelated question. You might want to check out other questions that are more closely related to your question. For example: [Is it possible to reference an assembly that targets .net 4.5 in a project that targets 4.0?](http://stackoverflow.com/questions/18700360/is-it-possible-to-reference-an-assembly-that-targets-net-4-5-in-a-project-that) or [Upgrade to .Net 4.5 causes assembly to fail?](http://stackoverflow.com/questions/16856474/upgrade-to-net-4-5-causes-assembly-to-fail) – Stephen McDaniel Mar 28 '14 at 17:00
  • Thanks Stephen McDaniel - I was hoping someone would come along with a better answer but I don't think that's going to happen. It seems that the correct answer is "No", but I'll accept your answer as it contains some useful info anyway. – Scott Apr 22 '14 at 01:20
  • Very interesting answer. I am surprised to find it that the supported .NET runtime is so obscure to find from an assembly, still good work with the "guess intelligently" approach. Do you have any idea, or reference to point me to, on how Mono handles this? I know the version of mono is irrelevant to the supported .NET runtime a mono-compiled assembly may support. Still I wonder, would your trick work in the same way for assemblies compiled with mono? – Ivaylo Slavov Apr 28 '15 at 17:59
  • 1
    @IvayloSlavov I would expect my guessing logic to work even in the context of mono. I took a look at a random class that I know only exists in .net 3.5 ([System.Linq.Enumerable](http://docs.go-mono.com/?link=T%3aSystem.Linq.Enumerable)) and the mono version of that is also declared in the `System.Core.dll` file. I would expect most types to be that way as well. But probably the best way to know for sure would be to test it out and see. :-) – Stephen McDaniel Apr 28 '15 at 20:35
11

If the assembly was compiled with the TargetFrameworkAttribute (assembly scoped) you can easily and uniformly determine the framework profile target.

Try this example and reference your own custom assemblies with different targets.

class Program
{
    static void Main(string[] args)
    {


        // Lets examine all assemblies loaded into the current application domain.
        var assems = AppDomain.CurrentDomain.GetAssemblies();

        // The target framework attribute used when the assemby was compiled.
        var filteredType = typeof(TargetFrameworkAttribute);

        // Get all assemblies that have the TargetFrameworkAttribute applied.
        var assemblyMatches = assems.Select(x => new { Assembly = x, TargetAttribute = (TargetFrameworkAttribute)x.GetCustomAttribute(filteredType) })
                                    .Where(x => x.TargetAttribute != null);

        // Report assemblies framework target
        foreach (var assem in assemblyMatches)
        {
            var framework = new System.Runtime.Versioning.FrameworkName(assem.TargetAttribute.FrameworkName);
            Console.WriteLine("Assembly: '{0}' targets .NET version: '{1}'.",
                                assem.Assembly.FullName,
                                framework.Version);
        }

        Console.ReadLine();
    }
}
njappboy
  • 1,266
  • 13
  • 14
3

This should work!

-> Compile bin\net45\myapp.exe = ".NET Framework 4.5"

var asm = Assembly.GetExecutingAssembly();
var b = asm.CustomAttributes.FirstOrDefault(a => a.AttributeType == typeof(TargetFrameworkAttribute));
var strFramework = b.NamedArguments[0].TypedValue.Value;

Console.WriteLine(strFramework);
Latency
  • 426
  • 3
  • 12
  • 2
    For a .NET 5 assemblies I could get a meaningful target version by using `b?.ConstructorArguments[0].Value` instead of `b.NamedArguments[0].TypedValue.Value`. – Alexei - check Codidact Jul 11 '21 at 09:48
0

Try also this example to retrieve both target and current versions of .NET framework at runtime (works for .NET V4.X) :

    Dim ca As Object() = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(False)

    For Each c In ca.Where(Function(x) x.TypeId.Name = "TargetFrameworkAttribute")
        Console.WriteLine("Target .NET framework for " & Application.ProductName & " : " & c.FrameworkDisplayName)
    Next

    Console.WriteLine("Current .NET framework for " & Application.ProductName & " : " & System.Diagnostics.FileVersionInfo.GetVersionInfo(GetType(Integer).Assembly.Location).ProductVersion)
Didier L
  • 1
  • 1
0

njappboy's solution works great. I needed VB.Net version, so here's the conversion.

Dim assems = AppDomain.CurrentDomain.GetAssemblies()
Dim filteredType = GetType(Runtime.Versioning.TargetFrameworkAttribute)
Dim assemblyMatches = assems.[Select](Function(x) New With {Key .Assembly = x,
            Key .TargetAttribute = CType(x.GetCustomAttribute(filteredType),
            Runtime.Versioning.TargetFrameworkAttribute)}).Where(
            Function(x) x.TargetAttribute IsNot Nothing)
For Each assem In assemblyMatches
    Dim framework = New System.Runtime.Versioning.FrameworkName(
        assem.TargetAttribute.FrameworkName)

    Console.WriteLine("Assembly: '{0}' targets .NET version: '{1}'.",
                      assem.Assembly.FullName, framework.Version)
Next
Gregory Bologna
  • 270
  • 1
  • 6
  • 20
0
function Write-Diagnostic {
    param(
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [string]$message
    )

    Write-Host
    Write-Host $message -ForegroundColor Green
    Write-Host
}

function Get-AssemblyInfo {
  param(
        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [string] $filename
    )

    if(-not (Test-Path -Path $filename)) {
        Write-Error "Could not find file: $filename"
        exit 1
    }

    function AssemblyInfo {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [uint32] $peFormat = $null,
            [parameter(Position = 1, Mandatory = $false, ValueFromPipeline = $true)]
            [uint32] $attributes = $null,
            [parameter(Position = 2, Mandatory = $false, ValueFromPipeline = $true)]
            [uint32] $machine = $null,
            [parameter(Position = 3, Mandatory = $false, ValueFromPipeline = $true)]
            [uint16] $characteristics = $null,
            [parameter(Position = 4, Mandatory = $false, ValueFromPipeline = $true)]
            [object] $optionalHeaders = $null,
            [parameter(Position = 5, Mandatory = $false, ValueFromPipeline = $true)]
            [uint32] $majorRuntimeVersion = $null,
            [parameter(Position = 6, Mandatory = $false, ValueFromPipeline = $true)]
            [uint32] $minorRuntimeVersion = $null,
            [parameter(Position = 7, Mandatory = $false, ValueFromPipeline = $true)]
            [string] $targetFramework = $null
        )

        $assemblyInfo = @{}

        # Major/minor
        $assemblyInfo.Filename = $filename
        $assemblyInfo.MajorRuntimeVersion = $majorRuntimeVersion
        $assemblyInfo.MinorRuntimeVersion = $minorRuntimeVersion
        $assemblyInfo.TargetFramework = $targetFramework
        $assemblyInfo.ModuleKind = GetModuleKind -characteristics $characteristics -subSystem $optionalHeaders.SubSystem
        $assemblyInfo.ModuleCharacteristics = GetModuleCharacteristics -characteristics $characteristics
        $assemblyInfo.ModuleAttributes = GetModuleAttributes -attributes $attributes

        ## PeFormat
        if($peFormat -eq 0x20b) { 
            $assemblyInfo.PEFormat = "PE32Plus" 
        } elseif($peFormat -eq 0x10b) { 
            $assemblyInfo.PEFormat = "PE32" 
        }

        ## ProcessorArchitecture
        $assemblyInfo.ProcessorArchitecture = "Unknown"

        switch -Exact ($machine) {
            0x014c {
                $assemblyInfo.ProcessorArchitecture = "x86"
                if($assemblyInfo.ModuleAttributes -contains "ILOnly") {
                    $assemblyInfo.ProcessorArchitecture = "AnyCpu"
                }
            }
            0x8664 {
                $assemblyInfo.ProcessorArchitecture = "x64" 
            }
            0x0200 {
                $assemblyInfo.ProcessorArchitecture = "IA64"
            }
            0x01c4 {
                $assemblyInfo.ProcessorArchitecture = "ARMv7" 
            }
            default {
                if($assemblyInfo.PEFormat -eq "PE32PLUS") {
                    $assemblyInfo.ProcessorArchitecture = "x64"
                } elseif($assemblyInfo.PEFormat -eq "PE32") {
                    $assemblyInfo.ProcessorArchitecture = "x86"
                }
            }
        }

        return New-Object -TypeName PSObject -Property $assemblyInfo
    }

    function GetModuleKind {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [uint32] $characteristics,
            [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
            [uint32] $subSystem
        )

        # ImageCharacteristics.Dll
        if($characteristics -eq ($characteristics -bor 0x2000)) {
            return "Dll"
        }

        # SubSystem.WindowsGui || SubSystem.WindowsCeGui
        if($subSystem -eq 0x2 -or $subSystem -eq 0x9) {
            return "WinExe"
        }

        return "Console"
    }

    function GetModuleCharacteristics {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [uint16] $characteristics
        )

        $moduleCharacteristics = @()

        if($characteristics -eq ($characteristics -bor 0x0020)) {
            $moduleCharacteristics += "HighEntropyVA"
        }

        if($characteristics -eq ($characteristics -bor 0x0040)) {
           $moduleCharacteristics += "DynamicBase"
        }

        if($characteristics -eq ($characteristics -bor 0x0400)) {
            $moduleCharacteristics += "NoSEH"
        }

        if($characteristics -eq ($characteristics -bor 0x0100)) {
           $moduleCharacteristics += "NXCompat"
        }

        if($characteristics -eq ($characteristics -bor 0x1000)) {
            $moduleCharacteristics += "AppContainer"
        }

        if($characteristics -eq ($characteristics -bor 0x8000)) {
            $moduleCharacteristics += "TerminalServerAware"
        }

        return $moduleCharacteristics
    }

    function GetModuleAttributes {
        param(
            [parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)]
            [uint32] $attributes = $null
        )

        $moduleAttributes = @()

        if($attributes -eq ($attributes -bor 0x1)) {
            $moduleAttributes += "ILOnly"
        }

        if($attributes -eq ($attributes -bor 0x2)) {
            $moduleAttributes += "Required32Bit"
        }

        if($attributes -eq ($attributes -bor 0x8)) {
            $moduleAttributes += "StrongNameSigned"
        }

        if($attributes -eq ($attributes -bor 0x00020000)) {
            $moduleAttributes += "Preferred32Bit"
        }

        return $moduleAttributes

    }

    function Advance {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.Stream] $stream,
            [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
            [int] $count
        )

        $stream.Seek($count, [System.IO.SeekOrigin]::Current) | Out-Null
    }

    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/ImageReader.cs#L238
    function ReadZeroTerminatedString {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.BinaryReader] $binaryReader,
            [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
            [int] $length
        )

        $read = 0
        $buffer = New-Object char[] $length
        $bytes = $binaryReader.ReadBytes($length)
        while($read -lt $length) {
            $current = $bytes[$read]
            if($current -eq 0) {
                break
            }        

            $buffer[$read++] = $current
        }

        return New-Object string ($buffer, 0, $read)
    }

    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/Image.cs#L98
    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/Image.cs#L107
    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/Image.cs#L124
    function ResolveVirtualAddress {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [uint32] $rva,
            [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
            [object[]] $sections
        )

        $section = $null

        $sections | ForEach-Object {
            if($rva -ge $_.VirtualAddress -and $rva -lt $_.VirtualAddress + $_.SizeOfRawData) {
                $section = $_
                return
            }
        }

        if($section -eq $null) {
            Write-Error "Unable to resolve virtual address for rva address: " $rva
            exit 1
        }

        return [System.Convert]::ToUInt32($rva + $section.PointerToRawData - $section.VirtualAddress)
    }

    # # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/Image.cs#L53
    function MoveTo {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.Stream] $stream,
            [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
            [object] $dataDirectory,
            [parameter(Position = 2, Mandatory = $true, ValueFromPipeline = $true)]
            [object] $sections
        )

        $stream.Position = ResolveVirtualAddress -rva ([uint32] $dataDirectory.VirtualAddress) -sections $sections
     }

    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/BinaryStreamReader.cs#L46
    function ReadDataDirectory {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.BinaryReader] $binaryReader
        )

        $dataDirectory = @{}
        $dataDirectory.VirtualAddress = $binaryReader.ReadUInt32()
        $dataDirectory.VirtualSize = $binaryReader.ReadUInt32() 
        $dataDirectory.IsZero = $dataDirectory.VirtualAddress -eq 0 -and $dataDirectory.VirtualSize -eq 0

        return New-Object -TypeName PSObject -Property $dataDirectory
    }

    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/ImageReader.cs#L140
    function ReadOptionalHeaders {
        param(
           [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
           [System.IO.BinaryReader] $binaryReader,
           [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
           [System.IO.Stream] $stream,
           [parameter(Position = 2, Mandatory = $true, ValueFromPipeline = $true)]
           [uint16] $peFormat
        )

        $optionalHeaders = @{}
        $optionalHeaders.PEFormat = $peFormat
        $optionalHeaders.SubSystem = $null
        $optionalHeaders.SubSystemMajor = $null
        $optionalHeaders.SubSystemMinor = $null
        $optionalHeaders.Characteristics = $null
        $optionalHeaders.CLIHeader = $null
        $optionalHeaders.Debug = $null

        # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/ImageReader.cs#L136
        Advance -stream $stream -count 44

        # SubSysMajor 2
        # SubSystemMinor 2
        $optionalHeaders.SubSystemMajor = $binaryReader.ReadUInt16()
        $optionalHeaders.SubSystemMinor = $binaryReader.ReadUInt16()

        Advance -stream $stream -count 18

        # SubSystem 2
        $optionalHeaders.SubSystem = $binaryReader.ReadUInt16()

        # DLLFlags
        $optionalHeaders.Characteristics = $binaryReader.ReadUInt16()

        # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/ImageReader.cs#L197
        if($peFormat -eq 0x20b) { 
            Advance -stream $stream -count 88  
        } else { 
            Advance -stream $stream -count 72 
        }
        # Debug 8
        $optionalHeaders.Debug = ReadDataDirectory -binaryReader $binaryReader

        # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/ImageReader.cs#L209
        Advance -stream $stream -count 56 

        # CLIHeader
        $optionalHeaders.CLIHeader = ReadDataDirectory -binaryReader $binaryReader

        # Reserved 8
        Advance -stream $stream -count 8 

        return New-Object -TypeName PSObject -Property $optionalHeaders
    }

    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/BinaryStreamReader.cs#L48
    function ReadSections {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.BinaryReader] $binaryReader,
            [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.Stream] $stream,
            [parameter(Position = 2, Mandatory = $true, ValueFromPipeline = $true)]
            [uint16] $count
        )

        # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/ImageReader.cs#L289
        function ReadSection {
            param(
                [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
                [System.IO.Stream] $stream,
                [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
                [object] $section
            )

            # Save current position
            $position = $stream.Position

            # Move to pointer
            $stream.Position = $section.PointerToRawData

            # Reader pointer value
            $length = [System.Convert]::ToInt32($section.SizeOfRawData)
            $data = New-Object byte[] $length
            $offset = 0
            $read = 0

            while (($read = $stream.Read($data, $offset, $length - $offset)) -gt 0) {
                $offset += $read
            }

            $section.Data = $data

            # Restore old position
            $stream.Position = $position

            return $section

        }

        $sections = New-Object object[] $count

        for($i = 0; $i -lt $count; $i++) {

            $section = @{}

            # Name
            $section.Name = ReadZeroTerminatedString -binaryReader $reader -length 8

            # Data
            $section.Data = $null

            # VirtualSize 4
            Advance -stream $stream -count 4

            # VirtualAddress    4
            $section.VirtualAddress = $binaryReader.ReadUInt32()

            # SizeOfRawData 4
            $section.SizeOfRawData = $binaryReader.ReadUInt32()

            # PointerToRawData  4
            $section.PointerToRawData = $binaryReader.ReadUInt32()

            # PointerToRelocations      4
            # PointerToLineNumbers      4
            # NumberOfRelocations       2
            # NumberOfLineNumbers       2
            # Characteristics           4
            Advance -stream $stream -count 16 

            # Read section data
            $section = (ReadSection -stream $stream -section $section)

            # Add section
            $sections[$i] = New-Object -TypeName PSObject -Property $section

        }

        return $sections

    }

    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/ImageReader.cs#L307
    function ReadCLIHeader {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.BinaryReader] $binaryReader,
            [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.Stream] $stream,
            [parameter(Position = 2, Mandatory = $true, ValueFromPipeline = $true)]
            [object] $dataDirectory,
            [parameter(Position = 3, Mandatory = $true, ValueFromPipeline = $true)]
            [object] $sections
        )

        MoveTo -stream $stream -dataDirectory $dataDirectory -sections $sections

        # 4 because of major/minor
        Advance -stream $stream -count 4

        $cliHeader = @{}
        $cliHeader.MajorRuntimeVersion = $binaryReader.ReadUInt16()
        $cliHeader.MinorRuntimeVersion = $binaryReader.ReadUInt16()
        $cliHeader.Metadata = ReadDataDirectory -binaryReader $binaryReader 
        $cliHeader.Attributes = $binaryReader.ReadUInt32()
        $cliHeader.EntryPointToken = $binaryReader.ReadUInt32()
        $cliHeader.Resources = ReadDataDirectory -binaryReader $binaryReader 
        $cliHeader.StrongName = ReadDataDirectory -binaryReader $binaryReader 

        return New-Object -TypeName PSObject -Property $cliHeader
    }

    # https://github.com/jbevain/cecil/blob/master/Mono.Cecil.PE/ImageReader.cs#L334
    function GetTargetFrameworkVersion {
        param(
            [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.BinaryReader] $binaryReader,
            [parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true)]
            [System.IO.Stream] $stream,
            [parameter(Position = 2, Mandatory = $true, ValueFromPipeline = $true)]
            [object] $dataDirectory,
            [parameter(Position = 3, Mandatory = $true, ValueFromPipeline = $true)]
            [object] $sections,
            [parameter(Position = 4, Mandatory = $true, ValueFromPipeline = $true)]
            [object] $optionalHeaders   
       )


       $targetFramework = ""

       MoveTo -stream $stream -dataDirectory $dataDirectory -sections $sections

       if($binaryReader.ReadUInt32() -ne 0x424a5342) {
           Write-Error "BadImageFormat"
           exit 1
       }

       # 4 because of major/minor
       Advance -stream $stream -count 8

       # Read framework version
       $frameworkVersion = ReadZeroTerminatedString -binaryReader $binaryReader -length $binaryReader.ReadInt32()

       switch -Exact ($frameworkVersion[1]) {
            1 {
                if($frameworkVersion[3] -eq 0) { 
                    $targetFramework = "NET10" 
                } else { 
                    $targetFramework = "NET11"
                }
            }
            2 {
                $targetFramework = "NET20"
            }
            4 {
                # http://stackoverflow.com/questions/17499351/is-it-possible-to-run-a-net-4-5-app-on-xp
                if($optionalHeaders.SubSystemMinor -eq 0x6) {
                    $targetFramework = "NET45"
                } else {
                    $targetFramework = "NET40"
                }
            }
       }

       return $targetFramework
    }

    # Read assembly
    $stream = New-Object System.IO.FileStream($filename, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read)
    $reader = New-Object System.IO.BinaryReader($stream)
    $length = $stream.Length

    # Read PE format
    # ==============
    # The initial part here reads the PE format (not specific to .NET like cecil does)
    # because we are interested in determining generic PE metadata

    # Read pointer to PE header.
    $stream.Position = 0x3c
    $peHeaderPtr = $reader.ReadUInt32()
    if($peHeaderPtr -eq 0) {
        $peHeaderPtr = 0x80
    }

    # Ensure there is at least enough room for the following structures:
    #     24 byte PE Signature & Header
    #     28 byte Standard Fields         (24 bytes for PE32+)
    #    68 byte NT Fields               (88 bytes for PE32+)
    # >= 128 byte Data Dictionary Table
    if($peHeaderPtr > ($length - 256)) {
        Write-Error "Invalid PE header"
        exit 1
    }

    # Check the PE signature.  Should equal 'PE\0\0'.
    $stream.Position = $peHeaderPtr
    $peSignature = $reader.ReadUInt32()
    if ($peSignature -ne 0x00004550) {
        Write-Error "Invalid PE signature"
        exit 1
    }

    # Read PE header fields.
    $machine = $reader.ReadUInt16()
    $numberOfSections = $reader.ReadUInt16()

    Advance -stream $stream -count 14

    $characteristics = $reader.ReadUInt16()
    $peFormat = $reader.ReadUInt16()

    # Must be PE32 or PE32plus
    if ($peFormat -ne 0x10b -and $peFormat -ne 0x20b) {
        Write-Error "Invalid PE format. Must be either PE32 or PE32PLUS"
        exit 1
    }

    $optionalHeaders = ReadOptionalHeaders -binaryReader $reader -stream $stream -peFormat $peFormat
    if($optionalHeaders.CLIHeader.IsZero) {
        return AssemblyInfo -peFormat $peFormat -characteristics $characteristics -machine $machine
    }

    $sections = ReadSections -binaryReader $reader -stream $stream -count $numberOfSections

    $cliHeader = ReadCLIHeader -binaryReader $reader -stream $stream `
        -dataDirectory $optionalHeaders.CLIHeader -sections $sections

    $targetFramework = GetTargetFrameworkVersion -binaryReader $reader -stream $stream `
        -dataDirectory $cliHeader.Metadata -sections $sections -optionalHeaders $optionalHeaders

    $assemblyInfo = AssemblyInfo -peFormat $peFormat -attributes $cliHeader.Attributes -machine $machine -optionalHeaders $optionalHeaders `
            -characteristics $optionalHeaders.Characteristics -majorRuntimeVersion $cliHeader.MajorRuntimeVersion `
            -minorRuntimeVersion $cliHeader.MinorRuntimeVersion -targetFramework $targetFramework

    $reader.Dispose()
    $stream.Dispose()

    return $assemblyInfo

}

$rootFolder = "D:\Bruker_Data\anycpu-mixedplatforms"
$binFolder = Join-Path $rootFolder "bin"
Andre Soares
  • 1,968
  • 3
  • 21
  • 24
Mitun
  • 1