【问题标题】:Net5 calls the external DLL method and the return value is const char*, C# will directly crashNet5调用外部DLL方法返回值为const char*,C#会直接crash
【发布时间】:2021-04-14 00:20:39
【问题描述】:

C++ 代码:

__declspec(dllexport) const char* Get() {
    return "hello word!";
}

C#代码:

[DllImport("TestLink.dll")]
public static extern string Get();

调用后程序直接崩溃

【问题讨论】:

  • 但是使用IntPtr没有问题,我可以使用如下代码正常运行``` [DllImport("TestLink.dll")] public static extern IntPtr Get(); ```
  • 我已经在 C++ 代码中看到了一个缺失的 __stdcall... 而且至少还有另一个问题... C# 将尝试释放 C++ 字符串的内存(必须分配由Marshal.AllocCoTaskMem 而不一定是文字字符串)。您必须对 C# 说该字符串将是 ANSI 而不是 Unicode。到头来你走的是“正确”的相反方向。
  • 内部不安全静态字符串 ConvertToManaged(IntPtr cstr) { if (IntPtr.Zero == cstr) { return null; } 返回新字符串((sbyte*)((void*)cstr));我可以使用上面的代码,我明白了。 NET框架好像是一样的转换
  • 从 C++ 返回字符串的“经典”方式是使用 StringBuilder() C# 端。否则有各种解决方案

标签: c# c++ string pinvoke


【解决方案1】:

无论如何,当您从 C/C++/Native 端分配某些东西时,您必须使用 .NET 可以理解的 COM 分配器。所以返回字符串的方式有很多种,例如:

C++:

extern "C" __declspec(dllexport) void* __stdcall GetBSTR() {
    return SysAllocString(L"hello world"); // uses CoTaskMemAlloc underneath
}

extern "C" __declspec(dllexport) void* __stdcall GetLPSTR() {

    const char* p = "hello world";
    int size = lstrlenA(p) + 1;
    void* lp = CoTaskMemAlloc(size);
    if (lp)
    {
        CopyMemory(lp, p, size);
    }
    return lp;
}

extern "C" __declspec(dllexport) void* __stdcall GetLPWSTR() {

    const wchar_t* p = L"hello world";
    int size = (lstrlenW(p) + 1) * sizeof(wchar_t);
    void* lp = CoTaskMemAlloc(size);
    if (lp)
    {
        CopyMemory(lp, p, size);
    }
    return lp;
}

和 C#

[DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GetBSTR();

[DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string GetLPWSTR();

[DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetLPSTR();

【讨论】:

  • 我认为您应该指定调用约定...我认为代码原样使用 cdecl C++ 端和 stdcall C# 端。在 x64 上这可能不是什么大问题,因为 stdcall 和 cdecl 是相同的,但在 x86 上却是。
  • @xanatos - 是的。
  • 这对于 const 字符串来说太过分了。只需将返回类型声明为IntPtr 并调用Marshal.PtrToStringAnsi()Marshal.PtrToStringUTF8()
  • 值得指出,Q中代码失败的原因是marshaler在返回的指针上调用CoTaskMemFree
猜你喜欢
  • 2019-12-29
  • 1970-01-01
  • 2015-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多