如何从Native(C ++)代码返回文本
我正在使用Pinvoke在本机(C ++)代码和托pipe(C#)代码之间进行互操作。 我想实现的是从本地代码中获取一些文本到托pipe代码中。 为此,我尝试了很多东西,例如,使用[IN]和[OUT]传递string / stringbuilder,使用LPSTR封装,从函数等返回string,但在我的情况下没有任何作用。 任何帮助一些小代码将不胜感激。
我会用BSTR
来做,因为这意味着你不必为每个string调用两次本地variables,一次获得长度,然后一次获得内容。
使用BSTR
,编组人员将负责使用正确的内存pipe理器来释放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
, std::string
, std::wstring
等转换为BSTR
。
以下是通过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); }
这里有一个话题是string编组已经讨论过了。
需要用属性标记参数
[MarshalAs(UnmanagedType.LPSTR)]
如果你返回一个char *
,并且不想修改C / C ++代码来为你的返回值分配内存(或者你不能修改那个代码),那么你可以改变你的C#的extern函数,原型来返回一个IntPtr
,并做自己的编组。
例如,下面是我为Pocketsphinx写的interop的一个片段:
[DllImport("sphinxbase.dll", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr /* char const * */ jsgf_grammar_name(IntPtr /* jsgf_t * */ jsgf);
这里是C# JsgfGrammar
类的获取访问器。 m_pU
是一个指向原始jsgf_t
对象的IntPtr
。
public string Name { get { IntPtr pU = jsgf_grammar_name(m_pU); if (pU == IntPtr.Zero) strName = null; else strName = Marshal.PtrToStringAnsi(pU); return strName; } }
为其他string格式(例如Unicode)修改这个例子应该是微不足道的。