从VB调用C DLL中的函数:访问冲突

时间:2012-03-01 13:01:47

标签: vb.net pinvoke dllimport access-violation

我正在尝试调用一个看起来像这样的Dll函数(在C / C ++ Dll中):

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

VB'包装'功能如下:

<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变量:

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参数做了一些其他的尝试,但我也无法让这个工作并放弃了这种方法,因为我理解编组应该自动处理(这是正确的吗?)。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

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

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

现在,我没有VB p / invoke的经验,所以我希望你不介意我给你一个C#版本。我希望你能够轻松翻译。

[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)

你可以这样调用这个函数:

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

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

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