Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

What’s wrong with this code, part 22 – Drawing Text…

What’s wrong with this code, part 22 – Drawing Text…

  • Comments 43

Recently I’ve been working on something that I’ve never done before in my almost 24 years at Microsoft. 

 

For the past 23ish years, I’ve been a plumber – all the work I’ve done has been under the covers.  But for the next version of Windows, I decided to stretch my boundaries a bit and try some UI programming.  I’ve just spent the past few days working on a cool change to the volume control (it’s not important what it is, and most people will never know about the change, but those that do will probably agree with me :)).

As part of the change, I needed to measure the dimensions of a text string.  This is a dummy version of some code I wrote, I simply called DrawText with the DT_CALCRECT into a memory DC that I created.

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }
<BEGIN LARRYS CODE>
   HDC hdc = CreateCompatibleDC(NULL);

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

   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);
<END LARRYS CODE> 
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

This is just code I took by using Visual Studio to create a Windows Win32 project and inserting the code between “BEGIN LARRYS CODE” and “END LARRYS CODE”.  The meat of the code is just 3 lines of code.

Even though there’s almost no code here, it still has a bug in it that was quite subtle and took me several hours to find.

  • CreateCompatibleDC requires that you call DeleteDC when you are done with the handle, so there's a GDI handle leak.

  • Aaron, Doh!

    Stupid bug, but not the one I was looking for.

  • Why would you use DT_END_ELLIPSIS if you're trying to measure the rectangle you need to fit the string in?

  • Will, that's a good question that's tied up with the actual use of the code.  It's not germane to the bug however.

    I could probably get rid of the DT_ENDELLIPSIS in my code actually.

  • You wrote that you needed the dimensions of the text but you also init'd the rect.  Not clear if you meant the centered, word broken dimensions or if those flags are the problem (DT_WORDBREAK will look at the rect dimensions).

  • Must your first application window be visible before calling CreateCompatibleDC with NULL?

  • Don't you need to call CreateCompatibleBitmap before using the DC? Or is it not required because you aren't really drawing to it?

  • Larry,

    I am not familiar with the usage of the second and third arguments in your call to string.Format().  Aren't those rectangular quantities graphical in nature and not textual?  In other words, seems you want string lengths not graphical dimensions for those arguments.

    Twain

  • I don't know if others feel the same but I dislike posts like this. Several programming blogs do it. It's like a non-post to me, or a tease. I can usually look at the code and see several bugs but I have no idea if they are the one that's meant to be interesting or not so it never really seems worth it.

    I'd rather the answer was given at the same time as the code. You could put it in spoiler tags for people who want to try to guess, while people who don't (or who have guessed and want to see if they got the the same thing) don't have to wait for the correct answer to be revealed.

    Knowing there are one or more bugs in some code isn't interesting to me. Knowing what the bug(s) is/are, so I can be aware of falling into similar traps, is what's interesting.

    If other people like this style of post (which might well be the case given how popular it seems to be with various blogs) then ignore me and carry on. I can always ignore the posts and wait for the follow-ups with the answers. But I thought I might not be alone in this and it might be worth putting out the idea of giving the solution in a "spoiler box" or something, so people can choose whether or not to reveal the answer. Then everyone's happy.

  • You didn't select a font into the memory DC. So... unless you plan to draw with the default font (ugh), you're gonna get incorrect results.

  • Isn't 'string' a keyword of standard C++?

  • I notice that you are not checking to make sure that CreateCompatibleDC returns a valid DC.  I'm not 100% sure that the application has a current screen before you've issued the ShowWindow() call, but it probably does.

    The more confusing thing is that you are using a variable named string which may cause problems with the STL libraries if you are using any of those.  

    An issue that you can sometimes run into is because each element in your RECT is declared as a LONG, the simple math you do in that format string is done with LONGs.  The format string itself is declaring that your output is %d which is just "int" and so the numbers might and up being strange in your output.  Either typecasting the math result to "int" or using the format string %ld would take care of the particular issue.

  • That's probably not it, but 2 things:

    1) You most likely want to set a nicer font than the default

    2) DT_CENTER looks unnecessary

  • Martin, the window visible thing isn't relevant, the code as written will exhibit the bug (I did check).

    William Bonner: The code dos return a valid DC, since it's a memory DC

    As for the string thing, you're right, I was sloppy.

    Leo: The "What's wrong with this code" posts are all about pointing out gotchas that aren't always obvious, usually after I've spent a long time trying to figure something out.  They're interactive, which allows people to comment (and to point out my mistakes as several people have above).  

    Ivo: DT_CENTER might not be necessary, I'm not sure.  But when I go ahead and actually draw the text, it will have the flag, so I added it.  

    And you're <i>very</i> close on the "nicer font" part of your comment.  The challenge is realizing why chosing a nicer font matters.

  • Doh! That must be it - you need to select the correct font with the correct size, orientation, etc. Unless you really want to use the default font/size. And I second Shog9's "ugh" on this.

Page 1 of 3 (43 items) 123