The evolution of dialog templates – 32-bit Classic Templates


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.

Comments (21)
  1. Frederik Slijkerman says:

    What’s the reason to pad fields in structures like this to DWORD boundaries? This is always a lot more work to program for — and test the special cases that require padding. It’s especially irritating with DIB scan lines.

    The only reason I can think of is that it is marginally faster. Sounds like a good example of premature optimization, or am I mistaken?

  2. Raymond Chen says:

    It’s not an optimization. It’s a necessity.

    The overwhelming dominance of the x86 architecture in the world of Win32 makes people think that the way the x86 does things is normal, when in reality the x86 is the weirdo.

    PPC, MIPS, Alpha AXP, ia64 all crash if you access unaligned data. x86 is the weirdo: It permits unaligned data access.

  3. Cooney says:

    It could be a remnant of the Alpha/MIPS NT effort – accessing unaligned data caused a segfault instead of simply being slow. Alternate theory: the structs used to hold this data are compiled with dword alignment and loaded directly from a file, so unaligned data is misinterpreted.

  4. JamKnight says:

    I don’t understand why you would even want to have unaligned data access. In my opinion it only makes things more complicated.

    Can anyone point to a case where it is better/cleaner than using aligned?

  5. Cooney says:

    If space is really tight, as it was way back when, then every byte counts. Otherwise, go right a head and align your data – I do.

  6. asdf says:

    It makes your code cleaner if you don’t have to worry about unaligned stuff. For the most part this is transparent to you but there are some areas where it isn’t. You pay for unaligned memory access in higher cycle counts, not having atomic operations, and segfaults in non x86 processors. Win64 supports bad code with SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT) or the __unaligned qualifier (how thoughtful of them).

  7. JamKnight:

    Unaligned is better than aligned when you’re dealing with different CPUs that have different alignment requirements from the get go.

    (eg. a CPU that required 16bit alignment vs. one that requires 64bit alignment… but you design everything before the 64bit one is on the horizon).

    But yeah, it’s a real edge case.

  8. doynax says:

    (eg. a CPU that required 16bit alignment vs. one that requires 64bit alignment… but you design everything before the 64bit one is on the horizon).

    This doesn’t have to be a problem, just align everything according to the largest datatype.

  9. Waleri says:

    Unfortunatly, one cannot use control’s text ordinal to display text – it works for icon and bitmaps only. pity

  10. asdf says:

    Raymond: the x, y, cx, cy fields should be signed and there is no 0xFF 0x00 to specify ordinals, it’s all 0xFFFF.

  11. Holger Dors says:

    You write "strings got changed from ANSI to Unicode": what kind of encoding is used for Unicode? Is it UCS-2 or UTF-16?

    I never have been able to find the answer on this…

  12. Raymond Chen says:

    Holger: What’s the difference between UCS-2 and UTF-16? As far as I can tell they both use surrogate pairs.

  13. Holger Dors says:

    @Raymond: no, UCS-2 cannot use surrogate pairs and is limit to codepoints up tp $FFFF. AFAIK that was the reason why UTF-16 was created in the first place.

  14. Raymond Chen says:

    I have no idea. But instead of just saying "Gosh I will never know", I decided to search on google for "site:msdn.microsoft.com surrogate pairs" and it found this page

    http://msdn.microsoft.com/library/en-us/intl/unicode_192r.asp . Please, people, don’t be lazy. Do some searching yourself before asking questions. I have better things to do with my time.

  15. Holger Dors says:

    I’m sorry to have wasted your time, Raymond, but I really did search earlier for an answer to this. It just never occured to me to search for "surrogate pairs".

    But finally, thanks to you, I have an answer: starting with W2K UTF-16 (and therefore surrogate pairs) are supported, up until then it was UCS-2.

    Many thanks for your help!

  16. Frederik Slijkerman says:

    For example, with 24-bit DIBs it is a pain in the behind because you can’t write

    lpPixel = lpData + 3 * (x + y * width);

    but the tedious:

    byteWidth = (width * 3 + 3) & 0xFFFFFFFC;

    lpPixel = lpData + 3 * x + y * byteWidth;

  17. Marcdlg says:

    PPC, MIPS, Alpha AXP, ia64 all crash if you access unaligned data. x86 is the weirdo: It permits unaligned data access.

    Raymond,

    I cannot resist the pedantic urge to correct this.

    -AFAIK there are no PPC CPUs which do not support unaligned integer data access in hardware.

    -MIPS generate a precise exception that allows the kernel to emulate in software the unaligned access. (Granted this is dead slow. And a more than sufficient reason to avoid unaligned data at all cost. (e.g. for a packed struct a MIPS compiler will generate 4 load byte instructions to access a dword)

    -Alpha probably does software emulation too, since it is frequently impossible to statically determine if an access is aligned or not.

    And having worked on optimizing network stack code for MIPS, x86 is a smart "weirdo".

  18. Ben Hutchings says:

    Marcdlg: Support for misaligned access is an optional feature of the PowerPC architecture, at least in little-endian mode (which is what Windows uses). The early PowerPCs (601, 603, 604) raise an exception for it.

  19. Raymond Chen says:

    asdf: 0xFF 0xFF and 0xFF 0x00 are both valid and mean different things! I was not sufficient clear.

    0xFF 0xFF means "What comes next is a special class ordinal (0x80 = button, etc.)".

    0xFF 0x00 means "What comes next is a class ordinal (atom)."

  20. Trying to capture the changes in a table.

Comments are closed.