GetDialogBaseUnits is a crock


There are two functions that let you convert dialog units (DLUs) to pixels. One is GetDialogBaseUnits, which returns the base units directly (leaving you to do the math), and another is MapDialogRect, which takes a rectangle in DLUs and returns a rectangle in pixels.

What's the difference?

As you can guess from the title of this entry, GetDialogBaseUnits is a crock. Since there is no HWND parameter to GetDialogBaseUnits, it doesn't know which dialog box's DLUs you want to retrieve. So it guesses.

And it always guesses wrong.

GetDialogBaseUnits returns the dialog base units for dialog boxes that use the default system font. But nobody uses the default system font any more. It screams "old and dorky". But it remains the default for compatibility reasons. (And therefore so too does GetDialogBaseUnits.)

Everybody selects a custom font for their dialog box, usually "MS Sans Serif 8" or possibly "Tahoma" if they are one of those hip Windows 2000 folks.

That is why you must use MapDialogRect. The MapDialogRect function accepts a dialog box handle so it can use the correct font.

Comments (27)
  1. Anonymous says:

    This might be a silly question, but why is the system font still that constant-pitched thing that nobody uses anymore?

    I know the recommendation is to pretty much put "MS Shell Dlg" as the font specification in dialog box resources so it gets replaced with "whatever looks good", but why not just do this with the system default font?

    My copy of Longhorn 4051 had a bit of a glitch the other day, and suddenly every dialog any application displayed used System instead of Tahoma.. was a very strange (and 3.0-esque) experience…

  2. Anonymous says:

    Because older programs were written on the assmuption that the system font was that ugly font. If you change the system font, then the dialogs would not lay out properly. DLUs are supposed to make dialogs font-independent but that’s more a dream than reality. I’ll write more about it later.

  3. Anonymous says:

    Makes sense. However:

    "The GetDialogBaseUnits function retrieves the system’s dialog base units, which are the average width and height of characters in the system font."

    "For either type of dialog box, it is easier to use the MapDialogRect function to perform the conversion"

    That’s what’s in the MSDN documentation. Maybe you should have them make it more clean. Notice that "system font" is not capitalized. If I were Joe Just-Learning-Win32, I would think that the system font were the shell dlg font. They should have a big line in red that says "This function is provided for compatibility only. Please see MapDialogRect"

  4. Anonymous says:

    "more clean" = "more clear." oops.

    You may want to talk more about dialog units in the context of large fonts btw, since some people may not realize why they can’t hard code pixel values…

  5. Anonymous says:

    I remember working on a Windows NT 4.0 Server without Tahoma. All those “hip Windows 2000” programs out there used the “old and dorky” System.

    I also remember teaching Windows 3.1 to use MS Sans Serif for System. There was some kind of an .ini hack…

  6. Anonymous says:

    To get dialogs looking good, or at least at home, on both systems, you’re supposed to set the DS_SHELLFONT style and specify "MS Shell Dlg" as the font. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/apcompat/apcompat/use_dialogex_to_have_the_system_font.asp. This is a special font name which maps to the appropriate font.

    However, your development tools will fight you. In VB6 or a .NET tool, you’ll probably have to set the font at run-time. You can set this up in a resource file for a C++ program, but IIRC Visual Studio tends to break it.

    Even the shell isn’t immune from wrong-font problems: the Properties window for a file uses MS Sans Serif on Windows XP.

  7. Anonymous says:

    Actually, the shell is "going with the flow". If there are any pages on a property sheet that do NOT use DS_SHELLFONT, then *all* pages lose DS_SHELLFONT so that the property sheet looks consistent.

  8. Anonymous says:

    MS Shell Dlg + DS_SHELLFONT is problematic on VC6 because the resource editor doesn’t let you enter either of those. You have to edit the RC file by hand. Plus, every time you change and save the RC file, DS_SHELLFONT gets lost because VC6 doesn’t know about that style. So before every release you have to remember to change DS_FIXEDSYS to DS_SHELLFONT in the RC file.

  9. Anonymous says:

    You don’t need to do that. DS_SHELLFONT = DS_SETFONT | DS_FIXEDSYS. Since you customized the font to "MS Shell Dlg", you have DS_SETFONT set already; all that’s missing is DS_FIXEDSYS

  10. Anonymous says:

    VC6 will not let you specify MS Shell Dlg as your dialog font. It will replace it with whatever that "font" maps to on your development machine. Mike is right, you have to edit your resources manually…

  11. Anonymous says:

    hmm… I _know_ my dialogs end up with Sans Serif instead of Tahoma (on 2K/XP) if I don’t change DS_FIXEDSYS to DS_SHELLFONT. I’ll have to try your suggestion later today. (It would be so nice not to have to do that change all the time. ;) )

  12. Anonymous says:

    SIZE GetDlgUnits(HWND hwnd)

    {

    RECT r;

    r.left = 4L;

    r.top = 8L;

    MapDialogRect(hwnd, &r);

    return *(SIZE*)&r;

    }

  13. Anonymous says:

    You also need to initialize r.right and r.bottom even though you don’t intend to use them. Otherwise this code will fail on Win9x if the uninitialized garbage is greater than 65535. (Recall that Win9x’s GDI is 16-bit.)

  14. Anonymous says:

    This article shows how you can convert DLUs to pixels when you only have a font handle:

    http://support.microsoft.com/default.aspx?scid=kb;EN-US;145994

  15. Anonymous says:

    Copy of a post at Old New Thing for archival purposes: There are two functions that let you convert dialog units (DLUs) to pixels. One is GetDialogBaseUnits, which returns the base units directly (leaving you to do the math), and…

  16. Anonymous says:

    Raymond,

    I am having problems with high DPI and dialog sizes. This is probably because of my weak design. I want to calculate a dialog’s size under high DPI, and resize parent window according to that size. I am using a child dialog in a frame window, and I am currently using a very neat solution; get the DPI and resize parent to a hard coded size I have written in code.

  17. Anonymous says:

    Bear in mind that you’re going to have problems going between low and high DPI anyway; due to true-type font hinting, any labels or other text which you have in your dialog will not scale linearly.

    (This is why if you switch to 120dpi "Large" font mode from 96dpi, you still need to test all of your resources and dialogs – they will *not* scale safely, and some text will get hidden).

    In other words, be careful. If you’re going to do scaling tricks with dialogs, either test everywhere, or come up with some other mechanism on top of dialog units to handle text which no longer fits.

    If you’re doing localisation (or i8n) work, you’re going to need to worry about expanding text anyway, so you might want to consider all of those problems as one set.

  18. Anonymous says:

    I dunno about System being old and dorky. Nostalgia is in. Windows UI has been going back to the flat look (pre-CTL3D era Windows)… can a dorky bold font be far behind? ;)

    BTW I remembered my dialog font thing. The key is to make the dialog sections be DIALOGEX resources instead of plain DIALOG ones. It looks like DIALOGEX+DS_FIXEDSYS+MS Shell Dlg is the magical trifecta.

  19. Anonymous says:

    >SIZE GetDlgUnits(HWND hwnd)

    >{

    >RECT r;

    >r.left = 4L;

    >r.top = 8L;

    >MapDialogRect(hwnd, &r);

    >return *(SIZE*)&r;

    >}

    Invalid code, breaks aliasing rules, and wrong code will be generated on good optimizing compilers or CPU with more registers than x86 (say, x86-64).

  20. Anonymous says:

    It’s bad because the RECT structure is not completely filled out (as pointed out earlier). However, it’s otherwise valid C. If a compiler generates bad code as a result of optimisation here, then the compiler is buggy.

    The thing with aliasing is that the compiler has to be conservative if it can’t prove that no aliasing will occur. Some compilers (e.g. Visual C++) have a switch that lets you tell it that it’s okay to assume no aliasing will occur, but that has to be a user supplied option, not the default.

    You can help the compiler out in some cases by explicitly spilling to local variables, which lets it deduce that aliasing cannot occur. This is certainly a valid optimisation on CPUs with more registers, but it shouldn’t be required for correct (as opposed to optimal) code generation.

  21. Anonymous says:

    Mention of DIALOGEX is made in multiple places but apparently needs to be made in still more places so people won’t be caught out by this.

    http://msdn.microsoft.com/library/en-us/apcompat/apcompat/use_dialogex_to_have_the_system_font.asp

    http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/dialogboxes/aboutdialogboxes.asp

    "To have your application use the system font no matter which system it is running on, use DS_SHELLFONT with the typeface MS Shell Dlg, and use the DIALOGEX Resource instead of the DIALOG Resource. Note that DS_SHELLFONT has no effect if the typeface is not MS Shell Dlg."

    This para is linked to from the list of dialog box styles. Where else should I drop hint about DIALOGEX?

  22. Anonymous says:

    See, that’s why I always change DS_FIXEDSYS to DS_SHELLFONT – the docs told me to. ;)

  23. Anonymous says:

    Tony (hello btw, do you remember me?): The point is that both C and C++ standards say that two pointers of different type can *never* alias (unless they are char*, which has a special rule so that it can alias everything). This is why the source code is invalid and undefined behaviour occurs.

    Whether the compilers are smart enough to enforce this rule or not, it’s irrelevant. GCC for instance will do that by default when optimizations are on.

  24. Anonymous says:

    I think that the statement about pointer aliasing is fairly wrong. Consider base class and derived class pointers, for one. If you like, I can look up aliasing in my copy of "The C++ Programming Language", but I think the base/derived case is enough to disprove it.

  25. Anonymous says:

    My statement was incorrect for C++, what I meant was really "two pointers of different and unrelated types can never alias". For C, it still holds true.

    For C++, the proposed code is equivalent to a reintrerpret_cast<>, and standard says that "Except that converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types

    and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified." (5.2.10/7).

    Notice that, for instance, Linux Kernel abuses such constructurs (breaking C aliasing rules), and in fact they have to compile it with -fno-strict-aliasing.

  26. Anonymous says:

    Two comments here.

    February 17, 2004 7:00 AM by Mr. Chen:

    > Everybody selects a custom font for their

    > dialog box, usually "MS Sans Serif 8" or

    > possibly "Tahoma" if they are one of those

    > hip Windows 2000 folks.

    Too many developers still do exactly that, but others are learning to select fonts properly as discussed in some of the replies. The problem with a developer selecting a named font this way is that the font even gets used for strings that are hard-coded in MFC or the Windows API, and for filenames. The language version of MFC and Windows API, and languages that were used in naming files, frequently cannot be displayed through a font that gets imposed this way. For any dialog that might contain any strings that the developer didn’t hard-code, the developer had better ask the OS which font to use.

    2/20/2004 8:50 AM Giovanni Bajo:

    > My statement was incorrect for C++, what I

    > meant was really "two pointers of different

    > and unrelated types can never alias". For C,

    > it still holds true.

    For C it still doesn’t hold true. The rules for dereferencing are different from the rules for merely storing a pointer value.

    By the way Mr. Chen, thank you for some more great articles during recent weeks. (If I have time to catch up and then magically have time to post more responses then I have a few more complaints about Windows and the MSDN library waiting in the queue. MSDN library documentation atrociously does not conform to the present version of the SDK. But it will be a while before I really have time to catch up.)

Comments are closed.