我正在将一些代码从.NET(4.5)转移到.NET Core(2),并有一个像这样的多目标项目…
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>net45;netcoreapp2.0</TargetFrameworks>
代码库使用了kernel32的Win32 API函数CopyMemory
,但是我发现我需要使用不同的入口点名称,具体取决于我所针对的框架。
#if NET45 [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] #else [DllImport("kernel32.dll", EntryPoint = "RtlCopyMemory", SetLastError = false)] #endif public static extern void CopyMemory(IntPtr dest, IntPtr src, IntPtr count);
我原以为这个比.NET要低
所以,问题是…为什么?
要求CopyMemory
实际上是一个非常糟糕的主意,如果你想要可预测的结果。 对于初学者,没有非托管应用程序调用任何名为CopyMemory
函数,因为它被定义为Windows头文件中的C memcpy
函数的简单别名。 kernel32.dll
中没有CopyMemory
导出, RtlCopyMemory
是否可用取决于您的平台。 当您要求CopyMemory
为P / Invoked(如果有)时,应用于哪个函数被导入的逻辑因平台而异。 这是一个适用于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 /调用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上运行就行了。
它与你的目标框架没有任何关系,重要的是这个过程的一点点。 一个.NET 4.5项目开始,项目>属性>生成选项卡>“首选32位”复选框打开。 .NETCore大量支持64位代码,并让你跳跃到32位运行时。
CopyMemory()是古老的,可以追溯到早期的16位Windows版本。 他们必须保留它的32位winapi,有很多VBx程序使用它。 但是,他们的脚下了64位。 否则在MSDN文章中详细记录:“该函数被定义为RtlCopyMemory函数,它的实现是内联提供的,更多信息请参见WinBase.h和WinNT.h”。 值得一看,你会看到“内联”的含义。 RtlCopyMemory实际上并没有被使用,而是用memcpy()代替它。
所以只需使用RtlCopyMemory来代替任何一种风格。 请记住,在Linux或MacOS上部署时,它无法工作。