Подтвердить что ты не робот

Передача строк из С# в С++ 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. Кажется, это самая сложная вещь, но я полностью застрял на ней. Когда я попытался сделать подобный эксперимент с функцией "Добавить", которая взяла два удвоения и вернула двойной, у меня не было проблем.

4b9b3361

Ответ 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");
     }
 }