我正在使用PInvoke实现本机代码(C ++)和托管代码(C#)之间的互操作性。 我只写了一个简单的函数,它从C ++代码中获取一个字符串。我的代码看起来像
C#代码:
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects() {
string s = GetSomeText();
return s;
}
C ++代码
char* GetSomeText() {
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
所有在C ++端都可以正常工作,即变量pchr
包含“Some Text Here”,但在C#中,字符串s
包含注释。我不知道我做错了什么。任何帮助将不胜感激
答案 0 :(得分:15)
首先,正如其他人所指出的,即使在尝试互操作之前,您的C ++也已被破坏。您正在返回指向stri
缓冲区的指针。但是因为函数返回后stri
被销毁,所以返回值无效。
更重要的是,即使你修复了这个问题,你也需要做更多。它将无法在C ++代码中分配内存,而这需要C#代码来解除分配。
有几个选项可以做到。
您的C#代码可以询问C ++代码字符串的长度。然后创建一个C#StringBuilder并将其分配给适当的大小。接下来,StringBuilder对象将传递给C ++代码,其默认编组将作为LPWSTR。在这种方法中,C#代码分配字符串,您的C ++代码接收一个C字符串,它必须复制缓冲区。
或者,您可以从C ++返回BSTR,它允许在本机C ++代码中进行分配并在C#代码中取消分配。
BSTR方法可能就是我的方法。它看起来像这样:
<强> 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();
<强>更新强>
Hans Passant在评论中补充了几个有用的观察结果。首先,大多数P / Invoke互操作是针对现有的无法更改的界面完成的,并且您无法选择首选的互操作接口方法。看来情况并非如此,应选择哪种方法?
选项1是在首次询问本机代码需要多少空间之后,在托管代码中分配缓冲区。也许使用双方都同意的固定大小的缓冲区就足够了。
当选项1落下时,组装字符串是昂贵的,你不想做两次(例如一次返回它的长度,再一次为内容)。这是选项2,BSTR
发挥作用的地方。
汉斯指出了BSTR
的一个缺点,即它带有UTF-16有效载荷,但你的源数据可能char*
,这是一个“麻烦”。
为了克服麻烦,您可以将char*
转换为BSTR
,如下所示:
BSTR ANSItoBSTR(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::wrstring
。
答案 1 :(得分:0)
这是因为
std::string stri= "Some Text Here";
是一个堆栈对象,它在GetSomeText()调用后被销毁。在您返回的调用pchr指针无效之后。
您可能需要动态地为文本分配空间,以便后者能够访问它 将您的C ++函数定义更改为:
char* GetSomeText()
{
std::string stri = "Some Text Here";
return strcpy(new char[stri.size()], stri.c_str());
}
像上面这样的东西。你明白了......
答案 2 :(得分:-1)
从C ++获取字符串的另一种方法。如果您无法修改您的c ++ DLL。您可以使用IntPtr而不是字符串声明DllImport。调用该函数时,可以将Ptr编组回String。
[DllImport("MyDll.dll")]
private static extern IntPtr GetSomeText();
public static string GetAllValidProjects()
{
string s = Marshal.PtrToStringAnsi(GetSomeText());
return s;
}
注意:如前一篇文章所述。 &#34;一些文字在这里&#34;在堆栈上分配,因此只要函数返回,堆栈就会解除连接。因此,数据可能被覆盖。因此,您应在调用后立即使用Marshal.PtrToStringAnsi。不要坚持使用IntPtr。
char* GetSomeText()
{
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
答案 3 :(得分:-3)
GetSomeText()返回char的指针,如果你声明字符串变量来存储它就不会工作。
试
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects()
{
string s = new string(GetSomeText());
return s;
}
有一个带 char *
的构造函数