Vas*_*sya 6 .net c# winapi marshalling dllimport
我刚刚在 C# 中遇到了 DllImport 的奇怪行为,我无法解释。我想知道它是如何可能的,以及我可以在哪里阅读它。案例是通过 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.
}
Run Code Online (Sandbox Code Playgroud)
没有抛出 EntryPointNotFoundException - 代码工作正常。如果将 ZeroMemory 的名称更改为 ZeroMemory1 或类似的名称 - 将引发异常。但是在kernel32.dll的导出表中我们看到:
根本没有 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
Run Code Online (Sandbox Code Playgroud)
显然,在 C++ ZeroMemory 中,它实际上是通过 ntdll.dll 中的 RtlFillMemory 工作的。但它是在 C++ 中的!!!为什么它在 C# 中工作?在此处的DllImport 属性的官方文档中,我们可以阅读以下内容:
作为最低要求,您必须提供包含入口点的 DLL 的名称 。
但在这种情况下,kernel32.dll 无法连接 ZeroMemory 的入口点。到底是怎么回事??请帮忙。
OP 是正确的,因为kernel32.dll没有导出ZeroMemory导出,但 C#DllImport却以某种方式成功地神奇地解析了针对框架(但不是核心)的 .NET 应用程序中ZeroMemory对正确导出的引用。RtlZeroMemory
事实证明,框架代码专门检查了一些记录为内联/宏 ( MoveMemory、CopyMemory FillMemory、 ) 的 Win32 API,并在内部重新路由到正确的导出。ZeroMemory虽然没有正式记录,但 MS 批准的.NET 运行时问题下的评论中承认了这一点。
仅供参考,.NET Framework 中有一些特殊大小写的 P/Invoke 名称:
MoveMemory、CopyMemory、FillMemory和ZeroMemory。当指向 .NET Framework 上的 kernel32 时,所有这些都将起作用,但在 .NET Core 上将失败。请使用属性EntryPoint中的属性DllImport来获得所需的行为。可以使用dumpbin /exports kernel32.dllVisual Studio 命令提示符找到正确的导出名称。
上面建议添加显式声明EntryPoint以在 Framework 和 Core 中工作,例如:
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
public static extern void ZeroMemory(IntPtr address, IntPtr count);
Run Code Online (Sandbox Code Playgroud)
神奇的名称重新映射发生在开源 .NET 代码之外,但可以在Simon Mourier在评论中提供的反汇编中清楚地看到。
| 归档时间: |
|
| 查看次数: |
153 次 |
| 最近记录: |