Mega Code Archive

 
Categories / Delphi / Examples
 

Apiwin

The Windows API: An Example Of Use By Joe C. Hecht, Borland Delphi Technical Support Group This paper is targeted to developers who wish an example of interfacing to the Windows Application Programming Interface (API). The example program demonstrates creating both an Application and a Dynamic Link Library that use Windows API calls, including the use of "callback" functions. As Pascal was originally designed to be a teaching language, the code examples presented are written in Pascal, and are designed to be easily portable to other languages such as "C". The concepts contained apply to all versions of Windows. The example program assumes that you will be targeting for Windows version 3.1 or later, Windows 95 and/or Windows NT, and can be directly compiled using Turbo Pascal for Windows, Borland Pascal, Delphi, or Delphi 2.0. Part I: The Primer Windows API calls are just calls to dynamic link libraries Loading a dynamic link library Dynamic loading at runtime Resource only dynamic link libraries Stack usage and dynamic link libraries Callback functions The Hook functions Data storage in dynamic link libraries Instanced addresses and thunking Part II: Getting our hands dirty Windows API calls are just calls to dynamic link libraries Back in the early days of Windows, PCs had much less memory available than today. The first version of Windows required only 256k of memory and two floppy drives... amazingly little by today's standards. The designers of windows had to fit a lot of code and resources into that 256k of memory, and still leave room for multiple programs to run. This feat was achieved by allowing multiple programs to share commonly used code and resources. The vehicle used to provide this magic is the dynamic link library. Functions and resources contained in dynamic link libraries can be shared by multiple applications. The code and resources contained in a dynamic link library are generally not loaded until needed, and can be freed when not in use. Most of the Windows system is comprised of dynamic link libraries. Usually dynamic link libraries have the extension of ".DLL", but this is not a requirement. An example would be in the heart of Windows itself: GDI.EXE, GDI32.DLL, USER.EXE, USER32.DLL, KERNEL.EXE, KERNEL32.DLL, printer drivers (.drv), system drivers(.drv), and some fonts(.fon). An example of a resource only dynamic link library is contained in the MORICONS.DLL file that ships with Windows. It contains a variety of extra icons for Windows users to brighten their desktops with. Some of the other advantages of placing code and resources in a dynamic link library are: Disk space is saved if multiple applications use a dynamic link library. This can sometimes make a drastic difference in the cost of deploying your applications since you can place common functions and resources in a single .DLL file, reducing the size of your executables. The ability to upgrade and replace parts of the program without shipping a whole new executable. The ability to load and execute different resources and code based on some specific criteria available only at runtime. A good example would be "PlugIn" applets that can be dynamically loaded by your application at runtime, without knowing what "PlugIns" might be available at compile time. This can make your program automatically extendible with after-market products. Loading a dynamic link library Your compiler most likely includes interface files to most of the Windows system functions. You simply link to the interface files, and any dynamic link libraries that are used by your program are automatically loaded when your program is started. In 16 Bit Windows, the actual function code is not added to your program, but rather a pointer to the module name of the dynamic link library and the name of the function (or its exported index number) is added. In Win32, the DLL is mapped into the process' address space. The dynamic link library's file must exist at runtime or an error will occur. If your compiler does not include an interface to the function or dynamic link library you want to use, you are going to have to declare a function prototype, and a linking interface, to let your compiler know about the function you are dynamically linking to. The instructions to do this should be included with your compiler's documentation. A short example of a function prototype in Pascal for a dynamic link library would be: {$IFDEF Win32} function MessageBeep(BeepType: Integer):Bool stdcall; external 'USER32'; {$ELSE} procedure MessageBeep(BeepType: Integer); far; external 'USER'; {$ENDIF} This prototype tells the compiler to link to a function named "MessageBeep" located in USER32.DLL (if compiling under 32 bits), or a procedure named "MessageBeep" located in USER.EXE (if compiling under 16 bits). The compiler is also made aware that "MessageBeep" takes a integer size parameter, and uses the "stdcall" passing convention (if compiling under 32 bits). When declaring an interface to a dynamic link library, you must be careful to specify the way that the function expects to have its parameters and return value passed, and who will maintain the stack. There are various calling techniques used including: PASCAL, CDECL, STDCALL, and FASTCALL. In 16 bit Windows, most functions are exported using the PASCAL calling convention. In 32 bit Windows, STDCALL is preferred since this should be compatible w/most other languages. Most API functions are declared with "C" prototypes. If you are wanting to port an interface to a Pascal based language, and you are not familiar with "C", your best bet is to get Alain Tadros's technical paper "Using Borland's Delphi and C++ Together". It is available from Compuserve, and the Borland BBS system and has some excellent additions by Eric Uber. Dynamic loading at runtime Loading a dynamic link library at runtime allows you to load dynamic link libraries that are not known at compile time, or load a library for only a short time. Printer drivers are a good example: You don't know what printer drivers will be available on a users system until runtime, and you would only want to load a printer driver during the printing phase of your program. One of the advantages to loading libraries dynamically is that you don't use up resources (memory) until you actually load the library and the function you wish to call. You load a dynamic link library at runtime by calling the Windows API function LoadLibrary(). Then you can search for a given function within the library using the Windows API function GetProcAddress(). Once you have the address of the function you want, you can call it from your code. One thing to remember: Always call the Windows API function FreeLibrary() when you are done using the dynamic link library. Example: procedure Win31LoadUserAndCallBeepFn; var DllHandle: THandle; BeepFn: procedure(BeepType: Integer); begin DllHandle := LoadLibrary('USER'); if DllHandle >= 32 then begin @BeepFn := GetProcAddress(DllHandle, 'MessageBeep'); if @BeepFn <> nil then BeepFn(-1); FreeLibrary(DllHandle); end; end; procedure Win32LoadUserAndCallBeepFn; var DllHandle: THandle; BeepFn: function(BeepType: Integer): Bool stdcall; begin DllHandle := LoadLibrary('USER32'); if DllHandle <> 0 then begin @BeepFn := GetProcAddress(DllHandle, 'MessageBeep'); if @BeepFn <> nil then BeepFn($FFFFFFFF); FreeLibrary(DllHandle); end; end; Here we have two code snippets showing the differences between loading a library under 16 and 32 bit Windows. In this case, we must mark the function we are calling as using the "stdcall" calling convention under 32 bit Windows, where under 16 bit windows the "pascal" calling convention is assumed. Note that the 16 bit version of MessageBeep() is a procedure, and the 32 bit version is a function. The LoadLibrary() function takes a null-terminated string that names the library file to be loaded. If the string does not contain a path, Windows searches for the library in this order: the current directory; the Windows directory; the Windows system directory; the directory containing the executable file for the current task; the directories listed in the PATH environment variable; and finally, the list of directories mapped in a network. If no extension is used, the extension of ".DLL" is assumed. We check the value of the handle returned by LoadLibrary() for an error. Under 16 bit Windows, the return value will be less than 32 if an error occurs, and under 32 bit Windows, the return value will be 0 if an error occurs. We then call the GetProcAddress() function to get the address of the MessageBeep() function. The spelling of the function name is critical, and can be case sensitive under different environments. If GetProcAddress() successfully returns the address of the function, then we can safely call the function. Finally we unload the library to free up memory. Resource only dynamic link libraries As mentioned, dynamic link libraries can hold resources as well. To use resources contained in a dynamic link library, you simply make a call to LoadLibrary(), then you can load resources from the library using one of the Windows API resource functions such as LoadBitmap(), passing the handle to the dynamic link library that you received from the LoadLibrary() call. Just don't forget to free your resources and the library when you are done. You can use the snipit from the above section "Loading dynamic link libraries at runtime" to load a dynamic link library, and then load a resource from the library. Example: LoadBitmap(DllHandle,'SOMEBMP'); Stack usage and dynamic link libraries When you call a function in a dynamic link library, the library generally uses the stack of the calling application. Applications running under 16 Bit Windows had much less stack space available (<64k) than what is available under 32 bit Windows (1 MB). Since functions in a dynamic link library can call functions in other dynamic link libraries as well as making callbacks, you should always thoroughly test your program to make sure you have setup adequate stack space. Callback functions Callback functions are a really wonderful feature of the Windows environment. Callback functions allow you to send the "instanced" address of a function in your application to a function contained in a dynamic link library. The dynamic link library function can then call back to the function in your application to pass back information. A good example of a Windows API function that utilizes callbacks is the EnumFonts() function. The EnumFonts() function enumerates fonts available to a given device context. For each font enumerated, the EnumFonts() function will call back to a function in your application, passing back information about that font. This process continues as long as their are more fonts to enumerate, or until your function returns zero, meaning that you wish to stop further enumerations. Most Windows API functions that accept a callback function as a parameter will also accept an lpData parameter. This lpData parameter is used for "application defined data". You are free to define what this parameter is used for. The lpData parameter is usually passed to the dynamic link library as a longint, and rarely as a far pointer. It is likely you will want to send something besides a longint. You can always typecast a pointer to a data structure in your application to a longint when calling dynamically linked functions, and typecast the longint passed back to your callback function to a pointer so your may access your data structure. As mentioned, when your callback function is called by the Windows API function, the lpData parameter you originally passed to the Windows API function will be passed back to your callback function for your use. This avoids having to declare a global variable in your program. A good example of how this works would be if you were to create a program containing a dialog box to show all the available fonts in a drop-down listbox. When you process the WM_INITDIALOG message, you call the EnumFonts() function, passing the instanced address of your callback function that will process the font information. Your callback function will perform the work to add each font's name to the listbox. The only problem: your callback function does not have a handle to the listbox. You could create a global variable to hold the listbox handle, but instead, you should pass the handle of the listbox to your callback function through the lpData parameter, and let the dynamically linked function pass the handle of the listbox back to your callback function. While it is always nice to keep global variables to a minimum, having the ability to pass user defined data to the callback function becomes critically important when you create callback functions inside a 16-bit dynamic link library. Generally, under 16-bit Windows, a dynamic link library has only one data segment. Since multiple applications can use a single dynamic link library, and, since a 16-bit dynamic Link Library has a single data segment, any global variables defined in the dynamic link library exist in memory only once. If you create a callback function in a dynamic link library, and use a global variable to store data used during the callback, you run the risk of data corruption. Consider the following scenario: You have written several applications that use your font dialog box. Rather than include your font dialog in each application, you opt to store the dialog in a dynamic link library. Suppose that you use a global variable in the dynamic link library to store the listbox handle for your callback function. Now suppose that more than one application loads the dialog box at the same time. Each "copy" of your font dialog box contains a different handle to it's listbox. Clearly, using a global variable to hold the listbox handle will not work since the listbox handle will be different for each dialog, and the global variable used to hold this handle will get trashed. The way around this, is to pass the listbox handle to the EnumFonts() function using the lpData parameter. This is not a problem, when writing 32-bit dynamic link libraries for use under Windows 95 and Windows NT, as under both environments, dynamic link libraries have a separate data area for each application that loads them. None the less, it is still a good practice to always keep global variables to a minimum. The Hook functions A "hook" function is a callback function that can be inserted in the Windows message system so an application can access the message stream before other processing of the message takes place. Often times, there will be other hooks already installed in the messaging system, so there will be times you will need to call the next hook in the "Hook Chain" to allow the other hooks in the chain to access the messages as well. When you install a hook function using the Windows API function SetWindowsHookEx(), you will receive a 32 bit handle to your installed hook function. You will use this handle to both remove the hook when you are done via the Windows API function UnHookWindowsHookEx(), and when calling the next hook in the "Hook Chain" via the Windows API function CallNextHookEx(). We have chosen to use the Windows "Ex" hook functions since we want to be able to port to 32 bit Windows. System wide hooks require that your hook filter function reside in a dynamic link library. The filter function for an application specific hook may reside either in the application or a dynamic link library. Different types of Windows Hooks: Windows defines the following types of hook functions: WH_CALLWNDPROC- A window procedure hook called whenever the SendMessage() function is called. WH_CBT- A computer based training hook called before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event; before setting input focus; and before synchronizing the system message queue. WH_DEBUG-A debugging hook called before any other filter installed by the SetWindowsHookEx() function is called. WH_GETMESSAGE- A message hook called whenever the GetMessage() function has retrieved a message from an application queue. WH_HARDWARE- A nonstandard hardware message hook called whenever the application calls the GetMessage() or PeekMessage() function and there is a hardware event (other than a mouse or keyboard event) to process. WH_JOURNALRECORD- A journaling record hook called when the system removes messages from the system message queue. WH_JOURNALPLAYBACK- A journaling playback hook used to insert mouse and keyboard messages into the system message queue. WH_KEYBOARD- A keyboard hook called whenever the application calls the GetMessage() or PeekMessage() function and there is a WM_KEYUP or WM_KEYDOWN keyboard message to process. WH_MOUSE- A mouse message hook called whenever the application calls the GetMessage() or PeekMessage() function and there is a mouse message to process. WH_MSGFILTER- An application message hook called after a dialog box, message box, or menu has retrieved a message, but before the message is processed. WH_SHELL- A shell application hook called when notification messages from the system have been made. WH_SYSMSGFILTER- A system wide message hook called after a dialog box, message box, or menu has retrieved a message, but before the message is processed. Note: Debugging Windows hooks can be very difficult. The documentation has much to be desired, so before you make the jump, always consult the Microsoft Knowledge Base for any additional information regarding the hook you are designing for. If your hook hangs the system, pressing CTRL+ESC or CTRL+AlT+DEL should unhook any system wide hooks that are in effect. Data storage in dynamic link libraries Under 16 bit Windows, a dynamic link library is only loaded once no matter how many applications request the library to be loaded, generally contains a single data segment, and all global variables in the dynamic link library exist across all applications that load the dynamic link library. If any process causes corruption in the data segment of the dynamic link library, other applications using the dynamic link library can be affected. Many argue that sharing data between applications through a 16 bit dynamic link library is considered a very bad practice. Under Windows 95, the code for a dynamic link library is only loaded once no matter how many applications request the library to be loaded, and the link to a running application gets its own data storage area. This provides some additional crash protection to other applications, since any corrupted global variables in a dynamic link library caused by one application will not adversely affect other applications using that dynamic link library. Under Windows NT, a separate copy of both the dynamic link library's data and code is linked to the calling application. If we think about how the DLL is loaded, we can still realize a memory saving when we talk about physical memory usage. Each process does indeed gets its own copy of the DLL and the DLL data. However, it obtains this through a memory mapping of the DLL. That is, the DLL code is mapped to each processes address space. In physical memory (as seen by the system) the DLL is loaded only once. Instanced addresses and thunking As already mentioned, 16-bit dynamic link libraries have only one data segment. When you call a function in a dynamic link library, the data segment must be switched from your application's data segment to that of the dynamic link library's data segment, and back again after the function returns. If the dynamically linked function happens to make a callback to a function in your application, the data segment must be switched back to your application's data segment during the callback, and back again to the dynamic link library's data segment when your function returns. When making a call to a dynamically linked function, the fixup code to switch the data segment is transparent. In the case of a callback, additional magic is needed by your application. The magic is made possible by using the Windows API function MakeProcInstance() to receive an "instance thunk" to your callback procedure. This "instance thunk" is the glue that holds your callback function and your data segment together during the callback. The procedure address passed to the MakeProcInstance() function needs to be marked with both the "far" and "exported" attributes. After the dynamic link library is through using your callback function, and returns, you should always call the Windows API FreeProcInstance() function to free the instance thunk. Using the MakeProcInstance() function is not necessary in 32 bit applications, or any dynamic link library (16 or 32 bit). It is, however, safe to use under these conditions, so any code you have that uses these functions can be safely ported to dynamic link libraries and 32 bit versions of Windows.