2010-02-01 3 views
12

Ich versuchte verschiedene Dinge, aber ich bin verrückt mit Interop.Pass C# String zu C++ und übergeben C++ Ergebnis (String, Char * .. was auch immer) zu C#

(hier ist das Wort String nicht auf einen Variablentyp sondern "eine Sammlung von char"): Ich habe eine nicht verwaltete C++ - Funktion, in einer DLL definiert, die ich versuche, von C#, diese Funktion zuzugreifen hat einen String-Parameter und einen String-Rückgabewert wie folgt:

Was sollte String in C++ Seite sein? und C# eins? und welche Parameter brauchen DllImport dafür?

Antwort

17

Was ich gefunden habe, um am besten zu arbeiten, ist, genauer zu sein, was hier vor sich geht. Eine Zeichenfolge als Rückgabetyp wird in dieser Situation wahrscheinlich nicht empfohlen.

Ein gängiger Ansatz besteht darin, die C++ - Seite die Puffer- und Puffergröße zu übergeben. Wenn es nicht groß genug für das ist, was GetString hineinlegen muss, wird die pufferSize-Variable modifiziert, um anzuzeigen, wie groß die passende Größe wäre. Das aufrufende Programm (C#) würde dann die Größe des Puffers auf die geeignete Größe erhöhen.

Wenn dies Ihr exportierte DLL-Funktion (C++):

void GetString(StringBuilder buffer, ref int bufferSize); 

So zu verwenden, um dies in C# würde man dann etwas tun, wie folgt aus:

extern "C" __declspec void GetString(char* buffer, int* bufferSize); 

Matching C# die folgenden wäre :

int bufferSize = 512; 
StringBuilder buffer = new StringBuilder(bufferSize); 
GetString(buffer, ref bufferSize); 
+1

Dies ist der richtige Ansatz. Beachten Sie, dass Sie die Puffergröße nicht als Referenz übergeben müssen. Das C/C++ sollte die Zeichenfolge beenden, buffer.ToString() erzeugt den Rückgabewert. –

+0

Vielen Dank für die Antworten. Ich habe irgendwo gelesen, dass StringBuilder für die Ausgabe-String verwendet wird, aber ich muss auch an eine C++ - Funktion übergeben eine Eingabe-String .. welcher Typ sollte ich verwenden? In der C++ - Seite habe ich char * und C# -Seite Zeichenfolge, aber es funktioniert nicht. Schließlich kann ich die Ausgabe "String" (C++ Seite) als const char * definieren? Oder muss ich einen char * haben? – Smjert

+0

@Smjert: Die Verwendung eines const char * ist ein guter Weg zu gehen, wenn Sie es nicht ändern. Dies sollte gut auf eine Zeichenfolge auf der C# -Seite abgebildet werden. Welche Art von Problemen siehst du? –

2

Der einzige gute Weg, den ich davon weiß, ist eine .NET C++ - Wrapper-Klasse mit Managed C++ Extensions zu schreiben, und innerhalb der .NET C++ - Objekt Ihren nativen C++ - Code aufrufen. Es gibt Funktionen in den verwalteten Erweiterungen, um einen System.String in einen char * oder einen beliebigen anderen Typ einer nicht verwalteten Zeichenfolge zu konvertieren.

Im Grunde erstellen Sie eine .NET-Klasse mit C++ und stellen sie aus einer Assembly dar, und intern können Sie diesen nativen C++ - Code aufrufen. Die andere Möglichkeit besteht darin, Ihrem C++ - Code mit P/Invoke eine reine C-Funktion hinzuzufügen und dann Ihren C-Code aus C# aufzurufen und Ihre C-Funktion Ihren C++ - Code aufrufen zu lassen. Das wird funktionieren, aber ich versuche, so oft wie möglich verwalteten Code zu verwenden.

2

Das größte Problem beim Übergeben von Strings von C++ zurück nach C# ist die Speicherzuweisung. Der GC sollte wissen können, wie der für diese Zeichenfolge reservierte Speicher bereinigt wird. Da C# umfangreiche COm-Interop-Unterstützung hat, weiß es über COM-BSTRs und wie diese zugewiesen und freigegeben werden. Daher wäre der einfachste Weg, dies zu tun, BSTR auf der C++ Seite und string auf der C# -Seite zu verwenden.

Beachten Sie, dass die Verwendung von BSTRs nicht bedeutet, dass Ihre Funktion über COM verfügbar sein muss.

2

Der Rückgabewert "Zeichenfolge" ist das Problem. Der P/Invoke-Marshaller ruft CoTaskMemFree() für den zurückgegebenen Zeiger auf. Das wird nicht gut funktionieren, wenn Sie CoTaskMemAlloc() in Ihrem C/C++ - Code nicht verwenden, um den Zeichenfolgenpuffer zuzuweisen. Was ist eine ziemlich ungewöhnliche Sache zu tun.

Die beste Lösung besteht darin, dem Aufrufer Ihres Codes zu ermöglichen, einen Zeiger auf einen Puffer und die Pufferlänge als Argumente an Sie übergeben. Auf diese Weise findet die gesamte Speicherzuweisung auf der einen Seite statt. Scott hat dir gezeigt, wie man das macht.

1

I hatte eine Unicode-C# Zeichenfolge in eine Multibyte-Darstellung umzuwandeln, um zu char * in c konvertiert ++ (Diese teilweise ist eine Möglichkeit Lösung)

ich folgendes sehr nützlich

string st; 
IntPtr stPtr = Marshal.StringToHGlobalAnsi(st); 
// Do your thing in C++ 

Marshal.FreeHGlobal(stPtr); 

gefunden Dies kann ineffizient sein und nicht in C# Art und Weise, ich bin neu in C#, hoffe das hilft