June, 2011

  • The Old New Thing

    You'd think that with the name scratch, people wouldn't expect it to be around for a long time

    • 61 Comments

    There is a server run by the Windows team called scratch. Its purpose is to act as a file server for storing files temporarily. For example, if you want to send somebody a crash dump, you can copy it to the scratch server and send the person a link. The file server is never backed up and is not designed to be used as a permanent solution for anything.

    The Windows team likes to use the server to test various file server features. For example, the scratch server uses hierarchical storage management and migrates files to tape relatively aggressively, so that the HSM development team can get real-world usage of their feature.

    The file system team will occasionally wipe the hard drives on the server and reformat them with a new version of NTFS, so that they can put the new file system driver through its paces in a situation where it is under heavy load.

    When these sort of "mass extinction events" takes place, you can count on somebody sending out email saying, "Hey, what happened to the sprocket degreaser? It was on \\scratch\temp\sprocket_degreaser, but now I can't find it. I have an automated test that relies on the sprocket degreaser as well as some data files on \\scratch\temp\foobar_test_data, and they're all gone!"

    Um, that's a scratch machine. Why would you put important stuff on it?

    "Um, well..." (scratches forehead)

    Okay, well before we reformatted the hard drive, we copied the data to \\scratch2\save, so try looking there. But remember, the scratch server is for temporary file storage and comes with no service level agreement.

    "Oh, phew, thanks."

    You'd think that with the name scratch, people wouldn't expect it to be around for a long time. Maybe they could call it can_be_reformatted_at_any_time.

    [Raymond is currently on his way to sunny Hawaii; this message was pre-recorded. Mahalo.]

  • The Old New Thing

    Why do Group Policy settings require me to have a degree in philosophy?

    • 11 Comments

    Josh points out that Group Policy settings often employ double-negatives (and what's the difference between turning something off and not configuring it)?

    Group Policy settings are unusual in that they are there to modify behavior that would continue to exist without them. They aren't part of the behavior but rather a follow-on. Suppose that the default behavior is to do XYZ automatically, but due to requests from corporate customers, a Group Policy is added to alter this behavior. The Group Policy for this might look like this:

    Don't do XYZ automatically




    The template for boolean Group Policy settings is

    Blah blah blah




    Consequently, every boolean Group Policy setting is forced into the above format, even if the setting is reverse-sense, as our sample one is. In general, the three settings for a Group Policy mean

    Enabled Change the behavior as described in the title of the group policy.
    Disabled Do not change the behavior and continue with the default behavior.
    Not configured Let somebody else decide.

    The difference between Disabled and Not configured is that when you disable a Group Policy, then you're saying "Restore default behavior." On the other hand, if you don't configure a Group Policy setting, then you're saying "I have no opinion about whether this Group Policy should be enabled or disabled, so keep looking, because there might be another Group Policy Object that does express an opinion."

    Recall that multiple Group Policy Objects can apply to a specific user. For example, a typical user may be subject to a Local Group Policy, a Non-Administrator Local Group Policy, a series of other Group Policies depending on what security groups the user belongs to, and then a User-Specified Group Policy. You can use the Resultant Set of Policy snap-in to see how all these different Group Policy Objects interact.

    The upshot of this is that Group Policy settings often end up using double negatives if the policy is to disable a default behavior. You "Enable" the setting to disable the default behavior, you "Disable" the setting to enable the default behavior, and you leave the setting "Not configured" if you want to let some other Group Policy Object decide. Even when there is a more clever way of wording the options to avoid the double negative, the people who write Group Policy descriptions are so used to double-negatives that it doesn't even occur to them that a particular setting setting permits an optimization. (Either that, or they figure that system administrators are so used to seeing double-negatives, that when it's not there, they get confused!)

  • The Old New Thing

    Swamping the thread pool with work faster than it can drain

    • 9 Comments

    This scenarios is real, but details have been changed to protect the guilty.

    Consider a window showing the top of a multi-page document. The developers found that when the user clicks the down-arrow button on the scroll bar, the program locks up for 45 seconds, over a dozen threads are created, and then suddenly everything clears up and the window displays the final paragraph of the document (i.e., it scrolled all the way to the bottom).

    The problem was traced to queueing tasks to the thread pool faster than they can drain.

    The document is an object which, unlike a window, has no thread affinity. (Naturally, there are critical sections around the various document methods so you don't have corruption if two threads try to modify the document at the same time.) The way to display a different portion of the document is to call a method which changes the viewport location.

    When the user clicks the down-arrow button on the scroll bar, the main thread creates a periodic timer at four fifths of the double-click speed, and each time the timer elapses, it does the equivalent of document.ScrollDown(). The code cancels the timer once the user releases the mouse button.

    The catch is that the document was so complicated that it takes a long time to change the view top and redraw the new view. (I don't remember exactly how long, but let's say it was 700ms. The important thing is that it takes longer than 400ms.)

    Given that set-up, you can see what happens when the user clicks the scroll down-arrow. The initial scroll is initiated, and before it can complete, another scroll is queued to the thread pool. The document view keeps trying to update its position, but the periodic timer generates scroll requests faster than the document view can keep up.

    If that description was too terse, here's a longer version.

    The code for scrolling went something like this:

    OnBeginScrollDown()
    {
     // Start a timer to do the scrolling
     CreateTimerQueueTimer(&htimer, NULL, ScrollAgain, NULL,
         0, GetDoubleClickTime() * 4 / 5, WT_EXECUTEDEFAULT);
    }
    
    OnEndScrollDown()
    {
     if (htimer != NULL) {
      DeleteTimerQueueTimer(NULL, htimer, INVALID_HANDLE_VALUE);
      htimer = NULL;
     }
    }
    
    ScrollAgain(...)
    {
     document.ScrollDown();
    }
    

    (In reality, the program didn't use the CreateTimerQueueTimer function—it had a custom timer queue and a custom thread pool—but the effect is the same.)

    At time T = 0, the user clicks on the scroll bar down-arrow. The UI thread starts the timer with an initial delay of zero and a period of 400ms. The timer fires immediately, and a thread pool thread is asked to run ScrollAgain. The ScrollAgain function calls ScrollDown, which begins the process of scrolling the document.

    At time T = 400ms, the periodic timer fires, and a new thread pool thread is created to service it. Pool thread 2 calls ScrollDown() and blocks.

    At time T = 700ms, the ScrollDown call on pool thread 1 completes, and now pool thread 2 can begin its call to ScrollDown().

    At time T = 800ms, the periodic timer fires again, and pool thread 1 (now idle) is asked to handle it. Pool thread 1 calls ScrollDown() and blocks.

    At time T = 1200ms, the periodic timer fires yet again. This time, there are no idle threads in the thread pool, so the thread pool manager creates yet another thread to service the timer. Pool thread 3 calls ScrollDown() and blocks.

    At time T = 1400ms, the ScrollDown() call issued by pool thread 2 completes. Pool thread 2 now returns to idle. Now the call to ScrollDown() from pool thread 1 (issued at time T = 800ms) can start.

    At time T = 1600ms, the periodic timer fires again, and pool thread 2 is chosen to service it. Pool thread 2 calls ScrollDown() and blocks.

    At time T = 2000ms, the periodic timer fires again, and a new pool thread is created to service it. Pool thread 4 calls ScrollDown() and blocks.

    You can see where this is going, I hope. Work is being generated by the periodic timer at a rate of one work item per 400ms, but it takes 700ms to carry out each work item, and the tasks are serialized on the document. It's like Lucy in the chocolate factory. The document is frantically trying to carry out all the work, and it never manages to catch up. Eventually, the document scrolls all the way to the bottom, and the mass of pent-up calls to ScrollDown() all return immediately since there is no more scrolling possible.

    Now that the document is idle, it can paint, and that's where the user finally sees the document, scrolled all the way to the bottom.

    There are a number of possible solutions here.

    One way is not to queue up another scroll while an old one is still running. Instead, just wait for it to finish, and then issue a new scroll that accumulates all the scrolling that had taken place while you were waiting for the first to complete. This results in jerky scrolling, however, and it creates a lag of up to 700ms between the user releasing the mouse button and scrolling actually stopping.

    Another approach is to disable repainting the entire document when you detect that you are in the document is too complex to scroll quickly case and just scroll the scrollbar thumb. When the user stops scrolling, re-enable painting and boom the document appears at the user's chosen location. This preserves responsiveness, but you lose the ability to see the document as you scroll it.

    I don't know what solution the customer finally went with. I was just there to help with the debugging.

    Bonus example: Larry Osterman describes another situation with the same underlying cause.

    Hidden take-away: Observe that both of these examples illustrate one of the subtle consequences of a design which moves all processing off the UI thread.

    Update: Note that Set­Timer wouldn't have helped here.

    case WM_TIMER:
      if (wParam == SCROLLTIMER) {
        QueueUserWorkItem(ScrollAgain, NULL, WT_EXECUTEDEFAULT);
      }
      ...
    

    Since the processing has been moved off the UI thread, the WM_TIMER messages are free to keep flowing in and queue up work faster than the background thread can keep up.

  • The Old New Thing

    How do I control X-Mouse settings (active window tracking)?

    • 38 Comments

    For quite some time, Windows has had a setting officially called active window tracking but which informally goes by the name X-Mouse, because that was the name of the PowerToy which first exposed the feature. (The PowerToy was in turn so-named because it made the mouse behave in a manner similar to many X window managers.) The setting is exposed to end users in Windows 7 on Make the mouse easier to use under Activate a window by hovering over it with the mouse.

    If you want to write your own PowerToy to control this setting, you can do so by calling the SystemParametersInfo function. There are three settings which collectively control the X-Mouse feature:

    1. SPI_SETACTIVEWINDOWTRACKING: This is the master switch which enables the behavior.
    2. SPI_SETACTIVEWNDTRKZORDER: This controls whether the window is brought to the top when it is activated by active window tracking.
    3. SPI_SETACTIVEWNDTRKTIMEOUT: This controls how long the mouse has to be in a window before it is auto-activated.

    Note that X-Mouse is a user preference. Applications should not change the setting without the user's permission.

  • The Old New Thing

    How do I prevent users from pinning my program to the taskbar?

    • 30 Comments

    A customer wanted to prevent users from pinning their application to the taskbar.

    I have an application that is launched as a helper by a main application. Users shouldn't be launching it directly, but rather should be launching the main application. But since the helper shows up in the taskbar, users may be tempted to right-click on the taskbar icon and select "Pin to taskbar." Unfortunately, this pins the helper program to the taskbar instead of the main application, and launching the helper program directly doesn't work. Is there a way I can prevent users from pinning the helper program?

    It so happens that there are a number of ways of marking your helper program as Don't pin me. Given the description above, the most direct way is probably to set the System.App­User­Model.Prevent­Pinning property on the window created by the helper program.

    Take our scratch program and make the following changes:

    #include <shellapi.h>
    #include <propsys.h>
    #include <propkey.h>
    
    HRESULT MarkWindowAsUnpinnable(HWND hwnd)
    {
     IPropertyStore *pps;
     HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
     if (SUCCEEDED(hr)) {
      PROPVARIANT var;
      var.vt = VT_BOOL;
      var.boolVal = VARIANT_TRUE;
      hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var);
      pps->Release();
     }
     return hr;
    }
    
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     MarkWindowAsUnpinnable(hwnd);
     return TRUE;
    }
    

    I set the PROP­VARIANT manually instead of using Init­Prop­Variant­From­Boolean just to emphasize that the boolVal must be VARIANT_TRUE and not TRUE. In real life, I probably would have used Init­Prop­Variant­From­Boolean.

    Run this program and observe that "Pin this program to taskbar" does not appear on the menu when you right-click on the taskbar button.

    Even better would be to permit pinning, but set the System.App­User­Model.Relaunch­Command, .Relaunch­Display­Name­Resource and optionally .Relaunch­Icon­Resource properties so that if the user tries to pin the helper, it actually pins the main application.

    Start with a new scratch program and make these changes:

    #include <shellapi.h>
    #include <propsys.h>
    #include <propkey.h>
    #include <propvarutil.h>
    
    HRESULT IPropertyStore_SetValue(IPropertyStore *pps,
        REFPROPERTYKEY pkey, PCWSTR pszValue)
    {
     PROPVARIANT var;
     HRESULT hr = InitPropVariantFromString(pszValue, &var);
     if (SUCCEEDED(hr))
     {
      hr = pps->SetValue(pkey, var);
      PropVariantClear(&var);
     }
     return hr;
    }
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     IPropertyStore *pps;
     HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
     if (SUCCEEDED(hr)) {
      IPropertyStore_SetValue(pps,
        PKEY_AppUserModel_ID, L"Contoso.Scratch");
      IPropertyStore_SetValue(pps,
        PKEY_AppUserModel_RelaunchCommand,
        L"notepad.exe %windir%\\system.ini");
      IPropertyStore_SetValue(pps,
        PKEY_AppUserModel_RelaunchDisplayNameResource,
        L"C:\\full\\path\\to\\scratch.exe,-1");
      // optionally also set PKEY_AppUserModel_RelaunchIconResource
      pps->Release();
     }
     return TRUE;
    }
    
    // resource file
    STRINGTABLE BEGIN
     1 "Open system.ini"
    END
    
    

    I'm pulling a fast one here and pretending that Notepad is my main application. Obviously you'd use your actual main application. (I'm also hard-coding the path to my scratch program.)

    When you run this program, right-click on the taskbar button. Observe that the option to run a new copy of the program is called Open system.ini and if you pick it (or use the middle-mouse-button shortcut), Notepad runs. If you pin the program, the pinned icon runs Notepad.

    Even if you don't need to redirect the pinned item to another program, you can use this second technique to pass a custom command line for the pinned icon.

Page 3 of 3 (25 items) 123