2010-07-06 52 views
1

我有一個生成值的C++ plus方法。我從一個C#應用程序調用這個方法。
C++的方法是這樣的:char *在託管代碼中?

extern "C" REGISTRATION_API char * generate(char dIn[],char dOut[]) 

generate方法返回字符數組(sOut[]=returnvalue; return sOut;

現在我從我的C#應用​​程序調用該方法:

[DllImport("mydll.dll")] 
static extern string generate(string sIn, string sOut); 

如你所見,c#中的返回類型是string。發生的事情是,c#中的返回值不正確,並且已損壞。 (該值是generate方法中是正確的,但每當我把它從C#中提取它,我得到一些錯誤的值。)

那是正確的,我在C#方法有string返回值,而在C++中,它是一個char*? 請分享您的意見,這是緊急的,謝謝。

+1

你'char *'實際上代表一個字符串(ANSI/Unicode?)還是一個字節序列?如果是字符串,請使用StringBuilder,否則請在'static extern'聲明中使用byte []。 – dtb 2010-07-06 15:42:50

回答

1

無法從本地C++應用程序安全地調用此方法。它返回一個指向棧幀數組的指針。對另一個函數的任何調用都會覆蓋該堆棧幀並損壞該數組。

由於在獲取此函數的返回值後沒有函數調用,因此現在可能在C++程序中偶然發生作用。當P/Invoke編組人坐在中間時,這種運氣不再可用。

您將不得不重新設計C++函數。你應該給它的參數允許調用者傳遞它自己的緩衝區來填充結果。例如:

extern "C" void __stdcall generate(const char* input, char* output, int outputSize) 

調用此在C#代碼通過傳遞一個StringBuilder的輸出參數,正確初始化以足夠的容量來接收的字符串。 outputSize參數確保C++函數可以避免寫入緩衝區末尾並破壞垃圾回收堆。不要忽視它。

3

不幸的是,char*可能有些模棱兩可。 (從字面上看,它是一個指向單個字符的指針,在那裏你可能或者可能不能通過像數組那樣解引用指針來獲得其他字符。)通常,它是一個指向單個字節的空終止字符串的指針ANSI編碼(微軟稱之爲LPStr)。默認情況下,P/Invoke編組System.String的實例作爲BStr類型,它類似於Unicode Pascal字符串(開頭的長度)。您需要使用MarshalAs attribute明確指定您希望託管代碼與非託管代碼之間的字符串編組方式。

最有可能的,你要這樣聲明:

[DllImport("mydll.dll")] 
[return: MarshalAs(UnmanagedType.LPStr)] 
static extern string generate(
    [MarshalAs(UnmanagedType.LPStr)] string sIn, 
    [MarshalAs(UnmanagedType.LPStr)] out string sOut); 

然而,你的函數簽名是困惑:這是返回字符串,或者將它設置一個輸出變量?您可能需要調整P/Invoke聲明以適應您的需求。

2

那是正確的,我在C#方法有 字符串返回值,而在C++中它是 一個char *?

這要看真的。

如果C++方法爲字符串分配內存並期望調用者釋放它,那麼你有問題......大問題。你可以嘗試調用Marshal.FreeHGlobal,但是沒有保證C++方法實際上在全局堆中分配內存。大多數API都可以將指針送回其中一種方法來釋放內存。

char *也可能指向不需要釋放的內存。在這種情況下,使用string應該沒問題。

您可以讓C#聲明返回IntPtr然後調用其中一個Marhsal.PtrToStringXXX方法。