web-dev-qa-db-fra.com

Passage de chaînes de C # à C++ DLL et retour - exemple minimal

J'essaye de faire l'exemple minimal le plus simple et absolu de la façon de passer des chaînes vers et depuis un C++ DLL en C #.

Mon C++ ressemble à ceci:

using std::string;

extern "C" {
    string concat(string a, string b){
        return a + b;
    }
}

Avec un en-tête comme

using std::string;

extern "C" {
    // Returns a + b
    __declspec(dllexport) string concat(string a, string b);
}

Mon C # est

[DllImport("*****.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern string concat(string a, string b);
}

Et je l’appelle avec: Console.WriteLine (concat ("a", "b"));

Mais cela donne une System.AccessViolationException. Cela semble être la chose la plus insignifiante à traiter, mais je suis complètement coincé là-dessus. Lorsque j'ai essayé de faire une expérience similaire avec une fonction "Ajouter" qui prenait deux doubles et renvoyait un double, je n'avais aucun problème.

33
asutherland

Vous ne pouvez pas transmettre un C++ std::string à travers une limite d'interopérabilité. Vous ne pouvez pas en créer un dans votre code C #. Donc, votre code ne peut jamais fonctionner. 

Vous devez utiliser des types compatibles interop à la limite d'interopérabilité. Par exemple, des tableaux de caractères à terminaison nulle. Cela fonctionne bien lorsque vous allouez et désallouez la mémoire dans le même module. Donc, c'est assez simple lorsque vous passez des données de 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");

Dans l’autre sens, vous vous attendez généralement à ce que l’appelant alloue le tampon dans lequel l’appelé peut écrire:

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);
57
David Heffernan

C’est le moyen le plus simple que j’aime: passer une chaîne et utiliser un lambda pour obtenir la réponse.

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");
     }
 } 
0
Jonny