C#でC++ DLLとの間で文字列をやり取りする方法の絶対的な最も簡単な最小限の例を作成しようとしています。
私のC++は次のようになります。
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);
}
私のC#は
[DllImport("*****.dll", CallingConvention = CallingConvention.Cdecl)]
static extern string concat(string a, string b);
}
そして、私はそれを次のように呼び出しています:Console.WriteLine(concat( "a"、 "b"));
ただし、これによりSystem.AccessViolationExceptionが発生します。これは対処するのが最も些細なことであるように思えますが、私は完全にそれにこだわっています。 2つのdoubleを取り、doubleを返す関数「Add」を使って同様の実験をしようとしたとき、問題はありませんでした。
相互運用境界を越えてC++ std::string
を渡すことはできません。 C#コードでこれらのいずれかを作成することはできません。したがって、コードは機能しません。
相互運用境界では、相互運用フレンドリータイプを使用する必要があります。たとえば、ヌルで終わる文字の配列。同じモジュールでメモリの割り当てと割り当て解除を行うと、うまく機能します。そのため、C#からC++にデータを渡すときは非常に簡単です。
C++
void foo(const char *str)
{
// do something with str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(string str);
....
foo("bar");
他の方向では、通常、呼び出し元がバッファーを割り当てることを期待します。バッファーは、呼び出し先が書き込むことができます。
C++
void foo(char *str, int len)
{
// write no more than len characters into str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(StringBuilder str, int len);
....
StringBuilder sb = new StringBuilder(10);
foo(sb, sb.Capacity);
これは私が好きな最も簡単な方法です-文字列を渡し、ラムダを使用して応答を取得します
C#
[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");
}
}