Anatomy of a DLL Procedure Call

We’ve been pretending that the ZAPI functions are written in Basic to be called by Basic, but in reality they are written as a C DLL to be called from C, Pascal, Logo, Scheme, and the current language of the month. The ZAPI library couldn’t be a Visual Basic DLL because it needs to support all languages directly, not through COM Automation. Also, many callers won’t have the Visual Basic run-time DLL on their disks. The last thing the author of ZAPI had in mind was making it easy to call API functions from Basic. Internally, ZAPI uses whatever conventions are most efficient (usually taken from C, but sometimes from Pascal).

In order to call a procedure in a DLL, you need either a type library entry or a Declare statement for it. Since Declare statements are written in Basic, I’ll concentrate on them. The Zapem API function, shown in Figure 2-5, serves as a preliminary model for writing and using Declare statements.

Figure 2-5. Declaring and using a DLL function.

A lot is packed into this short bit of code. Take a look at the labeled sections in Figure 2-5, which correspond to the numbered sections here:

  1. A Declare statement looks sort of like the first line of a procedure
    definition except that it starts with the Declare keyword. You specify whether the procedure is a Sub or a Function and then give the name of the procedure followed by the argument list and, for Functions, the return value.

  2. You must include the name of the DLL containing the procedure in
    a Lib clause. If you’re calling your own DLL, you probably know the name. If you’re calling the Windows API, you sometimes have to guess. C programmers don’t need to put the DLL name in their declarations, and since most API documentation is designed for C programmers, you might have trouble figuring it out. Microsoft Visual C++ comes with a file named WIN32API.CSV that tells all for Win32. Another tech­nique is to use the /DUMP option of the LINK program provided with Visual Basic. The command LINK /DUMP /EXPORTS ODBC32.DLL, for example, will show all the functions provided by the ODBC32 DLL. If that doesn’t help, use the trial-and-error method, starting with the DLLs shown in Table 2-1.
    Services DLL
    Common controls COMCTL32
    Common dialogs COMDLG32
    Drag and drop, icon extraction, Windows 95 shell SHELL32
    Graphics Device Interface GDI32
    Graphics (3-D lines and surfaces) OPENGL32 (NT only)
    Graphics (games and animation) WING32
    Memory, disks, processes, resources, tasks, modules KERNEL32
    Multimedia, sound, MIDI, joysticks, timing WINMM
    Networks (WNet) MPR
    Networks (LanMan) NETAPI32
    NT Security, Registry, and other advanced services ADVAPI32
    Component Object Model (COM) OLE32
    Automation and type conversion OLEAUT32
    Version checking VERSION
    Windows, menus, strings, messages USER32

Table 2-1. Windows system DLLs.

  1. The big question is whether to pass by value or by reference. Most
    arguments should be passed by value, but you’ll hear a lot about the exceptions later in this chapter.

  2. The original Basic version of Zapem returned a Boolean value, but
    the Windows BOOL type isn’t the same as a Basic Boolean. A BOOL
    is actually an int, which is 32 bits wide. To C programmers, a Boolean
    is actually a typedef called VARIANT_BOOL that evaluates to a C short (16 bits). In other words, a Boolean is the same size as a Basic Integer. Although you should declare what Windows calls BOOL as Long, you can assign the result to a Boolean. Basic automatically performs the type conversion from Long to Boolean on return values.

That’s the quick introduction to API calls. When you get down to actual coding, though, things get complicated. Every type of argument has its own quirks and patterns, and you must look at each type specifically. Fortunately, the Windows API never uses the Variant, Currency, Single, or Double type. The remaining types fall into patterns that I can discuss one by one.

The COM Automation API uses Variant and other Visual Basic data types. Theoretically, you could use them in declarations for the COM Automation system DLLs or for COM Automation–compatible DLLs that you write. You can even use the Optional and ParamArray attributes in Declare statements. This chapter, however, concentrates on the integer and string types used by the Windows API.