Como retornar o texto do código nativo (C ++)

Eu estou usando Pinvoke para interoperabilidade entre código nativo (C ++) e código gerenciado (c #). O que eu quero alcançar é obter algum texto do código nativo em meu código gerenciado. Para isso eu tento muitas coisas, por exemplo, passando string / stringbuilder por ref, usando [IN] e [OUT], Marshaling para LPSTR, retornando string da function etc. mas nada funciona no meu caso. Qualquer ajuda com algum pequeno código seria muito apreciada.

Eu faria isso com um BSTR pois isso significa que você não precisa chamar nativo duas vezes por string, uma vez para obter o tamanho e depois uma vez para obter o conteúdo.

Com um BSTR o marshaller cuidará de desalocar o BSTR com o gerenciador de memory correto, para que você possa passá-lo com segurança de seu código C ++.

C ++

 #include  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(); 

Há uma pequena desvantagem do BSTR , ou seja, ele carrega uma carga UTF-16, mas seus dados de origem podem ser char* .

Para superar isso, você pode finalizar a conversão de char* para BSTR assim:

 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; } 

Esse é o mais difícil de todos, e agora é fácil adicionar outros wrappers para converter para BSTR de LPWSTR , std::string , std::wstring etc.

Aqui está um exemplo de fazer isso através de c #. Eu estou chamando function nativa GetWindowText através de c # por pInvoking. GetWindowText retorna a legenda da janela cujo handle é passado para ele.

  [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); } 

Aqui está um tópico onde o marshaling de strings foi discutido.

É preciso marcar o parâmetro com atributo

 [MarshalAs(UnmanagedType.LPSTR)] 

Se você está retornando um char * , e não quer ter que modificar o código C / C ++ para alocar memory para o seu valor de retorno (ou você não pode modificar esse código), então você pode mudar sua function externa C #. protótipo para retornar um IntPtr e fazer o empacotamento.

Por exemplo, aqui está um trecho da interoperabilidade que escrevi para o Pocketsphinx :

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

E aqui está o get-accessr para a class C # JsgfGrammar . m_pU é um IntPtr que aponta para o object jsgf_t bruto.

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

Modificar este exemplo para outros formatos de string (por exemplo, Unicode) deve ser trivial.