I\'ve got an arbitrary list of .NET assemblies.
I need to programmatically check if each DLL was built for x86 (as opposed to x64 or Any CPU). Is this possible?
An alternative to already mentioned tools is Telerik JustDecompile (free tool) which will display the information next to the assembly name:
cfeduke notes the possibility of calling GetPEKind. It's potentially interesting to do this from PowerShell.
Here, for example, is code for a cmdlet that could be used: https://stackoverflow.com/a/16181743/64257
Alternatively, at https://stackoverflow.com/a/4719567/64257 it is noted that "there's also the Get-PEHeader cmdlet in the PowerShell Community Extensions that can be used to test for executable images."
Another way to check the target platform of a .NET assembly is inspecting the assembly with .NET Reflector...
@#~#€~! I've just realized that the new version is not free! So, correction, if you have a free version of .NET reflector, you can use it to check the target platform.
A more advanced application for that you can find here: CodePlex - ApiChange
Examples:
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
One more way would be to use dumpbin from the Visual Studio tools on DLL and look for the appropriate output
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
Note: Above o/p is for 32bit dll
One more useful option with dumpbin.exe is /EXPORTS, It will show you the function exposed by the dll
dumpbin.exe /EXPORTS <PATH OF THE DLL>
More generic way - use file structure to determine bitness and image type:
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);
}
}
Compilation mode enumeration
[Flags]
public enum CompilationMode
{
Invalid = 0,
Native = 0x1,
CLR = Native << 1,
Bit32 = CLR << 1,
Bit64 = Bit32 << 1
}
Source code with explanation at GitHub