Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Dynamic Dialog Boxes and C++


NOV92: DYNAMIC DIALOG BOXES AND C++

Bob is a professional C++ programmer with a PhD in mathematics and experience in business and manufacturing applications. He can be reached at 20 E. Scott, Chicago, IL 60610.


Recently, I had to write a Windows front end for a collection of database tables, with records consisting of varying numbers of strings of varying lengths. A dialog box was to be used to edit selected records of these tables. Because the record specification was not constant, and in fact could only be determined while the program was running, the dialog box had to be dynamic; that is, it had to create itself at run time, rather than be created from a resource file.

My solution starts with a published example of programming a dynamic dialog box in C, and consists of two C++ classes: DialogTemplate, a class for a Windows dialog-box template, and StringsBox, a class for a dialog box used to edit strings, as required by my application. The result demonstrates how even a complicated Windows object, once incorporated into a C++ class, can be easily used by an application program. It also demonstrates a new technique for associating an instance of a predefined Windows class--in this case a dialog box--with a C++ object.

The Dialog Template Class

To create a dialog box, it is first necessary to allocate a block of memory called a "dialog template," described in the Microsoft Windows Programmer's Reference (Microsoft Press, 1990) on pages 7-31 through 7-35, and to fill it with information defining the dialog box and its controls. Once the dialog template is prepared, a handle to it is passed to DialogBoxIndirect() or DialogBoxIndirectParam(), which realizes the information in the dialog template as a modal dialog box. In most applications, the two steps of preparing the dialog template and realizing the dialog box occur together in a single call to DialogBox(), which creates a dialog box specified as a resource. To create a dialog box dynamically, the application must follow these two steps explicitly; Jeffrey Richter shows how to do this in a C program in his Windows 3: A Developer's Guide (M&T Books, 1991) on pages 159-170.

The DialogTemplate class in Listing One(page 122) is a straightforward adaptation of Richter's technique, although the added power of C++ makes the process somewhat simpler. For example, Richter temporarily stores the size of the dialog template inside the template itself; with C++, it is more natural to define a field nbytes inside the DialogTemplate class instead.

The dialog template is built in steps, with one call to the class constructor, one call to SpecifyDialogBox(), and, optionally, one call to SpecifyFont(); omitting this last step causes the dialog box to use the system font. AddItem() is then called once for each control to be added. Finally, the dialog box is realized with a call to DialogTemplate:: DialogBoxIndirect() or DialogTemplate:: DialogBoxIndirectParam().

A pointer to a DialogTemplate object is included in the class definition of StringsBox, discussed next.

The StringsBox Class

Listing Two(page 122) contains StringsBox, a class used by the example program in Listing Three (page 124) to create the dialog box. To create the dialog box, an application first fills in an array of StringSpec structures to define the title and maximum length of each string. It then passes this array and the number of its elements to SetUp(), which creates a dialog template for the dialog box. Calling GetStrings() displays the dialog box.

StringsBox stores the strings for its edit windows contiguously in a block of memory; the public field hStrings is a handle to this block, which the application uses to initialize or access the strings. To display the dialog box with edit windows initialized, the application locks hStrings, fills the block, unlocks hStrings, and calls GetStrings() with parameter InitializedFlag set to True; to display the box with edit windows initially blank, the application calls GetStrings() with InitializedFlag set to False. GetStrings() returns True if the user selects OK and False if the user selects "Cancel;" on the return of True, the application can access the user's string values by locking hStrings, reading the block, and unlocking hStrings again. (Note that the hStrings block is allocated by StringsBox rather than by the application, and that its contents are only changed when the user selects OK or when altered by the application; successive calls to GetStrings() with InitializedFlag set to True will continue to display the dialog box with edit windows as last set by the user.)

Two Friend Functions and a Linked List

A universal problem in writing Windows applications in C++ is that, since Windows is written in C, a Windows procedure can be at best a friend or static member of a C++ class, rather than a true member. This means that, during run time, an instance of a Windows procedure does not automatically possess a this pointer to its corresponding C++ object. In the case of StringsBox, the dialog procedure StringsBox_DlgProc needs a pointer to the proper StringsBox object in order to access the strings in its hStrings block.

For Windows classes defined from scratch, the standard solution is to allocate enough extra bytes in the Window class to hold the this pointer. However, this technique will not work for predefined Windows classes, such as a dialog box.

StringsBox uses a different technique of a linked list of pointers to StringsBox objects with its first element embedded in the class as a static member; being static means that there will be one copy of this element, shared by all instances of the StringsBox class. Each object contains two additional, nonstatic pointers to the objects preceding and succeeding it in the list. The class constructor and destructor manage the list by resetting these pointers appropriately.

The lookup field for the linked list, the handle hDlg, can only be determined once the dialog box is displayed. To do this, DialogTemplate::DialogBox-IndirectParam() is sent the StringsBox object's this pointer as its last parameter, which allows the dialog-box procedure StringsBox_DlgProc() to recover it (but only during WM_INITDIALOG). StringsBox_DlgProc() then sets the hDlg field of the object referenced by this pointer. The lookup function GetStringsBox() is provided to let StringsBox_DlgProc() find the this pointer on subsequent calls; GetStringsBox() is also a friend rather than a member function, since it is called from a nonmember function.

Example Program

Listing Three contains dboxdemo.cpp, a simple program that demonstrates the StringsBox class. This program declares a global StringsBox object EditBox, which it displays when the user double-clicks on the main window. StringsBox::GetStrings() is called with the InitializedFlag set to True, so the contents of the dialog box's edit controls are preserved between calls. These strings are also displayed in the main window by the WM_PAINT command to demonstrate how the application accesses the contents of the StringsBox's hStrings block.

Conclusion

The two classes and example presented here demonstrate how C++ can make complex objects easily accessible from a Windows application; and the technique of embedding a linked list in the StringsBox class shows how to make the predefined Windows class for a dialog box available to a C++ object.

All of the code can be made more object oriented. The dboxdemo program in particular looks more like a C program than a C++ program; this is mainly to show how a library of C++ classes can add power even to a traditional-style Windows program.

It would be natural to expand the StringsBox class into a collection of many customized or dynamic dialogbox classes. In this case, it would be appropriate to embed the linked-list members in a base class, and make StringsBox and the other dialog-box classes into derived classes of this base class. However, it is probably not a good idea to include the dialog template in the base class. Leaving DialogTemplate as a separate class and having StringsBox contain a pointer to a DialogTemplate object allows a constructor of the form StringsBox(const StringsBox &x), which would let several StringsBox objects use the same dialog template.

DialogTemplate and StringsBox were written with the future possibility of a DLL in mind, so all pointers passed to public member functions are far. The second parameter of StringsBox::Setup() is a far pointer to a structure containing a near pointer as one of its fields; the near pointer is converted to a far pointer by the MakeFarPointer macro, defined at the top ofListing Two.



_DYNAMIC DIALOG BOXES IN C++_
by Robert Sardis


[LISTING ONE]
<a name="025d_000a">

/***** DialogTemplate class ******/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mem.h>

int ErrorMessage(LPSTR);
class DialogTemplate
{
   public:
      DialogTemplate(void);
      ~DialogTemplate(void);
      void SpecifyDlgBox(long Style, int x, int y, int width, int height,
     LPSTR MenuName, LPSTR ClassName, LPSTR CaptionText);
      void SpecifyFont(short int PointSize, LPSTR TypeFace);
      void AddItem(int x, int y, int width, int height, int ID, long Style,
     LPSTR Class, LPSTR Text, BYTE DataBytes, LPBYTE Data);
      int DialogBoxIndirect(HANDLE hInstance, HWND hWndParent,
     FARPROC lpDialogProc);
      int DialogBoxIndirectParam(HANDLE hInstance, HWND hWndParent,
     FARPROC lpDialogProc, DWORD dwInitParam);
   private:
      HANDLE hMem;
      int nBytes;
      int nItems;
};
typedef struct
{
   long Style;
   BYTE nItems;
   int x;
   int y;
   int width;
   int height;
   // char MenuName[];
   // char ClassName[];
   // char CaptionText[];
} DLGTEMPLATE, FAR *LPDLGTEMPLATE;
typedef struct
{
   short int PointSize;
   // char TypeFace[];
} FONTINFO, FAR *LPFONTINFO;
typedef struct
{
   int x;
   int y;
   int width;
   int height;
   int ID;
   long Style;
   // char Class[];
   // char Text[];
   // BYTE Info;
   // PTR Data;
} DLGITEMTEMPLATE, FAR *LPDLGITEMTEMPLATE;
DialogTemplate::DialogTemplate(void)
{
   nBytes = 0;
   nItems = 0;
   hMem = 0;
}
DialogTemplate::~DialogTemplate(void)
{
   GlobalFree(hMem);
}
void DialogTemplate::SpecifyDlgBox(long Style, int x, int y,
   int width, int height, LPSTR MenuName, LPSTR ClassName, LPSTR CaptionText)
{
   LPDLGTEMPLATE lpDlg;
   LPSTR lpText;
   int MenuNameBytes = lstrlen(MenuName) + 1;          // sizes of strings,
   int ClassNameBytes = lstrlen(ClassName) + 1;        // including null
   int CaptionTextBytes = lstrlen(CaptionText) + 1;    // terminator

   nBytes = sizeof(DLGTEMPLATE) + MenuNameBytes + ClassNameBytes +
      CaptionTextBytes;

   nItems = 0;
   GlobalFree(hMem);
   hMem = GlobalAlloc(GHND, nBytes);
   if (hMem == NULL)
   {
      ErrorMessage("Memory allocation error creating dialog template");
      return;
   }
   lpDlg = (LPDLGTEMPLATE) GlobalLock(hMem);        // add the "fixed size"
   lpDlg->Style = Style;                            // fields of the template
   lpDlg->nItems = 0;
   lpDlg->x = x;
   lpDlg->y = y;
   lpDlg->width = width;
   lpDlg->height = height;

   lpText = ((LPSTR) lpDlg) + sizeof (DLGTEMPLATE);   // append the three
   _fmemcpy(lpText, MenuName, MenuNameBytes);         // null-terminated text
   lpText += MenuNameBytes;                           // strings
   _fmemcpy(lpText, ClassName, ClassNameBytes);
   lpText += ClassNameBytes;
   _fmemcpy(lpText, CaptionText, CaptionTextBytes);
   GlobalUnlock(hMem);
}
void DialogTemplate::SpecifyFont(short int PointSize, LPSTR TypeFace)
{
   LPDLGTEMPLATE lpDlg;
   LPFONTINFO lpFont;
   LPSTR lpText;
   int OldnBytes = nBytes;
   int TypeFaceBytes = lstrlen(TypeFace) + 1;

   nBytes += sizeof(FONTINFO) + TypeFaceBytes;
   hMem = GlobalReAlloc(hMem, nBytes, GHND);
   if (hMem == NULL)
   {
      ErrorMessage("Memory allocation error adding dialog font");
      return;
   }
        // add DS_SETFONT to style to indicate font template is being added
   lpDlg = (LPDLGTEMPLATE) GlobalLock(hMem);
   lpDlg->Style |= DS_SETFONT;
                   // append font template to dialog template
   lpFont = (LPFONTINFO) (((LPSTR) lpDlg) + OldnBytes);
   lpFont->PointSize = PointSize;       // append fixed-size field of font info
   lpText = ((LPSTR) lpFont) + sizeof(LPFONTINFO);    // append null-termi-
   _fmemcpy(lpText, TypeFace, TypeFaceBytes);         // nated text string
   GlobalUnlock(hMem);
}
void DialogTemplate::AddItem(int x, int y, int width, int height,
   int ID, long Style, LPSTR Class, LPSTR Text, BYTE DataBytes, LPBYTE Data)
{
   LPDLGTEMPLATE lpDlg;
   LPDLGITEMTEMPLATE lpItem;
   LPSTR lpText;
   int OldnBytes = nBytes;
   int ClassBytes = lstrlen(Class) + 1;
   int TextBytes = lstrlen(Text) + 1;

   nBytes += sizeof(DLGITEMTEMPLATE) + ClassBytes + TextBytes + sizeof(BYTE)
      + DataBytes;
   hMem = GlobalReAlloc(hMem, nBytes, GHND);
   if (hMem == NULL)
   {
      ErrorMessage("Memory allocation error adding dialog item");
      return;
   }
   nItems++;
   lpDlg = (LPDLGTEMPLATE) GlobalLock(hMem);
   lpDlg->nItems = nItems;                           // update # items
                 // append item template to template block
   lpItem = (LPDLGITEMTEMPLATE) (((LPSTR) lpDlg) + OldnBytes);
   lpItem->x = x;                                   // append fixed-size
   lpItem->y = y;                                   // fields
   lpItem->width = width;
   lpItem->height = height;
   lpItem->ID = ID;
   lpItem->Style = Style | WS_CHILD | WS_VISIBLE;

   lpText = ((LPSTR) lpItem )+ sizeof(DLGITEMTEMPLATE);  // append variable
   _fmemcpy(lpText, Class, ClassBytes);                  // length portion:
   lpText += ClassBytes;                                 // two strings,
   _fmemcpy(lpText, Text, TextBytes);                    // one byte, and a
   lpText += TextBytes;                                  // data block.
   *lpText = DataBytes;
   lpText += sizeof(BYTE);
   _fmemcpy(lpText, Data, DataBytes);

   GlobalUnlock(hMem);
}
int DialogTemplate::DialogBoxIndirect(HANDLE hInstance, HWND hWndParent,
   FARPROC lpDialogProc)
{
   return ::DialogBoxIndirect(hInstance, hMem, hWndParent, lpDialogProc);
}
int DialogTemplate::DialogBoxIndirectParam(HANDLE hInstance, HWND hWndParent,
   FARPROC lpDialogProc, DWORD dwInitParam)
{
   return ::DialogBoxIndirectParam(hInstance, hMem, hWndParent,
   (FARPROC) lpDialogProc, dwInitParam);
}



<a name="025d_000b">
<a name="025d_000c">
[LISTING TWO]
<a name="025d_000c">

/****** StringsBox class ******/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mem.h>
#include <math.h>

int ErrorMessage(LPSTR);
#define max(A,B) ((A) > (B) ? A : B)

#define MakeFarPointer(N,F) \
  (sizeof(PSTR)==sizeof(LPSTR) ? (LONG)N : MAKELONG((WORD)N,HIWORD((LONG)F)))
         // macro to convert near pointer "N" to far -- "F" is a
         // reference far pointer, in the same segment as N. Used for DLLs
struct StringSpec
{
   char *Title;
   int MaxLength;
};
class StringsBox
{
   public:
      StringsBox(void);
      ~StringsBox(void);
      void SetUp(LPSTR Caption, struct StringSpec far *Items, int NumItems);
      int GetStrings(HANDLE hInstance, HWND hwnd, BOOL InitializedFlag);
      friend BOOL FAR PASCAL _export
     StringsBox_DlgProc(HWND hDlg, WORD msg, WORD wParam, LONG lParam);
      HANDLE hStrings;
   private:
      DialogTemplate *DT;
      int nItems;
      int *ItemLengths;
      static StringsBox *FirstBox;        // linked list parameters so the
      StringsBox *PrevBox;                // function DlgProc() can
      StringsBox *NextBox;                // determine the StringsBox
      HWND hDlg;                          // corresponding to the handle hDlg
      friend StringsBox *GetStringsBox(HWND hDlg);
      BOOL InitFlag;
};
StringsBox *StringsBox::FirstBox = NULL;   // initialize linked list
StringsBox::StringsBox(void)
{
   DT = new DialogTemplate;
   ItemLengths = NULL;
   nItems = 0;
   hStrings = 0;
   hDlg = 0;
   if (FirstBox == NULL)            // insert new object in the linked list--
   {                                // either at the beginning, if the list
      FirstBox = this;              // is empty ...
      PrevBox = NULL;
      NextBox = NULL;
   }
   else                             // or else at the end, after the first
   {                                // StringsBox whose NextBox pointer is
      StringsBox *pBox;             // NULL
      for (pBox = FirstBox; pBox->NextBox != NULL; pBox = pBox->NextBox);
      PrevBox = pBox;
      pBox->NextBox = this;
      NextBox = NULL;
   }
}
StringsBox::~StringsBox(void)
{
   delete DT;
   GlobalDiscard(hStrings);
   delete ItemLengths;
   if (this == FirstBox)                // take object out of linked list
      FirstBox = NextBox;
   if (PrevBox != NULL)
      PrevBox->NextBox = NextBox;
   if (NextBox != NULL)
      NextBox->PrevBox = PrevBox;
}
#define LINE_HEIGHT 2*cy
#define LINE_TEXT_HEIGHT 1.5*cy
#define HSPACE 2*cx
#define IDD_ITEM(A) A+101
void StringsBox::SetUp(LPSTR Caption,struct StringSpec far *Items,int NumItems)
{
   int i, y;
   LPSTR lpStringsBlock;
   int MaxTitleWidth = 0;
   int MaxEditWidth = 0;
   int hStringSize = 0;
   int cx = 4;                 // character average width and height,
   int cy = 8;                 // in dialog box units
   // get max dimensions of titles and edit windows -- hdc needed for call to
   // GetTextExtent(), tm needed to convert return value of GetTextExtent()
   // from logical units to dialog box units
   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
   TEXTMETRIC tm;
   HFONT hFont = GetStockObject(SYSTEM_FONT);
   SelectObject(hdc, hFont);
   GetTextMetrics(hdc, &tm);

   for (i = 0; i < NumItems; i++)
   {
      MaxTitleWidth = max(MaxTitleWidth, LOWORD(GetTextExtent(hdc,
        (LPSTR)MakeFarPointer(Items[i].Title, Items),
        lstrlen((LPSTR)MakeFarPointer(Items[i].Title, Items)))));
      MaxEditWidth  = max(MaxEditWidth, cx*(Items[i].MaxLength+1));
   }
   // convert MaxTitleWidth from logical units to dialog box units:
   // multiply by ratio of (ave char width in dialog box units) to
   // (ave char width in logical units), and round up to next integer
   MaxTitleWidth = ceil(((double)MaxTitleWidth * (double)cx) /
      (double)tm.tmAveCharWidth);
   DeleteDC(hdc);
                 // calculate locations of controls
   int ItemTitleX  = HSPACE;
   int ItemEditX   = ItemTitleX + MaxTitleWidth + HSPACE;
   int FirstItemY  = LINE_HEIGHT;
   int ButtonWidth = 10*cx;
   int ButtonY     = FirstItemY +  NumItems*LINE_HEIGHT + LINE_HEIGHT;

   int BoxX        = 1;
   int BoxY        = 1;
   int BoxWidth    =
          max(MaxTitleWidth + MaxEditWidth + HSPACE + 2*HSPACE,
         2*ButtonWidth + 4*HSPACE);
   int BoxHeight   = ButtonY + LINE_HEIGHT;
   int CenterX = BoxWidth / 2;
               // set up dialog template
   DT->SpecifyDlgBox(WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_POPUP,
      BoxX, BoxY, BoxWidth, BoxHeight, "", "", Caption);
   // DT->SpecifyFont() not called -- use default font
   for (i = 0; i < NumItems; i++)
   {
      y = FirstItemY + i*LINE_HEIGHT;
      // Item title
      DT->AddItem(ItemTitleX, y, MaxTitleWidth, LINE_TEXT_HEIGHT,
     -1, SS_LEFT | WS_GROUP, "STATIC",
    (LPSTR) MakeFarPointer(Items[i].Title, Items), 0, NULL);
      // Item edit
      DT->AddItem(ItemEditX, y, cx*(Items[i].MaxLength+1), LINE_TEXT_HEIGHT,
    IDD_ITEM(i), ES_LEFT | ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP, "EDIT",
    "", 0, NULL);
   }
   // 'OK' button
   DT->AddItem(CenterX - ButtonWidth - HSPACE, ButtonY, ButtonWidth,
      LINE_TEXT_HEIGHT, IDOK, BS_DEFPUSHBUTTON | WS_TABSTOP | WS_GROUP,
      "BUTTON", "OK", 0, NULL);
   // 'CANCEL' button
   DT->AddItem(CenterX + HSPACE, ButtonY, ButtonWidth,
      LINE_TEXT_HEIGHT, IDCANCEL, BS_PUSHBUTTON | WS_TABSTOP | WS_GROUP,
      "BUTTON", "Cancel", 0, NULL);
                 // set class parameters
   nItems = NumItems;
   if (ItemLengths != NULL)
      delete ItemLengths;
   ItemLengths = new int[nItems];
   for (i = 0; i < nItems; i++)
      hStringSize += (ItemLengths[i] = Items[i].MaxLength);
              // allocate hStrings block and initialize it to nulls
   if (hStrings == 0)
      hStrings = GlobalAlloc(GHND, hStringSize);
   else
      hStrings = GlobalReAlloc(hStrings, hStringSize, GHND);
   lpStringsBlock = GlobalLock(hStrings);
   _fmemset(lpStringsBlock, '\0', hStringSize);
   GlobalUnlock(hStrings);
}
int StringsBox::GetStrings(HANDLE hInstance, HWND hwnd, BOOL InitializedFlag)
{
   FARPROC lpDialogProc;
   int RetVal;
   InitFlag = InitializedFlag;

   lpDialogProc = MakeProcInstance((FARPROC) StringsBox_DlgProc, hInstance);
                  // pass "this" pointer so dialog box procedure
                  // can recover it during WM_INITDIALOG
   RetVal = DT->DialogBoxIndirectParam(hInstance, hwnd, (FARPROC)lpDialogProc,
      (DWORD) this);
   FreeProcInstance(lpDialogProc);
   return RetVal;
}
BOOL FAR PASCAL _export
StringsBox_DlgProc(HWND hDlg, WORD msg, WORD wParam, LONG lParam)
{
   int i;
   LPSTR lpText;
        // find "this" pointer of corresponding StringsBox object --
                // on WM_INITDIALOG, it is passed as lParam; on subsequent
                // calls, use GetStringsBox() to look it up in the linked list
   StringsBox *pBox =
      (msg == WM_INITDIALOG ? (StringsBox *) lParam : GetStringsBox(hDlg));
   switch(msg)
   {
      case WM_INITDIALOG:
     pBox->hDlg = hDlg;    // insert "this" pointer in linked list -- set
                   // Box->hDlg so GetStringsBox() can find it on
                   // subsequent calls from this dialog procedure
     if (pBox->InitFlag)
     {
        lpText = GlobalLock(pBox->hStrings);
        for (i = 0; i < pBox->nItems; i++)
        {
           SetDlgItemText(hDlg, IDD_ITEM(i), lpText);
           lpText += pBox->ItemLengths[i];
        }
        GlobalUnlock(pBox->hStrings);
     }
     return TRUE;
      case WM_COMMAND:
     switch(wParam)
     {
        case IDOK:
           lpText = GlobalLock(pBox->hStrings);
           for (i = 0; i < pBox->nItems; i++)
           {
          GetDlgItemText(hDlg, IDD_ITEM(i), lpText,
             pBox->ItemLengths[i]);
          lpText += pBox->ItemLengths[i];
           }
           GlobalUnlock(pBox->hStrings);
           pBox->hDlg = 0;                  // set pBox->hDlg back to
                        // an invalid value
           EndDialog(hDlg, TRUE);
           return TRUE;
        case IDCANCEL:
           pBox->hDlg = 0;
           EndDialog(hDlg, FALSE);
           return TRUE;
     }
     break;
   }
   return FALSE;
}
StringsBox * GetStringsBox(HWND hDlg)
{
   StringsBox *pBox;
   for (pBox = StringsBox::FirstBox; pBox != NULL; pBox = pBox->NextBox)
   {
      if (pBox->hDlg == hDlg)
     return pBox;
   }
   return NULL;
}


<a name="025d_000d">
<a name="025d_000e">
[LISTING THREE]
<a name="025d_000e">

/***** dboxdemo.cpp -- C++ dynamic dialog box example *****/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "wbdialog.hpp"    // header file containing DialogTemplate and
               // StringsBox class definitions
StringsBox EditBox;
struct StringSpec StringFieldSpec[] =        // field spec for EditBox
{
   "Name",      20,
   "Address",   30,
   "Telephone", 15,
};
int NumStrings = sizeof(StringFieldSpec) / sizeof(struct StringSpec);
struct
{
   HANDLE hInstance;
} InsGlobs;
struct
{
   HWND hwnd;
   short cxChar;
   short cyChar;
} WndGlobs;
long FAR PASCAL _export WndProc(HWND, WORD, WORD, LONG);
int ErrorMessage(char *msg);
char szAppName[] = "dboxdemo";
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
   LPSTR lpszCmdLine, int nCmdShow)
{
   HWND hwnd;
   MSG msg;
   WNDCLASS wndclass;
   if(!hPrevInstance)
   {
      wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
      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 = GetStockObject(WHITE_BRUSH);
      wndclass.lpszMenuName = szAppName;
      wndclass.lpszClassName = szAppName;
      RegisterClass(&wndclass);
   }
   hwnd = CreateWindow(szAppName, "C++ Dynamic Dialog Box Demo",
      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
      CW_USEDEFAULT, CW_USEDEFAULT,
      CW_USEDEFAULT, CW_USEDEFAULT,
      NULL, NULL, hInstance, NULL);
   WndGlobs.hwnd = hwnd;
   InsGlobs.hInstance = hInstance;
   ShowWindow(WndGlobs.hwnd, nCmdShow);
   UpdateWindow(WndGlobs.hwnd);
   while (GetMessage(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
   return msg.wParam;
}
long FAR PASCAL _export WndProc(HWND hwnd, WORD message, WORD wParam,
   LONG lParam)
{
   HDC hdc;
   TEXTMETRIC tm;
   PAINTSTRUCT ps;
   LPSTR lpString;
   int i;
   switch (message)
   {
      case WM_CREATE:
     hdc = GetDC(hwnd);
     GetTextMetrics(hdc, &tm);
     WndGlobs.cxChar = tm.tmAveCharWidth;
     WndGlobs.cyChar = tm.tmHeight + tm.tmExternalLeading;
     ReleaseDC(hwnd, hdc);
     EditBox.SetUp("Edit Fields", StringFieldSpec, NumStrings);
     return 0;
      case WM_LBUTTONDBLCLK:       // display dialog box on double-click
                   // StringsBox::GetStrings() returns TRUE if
                   // user selects OK button; in this case,
                   // cause window to be repainted to display
                   // latest contents of EditBox.hStrings block
     if ((EditBox.GetStrings(InsGlobs.hInstance, WndGlobs.hwnd, TRUE)
        == TRUE))
        InvalidateRect(WndGlobs.hwnd, NULL, TRUE);
     return 0;
      case WM_PAINT:
     hdc = BeginPaint(WndGlobs.hwnd, &ps);
     if (EditBox.hStrings != 0)
     {
        lpString = GlobalLock(EditBox.hStrings);
        for (i = 0; i < NumStrings; i++)
        {
           TextOut(hdc, 0, i*WndGlobs.cyChar, lpString,
          _fstrlen(lpString));
           lpString += StringFieldSpec[i].MaxLength;
        }
        GlobalUnlock(EditBox.hStrings);
     }
     EndPaint(WndGlobs.hwnd, &ps);
     return 0;
      case WM_DESTROY:
     PostQuitMessage(0);
     return 0;
   }
   return DefWindowProc(hwnd, message, wParam, lParam);
}
int ErrorMessage(LPSTR msg)
{
   MessageBox(WndGlobs.hwnd, msg, szAppName, MB_ICONINFORMATION|MB_OK);
   return 0;
}









Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.