如何从Native(C ++)代码返回文本

时间:2011-03-15 07:16:13

标签: c# c++ string pinvoke

我正在使用Pinvoke实现Native(C ++)代码和Managed(C#)代码之间的互操作性。我想要实现的是从本机代码中获取一些文本到我的托管代码中。为此,我尝试了很多东西,例如通过ref传递string / stringbuilder,使用[IN]和[OUT],Marshaling到LPSTR,从函数返回字符串等但在我的情况下没有任何作用。任何小代码的帮助都将受到高度赞赏。

5 个答案:

答案 0 :(得分:45)

我用BSTR来做,因为这意味着你不必每个字符串调用两次,一次获取长度,然后一次获取内容。

使用BSTR marshaller将负责将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;
}

这是最困难的一个,现在很容易添加其他包装器以从BSTRLPWSTRstd::string等转换为std::wstring

答案 1 :(得分:4)

Here是一个主题,已经讨论了字符串编组。

需要使用属性

标记参数
[MarshalAs(UnmanagedType.LPSTR)]

答案 2 :(得分: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);
    }

答案 3 :(得分: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);

这里是C#JsgfGrammar类的get-accessor。 m_pUIntPtr,指向原始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)修改此示例应该是微不足道的。

答案 4 :(得分:-1)

为什么不构建自己的文本结构:

struct mystr { mystr(const char *_) : _text((_ == 0) 
                                           ? 0 
                                           : ::strdup(_)
                                          ) 
                                      {}
              ~mystr() { if(this->_text != 0) delete [] this->_text; }
               operator char * & () { return this->_text; }

              private : char *_text;
             };