我试图从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
返回类型或编组有问题吗?
答案 0 :(得分:4)
extern "C" __declspec(dllexport) LPSTR Get_GetDescription(HANDLE)
返回这样的指针是相当危险的,因为不清楚谁拥有记忆,因此谁应该负责释放它。
在VB代码中创建缓冲区并将其传递到可以memcpy的值的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 ++代码中有一个get size函数,可用于确定多少应该为缓冲区分配空间。