Почему я не могу вернуть строку char * из C ++ в C # в сборке выпуска? - PullRequest
19 голосов
/ 10 июня 2011

Я пытаюсь вызвать следующую тривиальную функцию C из C #:

SIMPLEDLL_API const char* ReturnString()
{
    return "Returning a static string!";
}

Со следующим объявлением P / Invoke (с атрибутом return или без него это не имеет значения):

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

Работает, если 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);

Ответы [ 5 ]

26 голосов
/ 10 июня 2011

Или, возможно, попробуйте использовать

[DllImport("SimpleDll")]
public static extern IntPtr ReturnString();

, а в коде вызова используйте класс маршала

string ret = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(PInvoke.ReturnString());
10 голосов
/ 30 июля 2016

Это также можно сделать с помощью пользовательского 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;
    }
}

И используйте это так:

[DllImport("SimpleDll")]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ConstCharPtrMarshaler))]
public static extern string ReturnString();
3 голосов
/ 10 июня 2011

Компилятор C ++ в режиме Release помещает константу в защищенную страницу данных; попытка передать это C # вызывает проблемы. В режиме отладки компилятор не оптимизирует константу на странице данных, поэтому проблем с защитой нет.

2 голосов
/ 10 июня 2011

В моем опыте P / Invoke у вас обычно есть 2 параметра: 1. предварительно выделенный буфер и 2 - длина буфера. Возвращаемое значение - это длина возвращаемых данных (не должна превышать исходную длину).

Обычно релизы DEBUG не перемещают память так сильно.

Кстати, вы можете передать предварительно выделенный StringBuilder, затем установить sb.Lenght = возвращаемое значение функции C, тогда у вас не будет куча \ 0 нулей в конце вашей строки.

0 голосов
/ 10 июня 2011

Я столкнулся с подобной проблемой, и указание «CharSet», похоже, исправило ее.

[DllImport("SimpleDll", CharSet = CharSet.Ansi)]

Не уверен, почему это будет иначе в отладке и выпуске.

...