History

  • The Old New Thing

    The evolution of dialog templates - Summary

    • 4 Comments

    As promised, here's the tabular version of the evolution of dialog templates. It doesn't contain any new information, but it may give you a little glimpse into how things evolved to see the small changes highlighted against each other.

    16-bit classic 32-bit classic
    16-bit extended 32-bit extended
    Header style
    8-bit item count
    coordinates
    extended style, style
    16-bit item count
    coordinates
    help ID, extended style, style
    8-bit item count
    coordinates
    help ID, extended style, style
    16-bit item count
    coordinates
    Menu ASCIIZ or ordinal UNICODEZ or ordinal
    ASCIIZ or ordinal UNICODEZ or ordinal
    Class ASCIIZ or ordinal UNICODEZ or ordinal
    ASCIIZ or ordinal UNICODEZ or ordinal
    Caption ASCIIZ UNICODEZ
    ASCIIZ UNICODEZ
    Font
    (if DS_SETFONT)
    size
    ASCIIZ font name
    size
    UNICODEZ font name
    size, weight, italic, charset
    ASCIIZ font name
    size, weight, italic, charset
    UNICODEZ font name
    Item template alignment BYTE DWORD
    BYTE DWORD
    Item templates size, position
    16-bit ID
    style
    class, ASCIIZ text/ordinal
    8-bit extra data
    size, position
    16-bit ID
    extended style, style
    class, UNICODEZ text/ordinal
    16-bit extra data
    size, position
    32-bit ID
    help ID, extended style, style
    class, ASCIIZ text/ordinal
    16-bit extra data
    size, position
    32-bit ID
    help ID, extended style, style
    class, UNICODEZ text/ordinal
    16-bit extra data
  • The Old New Thing

    The evolution of dialog templates - 32-bit Extended Templates

    • 10 Comments

    At last we reach our goal, the 32-bit extended dialog template, known in resource files as DIALOGEX. I will celebrate this with a gratuitous commutative diagram:

    16-bit DIALOG
    32
    32-bit DIALOG
    EX EX
    16-bit DIALOGEX
    32
    32-bit DIALOGEX

    Isn't that special.

    Okay, so let's get going. The 32-bit extended dialog template is the 32-bit version of the 16-bit extended dialog template, so you won't see any real surprises if you've been following along.

    Once again, we start with a header, this time the 32-bit extended header.

    WORD  wDlgVer;      // version number - always 1
    WORD  wSignature;   // always 0xFFFF
    DWORD dwHelpID;     // help ID
    DWORD dwExStyle;    // window extended style
    DWORD dwStyle;      // dialog style
    WORD  cItems;       // number of controls in this dialog
    WORD  x;            // x-coordinate
    WORD  y;            // y-coordinate
    WORD  cx;           // width
    WORD  cy;           // height
    

    The first two fields serve exactly the same purpose as the 16-bit extended template: They identify this header as an extended dialog template.

    As before, the next two fields are new. The help identifier is attached to the dialog via the SetWindowContextHelpId function, and the extended dialog style shouldn't be a surprise.

    You know the drill: Next come the three strings for the menu, class, and dialog title. Since this is the 32-bit template, the strings are Unicode.

    As with the 16-bit extended template, the optional custom font consists of a little more information than the non-extended template:

    WORD wPoint;        // point size
    WORD wWeight;       // font weight
    BYTE bItalic;       // 1 if italic, 0 if not
    BYTE bCharSet;      // character set
    WCHAR szFontName[]; // variable-length
    

    As before, the point, weight, italic and character set are all passed to the CreateFont function.

    After the header come the dialog item templates, each of which must be aligned on a DWORD boundary.

    DWORD dwHelpID;     // help identifier
    DWORD dwExStyle;    // window extended style
    DWORD dwStyle;      // window style
    WORD  x;            // x-coordinate (DLUs)
    WORD  y;            // y-coordinate (DLUs)
    WORD  cx;           // width (DLUs)
    WORD  cy;           // height (DLUs)
    DWORD dwID;         // control ID
    WCHAR szClassName[];// variable-length (possibly ordinal)
    WCHAR szText[];     // variable-length (possibly ordinal)
    WORD  cbExtra;      // amount of extra data
    BYTE  rgbExtra[cbExtra]; // extra data follows (usually none)
    

    The changes here:

    • New dwHelpID and dwExStyle fields.
    • The dwStyle field has moved.
    • The control ID has grown to a 32-bit value.

    Not that expanding the control ID to a 32-bit value helps any, because WM_COMMAND and similar messages still use a 16-bit value to pass the control ID. So in practice, you can't use a value greater than 16 bits. (Well, you can always ignore the control ID field and retrieve the full 32-bit control ID via the GetDlgCtrlID function, assuming you have the window handle of the control available.)

    And that's all there is to it.

    Here's the customary annotated hex dump.

    0000  01 00 FF FF 00 00 00 00-00 00 00 00 C4 00 C8 80  ................
    0010  0B 00 24 00 2C 00 E6 00-5E 00 00 00 00 00 52 00  ..$.,...^.....R.
    0020  65 00 70 00 6C 00 61 00-63 00 65 00 00 00 08 00  e.p.l.a.c.e.....
    0030  00 00 00 01 4D 00 53 00-20 00 53 00 68 00 65 00  ....M.S. .S.h.e.
    0040  6C 00 6C 00 20 00 44 00-6C 00 67 00 00 00 00 00  l.l. .D.l.g.....
    0050  00 00 00 00 00 00 00 00-00 00 02 50 04 00 09 00  ...........P....
    0060  30 00 08 00 FF FF FF FF-FF FF 82 00 46 00 69 00  0...........F.i.
    0070  26 00 6E 00 64 00 20 00-57 00 68 00 61 00 74 00  &.n.d. .W.h.a.t.
    0080  3A 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  :...............
    0090  80 00 83 50 36 00 07 00-72 00 0C 00 80 04 00 00  ...P6...r.......
    00A0  FF FF 81 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00B0  00 00 02 50 04 00 1A 00-30 00 08 00 FF FF FF FF  ...P....0.......
    00C0  FF FF 82 00 52 00 65 00-26 00 70 00 6C 00 61 00  ....R.e.&.p.l.a.
    00D0  63 00 65 00 20 00 77 00-69 00 74 00 68 00 3A 00  c.e. .w.i.t.h.:.
    00E0  00 00 00 00 00 00 00 00-00 00 00 00 80 00 83 50  ...............P
    00F0  36 00 18 00 72 00 0C 00-81 04 00 00 FF FF 81 00  6...r...........
    0100  00 00 00 00 00 00 00 00-00 00 00 00 03 00 03 50  ...............P
    0110  05 00 2E 00 68 00 0C 00-10 04 00 00 FF FF 80 00  ....h...........
    0120  4D 00 61 00 74 00 63 00-68 00 20 00 26 00 77 00  M.a.t.c.h. .&.w.
    0130  68 00 6F 00 6C 00 65 00-20 00 77 00 6F 00 72 00  h.o.l.e. .w.o.r.
    0140  64 00 20 00 6F 00 6E 00-6C 00 79 00 00 00 00 00  d. .o.n.l.y.....
    0150  00 00 00 00 00 00 00 00-03 00 01 50 05 00 3E 00  ...........P..>.
    0160  3B 00 0C 00 11 04 00 00-FF FF 80 00 4D 00 61 00  ;...........M.a.
    0170  74 00 63 00 68 00 20 00-26 00 63 00 61 00 73 00  t.c.h. .&.c.a.s.
    0180  65 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  e...............
    0190  01 00 03 50 AE 00 04 00-32 00 0E 00 01 00 00 00  ...P....2.......
    01A0  FF FF 80 00 26 00 46 00-69 00 6E 00 64 00 20 00  ....&.F.i.n.d. .
    01B0  4E 00 65 00 78 00 74 00-00 00 00 00 00 00 00 00  N.e.x.t.........
    01C0  00 00 00 00 00 00 01 50-AE 00 15 00 32 00 0E 00  .......P....2...
    01D0  00 04 00 00 FF FF 80 00-26 00 52 00 65 00 70 00  ........&.R.e.p.
    01E0  6C 00 61 00 63 00 65 00-00 00 00 00 00 00 00 00  l.a.c.e.........
    01F0  00 00 00 00 00 00 01 50-AE 00 26 00 32 00 0E 00  .......P..&.2...
    0200  01 04 00 00 FF FF 80 00-52 00 65 00 70 00 6C 00  ........R.e.p.l.
    0210  61 00 63 00 65 00 20 00-26 00 41 00 6C 00 6C 00  a.c.e. .&.A.l.l.
    0220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 01 50  ...............P
    0230  AE 00 37 00 32 00 0E 00-02 00 00 00 FF FF 80 00  ..7.2...........
    0240  43 00 61 00 6E 00 63 00-65 00 6C 00 00 00 00 00  C.a.n.c.e.l.....
    0250  00 00 00 00 00 00 00 00-00 00 01 50 AE 00 4B 00  ...........P..K.
    0260  32 00 0E 00 0E 04 00 00-FF FF 80 00 26 00 48 00  2...........&.H.
    0270  65 00 6C 00 70 00 00 00-00 00                    e.l.p.....
    

    As always, the header comes first.

    0000  01 00         // wVersion
    0002  FF FF         // wSignature
    0004  00 00 00 00   // dwHelpID
    0008  00 00 00 00   // dwExStyle
    000C  C4 00 C8 80   // dwStyle
    0010  0B 00         // cItems
    0012  24 00 2C 00   // x, y
    0016  E6 00 5E 00   // cx, cy
    

    Nothing surprising here; you've seen it before.

    wVersion = 0x0001 = 1
    wSignature = 0xFFFF
    dwHelpID = 0x00000000 = 0
    dwExStyle = 0x00000000 = 0
    dwStyle = 0x80C800C4 = WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK
    cItems = 0x000B = 11
    x = 0x0024 = 36
    y = 0x002C = 44
    cx = 0x00E6 = 230
    cy = 0x005E = 94

    After the header come the menu name, class name, and dialog title:

    001A  00 00         // no menu
    001C  00 00         // default dialog class
    001E  52 00 65 00 70 00 6C 00 61 00 63 00
          65 00 00 00   // "Replace"
    

    And since DS_SETFONT is set in the dialog style, font information comes next. Notice that the additional font characteristics are included in the extended template.

    002E  08 00         // wSize = 8
    0030  00 00         // wWeight = 0x0000 = FW_DONTCARE
    0032  00            // Italic
    0033  01            // Character set = 0x01 = DEFAULT_CHARSET
    0034  4D 00 53 00 20 00 53 00 68 00 65 00 6C 00
          6C 00 20 00 44 00 6C 00 67 00 00 00
                        // "MS Shell Dlg"
    

    You've seen this all before. Here come the extended dialog item templates. Remember, these must be DWORD-aligned.

    004E  00 00         // padding to achieve DWORD alignment
    
    // First control
    0050  00 00 00 00   // dwHelpID
    0054  00 00 00 00   // dwExStyle
    0058  00 00 02 50   // dwStyle
    005C  04 00 09 00   // x, y
    0060  30 00 08 00   // cx, cy
    0064  FF FF FF FF   // wID
    0068  FF FF 82 00   // szClass = ordinal 0x0082 = "static"
    006C  46 00 69 00
    0070  26 00 6E 00 64 00 20 00 77 00 68 00 61 00 74 00
    0080  3A 00 00 00   // "Fi&nd what:"
    0084  00 00         // no extra data
    0086  00 00         // padding to achieve DWORD alignment
    
    // Second control
    
    0088  00 00 00 00   // dwHelpID
    008C  00 00 00 00   // dwExStyle
    0090  80 00 83 50   // dwStyle
    0094  36 00 07 00   // x, y
    0098  72 00 0C 00   // cx, cy
    009C  80 04 00 00   // wID
    00A0  FF FF 81 00   // "edit"
    00A4  00 00         // ""
    00A6  00 00         // no extra data
    
    // Third control
    00A8  00 00 00 00   // dwHelpID
    00AC  00 00 00 00   // dwExStyle
    00B0  00 00 02 50   // dwStyle
    00B4  04 00 1A 00   // x, y
    00B8  30 00 08 00   // cx, cy
    00BC  FF FF FF FF   // wID
    00C0  FF FF 82 00   // "static"
    00C4  52 00 65 00 26 00 70 00 6C 00 61 00
    00D0  63 00 65 00 20 00 77 00 69 00 74 00 68 00 3A 00
    00E0  00 00         // "Re&place with:"
    00E2  00 00         // no extra data
    
    // Fourth control
    00E4  00 00 00 00   // dwHelpID
    00E8  00 00 00 00   // dwExStyle
    00EC  80 00 83 50   // dwStyle
    00F0  36 00 18 00   // x, y
    00F4  72 00 0C 00   // cx, cy
    00F8  81 04 00 00   // wID
    00FC  FF FF 81 00   // "edit"
    0100  00 00         // ""
    0102  00 00         // no extra data
    
    // Fifth control
    0104  00 00 00 00   // dwHelpID
    0108  00 00 00 00   // dwExStyle
    010C  03 00 03 50   // dwStyle
    0110  05 00 2E 00   // x, y
    0114  68 00 0C 00   // cx, cy
    0118  10 04 00 00   // wID
    011C  FF FF 80 00   // "button"
    0120  4D 00 61 00 74 00 63 00 68 00 20 00 26 00 77 00
    0130  68 00 6F 00 6C 00 65 00 20 00 77 00 6F 00 72 00
    0140  64 00 20 00 6F 00 6E 00 6C 00 79 00 00 00
                        // "Match &whole word only"
    014E  00 00         // no extra data
    
    // Sixth control
    0150  00 00 00 00   // dwHelpID
    0154  00 00 00 00   // dwExStyle
    0158  03 00 01 50   // dwStyle
    015C  05 00 3E 00   // x, y
    0160  3B 00 0C 00   // cx, cy
    0164  11 04 00 00   // wID
    0168  FF FF 80 00   // "button"
    016C  4D 00 61 00
    0170  74 00 63 00 68 00 20 00 26 00 63 00 61 00 73 00
    0180  65 00 00 00   // "Match &case"
    0184  00 00         // no extra data
    0186  00 00         // padding to achieve DWORD alignment
    
    // Seventh control
    0188  00 00 00 00   // dwHelpID
    018C  00 00 00 00   // dwExStyle
    0190  01 00 03 50   // dwStyle
    0194  AE 00 04 00   // x, y
    0198  32 00 0E 00   // cx, cy
    019C  01 00 00 00   // wID
    01A0  FF FF 80 00   // "button"
    01A4  26 00 46 00 69 00 6E 00 64 00 20 00
    01B0  4E 00 65 00 78 00 74 00 00 00
                        // "&Find Next"
    01BA  00 00         // no extra data
    
    // Eighth control
    01BC  00 00 00 00   // dwHelpID
    01C0  00 00 00 00   // dwExStyle
    01C4  00 00 03 50   // dwStyle
    01C8  AE 00 15 00   // x, y
    01CC  32 00 0E 00   // cx, cy
    01D0  00 04 00 00   // wID
    01D4  FF FF 80 00   // "button"
    01D8  26 00 52 00 65 00 70 00
                        // "&Replace"
    01E0  6C 00 61 00 63 00 65 00 00 00
    01EA  00 00         // no extra data
    
    // Ninth control
    01EC  00 00 00 00   // dwHelpID
    01F0  00 00 00 00   // dwExStyle
    01F4  00 00 03 50   // dwStyle
    01F8  AE 00 26 00   // x, y
    01FC  32 00 0E 00   // cx, cy
    0200  01 04 00 00   // wID
    0204  FF FF 80 00   // "button"
    0208  52 00 65 00 70 00 6C 00
    0210  61 00 63 00 65 00 20 00 26 00 41 00 6C 00 6C 00
    0220  00 00         // "Replace &All"
    0222  00 00         // no extra data
    
    // Tenth control
    0224  00 00 00 00   // dwHelpID
    0228  00 00 00 00   // dwExStyle
    022C  00 00 01 50   // dwStyle
    0230  AE 00 37 00   // x, y
    0234  32 00 0E 00   // cx, cy
    0238  02 00 00 00   // wID
    023C  FF FF 80 00   // "button"
    0240  43 00 61 00 6E 00 63 00 65 00 6C 00 00 00
                        // "Cancel"
    024E  00 00         // no extra data
    
    // Eleventh control
    0250  00 00 00 00   // dwHelpID
    0254  00 00 00 00   // dwExStyle
    0258  00 00 03 50   // dwStyle
    025C  AE 00 4B 00   // x, y
    0260  32 00 0E 00   // cx, cy
    0264  0E 04 00 00   // wID
    0268  FF FF 80 00   // "button"
    026C  26 00 48 00
    0270  65 00 6C 00 70 00 00 00
                        // "&Help"
    0278  00 00         // no extra data
    

    The original dialog template is, of course, the one you're probably sick of by now. The only change is that the DIALOG keyword has been changed to DIALOGEX.

    DIALOGEX 36, 44, 230, 94
    ...
    

    So that's the last of the dialog template formats. Tomrrow, a chart that tries to summarize everything at a glance.

  • The Old New Thing

    The evolution of dialog templates - 16-bit Extended Templates

    • 4 Comments

    The next step in the evolution of dialog templates is the Extended Dialog or DIALOGEX. First, let's look at the 16-bit version.

    The 16-bit extended dialog template is purely historical. The only operating systems to support it were the Windows 95/98/Me series. It is interesting only as a missing link in the evolution towards the 32-bit extended dialog template.

    The basic rules are the same as for the non-extended template. The extended dialog template starts off with a different header:

    WORD  wDlgVer;      // version number - always 1
    WORD  wSignature;   // always 0xFFFF
    DWORD dwHelpID;     // help ID
    DWORD dwExStyle;    // window extended style
    DWORD dwStyle;      // dialog style
    BYTE  cItems;       // number of controls in this dialog
    WORD  x;            // x-coordinate
    WORD  y;            // y-coordinate
    WORD  cx;           // width
    WORD  cy;           // height
    

    The first two fields specify a version number (so far, only version 1 extended dialogs have been defined), and a signature value 0xFFFF which allows this template to be distinguished from a non-extended dialog template.

    Next come two new fields. The help identifier is an arbitrary 32-bit value that you can retrieve from the dialog later with the GetWindowContextHelpId function. The extended dialog style you've seen before.

    As before, after the header come the strings. First comes the menu, then the class, then dialog title, all encoded the same way as with the non-extended template.

    If the DS_SETFONT style is set, then a custom font exists in the template. The format of the font information is slightly different for extended templates. In classic templates, all you get is a WORD of point size and a font name. But in the extended template, the font information is a little richer:

    WORD wPoint;        // point size
    WORD wWeight;       // font weight
    BYTE bItalic;       // 1 if italic, 0 if not
    BYTE bCharSet;      // character set (see CreateFont)
    CHAR szFontName[];  // variable-length
    

    New fields are the weight, character set, and whether the font is italic.

    After the header come the dialog item templates, each of which looks like this:

    DWORD dwHelpID;     // help identifier
    DWORD dwExStyle;    // window extended style
    DWORD dwStyle;      // window style
    WORD  x;            // x-coordinate (DLUs)
    WORD  y;            // y-coordinate (DLUs)
    WORD  cx;           // width (DLUs)
    WORD  cy;           // height (DLUs)
    DWORD wID;          // control ID
    CHAR  szClassName[];// variable-length (possibly ordinal)
    CHAR  szText[];     // variable-length (possibly ordinal)
    WORD  cbExtra;      // amount of extra data
    BYTE  rgbExtra[cbExtra]; // extra data follows (usually none)
    

    This takes the classic item template and adds the following:

    • New dwHelpID and dwExStyle fields.
    • dwStyle field moved.
    • control ID expanded to DWORD.
    • cbExtra expanded to WORD.

    Not that expanding the control ID to a 32-bit value accomplishes much in 16-bit Windows, but it's there nonetheless.

    And that's all.

    Now the obligatory annotated hex dump.

    0000  01 00 FF FF 00 00 00 00-00 00 00 00 C4 00 C8 80  ................
    0010  0B 24 00 2C 00 E6 00 5E-00 00 00 52 65 70 6C 61  .$.,...^...Repla
    0020  63 65 00 08 00 90 01 00-00 4D 53 20 53 68 65 6C  ce.......MS Shel
    0030  20 44 6C 67 00 00 00 00-00 00 00 00 00 00 00 02   Dlg............
    0040  50 04 00 09 00 30 00 08-00 FF FF FF FF 82 46 69  P....0........Fi
    0050  26 6E 64 20 77 68 61 74-3A 00 00 00 00 00 00 00  &nd what:.......
    0060  00 00 00 00 80 00 83 50-36 00 07 00 72 00 0C 00  .......P6...r...
    0070  80 04 00 00 81 00 00 00-00 00 00 00 00 00 00 00  ................
    0080  00 00 02 50 04 00 1A 00-30 00 08 00 FF FF FF FF  ...P....0.......
    0090  82 52 65 26 70 6C 61 63-65 20 77 69 74 68 3A 00  .Re&place with:.
    00A0  00 00 00 00 00 00 00 00-00 00 80 00 83 50 36 00  .............P6.
    00B0  18 00 72 00 0C 00 81 04-00 00 81 00 00 00 00 00  ..r.............
    00C0  00 00 00 00 00 00 03 00-03 50 05 00 2E 00 68 00  .........P....h.
    00D0  0C 00 10 04 00 00 80 4D-61 74 63 68 20 26 77 68  .......Match &wh
    00E0  6F 6C 65 20 77 6F 72 64-20 6F 6E 6C 79 00 00 00  ole word only...
    00F0  00 00 00 00 00 00 00 00-03 00 01 50 05 00 3E 00  ...........P..>.
    0100  3B 00 0C 00 11 04 00 00-80 4D 61 74 63 68 20 26  ;........Match &
    0110  63 61 73 65 00 00 00 00-00 00 00 00 00 00 00 01  case............
    0120  00 03 50 AE 00 04 00 32-00 0E 00 01 00 00 00 80  ..P....2........
    0130  26 46 69 6E 64 20 4E 65-78 74 00 00 00 00 00 00  &Find Next......
    0140  00 00 00 00 00 00 00 03-50 AE 00 15 00 32 00 0E  ........P....2..
    0150  00 00 04 00 00 80 26 52-65 70 6C 61 63 65 00 00  ......&Replace..
    0160  00 00 00 00 00 00 00 00-00 00 00 03 50 AE 00 26  ............P..&
    0170  00 32 00 0E 00 01 04 00-00 80 52 65 70 6C 61 63  .2........Replac
    0180  65 20 26 41 6C 6C 00 00-00 00 00 00 00 00 00 00  e &All..........
    0190  00 00 00 03 50 AE 00 37-00 32 00 0E 00 02 00 00  ....P..7.2......
    01A0  00 80 43 61 6E 63 65 6C-00 00 00 00 00 00 00 00  ..Cancel........
    01B0  00 00 00 00 00 03 50 AE-00 4B 00 32 00 0E 00 0E  ......P..K.2....
    01C0  04 00 00 80 26 48 65 6C-70 00 00 00              ....&Help...
    

    Once again, we start with the header.

    0000  01 00         // wVersion
    0002  FF FF         // wSignature
    0004  00 00 00 00   // dwHelpID
    0008  00 00 00 00   // dwExStyle
    000C  C4 00 C8 80   // dwStyle
    0010  0B            // cItems
    0011  24 00 2C 00   // x, y
    0015  E6 00 5E 00   // cx, cy
    

    The header breaks down as follows:

    wVersion = 0x0001 = 1
    wSignature = 0xFFFF
    dwHelpID = 0x00000000 = 0
    dwExStyle = 0x00000000 = 0
    dwStyle = 0x80C800C4 = WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK
    cItems = 0x0B = 11
    x = 0x0024 = 36
    y = 0x002C = 44
    cx = 0x00E6 = 230
    cy = 0x005E = 94

    Next come the menu name, class name, and dialog title:

    0019  00            // no menu
    001A  00            // default dialog class
    001B  52 65 70 6C 61 63 65 00 // "Replace"
    

    Same as the 16-bit classic template.

    The presence of DS_SETFONT means that there's font information ahead. This looks slightly different.

    0023  08 00         // wSize = 8
    0025  90 01         // wWeight = 0x02BC = 700 = FW_NORMAL
    0027  00            // Italic
    0028  00            // Character set = 0x00 = ANSI_CHARSET
    0029  4D 53 20 53 68 65 6C 20 44 6C 67 00
                        // "MS Shell Dlg"
    

    Now follow the extended dialog item templates. This should all be old hat by now, so I won't go into detail.

    // First control
    0035  00 00 00 00   // dwHelpID
    0039  00 00 00 00   // dwExStyle
    003D  00 00 02 50   // dwStyle
    0041  04 00 09 00   // x, y
    0045  30 00 08 00   // cx, cy
    0049  FF FF FF FF   // dwID
    004D  82            // szClass = ordinal 0x82 = "static"
    004E  46 69 26 6E 64 20 77 68 61 74 3A 00
                        // "Fi&nd what:"
    005A  00 00         // no extra data
    
    // Second control
    005C  00 00 00 00   // dwHelpID
    0060  00 00 00 00   // dwExStyle
    0064  80 00 83 50   // dwStyle
    0068  36 00 07 00   // x, y
    006C  72 00 0C 00   // cx, cy
    0070  80 04 00 00   // dwID
    0074  81            // "edit"
    0075  00            // ""
    0076  00 00         // no extra data
    
    // Third control
    0078  00 00 00 00   // dwHelpID
    007C  00 00 00 00   // dwExStyle
    0080  00 00 02 50   // dwStyle
    0084  04 00 1A 00   // x, y
    0088  30 00 08 00   // cx, cy
    008C  FF FF FF FF   // dwID
    0090  82            // "static"
    0091  52 65 26 70 6C 61 63 65 20 77 69 74 68 3A 00
                        // "Re&place with:"
    00A0  00 00         // no extra data
    
    // Fourth control
    00A2  00 00 00 00   // dwHelpID
    00A6  00 00 00 00   // dwExStyle
    00AA  80 00 83 50   // dwStyle
    00AE  36 00 18 00   // x, y
    00B2  72 00 0C 00   // cx, cy
    00B6  81 04 00 00   // dwID
    00BA  81            // "edit"
    00BB  00            // ""
    00BC  00 00         // no extra data
    
    // Fifth control
    00BE  00 00 00 00   // dwHelpID
    00C2  00 00 00 00   // dwExStyle
    00C6  03 00 03 50   // dwStyle
    00CA  05 00 2E 00   // x, y
    00CE  68 00 0C 00   // cx, cy
    00D2  10 04 00 00   // dwID
    00D6  80            // "button"
    00D7  4D 61 74 63 68 20 26 77 68 6F 6C 65 20 77
          6F 72 64 20 6F 6E 6C 79 00
                        // "Match &whole word only"
    00EE  00 00         // no extra data
    
    // Sixth control
    00F0  00 00 00 00   // dwHelpID
    00F4  00 00 00 00   // dwExStyle
    00F8  03 00 01 50   // dwStyle
    00FC  05 00 3E 00   // x, y
    0100  3B 00 0C 00   // cx, cy
    0104  11 04 00 00   // dwID
    0108  80            // "button"
    0109  4D 61 74 63 68 20 26 63 61 73 65 00
                        // "Match &case"
    0115  00 00         // no extra data
    
    // Seventh control
    0117  00 00 00 00   // dwHelpID
    011B  00 00 00 00   // dwExStyle
    011F  01 00 03 50   // dwStyle
    0123  AE 00 04 00   // x, y
    0127  32 00 0E 00   // cx, cy
    012B  01 00 00 00   // dwID
    012F  80            // "button"
    0130  26 46 69 6E 64 20 4E 65 78 74 00
                        // "&Find Next"
    013B  00 00         // no extra data
    
    // Eighth control
    013D  00 00 00 00   // dwHelpID
    0141  00 00 00 00   // dwExStyle
    0145  00 00 03 50   // dwStyle
    0149  AE 00 15 00   // x, y
    014D  32 00 0E 00   // cx, cy
    0151  00 04 00 00   // dwID
    0155  80            // "button"
    0156  26 52 65 70 6C 61 63 65 00
                        // "&Replace"
    015F  00 00         // no extra data
    
    // Ninth control
    0161  00 00 00 00   // dwHelpID
    0165  00 00 00 00   // dwExStyle
    0169  00 00 03 50   // dwStyle
    016D  AE 00 26 00   // x, y
    0171  32 00 0E 00   // cx, cy
    0175  01 04 00 00   // dwID
    0179  80            // "button"
    017A  52 65 70 6C 61 63 65 20 26 41 6C 6C 00
                        // "Replace &All"
    0187  00 00         // no extra data
    
    // Tenth control
    0189  00 00 00 00   // dwHelpID
    018D  00 00 00 00   // dwExStyle
    0191  00 00 03 50   // dwStyle
    0195  AE 00 37 00   // x, y
    0199  32 00 0E 00   // cx, cy
    019D  02 00 00 00   // dwID
    01A1  80            // "button"
    01A2  43 61 6E 63 65 6C 00
                        // "Cancel"
    01A9  00 00         // no extra data
    
    // Eleventh control
    01AB  00 00 00 00   // dwHelpID
    01AF  00 00 00 00   // dwExStyle
    01B3  00 00 03 50   // dwStyle
    01B7  AE 00 4B 00   // x, y
    01BB  32 00 0E 00   // cx, cy
    01BF  0E 04 00 00   // dwID
    01C3  80            // "button"
    01C4  26 48 65 6C 70 00
                        // "&Help"
    01CA  00 00         // no extra data
    

    The original dialog template is the one you've seen twice already, with only one change: The DIALOG keyword has been changed to DIALOGEX.

    DIALOGEX 36, 44, 230, 94
    ...
    

    Tomorrow, we reach the modern era with the 32-bit DIALOGEX template.

  • The Old New Thing

    The evolution of dialog templates - 32-bit Classic Templates

    • 21 Comments

    Okay, last time we talked about the 16-bit classic DIALOG template. This time, we're going to talk about the 32-bit classic DIALOG template.

    There really isn't much going on. Some 8-bit fields got expanded to 16-bit fields, some 16-bit fields got expanded to 32-bit fields, extended styles were added, and all strings got changed from ANSI to Unicode.

    The template starts like this:

    DWORD dwStyle;   // dialog style
    DWORD dwExStyle; // extended dialog style
    WORD  cItems;    // number of controls in this dialog
    WORD  x;         // x-coordinate
    WORD  y;         // y-coordinate
    WORD  cx;        // width
    WORD  cy;        // height
    

    This is basically the same as the 16-bit dialog template, except that there's a new dwExStyle field, and the cItems went from a BYTE to a WORD. Consequently, the maximum number of controls per 32-bit dialog is 65535. That should be enough for a while.

    After this header come a series of strings, just like in 16-bit dialog templates. But this time, the strings are Unicode. For example, if you wanted to store the string "Hello", you would write out the twelve bytes

    48 00 65 00 6C 00 6C 00 6F 00 00 00 ; "Hello"
    

    As with the 16-bit case, in the 32-bit dialog template, you can often specify an ordinal instead of a string. Here, it's done by writing the bytes FF 00 followed by the 16-bit ordinal (in little-endian format). For example, if you wanted to specify the ordinal 42, you would write out the four bytes

    FF 00 2A 00        ; 00FF followed by WORD (little-endian)
    

    The three strings are the same as last time:

    • The menu name, which can be a string or an ordinal.
    • The class, which must be a string (no ordinals allowed).
    • The dialog title, which must be a string (no ordinals allowed).

    If the DS_SETFONT style is set, then what follows next is a WORD indicating the point size and a string specifying the font name. Otherwise, there is no font information. Same as in the 16-bit dialog template.

    So far, everything has been WORD-aligned.

    After the header comes a series of dialog item templates. Each item template begins on a DWORD boundary. (Insert padding if necessary to achieve this.)

    DWORD dwStyle;   // window style
    DWORD dwExStyle; // window extended style
    WORD  x;         // x-coordinate (DLUs)
    WORD  y;         // y-coordinate (DLUs)
    WORD  cx;        // width (DLUs)
    WORD  cy;        // height (DLUs)
    WORD  wID;       // control ID
    

    As before, the dialog coordinates are recorded in dialog units (DLUs).

    Next comes the class name, either as a null-terminated Unicode string or as an ordinal. The ordinal codes for the six "standard" window classes are the same as for 16-bit dialog templates:

    • 0x0080 = "button"
    • 0x0081 = "edit"
    • 0x0082 = "static"
    • 0x0083 = "listbox"
    • 0x0084 = "scrollbar"
    • 0x0085 = "combobox"

    After the class name comes the control text, either as a null-terminated string or as an ordinal, following the same rules as for the 16-bit template. Extra weirdness: To specify an ordinal here, use FFFF instead of 00FF as the ordinal marker. I don't know why.

    After the control text comes up to 65535 bytes of "extra data" in the form of a 16-bit count, followed by the actual data. If there is no "extra data", then use a count of zero.

    And that's all there is. As with last time, I'll present an annotated dialog template.

    0000  C4 20 C8 80 00 00 00 00-0B 00 24 00 2C 00 E6 00  . ........$.,...
    0010  5E 00 00 00 00 00 52 00-65 00 70 00 6C 00 61 00  ^.....R.e.p.l.a.
    0020  63 00 65 00 00 00 08 00-4D 00 53 00 20 00 53 00  c.e.....M.S. .S.
    0030  68 00 65 00 6C 00 6C 00-20 00 44 00 6C 00 67 00  h.e.l.l. .D.l.g.
    0040  00 00 00 00 00 00 02 50-00 00 00 00 04 00 09 00  .......P........
    0050  30 00 08 00 FF FF FF FF-82 00 46 00 69 00 26 00  0.........F.i.&.
    0060  6E 00 64 00 20 00 77 00-68 00 61 00 74 00 3A 00  n.d. .w.h.a.t.:.
    0070  00 00 00 00 80 00 83 50-00 00 00 00 36 00 07 00  .......P....6...
    0080  72 00 0C 00 80 04 FF FF-81 00 00 00 00 00 00 00  r...............
    0090  00 00 02 50 00 00 00 00-04 00 1A 00 30 00 08 00  ...P........0...
    00A0  FF FF FF FF 82 00 52 00-65 00 26 00 70 00 6C 00  ......R.e.&.p.l.
    00B0  61 00 63 00 65 00 20 00-77 00 69 00 74 00 68 00  a.c.e. .w.i.t.h.
    00C0  3A 00 00 00 00 00 00 00-80 00 83 50 00 00 00 00  :..........P....
    00D0  36 00 18 00 72 00 0C 00-81 04 FF FF 81 00 00 00  6...r...........
    00E0  00 00 00 00 03 00 03 50-00 00 00 00 05 00 2E 00  .......P........
    00F0  68 00 0C 00 10 04 FF FF-80 00 4D 00 61 00 74 00  h.........M.a.t.
    0100  63 00 68 00 20 00 26 00-77 00 68 00 6F 00 6C 00  c.h. .&.w.h.o.l.
    0110  65 00 20 00 77 00 6F 00-72 00 64 00 20 00 6F 00  e. .w.o.r.d. .o.
    0120  6E 00 6C 00 79 00 00 00-00 00 00 00 03 00 01 50  n.l.y..........P
    0130  00 00 00 00 05 00 3E 00-3B 00 0C 00 11 04 FF FF  ......>.;.......
    0140  80 00 4D 00 61 00 74 00-63 00 68 00 20 00 26 00  ..M.a.t.c.h. .&.
    0150  63 00 61 00 73 00 65 00-00 00 00 00 01 00 03 50  c.a.s.e........P
    0160  00 00 00 00 AE 00 04 00-32 00 0E 00 01 00 FF FF  ........2.......
    0170  80 00 26 00 46 00 69 00-6E 00 64 00 20 00 4E 00  ..&.F.i.n.d. .N.
    0180  65 00 78 00 74 00 00 00-00 00 00 00 00 00 01 50  e.x.t..........P
    0190  00 00 00 00 AE 00 15 00-32 00 0E 00 00 04 FF FF  ........2.......
    01A0  80 00 26 00 52 00 65 00-70 00 6C 00 61 00 63 00  ..&.R.e.p.l.a.c.
    01B0  65 00 00 00 00 00 00 00-00 00 01 50 00 00 00 00  e..........P....
    01C0  AE 00 26 00 32 00 0E 00-01 04 FF FF 80 00 52 00  ..&.2.........R.
    01D0  65 00 70 00 6C 00 61 00-63 00 65 00 20 00 26 00  e.p.l.a.c.e. .&.
    01E0  41 00 6C 00 6C 00 00 00-00 00 00 00 00 00 01 50  A.l.l..........P
    01F0  00 00 00 00 AE 00 37 00-32 00 0E 00 02 00 FF FF  ......7.2.......
    0200  80 00 43 00 61 00 6E 00-63 00 65 00 6C 00 00 00  ..C.a.n.c.e.l...
    0210  00 00 00 00 00 00 01 50-00 00 00 00 AE 00 4B 00  .......P......K.
    0220  32 00 0E 00 0E 04 FF FF-80 00 26 00 48 00 65 00  2.........&.H.e.
    0230  6C 00 70 00 00 00 00 00                          l.p.....
    

    As before, we start with the header.

    0000  C4 20 C8 80  // dwStyle
    0004  00 00 00 00  // dwExStyle
    0008  0B 00        // cItems
    000A  24 00 2C 00  // x, y
    000E  E6 00 5E 00  // cx, cy
    

    In other words, the header says

    dwStyle = 0x80C820C4 = WS_POPUP | WS_CAPTION | WS_SYSMENU |
      DS_CONTEXTHELP | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK
    dwExStyle = 0x00000000
    cItems = 0x0B = 11
    x = 0x0024 = 36
    y = 0x002C = 44
    cx = 0x00E6 = 230
    cy = 0x005E = 94

    After the header come the menu name, class name, and dialog title:

    0012  00 00         // no menu
    0014  00 00         // default dialog class
    0016  52 00 65 00 70 00 6C 00 61 00 63 00
          65 00 00 00   // "Replace"
    

    Again, since the DS_SETFONT bit is set in the style, the next section describes the font to be used by the dialog:

    0026  08 00         // wSize = 8
    0028  4D 00 53 00 20 00 53 00 68 00 65 00 6C 00
          6C 00 20 00 44 00 6C 00 67 00 00 00
                        // "MS Shell Dlg"
    

    This dialog box uses 8pt "MS Shell Dlg" as its dialog font.

    Next come the eleven dialog item templates. Not remember that each template must be DWORD-aligned, so we need some padding here to get up to a four-byte boundary.

    0042 00 00          // Padding for alignment
    

    Now that we are once again DWORD-aligned, we can read the first dialog item template.

    0044  00 00 02 50   // dwStyle
    0048  00 00 00 00   // dwExStyle
    004C  04 00 09 00   // x, y
    0050  30 00 08 00   // cx, cy
    0054  FF FF         // wID
    0056  FF FF 82 00   // "static"
    005A  46 00 69 00 26 00
    0060  6E 00 64 00 20 00 77 00-68 00 61 00 74 00 3A 00
    0070  00 00         // "Fi&nd what:"
    0072  00 00         // no extra data
    

    Notice here that the "static" class was encoded as an ordinal. The template for this item is therefore

    dwStyle = 0x50020000 = WS_CHILD | WS_VISIBLE | WS_GROUP | SS_LEFT
    dwExStyle = 0x00000000
    x = 0x0004 = 4
    y = 0x0009 = 9
    cx = 0x0030 = 48
    cy = 0x0008 = 8
    wID = 0xFFFF = -1
    szClass = ordinal 0x0082 = "static"
    szText = "Fi&nd what:"

    The other controls are similarly unexciting.

    // Second control
    0074  80 00 83 50   // dwStyle
    0078  00 00 00 00   // dwExStyle
    007C  36 00 07 00   // x, y
    0080  72 00 0C 00   // cx, cy
    0084  80 04         // wID
    0086  FF FF 81 00   // "edit"
    008A  00 00         // ""
    008C  00 00         // no extra data
    008E  00 00         // padding to achieve DWORD alignment
    
    // Third control
    0090  00 00 02 50   // dwStyle
    0094  00 00 00 00   // dwExStyle
    0098  04 00 1A 00   // x, y
    009C  30 00 08 00   // cx, cy
    00A0  FF FF         // wID
    00A2  FF FF 82 00   // "static"
    00A6  52 00 65 00 26 00 70 00 6C 00
    00B0  61 00 63 00 65 00 20 00 77 00 69 00 74 00 68 00
    00C0  3A 00 00 00   // "Re&place with:"
    00C4  00 00         // no extra data
    00C6  00 00         // padding to achieve DWORD alignment
    
    // Fourth control
    00C8  80 00 83 50   // dwStyle
    00CC  00 00 00 00   // dwExStyle
    00D0  36 00 18 00   // x, y
    00D4  72 00 0C 00   // cx, cy
    00D8  81 04         // wID
    00DA  FF FF 81 00   // "edit"
    00DE  00 00         // ""
    00E0  00 00         // no extra data
    00E2  00 00         // padding to achieve DWORD alignment
    
    // Fifth control
    00E4  03 00 03 50   // dwStyle
    00E8  00 00 00 00   // dwExStyle
    00EC  05 00 2E 00   // x, y
    00F0  68 00 0C 00   // cx, cy
    00F4  10 04         // wID
    00F6  FF FF 80 00   // "button"
    00FA  4D 00 61 00 74 00
    0100  63 00 68 00 20 00 26 00 77 00 68 00 6F 00 6C 00
    0110  65 00 20 00 77 00 6F 00 72 00 64 00 20 00 6F 00
    0120  6E 00 6C 00 79 00 00 00
                        // "Match &whole word only"
    0128  00 00         // no extra data
    012A  00 00         // padding to achieve DWORD alignment
    
    // Sixth control
    012C  03 00 01 50   // dwStyle
    0130  00 00 00 00   // dwExStyle
    0134  05 00 3E 00   // x, y
    0138  3B 00 0C 00   // cx, cy
    013C  11 04         // wID
    013E  FF FF 80 00   // "button"
    0142  4D 00 61 00 74 00 63 00 68 00 20 00 26 00
    0150  63 00 61 00 73 00 65 00 00 00
                        // "Match &case"
    015A  00 00         // no extra data
    
    // Seventh control
    015C  01 00 03 50   // dwStyle
    0160  00 00 00 00   // dwExStyle
    0164  AE 00 04 00   // x, y
    0168  32 00 0E 00   // cx, cy
    016C  01 00         // wID
    016E  FF FF 80 00   // "button"
    0172  26 00 46 00 69 00 6E 00 64 00 20 00 4E 00
    0180  65 00 78 00 74 00 00 00
                        // "&Find Next"
    0188  00 00         // no extra data
    018A  00 00         // padding to achieve DWORD alignment
    
    // Eighth control
    018C  00 00 01 50   // dwStyle
    0190  00 00 00 00   // dwExStyle
    0194  AE 00 15 00   // x, y
    0198  32 00 0E 00   // cx, cy
    019C  00 04         // wID
    019E  FF FF 80 00   // "button"
    01A2  26 00 52 00 65 00-70 00 6C 00 61 00 63 00
    01B0  65 00 00 00   // "&Replace"
    01B4  00 00         // no extra data
    01B6  00 00         // padding to achieve DWORD alignment
    
    // Ninth control
    01B8  00 00 01 50   // dwStyle
    01BC  00 00 00 00   // dwExStyle
    01C0  AE 00 26 00   // x, y
    01C4  32 00 0E 00   // cx, cy
    01C8  01 04         // wID
    01CA  FF FF 80 00   // "button"
    01CE  52 00
    01D0  65 00 70 00 6C 00 61 00 63 00 65 00 20 00 26 00
    01E0  41 00 6C 00 6C 00 00 00
                        // "Replace &All"
    01E8  00 00         // no extra data
    01EA  00 00         // padding to achieve DWORD alignment
    
    // Tenth control
    01EC  00 00 01 50   // dwStyle
    01F0  00 00 00 00   // dwExStyle
    01F4  AE 00 37 00   // x, y
    01F8  32 00 0E 00   // cx, cy
    01FC  02 00         // wID
    01FE  FF FF 80 00   // "button"
    0202  43 00 61 00 6E 00 63 00 65 00 6C 00 00 00
                        // "Cancel"
    0210  00 00         // no extra data
    0212  00 00         // padding to achieve DWORD alignment
    
    // Eleventh control
    0214  00 00 01 50   // dwStyle
    0218  00 00 00 00   // dwExStyle
    021C  AE 00 4B 00   // x, y
    0220  32 00 0E 00   // cx, cy
    0224  0E 04         // wID
    0226  FF FF 80 00   // "button"
    022A  26 00 48 00 65 00 6C 00 70 00 00 00
                        // "&Help"
    0236  00 00         // no extra data
    

    Whew. Tedious and entirely unexciting. Here's the original resource compiler source code that we reverse-engineered:

    DIALOG 36, 44, 230, 94
    STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_3DLOOK | NOT WS_VISIBLE
    CAPTION "Replace"
    FONT 8, "MS Shell Dlg"
    BEGIN
        CONTROL "Fi&nd What:", -1, "static", WS_GROUP | SS_LEFT,
                4, 9, 48, 8
    
        CONTROL "", 0x0480, "edit",
                WS_BORDER | WS_GROUP | WS_TABSTOP | ES_AUTOHSCROLL,
                54, 7, 114, 12
    
        CONTROL "Re&place with:", -1, "static", WS_GROUP | SS_LEFT,
                4, 26, 48, 8
    
        CONTROL "", 0x0481, "edit",
                WS_BORDER | WS_GROUP | WS_TABSTOP | ES_AUTOHSCROLL,
                54, 24, 114, 12
    
        CONTROL "Match &whole word only", 0x0410, "button",
                WS_GROUP | WS_TABSTOP | BS_AUTOCHECKBOX,
                5, 46, 104, 12
    
        CONTROL "Match &case", 0x0411, "button",
                WS_TABSTOP | BS_AUTOCHECKBOX,
                5, 62, 59, 12
    
        CONTROL "&Find Next", IDOK, "button",
                WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON,
                174, 4, 50, 14
    
        CONTROL "&Replace", 0x0400, "button",
                WS_TABSTOP | BS_PUSHBUTTON,
                174, 21, 50, 14
    
        CONTROL "Replace &All", 0x0401, "button",
                WS_TABSTOP | BS_PUSHBUTTON,
                174, 38, 50, 14
    
        CONTROL "Cancel", IDCANCEL, "button",
                WS_TABSTOP | BS_PUSHBUTTON,
                174, 55, 50, 14
    
        CONTROL "Cancel", 0x040E, "button",
                WS_TABSTOP | BS_PUSHBUTTON,
                174, 75, 50, 14
    END
    

    As before, we didn't explicitly say "DS_SETFONT" in the dialog's STYLE directive since that is implied by the "FONT" directive, and we took advantage of the fact that WS_VISIBLE is on by default.

    And you probably recognize this dialog from yesterday. It's the replace dialog from findtext.dlg. (Though it's not exactly the same since the findtext.dlg template uses some shorthand directives like DEFPUSHBUTTON instead of manually writing out the details of the button control as a CONTROL.)

    Next time: The 16-bit extended dialog template, also known as DIALOGEX.

  • The Old New Thing

    The evolution of dialog templates - 16-bit Classic Templates

    • 14 Comments

    In the history of Windows, there have been four versions of dialog templates. And despite the changes, you'll see that they're basically all the same.

    First, there was the classic Windows 1.0 dialog template. It starts like this:

    DWORD dwStyle; // dialog style
    BYTE  cItems;  // number of controls in this dialog
    WORD  x;       // x-coordinate
    WORD  y;       // y-coordinate
    WORD  cx;      // width
    WORD  cy;      // height
    

    Notice that this is where the 255-controls-per-dialog limit comes from on 16-bit Windows, since the field that records the number of controls on the dialog is only a byte.

    After this header come a series of strings. All strings in the 16-bit dialog template permit a null-terminated ANSI string. For example, if you wanted to store the string "Hello", you would write out the six bytes

    48 65 6C 6C 6F 00  ; "Hello"
    

    (As a special case of this: If you write out a single 00 byte, then that represents a null string. Handy when you don't actually want to store a string but the dialog format requires you to store one.)

    Sometimes you are allowed to specify a 16-bit ordinal value instead of a string. In that case, you write out the byte 0xFF followed by the ordinal. For example, if you wanted to specify the ordinal 42, you would write out the three bytes

    FF 2A 00           ; FF followed by WORD (little-endian)
    

    Okay, back to the dialog template. After the header, there are three strings:

    • The menu name, which can be a string or an ordinal. This is typically null, indicating that you don't want a menu. If non-null, then the menu will be loaded via LoadMenu using the specified string or resource from the instance handle passed to the dialog creation function via the HINSTANCE parameter.
    • The class, which must be a string (no ordinals allowed). This is typically also null, indicating that you want the default dialog class. We have seen earlier how you can override the default dialog class to get special behavior. If non-null, the class will be also be looked up relative to the instance handle passed to the dialog creation function via the HINSTANCE parameter.
    • The dialog title, which must be a string (no ordinals allowed).

    If the DS_SETFONT style is set, then what follows next is a WORD indicating the point size and a string specifying the font name. Otherwise, there is no font information.

    That's the end of the header section. Next come a series of dialog item templates, one for each control.

    Each item template begins the same way:

    WORD  x;       // x-coordinate (DLUs)
    WORD  y;       // y-coordinate (DLUs)
    WORD  cx;      // width (DLUs)
    WORD  cy;      // height (DLUs)
    WORD  wID;     // control ID
    DWORD dwStyle; // window style
    

    Recall that the dialog coordinates are recorded in dialog units (DLUs). Four x-DLUs and eight y-DLUs equals one "average" character.

    After the fixed start of the item template comes the class name, either as a null-terminated ANSI string or (and this is particularly weird) as single byte in the range 0x80 through 0xFF which encodes one of the "standard" window classes:

    • 0x80 = "button"
    • 0x81 = "edit"
    • 0x82 = "static"
    • 0x83 = "listbox"
    • 0x84 = "scrollbar"
    • 0x85 = "combobox"

    (Note that this encoding means that the first character of a window class name cannot be an extended character if you want to use it in a dialog template!)

    After the class name comes the control text, either as a null-terminated string or as an ordinal. If you use an ordinal, then the lpszName member of the CREATESTRUCT is a pointer to the three-byte ordinal sequence (0xFF followed by the ordinal); otherwise it's a pointer to the string. The only control I know of that knows what to do with the ordinal is the static control if you put it into one of the image modes (SS_ICON or SS_BITMAP), in which case the ordinal is a resource identifier for the image that the static displays.

    After the control text comes up to 256 bytes of "extra data" in the form of a byte count, followed by the actual data. If there is no "extra data", then use a byte count of zero.

    When the dialog manager creates a control, it passes a pointer to the "extra" data as the final LPVOID parameter to the CreateWindowEx function. (As far as I can tell, there is no way to tell the resource compiler to insert this extra data. It's one of those lurking features that nobody has taken advantage of yet.)

    Okay, that's all great and theoretical. But sometimes you just need to see it in front of you to understand it. So let's take apart an actual 16-bit dialog resource. I took this one from COMMCTRL.DLL; it's the search/replace dialog.

    0000  C0 00 C8 80 0B 24 00 2C-00 E6 00 5E 00 00 00 52  .....$.,...^...R
    0010  65 70 6C 61 63 65 00 08-00 48 65 6C 76 00 04 00  eplace...Helv...
    0020  09 00 30 00 08 00 FF FF-00 00 00 50 82 46 69 26  ..0........P.Fi&
    0030  6E 64 20 57 68 61 74 3A-00 00 36 00 07 00 72 00  nd What:..6...r.
    0040  0C 00 80 04 80 00 83 50-81 00 00 04 00 1A 00 30  .......P.......0
    0050  00 08 00 FF FF 00 00 00-50 82 52 65 26 70 6C 61  ........P.Re&pla
    0060  63 65 20 57 69 74 68 3A-00 00 36 00 18 00 72 00  ce With:..6...r.
    0070  0C 00 81 04 80 00 83 50-81 00 00 05 00 2E 00 68  .......P.......h
    0080  00 0C 00 10 04 03 00 03-50 80 4D 61 74 63 68 20  ........P.Match
    0090  26 57 68 6F 6C 65 20 57-6F 72 64 20 4F 6E 6C 79  &Whole Word Only
    00A0  00 00 05 00 3E 00 3B 00-0C 00 11 04 03 00 01 50  ....>.;........P
    00B0  80 4D 61 74 63 68 20 26-43 61 73 65 00 00 AE 00  .Match &Case....
    00C0  04 00 32 00 0E 00 01 00-01 00 03 50 80 26 46 69  ..2........P.&Fi
    00D0  6E 64 20 4E 65 78 74 00-00 AE 00 15 00 32 00 0E  nd Next......2..
    00E0  00 00 04 00 00 03 50 80-26 52 65 70 6C 61 63 65  ......P.&Replace
    00F0  00 00 AE 00 26 00 32 00-0E 00 01 04 00 00 03 50  ....&.2........P
    0100  80 52 65 70 6C 61 63 65-20 26 41 6C 6C 00 00 AE  .Replace &All...
    0110  00 37 00 32 00 0E 00 02-00 00 00 03 50 80 43 61  .7.2........P.Ca
    0120  6E 63 65 6C 00 00 AE 00-4B 00 32 00 0E 00 0E 04  ncel....K.2.....
    0130  00 00 03 50 80 26 48 65-6C 70 00 00              ...P.&Help..
    

    Let's start with the header.

    0000  C0 00 C8 80  // dwStyle
    0004  0B           // cItems
    0005  24 00 2C 00  // x, y
    0009  E6 00 5E 00  // cx, cy
    

    In other words, the header says

    dwStyle = 0x80C800C0 = WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT | DS_MODALFRAME
    cItems = 0x0B = 11
    x = 0x0024 = 36
    y = 0x002C = 44
    cx = 0x00E6 = 230
    cy = 0x005E = 94

    After the header come the menu name, class name, and dialog title:

    000D  00            // no menu
    000E  00            // default dialog class
    000F  52 65 70 6C 61 63 65 00 // "Replace"
    

    Now, since the DS_SETFONT bit is set in the style, the next section describes the font to be used by the dialog:

    0017  08 00         // wSize = 8
    0019  48 65 6C 76 00 // "Helv"
    

    Aha, this dialog box uses 8pt Helv.

    Next come the eleven dialog item templates.

    001E  04 00 09 00   // x, y
    0022  30 00 08 00   // cx, cy
    0026  FF FF         // wID
    0028  00 00 00 50   // dwStyle
    

    So this dialog item template says

    x = 0x0004 = 4
    y = 0x0009 = 9
    cx = 0x0030 = 48
    cy = 0x0008 = 8
    wID = 0xFFFF = -1
    dwStyle = 0x50000000 = WS_CHILD | WS_VISIBLE | SS_LEFT

    How did I know that the style value 0x0000 should be interpreted as SS_LEFT and not, say, BS_PUSHBUTTON? Because the window class tells me that what I have is a static control.

    002C  82            // "static"
    

    After the class name comes the control text.

    002D  46 69 26 6E 64 20 57 68 61 74 3A 00 // "Fi&nd What:"
    

    And finally (for this dialog item template), we specify that we have no extra data:

    0039  00            // no extra data
    

    Now we repeat the above exercise for the other ten controls. I'll just summarize here:

    // Second control
    003A  36 00 07 00   // x, y
    003E  72 00 0C 00   // cx, cy
    0042  80 04         // wID
    0044  80 00 83 50   // dwStyle
    0048  81            // "edit"
    0049  00            // ""
    004A  00            // no extra data
    
    // Third control
    004B  04 00 1A 00   // x, y
    004F  30 00 08 00   // cx, cy
    0053  FF FF         // wID
    0055  00 00 00 50   // dwStyle
    0059  82            // "static"
    005A  52 65 26 70 6C 61 63 65 20 57 69 74 68 3A 00
                        // "Re&place With:"
    0069  00            // no extra data
    
    // Fourth control
    006A  36 00 18 00   // x, y
    006E  72 00 0C 00   // cx, cy
    0072  81 04         // wID
    0074  80 00 83 50   // dwStyle
    0078  81            // "edit"
    0079  00            // ""
    007A  00            // no extra data
    
    // Fifth control
    007B  05 00 2E 00   // x, y
    007F  68 00 0C 00   // cx, cy
    0083  10 04         // wID
    0085  03 00 03 50   // dwStyle
    0089  80            // "button"
    008A  4D 61 74 63 68 20 26 57 68 6F 6C 65 20 57
          6F 72 64 20 4F 6E 6C 79 00
                        // "Match &Whole Word Only"
    00A1  00            // no extra data
    
    // Sixth control
    00A2  05 00 3E 00   // x, y
    00A6  3B 00 0C 00   // cx, cy
    00AA  11 04         // wID
    00AC  03 00 01 50   // dwStyle
    00B0  80            // "button"
    00B1  4D 61 74 63 68 20 26 43 61 73 65 00
                        // "Match &Case"
    00BD  00            // no extra data
    
    // Seventh control
    00BE  AE 00 04 00   // x, y
    00C2  32 00 0E 00   // cx, cy
    00C6  01 00         // wID
    00C8  01 00 03 50   // dwStyle
    00CC  80            // "button"
    00CD  26 46 69 6E 64 20 4E 65 78 74 00
                        // "&Find Next"
    00D8  00            // no extra data
    
    // Eighth control
    00D9  AE 00 15 00   // x, y
    00DD  32 00 0E 00   // cx, cy
    00E1  00 04         // wID
    00E3  00 00 03 50   // dwStyle
    00E7  80            // "button"
    00E8  26 52 65 70 6C 61 63 65 00
                        // "&Replace"
    00F1  00            // no extra data
    
    // Ninth control
    00F2  AE 00 26 00   // x, y
    00F6  32 00 0E 00   // cx, cy
    00FA  01 04         // wID
    00FC  00 00 03 50   // dwStyle
    0100  80            // "button"
    0101  52 65 70 6C 61 63 65 20 26 41 6C 6C 00
                        // "Replace &All"
    010E  00            // no extra data
    
    // Tenth control
    010F  AE 00 37 00   // x, y
    0113  32 00 0E 00   // cx, cy
    0117  02 00         // wID
    0119  00 00 03 50   // dwStyle
    011D  80            // "button"
    011E  43 61 6E 63 65 6C 00
                        // "Cancel"
    0125  00            // no extra data
    
    // Eleventh control
    0126  AE 00 4B 00   // x, y
    012A  32 00 0E 00   // cx, cy
    012E  0E 04         // wID
    0130  00 00 03 50   // dwStyle
    0134  80            // "button"
    0135  26 48 65 6C 70 00
                        // "&Help"
    013B  00            // no extra data
    

    And that's the dialog template. We can now reconstruct the resource compiler source code from this template:

    DIALOG 36, 44, 230, 94
    STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | NOT WS_VISIBLE
    CAPTION "Replace"
    FONT 8, "Helv"
    BEGIN
        CONTROL "Fi&nd What:", -1, "static", SS_LEFT,
                4, 9, 48, 8
    
        CONTROL "", 0x0480, "edit",
                WS_BORDER | WS_GROUP | WS_TABSTOP | ES_AUTOHSCROLL,
                54, 7, 114, 12
    
        CONTROL "Re&place With:", -1, "static", SS_LEFT,
                4, 26, 48, 8
    
        CONTROL "", 0x0481, "edit",
                WS_BORDER | WS_GROUP | WS_TABSTOP | ES_AUTOHSCROLL,
                54, 24, 114, 12
    
        CONTROL "Match &Whole Word Only", 0x0410, "button",
                WS_GROUP | WS_TABSTOP | BS_AUTOCHECKBOX,
                5, 46, 104, 12
    
        CONTROL "Match &Case", 0x0411, "button",
                WS_TABSTOP | BS_AUTOCHECKBOX,
                5, 62, 59, 12
    
        CONTROL "&Find Next", IDOK, "button",
                WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON,
                174, 4, 50, 14
    
        CONTROL "&Replace", 0x0400, "button",
                WS_GROUP | WS_TABSTOP | BS_PUSHBUTTON,
                174, 21, 50, 14
    
        CONTROL "Replace &All", 0x0401, "button",
                WS_GROUP | WS_TABSTOP | BS_PUSHBUTTON,
                174, 38, 50, 14
    
        CONTROL "Cancel", IDCANCEL, "button",
                WS_GROUP | WS_TABSTOP | BS_PUSHBUTTON,
                174, 55, 50, 14
    
        CONTROL "Cancel", 0x040E, "button",
                WS_GROUP | WS_TABSTOP | BS_PUSHBUTTON,
                174, 75, 50, 14
    END
    

    Notice that we didn't explicitly say "DS_SETFONT" in the dialog's STYLE directive since that is implied by the "FONT" directive. And since WS_VISIBLE is on by default, we didn't have to say it; rather we had to explicitly refute it in the places it wasn't wanted.

    Now if you take a look in your SDK header files, you'll find dlgs.h and findtext.dlg which pretty much match up with the template above, giving names to the magic values like 0x0400 and positioning the controls in the same place as above. You'll find some minor differences, though, since the header files in the SDK are for the 32-bit Find/Replace dialog and the one above is the 16-bit Find/Replace dialog, but you'll see that it still matches up pretty well.

    Next time: The 32-bit DIALOG template.

  • The Old New Thing

    The evolution of dialog templates - Introduction

    • 10 Comments

    In the history of Windows, there have been four versions of dialog templates. And despite the changes, you'll see that they're basically all the same.

    My secret goal in this six-part series is to address questions people have had along the lines of "I'm trying to generate a dialog template in code, and it's not working. What am I doing wrong?"

    As it turns out, that you can get the resource compiler to tell you what you're doing wrong. Take the template that you're trying to generate, create an *.rc file for it and run it through the resource compiler. Attach the resource to a dummy program and dump the bytes! Compare the compiler-generated template against the one you generated. Look for the difference.

    In other words: To see what you're doing wrong, take somebody who does it right and compare. Clearly there's a difference somewhere. It's just bytes.

    Anyway, enough of the rant against laziness. The next several days will cover the evolution of the dialog template, with annotated byte dumps for people who are trying to figure out why their dialog template isn't working.

    Non-geeks may want to go into hibernation for a while, since this will take over a week to play out. I'll try to keep you amused with the non-technical side-postings.

  • The Old New Thing

    What was the purpose of the hPrevInstance parameter to WinMain?

    • 28 Comments

    Once your average GUI program picks itself up off the ground, control begins at your WinMain function. The second parameter, hPrevInstance, is always zero in Win32 programs. Certainly it had a meaning at some point?

    Of course it did.

    In 16-bit Windows there was a function called GetInstanceData. This function took an HINSTANCE, a pointer, and a length, and copied memory from that instance into your current instance. (It's sort of the 16-bit equivalent to ReadProcessMemory, with the restriction that the second and third parameters had to be the same.)

    (Since 16-bit Windows had a common address space, the GetInstanceData function was really nothing more than a hmemcpy, and many programs relied on this and just used raw hmemcpy instead of using the documented API. Win16 was actually designed with the possibility of imposing separate address spaces in a future version - observe flags like GMEM_SHARED - but the prevalence of tricks like hmemcpy'ing your previous instance reduced this potential to an unrealized dream.)

    This was the reason for the hPrevInstance parameter to WinMain. If hPrevInstance was non-NULL, then it was the instance handle of a copy of the program that is already running. You can use GetInstanceData to copy data from it, get yourself up off the ground faster. For example, you might want to copy the main window handle out of the previous instance so you could communicate with it.

    Whether hPrevInstance was NULL or not told you whether you were the first copy of the program. Under 16-bit Windows, only the first instance of a program registered its classes; second and subsequent instances continued to use the classes that were registered by the first instance. (Indeed, if they tried, the registration would fail since the class already existed.) Therefore, all 16-bit Windows programs skipped over class registration if hPrevInstance was non-NULL.

    The people who designed Win32 found themselves in a bit of a fix when it came time to port WinMain: What to pass for hPrevInstance? The whole module/instance thing didn't exist in Win32, after all, and separate address spaces meant that programs that skipped over reinitialization in the second instance would no longer work. So Win32 always passes NULL, making all programs believe that they are the first one.

    And amazingly, it actually worked.

  • The Old New Thing

    What is the difference between HINSTANCE and HMODULE?

    • 15 Comments

    They mean the same thing today, but at one time they were quite different.

    It all comes from 16-bit Windows.

    In those days, a "module" represented a file on disk that had been loaded into memory, and the module "handle" was a handle to a data structure that described the parts of the file, where they come from, and where they had been loaded into memory (if at all). On the other hand an "instance" represented a "set of variables".

    One analogy that might (or might not) make sense is that a "module" is like the code for a C++ class - it describes how to construct an object, it implements the methods, it describes how the objects of the class behave. On the other hand, an "instance" is like a C++ object that belongs to that class - it describes the state of a particular instance of that object.

    In C# terms, a "module" is like a "type" and an instance is like an "object". (Except that modules don't have things like "static members", but it was a weak analogy anyway.)

    Here's a diagram. (Recall that we discussed 16-bit HRSRC in a previous entry.)

    USER32 HMODULE USER32 HINSTANCE
    code segment descriptor USER32 code... USER32 data...
    code segment descriptor (not in memory)
    code segment descriptor USER32 code...
    data segment descriptor
    HRSRC (not in memory)
    HRSRC USER32 resource...
    HRSRC (not in memory)
    exports table

    In 16-bit Windows, all programs ran in a single address space, and if a DLL was used by five programs, it was loaded only once into memory. In particular, it got only one copy of its data segment. (In C++/C# terms, a DLL is like a "singleton class".)

    That's right, DLLs were system-global rather than per-process. The DLL did not get a separate copy of its data for each process that loaded it. If that was important to your DLL, you had to keep track of it yourself.

    In geek terms, there was only one "instance" of a DLL in the system.

    On the other hand, if you ran two copies of Notepad, each one got its separate set of variables - there were two "instances".

    NOTEPAD HMODULE HINSTANCE
    code segment descriptor NOTEPAD code... NOTEPAD data...
    code segment descriptor (not in memory)
    data segment descriptor HINSTANCE
    HRSRC (not in memory) NOTEPAD data...
    HRSRC NOTEPAD resource...

    Both running copies of Notepad shared the NOTEPAD module (so the code and resources were shared), but each had its own copy of its variables (separate data segment). There were two "instances" of Notepad.

    The "instance" handles in the above diagrams are the data segments.

    Programs are identified by their the instance handle. You can't use the module handle, because the two copies of Notepad have the same module handle (since the same code is running in each). The thing that makes them different is that each has its own set of global variables.

    This is why the WinExec and ShellExecute functions return HINSTANCE: They are holdovers from 16-bit Windows, where HINSTANCEs were the way to identify running programs.

    The method by which code receives its HINSTANCE (i.e., knows where its global variables are) I will leave for a future article. It is somehow related to the now-obsolete MakeProcInstance function.

    When it came to design Win32, the question then arose, "What do we do with HINSTANCE and HMODULE for Win32?" Since programs ran in separate address spaces, you didn't have instance handles visible across process boundaries. So the designers took the only thing they had: The base address of the module. This was analogous to the HMODULE, since the file header describes the contents of the file and its structure. And it was also analogous to the HINSTANCE, since the data was kept in the data segment, which was mapped into the process directly.

    So in Win32, HINSTANCE and HMODULE are both just the base address of the module.

    Tomorrow, I'll talk about that mysterious hinstPrev parameter to WinMain.

  • The Old New Thing

    Do not underestimate the power of the game Deer Hunter

    • 35 Comments

    During the run-up to Windows XP Service Pack 2 Beta in December of last year, there was a list of five bugs that the release management team decided were so critical that they were going to slip the beta until those bugs got fixed.

    The third bug on the list: Deer Hunter 4 won't run.

    Deer Hunter has the power to stop a beta.

  • The Old New Thing

    My first death threat

    • 32 Comments

    Actual feedback submitted to the microsoft.com web site many years ago.

    id: 13726
    Date: 1996-07-29 17:27:41.997
    Name: ***********
    Email: *************
    Area: Windows 95
    Comments:
    PLEASE read this entire email as it is quite serious. I just discovered today that in the Windows 95 operating system, there are no switches, command line options, or any way whatsoever to have the XCOPY command include hidden/system files in it's operations. It is clear that at some point in the development of the Windows 95 product, that somebody made a conscious decision to implement the xcopy command in this manner. It is also clear from looking at the Windows NT XCOPY command that it can be implemented in the manner I describe. Therefore, let me give fair warning. This may not be easy, and I will expect no help from Microsoft in finding out who this person (or persons) was that made this decision, but....eventually I will find out who made this decision, and I will kill them. This is not an idle threat - I will pursue this matter until it is resolved...whoever is responsible for this incredibly ridiculous implementation of what would be an otherwise useful tool will die at my hands, hopefully in a bloody, painful fashion. You will not get away. -J*hn ******

    J*hn, if you're still out there... the switch for copying hidden files on Windows 95 is /H. Same as Windows NT.

    Please don't kill me.

Page 39 of 49 (485 items) «3738394041»