关于 vb.net:从 VB 调用 C DLL 中的函数:访问冲突

Call a function in a C DLL from VB: Access Violation

我正在尝试调用如下所示的 Dll 函数(在 C/C Dll 中):

1
__declspec(dllexport) void execute(char** ReturnMsg, char* serverAddress, char* commandLine)

VB\\'wrapper\\' 函数如下所示:

1
2
3
4
5
<DllImport("TPClient.dll", EntryPoint:="execute", CallingConvention:=CallingConvention.Cdecl, CharSet:=CharSet.Auto, ExactSpelling:=True)> _
Public Shared Sub tg_execute(<Out()> <MarshalAs(UnmanagedType.LPStr)> ByRef returnString As System.Text.StringBuilder, _
                             <MarshalAs(UnmanagedType.LPStr)> ByVal serverAddress As String, _
                             <MarshalAs(UnmanagedType.LPStr)> ByVal commandLine As String)
End Sub

参数为:
returnString:我需要从函数中取回的字符串,发送命令的结果;
serverAddress:字符串,仅输入(IP 或 DNS 名称);和
commandLine:字符串,仅输入(任何命令)

为了调用该函数,我创建了一个具有足够容量的 StringBuilder 对象以用作 returnString 变量:

1
2
3
Dim returnString As New System.Text.StringBuilder(128)

tg_execute(returnString, TextBox_serverName.Text.Trim, TextBox_Command.Text.Trim)

当我运行代码时,我确实在 returnString 中得到了预期的字符串(正如我在调试器中看到的那样),但是我也得到了一个 AccessViolationException。因此,当我在 commandLine 中使用命令 "uname -r" 时,我会在 returnString 中得到例如 "2.6.30.8-x86"。但是由于内存错误,代码挂起。

现在我对 VB 和 P/Invoke 不太熟悉,我不得不反复试验才能将参数传递给 DLL(我也在编写和调试)。这也是我最终使用"MarshalAs(UnmanagedType.LPStr)"属性的方式。但是现在我不知道为什么会出现这些内存错误。

我使用 IntPtr 参数进行了一些其他尝试,但我也无法使其正常工作并放弃了这种方法,因为我的理解应该自动处理编组(正确吗?)。

非常感谢任何帮助。


返回值 char** ReturnMsg 表明 ReturnMsg 是指向 C 字符串的指针。这意味着本机代码负责分配缓冲区。所以 StringBuilder 在这里不合适。

这里实际上没有足够的信息来知道如何调用这个函数。缺少的是知道哪一方负责解除分配字符串。它可能是任何一方,我将假设 C 代码会这样做,可能是通过静态分配的字符串,例如常量。

现在,我没有使用 VB p/invoke 的经验,所以我希望你不介意我给你一个 C# 版本。我希望你可以很容易地翻译。

1
2
3
4
[DllImport("TPClient.dll", CallingConvention=CallingConvention.Cdecl,
    CharSet=CharSet.Ansi, EntryPoint="execute", ExactSpelling=true)]
private static void tg_execute(out IntPtr returnString,
    string serverAddress, string commandLine)

你这样调用函数:

1
2
3
IntPtr returnStringPtr;
tg_execute(out returnStringPtr, serverAddress, commandLine);
string returnString = Marshal.PtrToStringAnsi(returnStringPtr);

请注意,您的字符集在问题中不正确。您必须使用 Ansi,因为本机代码使用 char。我还认为您的 MarshalAs 属性是虚假的,因为您只是在重新说明这些参数类型的默认编组。

现在,如果本机代码希望调用者释放内存,那么本机代码必须导出一个函数来执行此操作。如果是这种情况,那么您可以将其称为按值传递 returnStringPtr