August, 2005

  • The Old New Thing

    Why is processor affinity inherited by child processes?

    • 23 Comments

    Consider why a typical program launches child processes. (Shell programs like Explorer aren't typical.) It's because the task at hand is being broken down into sub-tasks which for whatever reason has been placed into a child process. An Example of this would be, say, a multi-pass compiler/linker, where each pass is implemented as a separate process in a pipeline.

    Now consider why you might want to set a process's affinity mask to restrict it to a single processor. One reason is that the program may have bugs that cause it to crash or behave erratically on multi-processor machines. This was common for older programs that were written for uni-processor versions of Windows or when multi-processor machines were still prohibitively expensive. In this case, you would launch the program in a suspended state, by passing the CREATE_SUSPENDED flag to the CreateProcess function, then set the processor affinity mask for that process to a single processor, then resume the process's main thread.

    But what if the problem was in a child process of the process you're launching? Since you don't have control over how the process launches its child, you have no way to sneak in and set the child process's processor affinity mask. That's why the processor affinity mask is inherited: If you set it on the parent process, this covers all the child helper processes that process may launch as part of its execution.

    Another reason why you might want to set a process's affinity mask is to restrict its CPU usage. (For example, you might restrict a CPU-intensive application to a single processor of your dual-processor machine.) And again, if the process launches child processes, you want those child processes to be subject to the same restriction as their parent so that the task as a whole remains restricted to a single processor.

    That's why processor affinity is inherited by child processes. Because it's nearly always what you want.

  • The Old New Thing

    When people ask for security holes as features: Silent install of uncertified drivers

    • 79 Comments

    Probably the single greatest source of bluescreen crashes in Windows XP is buggy device drivers. Since drivers run in kernel mode, there is no higher authority checking what they're doing. If some user-mode code runs amok and corrupts memory, it's just corrupting its own memory. The process eventually crashes, but the system stays up. On the other hand, if a driver runs amok and corrupts memory, it's corrupting your system and eventually your machine dies.

    In acknowledgement of the importance of having high-quality drivers, Windows XP warns you when an uncertified driver is being installed. Which leads to today's topic, a question from a device driver author.

    When I try to install any driver, I get a User Consent Dialog box which tells the user that this is an unsigned driver. Is it possible to author a driver installation package that by-passes this user consent dialog box?

    The whole purpose of that dialog is to prevent the situation you desire from happening! [typo fixed 5pm] If you don't want the warning dialog, submit your driver for certification. (For testing purposes, you can sign your drivers with the test root certificate and install the test root certificate before running your setup program. Of course, installing the test root certificate also causes the desktop to read "For test purposes only" as a reminder that your machine is now allowing test-signed drivers to be installed.)

    Driver writers, of course, find the certification process cumbersome and will do whatever they can to avoid it. Because, of course, if you submit your driver for certification, it might fail! This has led to varying degrees of shenanigans to trick the WHQL team into certifying a driver different from the one you intend to use. My favorite stunt was related to my by a colleague who was installing a video card driver whose setup program displayed a dialog that read, roughly, "After clicking OK, do not touch your keyboard or mouse while we prepare your system." After you click OK, the setup program proceeds to move the mouse programmatically all over the screen, opening the Display control panel, clicking on the Advanced button, clicking through various other configuration dialogs, a flurry of activity for what seems like a half a minute. When faced with a setup program that does this, your natural reaction is to scream, "Aaaiiiiigh!"

  • The Old New Thing

    On being attacked by a sidewalk

    • 23 Comments

    Yesterday, I was attacked by a sidewalk.

    I was cycling from work to a friend's house for dinner and was northbound in the shoulder on 172nd Ave NE in Redmond. As I reached the intersection with NE 138th St., I was momentarily distracted, perhaps by my water bottle, I forget. This lapse of attention resulted in my failing to notice that north of NE 138th St., what used to be the shoulder becomes completely consumed by a sidewalk which leaps into the road out of nowhere.

    A sidewalk with a sharp curb.

    Fortunately, the curb curves into position, so instead of running headfirst into a curb, my wheels were merely nudged to the left about sixty centimeters. Still, it was more than enough to throw me flying off the seat onto the ground at around 30kph. Luckily, my rear end took the brunt of the impact, with minor scraping on my right leg and right arm.

    I'm writing this to serve as a warning to anybody who cycles along 172nd Ave NE: There a sidewalk lying in wait at NE 138th St.

    (After dinner, we watched the most recent episode of Battlestar Galactica then camped in the backyard to observe the Perseid meteor shower. I spotted only three meteors. I'm not a very good meteor-spotter—I glimpsed only three—what with my smudgy glasses and poor night vision.)

  • The Old New Thing

    The poor man's way of identifying memory leaks

    • 17 Comments

    There is a variety of tools available for identifying resource leaks, but there's one method that requires no tools or special compiler switches or support libraries: Just let the leak continue until the source becomes blatantly obvious.

    Nightly automated stress testing is a regular part of any project. Some teams use screen savers as the trigger, others use a custom program, still others require manual launching of the stress test, but by whatever means, after you've gone home for the day, your computer connects to a central server and receives a set of tests that it runs all night.

    One of the things that these overnight tests often turn up are memory leaks of one sort or another, identified by the stress team because your program's resource usage has gone abnormally high. But how do you debug these failures? These machines aren't running a special instrumented build with your leak detection tool, so you can't use that.

    Instead, you use the "target-rich environment" principle.

    Suppose you're leaking memory. After fifteen hours of continuous heavy usage, your program starts getting out-of-memory failures. You're obviously leaking something, but what?

    Think about it: If you are leaking something, then there are going to be a lot of them. Whereas things you aren't leaking will be few in number. Therefore, if you grab something at random, it will most likely be a leaked object! In mathematical terms, suppose your program's normal memory usage is 15 megabytes, but for some reason you've used up 1693 megabytes of dynamically-allocated memory. Since only 15 megabytes of that is normal memory usage, the other 1678 megabytes must be the leaked data. If you dump a random address from the heap, you have a greater-than-99% chance of dumping a leaked object.

    So grab a dozen or so addresses at random and dump them. Odds are you'll see the same data pattern over and over again. That's your leak. If it's a C++ object with virtual methods, dumping the vtable will quickly identify what type of object it is. If it's a POD type, you can usually identify what it is by looking for string buffers or pointers to other data.

    Your mileage may vary, but I've found it to be an enormously successful technique. Think of it as applied psychic powers.

  • The Old New Thing

    Adding a lookup control to the dictionary: Searching Pinyin

    • 4 Comments

    Finally we start searching. For now, the search algorithm is going to be very simple: The string you type into the edit control will be treated as the start of a Pinyin word or phrase. We'll make it fancier later.

    Here is where a lot of the groundwork (some of which I called out explicitly and some of which I slipped in without calling attention to it) starts to pay off.

    Up until now, the items in the listview came directly from the dictionary. Of course, when a word is being looked up, we want to reduce the list to those that match the word or phrase being searched for. We will introduce a new member m_vMatch which is a vector of pointers to the items we actually want to display.

    class RootWindow : public Window
    {
     ...
     // const DictionaryEntry& Item(int i) { return m_dict.Item(i); }
     // int Length() { return m_dict.Length(); }
     const DictionaryEntry& Item(int i) { return *m_vMatch[i]; }
     int Length() { return m_vMatch.size(); }
     ...
     void OnCommand(UINT id, UINT cmd);
     void Refilter();
     ...
    private:
     ...
     vector<const DictionaryEntry*> m_vMatch;
    };
    

    By tweaking our Item and Length member functions, we can now render out of the list of matches instead of out of the entire dictionary.

    LRESULT RootWindow::OnCreate()
    {
     ...
     // ListView_SetItemCount(m_hwndLV, Length());
     ...
     m_hwndLastFocus = m_hwndEdit;
     m_vMatch.reserve(m_dict.Length());
     Refilter();
    
     return 0;
    }
    

    Since the list of matches is at most the number of words in the dictionary, we can reserve that size up front and avoid needless reallocations. Once we've done that, we call our new Refilter method to compute the matches (which populates the listview). It is Refilter that will do the ListView_SetItemCount, so there's no point in us doing it here.

    void RootWindow::OnCommand(UINT id, UINT cmd)
    {
     switch (id) {
     case IDC_EDIT:
      switch (cmd) {
      case EN_CHANGE:
       Refilter();
      }
      break;
     }
    }
    
      // add to RootWindow::HandleMessage()
      case WM_COMMAND:
       OnCommand(GET_WM_COMMAND_ID(wParam, lParam),
                 GET_WM_COMMAND_CMD(wParam, lParam));
       break;
    

    We also rebuild the list of matches if the user makes a change to the edit control. This means that there is no need for a "Search" button. The listview auto-filters as you type.

    void RootWindow::Refilter()
    {
     WCHAR szBuf[256];
     DWORD cchBuf = GetWindowText(m_hwndEdit, szBuf, 256);
     m_vMatch.clear();
     for (int i = 0; i < m_dict.Length(); i++) {
      const DictionaryEntry& de = m_dict.Item(i);
      if (StrCmpNIW(de.m_pszPinyin, szBuf, cchBuf) == 0) {
       m_vMatch.push_back(&de);
      }
     }
     ListView_SetItemCount(m_hwndLV, Length());
     ListView_SetItemState(m_hwndLV, -1, 0, LVIS_SELECTED);
     InvalidateRect(m_hwndLV, NULL, FALSE);
    }
    

    Building the list of matches is rather simple and anticlimactic. We get the string the user typed into the edit control and walk through all the words in the dictionary, seeing if the Pinyin begins with the user's typing. If so, then we add it to the match vector.

    Once the match list is built up, we tell the listview how many we found, clear the selection (so that the selection doesn't appear to move around from one word to another as items are filtered in or out), and invalidate the client rectangle to trigger a repaint.

    That's all there is to it. If you run this program and start typing into the edit control, you'll see the list of words in the listview grow and shrink as you type.

    That's all for this month. Next month, we'll work on expanding the scope of the search.

  • 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.)

Page 3 of 5 (42 items) 12345