У меня есть три проблемы, которые я вижу.
- Соглашения о вызовах не совпадают.Ваш код C ++
cdecl
, а код C # stdcall
. - В коде C ++ используются широкие строки, но в коде C # используются строки ANSI.
- Второй параметр не соответствует,Ваш код C # предполагает, что код C ++ возвращает новый указатель на строку C, которую код C # затем освобождает с помощью COM-распределителя.Ваш код на C ++ этого не делает.
Теперь рассмотрим их более подробно.
Соглашения о вызовах
Это довольнолегко исправитьПросто измените код C ++ на stdcall
или код C # на cdecl
.Но не делай и того, и другого.Я бы изменил код C #:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl]
Unicode / ANSI-строки
Я предполагаю, что вы хотите использовать Unicode-строки, поскольку вы явно выбрали их вC ++ код.Но по умолчанию P / invoke используется для сортировки строк ANSI.Вы можете изменить это снова в DllImport
следующим образом:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
Возвращение строки из C ++ в C #
Ваше текущее объявление функции C ++ выглядит так:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
Декоратор __out
не имеет никакого реального эффекта, кроме документирования того, что вы хотите изменить буфер, на который указывает strOut
, и вернуть эти изменения вызывающей стороне.
ВашОбъявление C #:
static extern bool func(String strIn, ref String strOut);
Теперь ref String strOut
просто не совпадает.Строковый параметр ref
соответствует этому в C ++:
BOOL func(LPWSTR strIn, LPWSTR *strOut)
Другими словами, код C # ожидает, что вы вернете новый указатель.На самом деле он затем приступит к освобождению буфера, который вы вернули в strOut
, вызвав CoTaskMemFree
.Я уверен, что это не то, что вам нужно.
Ваш исходный код C ++ может возвращать строку в код C # только путем изменения буфера, который был ему передан.Этот код будет выглядеть следующим образом:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
...
wcscpy(strOut, L"the returned string");
...
}
Если это то, что вам нужно, вам следует выделить достаточный буфер в C # для объекта StringBuilder
.
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
static extern bool func(string strIn, StringBuilder strOut);
...
StringBuilder strOutBuffer = new StringBuilder(128);
bool res = func("input string", strOutBuffer);
string strOut = StringBuilder.ToString();
Если вы простов коде C # не можете решить, какой размер буфера вам нужен, поэтому лучше всего использовать BSTR
для маршалинга strOut.См. ответ для деталей.