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

时间:2017-10-30 11:52:02

标签: .net windows .net-core pinvoke

我正在将一些代码从.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

更低的水平

所以,问题是......为什么?

2 个答案:

答案 0 :(得分:4)

如果您想要可预测的结果,请求CopyMemory实际上是一个非常糟糕的主意。对于初学者,没有非托管应用程序调用任何名为CopyMemory的函数,因为它被定义为Windows标头中C memcpy函数的简单别名。 CopyMemory中根本没有kernel32.dll导出,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上运行)。

答案 1 :(得分:2)

它与您定位的框架没有任何关系,重要的是过程的重要性。 .NET 4.5项目从Project&gt;开始。属性&gt;构建标签&gt; &#34;喜欢32位&#34;复选框已打开。 .NETCore大量使用64位代码,让你跳过一个环节来获得32位运行时。

CopyMemory()已经过时,可以追溯到早期的16位Windows版本。他们不得不为32位winapi保留它,有很多VBx程序使用它。但是要把他们的脚放到64位。否则在MSDN文章中有详细记载:&#34;此函数被定义为RtlCopyMemory函数。它的实现是内联提供的。有关更多信息,请参阅WinBase.h和WinNT.h&#34;。哪些值得一看,你会看到什么&#34;内联&#34;手段。实际上也没有使用RtlCopyMemory,而是用memcpy()代替它。

所以只需使用RtlCopyMemory代替任何一种风味。请记住,在Linux或MacOS上部署时,它无法正常工作。