2011-05-27 115 views
5

我已經閱讀了其他類似的問題,但他們沒有解決這個問題。我有一個帶touppercase函數的舊C庫(作爲例子)。這需要一個char *並返回一個char *。但是,返回的指針是一個指向同一個字符串的指針(不要問我,我沒有寫它)。使用char *參數調用C DLL函數使用P/Invoke

功能如下:

__declspec(dllexport) 
char * __cdecl touppercase(char *ps_source) 
{ 
    char *ps_buffer = NULL; 

    assert (ps_source != NULL); 

    ps_buffer = ps_source; 
    while (*ps_buffer != '\0') 
    { 
     *ps_buffer = toupper(*ps_buffer); 
     ps_buffer++; 
    } 
*ps_buffer = '\0'; 
    return (ps_source); 
} 

的C#代碼來聲明這個樣子:

[DllImport("mydll.dll", EntryPoint = "touppercase", 
       CharSet = CharSet.Ansi, ExactSpelling = true, 
       CallingConvention = CallingConvention.Cdecl)] 
    private static extern System.IntPtr touppercase(string postData); 

在我的應用程序這個調用看起來像

 string sTest2 = Marshal.PtrToStringAnsi(to_uppercase(sTest)); 

然而sTest2最終只是一個隨機字符串。

我添加了一個測試函數給具有相同參數的同一個dll,但是這個在本地分配了內存並且複製了字符串。這工作正常。爲什麼現在的原始版本有效?

注意:更新dll庫本身不是一個選項。

+0

調用此函數後sTest會發生什麼?它會改變嗎? – 2011-05-27 14:19:01

+0

你爲什麼要宣佈調用約定。默認情況下C#使用調用約定而不是標準約定(C/C++)。你已經改變了C庫中的約定。如果C代碼沒有聲明爲默認值之外的其他值,則只需更改約定。 – 2011-05-27 14:41:29

+1

@ramhound:明確指定導出函數的調用約定是最安全的。您不希望導出受'/ Gr'或'/ Gz'的影響。 – 2011-05-27 16:02:12

回答

10

的功能修改參考,所以你應該使用StringBuilder:

[DllImport("dll.dll", EntryPoint="touppercase", 
CharSet = CharSet.Ansi, ExactSpelling = true, 
CallingConvention = CallingConvention.Cdecl)] 
private static extern System.IntPtr touppercase(StringBuilder postData); 

說它是這樣的:

StringBuilder sTest = new StringBuilder("abs"); 
    touppercase(sTest); 
    string result = sTest.ToString(); 

對於返回指針的解釋 - >本福格特

+0

工作奇蹟。非常感謝。 – Jonnster 2011-05-27 15:12:53

3

正如你所看到的,返回類型是一個指向相同字符串的指針。提供給該函數的字符串是.NET用於Unicode-> ANSI轉換的臨時緩衝區,在P/Invoke返回之前解除分配。所以它是一個你必須忽略的野指針(最好只使用C#void返回類型)。

我也建議使用StringBuilder作爲參數而不是String,這讓.NET知道函數會改變傳遞的參數。 StringBuilder的內容將被該函數修改,這就是您如何得到沒有返回值的結果。

+0

非常感謝。 – Jonnster 2011-05-27 15:13:23