Eu tenho uma lista arbitrária de assemblies .NET.
Preciso verificar programaticamente se cada DLL foi criada para x86 (em oposição a x64 ou qualquer CPU). Isso é possível?
Eu tenho uma lista arbitrária de assemblies .NET.
Preciso verificar programaticamente se cada DLL foi criada para x86 (em oposição a x64 ou qualquer CPU). Isso é possível?
Respostas:
Olhe para System.Reflection.AssemblyName.GetAssemblyName(string assemblyFile)
Você pode examinar os metadados do assembly a partir da instância AssemblyName retornada:
Usando o PowerShell :
[36] C: \> [reflection.assemblyname] :: GetAssemblyName ("$ {pwd} \ Microsoft.GLEE.dll") | fl Nome: Microsoft.GLEE Versão: 1.0.0.0 CultureInfo: CodeBase: arquivo: /// C: / projects / powershell / BuildAnalyzer / ... EscapedCodeBase: file: /// C: / projects / powershell / BuildAnalyzer / ... Arquitetura do Processador: MSIL Sinalizadores: PublicKey HashAlgorithm: SHA1 VersãoCompatibilidade: SameMachine KeyPair: Nome completo: Microsoft.GLEE, versão = 1.0.0.0, Culture = neut ...
Aqui, ProcessorArchitecture identifica a plataforma de destino.
Estou usando o PowerShell neste exemplo para chamar o método
[reflection.assemblyname]::GetAssemblyName("${pwd}\name.dll")
como às vezes diretório atual do processo não é o mesmo que o provedor atual de (que é onde eu assumir a DLL é para você)
// 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.
Infelizmente, não há como ler ProcessorArchitecture sem usar o GetName instance method
; usando AssemblyName constructor
, o campo é sempre definido como None
.
Você pode usar a ferramenta CLI do CorFlags (por exemplo, C: \ Arquivos de programas \ Microsoft SDKs \ Windows \ v7.0 \ Bin \ CorFlags.exe) para determinar o status de um assembly, com base em sua saída e abrir um assembly como um ativo binário, você deve poder determinar onde precisa determinar se o sinalizador 32BIT está definido como 1 ( x86 ) ou 0 ( qualquer CPU ou x64 , dependendo ):PE
Option | PE | 32BIT
----------|-------|---------
x86 | PE32 | 1
Any CPU | PE32 | 0
x64 | PE32+ | 0
A postagem do blog x64 Development with .NET possui algumas informações sobre corflags
.
Ainda melhor, você pode usarModule.GetPEKind
para determinar se um assembly é PortableExecutableKinds
valor PE32Plus
(64 bits), Required32Bit
(32 bits e WOW) ou ILOnly
(qualquer CPU) junto com outros atributos.
Apenas para esclarecimento, CorFlags.exe faz parte do .NET Framework SDK . Eu tenho as ferramentas de desenvolvimento em minha máquina e a maneira mais simples para eu determinar se uma DLL é apenas de 32 bits é:
Abra o prompt de comando do Visual Studio (no Windows: menu Iniciar / Programas / Microsoft Visual Studio / Ferramentas do Visual Studio / prompt de comando do Visual Studio 2008)
CD para o diretório que contém a DLL em questão
Execute corflags assim:
corflags MyAssembly.dll
Você obterá algo como isto:
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
Conforme comentários, os sinalizadores acima devem ser lidos da seguinte forma:
32BITREQ
e 32BITPREF
não um único 32BIT
valor.
Que tal você escrever sua própria? O núcleo da arquitetura do PE não foi seriamente alterado desde sua implementação no Windows 95. Aqui está um exemplo de 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;
}
}
Agora, as constantes atuais são:
0x10B - PE32 format.
0x20B - PE32+ format.
Mas com este método, ele permite as possibilidades de novas constantes, apenas valida o retorno como achar melhor.
Tente usar CorFlagsReader deste projeto no CodePlex . Não possui referências a outros conjuntos e pode ser usado como está.
[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);
}
}
Abaixo está um arquivo em lotes que será executado corflags.exe
em todos dlls
e exes
no diretório de trabalho atual e em todos os subdiretórios, analisará os resultados e exibirá a arquitetura de destino de cada um.
Dependendo da versão do corflags.exe
que é usado, os itens de linha na saída ou incluir 32BIT
, ou 32BITREQ
(e 32BITPREF
). Qualquer um desses dois está incluído na saída é o item de linha crítico que deve ser verificado para diferenciar entre Any CPU
e x86
. Se você estiver usando uma versão mais antiga do corflags.exe
(anterior ao SDK do Windows v8.0A), somente o 32BIT
item de linha estará presente na saída, como outros indicaram nas respostas anteriores. Caso contrário, 32BITREQ
e 32BITPREF
substituí-lo.
Isso assume que corflags.exe
está no %PATH%
. A maneira mais simples de garantir isso é usar a Developer Command Prompt
. Como alternativa, você pode copiá-lo de seu local padrão .
Se o arquivo em lotes abaixo for executado em um não gerenciado dll
ou exe
, ele será exibido incorretamente como x86
, pois a saída real de Corflags.exe
será uma mensagem de erro semelhante a:
corflags: erro CF008: o arquivo especificado não possui um cabeçalho gerenciado válido
@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.
Mais uma maneira seria usar o dumpbin das ferramentas do Visual Studio na DLL e procurar a saída apropriada
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: Acima o / p é para 32 bits dll
Uma opção mais útil com o dumpbin.exe é / EXPORTS, que mostra a função exposta pela DLL
dumpbin.exe /EXPORTS <PATH OF THE DLL>
Maneira mais genérica - use a estrutura de arquivos para determinar o tipo de imagem e imagem:
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);
}
}
Enumeração do modo de compilação
[Flags]
public enum CompilationMode
{
Invalid = 0,
Native = 0x1,
CLR = Native << 1,
Bit32 = CLR << 1,
Bit64 = Bit32 << 1
}
Código fonte com explicação no GitHub
Eu clonei uma ferramenta super útil que adiciona uma entrada de menu de contexto para montagens no Windows Explorer para mostrar todas as informações disponíveis:
Faça o download aqui: https://github.com/tebjan/AssemblyInformation/releases
Outra maneira de verificar a plataforma de destino de um assembly .NET é inspecionar o assembly com o .NET Reflector ...
@ # ~ # € ~! Acabei de perceber que a nova versão não é gratuita! Portanto, correção, se você possui uma versão gratuita do refletor .NET, pode usá-lo para verificar a plataforma de destino.
cfeduke observa a possibilidade de chamar GetPEKind. É potencialmente interessante fazer isso no PowerShell.
Aqui, por exemplo, está o código para um cmdlet que pode ser usado: https://stackoverflow.com/a/16181743/64257
Como alternativa, em https://stackoverflow.com/a/4719567/64257 , observe que "também há o cmdlet Get-PEHeader nas extensões de comunidade do PowerShell que pode ser usado para testar imagens executáveis".
Um aplicativo mais avançado para o qual você pode encontrar aqui: CodePlex - ApiChange
Exemplos:
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
Uma alternativa às ferramentas já mencionadas é o Telerik JustDecompile (ferramenta gratuita) que exibirá as informações ao lado do nome da montagem: