Sample Embedded Window DLL

In this section, we’ll analyze more source code in the sample DLL, and point out the functions and definitions required for embedded windows. The EWDEMO.C file contains the source code for a sample embedded window. The sample DLL displays a dynamic list of network printers in an embedded window. You can use this sample code as a template for creating your own embedded window DLLs.

Functions

The DLL consists of four functions: LibMain, WEP, PrinterListProc, and InitPrinterList. LibMain is the standard DLL function that registers the window class, and WEP is the standard function that terminates the DLL. PrinterListProc performs the message processing, including processing for the embedded window messages EWM_RENDER and EWM_QUERYSIZE. It also calls InitPrinterList to initialize the printer list after it receives a WM_CREATE message from Windows Help.

Initialization (WM_CREATE)

The PrinterListProc function is responsible for creating an embedded window when the window first receives a WM_CREATE message. The type for the structure passed in the WM_CREATE message is defined in DLL.H, as shown below:

typedef struct
  {
       short   idMajVersion;
       short   idMinVersion;
       LPSTR   szFileName;
       LPSTR   szAuthorData;
       HANDLE  hfs;
       DWORD   coFore;
       DWORD   coBack;  } 
 EWDATA, FAR * QEWDATA;

As noted earlier, the szFileName field points to the relative path of the .HLP file, and the szAuthorData field points to the author-data parameter in the ewl, ewc, or ewr command from the source RTF file. The local variable qci is defined using this type.

PrinterListProc first takes the information passed in the lParam parameter to WM_CREATE and copies it into the fields of the qci structure, as follows:

case WM_CREATE:
      qci = (QCI)((CREATESTRUCT FAR *)lParam)->lpCreateParams;

      /*------------------------------------------------------------*\
      | Save the WM_CREATE information.
      \*------------------------------------------------------------*/
      lstrcpy( rgchFileName, qci->szFileName );
      lstrcpy( rgchAuthorText, qci->szAuthorData );

Next, PrinterListProc adds a border to the embedded window and initializes the printer list, as follows:

/*------------------------------------------------------------*\
      | Add a border to this window.
      \*------------------------------------------------------------*/
      SetWindowLong( hwnd, GWL_STYLE,
             GetWindowLong( hwnd, GWL_STYLE ) | WS_BORDER );
      /*------------------------------------------------------------*\
      | Initialize the printer list.  This could be updated more
      | dynamically, say on each WM_PAINT message.
      \*------------------------------------------------------------*/
      InitPrinterList();

      return 0L;

Painting (WM_PAINT)

PrinterListProc is also responsible for painting the embedded window. When the window receives a WM_PAINT message, PrinterListProc lists the names of the printers in the list initialized by InitPrinterList, as follows:

case WM_PAINT:
      BeginPaint( hwnd, &ps );
      GetTextMetrics( ps.hdc, &tm );
      SetTextColor( ps.hdc, GetSysColor( COLOR_WINDOWTEXT ) );
      SetBkColor( ps.hdc, GetSysColor( COLOR_WINDOW ) );
      for (irgch = 0; irgch  MAX_PRINTERS && rgrgchPrinters[irgch][0];
           irgch++)
           TextOut( ps.hdc, tm.tmMaxCharWidth/2,
                    (tm.tmHeight + tm.tmExternalLeading)/2 +
                    irgch*(tm.tmHeight + tm.tmExternalLeading),
                    rgrgchPrinters[irgch], lstrlen(rgrgchPrinters[irgch]) );
      EndPaint( hwnd, &ps );
      return 0L;

Obtaining Rendering Information (EWM_RENDER)

PrinterListProc also processes the EWM_RENDER message sent by Windows Help. The wParam parameter determines the type of rendering information that PrinterListProc returns. This parameter has the value CF_BITMAP or CF_TEXT.

CF_BITMAP

If wParam is CF_BITMAP, PrinterListProc creates a bitmap listing the system printers and returns a handle to this bitmap (hbm). PrinterListProc gets the information it needs to create the bitmap in two ways:

nCopying the values passed in the lParam parameter to the EWM_RENDER message.

nSending an EWM_QUERYSIZE message to the embedded window and obtaining its size.

The lParam parameter points to a RENDERINFO structure. Type RENDERINFO is defined (in DLL.H), as follows:

typedef struct
  {
  RECT  rc;
  HDC   hdc;
  } RENDERINFO, FAR *QRI;

To create the bitmap, PrinterListProc copies the device context from lParam. It then obtains the size required for the bitmap by sending an EWM_QUERYSIZE message, as follows:

switch( wParam )
        {
        case CF_BITMAP:
      /*------------------------------------------------------------*\
      | Prepare a bitmap image of the printer list.  This will
      | appear in a similar manner as the layout, but we will need
      | to draw the border explicitly here, as well as making sure
      | the background is filled in correctly.  We must use the
      | default colors for this hdc.
      \*------------------------------------------------------------*/
qri = (QRI)lParam;
       hdc = CreateCompatibleDC( NULL );

       if (hdc)
       {
            hfont = 0;

            /*------------------------------------------------------------*\
            | Create a monochrome bitmap for the DC, sized for the screen.
            \*------------------------------------------------------------*/
            SendMessage( hwnd, EWM_QUERYSIZE, hdc, (long)(LPPOINT)&pt );
            rc.left = 0;
            rc.top = 0;
            rc.right = pt.x;
            rc.bottom = pt.y;

PrinterListProc creates the bitmap using the default foreground and background colors, but it draws the window border explicitly, as follows:

hbm = CreateCompatibleBitmap( qri->hdc, pt.x, pt.y );
            if (hbm)
            {
                 hbmDefault = SelectObject( hdc, hbm );
                 /*------------------------------------------------------*\
                 | Clear out the bitmap.
                 \*------------------------------------------------------*/
                 hbrush = CreateSolidBrush( GetBkColor( hdc ) );
                 if (hbrush)
                 {
                      FillRect( hdc, &rc, hbrush );
                      DeleteObject( hbrush );
                 }

                 Rectangle( hdc, rc.left, rc.top, rc.right, rc.bottom );

                 GetTextMetrics( hdc, &tm );
                 for (irgch = 0; irgch  MAX_PRINTERS &&
                     rgrgchPrinters[irgch][0]; irgch++)
                 TextOut( hdc, tm.tmMaxCharWidth/2,
                          (tm.tmHeight + tm.tmExternalLeading)/2 +
                          irgch*(tm.tmHeight + tm.tmExternalLeading),
                          rgrgchPrinters[irgch],
                          lstrlen( rgrgchPrinters[irgch] ) );
              /*----------------------------------------------------------*\
              | At this point, hbm is the desired bitmap, sized for a
              | screen display.  This will be stretched as needed for
              | the target display.
              \*----------------------------------------------------------*/
              hbm = SelectObject( hdc, hbmDefault );
        }
       else
        {
              /*---------------------------------------------------------*\
              | Not enough memory.  Clean up and return NULL.
              \*---------------------------------------------------------*/
              hbm = NULL;
        }

        DeleteDC( hdc );
        }

     lReturn = (long)hbm;
     break;

PrinterListProc creates the bitmap using the default foreground and background colors for the device context, but it draws the window border explicitly, as follows:

hdc = CreateCompatibleDC( qri->hdc );
       /*------------------------------------------------------------*\
       | Make hdc really compatible with the source hdc.
       \*------------------------------------------------------------*/
       if (hdc)
       {
            SetTextColor( hdc, GetTextColor( qri->hdc ) );
            SetBkColor( hdc, GetBkColor( qri->hdc ) );
            hbrushSource = SelectObject( qri->hdc,
                                         GetStockObject( NULL_BRUSH ) );
            if (hbrushSource)
                 SelectObject( hdc, hbrushSource );
            hpenSource = SelectObject(qri->hdc, GetStockObject(NULL_PEN) );
            if (hpenSource)
                 SelectObject( hdc, hpenSource );
       }

       hbm = CreateCompatibleBitmap( qri->hdc, rc.right, rc.bottom );
       if (hdc && hbm && (hbmDefault = SelectObject( hdc, hbm )))
       {
            GetTextMetrics( hdc, &tm );
            for (irgch = 0; irgch  MAX_PRINTERS && rgrgchPrinters[irgch][0];
                 irgch++)
                 TextOut( hdc, tm.tmMaxCharWidth/2,
                          (tm.tmHeight + tm.tmExternalLeading)/2 +
                          irgch*(tm.tmHeight + tm.tmExternalLeading),
                          rgrgchPrinters[irgch],
                          lstrlen( rgrgchPrinters[irgch] ) );
            hbm = SelectObject( hdc, hbmDefault );
       }
       .
       .
       .
       lReturn = (long)hbm;
       break;

CF_TEXT

If wParam is CF_TEXT, PrinterListProc simply creates an ASCII list of the system printers in a Clipboard compatible format, as follows:

case CF_TEXT:
       /*------------------------------------------------------------*\
       | List out the printers in a format suitable for the Clipboard.
       | Since this list will be embedded in the text of the topic,
       | use blank lines for separators.
       \*------------------------------------------------------------*/
       gh = GlobalAlloc( GMEM_MOVEABLE | GMEM_NOT_BANKED,
                         sizeof(rgrgchPrinters) );
       lReturn = (long)gh;
       if (gh)
       {
            sz = GlobalLock( gh );
            lstrcpy( sz, "\r\n" );
            for (irgch = 0; irgch  MAX_PRINTERS && rgrgchPrinters[irgch][0];
                 irgch++)
            {
                 lstrcat( sz, rgrgchPrinters[irgch] );
                 lstrcat( sz, "\r\n" );
            }
            GlobalUnlock( gh );
       }
       break;

Obtaining the Window Size (EWM_QUERYSIZE)

The PrinterListProc function is also responsible for processing EWM_QUERYSIZE messages. These messages can come from Windows Help, or they can be sent by the code in PrinterListProc that processes the EWM_RENDER message.

As described earlier, the EWM_QUERYSIZE message returns the dimensions of the embedded window. The wParam parameter to EWM_QUERYSIZE is the device context for the window display, and the lParam parameter points to a POINT structure that returns the length and height of the window display, as shown below:

case EWM_QUERYSIZE:
      /*-----------------------------------------------------------------*\
      *   Size query message from Windows Help
      *   wParam is the target hdc.
      *   wLong is the address of the point to return size in.
      *   Return non-zero to indicate that we did something.
      \*-----------------------------------------------------------------*/
      hfont = SelectObject( (HDC)wParam, GetStockObject( SYSTEM_FONT ) );
      GetTextMetrics( (HDC)wParam, &tm );
      ((LPPOINT)lParam)->x = ((LPPOINT)lParam)->y = 0;
      for (irgch = 0; irgch < MAX_PRINTERS && rgrgchPrinters[irgch][0];
            irgch++)
          {
          DWORD dwExt = GetTextExtent( (HDC)wParam, rgrgchPrinters[irgch], 
            lstrlen( rgrgchPrinters[irgch] ) );
          ((LPPOINT)lParam)->x = max( (WORD)((LPPOINT)lParam)->x, LOWORD(dwExt) );
          ((LPPOINT)lParam)->y += HIWORD(dwExt);
          }
      ((LPPOINT)lParam)->x += tm.tmMaxCharWidth;
      ((LPPOINT)lParam)->y += tm.tmHeight + tm.tmExternalLeading;
      if (hfont)
        SelectObject( (HDC)wParam, hfont );
      return 1;
    }