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

Bra*_*son 5 .net windows pinvoke .net-core

我正在将一些代码从 .NET (4.5) 移动到 .NET Core (2) 并且有一个像这样的多目标项目......

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net45;netcoreapp2.0</TargetFrameworks>
Run Code Online (Sandbox Code Playgroud)

代码库使用CopyMemory来自 kernel32的 Win32 API 函数,但我发现我需要根据我的目标框架使用不同的入口点名称。

#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);
Run Code Online (Sandbox Code Playgroud)

我会认为这一切都比 .NET 低

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

Jer*_*ert 7

CopyMemory如果您想要可预测的结果,要求实际上是一个非常糟糕的主意。首先,没有非托管应用程序调用任何名为 的函数CopyMemory,因为它被定义为memcpyWindows 头文件中C函数的简单别名。根本没有CopyMemory导出kernel32.dll,是否RtlCopyMemory可用取决于您的平台。当您要求CopyMemoryP/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                      |
+--------------+---------------+------------------------------+
Run Code Online (Sandbox Code Playgroud)

对于 .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 to RtlMoveMemory,因为它存在于 32 位和 64 位 Windows 上:

[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", ExactSpelling = true)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, IntPtr count);
Run Code Online (Sandbox Code Playgroud)

这将在 .NET Core 和 .NET Framework 上正常工作,对于两种位数(当然,只要您在 Windows 上运行)。