我刚刚遇到了DllImport在C#中的奇怪行为,我无法解释。我想知道它是如何可能的,我可以在哪里读到它。情况是,通过DllImport,您可以调用不真正导出表单dll的函数。在我的例子中,它是kernel32.dll和函数ZeroMemory (但是对于复制/移动/填充内存这样的行为)。所以,我的代码:
[DllImport("kernel32", EntryPoint = "LoadLibraryW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string libName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr module, string procName);
//WTF???
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ZeroMemory(IntPtr address, int size);
static void TestMemory()
{
IntPtr mem = Marshal.AllocHGlobal(100); //Allocate memory block of 100 bytes size
Marshal.WriteByte(mem, 55); //Write some value in the first byte
ZeroMemory(mem, 100); //Clearing block of memory
byte firstByte = Marshal.ReadByte(mem); //Read the first byte of memory block
Console.WriteLine(firstByte); //Output 0 (not 55) - ZeroMemory is working
//Getting address of ZeroMemory from kernel32.dll
IntPtr kernelHandle = LoadLibrary("kernel32.dll");
IntPtr address = GetProcAddress(kernelHandle, "ZeroMemory");
Console.WriteLine(address.ToString("X")); //Output 0 - Library kernel32.dll DOESN'T export function ZeroMemory!!!
//Testing GetProcAddress via getting address of some exported function
Console.WriteLine(GetProcAddress(kernelHandle, "AllocConsole").ToString("X")); //Output some address value - all is OK.
}
没有抛出EntryPointNotFoundException -代码工作正常。如果将ZeroMemory的名称更改为ZeroMemory1或类似的-异常将被抛出。但在内核出口表中,我们可以看到:
根本没有ZeroMemory函数!如果我们查看msdn,就会看到ZeroMemory只是C++的WinBase.h头文件中的一个宏。我们在里面看到:
#define RtlMoveMemory memmove
#define RtlCopyMemory memcpy
#define RtlFillMemory(d,l,f) memset((d), (f), (l))
#define RtlZeroMemory(d,l) RtlFillMemory((d),(l),0)
#define MoveMemory RtlMoveMemory
#define CopyMemory RtlCopyMemory
#define FillMemory RtlFillMemory
#define ZeroMemory RtlZeroMemory
显然,在C++ ZeroMemory中,这实际上是通过ntdll.dll的RtlFillMemory实现的。但它在C++!为什么它在C#中工作??关于DllImport属性这里的正式文档,我们可以阅读下面的内容:
作为最低要求,您必须提供包含入口点.的DLL 的名称。
但在这种情况下,kernel32.dll不能满足ZeroMemory的入口点。怎么一回事??救命求你了。
发布于 2021-03-13 07:19:53
OP是正确的,因为kernel32.dll
没有导出ZeroMemory
导出,然而C# DllImport
以某种方式成功地神奇地解析了针对框架的.NET应用程序中正确的RtlZeroMemory
导出的ZeroMemory
引用(但不是在核心)。
结果是,框架代码专门检查了一些被记录为内联/宏的Win32 API (MoveMemory
、CopyMemory
FillMemory
、ZeroMemory
),并将其内部重新路由到正确的导出。虽然没有正式记录,但这一点在MS批准的评论中在.NET运行时问题下得到了承认.
作为一个FYI,.NET框架中有一些特殊的大小写P/Invoke名称:
MoveMemory
、CopyMemory
、FillMemory
和ZeroMemory
。当指向kernel32 on .NET框架时,所有这些都可以工作,但在.NET核心上将失败。请使用EntryPoint
属性中的DllImport
属性来获得所需的行为。可以使用Visual命令提示符中的dumpbin /exports kernel32.dll
找到正确的导出名称。
以上建议为声明添加一个显式EntryPoint
,以便在框架和核心中都工作,例如:
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
public static extern void ZeroMemory(IntPtr address, IntPtr count);
神奇的名称重新映射发生在开源.NET代码之外,但是可以在西蒙·穆里尔在注释中提供的反汇编中清楚地看到。
https://stackoverflow.com/questions/66596448
复制相似问题