• The Old New Thing

    Whatever you do, don't ask for coffee

    • 9 Comments

    Heather Hamilton makes an odd discovery on her flight to Seattle.

  • The Old New Thing

    Adding a lookup control to the dictionary: Just getting it on the screen

    • 0 Comments

    When we last left the dictionary project, we were able to display the dictionary entries but hadn't yet gotten around to searching it. Today, we'll place the lookup control, though we won't hook it up until next time.

    First, we give the edit control an ID and create some member variables to keep track of it.

    class RootWindow : public Window
    {
     ...
     enum {
      IDC_LIST = 1,
      IDC_EDIT = 2,
     };
     ...
    private:
     HWND m_hwndLV;
     HWND m_hwndEdit;
     int  m_cyEdit;
     COLORREF m_clrTextNormal;
     Dictionary m_dict;
    };
    

    Of course, we need to create the edit control, too.

    LRESULT RootWindow::OnCreate()
    {
     ...
     ListView_SetItemCount(m_hwndLV, m_dict.Length());
    
     m_hwndEdit = CreateWindow(TEXT("edit"), NULL,
                      WS_VISIBLE | WS_CHILD | WS_TABSTOP |
                      ES_LEFT | ES_AUTOHSCROLL,
                      0, 0, 0, 0,
                      m_hwnd,
                      (HMENU)IDC_EDIT,
                      g_hinst,
                      NULL);
     if (!m_hwndEdit) return -1;
    
     HFONT hfLV = GetWindowFont(m_hwndLV);
     SetWindowFont(m_hwndEdit, hfLV, FALSE);
    
     m_cyEdit = 0;
     HDC hdc = GetDC(m_hwndEdit);
     if (hdc) {
      HFONT hfPrev = SelectFont(hdc, hfLV);
      if (hfPrev) {
       SIZE siz = { 0, 0 };
       if (GetTextExtentPoint32(hdc, TEXT("0"), 1, &siz)) {
         RECT rc = { 0, 0, siz.cx, siz.cy };
         AdjustWindowRectEx(&rc, GetWindowStyle(m_hwndEdit), FALSE,
                                 GetWindowExStyle(m_hwndEdit));
         m_cyEdit = rc.bottom - rc.top;
       }
       SelectFont(hdc, hfPrev);
      }
      ReleaseDC( m_hwndEdit, hdc);
     }
     if (!m_cyEdit) return -1;
    
     return 0;
    }
    

    After creating it, we give it the same font that the listview is using, so that they match. We then measure that font to figure out how big the edit control needs to be in order to accomodate the text.

    We use this size information to guide how we lay out our window.

    LRESULT RootWindow::HandleMessage(
                              UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
     ...
      case WM_SIZE:
       if (m_hwndEdit) {
        SetWindowPos(m_hwndEdit, NULL, 0, 0,
                     GET_X_LPARAM(lParam), m_cyEdit,
                     SWP_NOZORDER | SWP_NOACTIVATE);
       }
       if (m_hwndLV) {
        SetWindowPos(m_hwndLV, NULL, 0, m_cyEdit,
                     GET_X_LPARAM(lParam),
                     GET_Y_LPARAM(lParam) - m_cyEdit,
                     SWP_NOZORDER | SWP_NOACTIVATE);
       }
       return 0;
     ...
    }
    

    The edit control goes at the top of our client area, and the listview goes directly below it.

    Finally, we add a call to the IsDialogMessage function to our message loop, making the dialog manager do the heavy lifting of navigating around our window via the Tab and Shift+Tab keys.

      RootWindow *prw = RootWindow::Create();
      if (prw) {
       ShowWindow(prw->GetHWND(), nShowCmd);
       MSG msg;
       while (GetMessage(&msg, NULL, 0, 0)) {
        if (IsDialogMessage(prw->GetHWND(), &msg)) {
         /* processed */
        } else {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
        }
       }
      }
    

    When you run this program, observe that the edit control and listview position themselves correctly as you resize the window, and that you can Tab between them. But there's still something wrong: Focus always returns to the listview when you switch away and back. That's because I missed a spot.

    class RootWindow : public Window
    {
    private:
     HWND m_hwndLV;
     HWND m_hwndEdit;
     HWND m_hwndLastFocus;
     int  m_cyEdit;
     ...
    };
    
    LRESULT RootWindow::OnCreate()
    {
     ...
     if (!m_cyEdit) return -1;
    
     m_hwndLastFocus = m_hwndEdit;
    
     return 0;
    }
    
    LRESULT RootWindow::HandleMessage(
                              UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
     ...
      case WM_SETFOCUS:
       if (m_hwndLastFocus) {
        SetFocus(m_hwndLastFocus);
       }
       return 0;
    
      case WM_ACTIVATE:
       if (wParam == WA_INACTIVE) {
        m_hwndLastFocus = GetFocus();
       }
       break;
     ...
    }
    

    The new member variable keeps track of which control had focus last. We update it when we lose activation and restore it when we regain focus. (Its initial value is set at creation so we know whom to give focus to when the window is shown for the first time.)

    Okay, that was an awful lot of typing without very much payoff. Next time, we'll start searching the dictionary.

  • The Old New Thing

    Why is Windows Error Reporting nicknamed "Dr. Watson"?

    • 33 Comments

    The nickname for the feature known as Windows Error Reporting is "Dr. Watson". Where did that name come from?

    As you have probably guessed, The name Dr. Watson was inspired by the character of Dr. Watson, the assistant to Sherlock Holmes in the stories by Arthur Conan Doyle.

    It is my understanding that the doctor was originally developed as part of Windows 3.0 beta testing. His job was to record data about application crashes to a file, so that the file could be uploaded and included with bug reports. The icon was (and continues to be) a friendly doctor using his stethoscope to investigate a problem.

    The Doctor has remained true to the "capture information about an error" aspect of his job. In the meantime, the word "Watson" has expanded its meaning to encompass anonymous end-user feedback mechanisms in general, such as "Content Watson". (But if you hear "Watson" by itself, the speaker is almost certainly talking about error reporting.)

  • The Old New Thing

    How can a company get access to Windows Error Reporting data?

    • 38 Comments

    What happens to all the crashes in programs not written by Microsoft that are submitted via Windows Error Reporting? Microsoft still collects and indexes them, and vendors can sign up to gain access to the error reporting database to see the crashes in their programs. The service is free, although it does require a Verisign ID so that the Winqual system can confirm that you are who you claim you are. (It would be bad if somebody could pretend to be, say, Adobe, and get all their crash data.)

    There will be a session at the PDC which will go into how you the software developer can take advantage of all this data in order to improve your product from pre-release through general availability and even into sustained engineering.

    Windows Vista: Improving your Products Code Quality through Windows Feedback Services

    Your customers are sharing their pain and product experiences with you. Are you listening? Learn how to use the updated automated feedback mechanisms in Windows to gather, view and act upon customer Feedback data. Windows Feedback data will enable you to improve quality early in your product cycle, prioritize work and fix problems your users are encountering on a daily basis; even lower your support costs while increasing customer satisfaction and reliability of your applications.

    Yes, it's a kind of boring title. The original title was "Turning dissatisfied customers into gold". I would've used something like "Your program is crashing. Are you listening?" Oh well, the title isn't important. What's important is that you can use this information to make your products better.

  • The Old New Thing

    That's about the size of it

    • 37 Comments

    News reporters seem to have some difficulty reporting the size of things. In order to make things more accessible to their readers, they try to compare it to the size of an everyday object, but to me it seems they try a bit too hard. For example, this story about the recent rescue of the crew of a sunken submarine noted,

    [T]he layers of stretched nylon appeared to be as thick as a 1 1/2-inch cable.

    "As thick as a 1 1/2-inch cable". As opposed to "as thick as a 1 1/2-inch stick" or "as thick as a 1 1/2-inch thing that is about 1 1/2 inches thick". May I humbly suggest, "The layers of stretched nylon appeared to be 1 1/2 inches thick", or if you want to emphasize the shape, "The layers of stretched nylon appeared to form a cable 1 1/2 inches thick".

    On a similar note, back in 1997, All Things Considered talked to Major Mike Morgan, deputy director of the Space Control Center, the people who track space objects 10cm or larger. Major Morgan translated this from metric to American units as follows:

    That's about the size of a three-inch bolt.

    Actually, 10cm is 3.9 inches; if he wanted to remain fixated on bolts, he should've said "That's about the size of a four-inch bolt." (And don't forget that a kilogram weighs about the same as two pounds of pebbles.)

    On the other hand, you can sometimes miss the mark by providing too little information about an everyday object. Describing an inflatable satellite being deployed by the Space Shuttle back in 1996, the NPR space reporter said that it "grew to the size of a giant silver pie pan".

    How big is a giant silver pie pan? I figure about 40cm (18in), that's a pretty big pie. (In reality, NASA says it was about 50 feet [15m] in diameter.)

  • The Old New Thing

    The dangers of messing with activation when handling a WM_ACTIVATE message

    • 5 Comments
    This is basically the same thing as The dangers of playing focus games when handling a WM_KILLFOCUS message, just with activation in place of focus. One developer discovered the hard way that if you mess with activation inside your WM_ACTIVATE handler, things get weird. The author noted that if he posted a message and did the work from the posted message, then everything came out okay.

    A follow-up to the original message noted that passing the SWP_NOACTIVATE flag to the SetWindowPos function solves the problem. Do you understand why?

  • The Old New Thing

    Hey, who flattened the hills?

    • 4 Comments

    This weekend, I went to a friend's house on Mercer Island to see the US Navy Blue Angels flight demonstration team perform. They do this every summer as part of Seattle's Seafair festival, but this was the first time I got to see the show up close and personal. The I-90 bridge is closed for safety reasons, and people are allowed to walk onto the highway (at least for a short distance) to get a better view of the demonstration.

    Two summers ago my friend invited me to his place, but I made the mistake of trying to drive to Mercer Island—traffic was horrific and I finally arrived just as the show was wrapping up. (I did manage to glimpse a plane pass by at very low altitude though.) This year I decided to ride my bicycle to his house. It's just an hour's ride and I don't have to worry about traffic! It seems that somebody flattened the hills along the bike path that follows highway I-90. Okay maybe it's just that I'm in better shape now but I like to think it's because they were nice enough to flatten the hills.

  • The Old New Thing

    The dangers of playing focus games when handling a WM_KILLFOCUS message

    • 8 Comments

    I had noted last year that WM_KILLFOCUS is the wrong time to do field validation. Here's another example of how messing with the focus during a WM_KILLFOCUS message can create confusion.

    Consider an edit control that displays feedback via a balloon tip. For example, password edit controls often warn you if you're typing your password while CapsLock is in effect. One of the things you probably want to do is to remove the balloon tip if the user moves focus to another control, since there's no point telling the user about a problem with something they aren't using. You might be tempted to subclass the edit control and do something like this:

    LRESULT CALLBACK EditSubclass(
        HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
      switch (uMsg) {
      ...
      case WM_KILLFOCUS:
        if (hwndBalloonTip) {
          DestroyWindow(hwndBalloonTip);
          hwndBalloonTip = NULL;
        }
        break;
      ...
      }
      return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam);
    }
    

    When you give this code a shot, it works great... unless the user clicks on the balloon tip itself the edit control's caret (the blinking insertion point thingie) disappears. What happened?

    What happened is that you gummed up the focus change process by destroying the window that focus was going to! The focus change process goes like this:

    • Put focus on new focus window.
    • Send WM_KILLFOCUS to old focus window (if any).
    • Send WM_SETFOCUS to new focus window (if any).

    But in the second step, we destroyed the new focus window. When the focus window is destroyed, the window manager tries to find a new focus window, and it settles upon the edit control itself. This starts a recursive focus change cycle, telling the edit control that it now has focus again.

    Let's look at the flow in this nested focus change scenario when the user clicks on the tooltip window.

    • Put focus on tooltip.
    • Send WM_KILLFOCUS to edit control.
      • EditSubclass destroys the tooltip.
        • Window manager puts focus on the edit control.
        • Nobody to send WM_KILLFOCUS to.
        • Send WM_SETFOCUS to edit control.
          • EditSubclass passes WM_SETFOCUS to the original window procedure.
      • EditSubclass passes WM_KILLFOCUS to the original window procedure.
    • Send WM_SETFOCUS to tooltip - fails (tooltip was destroyed).

    Do you see the problem yet?

    Look at the message traffic as it reaches the original edit control window procedure:

    • WM_SETFOCUS (from the nested focus change)
    • WM_KILLFOCUS (from the original focus change)

    As far as the edit control is concerned, it gained focus then lost it. Therefore, no caret, since the edit control displays a caret only when it has focus, and your recursive focus changing has resulted in the edit control thinking it doesn't have focus even though it does.

    There are many ways out of this mess.

    First, notice that you don't need to subclass the edit control; you can just react to the EN_KILLFOCUS notification. Second, you can respond to the EN_KILLFOCUS by posting yourself a message and destroying the tooltip on receipt of that posted message. By doing it via a posted message, you avoid the recursive focus change since your work is now being done outside a focus change cycle.

  • The Old New Thing

    Have you made any assignments in this space?

    • 35 Comments

    Riffing on Larry's profound distaste for the use of the word 'ask' as a noun (a distaste I share)...

    It's been three years since I heard the question "Have you made any assignments in this space?" during a meeting and I still don't know what the person was trying to say.

  • The Old New Thing

    Why does the Internet Explorer animated logo arrange its frame vertically?

    • 17 Comments

    If you ever tried to build a custom animated logo for Internet Explorer, you cetainly noticed that the frames of the animation are arranged vertically rather than horizontally. Why is that?

    Because it's much more efficient.

    Recall that bitmaps are stored as a series of rows of pixels. In other words, if you number the pixels of a bitmap like this:

    123
    456
    789
    

    then the pixels are stored in memory in the order 123456789. (Note: I'm assuming a top-down bitmap, but the same principle applies to bottom-up bitmaps.) Now observe what happens if you store your animation strip horizontally:

    12
    AB
    34
    CD
    56
    EF
    78
    GH

    These pixels are stored in memory in the order 12345678ABCDEFGH. To draw the first frame requires pixels 1, 2, A and B. The second frame takes 3, 4, C, and D. And so on. Observe that the pixels required for each frame are not contiguous in memory. This means that they occupy different cache lines at least, and for a bitmap of any significant size, they also span multiple memory pages.

    Now consider a vertically-arranged animation strip:

    12
    34
    56
    78
    AB
    CD
    EF
    GH

    Again, the pixels are stored in memory in the order 12345678ABCDEFGH, [typo fixed, 15 Aug] but this time, the pixels of the first frame are 1, 2, 3 and 4; the second frame consists of 5, 6, 7, and 8; and so on. This time, all the pixels for a single frame are adjacent in memory. This means that they can be packed into a small number of cache lines, and reading the pixels for a single image will not force you to jump across multiple pages.

    Let's illustrate with some pictures: Let's say that the large animation is a series of twelve 38x38 frames, for a total bitmap dimension of 38x456. Let's assume further, for the sake of example, that it's a 32bpp bitmap and that the page size is 4KB.

    If the bitmap were stored as a horizontal strip (456x38), then the memory layout would look like this, where I've color-coded each memory page.

    Observe that no matter which frame you draw, you will have to touch every single page since each frame containes a few bytes from each page.

    Storing the bitmap vertically, on the other hand, arranges the pixels like so:

    Notice that with the vertical strip, each frame touches only two or three pages; compare the horizontal strip, where each frame touches seventeen pages. This is quite a savings especially when you realize that most of the time, the only frame being drawn is the first one. The other frames are used only during animation. In other words, this simple change trimmed 60KB out of the normal working set.

Page 366 of 450 (4,494 items) «364365366367368»