Импортированная C -функция работает в C#, но не в VB6 - PullRequest
3 голосов
/ 23 марта 2020

Я работаю над проблемой уже несколько дней и не могу найти ответ. У меня есть сторонняя dll, написанная на C, которую я должен использовать в приложении VB6.

Функция внутри dll выглядит примерно так:

someFunction(WContext* context, const unsigned char* seed, int seedLength, const unsigned char* name, int nameLength, 
             const unsigned char* pin, int pinLength, const char* description)

У меня есть пример написано в c#. Я попробовал это, и это работает просто отлично. Вот как это выглядит в C#

[DllImport("Filename.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern int someFunction(IntPtr context, byte[] seed, int seedLength, byte[] name, int nameLength, 
                                       byte[] pin, int pinLength, string description)

Это позже будет использовано в следующем примере:

byte[] seed = Encoding.ASCII.GetBytes("seed")
byte[] name = Encoding.ASCII.GetBytes("name")
byte[] pin = Encoding.ASCII.GetBytes("1234")
string description = "description"

int result = someFunction(context, seed, seed.length, name, name.Length, pin, pin.Length, description)

Теперь это прекрасно работает в C#. В результате я получаю 0, что означает, что в этом случае операция прошла успешно. Я хочу сделать эту работу в VB6. Я попробовал это с некоторыми другими функциями DLL, и они работают так же, как они должны. Этот вызывает у меня головную боль. Вот то, что я попробовал так же, как и с любой другой функцией:

Сначала я импортировал функцию в свой код, чтобы потом использовать ее. Я сделал это с несколькими другими функциями таким же образом, и он работал нормально.

Private Declare Function someFunction Lib "C:\DllPath\Filename.dll" (ByVal context As Long, ByRef seed as Byte, _
                                                                     ByVal seedLength As Integer, ByRef name As Byte, _
                                                                     ByVal nameLength As Integer, ByRef pin As Byte, _
                                                                     ByVal pinLength As Integer, ByVal description As String) As Integer

Следующим шагом для меня было вызвать функцию. Я сделал это следующим образом: (Я получаю контекст из другой функции ранее, так что это уже имеет значение. Другие функции отлично работают с этой переменной)

Dim seed() As Byte
Dim seedLength As Integer
Dim name() As Byte
Dim nameLength As Integer
Dim pin() As Byte
Dim pin As Integer
Dim description As String
Dim result as Integer

seed = StrConv("seed", vbFromUnicode)
seedLength = UBound(seed) + 1
name = StrConv("name", vbFromUnicode)
nameLength = UBound(name) + 1
pin = StrConv("1234", vbFromUnicode)
pinLength = UBound(pin) + 1
description = "description"

result = someFunction(context, seed(0), seedLength, name(0), nameLength, pin(0), pinLength, description)

Значение для результата равно 1. По документации Я получил это означает недопустимый параметр. Сейчас я много исследовал. Прочитайте, что в VB6 я должен дать первый элемент массива байтов, как я сделал в своем коде. Пробовал сначала со всем массивом, получил тот же результат. Я думаю, что это как-то связано с массивами, так как это первая функция, которую я перенес в свой код, в котором они есть. Я также не очень хорошо знаю VB6, но я должен это сделать. Может быть, некоторые из вас, ребята, знают, почему это не работает. Возможно, я просто незначительная ошибка.

Ответы [ 3 ]

2 голосов
/ 29 марта 2020

В VB6 это так просто, как это

Option Explicit

Private Declare Function someFunction Lib "C:\DllPath\Filename.dll" (ByVal context As Long, ByVal seed As String, _
                                                                     ByVal seedLength As Long, ByVal name As String, _
                                                                     ByVal nameLength As Long, ByVal pin As String, _
                                                                     ByVal pinLength As Long, ByVal description As String) As Long

Private Sub Form_Load()
    Dim context     As Long
    Dim seed        As String
    Dim name        As String
    Dim pin         As String
    Dim description As String
    Dim result      As Long

    seed = "seed"
    name = "name"
    pin = "1234"
    description = "description"
    result = someFunction(context, seed, Len(seed), name, Len(name), pin, Len(pin), description)
End Sub

Просто не используйте Integer вообще (только Long с), используйте ByVal ... As String для времени выполнения, чтобы сделать Unicode-> ANSI преобразование и все, что нужно сделать.

Оригинальный API странным образом объявлен с указателем unsigned char * и отдельным параметром длины int, вероятно, потому что строки seed, name и pin могут содержать \0 встроенный, в то время как description - простая строка с нулевым символом в конце, которая объясняет PITA с прототипом C / C ++.

Строки VB6 имеют префикс длины ( и нулевой символ) с get go и могут содержать Chr$(0), поэтому нет необходимости предпринимать какие-либо дополнительные меры, такие как C# sample w / явное byte[] преобразование массивов и изменение подписи прототипа функции. Просто используйте ByVal ... As String в объявлении API.

0 голосов
/ 23 марта 2020

При вызове объявленного API VB6 автоматически преобразует String в байтовый массив ANSI, поэтому вы можете объявить seed, name и pin как строки ByVal.

Описание немного сбивает меня с толку, так как я не вижу длины, передаваемой для этого. Это действительно строка с нулевым символом в конце (pszDescription)? Как API, который вы вызываете, знает, как долго этот символьный буфер?

0 голосов
/ 23 марта 2020

Вы можете попробовать изменить объявление функции в VB, сделав seed, name и pin ByRef As Byte (),

обратите внимание на круглые скобки:

Private Declare Function someFunction Lib "C:\DllPath\Filename.dll" (ByVal context As Long, ByRef seed as Byte(), _
                                                                 ByVal seedLength As Integer, ByRef name As Byte(), _
                                                                 ByVal nameLength As Integer, ByRef pin As Byte(), _
                                                                 ByVal pinLength As Integer, ByVal description As String) As Integer

Затем при вызове функция, передающая целые массивы, а не первые элементы:

result = someFunction(context, seed, seedLength, name, nameLength, pin, pinLength, description)
...