Вызов функции из C DLL из VB: нарушение прав доступа - PullRequest
1 голос
/ 01 марта 2012

Я пытаюсь вызвать функцию Dll, которая выглядит следующим образом (в C / C ++ Dll):

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

Функция VB 'wrapper' выглядит следующим образом:

<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: строка, которую мне нужно вернуть из функции, результат отправленной команды; адрес сервера: строка, только для ввода (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. Итак, я получаю, например, «2.6.30.8-x86» в returnString, когда я использую команду «uname -r» в commandLine. Но код зависает из-за ошибки памяти.

Теперь я не слишком знаком с VB и P / Invoke, и мне пришлось сделать несколько проб и ошибок, чтобы передать аргументы в DLL (которую я также пишу и отлаживаю). Так же я и использовал атрибуты MarshalAs (UnmanagedType.LPStr). Однако теперь я не знаю, почему я получаю эти ошибки памяти.

Я предпринял несколько других попыток, используя аргументы IntPtr, но также не смог заставить это работать и отказался от этого подхода, так как, по моему мнению, маршалинг должен обрабатываться автоматически (это правильно?).

Любая помощь очень ценится.

1 Ответ

1 голос
/ 01 марта 2012

Возвращаемое значение 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 по значению.

...