Come posso determinare se un assembly .NET è stato creato per x86 o x64?


327

Ho un elenco arbitrario di assembly .NET.

Devo controllare a livello di programmazione se ogni DLL è stata creata per x86 (al contrario di x64 o qualsiasi CPU). È possibile?



2
Potresti anche voler dare un'occhiata a questo: check-if-unmanaged-dll-is-32-bit-or-64-bit .
Matt,

2
Nella versione successiva di CorFlags, corrispondente a .NET 4.5, "32BIT" è stato sostituito da "32BITREQ" e "32BITPREF". .
Peter Mortensen,

Risposte:


281

Guarda a System.Reflection.AssemblyName.GetAssemblyName(string assemblyFile)

È possibile esaminare i metadati dell'assembly dall'istanza AssemblyName restituita:

Utilizzando PowerShell :

[36] C: \> [reflection.assemblyname] :: GetAssemblyName ("$ {pwd} \ Microsoft.GLEE.dll") | fl

Nome: Microsoft.GLEE
Versione: 1.0.0.0
CultureInfo:
Base di codice: file: /// C: / projects / powershell / BuildAnalyzer / ...
EscapedCodeBase: file: /// C: / projects / powershell / BuildAnalyzer / ...
ProcessorArchitecture: MSIL
Bandiere: PublicKey
Algoritmo hash: SHA1
Compatibilità versione: SameMachine
KeyPair:
Nome completo: Microsoft.GLEE, Versione = 1.0.0.0, Cultura = neut ... 

Qui, ProcessorArchitecture identifica la piattaforma di destinazione.

  • Amd64 : un processore a 64 bit basato sull'architettura x64.
  • Arm : un processore ARM.
  • IA64 : solo un processore Intel Itanium a 64 bit.
  • MSIL : neutro rispetto al processore e bit per parola.
  • X86 : un processore Intel a 32 bit, nativo o in ambiente Windows su Windows su una piattaforma a 64 bit (WOW64).
  • Nessuno : combinazione sconosciuta o non specificata di processore e bit per parola.

Sto usando PowerShell in questo esempio per chiamare il metodo.


60
Perdona la domanda stupida - ma cosa ti dice che è x86?
George Mauer,

53
Il campo ProcessorArchitecture è un elenco; nell'esempio sopra è impostato su MSIL, che significa "Neutro rispetto al processore e bit per parola". Altri valori includono X86, IA64, Amd64. Vedere msdn.microsoft.com/en-us/library/… per maggiori dettagli.
Brian Gillespie,

4
Prova con [reflection.assemblyname]::GetAssemblyName("${pwd}\name.dll")come a volte la directory corrente del processo non è la stessa del provider corrente (che è dove presumo che la DLL sia per te)
x0n

2
Un altro avvertimento a cui prestare attenzione è dimenticare di "sbloccare" la DLL se è stata scaricata da Internet. Usa file di sblocco o fai clic con il pulsante destro del mouse / proprietà / sblocca da Explorer. Dovrai riavviare la shell affinché riconosca lo stato sbloccato se hai già fallito una volta nella sessione corrente (dai la colpa a Internet Explorer per questo - sì, davvero.)
x0n

1
Nel codice ASP.NET MVC, c'è un commento // DevDiv 216459: This code originally used Assembly.GetName(), but that requires FileIOPermission, which isn't granted in medium trust. However, Assembly.FullName *is* accessible in medium trust.Purtroppo, non c'è modo di leggere ProcessorArchitecture senza usare il GetName instance method; utilizzando AssemblyName constructor, il campo è sempre impostato su None.
metadings

221

È possibile utilizzare lo strumento CLI di CorFlags (ad esempio, C: \ Programmi \ Microsoft SDKs \ Windows \ v7.0 \ Bin \ CorFlags.exe) per determinare lo stato di un assembly, in base al suo output e aprendo un assembly come risorsa binaria dovresti essere in grado di determinare dove devi cercare per determinare se il flag 32BIT è impostato su 1 ( x86 ) o 0 ( qualsiasi CPU o x64 , a seconda ):PE

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

Il post sul blog Sviluppo x64 con .NET contiene alcune informazioni su corflags.

Ancora meglio, è possibile utilizzareModule.GetPEKind per determinare se un assembly è PortableExecutableKindsvalue PE32Plus(64-bit), Required32Bit(32-bit e WOW) o ILOnly(qualsiasi CPU) insieme ad altri attributi.


1
Dopo aver visto l'aggiornamento, l'utilizzo di GetPEKind sembra essere il modo corretto per farlo. Ho segnato la tua come risposta.
Giuda Gabriel Himango,

9
GetPEKind ha esito negativo in un processo a 64 bit durante il controllo degli assembly a 32 bit
jjxtra,

2
Devi chiamare GetPEKind dal processo a 32 bit
Ludwo il

2
Installo VS 2008, VS 2010, VS 2012 e VS 2013. Ho 8 file CorFlags.exe in sottocartelle in C: \ Programmi (x86) \ Microsoft SDKs \ Windows \. Quale dovrei usare?
Kiquenet,

5
Come sottolineato in questa risposta , in .NET 4.5 sono presenti 32BITREQ e 32BITPREF anziché il flag 32BIT. PE32 / 0/0 e PE32 / 0/1 sono rispettivamente AnyCPU e AnyCPU a 32 bit.
angularsen,

141

Solo per chiarimenti, CorFlags.exe fa parte di .NET Framework SDK . Ho gli strumenti di sviluppo sulla mia macchina e il modo più semplice per me determinare se una DLL è solo a 32 bit è:

  1. Apri il prompt dei comandi di Visual Studio (in Windows: menu Start / Programmi / Microsoft Visual Studio / Visual Studio Tools / Prompt dei comandi di Visual Studio 2008)

  2. CD nella directory contenente la DLL in questione

  3. Esegui corflags in questo modo: corflags MyAssembly.dll

Otterrai un output simile a questo:

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

Come da commenti, i flag sopra devono essere letti come segue:

  • Qualsiasi CPU: PE = PE32 e 32BIT = 0
  • x86: PE = PE32 e 32BIT = 1
  • 64 bit: PE = PE32 + e 32BIT = 0

12
Questo sembra essere cambiato nel frattempo; ora vengono visualizzati corflags 32BITREQe 32BITPREFnon un singolo 32BITvalore.
OR Mapper

1
Microsoft .NET 4.5 ha introdotto una nuova opzione, Qualsiasi CPU a 32 bit preferita. Ecco i dettagli
RBT

Il "Prompt dei comandi di Visual Studio" è oggi chiamato " Prompt dei comandi per gli sviluppatori di Visual Studio 2019 ".
Uwe Keim,

22

Che ne dici di scrivere solo il tuo? Il nucleo dell'architettura PE non è stato seriamente modificato dalla sua implementazione in Windows 95. Ecco un esempio in 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;
    }
}

Ora le costanti attuali sono:

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

Ma con questo metodo consente le possibilità di nuove costanti, basta convalidare il ritorno come meglio credi.


1
Interessante, grazie per il codice con spiegazione. Module.GetPEKind è probabilmente il percorso più semplice. Ma questo è utile per il bene dell'apprendimento. Grazie.
Judah Gabriel Himango,

3
Molto interessante ma quando ho un'applicazione compilata con qualsiasi CPU, il risultato è 0x10B. Questo è sbagliato perché la mia applicazione viene eseguita in un sistema x64. C'è qualche altra bandiera da controllare?
Samuel,

GetPEArchitecture funziona per gli assembly compilati utilizzando .net 3.5, 4.0, 4.5 e 4.5.1? Ad ogni modo, penso, Module.GetPEKind fallisce in un processo a 64 bit quando si controllano gli assembly a 32 bit.
Kiquenet,

9

Prova a utilizzare CorFlagsReader da questo progetto su CodePlex . Non ha riferimenti ad altri assiemi e può essere utilizzato così com'è.


1
Questa è la risposta più accurata e utile.
Kirill Osenkov il

Il collegamento funziona ancora al momento della stesura di questo documento, ma poiché CodePlex sta per essere chiuso, sarebbe bene fare l'azione appropriata prima che sia troppo tardi.
Peter Mortensen,


6
[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);
    }
}

Grazie per questo, una delle nostre applicazioni deve essere costruita come x86, l'aggiunta di un test unit assicura che le librerie di build del server di build siano a 32 bit ed evita che si verifichino tali errori :)
Mido

5

Di seguito è riportato un file batch che verrà eseguito corflags.exesu tutti dllseexes nella directory di lavoro corrente e in tutte le sottodirectory, analizza i risultati e visualizza l'architettura di destinazione di ciascuno.

A seconda della versione di corflags.exeche viene utilizzato, le voci in uscita sarà o includere 32BIT, o 32BITREQ (e 32BITPREF). Qualunque di questi due sia incluso nell'output è l'elemento pubblicitario critico che deve essere verificato per distinguere tra Any CPUe x86. Se stai utilizzando una versione precedente di corflags.exe(pre Windows SDK v8.0A), nell'output 32BITsarà presente solo l' elemento pubblicitario, come altri hanno indicato nelle risposte precedenti. Altrimenti 32BITREQe 32BITPREFsostituirlo.

Questo presuppone corflags.exesia nel %PATH%. Il modo più semplice per assicurarsi che ciò sia utilizzare a Developer Command Prompt. In alternativa, è possibile copiarlo dalla posizione predefinita .

Se il file batch di seguito viene eseguito su un non gestito dllo exe, lo visualizzerà erroneamente come x86, poiché l'output effettivo da Corflags.exesarà un messaggio di errore simile a:

corflags: errore CF008: il file specificato non ha un'intestazione gestita valida

@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.

2

Un altro modo sarebbe utilizzare dumpbin dagli strumenti di Visual Studio su DLL e cercare l'output appropriato

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

Nota: sopra o / p è per dll a 32 bit

Un'altra opzione utile con dumpbin.exe è / EXPORTS, che mostrerà la funzione esposta dalla dll

dumpbin.exe /EXPORTS <PATH OF THE DLL>

2

Modo più generico: utilizzare la struttura dei file per determinare il tipo di immagine e testimone:

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

Enumerazione della modalità di compilazione

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

Codice sorgente con spiegazione su GitHub



1

Un altro modo per verificare la piattaforma di destinazione di un assembly .NET è l'ispezione dell'assembly con .NET Reflector ...

@ # ~ # € ~! Ho appena realizzato che la nuova versione non è gratuita! Quindi, correzione, se hai una versione gratuita di .NET reflector, puoi usarla per controllare la piattaforma di destinazione.


9
Usa ILSpy , è un'app open source di base che fa più o meno le stesse cose di Reflector
Binary Worrier


1

Un'applicazione più avanzata per questo è possibile trovare qui: CodePlex - ApiChange

Esempi:

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

0

Un'alternativa agli strumenti già menzionati è Telerik JustDecompile (strumento gratuito) che visualizzerà le informazioni accanto al nome dell'assembly:

Qualsiasi informazione o x86 o x64 in Telerik

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.