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

    In Windows, the directory is the application bundle

    • 70 Comments

    Aaargh! wonders why Windows doesn't just steal the concept of bundles from Apple and package up each application with all its libraries and everything else it needs into a single directory.

    This is such a great idea, it's been around since the early 1990's. It's just that Windows didn't give it a cute named like bundle. It just gave it the boring name directory. In other words, it's a victim of bad marketing. Maybe we should have given it a cute name like... elfgrass.

    The first directory searched by the LoadLibrary function is the directory containing the application. If you put all your supporting libraries in the same directory as your EXE, and if you access non-library data via paths relative to your application directory, then then you have successfully packaged up your application with all its libraries and everything else it needs into a single directory. Congratulations, you pre-invented the bundle.

    Indeed, Microsoft's guidance for over a decade has been to discourage copying files into the System32 directory.

    If this facility has been around for nearly twenty years, why do people still copy files into the System32 directory?

    Probably inertia. "We've always installed that way, and we're not changing it because we don't know what would stop working."

    There may be some people concerned about disk space. "We have a bunch of applications which share some common libraries, and by putting the common libraries in a shared location (instead of in the application directory), we avoid wasting the user's disk space." Which raises the troublesome question: Is disk space still an issue nowadays?

    Some people would say that no, disk space is no longer an issue. Hard drives are hundreds of gigabytes in size. Increasing the size of each application by a few dozen megabytes is just noise.

    Other people would say that yes, disk space is still an issue. (Note how many people got excited when Snow Leopard took up less disk space.) Solid-state hard drives are still limited in size, and even for people with large hard drives, you see them freaking out about the disk space used by things like Windows Update, volume snapshot services, and System Restore. (Nevermind that at least for volume snapshots and System Restore, the disk space is automatically released when disk space starts to run low. It's like getting upset that the disk cache uses so much of your memory even though the computer is not under any memory pressure.)

    Bonus reading: Disk space and Windows 7.

  • The Old New Thing

    How to get Windows Media Player to single-step a video

    • 37 Comments

    I always forget how to do this, so I'm going to write it down so I can look it up later.

    When a video is playing, right-click the volume control slider and select Enhancements, then Play speed settings. (Actually, it also works if you right-click the speaker icon, the Stop button, the Replay button, or the Shuffle button, but the volume control slider is the biggest target.)

    On the Play speed settings dialog, the single-step controls are at the bottom; they look like triangles.

    Update: There's an even easier way.

  • 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 compress files (via NTFS compression) from the command line?

    • 33 Comments

    A customer wanted to know whether there was a way to compress files and directories (in the sense of NTFS compression) from a script or from the command line. They knew about the Properties dialog, but they wanted something scriptable.

    The command-line tool for this is COMPACT.EXE. Type compact /? for usage information.

    The customer liaison was grateful for this information.

    Thanks for the prompt response, and yes, this will meet our customer's need to compress specific files such as *.docx under a particular directory and all its subdirectories.

    Um, *.docx files are already compressed. Compressing them again gains you nothing.

    Bonus reading: Functions for manipulating documents which follow the Open Package Conventions are available in both managed and unmanaged versions. Check out the Packaging Team Blog for more information, including a comparison of the managed and unmanaged versions.

  • The Old New Thing

    Why doesn't my MessageBox wrap at the right location?

    • 44 Comments

    A customer reported that the MessageBox function was wrapping text "at the wrong location."

    Our program displays a message with the MessageBox function, and we use the '\n' character to force line breaks at positions we choose. But we've found that starting in Windows Vista, the line breaks we placed are not working. The MessageBox function is inserting its own line breaks, which interferes with our custom text layout. It used to be that the width of the message box would expand to fit the longest line in the message.

    The MessageBox function is one of those "leave the driving to us" type of functions. You give it a string to display, select which buttons you want, and sit back and relax while the MessageBox function does the work. The trade-off for the simplicity is that you also lose control over the experience. The MessageBox function decides where to place the dialog and how big to make it, which in turn determines where the line breaks go.

    The algorithm used by MessageBox to determine the size of the message box has changed many times over the lifetime of Windows. In Windows 3.1, it was the width of the longest line, or 5/8 of the width of the screen, whichever was smaller. Windows 95 chose the smallest of the following which resulted in a dialog box that fit inside the working area:

    • the width of the longest line,
    • 5/8 of the width of the working area,
    • 3/4 of the width of the working area,
    • 7/8 of the width of the working area,

    Notice that even in Windows XP, the dialog box was not guaranteed to be the width of the longest line. If the longest line was more than 5/8 of the width of the working area, the MessageBox is probably going to insert its own line breaks beyond ones you inserted explicitly.

    Windows Vista changed the algorithm again, in recognition of two things. First, monitors are now much larger than they were in 1995. And second, the consequence of these larger monitors is that the 7/8 rule resulted in message boxes that were unreadable because they were 10 inches wide and half an inch tall. The old algorithm did not age well, but then again, it was written back in the days when the really cool kids had 1024×768 screens. Nowadays, even the kids from the wrong side of the tracks have screens that are regularly 1400 or even 1600 pixels wide.

    The new algorithm merely adds another option to the table of choices:

    • the width of the longest line,
    • 278 DLU,
    • 5/8 of the width of the working area,
    • 3/4 of the width of the working area,
    • 7/8 of the width of the working area,

    Note that the details of the MessageBox line breaking algorithm are provided for historical purposes only. Do not write code which relies on them, because who knows, they may change again in the next version of Windows.

    The algorithm was changed at the request of the visual design team; after all, it's the job of the visual design team to make these sorts of visual design decisions. If you don't want some black-turtleneck-wearing designer in Redmond to decide where your line breaks go, then don't use MessageBox. And you don't have to go and convert every single message box to its own dialog template; just write a function called MessageBoxWithLineBreaksExactlyWhereIPutThem function that takes the same parameters as MessageBox but lays out the text exactly the way you want it. (The DT_CALCRECT flag will be useful here.)

  • The Old New Thing

    What happens when applications try to copy text by sending Ctrl+C

    • 20 Comments

    I'm stealing this story from one of my colleagues.

    I remember one app from a long time ago that had a feature where you could hit a global short cut key (or something like that) to launch the dictionary. It was also smart in that it would detect the current selected text and immediately search the dictionary for that term.

    One day I was running a Perl script that took several hours to run. It was nearly done and for whatever I decided to launch the dictionary. It sent a Ctrl+C to my Perl script and killed it.

    And that's why you don't send Ctrl+C to arbitrary applications.

    Active Accessibility gives you access to the text under the cursor. There's also a newer interface known as UI Automation which has a handy method called IText­Provider::Get­Selection. (On the managed side, you have System.Windows.Automation.Text­Pattern.Get­Selection.)

    Update: Commenter parkrrrr points out the IText­Provider::Get­Selection. is the provider-side interface. The interface for applications wishing to read the selected text is IUI­Automation­Text­Pattern.

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

  • 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

    Generally speaking, if your function fails, you should return a failure code

    • 22 Comments

    A customer requested assistance with their shell namespace extension, and the request worked its way to me for resolution.

    Unhandled exception at 0x76fab89c (shell32.dll) in explorer.exe: 0xC0000005:
    Access violation reading location 0x00000000.
    
    shell32.dll!CShellItem::_GetPropertyStoreWorker()  + 0x44 bytes
    shell32.dll!CShellItem::GetPropertyStoreForKeys()  + 0x38 bytes
    thumbcache.dll!CThumbnailCache::_GetMonikerDataFromShellItem()  + 0x8b bytes
    thumbcache.dll!CThumbnailCache::GetThumbnail()  + 0x11c bytes
    shell32.dll!CSetOperationCallback::_LookupThumbnail()  + 0x8d bytes
    shell32.dll!CSetOperationCallback::_PrefetchCachedThumbnails()  + 0xb6 bytes
    shell32.dll!CSetOperationCallback::OnNextBatch()  + 0x4f bytes
    shell32.dll!CEnumTask::_PushBatchToView()  + 0x68 bytes
    shell32.dll!CEnumTask::_IncrFillEnumToView()  + 0x2ca5 bytes
    shell32.dll!CEnumTask::_IncrEnumFolder()  + 0x8da5a bytes
    shell32.dll!CEnumTask::InternalResumeRT()  + 0xa6 bytes
    shell32.dll!CRunnableTask::Run()  + 0x92 bytes
    browseui.dll!CShellTask::TT_Run()  + 0x2d bytes
    browseui.dll!CShellTaskThread::ThreadProc()  + 0x87 bytes
    browseui.dll!CShellTaskThread::s_ThreadProc()  + 0x21 bytes
    shlwapi.dll!_ExecuteWorkItemThreadProc@4()  + 0xe bytes
    ntdll.dll!_RtlpTpWorkCallback@8()  + 0xaa bytes
    ntdll.dll!_TppWorkerThread@4()  + 0x274 bytes
    kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes
    ntdll.dll!__RtlUserThreadStart@8()  + 0x27 bytes
    

    The customer was at a loss because the customer's code was nowhere on the stack. What is wrong?

    The customer didn't provide a dump file or any other information beyond the stack trace. (Hint: When reporting a problem with a shell namespace extension, at least mention the last few method calls your namespace extension received before the crash.) I was forced to use my psychic powers to solve the problem. But you can, too. All the information you need is right there in front of you.

    The shell faulted on a null pointer in the function CShellItem::_GetPropertyStoreWorker, which from its name is clearly a worker function which obtains the property store from a shell item.

    At this point, you put on your thinking cap. Why is the shell taking a null pointer fault trying to retrieve the property store from a shell item? Remember that the problem is tied to a custom namespace extension.

    My psychic powers tell me that the namespace extension returned S_OK from GetUIObjectOf(IPropertyStoreFactory) but set the output pointer to NULL.

    (It turns out my psychic powers were weak without coffee, because the initial psychic diagnosis was GetUIObjecttOf(IPropertyStore) instead of IPropertyStoreFactory.)

    As a general rule, if your function fails, then you should return a failure code, not a success code. There are exceptions to this rule, particular when OLE automation is involved, but it's a good rule to start with.

    The customer reported that fixing their IShellFolder::BindToObject to return an error code when it failed fixed the problem. The customer then followed up with another crash, again providing startling little information.

    Unhandled exception at 0x763cf7e7 (shell32.dll) in explorer.exe: 0xC0000005: 
    Access violation reading location 0x000a0d70.
    
    Call Stack:
    
    shell32.dll!CInfotipTask::InternalResumeRT() + 0x2e bytes 
    shell32.dll!CRunnableTask::Run() + 0x92 bytes 
    browseui.dll!CShellTask::TT_Run() + 0x2d bytes 
    browseui.dll!CShellTaskThread::ThreadProc() + 0x87 bytes 
    browseui.dll!CShellTaskThread::s_ThreadProc() + 0x21 bytes 
    shlwapi.dll!_ExecuteWorkItemThreadProc@4() + 0xe bytes 
    ntdll.dll!_RtlpTpWorkCallback@8() + 0xaa bytes 
    ntdll.dll!_TppWorkerThread@4() + 0x274 bytes 
    kernel32.dll!@BaseThreadInitThunk@12() + 0x12 bytes 
    ntdll.dll!__RtlUserThreadStart@8() + 0x27 bytes
    

    The customer reported that IQueryInfo::SetInfoTip is getting called. The customer liaison added, "Raymond, I'm looking forward to your psychic powers again."

    Apparently, some people don't understand that psychic powers are not something you ask for. It's my way of scolding you for not providing enough information to make a quality diagnosis possible. You don't come back saying, "Hey, thanks for answering my question even though I did a crappy job of asking it. Here's another crappy question!"

    I reported back that my psychic powers were growing weary from overuse, and that the customer might want to expend a little more time investigating the problem themselves. Especially since it has the same root cause as their previous problem.

    [Raymond is currently away; this message was pre-recorded.]

Page 1 of 3 (25 items) 123