我正在尝试从使用 P/Invoke 返回字符串的 VB.Net 代码调用 C++ 函数,但它只返回单个字符。
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
返回类型或编码有问题吗?
最佳答案
extern "C" __declspec(dllexport) LPSTR Get_GetDescription(HANDLE)
像这样返回一个指针是相当危险的,因为不清楚谁拥有内存,因此谁应该负责释放它。
在您的 VB 代码中创建缓冲区并将其传递到 DLL 中会更安全,在 DLL 中可以存储值。因此我们可以像这样重写 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
我已将 CharSet:=CharSet.Ansi
添加到 DllImport。你的 C++ 代码没有使用 unicode 字符,而 VB 可能会使用,所以最好说明一下,你可能不需要把它放进去,但我喜欢把这些东西明确化。
请注意使用 StringBuilder
而不是 String
因为字符串在 VB 中是不可变的。最后,您需要注意在字符串构建器中为描述分配足够的空间:
Dim buffer As StringBuilder = New StringBuilder(512)
您可以通过在 VB 代码中使用大量数字来完成此操作,就像我刚刚所做的那样。但是,如果您的 C++ 代码复制的字符多于您分配的字符,这将导致问题。
其他更好的选择是将缓冲区大小传递给 C++ 代码,以便它知道允许写入多少,或者在 C++ 代码中使用获取大小函数来确定有多少应为缓冲区分配空间。
关于c++ - 从 C++ 函数返回字符串到 VB .Net,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8049478/