为什么我不能在Release版本中将C++中的char*字符串返回给C#?

Qwe*_*tie 20 .net pinvoke marshalling access-violation

我试图从C#调用以下简单的C函数:

SIMPLEDLL_API const char* ReturnString()
{
    return "Returning a static string!";
}
Run Code Online (Sandbox Code Playgroud)

使用以下P/Invoke声明(使用或不使用return属性,它没有区别):

[DllImport("SimpleDll")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string ReturnString();
Run Code Online (Sandbox Code Playgroud)

如果DLL是Debug构建但在Release构建(AccessViolationException)中崩溃,则它可以工作.

我正在调用十几个其他简单的函数,这是唯一失败的函数(这里是其他函数:)

[DllImport("SimpleDll")] public static extern int NextInt();
[DllImport("SimpleDll")] public static extern void SetNextInt(int x);
[DllImport("SimpleDll")] public static extern int AddInts(int a, int b);
[DllImport("SimpleDll")] public static extern int AddFourInts(int a, int b, int c, int d);
[DllImport("SimpleDll")] public static extern double AddDoubles(double x, double y);
[DllImport("SimpleDll")] public static extern IntPtr AddDoublesIndirect(ref double x, ref double y);
[DllImport("SimpleDll")] [return: MarshalAs(UnmanagedType.U1)]
public static extern char CharStringArgument([MarshalAs(UnmanagedType.LPStr)]string s);
[DllImport("SimpleDll")] [return: MarshalAs(UnmanagedType.U2)]
public static extern char WCharStringArgument([MarshalAs(UnmanagedType.LPWStr)]string s);
[DllImport("SimpleDll")] [return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string ReturnWString();
[DllImport("SimpleDll")] [return: MarshalAs(UnmanagedType.BStr)]
public static extern string ReturnBSTR();
[DllImport("SimpleDll")] public static extern System.Drawing.Point MakePoint(int x, int y);
[DllImport("SimpleDll")] public static extern IntPtr MakePointIndirect(int x, int y);
[DllImport("SimpleDll")] public static extern int GetPointY(System.Drawing.Point p);
[DllImport("SimpleDll")] public static extern int GetPointYIndirect(ref System.Drawing.Point pp);
[DllImport("SimpleDll")] public static extern int SumIntegers(ref int firstElem, int size);
Run Code Online (Sandbox Code Playgroud)

Wal*_*eed 26

或者也许尝试使用

[DllImport("SimpleDll")]
public static extern IntPtr ReturnString();
Run Code Online (Sandbox Code Playgroud)

并在您的调用代码中,使用Marshal类

string ret = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(PInvoke.ReturnString());
Run Code Online (Sandbox Code Playgroud)

  • @Qwertie,它不是.NET框架中的错误.默认情况下,`string`编组将创建`string`的托管副本,并尝试取消分配`char*`使用的`C`内存.使用`IntPtr`不会自动创建任何对象的托管副本,因此不会自动尝试取消分配内存.因为我相信你会同意,尝试从字符串文字中取消分配内存并不是一个好主意:)作为一个有趣的问题,你可能想看到这篇文章以获取更多信息:http:// stackoverflow. COM/A /一百二十四万四千六百三十零分之三十七万零五百十九 (12认同)
  • @Pooven,你的评论应该是一个答案:)...调用`CoTaskMemFree`对我来说似乎是非常奇怪的默认行为,因为我在Windows上编程了我的整个职业生涯并且从未调用它.CoTaskMemFree被记录为COM API,但如果我想以COM方式传递字符串,我将使用SysAllocString和SysFreeString并编组为BSTR. (3认同)

Ben*_*hon 10

使用自定义Marshaler也可以这样做:

class ConstCharPtrMarshaler : ICustomMarshaler
{
    public object MarshalNativeToManaged(IntPtr pNativeData)
    {
        return Marshal.PtrToStringAnsi(pNativeData);
    }

    public IntPtr MarshalManagedToNative(object ManagedObj)
    {
        return IntPtr.Zero;
    }

    public void CleanUpNativeData(IntPtr pNativeData)
    {
    }

    public void CleanUpManagedData(object ManagedObj)
    {
    }

    public int GetNativeDataSize()
    {
        return IntPtr.Size;
    }

    static readonly ConstCharPtrMarshaler instance = new ConstCharPtrMarshaler();

    public static ICustomMarshaler GetInstance(string cookie)
    {
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

[DllImport("SimpleDll")]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ConstCharPtrMarshaler))]
public static extern string ReturnString();
Run Code Online (Sandbox Code Playgroud)

  • 这是要走的路 :) (2认同)