【问题标题】:How can I determine if a .NET assembly was built for x86 or x64?如何确定 .NET 程序集是为 x86 还是 x64 构建的?
【发布时间】:2013-10-06 17:36:17
【问题描述】:

我有一个任意的 .NET 程序集列表。

我需要以编程方式检查每个 DLL 是否是为 x86(而不是 x64 或任何 CPU)构建的。这可能吗?

【问题讨论】:

标签: .net assemblies x86 64-bit x86-64


【解决方案1】:

您可以使用 CorFlags CLI 工具(例如,C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\CorFlags.exe)来确定程序集的状态,基于其输出并将程序集作为二进制资产打开,您应该能够确定需要寻找的位置以确定 32BIT 标志是设置为 1 (x86) 还是 0 (任何 CPU em> 或 x64,取决于PE):

Option    | PE    | 32BIT
----------|-------|---------
x86       | PE32  | 1
Any CPU   | PE32  | 0
x64       | PE32+ | 0

博文x64 Development with .NET有一些关于corflags的信息。

更好的是,您可以use Module.GetPEKind 来确定程序集是PortableExecutableKindsPE32Plus(64 位)、Required32Bit(32 位和 WOW)还是ILOnly(任何 CPU)与其他属性。

【讨论】:

  • 看到您的更新后,使用 GetPEKind 似乎是执行此操作的正确方法。我已将您的答案标记为答案。
  • GetPEKind 在检查 32 位程序集时在 64 位进程中失败
  • 你必须从 32 位进程调用 GetPEKind
  • 我安装了 VS 2008、VS 2010、VS 2012 和 VS 2013。我在 C:\Program Files (x86)\Microsoft SDKs\Windows\ 的子文件夹中有 8 个文件 CorFlags.exe。我应该使用哪个?
  • 正如this answer 中所指出的,在 .NET 4.5 中有 32BITREQ 和 32BITPREF 而不是 32BIT 标志。 PE32/0/0 和 PE32/0/1 分别是 AnyCPU 和 AnyCPU 32 位首选。
【解决方案2】:

System.Reflection.AssemblyName.GetAssemblyName(string assemblyFile)

您可以从返回的 AssemblyName 实例中检查程序集元数据:

使用 PowerShell

[36] C:\> [reflection.assemblyname]::GetAssemblyName("${pwd}\Microsoft.GLEE.dll") |佛罗里达州 名称:Microsoft.GLEE 版本:1.0.0.0 文化信息: 代码库:file:///C:/projects/powershell/BuildAnalyzer/... EscapedCodeBase : file:///C:/projects/powershell/BuildAnalyzer/... 处理器架构:MSIL 标志:公钥 哈希算法:SHA1 版本兼容性:SameMachine 密钥对: 全名:Microsoft.GLEE,版本=1.0.0.0,文化=中性...

这里,ProcessorArchitecture 标识目标平台。

  • Amd64:基于 x64 架构的 64 位处理器。
  • Arm:ARM 处理器。
  • IA64:仅限 64 位 Intel Itanium 处理器。
  • MSIL:与处理器和每字位数无关。
  • X86:32 位 Intel 处理器,本机或在 64 位平台 (WOW64) 上的 Windows on Windows 环境中。
  • :处理器和每字位数的未知或未指定组合。

我在本例中使用 PowerShell 来调用该方法。

【讨论】:

  • 请原谅这个愚蠢的问题——但这告诉你它是 x86 的?
  • ProcessorArchitecture 字段是一个枚举;在上面的示例中,它被设置为 MSIL,这意味着“相对于处理器和每字位数而言是中性的”。其他值包括 X86、IA64、Amd64。有关详细信息,请参阅msdn.microsoft.com/en-us/library/…
  • 尝试使用 PowerShell 时出现以下错误:Exception calling "GetAssemblyName" with "1" argument(s): "Could not load file or assembly '[DLLName].dll' or one of its dependencies. The system cannot find the file specified."(是的,我拼写正确)。
  • 尝试使用[reflection.assemblyname]::GetAssemblyName("${pwd}\name.dll"),因为有时进程的当前目录与当前提供程序的目录不同(我假设 DLL 是为你准备的)
  • 另一个需要注意的警告是,如果您从 Internet 下载 DLL,则忘记“解除阻止”该 DLL。使用取消阻止文件,或右键单击/属性/从资源管理器中取消阻止。如果您在当前会话中已经失败了一次,您将需要重新启动 shell 以识别未阻止状态(这要归咎于 Internet Explorer - 是的,真的。)
【解决方案3】:

为了澄清,CorFlags.exe 是.NET Framework SDK 的一部分。我的机器上有开发工具,确定 DLL 是否仅为 32 位的最简单方法是:

  1. 打开 Visual Studio 命令提示符(在 Windows 中:菜单开始/程序/Microsoft Visual Studio/Visual Studio 工具/Visual Studio 2008 命令提示符)

  2. CD 到包含相关 DLL 的目录

  3. 像这样运行标志: corflags MyAssembly.dll

你会得到类似这样的输出:

Microsoft (R) .NET Framework CorFlags Conversion Tool.  Version  3.5.21022.8
Copyright (c) Microsoft Corporation.  All rights reserved.

Version   : v2.0.50727
CLR Header: 2.5
PE        : PE32
CorFlags  : 3
ILONLY    : 1
32BIT     : 1
Signed    : 0

根据 cmets,上面的标志将被解读为:

  • 任何 CPU:PE = PE32 和 32BIT = 0
  • x86:PE = PE32 和 32BIT = 1
  • 64 位:PE = PE32+ 和 32BIT = 0

【讨论】:

  • 这似乎同时发生了变化; corflags 现在显示 32BITREQ32BITPREF 而不是单个 32BIT 值。
  • Microsoft .NET 4.5 引入了一个新选项,任何 CPU 32 位首选。 Here 是详细信息。
  • “Visual Studio 命令提示符”现在称为“Visual Studio 2019 Developer Command Prompt”。
【解决方案4】:

检查 .NET 程序集的目标平台的另一种方法是使用 .NET Reflector...检查程序集。

@#~#€~!我刚刚意识到新版本不是免费的!所以,更正一下,如果你有一个免费版本的 .NET 反射器,你可以用它来检查目标平台。

【讨论】:

  • 使用ILSpy,它是一个基本的开源应用程序,与Reflector 做很多相同的事情
【解决方案5】:

你自己写怎么样? PE 体系结构的核心自从在 Windows 95 中实现以来没有发生重大变化。下面是一个 C# 示例:

    public static ushort GetPEArchitecture(string pFilePath)
    {
        ushort architecture = 0;
        try
        {
            using (System.IO.FileStream fStream = new System.IO.FileStream(pFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
            {
                using (System.IO.BinaryReader bReader = new System.IO.BinaryReader(fStream))
                {
                    if (bReader.ReadUInt16() == 23117) //check the MZ signature
                    {
                        fStream.Seek(0x3A, System.IO.SeekOrigin.Current); //seek to e_lfanew.
                        fStream.Seek(bReader.ReadUInt32(), System.IO.SeekOrigin.Begin); //seek to the start of the NT header.
                        if (bReader.ReadUInt32() == 17744) //check the PE\0\0 signature.
                        {
                            fStream.Seek(20, System.IO.SeekOrigin.Current); //seek past the file header,
                            architecture = bReader.ReadUInt16(); //read the magic number of the optional header.
                        }
                    }
                }
            }
        }
        catch (Exception) { /* TODO: Any exception handling you want to do, personally I just take 0 as a sign of failure */}
        //if architecture returns 0, there has been an error.
        return architecture;
    }
}

现在的常量是:

0x10B - PE32  format.
0x20B - PE32+ format.

但是使用这种方法,它允许新常量的可能性,只需验证您认为合适的返回即可。

【讨论】:

  • 很有趣,感谢您提供带有解释的代码。 Module.GetPEKind 可能是最简单的路径。但这有助于学习。谢谢。
  • 非常有趣,但是当我使用 Any CPU 编译应用程序时,结果是 0x10B。这是错误的,因为我的应用程序在 x64 系统中运行。还有其他标志要检查吗?
  • GetPEArchitecture 适用于使用 .net 3.5、4.0、4.5 和 4.5.1 编译的程序集?无论如何,我认为,Module.GetPEKind 在检查 32 位程序集时在 64 位进程中失败。
【解决方案6】:

尝试使用 CorFlagsReader from this project at CodePlex。它没有对其他程序集的引用,可以按原样使用。

【讨论】:

  • 这是最准确、最有用的答案。
  • 该链接在撰写本文时仍然有效,但由于 CodePlex 即将关闭,最好在为时已晚之前采取适当的措施。
【解决方案7】:

cfeduke 注意到调用 GetPEKind 的可能性。从 PowerShell 执行此操作可能很有趣。

例如,这里是可以使用的 cmdlet 的代码:https://stackoverflow.com/a/16181743/64257

另外,https://stackoverflow.com/a/4719567/64257 指出“PowerShell Community Extensions 中还有 Get-PEHeader cmdlet,可用于测试可执行映像。”

【讨论】:

    【解决方案8】:
    [TestMethod]
    public void EnsureKWLLibrariesAreAll64Bit()
    {
        var assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies().Where(x => x.FullName.StartsWith("YourCommonProjectName")).ToArray();
        foreach (var assembly in assemblies)
        {
            var myAssemblyName = AssemblyName.GetAssemblyName(assembly.FullName.Split(',')[0] + ".dll");
            Assert.AreEqual(ProcessorArchitecture.MSIL, myAssemblyName.ProcessorArchitecture);
        }
    }
    

    【讨论】:

    • 感谢您,我们的其中一个应用程序必须构建为 x86,添加单元测试可确保构建服务器的构建库为 32 位并避免发生这些错误 :)
    【解决方案9】:

    下面是一个批处理文件,它将针对当前工作目录和所有子目录中的所有dllsexes 运行corflags.exe,解析结果并显示每个目标架构。

    根据使用的corflags.exe 版本,输出中的行项目将包括32BIT 32BITREQ(和32BITPREF)。无论这两者中的哪一个包含在输出中,都是必须检查以区分Any CPUx86 的关键行项目。如果您使用的是旧版本的corflags.exe(Windows SDK v8.0A 之前的版本),那么输出中只会出现32BIT 行项目,正如其他人在过去的答案中所指出的那样。否则 32BITREQ32BITPREF 替换它。

    这假设corflags.exe%PATH% 中。确保这一点的最简单方法是使用Developer Command Prompt。或者,您可以从 default location 复制它。

    如果下面的批处理文件针对非托管dllexe 运行,它将错误地显示为x86,因为Corflags.exe 的实际输出将是类似于以下内容的错误消息:

    corflags : 错误 CF008 : 指定的文件没有有效的托管标头

    @echo off
    
    echo.
    echo Target architecture for all exes and dlls:
    echo.
    
    REM For each exe and dll in this directory and all subdirectories...
    for %%a in (.exe, .dll) do forfiles /s /m *%%a /c "cmd /c echo @relpath" > testfiles.txt
    
    for /f %%b in (testfiles.txt) do (
        REM Dump corflags results to a text file
        corflags /nologo %%b > corflagsdeets.txt
    
       REM Parse the corflags results to look for key markers   
       findstr /C:"PE32+">nul .\corflagsdeets.txt && (      
          REM `PE32+` indicates x64
            echo %%~b = x64
        ) || (
          REM pre-v8 Windows SDK listed only "32BIT" line item, 
          REM newer versions list "32BITREQ" and "32BITPREF" line items
            findstr /C:"32BITREQ  : 0">nul /C:"32BIT     : 0" .\corflagsdeets.txt && (
                REM `PE32` and NOT 32bit required indicates Any CPU
                echo %%~b = Any CPU
            ) || (
                REM `PE32` and 32bit required indicates x86
                echo %%~b = x86
            )
        )
    
        del corflagsdeets.txt
    )
    
    del testfiles.txt
    echo.
    

    【讨论】:

      【解决方案10】:

      您可以在这里找到更高级的应用程序:CodePlex - ApiChange

      例子:

      C:\Downloads\ApiChange>ApiChange.exe -CorFlags c:\Windows\winhlp32.exe
      File Name; Type; Size; Processor; IL Only; Signed
      winhlp32.exe; Unmanaged; 296960; X86
      
      C:\Downloads\ApiChange>ApiChange.exe -CorFlags c:\Windows\HelpPane.exe
      File Name; Type; Size; Processor; IL Only; Signed
      HelpPane.exe; Unmanaged; 733696; Amd64
      

      【讨论】:

        【解决方案11】:

        另一种方法是在 DLL 上使用 Visual Studio 工具中的 dumpbin 并查找适当的输出

        dumpbin.exe /HEADERS <your dll path>
            FILE HEADER VALUE
                         14C machine (x86)
                           4 number of sections
                    5885AC36 time date stamp Mon Jan 23 12:39:42 2017
                           0 file pointer to symbol table
                           0 number of symbols
                          E0 size of optional header
                        2102 characteristics
                               Executable
                               32 bit word machine
                               DLL
        

        注意:以上 o/p 是针对 32bit dll 的

        dumpbin.exe 另一个有用的选项是 /EXPORTS,它将显示 dll 公开的功能

        dumpbin.exe /EXPORTS <PATH OF THE DLL>
        

        【讨论】:

          【解决方案12】:

          JetBrains 的 DotPeek 提供了查看 msil(anycpu)、x86、x64 的快捷方式

          【讨论】:

            【解决方案13】:

            更通用的方式 - 使用文件结构来确定位数和图像类型:

            public static CompilationMode GetCompilationMode(this FileInfo info)
            {
                if (!info.Exists) throw new ArgumentException($"{info.FullName} does not exist");
            
                var intPtr = IntPtr.Zero;
                try
                {
                    uint unmanagedBufferSize = 4096;
                    intPtr = Marshal.AllocHGlobal((int)unmanagedBufferSize);
            
                    using (var stream = File.Open(info.FullName, FileMode.Open, FileAccess.Read))
                    {
                        var bytes = new byte[unmanagedBufferSize];
                        stream.Read(bytes, 0, bytes.Length);
                        Marshal.Copy(bytes, 0, intPtr, bytes.Length);
                    }
            
                    //Check DOS header magic number
                    if (Marshal.ReadInt16(intPtr) != 0x5a4d) return CompilationMode.Invalid;
            
                    // This will get the address for the WinNT header  
                    var ntHeaderAddressOffset = Marshal.ReadInt32(intPtr + 60);
            
                    // Check WinNT header signature
                    var signature = Marshal.ReadInt32(intPtr + ntHeaderAddressOffset);
                    if (signature != 0x4550) return CompilationMode.Invalid;
            
                    //Determine file bitness by reading magic from IMAGE_OPTIONAL_HEADER
                    var magic = Marshal.ReadInt16(intPtr + ntHeaderAddressOffset + 24);
            
                    var result = CompilationMode.Invalid;
                    uint clrHeaderSize;
                    if (magic == 0x10b)
                    {
                        clrHeaderSize = (uint)Marshal.ReadInt32(intPtr + ntHeaderAddressOffset + 24 + 208 + 4);
                        result |= CompilationMode.Bit32;
                    }
                    else if (magic == 0x20b)
                    {
                        clrHeaderSize = (uint)Marshal.ReadInt32(intPtr + ntHeaderAddressOffset + 24 + 224 + 4);
                        result |= CompilationMode.Bit64;
                    }
                    else return CompilationMode.Invalid;
            
                    result |= clrHeaderSize != 0
                        ? CompilationMode.CLR
                        : CompilationMode.Native;
            
                    return result;
                }
                finally
                {
                    if (intPtr != IntPtr.Zero) Marshal.FreeHGlobal(intPtr);
                }
            }
            

            编译模式枚举

            [Flags]
            public enum CompilationMode
            {
                Invalid = 0,
                Native = 0x1,
                CLR = Native << 1,
                Bit32 = CLR << 1,
                Bit64 = Bit32 << 1
            }
            

            GitHub处有解释的源代码

            【讨论】:

              【解决方案14】:

              我克隆了一个超级方便的工具,它为 Windows 资源管理器中的程序集添加了一个上下文菜单条目,以显示所有可用信息:

              在此处下载: https://github.com/tebjan/AssemblyInformation/releases

              【讨论】:

              • “源代码 (zip)”下载有一个项目文件,其中包含您计算机上文件的硬编码路径,但不包含在下载中:(
              【解决方案15】:

              已经提到的工具的替代品是Telerik JustDecompile(免费工具),它将在程序集名称旁边显示信息:

              【讨论】:

                【解决方案16】:

                我喜欢 ILSpy 工具 家伙。它不仅展示了架构,还展示了目标框架:

                // linq2db, Version=3.0.0.0, Culture=neutral, PublicKeyToken=e41013125f9e410a
                // Global type: <Module>
                // Architecture: AnyCPU (64-bit preferred)
                // Runtime: v4.0.30319
                // This assembly is signed with a strong name key.
                // This assembly was compiled using the /deterministic option.
                // Hash algorithm: SHA1
                

                因此可以确定是.Net Core 2.1、.Net Framework 4.6还是其他任何一个:

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-03-17
                  • 2010-12-08
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-03
                  • 2017-05-10
                  • 2016-07-01
                  相关资源
                  最近更新 更多