如果想要可预测的结果,那么请求使用 CopyMemory
实际上是一个非常糟糕的主意。首先,没有任何非托管应用程序调用名为CopyMemory
的任何函数,因为它在Windows头文件中被定义为C语言memcpy
函数的简单别名。根本没有在 kernel32.dll
中导出 CopyMemory
函数,而是否有可用的 RtlCopyMemory
取决于您的平台。当您请求 P/Invoke CopyMemory
时,所应用的导入函数逻辑(如果有)会因平台而异。以下是适用于Windows 10的表格:
+--------------+---------------+------------------------------+
| Platform | ExactSpelling | Resulting unmanaged function |
+--------------+---------------+------------------------------+
| .NET, 32-bit | true | - |
| .NET, 64-bit | true | - |
| .NET, 32-bit | false | RtlMoveMemory |
| .NET, 64-bit | false | memmove |
+--------------+---------------+------------------------------+
对于.NET Core,逻辑要简单得多:.NET Core不关心这种向后兼容的胡言乱语。如果你请求
kernel32!CopyMemory
,它会尽力给你
kernel32!CopyMemory
。由于根本没有这样的导出,所以会失败。这对64位和32位运行时都是正确的。
在64位Windows上,实际上存在一个导出
RtlCopyMemory
,这就是为什么.NET Core(以及64位.NET Framework)能够正常工作的原因。值得注意的是,文档并不保证它存在,因此依赖它似乎是不明智的--除了更基本的问题之外,它还会使您的代码在非Windows系统上无法移植。
从.NET 4.6开始,
Buffer.MemoryCopy
提供了一种可移植的替代方案,在.NET Core 2.0中也可用。如果你必须P/Invoke到本机函数(希望只是临时措施),最好P/Invoke到
RtlMoveMemory
,因为它在32位和64位Windows上都存在。
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", ExactSpelling = true)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, IntPtr count);
这将在 .NET Core 和 .NET Framework 上都能正常工作,对于两种位数的操作系统也是如此(当然,只限于Windows系统)。
Buffer.MemoryCopy
,它也可用于.NET Core 2.0。虽然不安全,但是可移植的。 - Jeroen MostertRtlCopyMemory
而不是CopyMemory
--没有这样的导出。在.NET CLR的P/Invoke层中可能会发生一些事情,以透明地修复这些问题,类似于ANSI/Unicode如何方便地转换,而.NET Core则不会浪费时间处理这种细节(如果您传递ExactSpelling=true,您会注意到它在.NET上也无法使用CopyMemory)。您可以为两个平台都使用RtlCopyMemory
导入。 - Jeroen MostertRtlMoveMemory
和RtlZeroMemory
有向前引用,但没有RtlCopyMemory
。但更令人毛骨悚然的是,它也没有CopyMemory
导出,所以我不知道CLR正在做什么来使这个工作...投资于让您的代码库使用Buffer.MemoryCopy
似乎是一个推进的好方法。:-P - Jeroen Mostert