PInvoke 编组 const char * 失败并显示 UnmanagedType.LPStr

PInvoke marshalling const char * fails with UnmanagedType.LPStr

我不明白为什么 .NET Core 编组无法使用一种编组方法而不是另一种方法。在下面的示例中,Marshal.PtrToStringAnsi 可以成功编组来自本机 dll 的字符串,但是使用 [return: MarshalAs(UnmanagedType.LPStr)] 属性的相同调用不能吗?还是来自 IntPtr 的编组只是运气好并且还可以根据 OS 内存管理访问无效内存?

C 库代码是(编译为 C 而不是 C++):

static const char str[] = "Hello World";

__declspec(dllexport) const char* getstr() {
  return str;
}

C#测试程序:

using System;
using System.Runtime.InteropServices;

namespace CLibraryPinvoke
{
    class Program
    {
        public const string LIB_NAME = "CLibrary";

        [DllImport(LIB_NAME, EntryPoint = "getstr")]
        public static extern IntPtr getstrgood();

        [DllImport(LIB_NAME, EntryPoint ="getstr")]
        [return: MarshalAs(UnmanagedType.LPStr)]
        public static extern string getstrbad();

        static void Main(string[] args)
        {
            Console.WriteLine($"getstr={Marshal.PtrToStringAnsi(getstrgood())}.");

            // Crash.
            Console.WriteLine($"getstr={getstrbad()}.");
        }
    }
}

执行结果:

getstr=Hello World.
CLibraryPinvoke\bin\Debug\netcoreapp3.1\CLibraryPinvoke.exe (process 31848) exited with code -1073740940.    

PtrToStringAnsi 从 return 值指向的缓冲区复制字符串。

UnmanagedType.LPStr 从 return 值指向的缓冲区复制字符串,并释放它 with CoTaskMemFree。使用 CoTaskMemFree 释放 static const char[] 会使程序崩溃。

这是因为return值的语义是保存结果的内存是在被调用端分配的。

documented,如果内存是在被调用端分配的,但不应该用CoTaskMemFree释放,则必须将其编组为IntPtr并使用适当的方法。
static const char[] 备份的内存的适当方法是不要管它。

这要求您的功能的用户了解您的功能的实现细节,这不是一件好事。当然,您可以将该函数记录为 returning 永远不应释放的内存,但这样它可能不是一个非常有用的函数,如果您将来要更改它的行为,您将被卡住使用 returning 可能的 static const char[]s.
之一 您可以通过每次分配内存并让 .NET 运行时管理其余部分,或者让函数接受一个 char* 参数来将响应复制到

来避免所有这些情况。