Imported C-function works in C# but not in VB6

Viewed 144

I'm working on a problem for a few days now and can't seem to find the answer. I have a third party dll written in C, which i have to use in an VB6 application.

The function inside dll looks something like this:

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

I have an example written in c#. I tried it out and it works just fine. This is what it looks like in 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)

This is later used in the example like this:

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)

Now this works just fine in C#. I get 0 as a result which means in that case that the operation was a success. I want to make this work in VB6. I tried it with some other functions of the dll and they work just like they should. This one gives me a headache. Here is what I tried just like with any other function:

First I imported the function into my code so I could use it later. I did this with a few other functions the same way and it worked fine.

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

Next Step was for me to call the function. I did it this way: (I do get the context from another function earlier so this already has a value. Other functions work fine with that variable)

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)

The value for result is 1. By the documentation I got this means invalid parameter. Now I researched a lot. Read that in VB6 I have to give the first element of the byte array just like I did in my code. Tried it first with the whole array, got the same result. I think it has something to do with the arrays since its the first function that I've taken over to my code that had those. I'm also not really that native with VB6 but I have to do this. Maybe some of you guys know why this doesn't work. Probably just a minor mistake by myself.

3 Answers

In VB6 it's as simple as this

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

Just don't use Integer at all (only Longs) use ByVal ... As String for the runtime to do the Unicode->ANSI conversion and be done with it.

The original API is weirdly declared with unsigned char * pointer and separate int length parameter probably because the seed, name and pin strings can contain \0 embedded while description is plain zero-terminated string which explains the PITA w/ the C/C++ prototype.

VB6 strings are length-prefixed (and zero-terminated) from get go and can contain Chr$(0) by design so there is no need to take any additional measures like in C# sample w/ explicit byte[] arrays conversion and function prototype signature fiddling. Just use ByVal ... As String in the API declare.

When calling a declared API, VB6 will convert a String to an ANSI byte array automatically, so you can declare seed, name, and pin as ByVal Strings.

Description is a bit confusion to me though, since I don't see a length being passed for that one. Is this really a null-terminated string (pszDescription)? How does the API you are calling know how long this character buffer is?

You can try to change the function declaration in VB, making seed, name, and pin ByRef As Byte(),

note the parentheses:

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

Then, when calling the function, pass the entire arrays, not the first elements:

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