我正在使用Pinvoke实现Native(C ++)代码和Managed(C#)代码之间的互操作性。我想要实现的是从本机代码中获取一些文本到我的托管代码中。为此,我尝试了很多东西,例如通过ref传递string / stringbuilder,使用[IN]和[OUT],Marshaling到LPSTR,从函数返回字符串等但在我的情况下没有任何作用。任何小代码的帮助都将受到高度赞赏。
答案 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;
}
这是最困难的一个,现在很容易添加其他包装器以从BSTR
,LPWSTR
,std::string
等转换为std::wstring
。
答案 1 :(得分:4)
答案 2 :(得分:4)
以下是通过C#执行此操作的示例。我通过pInvoking通过C#调用Native函数GetWindowText
。 GetWindowText
会返回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_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)修改此示例应该是微不足道的。
答案 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;
};