What’s wrong with this code, Part 22 – the answers

The other day, I wrote about measuring the dimensions of a piece of text using the DrawText API.

My code worked just great when I initially tested it (obviously it’s a part of a larger chunk of code that does more complicated work).  The problem showed up when I started testing it on a machine running in High DPI mode (144DPI).

The code in question measured some text and then used that text to set the size of a button, when working in low DPI mode (96DPI), the font that is chosen had font metrics that closely matched the font that was used when painting the button.  The problem was that the button font that was used in high DPI mode had dramatically larger font metrics than the low DPI font. 

As a result, the rectangle returned by the initial DrawText call didn’t match the rectangle that was used when the button was painted.  That meant that the button text overflowed the button.

 

The fix to the code is to retrieve the font that is going to be used to draw the button and to use SelectObject to set the font in the memory DC to match the button font.  Once I made that change, the button drew perfectly.

Here’s the corrected code (new code in red):

    HDC hdc = CreateCompatibleDC(NULL); 

   RECT rcText = {0, 0, 88, 34}; 

   HFONT hFont = reinterpret_cast<HFONT>(SendMessage(m_hWndControl, WM_GETFONT, 0, 0)); 
   HFONT hFontOld = reinterpret_cast<HFONT>(SelectObject(hdc, hFont)); 

   DrawText(hdc, L"My Text String", -1, &rcText, DT_CENTER | DT_END_ELLIPSIS | DT_EDITCONTROL | DT_WORDBREAK | DT_NOPREFIX | DT_CALCRECT); 

   CAtlString string; 
   string.Format(L"Text String occupies: %d x %d pixels", rcText.right - rcText.left, rcText.bottom - rcText.top); 
   MessageBox(hWnd, string, L"String Size", 0); 

   SelectObject(hdc, hFontOld); 
   DeleteDC(hdc);

 

And yeah, I’m sure this is old-hat to experienced Windows UI programmers, but it stumped me for several hours so I figured I’d write it up for the blog in case someone else hits the same problem.

Kudos and omissions:

Aaron Ballman caught that I forgot to call DeleteDC when I was done with using the DC.  Stupid boneheaded mistake.

Shog9 and Ivo caught the root cause (selecting the wrong font).

 

And Eldan caught the Large Fonts implication.  There are other ways of catching this, but HighDPI is the easiest.

I also agree with his assertion that font handling of GDI is “complicated”.  On the other hand, the alternative is adding a dozen parameters to the DrawText API (you’d have to add foreground color, background color, font, etc to the call, which would complicate the API dramatically). 

 

PS For those who care about such things: When I’m testing UI changes, I start with standard DPI, then I retest the changes in HighDPI and again using high contrast mode.  Testing in high contrast mode also tests the code in a non themed mode, which helps to catch bugs where I accidentally depended on the theming logic.