ARTICLE - The beginner's guide to API and DLL calls
by Brad Moore (brad.moore@weyerhaeuser.com)
copyright 2002 - all rights reserved

Home

Paths and File Names
Observed online
Resources for the beginner
Graphics Drawing Rules
beginner's guide to API and DLL
Drawing IN MEMORY
Radiobuttons via API
NumbWord
Working with Comboboxes

Introduction -

Although the title seems to indicate that this is a beginners article, it is really far from it. The beginning programmer is not our target audience, since this person should be primarily concerned with learning the basics of Liberty Basic. Leveraging the internal working of an operating system is for the more seasoned programmer. So this article is intended for the intrepid Liberty Basic programmer who, having grasped a handle of the language, is ready to tread into the murky backwaters of the windows operating system.

Well, it really isn't that bad. Nothing so scary as murky backwaters here, just the fear of the unknown. So, lets get on with the journey...

Before we can begin we need to get a couple terms out of the way. When one speaks of a DLL they are talking about a Dynamic Linked Library. If one were to discuss an API they would be addressing the subject of the Application
Programmer's Interface (into Windows specifically). The terms API and DLL are often used interchangeably, and an API is nothing more than a very complex DLL. I will refer to both as a DLL henceforth.

Getting Down to Business -

Ok, a DLL is a Dynamic Linked Library, but what does that mean? In the simplest of terms - a DLL is no more than a
set of EXTERNAL functions that can be called by a program

If you have been using Liberty Basic for any length of time, then you are no doubt familiar with functions and sub
programs. You can write your own functions using LB - as example of what one may look like followings:

Function Avg3(a, b, c)
'return average of three values
Avg3 = (a + b + c) / 3
end function

That is a simple function. It requires three arguments (those are the variables in between parentheses). LB has
few variable types (actually only two: numeric and string) so we simply understand that these are not strings (since
they don't have a $ suffix) so they are numeric.

Had I been writing this in a more strictly 'typed' language (a language that requires I strictly follow the rules
regarding variable types and uses) I would have probably had to declare the types of variables I was going to expect to
have passed into the function. In such a case the function might look like this (this will not work in LB):

Function Avg3(a as integer, b as integer, c as integer)
'return average of three values
Avg3 = (a + b + c) / 3
end function

Although my program is not written in C code, a function in a strictly typed language like C might look something like
the one above. This is important, because much of Windows is written in C or C++. When you interface with DLLs that make

up the windows API, by calling its functions, then you must speak its language. It expects you to declare the variables
types you are planning to pass.

There are a quite few possible variable types used passing values to external functions that Liberty Basic supports:

Numeric Data (assign any Liberty Basic numeric variable to any of these types)
----------------------------
double (a double float)
ulong (4 bytes) -
long (4 bytes)
short (2 bytes)
ushort, word (2 bytes)

String Data (assign any Liberty Basic string variable to this type)
----------------------------
ptr (4 bytes, long pointer, for passing strings)

Special Purpose
----------------------------
struct (4 bytes, long pointer, for passing structs)
boolean (true/false expression)

As you can see, there are many ways to pass a numeric values to the called routine in a DLL. The called routine will
expect numeric data to be passed to it as a specific data type. You will have to investigate your DLL documentation
to know exactly how to pass the numeric value. Passing it wrong usually causes the program to fail.

Passing Lane Ahead -

Passing variables can get kind of messy when you want to pass strings. This is the fault of C and C++ (as well as other
languages that are similar). They do not use strings the way BASIC does, but rather refer to a string as an array of single characters. Also, due to this limitation, they do not permit a string to be passed as a string as a string, but rather as a pointer to the memory address containing the first character of the string. Usually these strings are
terminated (in memory) by a null character. This can get a little confusing to the Liberty Basic programmer venturing
into the world of DLLs for the first time.


What we need to do to tell a windows function about the string is pass a POINTER to the string. This is the value of
the memory location where the string beings in memory. Don't forget, this string must be null terminated. Let's
just peak into memory and see what a string looks like. This is our null terminated string:

A$ = "Hello" + chr$(0)

Here it is represented in memory (at a fictitious memory address):
H e l l o null
1001 1002 1003 1004 1005 1006

Our String is represented to a windows DLL function by its starting address, in this case the address 1001. If we want
to have the function work with our string we would pass a POINTER to the starting address of our string. This pointer
is simply a numeric value that contains the starting address of our string (that is 1001 in our case). We don't have to
know where our string begins in memory though, because Liberty Basic handles that behind the scenes. We simply
pass A$ string as a type PTR (which means pointer).

We will hit pointers again shortly, but first, I want to make certain that the point that I have alluded to it, is
made clear. A DLL (and the API by definition) is just a set of predefined routines that are made available to
programmers for adding functionality to their applications. The routines are external to the programming language from
which they are called. They are simply functions. They take arguments and generally return a value. There are
many, many types of DLL and API calls. Some create windows, some destroy windows, some handle fonts, some handle the hardware (Com ports, Printer ports) some handle printing, some manage the Windows central message queue, some manipulate graphics and others allow playing of music and video. The list goes on and on.

Open and Closed Case -

There are two requirements in Liberty Basic that are involved in using a DLL's function. You must first open the
DLL for use and then you must call the function by name, passing the correct arguments. We have been discussing the
arguments - what they are and why they are the way the are. We will look even further at some more advanced forms of arguments in a few moments. Now let us look at what we must do to open the DLL for use. The OPEN statement in Liberty Basic is used to open many objects, a DLL is just one of them. Every DLL must be opened before a function can be called with in. Liberty Basic version 3.x takes care of opening (and closing) several common DLL's that are part of the Windows API so that you do not need to do this - the help file calls this "Advanced DLL resolution". These are
DLL's and their associated handles are:

User32.dll - #user32 (the user DLL)
Kernel32.dll - #kernel32 (the OS kernel DLL)
GDI.dll - #gdi32 (Graphics DLL)
Winmm.dll - #winmm (Multimedia DLL)
Shell32.dll - #shell32 (the OS Shell DLL)
Comdlg32.dll - #comdlg32 (Common Dialog DLL)
Comctl32.dll - #comctl32 (Common Controls DLL)

IF you must open a DLL file that is not part of this list then you would use the following command syntax:

OPEN "filename.dll" for dll as #handle

Once it is open you are permitted to call the individual functions that are in the DLL. Call these using the CALLDLL
command. There is an example of this in the section that follows. If you opened a DLL for use in your code you must
also close it before your application terminates. To close a DLL file simply use the CLOSE command and pass it the
handle of the DLL to close. Here is the official syntax of the command:

CLOSE #handle

Return to me -

As you have no doubt learned in your programming experience, Functions (in Liberty Basic) return values. In our example above (the function Avg3) it is understood that the return value will be of a numeric type. Because Liberty Basic does not require us to be specific about the number we can return almost anything. A floating point value (I would expect an average to be a floating point value), an integer, and double precision float, etc.

Going back to our strongly typed languages (such as `C') we are required to specify the type of value that a function
will return. In my pseudo basic language I might write my function something like the one below (note: this will not
run in LB):

Function Avg3(a as integer, b as integer, c as integer)as float
'return average of three values
Avg3 = (a + b + c) / 3
end function

The AS FLOAT at the end of the function specifies that the function will return a floating point value. Liberty Basic
requires that when a DLL is called (using the command CALLDLL) that a value be returned. The return value is
always passed as the last argument in the call.

If the function above were part of a DLL it would be called using the code below (presuming the DLL had already been
opened - we will be examining this later):

Calldll #myLib, "Avg3", _
a as short, _
b as short, _
c as short, _
r as double

So Liberty Basic expects the calling DLL to have a return value. There are no exceptions, unfortunately many DLL's do
not return a value of any kind. In these case we say that the return value is of a type VOID - meaning it is nothing.
In such a case the last parameter passed would be something like `Result as void'. Using this special variable type
(you can only use it for a return value) tells Liberty Basic that the DLL does not pass a return value back. It is
important to know the DLL call and the arguments and return values. You can not specify a return type of VOID simply
because you do not know what it returns. You must know and you must declare it correctly.

A Pointer or Two -

Two different variable types pass pointers. They are PTR (used to pass a string) and STRUCT (used to pass a
structure). It is highly inefficient for a program to pass whole arrays of data to external functions. This would take
up large amounts of memory (in addition to the memory they currently occupy) and would slow down the program. In
response to this problem most languages pass and return a pointer to the array and not the entire array.

The workings of a pointer are both simple and confusing. To understand them we have to understand how memory is managed. In our computer we have a vast bank of small storage compartments. I like to think of these as little mail
boxes, kind of like the ones you would see in older hotels behind the front desk. Row after row of little boxes, some
with notes in them, some with keys in them and some are empty. If we continue the analogy we would notice that each
of these little compartments is numbered consecutively and in our computer they are either empty or contain a single
number. The number represents a variable, either a character or a numeric. (Technical Note: because LB uses floating point values for all numbers - more than one of the little boxes is required to store the number. For the purposes of this discussion we will ignore this fact and treat them as if can neatly fit into a single box.)

An array or a string (remember that a string is simply an array of characters) is said to occupy several consecutive
boxes. The first element (or character) is in the first box and the second in the second and so on.

The example above shows these hotel mailbox (or pidgin hole) style boxes with the string "Hello world" placed in it. It
was placed there with the following line of code:

MyString$ = "Hello world"

The string starts at box number 5 - we call box 5 the memory address of the string. This is a simple representation of
memory. In real life we don't get to choose the memory locations of our variables, and we don't even care what the
memory location they are in. This is all managed for us. What we do care about is being able to pass the location
where an array begins. This is the job of the pointer. Pointers do not have a great deal of use in the core language of Liberty Basic. We would not say that variable `A' is a pointer as we would were we programming in C++. The only usage of this value is as an argument to a DLL call.

MyString$ as PTR.

The PTR tells Liberty Basic that I do not want the value of the string, just the address. That is 5 in this case. The
called DLL will be able to receive this value and go look at that location in memory (the very same location we wrote to
when we placed "Hello world" into memory) and use and even alter the variable. Well, I am sure that is clear as mud.
It is not necessary to fully understand the dynamics of the pointer to use the pointer, but it helps to know to a degree
what is going on.

Getting Structured -

Pointers to strings have been discussed, but there is also another pointer type called a STRUCT. Many languages
support formations such as Structs. They are sometimes call use defined variable types or in other cases structures.
They are simple records of multiple variable types (elements) that are bound together. Some languages let you
define the type and then create multiple variables of that type. For instance the user defined type EMPLOYEE contains
the following elements:

Name
Identification Number
Start Date
End Date

Each of these elements are of a specific data type. The one named Name is a string. Identification Number could be a
long integer. In Liberty Basic we must declare the data types for each element as we define the element name. We
also must give the struct a name. The example above might look like the following if we were to define it in Liberty
Basic code:

Struct Employee,
Name as string, _
Identifacation_Num as integer, _
Start_Date as char[10], _
End_Date as char[10]

The valid data types that can be declared are very similar to those used when passing variables to DLL's as arguments.
One of the interesting items to note is that a struct can contain another struct as shown by the list. Also a unique
variable type is available in this list that can not be used in calling DLL's, and that is the type CHAR. It is used to
define fixed length strings. The example above used CHAR to define the date elements as a 10 character string.

Additional characters can not be stored in this variable. Obviously the length of the fixed length string is specified
by the value between the square brackets. Also note in the list below that you may not use the variable type VOID in a
struct, as it has no meaning in this context. Here is the list of valid types:

Numeric Data (assign any Liberty Basic numeric variable to any of these types)
----------------------------
double (a double float)
ulong (4 bytes) -
long (4 bytes)
short (2 bytes)
ushort, word (2 bytes)

String Data (assign any Liberty Basic string variable to this type)
----------------------------
ptr (4 bytes, long pointer, for passing strings)
char (2 byte charater)
char[n] (4 byte pointer to fixed length string - specify
length as value n)

Special Purpose
----------------------------
struct (4 bytes, long pointer, for passing structs)
boolean (true/false expression)


Liberty Basic's treatment of structs (user defined data types) differs from many languages. Most languages require
the programmer to first define the data type and then declare variables of that type. In this way you could have
multiple instances of variables of the type EMPLOYEE Liberty Basic combines the type creation and variable
creation together as a single step. The date type definition is the variable, and there can only be ONE
instance of that variable.

User defined variable types are powerful (especially in database programming) and add significant utility to a
language. Carl has said he will be looking into enhancing Liberty Basic to allow multiple instances of a struct
(instead of just the one by definition). This will be a welcome addition.

Structs can be used both by the Liberty Basic core language and in calling DLLs for passing and receiving complex data
structures. The single instance limitation does not give them as much utility in the core language, but they are
indispensable in use with DLLs. Take for instance the call to get the window dimensions of a window. It gets the
various dimensions returned in the form of a struct (data record).

CallDLL #user32, "GetWindowRect", hwnW As long, _
winrec As struct, _
result As long

Here is the definition of that struct that is used in the DLL call above:

Struct winrec, x1 As long, y1 As long, x2 As long, y2 As long

It is a record called "winrec" consisting of four values all of which are long integers. This record is stored in memory
in much the same way an array will be stored - each data element in consecutive memory locations. Since the language
has no way of knowing what the contents, size or make up of a struct will be, it is difficult to pass these as whole
entities. Luckily, since they are stored in consecutive memory locations this is not necessary. All that needs to
be passed is the beginning memory location of where the struct starts in memory. Again this is a pointer. It
points to a memory location.

As I mentioned, the Struct command is useful in calling DLL's and also for use with the core language. There are
several cases where one might intentionally use a struct in the course of programming without ever calling a DLL. The
most obvious one to me currently is to supplement the lack of globals by creating a global struct to hold all of your
global values. It might look like this:

Struct global, _
Name as string, _
Date as string, _
Counter as integer

These could be accessed, changed and utilized through out the program since (as I recently learned) structs are global
in scope.

To set a value, or access a value in the struct we use the following command syntax: struct_name.element.STRUCT. The word "STRUCT" in the syntax is the keyword that defines the variable as an element of a struct. Below is a couple
example of code that assigns a value to a element in the global struct mentioned above - also shown is the code to
print an element from the same struct.

global.Name.struct = "Brad"
Print global.Counter.struct

There are a couple special functions that are used in conjunction with structs. The first is used to retrieve the contents of a memory location pointed to by an element of the struct. In the struct above if, after assigning "Brad" to the Name element, we simple printed the Name element we would get a number. It is the memory location where the string Name is stored. A pointer again. To print the value of the string (or otherwise retrieve it) we need to use the function Winstring. Winstring requires a pointer as its argument. According to the help file it:

".returns a string when a function returns a pointer to a string. This function is especially useful when retrieving
the text string from a STRUCT that has been altered by a function." Using that function we could print the value
pointed to by the element Name in the global struct. Here is that code:

Print winstring(global.Name.struct)

Another useful technique is determining the length (size) of a struct using the LEN() function. This is most often used
in conjunction with calling a DLL where the DLL requires the length (size) of the struct that is being passed. The
following assigns the size of the Employee struct (mentioned earlier) to the variable sizeofstruct:

sizeofsrtuct = len(Employee.struct)

Beyond the Basics -

Structs offer a great deal of power to the programmer and have opened up many possibilities in the realm of calling
DLL's. Even more recent and powerful feature is the callback. I will touch on this feature just briefly, as a
whole tutorial can be written about callbacks alone. They are by far the most complex area of DLL interface in Liberty
Basic, but with the complexity comes immense power.

DLL's sometimes require a callback function to be passed to them in the call. This is an address in memory of a
specific function that can be triggered by the DLL when an event is fired. The function is a regular Liberty Basic
function. To setup a callback you must specify it (using the command Callback), before you invoke the DLL call that
will require the callback. According to the help file the syntax is as follows:

callback addressPTR, functionName(type1, type2...), returnValue

Usage:

addressPTR - assigns a name to the memory address of the function.

functionName - is the name of the function in the Liberty BASIC program that will be called by the API function.

(type1, type2...) - a comma-separated list of parameters, which will be specific to the function used. The parameters must be valid data TYPES such as

"ulong" and "long".

returnValue - the type of return value is listed after the closing parenthesis. The Liberty BASIC function may return a value to the calling function.

The way this works is that the DLL is passed the addressPTR which points to a function that appears elsewhere in your
code. The helpfile has an example I recommend you look at if this is an area that interests you. It demonstrates the
DLL call enumwindows which returns a callback to the calling program. Also of interest and great utility is a DLL
written by Brent Thorn called WMLiberty.dll (get it at http://groups.yahoo.com/group/lbexp/files/). It utilizes
the callback capability of Liberty Basic to trap messages in the windows message queue and to trigger events in Liberty
Basic based on those messages. The function is more complex than I intend to go into, but for the advanced Liberty Basic programmer looking to integrate advanced functions (such as MDI, Key Capture, Control Subclassing) into your
applications this is the tool you are looking for.

Dennis Mckinney" (dlm81854@accs.net) recently supplied an example of a callback being used to detect
the movement or resizing of a window. This example uses WMLiberty.dll to setup the callback that triggers the event
handler.

 
     '-- code --
         nomainwin
     
         Open "WMLiberty.dll" For DLL As #wmlib
     
         open "Test Size / Move Message" for window as #1
         print#1, "trapclose [quit]"
         hWin = hwnd(#1)
         hWin(0) = hWin
         Callback lpfnOnMove,OnMove(long,long,long,long),long
         CallDLL #wmlib, "SetWMHandler", _
                         hWin As long, _
                         _WM_EXITSIZEMOVE As _
                         long, _
                         lpfnOnMove As long, _
                         0 As long, _
                         ret As long
     
     [loop]
         Scan
     GoTo [loop]
     
     [quit]
         close #1
         close #wmlib
         end
     
     Function OnMove( hWnd, uMsg, wParam, lParam )
         If uMsg = _WM_EXITSIZEMOVE AND hWnd = hWin(0) then
             Notice "Window Moved or Resized"
         End If
     End Function


Final Words -

So what I have covered is the dynamics of calling DLL functions from Liberty Basic. I have merely scratched the surface of the material. There is much more to learn. There is one aspect of call the DLL that I have not covered, and it is perhaps one of the more difficult areas of understanding, and that is converting the call (which is often documented in C or C++) to Liberty Basic format. This is another subject that could easily consume another article. The foundation has been laid here. I would welcome someone of greater experience who would write about this. For now let me point you to a few resources that you can use in at least understanding the Windows API calls.

Alyce Watson has written a marvelous ebook called Mastering Liberty Basic. The new update for Liberty Basic 3.02 has just been released. It can be downloaded from the website "Alyce's Restaurant" at http://iquizme.0catch.com/lb/ - it
is shareware, but is hands down the best, most authoritative resource for Liberty Basic around.

There are several free sources of help files that cover the Windows API for the programmer. Alyce has links to many of these on her website at http://iquizme.0catch.com/lb/api/index.html . There you will find the file Win32api.zip - a downloadable reference. Another excellent source of information is the Windows 32 SDK by Borland - it can be downloaded from ftp://ftp.inprise.com/pub/bcppbuilder/techpubs/bcbhlp01.zip . A great website called All API Net
(http://www.allapi.net/ ) contains references to hundreds of API calls all formatted for Visual Basic. It also contains
an API viewer you can download. Don't forget that Microsoft has references to the API calls also at its developers
support site called the MDDN - you will have to dig a bit, but this link will get you started: http://www.microsoft.com/msdownload/platformsdk/sdkupdate/

I personally use a reference book which explains the Windows 32bit API and offers examples in Visual Basic 5. It is called Visual Basic 5.0 programmers guide to the WIN32 API by Danny Appleman. I recently checked http://www.Amazon.com and found that there is a new release of this book called Visual Basic Programmer's Guide to the Win32 API, Published by Sams - ISBN: 0672315904


Home

Paths and File Names
Observed online
Resources for the beginner
Graphics Drawing Rules
beginner's guide to API and DLL
Drawing IN MEMORY
Radiobuttons via API
NumbWord
Working with Comboboxes