Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

windows - Different P/Invoke entry point for .NET vs .NET Core 2

I'm in the process of moving some code from .NET (4.5) to .NET Core (2) and have a multi-targeted project like so...

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net45;netcoreapp2.0</TargetFrameworks>

The code base uses the Win32 API function CopyMemory from kernel32, but I've found I need to use a different entry point name depending on which framework I'm targeting.

#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);

I would have thought this was all at a lower level than .NET

So, the question is... why?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Asking for CopyMemory is actually a pretty bad idea, if you want predictable results. For starters, no unmanaged application calls any function named CopyMemory, as it's defined as a simple alias for the C memcpy function in the Windows headers. There is no CopyMemory export in kernel32.dll at all, and whether RtlCopyMemory is available is dependent on your platform. The logic applied for what function gets imported when you ask for CopyMemory to be P/Invoked (if any) varies by platform. Here's a little table that applies to 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                      |
+--------------+---------------+------------------------------+

For .NET Core, the logic is much simpler: .NET Core doesn't care about this backwards compatibility nonsense. If you ask for kernel32!CopyMemory, by golly it will try and get you kernel32!CopyMemory. And since there is no such export at all, it will fail. This is true for both 64-bit and 32-bit runtimes.

On 64-bit Windows RtlCopyMemory actually exists as an export, which is why that works for .NET Core (and 64-bit .NET Framework as well). It's worth noting, though, that the documentation does not guarantee that it exists at all, so it seems inadvisable to rely on this -- aside from the more basic problem that it makes your code unportable on anything that's not Windows.

From .NET 4.6 onwards, Buffer.MemoryCopy provides a portable alternative, available in .NET Core 2.0 as well. If you must P/Invoke to a native function (hopefully only as a stopgap measure) you're better off P/Invoking to RtlMoveMemory, since it exists on both 32-bit and 64-bit Windows:

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

This will work correctly on both .NET Core and .NET Framework, for both bitnesses (as long as you're running on Windows, of course).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...