У меня есть простая настройка ситуации:
Базовый код .NET:
[DllImport ... stuff... use cdecl]
public static extern void SetCallback(CallbackDelegate c);
[UnmanagedFunctionPointer ... use cdecl]
public delegate CallbackDelegate(MarshalAs(single byte string with null terminating character pointer) string c)
public static CallbackDelegate theNotGCdDelegate = null;
public void Start() {
theNotGCdDelegate = new CallbackDelegate(CallbackCalledHere);
SetCallback(theNotGCdDelegate);
}
public void CallbackCalledHere(string text) {
Debug.WriteLine(text);
}
Базовый код C (скомпилирован с MinGW):
__declspec(dllexport) void __cdecl SetCallback(void (__cdecl *TheCallback)(char* text)){
// This does not work as expected:
TheCallback("This is a string literal");
// This works as expected:
char pointerMessage[] = "This is also a string literal, but referenced by an array.";
TheCallback(pointerMessage);
}
Итакмоя ситуация (и я обещаю, что я не придумываю это!) заключается в том, что вызов SetCallback из C со строковым литералом direct (т. е. TheCallback ("Hello World")) приводит к пустой строке (первый байт строкинулевой)..NET преобразует это в пустую строку (то есть, "").Я подтвердил, что это так, используя Marshal.ReadByte и сам Marshaling параметра как IntPtr.
Во втором случае (при объявлении указателя / массива и передаче этой переменной в TheCallback) яполучить строку идеально и как ожидалось!
Это не имеет смысла для меня теоретически.Обе строки должны быть помещены в раздел данных C DLL, и обе имеют указатели, которые никогда не должны двигаться!Обе ситуации должны передавать указатели на фактические данные.У меня нет предупреждений о компиляции.
Это может быть связано, но, возможно, нет: я работаю на 64-битной машине.Я на 100% уверен, что когда я компилирую 32-битную DLL в MinGW, я всегда вызываю эту DLL из 32-битного пространства процесса в моем приложении .NET (явно выводя 32-битную сборку).Аналогично, когда я компилирую 64-битную DLL, я всегда вызываю эту DLL из 64-битного пространства процесса в моем приложении .NET (явно выводя 64-битную сборку).
Я проверил, что правильный 32-битный флаготображается в диспетчере задач и отсутствует в 64-разрядной сборке.DLL находится только в каталоге bin и не является regsvr32'd.
Важно, что в 64-битной DLL эта проблема не возникает: обе строки правильно передаются в среду выполнения .NET.32-битная DLL имеет проблему, о которой я говорил выше.
Я компилирую / связываю 32-битную DLL с gcc / ld из MinGW (из http://mingw.org/). Я компилирую/ связывание 64-битной DLL с пакетом mingw-w64-bin_i686-mingw_20111220.zip из http://mingw -w64.sourceforge.net /
Может кто-нибудь объяснить, что здесь происходит? Или, может быть,вы бы предпочли другой компилятор или флаги компилятора?
EDIT
Я только что обнаружил, что GetFunctionPointerForDelegate работает только с функциями stdcall. Я думаю, что это важный момент.Я использовал этот вызов Marshal, но я полагаю, что теперь я буду полностью избегать его, поскольку обратный вызов должен быть cdecl. Я предполагаю, что нотации UnmanagedFunctionPointer должно быть достаточно для передачи указателя на функцию cdecl.