June, 2011

  • 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

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

    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

    PE resources must be 4-byte aligned, but that doesn't stop people from trying other alignments

    • 38 Comments

    Resources in PE-format files must be stored at offsets which are a multiple of four. This requirement is necessary for platforms which are sensitive to data alignment.

    That doesn't stop people from breaking the rules anyway. After all, it sort of works anyway, as long as you're careful. I mean, sure maybe if somebody running a non-x86 version of Windows tries to read your resources, they will crash, but who uses non-x86 versions of Windows, right?

    In Windows Vista SP1, additional hardening was added to the resource parsing code to address various security issues, but the one that's important today is that tests were made to verify that the data were properly aligned before accessing it. This prevents a file with a misaligned version resource from crashing any program that tried to read its resources. In particular, it is common for programs to read the version resources of arbitrary files—for example, Explorer does it when you view the file's properties or if you turn on the Description column in Details view—so enforcing alignment on resources closes that avenue of remote denial of service.

    And then the bug reports came in. "Program XYZ fails to install" because the program tries to read its own version resources and cannot, because the tool they used to build the program cheated on the alignment requirement and stored the resources at offsets that aren't multiples of 4. "I mean, come on, that wastes like three bytes per resource. Everything still worked when we removed the alignment padding, so we went ahead and shipped it that way."

    Another example of a program that stopped working when the alignment rules were enforced was a computer game expansion pack which could not install because the code that tried to verify that you had the base game found itself unable to read its version resources.

    Multiple programs refused to run, preferring to display the error message "AppName is not a valid Win32 application." Presumably, as part of initialization, they tried to read their own version resources, which failed with ERROR_BAD_EXE_FORMAT, which they then showed to the user.

    The fix was to relax the enforcement of the rules back to the previous level, and impose the stricter requirements only on architectures which raise exceptions on misaligned data. It does mean that you can have a program whose resources can be read on one machine but not on the other, but that was deemed a lesser evil than breaking all the programs which relied on being able to misalign their data without consequence.

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

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

    How do I make a window remain visible even when the user selects Show Desktop?

    • 32 Comments

    A customer had this question:

    I'd like to know how to get a window to remain visible, even when the user has selected Show Desktop. Right now, when the user picks Show Desktop, the desktop appears and covers my window.

    Um, yeah, because that's the whole point of Show Desktop: To show the desktop and get rid of all those windows that are in the way. Windows like yours.

    We're sorry that Windows was unprepared for a program as awesome as yours, because there's no way to mark your window as even if the user says to show the desktop instead of this window, override the user's command and show the window anyway. (They're probably angling for a nice bonus.)

    As a consolation prize, you can create a desktop gadget. Desktop gadgets are part of the desktop and raise with it. It so happens that upon further discussion, the customer was trying to write a clock-type program—this is something very well-suited to gadgetification.

    A different customer had a related question, but disguised it behind another question:

    I noticed that desktop gadgets remain on the desktop even if the user clicks Show Desktop. How does that work? How do gadget stay in front of the desktop when it is shown? What is the trick?

    This was a rather odd question to come through the customer channel. And it probably wasn't just idle curiosity. You don't burn a support request for idle curiosity.

    (While it's probably true that you don't burn a support request just for idle curiosity, I've seen support requests turn into idle curiosity. Once the customers got the answer to their question, they realized that since they had already burned a support request, they may as well pile on with follow-up questions that are just idle curiosity. That was not the case here, because the customer led with the question.)

    The "trick" is that gadgets and the desktop know about each other, so when the user clicks Show Desktop, the desktop covers up all the windows on the screen and then takes the gadgets and places them on the desktop.

    The customer's question is rather odd, because they ask "The system works like X. Are there any tricks for X?" The answer is the rather tautological "The trick for how the system works like X is that the system was designed to do X."

    I suspect that the customer has a secret agenda they are unwilling to share. My guess is that their secret agenda is that they want to write a program that is exempt from being covered by the desktop when the user clicks Show Desktop, and they think they can do it by emulating whatever it is that gadgets do.

    The customer liaison confirmed that that's what the customer is actually trying to do, but that the customer was being coy with the liaison as well and did not explain what the problem scenario was that made them think that they needed a program that is exempt from being covered by the desktop when the user clicks Show Desktop. The customer liaison went back to the customer with the explanation that the way to get the special gadget behavior is to be a gadget, and if they want to pursue it, then writing a gadget is what they need to do.

  • 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

    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