.NET与.NET Core 2不同的P / Invoke入口点

我正在将一些代码从.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上部署时,它无法工作。