Передача строк из С# в С++ DLL и обратно - минимальный пример
Я пытаюсь сделать абсолютный простейший минимальный пример того, как передавать строки в и из С++ DLL на С#.
Мой С++ выглядит так:
using std::string;
extern "C" {
string concat(string a, string b){
return a + b;
}
}
С заголовком, например
using std::string;
extern "C" {
// Returns a + b
__declspec(dllexport) string concat(string a, string b);
}
Мой С# -
[DllImport("*****.dll", CallingConvention = CallingConvention.Cdecl)]
static extern string concat(string a, string b);
}
И я звоню: Console.WriteLine(concat ( "a", "b" ));
Но это дает исключение System.AccessViolationException. Кажется, это самая сложная вещь, но я полностью застрял на ней. Когда я попытался сделать подобный эксперимент с функцией "Добавить", которая взяла два удвоения и вернула двойной, у меня не было проблем.
Ответы
Ответ 1
Вы не можете передать С++ std::string
через границу взаимодействия. Вы не можете создать один из них в коде С#. Таким образом, ваш код никогда не сможет работать.
Вам нужно использовать дружеские типы взаимодействия на границе взаимодействия. Например, массивы символов с нулевым символом. Это хорошо работает, когда вы выделяете и освобождаете память в том же модуле. Таким образом, это достаточно просто при передаче данных с С# на С++.
С++
void foo(const char *str)
{
// do something with str
}
С#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(string str);
....
foo("bar");
В другом направлении вы обычно ожидаете, что вызывающий абонент выделит буфер, в который вызываемый может написать:
С++
void foo(char *str, int len)
{
// write no more than len characters into str
}
С#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(StringBuilder str, int len);
....
StringBuilder sb = new StringBuilder(10);
foo(sb, sb.Capacity);
Ответ 2
Это самый простой способ, который мне нравится - передать строку и использовать лямбду, чтобы получить ответ
С#
[DllImport(@"MyDLL.dll", EntryPoint ="Foo", CallingConvention = CallingConvention.StdCall)]
public static extern void Foo(string str, ResponseDelegate response);
...
Foo("Input", s =>
{
// response is returned in s - do what you want with it
});
C++
typedef void(_stdcall *LPEXTFUNCRESPOND) (LPCSTR s);
extern "C"
{
__declspec(dllexport) void __stdcall Foo(const char *str, LPEXTFUNCRESPOND respond)
{
// Input is in str
// Put your response in respond()
respond("HELLO");
}
}