A Generic About... Box Handler

Joe's generic About... box handler is a single binary that can be used by all applications. He also incorporates it into a DLL that has some support code needed by the applications.


August 01, 1994
URL:http://www.drdobbs.com/tools/a-generic-about-box-handler/184409419

Figure 1


Copyright © 1994, Dr. Dobb's Journal

Figure 3


Copyright © 1994, Dr. Dobb's Journal

Figure 2


Copyright © 1994, Dr. Dobb's Journal

Figure 1


Copyright © 1994, Dr. Dobb's Journal

Figure 2


Copyright © 1994, Dr. Dobb's Journal

Figure 3


Copyright © 1994, Dr. Dobb's Journal

SP 94: A Generic About... Box Handler

A Generic About... Box Handler

Making use of code reuse

Joseph M. Newcomer

Dr. Newcomer received his PhD in 1975 in the area of compiler optimization. He is a Windows consultant and applications developer based in Pittsburgh, PA. His past experience has included computer graphics, document-processing software, operating-systems development, compiler development, CASE tooling, computer music, and real-time and embedded-systems development.


The generic "About..." box handler is usually written once and cloned many times. Recently, I found myself generating an application that would spawn a dozen or more "little servers," each of which looked like an ordinary application (top-level menu system, and so on). Consequently, each one needed an About... handler that gave its own name, potentially a separate copyright, and its own icon. I also anticipated the need for internationalization, so I didn't want to put any locale-specific literal text in the program itself. I wanted to create a single binary that could be used by all the applications, and as long as I was building a library, I decided to incorporate it into a DLL that had some other support code needed by the applications. The ultimate result was the code described here, which also supports a modeless About... box. This latter requirement was imposed by the need to have the main window continue to respond to messages, including user input, in a real-time system. As you will see, the differences between this and the normal modal About... box are minimal.

Figure 1 shows the modeless About... box. Its template is in Listing One and its include file is in Listing Two . The AB_ identifiers are the static text windows on the dialog and the icon. The MSG_ identifiers are for the STRINGTABLE, and IDD_ABOUT is the identifier for the dialog resource.

The resource file (available electronically, see "Availability," page 3) for the application contains all the application-specific descriptive text required by the dialog. The application resource compilation is composed of the following components: the application-specific resources, in however many files you have chosen; the About... customization file, libver.rch, used to define the parameters for the version resource; and the VERSIONINFO library file, version.rch.

Figure 3 shows the relationship between the files. The objects on the right of the vertical line are the shared files found in the library and include directories; the objects to the left are the files private to the application.

The VERSIONINFO Structure

The VERSIONINFO structure is the key to recording version information for an application. All of its pieces are documented in the SDK Programmer's Reference Volume 4: Resources, (SDK4), pages 213--222, but it is difficult to deduce exactly what is going on from the rather sketchy descriptions given in this manual. It is ignored in all the Windows documentation I have found, except for Jeffery Richter's lucid description in Windows 3.1: A Developer's Guide (M&T Books, 1993). The structure I use is based upon his example in Chapter 10, and is shown as the file version.rch in Listing Three . This file is placed in a shared directory and is never modified. (I put it in the same include directory as the about.h file.) To compile resources successfully, you must name this directory on the command line to rc using the --I switch.

Without repeating everything Richter explains so well, I will summarize the lines in version.rch; see Table 1. Note that each of the strings must have an explicit NUL byte, '\0', which terminates it. The file ver.h is part of the standard Windows SDK and defines the symbols required for the version declaration. Symbols such as VOS_DOS_WINDOWS16, VFT_APP, and the like, are defined in this file; see SDK4, pages 215--217. I've defined the symbols that begin with VER_; see the application-specific configuration file, libver.rc, (Listing Four). The order and spaces of the initial definitions must be as shown if you use the automated file version-incrementing technique discussed later in this article. When this file is included in your application's resource compilation, it includes the version.rch file, and thus the complete version information, in your resource file. If you are using the Microsoft Visual Workbench, you can cause this inclusion by selecting File|Set Includes_ and adding the file, as shown in Figure 2.

Extracting the Version Information

The version information is extracted by the procedures in abouter.c (Listing Five) and verinf.c (Listing Six). When the About... handler starts up, its WM_INITDIALOG message handler calls the procedure AboutVerInfo (Listing Five), which extracts information from the application's VERSIONINFO resource and places it in the dialog box. The OtherInfo procedure computes the standard, useful environment information, such as the Windows version and the free resources, and places that information in the dialog box.

The version resource block is obtained by a call to my procedure, GetInstanceVersion (Listing Seven). This takes an instance handle to the module whose version information is to be obtained. The ver.dll operations do not include an operation to obtain the version information of a running program; however, the GetModuleFileName call can be used to get the filename of the running program, and the GetFileVersionInfo call is used to obtain the actual information block. The GetFileVersionInfoSize call is used to obtain the size of the block allocated for GetFileVersionInfo to fill in. The GetVersionString is a thin layer on top of VerQueryValue so that I don't have to deal with the NULL test each time. This bulletproofs the handler against missing information or an entire missing VERSIONINFO structure.

In the procedure AboutVerInfo, I first extract information from the program that called the library. Therefore, I need the instance handle of the program itself; the hInst variable which is declared external is the instance handle of the DLL, set by the LibMain procedure, as shown in Listing Eight . This hInst value is used to retrieve the information local to the About... box handler, which must be retrieved from the DLL's resources. To get the handle to the running application, I first ask for the parent window of the dialog. With this handle, I can call GetWindowInstance (defined in windowsx.h) to get the instance handle that I pass to the procedure. This returns an LPVOID value, which is used in later version information queries.

Once I have obtained the version information block, I can extract the fields from it. I call SetAboutTitle to set the heading of the window. This procedure calls GetVersionString, which is part of ver.dll, and ideally obtains a pointer to the string that is the value. I then load a string from the STRINGTABLE, which is the phrase "About"; since this is in the resource file, it can be readily internationalized. I force a space to follow the string if one is not already there, then add the product name and do a SetWindowText. The remaining fields in the dialog box are filled in by passing in the control ID of the static text box in the dialog box and the query string for the version string that is to be set in it. Finally the VerFree procedure is called to release the space of the version resource.

The code in OtherInfo is straightforward. The only clever trick is to send a WM_QUERYDRAGICON to get an HICON handle to the icon to display in the About... box. This allows the generic dialog box to display the proper icon of its parent application.

Win32s/Win32c Considerations

This code was originally developed to run under Win32s and Win16, with the anticipation that it will be ported to the Chicago Win32c interface in the future. Therefore, the file export.h is used to define the linkage types for the exported procedures. The Win16 version is in Listing Nine . For the 32-bit implementations the definitions of EXPORTPROC and DLLEXPORTPROC are empty.

Version Numbering

Automatic version numbering is quite convenient for maintaining sanity. I have been using automatic version numbering in build procedures for about a decade, and have used a variety of tools to automatically update the version number on every build. The tool shown here is an awk script. The awk language is a string-processing, pattern-matching language created by Alfred V. Aho, Brian W. Kernighan, and Peter J. Weinberger at AT&T Bell Laboratories. It is popular on UNIX systems, and a number of commercial, shareware, and freeware implementations of varying quality and conformance to the "standard" UNIX version now run on PCs. I have used two without problems: the MKS Toolkit version and the Thompson Automation version. The GNU version is available from the Austin Codeworks and elsewhere. An alternative to awk is PERL, also widely available; however, I used awk for this project and will limit my description to that language.

The program shown in Listing Nine is a relatively simple use of awk. For more details on awk, refer to The AWK Programming Language, by Aho, Kernighan, and Weinberger (Addison-Wesley, 1988, ISBN 0-201-07981-X). The awk language is one of those truly marvelous tools that can make life a little less exasperating. I have used it to validate databases, do statistical analysis of programs to determine the cost of a language translation of 300K+ lines of source, analyze the statistics of a large lexicon, and perform consistency checks in a hypertext system.

In this project I use awk to increment the version numbers. The number I need to update is the FileVersion which appears both in a binary form (a 4-tuple) and a text string. The ProductVersion essentially remains constant over a long period of time, as it represents an interface specification and is not expected to change often.

The awk program is shown in Listing Ten . Before the program starts, I set the field separator, FS, to a regular expression that will cause a split at periods, commas, backslashes, spaces, and tabs. The patterns are matched against the input lines, and any one that matches a nonempty pattern will cause a new line to be written. It then executes the next operation, which causes the next line to be read, at which point the matching restarts from the top. (Think of this as equivalent to the continue statement applied to the read-line/match loop). If no pattern matches, the last pattern-action statement has no pattern, and will consequently be executed for every line that gets that far. This causes the input line to be written out verbatim.

I have required that the VER_FILEVERSION string appear first; this is the pattern match that triggers the incrementing of the version number. When the VER_BINARY_FILEVERSION line is found, its version number is replaced by the incremented version number that I have stored. In this way, the two version numbers never get out of sync.

In my normal build procedure, this awk program is run any time a successflink has completed. One unfortunate feature of the Microsoft Visual Workbench is that it does not provide for insertion of user-defined tools into the build process unless an external, manually maintained makefile is used, which obviates many of the advantages of the visual workbench, such as maintaining correct dependency lists for the build.

The default action of awk is to take its input from stdin and write its output to stdout. I use the input and output redirection command-line options to establish these connections. However, it is not possible to both read and write the same file via redirection, so I output to a temporary file and then copy that back to the input, as shown in the batch file in Listing Eleven .

Implementing a Modeless About... Box

The constraints of this project required that I not preempt the main GetMessage loop of the application by having a modal box. I did not want the application to have to deal with the details of this, so I defined a simple protocol for invoking the About... box that uses a state descriptor to determine if an About... box was currently active. This state descriptor is the ABOUT structure defined in the include file abouter.h (Listing Twelve). The protocol is that the About() procedure is called with a pointer to an ABOUT data structure. The structure is initialized to {NULL, NULL} and will not be freed up during program execution. (It is either statically or globally allocated once at the start of the program.) This is shown in Listing Thirteen , where the top-level command loop has been modified to handle the IsDialogMessage call required by a modeless dialog box. If the About... box has not been activated, the window component of the ABOUT data structure is NULL and nothing will happen. The About... menu handler simply calls the procedure About() whether the box is active or not.

In Listing Five, the About() procedure first tests that the HWND parameter is non-NULL; without a valid window handle, I cannot locate the version information, so I refuse to do anything if there is no parent window. If there is no instance thunk, I create one. Next, I check to see if a current About... dialog is being displayed. If so, I simply use SetWindowPos to move it to the top of the Z-axis list (HWND_TOP) and make sure it is visible (SWP_SHOWWINDOW). If there is no window instance, I create one with CreateDialogParam, passing in the pointer to the ABOUT structure.

Consulting the handler procedure AboutDlgProc, also in Listing Five, I first get the value of the DWL_USER word, which will initially be NULL. I then handle the WM_INITDIALOG message by storing lParam, the pointer to the ABOUT structure, in the DWL_USER word. On a WM_DESTROY message I see if the AboutData pointer, which would have been initialized from the GetWindowLong call on each entry, is non-NULL. If it is, I set the window handle to NULL so the next call on About() will be forced to create a new window. It is important that this information be kept with the window and not stored in a static variable because variables declared in a DLL are shared by all callers of the DLL. If I had stored the ABOUT structure in the DLL, the first modeless box to close would have NULLed out the handle of the last dialog box opened; any subsequent calls on the About() procedure from an application that already had an open About... box would cause a new About... box to be created. In this handler, the only static storage is the HINSTANCE handle to the DLL itself, which is private to the DLL. All other storage is either stack storage or passed in as pointers (such as the ABOUT structure pointer) to each caller's private space.

This illustrates a point about DLLs, particularly those with message loops that can yield: A DLL's data segment is shared with all callers, in a multithreaded fashion, and is therefore subject to being modified between any two events. Suppose two of my miniservers had their About... boxes up. By switching focus between the two About... boxes (and doing nothing else), the handler code is executed in an alternating fashion between the two applications. It must therefore be reentrant, and this code meets that criterion.

Figure 1 About... box. Figure 2 Adding a custom resource include from Microsoft's AppStudio. Figure 3 Structure of files.

Table 1: Fields used in VERSIONINFO structure.

 Field                              Description

 FILEVERSION                        Specifies file's version
                                     number; four 16-bit values. For
                                     example, "1,2,4,10" would specify
                                     version 1.2.4.10. In my application,
                                     I use only the first two values.

 PRODUCTVERSION                     Specifies product version for which this
                                     file is distributed. Four
                                     16-bit values, as in FILEVERSION.

 FILEFLAGMASK                       Should be set to VS_FFI_FILEFLAGMASK.

 FILEFLAGS                          A set of flags that describe the file
                                     type--debug, patched, private,
                                     prerelease, or special.

 FILEOS                             Specifies operating system for which
                                     file was designed. I use
                                     VOS_DOS_WINDOWS16.

 FILETYPE                           Type of file; for example, applications
                                     are VFT_APP, and DLLs are VFT_DLL.

 FILESUBTYPE                        Specifies details of what the file does;
                                     for example, designating
                                     what device a driver is intended for or
                                     font style. For applications,
                                     it is always VFT2_UNKNOWN.

 VarFileInfo\Translation            Specifies language and character set.
                                     Currently hardwired as 0x0409 (U.S. 
                                     English) and 1252 (Windows,
                                     Multilingual character set).
                                     Once internationalization begins, this
                                     implementation will change.

 BLOCK "040904E4"                   Designates the block of information that
                                     follows as belonging to U.S. English
                                     (0x0409) and Multinational character
                                     set (1252=0x04E4).

 StringFileInfo\Comments            A placeholder for comments. My
                                     applications use this field. In these
                                     examples, the comments are not
                                     meaningful.

 StringFileInfo\CompanyName*        The name of the company that produced
                                     the file. Particularly useful
                                     if you can have third-party add-on DLLs
                                     or executables.

 StringFileInfo\FileDescription*    A file description that (in Richter's
                                     words) is "to be presented to the
                                     user." This is what I do with it in the
                                     About... box.

 StringFileInfo\InternalName*       The file's version number. This is
                                     actually redundant with the binary
                                     FILEVERSION information,
                                     but both are required.

 StringFileInfo\InternalName*       The "internal name" for the file.

 StringFileInfo\LegalCopyright      Copyright notice for the file. I extract
                                     it and put it in the About... box.

 StringFileInfo\OriginalFileName*   The "original name" of the file in
                                     case the user renames it. Presumably
                                     allows installation procedures to
                                     locate older but differently named
                                     copies of a DLL.

 StringFileInfo\ProductName*        Name of the product for which this is
                                     distributed. Would ideally allow
                                     an UnInstall mechanism to locate all
                                     DLLs and executables related to a
                                     single product.

 StringFileInfo\ProductVersion*     Version number of the product. Like
                                     StringFileInfo\FileVersion, this is
                                     redundant with the FILEVERSION
                                     attribute.
 *Required fields

Listing One


#include "resource.h"
#include "windows.h"

IDD_ABOUT DIALOG DISCARDABLE  0, 0, 185, 161
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "About..."
FONT 8, "MS Sans Serif"
BEGIN
    ICON            "",AB_ICON,159,7,18,20
    CTEXT           "",AB_PROGTITLE,0,26,191,11
    CTEXT           "",AB_COPYRIGHT,0,37,191,8
    LTEXT           "Text",AB_PROGVERSION,103,57,46,8
    LTEXT           "Program Version:",-1,41,57,59,8
    GROUPBOX        "",IDD_ABOUT,30,49,122,31
    LTEXT           "Text",AB_FILEVERSION,103,67,46,8
    LTEXT           "File Version:",-1,41,67,59,8
    LTEXT           "(mode)",AB_MODE,29,108,124,8
    LTEXT           "00%",AB_RESOURCES,93,128,60,8
    LTEXT           "3.xx",AB_WINVER,93,98,65,8
    LTEXT           "00%",AB_MEMORY,93,118,67,8
    LTEXT           "Windows version:",-1,30,98,62,8
    LTEXT           "Free memory:",-1,30,118,52,8
    LTEXT           "Free resources:",-1,30,128,52,8
    DEFPUSHBUTTON   "OK",IDOK,67,143,50,14
END

STRINGTABLE DISCARDABLE 
BEGIN
    MSG_STANDARD            "Standard Mode"
    MSG_ENHANCED            "Enhanced mode"
    MSG_ABOUT           "About "
END

#include "libver.rc"




Listing Two


#define MSG_ENHANCED                    22
#define MSG_STANDARD                    23
#define MSG_ABOUT           24
#define IDD_ABOUT                       102
#define AB_PROGVERSION                  107
#define AB_FILEVERSION                  109
#define AB_ICON                         110
#define AB_WINVER                       118
#define AB_MEMORY                       119
#define AB_RESOURCES                    120
#define AB_MODE                         121
#define AB_COPYRIGHT                    122
#define AB_PROGTITLE                    123



Listing Three


#include "ver.h"
VS_VERSION_INFO VERSIONINFO
        FILEVERSION VER_BINARY_FILEVERSION
        FILEOS VOS_DOS_WINDOWS16
        FILETYPE VER_FILETYPE
    BEGIN
        BLOCK "VarFileInfo"
           BEGIN
               VALUE "Translation", 0x0409, 1252
           END
        BLOCK "StringFileInfo"
           BEGIN
             VALUE "Comments", VER_COMMENTS
             VALUE "CompanyName", VER_COMPANYNAME
             VALUE "FileDescription", VER_FILEDESCRIPTION
             VALUE "FileVersion", VER_FILEVERSION
             VALUE "InternalName", VER_INTERNALNAME
             VALUE "LegalCopyRight", VER_LEGALCOPYRIGHT
             VALUE "OriginalFileName", VER_ORIGINALFILENAME
             VALUE "ProductName", VER_PRODUCTNAME
         VALUE "ProductVersion", VER_PRODUCTVERSION
          END
    END



Listing Four


/* These lines must appear in this order */
#define VER_FILEVERSION "1.117\0"
#define VER_BINARY_FILEVERSION 1, 117, 0, 0
/* --- */
#define VER_PRODUCTVERSION "1.20\0"
#define VER_BINARY_PRODUCTVERSION "1, 20, 0, 0


#define VER_FILETYPE VFT_APP
#define VER_COMMENTS "------------------\0"
#define VER_COMPANYNAME "FooBar Associates, Ltd.\0"
#define VER_FILEDESCRIPTION "Sample About Handler\0"
#define VER_INTERNALNAME "Sample About Handler\0"
#define VER_LEGALCOPYRIGHT ") 1993, Joseph M. Newcomer., All Rights Reserved\0"
#define VER_ORIGINALFILENAME "ABOUT.EXE\0"
#define VER_PRODUCTNAME "Sample About Handler\0"

#include "version.rch"



Listing Five


#define _STRICT_
#include <windows.h>
#include <windowsx.h>
#include <ver.h>
#include <stdlib.h>
#include <string.h>

#include "export.h"
#include "abouter.h"
#include "resource.h"
#include "verinf.h"

extern HINSTANCE hInst; // Library instance handle set by LibMain

/****************************************************************************
*                                SetAboutTitle
* Inputs:
*   HWND hDlg: Dialog window reference
*       LPVOID VerInfo: Version information packet
*   LPSTR: Query for field which will hold title
* Result: void
* Effect: Changes the title of the dialog
****************************************************************************/

void SetAboutTitle(HWND hDlg, LPVOID VerInfo, LPSTR query)
    {
     char title[256];
     LPSTR data = GetVersionString(VerInfo, query);

     LoadString(GetWindowInstance(hDlg), MSG_ABOUT, (LPSTR)title, sizeof(title));
     if(title[lstrlen(title) - 1] != ' ')
    lstrcat(title, " ");

     if(data != NULL)
    lstrcat(title, data);

     SetWindowText(hDlg, title);
    }

/****************************************************************************
*                             SetDlgVersionString
* Inputs:
*       HWND hDlg: Dialog whose control text is to be set

*  UINT id:   Control id in dialog whose text is to be set
*   LPVOID VerInfo: Version information block
*   LPSTR query: Query string
* Result: void
* Effect:  Obtains the text VerInfo.query and sets it in hDlg.id
****************************************************************************/

static void SetDlgVersionString(HWND hDlg, UINT id, LPVOID VerInfo, LPSTR query)
    {
     LPSTR data;

     data = GetVersionString(VerInfo, query);
     if(data != NULL && lstrlen(data) != 0)
        { /* has data */
     SetDlgItemText(hDlg, id, data);
    } /* has data */
    }

/****************************************************************************
*                                AboutVerInfo
* Inputs:
*       HWND hDlg: Dialog window handle
* Result: void
* Effect: Fills in the fields of the dialog from the VERSIONINFO blocks for
*   the program and data files
****************************************************************************/

static void AboutVerInfo(HWND hDlg)
    {
     HWND hWnd = GetParent(hDlg);  /* get main window */
     LPVOID ProgVerInfo = NULL;
     UINT winver = (UINT)GetVersion();

     /****************************************************************
     * In order to get the version info, we need the file name of the file
     * We use GetModuleFileName to get the name and from that derive
     * the version information
     ****************************************************************/

     ProgVerInfo = GetInstanceVersion(GetWindowInstance(hWnd));

     SetAboutTitle(hDlg, ProgVerInfo, "\\StringFileInfo\\ProductName");

     SetDlgVersionString(hDlg, AB_FILEVERSION, ProgVerInfo,
                    "\\StringFileInfo\\FileVersion");

     SetDlgVersionString(hDlg, AB_PROGVERSION, ProgVerInfo,
                    "\\StringFileInfo\\ProductVersion");

     SetDlgVersionString(hDlg, AB_PROGTITLE, ProgVerInfo,
                    "\\StringFileInfo\\FileDescription");


     SetDlgVersionString(hDlg, AB_COPYRIGHT, ProgVerInfo,
                    "\\StringFileInfo\\LegalCopyright");

     VerFree(ProgVerInfo);

    }

/****************************************************************************
*                                  OtherInfo
* Inputs: HWND hDlg: Handle to dialog window
* Result: void
* Effect: Sets the other information such as CPU type in the dialog box
* Notes: Uses WM_QUERYDRAGICON to get the icon of the application
****************************************************************************/

void OtherInfo(HWND hDlg)
{
     char mode[256];
     int cpu;
     DWORD flags = GetWinFlags();
     char * plus = "";
     char tmp[20];

     /* Figure out CPU type */
     if(flags & WF_CPU286)
    cpu = 286;
     else
     if(flags & WF_CPU386)
    cpu = 386;
     else
     if(flags & WF_CPU486)
    cpu = 486;
     else
    { /* bigger */
     cpu = 486;
     plus= ">";
    } /* bigger */
    wsprintf(mode, "%s%d ", (LPSTR)plus, cpu);

    /* Figure out mode */
    if(flags & WF_ENHANCED)
       LoadString(GetWindowInstance(hDlg), MSG_ENHANCED, 
                    (LPSTR)&mode[lstrlen(mode)], sizeof(mode) - lstrlen(mode));
    else
    if(flags & WF_STANDARD)
    LoadString(GetWindowInstance(hDlg), MSG_STANDARD, &mode[lstrlen(mode)],
                    sizeof(mode) - lstrlen(mode));
     
    SetDlgItemText(hDlg, AB_MODE, mode);


    wsprintf(tmp,"%d.%02d", LOBYTE(winver), HIBYTE(winver));
    SetDlgItemText(hDlg, AB_WINVER, tmp);

    { /* Free space */
     long freespace = GetFreeSpace(0);
     wsprintf(tmp,"%ldK", freespace/1024L);
     SetDlgItemText(hDlg, AB_MEMORY, tmp);
    } /* Free space */
    
    { /* Percentage free */
     UINT free = GetFreeSystemResources(GFSR_SYSTEMRESOURCES);
     wsprintf(tmp,"%d%%", free);
     SetDlgItemText(hDlg, AB_RESOURCES, tmp);
    } /* Percentage free */

    /* Get the icon, if there is one.  To do this, we do a QUERYDRAGICON
       request on the parent window.  It will either be handled by the parent
       and return a specific icon, or handled by DefWindowProc in the parent
       and return the class icon.  If for some reason it returns NULL, we
       don't set the icon (we may want to put a default icon in here later).
    */
    {
     HICON icon = SendMessage(GetParent(hDlg), WM_QUERYDRAGICON, 0, 0L);
     if(icon != NULL)
    Static_SetIcon(GetDlgItem(hDlg, AB_ICON), icon);
    }
     
}

/****************************************************************************
*                    AboutDlgProc
* Effect:  Processes messages for "About" dialog box
* Messages: WM_INITDIALOG - initialize dialog box
*           WM_COMMAND    - Input received
****************************************************************************/

BOOL DLLEXPORTPROC AboutDlgProc(HWND hDlg,unsigned message,
                                                       WORD wParam,LONG lParam)
    {
     // Load the LPABOUT pointer (it may be NULL)
     LPABOUT AboutData = (LPABOUT) GetWindowLong(hDlg, DWL_USER);

     switch (message) 
    { /* message */
     case WM_DESTROY:
         {
          if(AboutData != NULL)
             AboutData->hAbout = NULL;
         }
         return TRUE;

    case WM_INITDIALOG:
         // Set up our pointer so this window has a reference
         // to the AboutData block

         AboutData = (LPABOUT)lParam;
         SetWindowLong(hDlg, DWL_USER, lParam);

         AboutVerInfo(hDlg);
         void OtherInfo(HWND hDlg);
         return TRUE;

     case WM_COMMAND:
         if (wParam == IDOK
            || wParam == IDCANCEL) 
            {
             DestroyWindow(hDlg);
             return TRUE;
            }
         break;
     default:
         break;
    } /* message */
     return FALSE;
    }

/****************************************************************************
*                                    About
* Inputs: HWND hWnd: Parent window
*     LPABOUT AboutData: A reference to a block of data (which must be
*              initialized to zeros!) which holds transient status.
* Result: void
* Effect: Displays the 'about' dialog box
* Notes:  Because of realtime considerations, we don't want to create a
*     modal dialog box.
****************************************************************************/

void DLLEXPORTPROC About(HWND hWnd, LPABOUT AboutData)
    {
     if(hWnd == NULL)
    return;  // requires non-NULL parent

     if(AboutData->AboutProc == NULL)
        { /* create procinstance */
     AboutData->AboutProc = MakeProcInstance(AboutDlgProc, hInst);
    } /* create procinstance */

     if(AboutData->hAbout != NULL)
        { /* show it */
     SetWindowPos(AboutData->hAbout, HWND_TOP, 0, 0, 0, 0, 
                SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);

    return;
    } /* show it */

     AboutData->hAbout = CreateDialogParam(hInst, 
                    MAKEINTRESOURCE(IDD_ABOUT),
                    hWnd,
                    AboutData->AboutProc,
                    (LPARAM)AboutData);
    }



Listing Six


#define _STRICT_
#include <windows.h>
#include <windowsx.h>
#include <ver.h>
#include <stdlib.h>

#include "export.h"
#include "verinf.h"

/****************************************************************************
*                             GetInstanceVersion
* Inputs: HINSTANCE hInst: Handle to instance of object
* Result: void
* Effect:  Obtains the VersionInfo data and puts it in the buffer
****************************************************************************/

LPVOID DLLEXPORTPROC GetInstanceVersion(HINSTANCE hInst)
    {
     char fname[_MAX_PATH];
     int result;
     DWORD size;
     DWORD handle;
     LPVOID buffer;

     result = GetModuleFileName(hInst, fname, sizeof(fname));
     if(result == 0)
        { /* no file */
     return NULL;
    } /* no file */

     size = GetFileVersionInfoSize(fname, &handle);

     if(size == 0)
        { /* no data */
     return NULL;
    } /* no data */

     /* We do not expect size to exceed 65K */

     buffer = (LPVOID)GlobalAllocPtr(GHND, (size_t)size);
     if(buffer == NULL)
        { /* no memory */

    return NULL;
    } /* no memory */
     result = GetFileVersionInfo(fname, handle, (DWORD)(size_t)size, buffer);
     if(!result)
        { /* load failed */
     GlobalFreePtr(buffer);
     return NULL;
    } /* load failed */
     return buffer;
    }

/****************************************************************************
*                             GetVersionString
* Inputs: void * VerInfo: Version information block
*     LPCSTR query: Query string
* Result: char *    Obtains the text VerInfo.query
* Effect: 
****************************************************************************/

LPSTR DLLEXPORTPROC GetVersionString(LPVOID VerInfo, LPCSTR query)
    {
     UINT length;
     void FAR * data;

     if(VerInfo == NULL)
    return "<<no version info found>>";

     VerQueryValue(VerInfo, query, &data, &length);
     return data;
    }

/****************************************************************************
*                                   VerFree
* Inputs: LPVOID data: Data to free
* Result: void
* Effect: Frees the storage allocated for the data
****************************************************************************/

void DLLEXPORTPROC VerFree(LPVOID data)
    {
     if(data == NULL)
    return;

     GlobalFreePtr(data);
    }



Listing Seven


LPSTR DLLEXPORTPROC GetVersionString(LPVOID VerInfo, LPCSTR query);
LPVOID DLLEXPORTPROC GetInstanceVersion(HINSTANCE hInst);
void DLLEXPORTPROC VerFree(LPVOID VerInfo);



Listing Eight


#define _STRICT_
#include <windows.h>
#include "export.h"

HINSTANCE hInst;

BOOL WINAPI LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine)
    {
     hInst = hInstance; // Store this so members of library can find it
     if (wHeapSize > 0)
    UnlockData(0);
     return 1;
    }

int DLLEXPORTPROC WEP(int wParam)
    {
     return 1;
    }




Listing Nine


#ifndef EXPORTPROC
#define EXPORTPROC FAR PASCAL __export
#define DLLEXPORTPROC EXPORTPROC __loadds
#endif


Listing Ten


BEGIN { FS= "[\\.\\\\, ]+"}

#############################################################################
#      #define VER_FILEVERSION     "1.20\0"
#                 |                   |  |  |
#      $1     | $2            |$3|$4|$5
#############################################################################

/VER_FILEVERSION/ {
        FileVersion = $4 + 1
        printf("%s %s %s.%s\\%s\n", $1, $2, $3, FileVersion, $5)
        next }


#############################################################################
#   #define VER_BINARY_FILEVERSION  1, 20,  0,  0
#   |      |                         |   |   |   |   
#   $1     |$2                    |$3| $4| $5| $6|
#############################################################################

/VER_BINARY_FILEVERSION/ {
        printf("%s %s %s, %d, %s, %s\n", $1, $2, $3, FileVersion, $5, $6)
        next}


#############################################################################

  { printf("%s\n", $0) }



Listing Eleven


awk -f version.awk <libver.rc >$$$.tmp
copy $$$.tmp libver.rc
del $$$.tmp



Listing Twelve


typedef struct {
        FARPROC AboutProc;
        HWND hAbout;
           } ABOUT, FAR * LPABOUT;

void DLLEXPORTPROC About(HWND hWnd, LPABOUT AboutData);



Listing Thirteen


// At the head of the file
static ABOUT AboutData = {NULL, NULL};

//*****************************************************************************
// The main dispatch loop
     while(GetMessage(&msg, NULL, 0, 0))
        { /* dispatch */
      if(hAccel == NULL || !TranslateAccelerator(hWnd, hAccel, &msg))
         { /* not accelerator */
          // In case we have a modeless About... box we process it here
          // If we start doing more than that, we will use the general
          // registry mechanism (DDJ 18,5 (May 1993): "Modless Dialog Boxes
          // for Windows").

          if(AboutData.hAbout != NULL && 
                 IsDialogMessage(AboutData.hAbout,&msg))
          continue;

          TranslateMessage(&msg);
          DispatchMessage(&msg);
         } /* not accelerator */
     } /* dispatch */

//*****************************************************************************
// In the handler:

      case ID_ABOUT:
           About(hWnd, &AboutData);
           return 1;



Copyright © 1994, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.