VC++ 6.0 ebook chapter index
homepage aranna.altervista.org
Free counters!

Miscellaneous DLL Topics

I mentioned earlier that a dynamic library module doesn't receive messages. However, a library module can call GetMessage and PeekMessage. The messages the library pulls from the queue with these functions are actually messages for the program that called the library function. In general, the library works on behalf of the program calling it—a rule that holds for most Windows functions that a library calls.

A dynamic library can load resources (such as icons, strings, and bitmaps) either from the library file or from the file of the program that calls the library. The functions that load resources require an instance handle. If the library uses its own instance handle (which is passed to the library during initialization), the library can obtain resources from its own file. To load resources from the calling program's .EXE file, the library function requires the instance handle of the program calling the function.

Registering window classes and creating windows in a library can be a little tricky. Both the window class structure and the CreateWindow call require an instance handle. Although you can use the library module's instance handle in creating the window class and the window, the window messages still go through the message queue of the program calling the library when the library creates the window. If you must create window classes and windows within a library, it's probably best to use the calling program's instance handle.

Because messages for modal dialog boxes are retrieved outside a program's message loop, you can create a modal dialog box in a library by calling DialogBox. The instance handle can be that of the library, and the hwndParent argument to DialogBox can be set to NULL.

Dynamic Linking Without Imports

Rather than have Windows perform dynamic linking when your program is first loaded into memory, you can link a program with a library module while the program is running.

For instance, you would normally call the Rectangle function like this:

Rectangle (hdc, xLeft, yTop, xRight, yBottom) ;

This works because the program has been linked with the GDI32.LIB import library, which supplied the address of Rectangle.

You can also call Rectangle in a very roundabout manner. You first use typedef to define a function type for Rectangle:

typedef BOOL (WINAPI * PFNRECT) (HDC, int, int, int, int) ;

You then define two variables:

HANDLE  hLibrary ;
PFNRECT pfnRectangle ;

Now you set hLibrary to the handle of the library and lpfnRectangle to the address of the Rectangle function:

hLibrary = LoadLibrary (TEXT ("GDI32.DLL"))
pfnRectangle = (PFNPRECT) GetProcAddress (hLibrary, TEXT ("Rectangle"))

The LoadLibrary function returns NULL if the library file can't be found or if some other error occurs. Now you can call the function and then free the library:

pfnRectangle (hdc, xLeft, yTop, xRight, yBottom) ;
FreeLibrary (hLibrary) ;

Although this technique of run-time dynamic linking doesn't make much sense for the Rectangle function, it can come in handy when you don't know the name of the library module until run time.

The code above uses the LoadLibrary and FreeLibrary functions. Windows maintains "reference counts" for all library modules. LoadLibrary causes the reference count to be incremented. The reference count is also incremented when Windows loads any program that uses the library. FreeLibrary causes the reference count to be decremented, as does the termination of an instance of a program that uses this library. When the reference count is 0, Windows can discard the library from memory, because the library is no longer needed.

Resource-Only Libraries

Any function in a dynamic-link library that a Windows program or another library can use must be exported. However, a DLL need not contain any exported functions. What would such a DLL contain? The answer is resources.

Let's say you're working on a Windows application that requires a number of bitmaps. Normally you would list these in the resource script of the program and load them into memory with the LoadBitmap function. But perhaps you want to create several sets of bitmaps, each set customized for one of the major display resolutions commonly used with Windows. It would make most sense to store these different sets of bitmaps in different files, because a user would need only one set of bitmaps on the fixed disk. These files are resource-only libraries.

Figure 21-5 shows how to create a resource-only library file called BITLIB.DLL that contains nine bitmaps. The BITLIB.RC file lists all the separate bitmap files and assigns each one a number. To create BITLIB.DLL, you need nine bitmaps named BITMAP1.BMP, BITMAP2.BMP, and so forth. You can use the bitmaps provided on this book's companion disc or create them yourself in the Visual C++ program. They are associated with numeric IDs of 1 through 9.

Figure 21-5. The BITLIB library.

BITLIB.C

/*--------------------------------------------------------------
   BITLIB.C -- Code entry point for BITLIB dynamic-link library
               (c) Charles Petzold,  1998

  --------------------------------------------------------------*/

#include <windows.h>

int WINAPI DllMain (HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
     return TRUE ;
}

BITLIB.RC (excerpts)

//Microsoft Developer Studio generated resource script.

#include "resource.h"

#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
// Bitmap

1                       BITMAP  DISCARDABLE     "bitmap1.bmp"
2                       BITMAP  DISCARDABLE     "bitmap2.bmp"
3                       BITMAP  DISCARDABLE     "bitmap3.bmp"
4                       BITMAP  DISCARDABLE     "bitmap4.bmp"
5                       BITMAP  DISCARDABLE     "bitmap5.bmp"
6                       BITMAP  DISCARDABLE     "bitmap6.bmp"
7                       BITMAP  DISCARDABLE     "bitmap7.bmp"
8                       BITMAP  DISCARDABLE     "bitmap8.bmp"
9                       BITMAP  DISCARDABLE     "bitmap9.bmp"
Create the BITLIB project in a workspace named SHOWBIT. Create the SHOWBIT program, shown in Figure 21-6, in another project named SHOWBIT, the same as before. However, don't make BITLIB a dependency of SHOWBIT; otherwise, the link step will require a BITLIB.LIB file, and one isn't created because BITLIB has no exported functions. Instead, build BITLIB and SHOWBIT separately by alternately setting each of them as the Active Project and building.

SHOWBIT.C reads the bitmap resources from BITLIB and displays them in its client area. You can cycle through the bitmaps by pressing a key on the keyboard.

Figure 21-6. The SHOWBIT program.

SHOWBIT.C

/*-----------------------------------------------------------
   SHOWBIT.C -- Shows bitmaps in BITLIB dynamic-link library
                (c) Charles Petzold, 1998

  -----------------------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

TCHAR szAppName [] = TEXT ("ShowBit") ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }

     hwnd = CreateWindow (szAppName, 
                          TEXT ("Show Bitmaps from BITLIB (Press Key)"),
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     if (!hwnd)
          return 0 ;
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

void DrawBitmap (HDC hdc, int xStart, int yStart, HBITMAP hBitmap)
{
     BITMAP bm ;
     HDC    hMemDC ;
     POINT  pt ;

     hMemDC = CreateCompatibleDC (hdc) ;
     SelectObject (hMemDC, hBitmap) ;
     GetObject (hBitmap, sizeof (BITMAP), &bm) ;
     pt.x = bm.bmWidth ;
     pt.y = bm.bmHeight ;
     
     BitBlt (hdc, xStart, yStart, pt.x, pt.y, hMemDC, 0, 0, SRCCOPY) ;
     
     DeleteDC (hMemDC) ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static HINSTANCE hLibrary ;
     static int       iCurrent = 1 ;
     HBITMAP          hBitmap ;
     HDC              hdc ;
     PAINTSTRUCT      ps ;

     switch (message)
     {
     case WM_CREATE:
          if ((hLibrary = LoadLibrary (TEXT ("BITLIB.DLL"))) == NULL)
          {
               MessageBox (hwnd, TEXT ("Can't load BITLIB.DLL."),
                           szAppName, 0) ;
               return -1 ;
          }
          return 0 ;
          
     case WM_CHAR:
          if (hLibrary)
          {
               iCurrent ++ ;
               InvalidateRect (hwnd, NULL, TRUE) ;
          }
          return 0 ;
          
     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          
          if (hLibrary)
          {
               hBitmap = LoadBitmap (hLibrary, MAKEINTRESOURCE (iCurrent)) ;

               if (!hBitmap) 
               {
                    iCurrent = 1 ;
                    hBitmap = LoadBitmap (hLibrary, 
                                          MAKEINTRESOURCE (iCurrent)) ;
               }
               if (hBitmap)
               {
                    DrawBitmap (hdc, 0, 0, hBitmap) ;
                    DeleteObject (hBitmap) ;
               }
          }
          EndPaint (hwnd, &ps) ;
          return 0 ;
          
     case WM_DESTROY:
          if (hLibrary)
               FreeLibrary (hLibrary) ;

          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

During processing of the WM_CREATE message, SHOWBIT gets a handle to BITLIB.DLL:

if ((hLibrary = LoadLibrary (TEXT ("BITLIB.DLL"))) == NULL)

If BITLIB.DLL isn't in the same directory as SHOWBIT.EXE, Windows will search for it as discussed earlier in this chapter. If LoadLibrary returns NULL, SHOWBIT displays a message box reporting the error and returns a -1 from the WM_CREATE message. This causes the CreateWindow call in WinMain to return NULL, and the program terminates.

SHOWBIT can obtain a handle to a bitmap by calling LoadBitmap with the library handle and the number of the bitmap:

hBitmap = LoadBitmap (hLibrary, MAKEINTRESOURCE (iCurrent)) ;

This returns an error if the bitmap corresponding to the number iCurrent isn't valid or if not enough memory exists to load the bitmap.

While processing the WM_DESTROY message, SHOWBIT frees the library:

FreeLibrary (hLibrary) ;

When the last instance of SHOWBIT terminates, the reference count of BITLIB.DLL drops to 0 and the memory it occupies is freed. As you can see, this is a simple method of implementing a "clip art" program that could load precreated bitmaps (or metafiles or enhanced metafiles) into the clipboard for use by other programs.