从C++函数返回字符串到VB .Net

3
我会尽力帮助您进行翻译。以下是需要翻译的内容:

我正在尝试从VB.Net代码中调用返回字符串的C++函数,使用P/Invoke,但它只返回单个字符。

C函数声明

  extern "C" __declspec(dllexport) LPSTR Get_GetDescription(HANDLE)

C函数定义
  LPSTR Get_GetDescription(HANDLE resultBreakDown){
    return LPSTR(((CalcBreakDown*)resultBreakDown)->GetDescription().c_str()); 
}

VB.Net 代码
    <DllImport("FeeEngineDll.dll", CallingConvention:=CallingConvention.Cdecl)> _
    Public Shared Function Get_GetDescription(ByVal resultBreakDown As IntPtr, ByVal indexSubs As Integer, ByVal indexLine As Integer) As <MarshalAsAttribute(LPStr)> String
    End Function

返回类型或编组是否存在问题?


1
两者都是。C++代码基本上有问题,它返回一个悬空指针。此外,在Vista和Win7中会发生严重的崩溃,作为函数返回的字符串缓冲区必须使用CoTaskMemAlloc()进行分配。 - Hans Passant
1个回答

4
extern "C" __declspec(dllexport) LPSTR Get_GetDescription(HANDLE)

像这样返回指针是相当危险的,因为不清楚谁拥有内存,因此谁应该负责释放它。

更安全的做法是在您的VB代码中创建缓冲区,并将其传递到DLL中,在那里可以进行memcpy操作。因此,我们可以重写C ++端:

extern "C" __declspec(dllexport) void Get_GetDescription(HANDLE, LPSTR)

void Get_GetDescription(HANDLE resultBreakDown, LPSTR buffer){
    memcpy(buffer,
           ((CalcBreakDown*)resultBreakDown)->GetDescription().c_str(),
           ((CalcBreakDown*)resultBreakDown)->GetDescription().length()+1); 
}

然后将VB代码重写如下:
<DllImport("FeeEngineDll.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)> _
Public Shared Sub Get_GetDescription(ByVal resultBreakDown As IntPtr, <MarshalAs(UnmanagedType.LPStr)> ByVal szFilename As StringBuilder)
End Sub

我已经在DllImport中添加了CharSet:=CharSet.Ansi。您的C++代码没有使用Unicode字符,而VB可能会使用,因此最好指定一下。您可能不需要这样做,但我喜欢让这些事情显式化。
请注意使用StringBuilder而不是String,因为在VB中字符串是不可变的。最后,您需要小心为描述分配足够的空间。
Dim buffer As StringBuilder = New StringBuilder(512)

你可以在VB代码中使用大数字来实现这一点,就像我刚才所做的。然而,如果你的C++代码复制的字符比你分配的更多,这将会导致问题。
其他更好的选择是要么向C++代码传递缓冲区大小,以便它知道允许写入多少,要么在C++代码中有一个获取大小的函数,可以用来确定应该为缓冲区分配多少空间。

非常感谢对这个问题的详细洞察。我也可以编写一个未管理的C++代码来释放内存,例如:void FreeUmanagedString(void* p){delete[]p;},并从我的VB.Net代码中调用此函数。这种方法是更好的还是你在答案中提到的方法更好。 - sachin
我仍然非常支持我概述的方法。正如汉斯在他对问题的评论中指出的,通过在 DLL 中分配内存并在其他地方使用它存在其他潜在陷阱。 - Charles Keepax
我已经使用了您的方法,但是出现了一个问题...缓冲区的额外空间中出现了不均匀的字符...当我显示从非托管代码返回的文本时。 - sachin
抱歉,我之前发布的代码没有复制字符串的NULL终止字节,已经修正。只需要将长度加1即可复制额外的字节。 - Charles Keepax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接