2010-02-01 233 views
12

我嘗試了不同的事情,但我對Interop生氣了。將C#字符串傳遞給C++並將C++結果(字符串,字符* ..無論)傳遞給C#

(這裏的字串沒有提到一個variabile的類型,但「字符集」): 我有一個非託管C++函數,在DLL中定義的,我是想從C#訪問,此功能有一個字符串參數,像這樣的字符串返回值:在C++側

string myFunction(string inputString) 
{ 
} 

應該用什麼字符串?和C#之一?和什麼參數需要DllImport呢?

回答

17

我發現最好的工作是更清楚地知道這裏發生了什麼。在這種情況下可能不推薦使用字符串作爲返回類型。

常用的方法是讓C++端傳遞緩衝區和緩衝區大小。如果它的大小不夠GetString必須放入它,緩衝區大小變量將被修改以指示適當的大小。調用程序(C#)會將緩衝區的大小增加到適當的大小。

如果這是你導出的DLL函數(C++):

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

匹配C#是以下幾點:

void GetString(StringBuilder buffer, ref int bufferSize); 

所以使用這個在C#中,你會做類似下面的:

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

這是正確的方法。請注意,您不必通過引用傳遞緩衝區大小。 C/C++應該終止字符串,buffer.ToString()產生返回值。 – 2010-02-01 21:02:23

+0

謝謝大家的答案。 我讀的地方,StringBuilder用於輸出字符串,但我還需要傳遞給C++函數一個輸入字符串..我應該使用什麼類型?在C++端我已經把char *和C#端的字符串,但它不起作用。 最後,我可以將輸出「字符串」(C++端)定義爲一個常量字符*嗎? 或者我必須有一個char *? – Smjert 2010-02-01 23:22:58

+0

@Smjert:如果你不改變它,使用const char *是一個好方法。這應該很好地映射到C#端的字符串。你看到什麼類型的問題? – 2010-02-01 23:59:13

2

我知道這樣做的唯一好方法是使用Managed C++ Extensions編寫一個.NET C++包裝類,並在.NET C++對象內調用您的本機C++代碼。託管擴展中有一些函數將System.String轉換爲char *或任何其他類型的非託管字符串。

基本上,您使用C++創建一個.NET類,並從程序集中公開它,並在內部向該程序集調用您的本機C++代碼。另一種方法是使用P/Invoke爲C++代碼添加純C函數,然後從C#中調用C代碼,並讓C函數調用C++代碼。這會起作用,但我傾向於嘗試儘可能地使用託管代碼。

2

將字符串從C++傳遞迴C#的最大問題是內存分配。 GC應該能夠知道如何清理爲該字符串分配的內存。由於C#具有廣泛的COm互操作支持,因此它確實知道COM BSTR以及如何分配和取消分配這些COM BSTR。因此,最簡單的方法是在C++端使用BSTR,在C#端使用string

請注意,使用BSTR並不意味着您的函數必須通過COM公開。

2

「string」返回值是問題。 P/Invoke編組器將在您返回的指針上調用CoTaskMemFree()。除非您在C/C++代碼中使用CoTaskMemAlloc()來分配字符串緩衝區,否則這樣做不會奏效。這是一件很不尋常的事情。

最好的解決方案是讓您的代碼的調用者傳遞一個指向緩衝區的指針,並將緩衝區的長度作爲參數傳遞給您。這樣所有的內存分配都發生在一邊。斯科特告訴你如何做到這一點。

1

我不得不一個unicode C#字符串轉換爲多字節表示以轉化爲char *在C + +(這是局部單程溶液)

我發現下面的非常有用

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

Marshal.FreeHGlobal(stPtr); 

這可能是低效的,而不是以C#的方式,我是C#的新手,希望這有助於