Declaring DLL Routines in Visual Basic

Most DLL routines are written in C. Developers using Visual Basic have to translate the syntax of a typical API routine into a Visual Basic DECLARE statement. Doing this requires some knowledge of how arguments are passed in both C and Visual Basic. The following information is helpful in determining how to set up DECLARE statements in the Global Module of a Visual Basic application.

In C, numeric arguments are usually passed by Value; a copy of the value of the argument is passed to the routine. Sometimes C arguments are pointers, and these arguments are said to be passed by reference. Passing an argument by reference allows the called routine to modify the argument and return it to the calling routine. C strings (null terminated strings) and arrays are always passed by reference.

By default, Visual Basic passes all of its arguments by reference. In effect, when arguments are passed to a Visual Basic procedure, they are actually passed as "far" (32 bit) pointers to those values. To pass arguments to a C routine expecting arguments to be passed by value, you have to use the ByVal keyword with the argument in the Declaration statement.

To complicate matters, Visual Basic strings do not use the same format as C strings. Visual Basic uses the ByVal keyword to mean, "pass a C string by reference," when it is used with a string argument in a Declare statement. The table in Figure 10 describes how selected arguments in an API call should be passed.

FIGURE 10: Visual Basic arguments and appropriate declarations

Argument                                             Declaration
A standard C string (LPSTR, char far *)              ByVal S$
A Visual Basic String (see note)                     S$
An integer (WORD, HANDLE, int)                       ByVal I%
A pointer to an integer (LPINT, int far *)           I%
A long (DWORD, unsigned long)                        ByVal L&
A pointer to a long (LPDWORD,LPLONG,DWORD far *)     L&
A standard C array (A[])                             Base type of 
A Visual Basic array                                 A()
A structure (user-defined TYPE)                      S As Struct
End of Figure 10

Never pass a Visual Basic string or array to a DLL routine unless the DLL was written specifically for use with Visual Basic. Visual Basic strings and arrays are represented in memory by descriptors, not pointers. These descriptors are useless to DLL routines that were not written with Visual Basic in mind.

Visual Basic also allows arguments to be passed AS ANY. The AS ANY declaration tells Visual Basic not to do any type checking for that argument. The developer can pass the function any parameter, as long as it is what the function is expecting. If not, the application can terminate with a UAE (Unrecoverable Application Error). If an argument is declared AS ANY, the call must specify how the argument will be passed to the calling function. To do this, use the ByVal keyword for strings and for arguments that should be passed by value, and omit ByVal keyword for arguments that should be passed by reference.

For example, using Btrieve, if the key buffer parameter must be passed differently depending on the operation that is to be performed, you would declare the Key Buffer parameter AS ANY in the Global Module and set up the Btrieve call as shown in Figure 11.

FIGURE 11: Visual Basic Key Buffer declaration and Btrieve call

'Global Module
Declare Function btrcall Lib "wbtrcall.dll" (ByVal Op%,
                                             ByVal Pb$,
                                             Db As Any,
                                             DL As Integer,
                                             Kb As Any,
                                             ByVal Kl%,
                                             ByVal Kn%) As Integer

'Open File
 FileName$ = "f:\data\file.btr "
 KLen% = Len(FileName$)

Status% = btrcall(OPEN%, PosBlk$, DataBuf, DL%, ByVal FileName$, KLen%, 0)

End of Figure 11

Notice that the key buffer parameter (FileName$) is specified to be passed by Value. Later in the code, if you wanted to pass a user-defined type for the key buffer on a GET EQUAL operation, you would pass the structure by name for the key buffer as shown in Figure 12.

FIGURE 12: Passing structure by name

'Global Module
Type DateStruct
     Day            As String * 1
     Month          As String * 1
     Year           As Integer
End Type
Global Birthdate As DateStruct

'Main Module 'Get Equal GE%=5 DLen% = Len(DataBuf) Birthdate.Day = Chr$(3) Birthdate.Month=Chr$(6) Birthdate.Year=1966 KbLen% = Len(Birthdate) KeyNum% = 1

Status% = btrcall(GE%, PosBlk$, DataBuf, DLen%, Birthdate, KbLen%, KeyNum%)

End of Figure 12

Interfacing GFA-Basic for Windows & WBTRCALL.DLL

To call Btrieve for Windows from a GFA-Basic Windows application, the declaration shown in Figure 3, Part A, must be made in the global section of your code. Since the Position Block, Data Buffer, Data Buffer Length, and Key Buffer parameters need to be FAR pointers, the "l" argument means LONG for the four-byte FAR address. The "w" argument stands for WORD, or two-byte addresses.

FIGURE 3: Declaration and Btrieve call for GFA-Basic for Windows 

Part A: Declaration DLL #10,"C:\WINDOWS\SYSTEM\WBTRCALL.DLL" DecL Word btrcall (w,l,l,l,l,w,w) Pascal EndDLL Part B: Btrieve Call Status& = ^btrcall(opcode&, V:Posblk$, V:DataBuf, V:DataBufLen, V:KeyBuf$, KeyBufLen&, KeyNum&) END of FIGURE 3

The actual Btrieve call to the DLL should look like the call shown in Part B of Figure 3. The "^" character before the btrcall function name tells the GFA compiler that the next variable name is an external function name. The "&" character after the variable name means "integer" in GFA-Basic, just as the "%" sign would in Microsoft Basic. The "V:" designation is equivalent to VARPTR in Microsoft Basic, which stands for "variable pointer."

Accessing Integer Keys in Visual Basic for Windows

In Visual Basic for Windows, there are only two accepted methods for passing the key buffer parameter:

Attempting to access integer keys by passing the key buffer as a string poses the problem of converting an integer to a two-byte string. In other Microsoft Basic compilers, this conversion can be accomplished with the MKI$ function. In Visual Basic for Windows, this function is not available so you must convert the integer in one of two other ways.

One method is to use the user-defined TYPE structure. First, pass an integer key value on a Get Equal operation as shown in the following code segment:

Type Index3Search
        Age    As Integer
End Type

Dim SearchOnKey3 As Index3Search

Then, make a Btrieve call similar to the following:

SearchOnKey3.Age = 38
KeyBufLen = LEN(SearchOnKey3)
KeyNum = 3

status = btrcall(BGETEQUAL, PosBlk$, DataBuf, DataBufLen, SearchOnKey3, KeyBufLen, KeyNum)

Another method of handling integer key types is to write an MKI$ Function equivalent to the one provided with the Microsoft QuickBasic compilers. One example of such a function call is shown in the following code segment.

Function MKI$ (I%)
  TempI& = I%
    If TempI& < 0 Then
      TempI& = TempI& + 65536
    End If
    Byte1% = TempI& / 256
    Byte2% = TempI& Mod 256
    C1$ = Chr$(Byte1%)
    C2$ = Chr$(Byte2%)
    MKI$ = C2$ + C1$
End Function

The parameter setup and Btrieve call you would issue next would look like:

SearchOnKey3$ = MKI$(38)
KeyBufLen = LEN(SearchOnKey3$)
KeyNum = 3

status = btrcall(BGETEQUAL, PosBlk$, DataBuf, DataBufLen, SearchOnKey3$, KeyBufLen, KeyNum)

Copyright © Madis Kaal 2000-