2011-03-15 69 views
25

我正在使用Pinvoke進行原生(C++)代碼和託管(C#)代碼之間的互操作性。我想實現的是從本機代碼中獲取一些文本到我的託管代碼中。爲此,我嘗試了很多東西,例如使用[IN]和[OUT]傳遞string/stringbuilder,封裝LPSTR,從函數等返回字符串,但在我的情況下沒有任何效果。任何幫助一些小代碼將不勝感激。如何從原生(C++)代碼返回文本

回答

42

,因爲這意味着你不必兩次調用本機每串,一旦獲得的長度,然後一旦獲得的內容我會用BSTR做到這一點。

隨着BSTR編組人員將負責釋放BSTR與正確的內存管理器,以便您可以安全地將它從您的C++代碼中傳出。

C++

#include <comutil.h> 
BSTR GetSomeText() 
{ 
    return ::SysAllocString(L"Greetings from the native world!"); 
} 

C#

[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)] 
[return: MarshalAs(UnmanagedType.BStr)] 
private static extern string GetSomeText(); 

還有就是BSTR之一小缺點,即它帶有UTF-16有效負載,但源數據可能是char*

爲了克服這一點,你可以從char*轉換包到BSTR這樣的:

BSTR ANSItoBSTR(const char* input) 
{ 
    BSTR result = NULL; 
    int lenA = lstrlenA(input); 
    int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0); 
    if (lenW > 0) 
    { 
     result = ::SysAllocStringLen(0, lenW); 
     ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW); 
    } 
    return result; 
} 

這是最難的一個出路,現在可以很容易地添加其他包裝從LPWSTR轉換爲BSTRstd::stringstd::wstring

+0

@rturrado非常感謝您在編輯中的更正。 – 2011-06-16 19:29:37

+0

答案是非常有用的,所以它值得。順便說一句,只是意識到'可能char *'缺少'be'。 – rturrado 2011-06-16 22:50:56

+0

您可以將'-1'作爲第四個參數傳遞給'MultiByteToWideChar'以使函數自動計算字符串的長度;避免需要自己調用'lstrlenA'。 – 2011-08-09 11:42:46

3

Here是討論過字符串編組的一個主題。

它的需要,以紀念參數與屬性

[MarshalAs(UnmanagedType.LPSTR)] 
4

這裏是通過C#這樣的一個例子。我通過pInvoking通過C#調用Native函數GetWindowTextGetWindowText返回窗口的標題,該窗口的handle傳遞給它。

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); 
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    static extern int GetWindowTextLength(IntPtr hWnd); 

    public static string GetText(IntPtr hWnd) 
    { 
     // Allocate correct string length first 
     int length = GetWindowTextLength(hWnd); 
     StringBuilder sb = new StringBuilder(length + 1); 
     GetWindowText(hWnd, sb, sb.Capacity); 
     return sb.ToString(); 
    }   

    private void button1_Click(object sender, EventArgs e) 
    { 
     string str = GetText(this.Handle); 
    } 
0

如果你要返回一個char *,並且不想修改C/C++代碼來爲你的返回值分配內存(或者你不能修改那個代碼),那麼你可以改變你的C#extern函數原型返回IntPtr,並進行自己的編組。

舉例來說,這裏是我寫的Pocketsphinx互操作的一個片段:

[DllImport("sphinxbase.dll", CallingConvention = CallingConvention.Cdecl)] 
private static extern IntPtr /* char const * */ jsgf_grammar_name(IntPtr /* jsgf_t * */ jsgf); 

而這裏的get訪問爲C#JsgfGrammar類。 m_pU是一個IntPtr,它指向原始對象jsgf_t

public string Name 
{ 
    get 
    { 
     IntPtr pU = jsgf_grammar_name(m_pU); 
     if (pU == IntPtr.Zero) 
      strName = null; 
     else 
      strName = Marshal.PtrToStringAnsi(pU); 
     return strName; 
    } 
} 

修改這個例子的其他字符串格式(例如Unicode)應該是微不足道的。

相關問題