Почему я не могу вернуть строку char * из С++ в С# в сборке Release?
Я пытаюсь вызвать следующую тривиальную функцию 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);
Ответы
Ответ 1
Или попробуйте использовать
[DllImport("SimpleDll")]
public static extern IntPtr ReturnString();
и в вашем кодовом коде используйте класс маршала
string ret = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(PInvoke.ReturnString());
Ответ 2
Это также можно сделать с помощью настраиваемого маршалера:
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
Компилятор С++ в режиме Release помещает константу в страницу данных, которая защищена; попытка передать это на С# вызывает проблемы. В режиме отладки компилятор не оптимизирует константу на странице данных, и поэтому нет проблем с защитой.
Ответ 4
В моем опыте P/Invoke у вас обычно есть 2 параметра:
1. предварительно выделенный буфер в и 2 - длина буфера.
Возвращаемое значение - это длина возвращаемых данных (не превышающая исходную длину).
Обычно релизы DEBUG не будут перемещать память примерно так же.
BTW, вы можете перейти в предварительно выделенный StringBuilder, а затем установить sb.Lenght = возвращаемое значение функции C, тогда у вас не будет пучка нулей \0 в конце вашей строки.
Ответ 5
У меня возникла аналогичная проблема, и указание "CharSet", похоже, исправить.
[DllImport("SimpleDll", CharSet = CharSet.Ansi)]
Не знаю, почему это было бы иначе в отладке и выпуске.