我有一个C#应用程序,在Visual Studio 2017中编译在任何CPU'目标,使用'首选32位'选项已禁用。在这个应用程序中,我试图pinvoke kernel32!GetBinaryType()。使用'首选32位'启用,它工作正常。从C ++可执行文件以32位或64位模式运行时,它可以正常工作。我不确定我在使用64位C#应用程序时遇到了什么问题。
这是我的pinvoke签名:
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetBinaryTypeW([MarshalAs(UnmanagedType.LPWStr)] string path, out UInt32 result);
从64位模式调用此项,GetLastError()
返回193,根据https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
ERROR_BAD_EXE_FORMAT。
我已经为我的C#应用程序和我的C ++应用程序附加了一个64位调试器,以确保字符串在堆栈中的正确位置到达GetBinaryTypeW()
,并且在我看来它是。
我担心我在封送者身上遇到了一些微妙的错误。
我的做法出了什么问题?
更新
评论中引用的问题与此情况不符。在该问题中,对LoadLibrary()
的调用失败,因为它被用于尝试将32位DLL加载到64位进程中。我不是要加载任何DLL。我只是试图使用GetBinaryType()来检查可执行文件的PE头。
答案 0 :(得分:2)
GetBinaryType
会做一些有趣的事情,而.NET运行时可能会加剧这种情况。不幸的是,我现在不记得所有的细节。但是,这是一个更强大的解决方案,适用于EXE和DLL。请注意,这仅检测PE二进制类型。如果您正在处理编译为AnyCPU的.NET程序集,您可能会或可能不会得到您期望的结果。我会留下任何进一步的调查作为读者的练习。
public enum BinaryType : uint
{
SCS_32BIT_BINARY = 0,
SCS_64BIT_BINARY = 6,
SCS_DOS_BINARY = 1,
SCS_OS216_BINARY = 5,
SCS_PIF_BINARY = 3,
SCS_POSIX_BINARY = 4,
SCS_WOW_BINARY = 2
}
public static BinaryType? GetBinaryType(string path)
{
using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
stream.Seek(0x3C, SeekOrigin.Begin);
using (var reader = new BinaryReader(stream))
{
if (stream.Position + sizeof(int) > stream.Length)
return null;
var peOffset = reader.ReadInt32();
stream.Seek(peOffset, SeekOrigin.Begin);
if (stream.Position + sizeof(uint) > stream.Length)
return null;
var peHead = reader.ReadUInt32();
if (peHead != 0x00004550) // "PE\0\0"
return null;
if (stream.Position + sizeof(ushort) > stream.Length)
return null;
switch (reader.ReadUInt16())
{
case 0x14c:
return BinaryType.SCS_32BIT_BINARY;
case 0x8664:
return BinaryType.SCS_64BIT_BINARY;
default:
return null;
}
}
}
}