2010-02-01 42 views
12

Ho provato diverse cose ma mi sto arrabbiando con Interop.Passare la stringa C# a C++ e passare il risultato C++ (stringa, char * .. qualunque) a C#

(qui la stringa di parole non è riferita a un tipo di variabile ma "una raccolta di caratteri"): Ho una funzione C++ non gestita, definita in una DLL, che sto provando ad accedere da C#, questa funzione ha un parametro stringa e un valore di restituzione stringa come questo:

string myFunction(string inputString) 
{ 
} 

Cosa deve essere una stringa in C++? e C# uno? e quali parametri necessitano di DllImport per questo?

risposta

17

Quello che ho trovato che funziona meglio è essere più espliciti su cosa sta succedendo qui. Avere una stringa come tipo di ritorno probabilmente non è raccomandato in questa situazione.

Un approccio comune è far passare il lato C++ al buffer e alle dimensioni del buffer. Se non è abbastanza grande per ciò che deve essere inserito da GetString, la variabile bufferSize viene modificata per indicare quale sarebbe la dimensione appropriata. Il programma chiamante (C#) aumenterebbe la dimensione del buffer alla dimensione appropriata.

Se questa è la funzione DLL esportata (C++):

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

Corrispondenza C# sarebbe il seguente:

void GetString(StringBuilder buffer, ref int bufferSize); 

Quindi, per utilizzare questo in C# si sarebbe poi fare qualcosa di simile alla seguente :

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

Questo è l'approccio corretto. Si noti che non è necessario passare la dimensione del buffer per riferimento. Il C/C++ dovrebbe terminare la stringa, buffer.ToString() produce il valore restituito. –

+0

Grazie a tutti per le risposte. Ho letto da qualche parte che StringBuilder è usato per la stringa di output, ma ho bisogno anche di passare alla funzione C++ una stringa di input .. che tipo dovrei usare? Nel lato C++ ho messo la stringa laterale char * e C#, ma non funziona. Infine, posso definire l'output "string" (lato C++) come const char *? Oppure devo avere un char *? – Smjert

+0

@Smjert: l'uso di un const char * è un buon modo per andare se non lo si cambia. Questo dovrebbe mappare perfettamente con una stringa sul lato C#. Che tipo di problemi vedi? –

2

L'unico buon modo che so di fare questo è scrivere una classe wrapper .NET C++ usando Managed C++ Extensions, e all'interno dell'oggetto .NET C++ chiama il tuo codice C++ nativo. Ci sono funzioni nelle estensioni gestite per convertire un System.String in un char * o qualsiasi altro tipo di stringa non gestita.

Fondamentalmente si crea una classe .NET utilizzando C++ e la si espone da un assembly e internamente a tale assembly è possibile chiamare il proprio codice C++ nativo. L'altro modo è aggiungere una pura funzione C al tuo codice C++ usando P/Invoke e poi chiamare il tuo codice C da C# e far sì che la tua funzione C chiami il tuo codice C++. Questo funzionerà, ma tendo a cercare di usare il codice gestito il più possibile.

2

Il problema più grande con il passaggio di stringhe da C++ a C# è l'allocazione della memoria. Il GC dovrebbe essere in grado di sapere come pulire la memoria allocata per questa stringa. Poiché C# dispone di un ampio supporto per l'interoperabilità di COm, conosce i BSTR COM e come allocarli e deallocarli. Quindi il modo più semplice per farlo sarebbe utilizzare BSTR sul lato C++ e string sul lato C#.

Nota: l'utilizzo di BSTR non implica che la funzione debba essere visualizzata tramite COM.

2

Il valore di ritorno "stringa" è il problema. Il marshaller P/Invoke chiamerà CoTaskMemFree() sul puntatore restituito. Non funzionerà bene a meno che non si sia utilizzato CoTaskMemAlloc() nel codice C/C++ per allocare il buffer delle stringhe. Qual è una cosa abbastanza insolita da fare.

La soluzione migliore è consentire al chiamante del codice di passare un puntatore a un buffer e la lunghezza del buffer a te come argomenti. In questo modo tutta l'allocazione di memoria avviene su un lato. Scott ti ha mostrato come farlo.

1

ho dovuto convertire un # stringa unicode C ad una rappresentazione multibyte per convertire in char * in C++ (questa è la soluzione parziale unidirezionale)

ho trovato questa molto utile

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

Marshal.FreeHGlobal(stPtr); 

Questo può essere inefficiente e non in modo C#, sono nuovo di C#, spero che questo aiuti

Problemi correlati