• The Old New Thing

    There's an awful lot of overclocking out there

    • 76 Comments

    A bunch of us were going through some Windows crashes that people sent in by clicking the "Send Error Report" button in the crash dialog. And there were huge numbers of them that made no sense whatsoever. For example, there would be code sequences like this:

       mov ecx, dword ptr [someValue]
       mov eax, dword ptr [otherValue]
       cmp ecx, eax
       jnz generateErrorReport
    

    Yet when we looked at the error report, the ecx and eax registers were equal! There were other crashes of a similar nature, where the CPU simply lots its marbles and did something "impossible".

    We had to mark these crashes as "possibly hardware failure". Since the crash reports are sent anonymously, we have no way of contacting the submitter to ask them follow-up questions. (The ones that the group I was in was investigating were failures that were hit only once or twice, but were of the type that were deemed worthy of close investigation because the types of errors they uncovered—if valid—were serious.)

    One of my colleagues had a large collection of failures where the program crashed at the instruction

      xor eax, eax
    

    How can you crash on an instruction that simply sets a register to zero? And yet there were hundreds of people crashing in precisely this way.

    He went through all the published errata to see whether any of them would affect an "xor eax, eax" instruction. Nothing.

    He sent email to some Intel people he knew to see if they could think of anything. [Aside from overclocking, of course. - Added because people apparently take my stories hyperliterally and require me to spell out the tiniest detail, even the stuff that is so obvious that it should go without saying. I didn't want to give away the story's punch line too soon!] They said that the only [other] thing they could think of was that perhaps somebody had mis-paired RAM on their motherboard, but their description of what sorts of things go wrong when you mis-pair didn't match this scenario.

    Since the failure rate for this particular error was comparatively high (certainly higher than the one or two I was getting for the failures I was looking at), he requested that the next ten people to encounter this error be given the opportunity to leave their email address and telephone number so that he could call them and ask follow-up questions. Some time later, he got word that ten people took him up on this offer, and he sent each of them e-mail asking them various questions about their hardware configurations, including whether they were overclocking. [- Continuing from above aside: See? Obviously overclocking was considered as a possibility.]

    Five people responded saying, "Oh, yes, I'm overclocking. Is that a problem?"

    The other half said, "What's overclocking?" He called them and walked them through some configuration information and was able to conclude that they were indeed all overclocked. But these people were not overclocking on purpose. The computer was already overclocked when they bought it. These "stealth overclocked" computers came from small, independent "Bob's Computer Store"-type shops, not from one of the major computer manufacturers or retailers.

    For both groups, he suggested that they stop overclocking or at least not overclock as aggressively. And in all cases, the people reported that their computer that used to crash regularly now runs smoothly.

    Moral of the story: There's a lot of overclocking out there, and it makes Windows look bad.

    I wonder if it'd be possible to detect overclocking from software and put up a warning in the crash dialog, "It appears that your computer is overclocked. This may cause random crashes. Try running the CPU at its rated speed to improve stability." But it takes only one false positive to get people saying, "Oh, there goes Microsoft blaming other people for its buggy software again."

  • The Old New Thing

    The end of one of the oldest computers at Microsoft still doing useful work

    • 45 Comments

    My building was scheduled for a carpet replacement—in all my years at Microsoft, I think this is the first time this has ever happened to a building I was in—so we all had to pack up our things so the carpeters could get clear access to the floor. You go through all the pain of an office move (packing all your things) but don't get the actual reward of a new office.

    One of the machines in my office probably ranked high on the "oldest computer at Microsoft still doing useful work" charts. It was a 50MHz 486 with 12MB of memory and 500 whole megabytes of disk space. (Mind you, it wasn't born this awesome. It started out with only 8MB of memory and 200MB of disk space, but I upgraded it after a few years.) This machine started out its life as a high-end Windows 95 test machine, then when its services were no longer needed, I rescued it from the scrap heap and turned it into my little web server where among other things, Microsoft employees could read my blog article queue months before publication. It also served as my "little computer for doing little things". For example, the Internet Explorer test team used it for FTP testing since I installed a custom FTP server onto it. (Therefore, I could make it act like any type of server, or like a completely bizarro server if a security scenario required it.) It also housed various "total wastes of time" such as the "What's Raymond doing right now?" program, and the "Days without a pony" web page.

    I added a CD-ROM drive, which cost me $200. This was back in the days when getting a CD-ROM drive meant plugging in a custom ISA card and installing a MS-DOS driver into the CONFIG.SYS file. Like an MS-DOS driver gets you anywhere any more. I had to write my own driver for it.

    I took it as a challenge to see how high I could get the machine's uptime. Once the hardware stabilized (which went a lot quicker once I gave up trying to get the old network card to stop wedging and just bought a new one), I put it on a UPS that had been gifted to me in exchange for debugging why the company's monitoring software wasn't working on Windows 95. Whenever I had to move offices, I found somebody who wasn't moving and relocated the computer there for a few days. The UPS kept the machine running while I carted it down the hall or into the next building. I think I got the uptime as high as three years before the building suffered a half-day power outage that drained the UPS.

    A few years later, the machine started rebooting for no apparent reason. Turns out the UPS battery itself was dying and generating its own mini-power outages. Ironic that a UPS ended up creating power outages instead of masking them. But on the other hand, it was free, so I can't complain. Without a UPS, the machine became victim of building-wide power outages and office moves.

    Over the years, more and more parts of the machine started to wear out and had to be worked around. The CMOS battery eventually died, so restarting the computer after an outage involved lots of typing. (It always thought the date was January 1983.) The clock also drifted, so I wrote a program to re-synchronize it automatically every few days.

    When I packed up the computer for the recarpeting, I assumed that afterwards, it would fire back up like the trooper it was. But alas, it just sat there. After much fiddling and removal of non-critical hardware, I got it to power on. Now it complains "no boot device".

    The hard drive (or perhaps the hard drive controller) had finally died. The shock of being shut off and restarted proved to be its downfall. Since it's nearly impossible to find replacement parts for a computer this old, I'm going to have to return it to the scrap heap.

    Good-bye, old friend. But you won't be forgotten. I'm going to transfer your name and IP address to another computer I rescued from the scrap heap many years ago for just this eventuality. But still no mouse.

    (Alas, this was the first of a series of computers to reach retirement age within days of each other. Perhaps I'll eulogize those other machines someday.)

  • The Old New Thing

    The dialog manager, part 9: Custom accelerators in dialog boxes

    • 2 Comments
    Along lines similar to last time, you can also add custom accelerators to your dialog box. (In a sense, this is a generalization of custom navigation, since you can make your navigation keys be accelerators.)

    So let's use accelerators to navigate instead of picking off the keys manually. Our accelerator table might look like this:

    IDA_PROPSHEET ACCELERATORS
    BEGIN
        VK_TAB      ,IDC_NEXTPAGE       ,VIRTKEY,CONTROL
        VK_TAB      ,IDC_PREVPAGE       ,VIRTKEY,CONTROL,SHIFT
    END
    

    Here you can see my comma placement convention for tables. I like to put commas at the far end of the field rather than jamming it up against the last word in the column. Doing this makes cut/paste a lot easier, since you can cut a column and paste it somewhere else without having to go back and twiddle all the commas.

    Assuming you've loaded this accelerator table into the variable "hacc", you can use that table in your custom dialog loop:

    while (<dialog still active> &&
           GetMessage(&msg, NULL, 0, 0, 0)) {
     if (!TranslateAccelerator(hdlg, hacc, &msg) &&
         !IsDialogMessage(hdlg, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
    }
    

    The TranslateAccelerator function checks if the message matches any entries in the accelerator table. If so, then it posts a WM_COMMAND message to the window passed as its first parameter. In our case, we pass the dialog box handle. Not shown above is the WM_COMMAND handler in the dialog box that responds to IDC_NEXTPAGE and IDC_PREVPAGE by performing a navigation.

    The same as last time, if you think there might be modeless dialogs owned by this message loop, you will have to do filtering so that you don't pick off somebody else's navigation keys.

    while (<dialog still active> &&
           GetMessage(&msg, NULL, 0, 0, 0)) {
     if (!((hdlg == msg.hwnd || IsChild(hdlg, msg.hwnd)) &&
           !TranslateAccelerator(hdlg, hacc, &msg)) &&
         !IsDialogMessage(hdlg, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
    }
    

    Okay, I think that's enough of dialog boxes for now.

  • The Old New Thing

    The dialog manager, part 8: Custom navigation in dialog boxes

    • 1 Comments

    Some dialog boxes contain custom navigation that goes beyond what the IsDialogMessage function provides. For example, property sheets use Ctrl+Tab and Ctrl+Shift+Tab to change pages within the property sheet. Remember the core of the dialog loop:

    while (<dialog still active> &&
           GetMessage(&msg, NULL, 0, 0, 0)) {
     if (!IsDialogMessage(hdlg, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
    }
    

    (Or the modified version we created in part 7.)

    To add custom navigation, just stick it in before calling IsDialogMessage.

    while (<dialog still active> &&
           GetMessage(&msg, NULL, 0, 0, 0)) {
     if (msg.message == WM_KEYDOWN &&
         msg.wParam == VK_TAB &&
         GetKeyState(VK_CONTROL) < 0) {
      ... do custom navigation ...
     } else if (!IsDialogMessage(hdlg, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
    }
    

    After retrieving a message, we check whether it was Ctrl+Tab before dispatching it or indeed even before letting IsDialogMessage see it. If so, then treat it as a navigation key.

    Note that if you intend to have modeless dialogs controlled by this message loop, then your test needs to be a little more focused, because you don't want to pick off keyboard navigation keys destined for the modeless dialog.

    while (<dialog still active> &&
           GetMessage(&msg, NULL, 0, 0, 0)) {
     if ((hdlg == msg.hwnd || IsChild(hdlg, msg.hwnd)) &&
         msg.message == WM_KEYDOWN &&
         msg.wParam == VK_TAB &&
         GetKeyState(VK_CONTROL) < 0) {
      ... do custom navigation ...
     } else if (!IsDialogMessage(hdlg, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
    }
    

    Next time, we'll see another way of accomplishing this same task.

  • The Old New Thing

    The dialog manager, part 7: More subtleties in message loops

    • 10 Comments

    Last time, we solved the problem with the EndManualModalDialog function by posting a harmless message. Today, we're going to solve the problem in an entirely different way.

    The idea here is to make sure the modal message loop regains control, even if all that happened were incoming sent messages, so that it can detect that the fEnded flag is set and break out of the modal loop.

    Instead of changing the EndManualModalDialog function, we will change the modal message loop.

    int DoModal(HWND hwnd)
    {
     DIALOGSTATE ds = { 0 };
     HWND hdlg = CreateDialogParam(g_hinst, MAKEINTRESOURCE(1),
                 hwnd, DlgProc, reinterpret_cast<LPARAM>(&ds));
     if (!hdlg) {
      return -1;
     }
    
     EnableWindow(hwnd, FALSE);
     MSG msg;
     msg.message = WM_NULL; // anything that isn't WM_QUIT
     while (!ds.fEnded) {
      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
       if (msg.message == WM_QUIT) { /*  fixed 8am */
        break;
       } else if (!IsDialogMessage(hdlg, &msg)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
       } /* fixed 10am */
      } else if (!ds.fEnded) {
       WaitMessage();
      }
     }
     if (msg.message == WM_QUIT) {
      PostQuitMessage((int)msg.wParam);
     }
     EnableWindow(hwnd, TRUE);
     DestroyWindow(hdlg);
     return ds.iResult;
    }
    

    We changed the call to GetMessage into a call to the PeekMessage function, asking to remove the peeked message if any. Like GetMessage, this delivers any incoming sent messages, then checks if there are any posted messages in the queue. The difference is that whereas GetMessage keeps waiting if there are no posted message, PeekMessage returns and tells you that there were no posted messages.

    That's the control we want. If PeekMessage says that it couldn't find a posted message, we check our fEnded flag once again, in case an incoming sent message set the fEnded flag. If not, then we call the WaitMessage function to wait until there is something to do (either an incoming sent message or a posted message).

    Exercise: If the whole point was to regain control after sent messages are delivered, why isn't there a test of the fEnded flag immediately after DispatchMessage returns?

  • The Old New Thing

    The dialog manager, part 6: Subtleties in message loops

    • 3 Comments

    Last time, I left you with a homework exercise: Find the subtle bug in the interaction between EndManualModalDialog and the modal message loop.

    The subtlety is that EndManualModalDialog sets some flags but does nothing to force the message loop to notice that the flag was actually set. Recall that the GetMessage function does not return until a posted message arrives in the queue. If incoming sent messages arrive, they are delivered to the corresponding window procedure, but the GetMessage function doesn't return. It just keeps delivering incoming sent messages until a posted message finally arrives.

    The bug, therefore, is that when you call EndManualModalDialog, it sets the flag that tells the modal message loop to stop running, but doesn't do anything to ensure that the modal message loop will wake up to notice. Nothing happens until a posted message arrives, which causes GetMessage to return. The posted message is dispatched and the while loop restarted, at which point the code finally notices that the fEnded flag is set and breaks out of the modal message loop.

    There are a few ways of fixing this problem. The quick solution is to post a meaningless message.

    void EndManualModalDialog(HWND hdlg, int iResult)
    {
     DIALOGSTATE *pds = reinterpret_cast<DIALOGSTATE*>
         (GetWindowLongPtr(hdlg, DWLP_USER));
     if (pds) {
      pds->iResult = iResult;
      pds->fEnded = TRUE;
      PostMessage(hdlg, WM_NULL, 0, 0);
     }
    }
    

    This will force the GetMessage to return, since we made sure there is at least one posted message in the queue waiting to be processed. We chose the WM_NULL message because it doesn't do anything. We aren't interested in what the message does, just the fact that there is a message at all.

    Next time, a different solution to the same problem.

  • The Old New Thing

    The dialog manager, part 5: Converting a non-modal dialog box to modal

    • 9 Comments

    Let's apply what we learned from last time and convert a modeless dialog box into a modal one. As always, start with the scratch program and make the following additions:

    INT_PTR CALLBACK DlgProc(
        HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
     switch (uMsg) {
     case WM_INITDIALOG:
      SetWindowLongPtr(hdlg, DWLP_USER, lParam);
      return TRUE;
     case WM_COMMAND:
      switch (GET_WM_COMMAND_ID(wParam, lParam)) {
      case IDOK:
       EndDialog(hdlg, 2005);
       break;
      case IDCANCEL:
       EndDialog(hdlg, 1776);
       break;
      }
     }
     return FALSE;
    }
    
    int DoModal(HWND hwnd)
    {
     return DialogBox(g_hinst, MAKEINTRESOURCE(1), hwnd, DlgProc);
    }
    
    void OnChar(HWND hwnd, TCHAR ch, int cRepeat)
    {
     switch (ch) {
     case ' ': DoModal(hwnd); break;
     }
    }
    
    // Add to WndProc
       HANDLE_MSG(hwnd, WM_CHAR, OnChar);
    
    // Resource file
    1 DIALOGEX DISCARDABLE  32, 32, 200, 40
    STYLE DS_MODALFRAME | DS_SHELLFONT | WS_POPUP |
          WS_VISIBLE | WS_CAPTION | WS_SYSMENU
    CAPTION "Sample"
    FONT 8, "MS Shell Dlg"
    BEGIN
     DEFPUSHBUTTON "OK",IDOK,20,20,50,14
     PUSHBUTTON "Cancel",IDCANCEL,74,20,50,14
    END
    

    Not a very exciting program, I grant you that. It just displays a dialog box and returns a value that depends on which button you pressed. The DoModal function uses the DialogBox function to do the real work.

    Now let's convert the DoModal function so it implements the modal loop directly. Why? Just to see how it's done. In real life, of course, there would normally be no reason to undertake this exercise; the dialog box manager does a fine job.

    First, we need to figure out where we're going to keep track of the flag we called <dialog still active> last time. We'll keep it in a structure that we hang off the dialog box's DWLP_USER window bytes. (I sort of planned ahead for this by having the DlgProc function stash the lParam into the DWLP_USER extra bytes when the dialog is initialized.)

    // fEnded tells us if the dialog has been ended.
    // When ended, iResult contains the result code.
    
    typedef struct DIALOGSTATE {
     BOOL fEnded;
     int iResult;
    } DIALOGSTATE;
    
    void EndManualModalDialog(HWND hdlg, int iResult)
    {
     DIALOGSTATE *pds = reinterpret_cast<DIALOGSTATE*>
         (GetWindowLongPtr(hdlg, DWLP_USER));
     if (pds) {
      pds->iResult = iResult;
      pds->fEnded = TRUE;
     }
    }
    

    The EndManualModalDialog takes the place of the EndDialog function: Instead of updating the dialog manager's internal "is the dialog finished?" flag, we update ours.

    All we have to do to convert our DlgProc from one using the dialog manager's modal loop to our custom modal loop, then, is to change the calls to EndDialog to call our function instead.

    INT_PTR CALLBACK DlgProc(
        HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
     switch (uMsg) {
     case WM_INITDIALOG:
      SetWindowLongPtr(hdlg, DWLP_USER, lParam);
      return TRUE;
     case WM_COMMAND:
      switch (GET_WM_COMMAND_ID(wParam, lParam)) {
      case IDOK:
       EndManualModeDialog(hdlg, 2005);
       break;
      case IDCANCEL:
       EndManualModeDialog(hdlg, 1776);
       break;
      }
     }
     return FALSE;
    }
    

    All that's left is to write the custom dialog message loop.

    int DoModal(HWND hwnd)
    {
     DIALOGSTATE ds = { 0 };
     HWND hdlg = CreateDialogParam(g_hinst, MAKEINTRESOURCE(1),
                 hwnd, DlgProc, reinterpret_cast<LPARAM>(&ds));
     if (!hdlg) {
      return -1;
     }
    
     EnableWindow(hwnd, FALSE);
     MSG msg;
     msg.message = WM_NULL; // anything that isn't WM_QUIT
     while (!ds.fEnded && GetMessage(&msg, NULL, 0, 0)) {
      if (!IsDialogMessage(hdlg, &msg)) {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
      }
     }
     if (msg.message == WM_QUIT) {
      PostQuitMessage((int)msg.wParam);
     }
     EnableWindow(hwnd, TRUE);
     DestroyWindow(hdlg);
     return ds.iResult;
    }
    

    Most of this should make sense given what we've learned over the past few days.

    We start by creating the dialog modelessly, passing a pointer to our dialog state as the creation parameter, which as we noted earlier, our dialog procedure squirrels away in the DWLP_USER window bytes for EndManualModalDialog to use.

    Next we disable the owner window; this is done after creating the modeless dialog, observing the rules for enabling and disabling windows. We then fall into our message loop, which looks exactly like what we said it should look. All we did was substitute !ds.fEnded for the pseudocode <dialog still active>. After the modal loop is done, we continue with the standard bookkeeping: Re-posting any quit message, re-enabling the owner before destroying the dialog, then returning the result.

    As you can see, the basics of modal dialogs are really not that exciting. But now that you have this basic framework, you can start tinkering with it.

    First, however, your homework is to find a bug in the above code. It's rather subtle. Hint: Look closely at the interaction between EndManualModalDialog and the modal message loop.

  • The Old New Thing

    The dialog manager, part 4: The dialog loop

    • 24 Comments

    The dialog loop is actually quite simple. At its core, it's just

    while (<dialog still active> &&
           GetMessage(&msg, NULL, 0, 0, 0)) {
     if (!IsDialogMessage(hdlg, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
    }
    

    If you want something fancier in your dialog loop, you can take the loop above and tinker with it.

    But let's start from the beginning. The work happens in DialogBoxIndirectParam. (You should already know by now how to convert all the other DialogBoxXxx functions into DialogBoxIndirectParam.)

    INT_PTR WINAPI DialogBoxIndirectParam(
        HINSTANCE hinst,
        LPCDLGTEMPLATE lpTemplate, HWND hwndParent,
        DLGPROC lpDlgProc, LPARAM lParam)
    {
     /*
      * App hack!  Some people pass GetDesktopWindow()
      * as the owner instead of NULL.  Fix them so the
      * desktop doesn't get disabled!
      */
     if (hwndParent == GetDesktopWindow())
      hwndParent = NULL;
    

    That's right, we start with an app hack. The problem of passing GetDesktopWindow() instead of NULL was discussed in an earlier entry. So many people make this mistake that we had to put this app hack into the core OS. It would be pointless to make a shim for it since that would mean that thousands of apps would need to be shimmed.

    Since only top-level windows can be owners, we have to take the putative hwndParent (which might be a child window) and walk up the window hierarchy until we find a top-level window.

     if (hwndParent)
      hwndParent = GetAncestor(hwndParent, GA_ROOT);
    

    With that second app hack out of the way, we create the dialog.

     HWND hdlg = CreateDialogIndirectParam(hinst,
                   lpTemplate, hwndParent, lpDlgProc,
                   lParam);
    

    Note: As before, I am going to ignore error checking and various dialog box esoterica because it would just be distracting from the main point of this entry.

    Modal windows disable their parent, so do it here.

     BOOL fWasEnabled = EnableWindow(hwndParent, FALSE);
    

    We then fall into the dialog modal loop:

     MSG msg;
     while (<dialog still active> &&
            GetMessage(&msg, NULL, 0, 0)) {
      if (!IsDialogMessage(hdlg, &msg)) {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
      }
     }
    

    Per the convention on quit messages, we re-post any quit message we may have received so the next outer modal loop can see it.

     if (msg.message == WM_QUIT) {
      PostQuitMessage((int)msg.wParam);
     }
    

    (Astute readers may have noticed an uninitialized variable bug: If EndDialog was called during WM_INITDIALOG handling, then msg.message is never set. I decided to ignore this fringe case for expository purposes.)

    Now that the dialog is complete, we clean up. Remember to enable the owner before destroying the owned dialog.

    if (fWasEnabled)
     EnableWindow(hwndParent, TRUE);
    DestroyWindow(hdlg);
    

    And that's all. Return the result.

     return <value passed to EndDialog>;
    }
    

    Congratulations, you are now an expert on dialog boxes. Tomorrow we'll look at how you can put this new expertise to good use.

    Exercise: Find a way to sneak through the two layers of hwndParent parameter "repair" and end up with a dialog box whose owner is the desktop window. Explain the dire consequences of this scenario.

  • The Old New Thing

    The dialog manager, part 3: Creating the controls

    • 14 Comments

    This is actually a lot less work than creating the frame, believe it or not.

    For each control in the template, the corresponding child window is created. The control's sizes and position is specified in the template in DLUs, so of course they need to be converted to pixels.

      int x = XDLU2Pix(ItemTemplate.x);
      int y = YDLU2Pix(ItemTemplate.y);
      int cx = XDLU2Pix(ItemTemplate.cx);
      int cy = YDLU2Pix(ItemTemplate.cy);
    

    The class name and caption also come from the template. There are also the optional extra bytes pExtra which nobody uses but which remain in the template definition for historical reasons. Once that information has been collected, it's time to make the donuts.

      HWND hwndChild = CreateWindowEx(
                  ItemTemplate.dwExStyle | WS_EX_NOPARENTNOTIFY,
                  pszClass, pwzCaption, ItemTemplate.dwStyle,
                  x, y, cx, cy, hdlg, ItemTemplate.dwId,
                  hinst, pExtra);
    

    Notice that the WS_EX_NOPARENTNOTIFY style is forced on for dialog controls.

    This next part often trips people up. "When I try to create my dialog, it fails and I don't know why." It's probably because one of the controls on the dialog could not be created, usually because you forgot to register the window class for that control. (For example, you forgot to call the InitCommonControlsEx function or you forgot to LoadLibrary the appropriate version of the RichEdit control.)

      if (!hwndChild) {
        DestroyWindow(hdlg);
        return NULL;
      }
    

    The DS_NOFAILCREATE style suppresses the failure check above.

    But if the control did get created, then it needs to be initialized.

      SetWindowContextHelpId(hwndChild, ItemTemplate.dwHelpID);
      SetWindowFont(hwndChild, hf, FALSE);
    

    Repeat once for each item template, and you now have a dialog box with all its child controls. Tell the dialog procedure that it can initialize its child windows, show the (now-ready) dialog box if we deferred the WS_VISIBLE bit when constructing the frame, and return the dialog box to our caller, ready for action.

      // The default focus is the first item that is a valid tab-stop.
      HWND hwndDefaultFocus = GetNextDlgTabItem(hdlg, NULL, FALSE);
      if (SendMessage(hdlg, WM_INITDIALOG, hwndDefaultFocus, lParam)) {
         SetDialogFocus(hwndDefaultFocus);
      }
    
      if (fWasVisible) ShowWindow(hdlg);
      return hdlg;
    }
    

    The SetDialogFocus function we saw last year.

    So there you have it: You have now seen how dialog box sausages are made.

    (Actually, reality is much sausagier, since I skipped over all the app compat hacks! For example, there's a program out there that relies on the subtle placement and absence of the WS_BORDER style to decide whether a control is a combo box or a listbox. I guess the GetClassName function was too much work?)

    I hope this helps you understand a little better how dialog templates fit into the big picture.

  • The Old New Thing

    Neuroscience in the service of basketball fans

    • 3 Comments

    Nick Shulz's Transition Game pointed out Slate's experiment in using neuroscience to distract opposing foul shooters. I thought this worth mentioning now that it seems that the entire country has been swept up in NCAA basketball fever.

Page 377 of 448 (4,472 items) «375376377378379»