• The Old New Thing

    Opening the classic folder browser dialog with a specific folder preselected

    • 24 Comments

    Today's Little Program shows how to set the initial selection in the SHBrowse­For­Folder dialog.

    The design of the SHBrowse­For­Folder function had a defect: The BROWSEINFO structure doesn't have a cbSize member at the start. This means that the structure cannot ever change because the function would have no way of knowing whether you are calling with the old structure or the new one. If it weren't for this defect, setting the initial selection would have been easy: Add a pidlInitialSelection member to the structure and have people fill it in.

    Alas, any new functionality in the SHBrowse­For­Folder function therefore requires that the new functionality be expressed within the constraints of the existing structure.

    Fortunately, there's a callback that takes a message number.

    The workaround, therefore, is to express any new functionalty in the form of new callback messages.

    And that's how the ability to set the initial selection in the folder browser dialog came about. A new message BFFM_INITIALIZED was created, and in handling that message, the callback can specify what it wants the selection to be.

    #define UNICODE
    #define _UNICODE
    #define STRICT_TYPED_ITEMIDS
    #include <windows.h>
    #include <ole2.h>
    #include <oleauto.h>
    #include <shlobj.h>
    #include <stdio.h> // horrors! Mixing C and C++!
    
    int CALLBACK Callback(
        HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
    {
     switch (uMsg) {
     case BFFM_INITIALIZED:
      SendMessage(hwnd, BFFM_SETSELECTION, TRUE,
                  reinterpret_cast<LPARAM>(L"C:\\Windows"));
      break;
     }
     return 0;
    }
    
    int __cdecl wmain(int, wchar_t **)
    {
     CCoInitialize init;
     TCHAR szDisplayName[MAX_PATH];
     BROWSEINFO info = { };
     info.pszDisplayName = szDisplayName;
     info.lpszTitle = TEXT("Pick a folder");
     info.ulFlags = BIF_RETURNONLYFSDIRS;
     info.lpfn = Callback;
     PIDLIST_ABSOLUTE pidl = SHBrowseForFolder(&info);
     if (pidl) {
      SHGetPathFromIDList(pidl, szDisplayName);
      wprintf(L"You chose %ls\n", szDisplayName);
      CoTaskMemFree(pidl);
     }
     return 0;
    }
    

    We initialize COM and then call the SHBrowse­For­Folder function with information that includes a callback. The callback responds to the BFFM_INITIALIZED message by changing the selection.

    It's not hard, but it's a bit cumbersome.

    Sorry.

    Bonus chatter: The presence of the callback means that the function cannot shunt the work to a new thread when called from an MTA thread because you are now stuck with the problem of which thread the callback should run on.

    • The callback may want to access resources that belong to the original thread, so that argues for the callback being run on the original thraed.
    • The callback may also want to access resources inside the dialog box, say in order to customize it. That argues for the callback being run on the new thread.

    You can't have it both ways, so you're stuck.

    But it doesn't really matter, because you shouldn't be performing UI from a multi-threaded apartment anyway. There's not much point in making it easier for people to do the wrong thing.

  • The Old New Thing

    Why is CreateToolhelp32Snapshot returning incorrect parent process IDs all of a sudden?

    • 28 Comments

    A customer reported a problem with the Create­Toolhelp32­Snapshot function.

    From a 32-bit process, the code uses Create­Toolhelp32­Snapshot and Process32­First/Process32­Next to identify parent processes on a 64-bit version of Windows. Sporadically, we find that the th32Parent­Process­ID is invalid on Windows Server 2008. This code works fine on Windows Server 2003. Here's the relevant fragment:

    std::vector<int> getAllChildProcesses(int pidParent)
    {
     std::vector<int> children;
    
     HANDLE snapshot = CreateToolhelp32Snapshot(
        TH32CS_SNAPPROCESS, 0);
     if (snapshot != INVALID_HANDLE_VALUE) {
      PROCESSENTRY32 entry;
      entry.dwSize = sizeof(entry); // weird that this is necessary
      if (Process32First(snapshot, &entry)) {
       do {
        if (entry.th32ParentProcessID == pidParent) {
         children.push_back(processEntry.th32ProcessID);
        } while (Process32Next(snapshot, &entry));
      }
      CloseHandle(snapshot);
     }
     return children;
    }
    

    (The customer snuck another pseudo-question in a comment. Here's why it is necessary.)

    One of my colleagues asked what exactly was "invalid" about the process IDs. (This is like the StackOverflow problem where somebody posts some code and says simply "It doesn't work".)

    My colleague also pointed out that the thParent­Process­ID is simply a snapshot of the parent process ID at the time the child process was created. Since process IDs can be recycled, once the parent process exits, the process ID is left orphaned, and it may get reassigned to another unrelated process. For example, consider this sequence of events:

    • Process A creates Process B.
    • Process A terminates, thereby releasing its ID for reuse.
    • Process C is created.
    • Process C reuses Process A's process ID.

    At this point, Process B will have a th32Parent­Process­ID equal to Process A, but since the ID for Process A has been reused for Process C, it will also be equal to Process C, even though there is no meaningful relationship between processes B and C.

    If Process B needs to rely on its parent process ID remaining assigned to that process (and not getting reassigned), it needs to maintain an open handle to the parent process. (To avoid race conditions, this should be provided by the parent itself.) An open process handle prevents the process object from being destroyed, and in turn that keeps the process ID valid.

    There is another trick of checking the reported parent process's creation time and seeing if it is more recent than the child process's creation time. If so, then you are a victim of process ID reuse, and the true parent process is long gone. (This trick has its own issues. For example, you may not have sufficient access to obtain the parent process's creation time.)

    After a few days, the customer liaison returned with information from the customer. It looks like all of the guidance and explanation provided by my colleague either never made it to the customer, or the customer simply ignored it.

    The customer wants to detect what child processes are spawned by a particular application, let's call it P. We built a special version with extra logging, and it shows that the PROCESS­ENTRY32.th32Parent­Process­ID for wininit.exe and csrss.exe were both 0x15C, which is P's process ID. This erroneous reporting occurs while P is still running and continues after P exits. Do you think it's possible that process 0x15C was used by some other process earlier?

    Yes, that possible. That is, in fact, what my colleague was trying to explain.

    It isn't clear why the customer is trying to track down all child processes of process P, but the way to do this is to create a job object and put process P in it. You can then call Query­Information­Job­Object with Job­Object­Basic­Process­Id­List to get the list of child processes.

  • The Old New Thing

    It appears that some programmers think that Windows already ships with that time machine the Research division is working on

    • 41 Comments

    There are some compatibility bugs we investigate where the root cause is that the application relied on time travel. Wait, let me explain.

    An application might issue an asynchronous request for a file to be renamed before they create the file itself. The program happened to work because it took time for the request to get scheduled and reach the file system, and that delay gave the application time to put the file on the disk just in time for the rename operation to see it.

    Another example is an application which installs a shortcut onto the Start menu that points to a file that they haven't installed yet. The installer happened to work because it took time for the Start menu to notice that a new shortcut was created, and by the time it went looking at the shortcut, the installer had copied the target into place.

    Okay, so maybe it's not so much a time machine as a race condition, but the inherent problem is that the application wanted to do some operation that was dependent on a prerequisite, but issued the operations in the wrong order, and they were relying on the fact that they could get the prerequisite done before the operation even noticed the problem.

    It's like writing a check with insufficient funds,¹ hoping that you can deposit money into the account before the check is cashed. If the check-cashing process ever gets optimized (say, by using electronic check presentation), your sneaky trick will stop working and your check will bounce.

    Now, the developer of the application probably wasn't consciously relying on this race condition, but they never noticed the problem during internal testing because they managed always to win the race. (Or maybe they did notice the problem during internal testing, but since it was sporadic, they chalked it up to "unreproducible failures".)

    In the case of the file renaming operation, losing the race condition means that the original file hangs around on the disk without being renamed. In the case of the shortcut, it means that your shortcut appears on the Start menu with a blank icon.

    If you have one operation that relies upon the successful completion of a previous operation, it would be in your best interest to wait for the previous operation to complete before issuing the dependent operation.

    ¹ As technologically advanced as the United States purports to be, it is still quite common that payments between parties are made by sending little pieces of paper back and forth. For those who live in genuinely technologically advanced countries to whom the idea of sending pieces of paper is rather quaint, here's how it works.

    The original model for checks is simple.

    • Alice has an account at Alligator Bank and wishes to send $10 to Bob.
    • Alice writes a check, which is a piece of paper that says roughly "I authorize Alligator Bank to pay $10 from my account to Bob."
    • Alice sends the check to Bob.
    • Bob goes to Alligator Bank and presents the check, along with proof that he is Bob.
    • Alligator Bank confirms the check's validity, deducts $10 from Alice's account, and gives Bob $10. (If this step fails, the check is said to have bounced.)
    • Alligator Bank stamps paid on the check and gives it back to Alice as confirmation that the payment occurred.

    It's inconvenient for Bob to have to go to Alligator Bank to get his money, but he can ask his bank to do it for him.

    • Bob has an account at Bunny Bank.
    • Bob goes to Bunny Bank and presents the check, along with proof that he is Bob.
    • Bunny Bank sends the check to Alligator Bank demanding payment.
    • Alligator Bank confirms the check's validity, deducts $10 from Alice's account, and sends $10 to Bunny Bank.
    • Bunny Bank credits $10 to Bob's account.

    Over the decades, there have been tweaks to the above process, but the basic system remains in place.

    • Instead of an O() algorithm (where each bank contacts each other bank), the system uses an O(n) algorithm (where each bank contacts a central clearinghouse, which then redistributes the checks).
    • Bunny Bank credits Bob's account before receiving confirmation from Alligator Bank that the check is valid.
    • Check images are sent between banks instead of physical checks.

    There is a category of scams that take advantage of the second detail. I'll leave you to read about them yourself.

    Electronic presentation is an alternative process wherein the information on the check is used to create an electronic payment, which is processed almost immediately, and the original check is never processed as a check.

  • The Old New Thing

    Under what circumstances will a dialog box not use the caption specified in the resource file?

    • 6 Comments

    Could it be space aliens?

    Under what circumstances will a dialog box not use the caption specified in the resource file? In particular, we have a modal dialog box that is not using the caption from the resource file. Even if we explicitly call Set­Window­Text from within the WM_INIT­DIALOG handler, the call succeeds but the caption remains unchanged.

    The dialog box's initial title is the value specified in the resource template. And if you set it again in the WM_INIT­DIALOG handler, then that new title overwrites the title from the resource template. Perhaps the problem is that some other code that runs after your WM_INIT­DIALOG handler is changing the title yet again.

    The customer sheepishly wrote back,

    [banging head against the wall]

    Being skeptical that there could ever be anything else overwriting the code I went to debug with Spy++. After some considerable effort I found out that yes, further down ~30 lines there's a call to Set­Window­Text that changes the title to something else.

    Thanks for making me look again.

    Sometimes the fault is not in our stars but in ourselves.

  • The Old New Thing

    What was the relationship between Outlook and Outlook Express?

    • 23 Comments

    Brian wonders whether project Stimpy became Outlook Express.

    As noted in the article, projects Ren and Stimpy were merged into a single project, which became Outlook. You could say that Stimpy became Outlook, too.

    Outlook Express (code name Athena) was originally known as Internet Mail and News. This was back in the day when the cool, hip thing for Web browsers to do was to incorporate as many Internet client features as possible. In the case of Internet Mail and News, this was POP (mail) and NNTP (news).

    After Outlook became a breakout hit, the Internet Mail and News project was renamed to Outlook Express in an attempt to ride Outlook's coattails. It was a blatant grab at Outlook's brand awareness. (See also: SharePoint Workspaces was renamed OneDrive for Business; Lync was renamed Skype for Business.)

    The decision to give two unrelated projects the same marketing name created all sorts of false expectations, because it implied that Outlook Express was a "light" version of Outlook. People expected that Outlook Express could be upgraded to Outlook, or that Outlook Express and Outlook data files were compatible with each other.

    Code name reuse is common at Microsoft, and for a time, the code names Ren and Stimpy were popular, especially for projects that were closely-related. (As I vaguely recall, there was a networking client/server project that called the server Ren and the client Stimpy. But I may be misremembering, and Ren and Stimpy may just have been the names of the two source code servers.) You may have heard the names Ren and/or Stimpy in reference to some other projects. Doesn't mean that your projects are related to any others with the same name.

  • The Old New Thing

    I want you to chase your sisters until they throw up

    • 7 Comments

    A friend of mine grew up in a rural area. The family got their water from a well and had to fluoridate it manually with tablets.

    When my friend was a little girl, she was playing around the house with one of her friends (let's call her friend Alice). They got into the kitchen cabinet and found these candy-like things and ate them. When her mother discovered that they had eaten fluoride tablets, she called the poison control center for advice. In addition to telling her to give the girls something-or-other, they instructed her to keep the girls moving until they vomited up the tablets.

    As it happens, my friend's brother and Alice's brother were playing outside. The boys were called inside, informed of what happened, and instructed to keep their sisters moving and try to get them to throw up.

    The boys couldn't believe their ears. You want us to chase our sisters until they throw up? This must be what heaven is like!

    The boys assumed their responsibilities with great enthusiasm, chasing the girls around the yard, putting them in a swing and spinning them around, all the stuff brothers dream of doing to torment their sisters, but this time they could do it without fear of punishment. Fortunately, the story had a happy ending. The girls soon vomited up the tablets and thereby avoided two horrible fates: (1) fluoride poisoning and (2) being forced to endure torture from their brothers in perpetuity.

  • The Old New Thing

    How do I make a boldface item on a context menu?

    • 8 Comments

    Today's Little Program displays a context menu with a bold item. I sort of talked about this some time ago, but now I'm going to actually do it. Remember, the boldface item represents the default item. You should set it to the action that would have taken place if the user had double-clicked the object.

    Start with our scratch program and make the following changes:

    void OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos)
    {
     HMENU hmenu = CreatePopupMenu();
     AppendMenu(hmenu, MF_STRING, 100, TEXT("&First"));
     AppendMenu(hmenu, MF_STRING, 101, TEXT("&Second"));
     AppendMenu(hmenu, MF_STRING, 102, TEXT("&Third"));
    
     SetMenuDefaultItem(hmenu, 101, FALSE);
    
     TrackPopupMenuEx(hmenu, 0, xPos, yPos, hwnd, nullptr);
    
     DestroyMenu(hmenu);
    }
    
        HANDLE_MSG(hwnd, WM_CONTEXTMENU, OnContextMenu);
    

    Note that for expository purposes (this is a Little Program, after all), I am not heeding the advice I gave some time ago. As a result, this program does not support multiple monitors or keyboard-invoked context menus. Read the linked article for instructions on how to fix the code.

    When you right-click on the window, the On­Context­Menu function creates a pop-up window, fills it with some dummy commands, and says that item 101 should be the default. Then it displays the context menu to the user, throws away the result, and destroys the menu to avoid a memory leak.

    When the menu pops up, the item Second appears in boldface.

  • The Old New Thing

    Flaw reported in Windows 95

    • 24 Comments

    One of my colleagues ran across this old news clipping from 20 years ago today.

    Flaw reported in Windows 95

    THE ASSOCIATED PRESS

    SAN MATEO, Calif. — Microsoft Corp.'s long-awaited Windows 95 operating system has a flaw that can freeze up computers when running certain applications, InfoWorld magazine said Friday.

    The company is aware of the problem but is shipping 450,000 anyway as part of a preview program, the magazine said.

    "I fear that unless Microsoft goes back to the drawing board on this operating system, only light users will get anything out of it," said Nicholas Petreley, the magazine's executive editor.

    He said the system's inability to handle several types of sophisticated applications at the same time made it questionable for business use.

    I can't find a copy of the original InfoWorld article online; all I can find are citations to it, like this one and this one.

    The clipping had a handwritten note attached:

    Bob,
    You guys may want to respond to this.
    Mom

    The report was shortly followed by a rebuttal from Windows Magazine, pointing out that this was a beta release, it is not unusual that an unfinished product has bugs, and that a similar bug in an earlier beta was fixed within two weeks.

    ZOMG! A beta product has a bug!

    I found it cute that my colleague's mother chose to bring this article to her son's attention.

  • The Old New Thing

    Switching on a tuple: Sneaky trick

    • 31 Comments

    This is a sneaky trick, but it's sometimes a handy sneaky trick.

    Suppose you have two values, and you want to switch on the tuple. In other words, you want a switch statement for something like this:

    if (val1 == 1 && val2 == 0) {
     Thing_1_0();
    } else if ((val1 == 1 && val2 == 1 ||
               (val1 == 1 && val2 == 2)) {
     Thing_1_12();
    } else if (val1 == 2 && val2 == 0) {
     Thing_2_0();
    } ... etc ...
    

    You could try writing

    switch (val1, val2) {
    case 1, 0:
        Thing_1_0();
        break;
    case 1, 1:
    case 1, 2:
        Thing_1_12();
        break;
    case 2, 0:
        Thing_2_0();
        break;
    ...
    }
    

    but that doesn't do what you think it does. (Because that comma is a comma operator.)

    The sneaky trick is to pack the two values into a single value that you can then switch on.

    switch (MAKELONG(val1, val2)) {
    case MAKELONG(1, 0):
        Thing_1_0();
        break;
    case MAKELONG(1, 1):
    case MAKELONG(1, 2):
        Thing_1_12();
        break;
    case MAKELONG(2, 0):
        Thing_2_0();
        break;
    ...
    }
    

    Note that there are dangers here beyond craziness. You have to make sure that your packing function is injective (i.e., that it does not assign the same packed value to two different inputs). If you use MAKE­LONG as your packing function, then the two values to be packed must fit into 16-bit integers.

  • The Old New Thing

    What's the difference between PathIsSystemFolder and Protected Operating System Files?

    • 17 Comments

    The way to detect weird directories that should be excluded from the user interface is to check for the FILE_ATTRIBUTE_HIDDEN and FILE_ATTRIBUTE_SYSTEM attributes being set simultaneously. This is the mechanism used when you uncheck Hide protected operating system files in the Folder Options dialog. (Programmatically, you detect whether the user wants to see protected operating system files by checking the fShow­Super­Hidden member of the SHELL­STATE structure.)

    Michael Dunn suggested using Path­Is­System­Folder to detect these special directories, but that is not quite right.

    Path­Is­System­Folder is for marking a directory as "This directory has a nondefault UI behavior attached to it. Please consult the desktop.ini file for more information." You do this when your directory is, say, the root of a namespace extension, or it has been subjected to folder customization. Windows uses it to indicate that the directory has a localized name, as well as other funky internal state.

    There are two ways to mark a folder as having nondefault UI. One is to set the FILE_ATTRIBUTE_READ­ONLY attribute, and the other is to set the FILE_ATTRIBUTE_SYSTEM attribute.

    Either one works, and Path­Is­System­Folder checks for both, returning a nonzero value if either attribute is set.

    In its default configuration, Windows uses the read-only flag to mark folders with nondefault UI. However, some applications mistakenly believe that if a directory is marked read-only, then files within the directory cannot be modified. As a result, these applications refuse to let you save your documents onto the desktop, for example. To work around this, you can use the Use­System­For­System­Folders to tell Windows to use the FILE_ATTRIBUTE_SYSTEM attribute instead. Of course, if you do that, you will run into problems with applications which mistakenly believe that if a directory is marked system, then the directory is inaccessible. So you get to pick your poison.

    Programmers who wish to mark a folder as having nondefault UI should use the Path­Make­System­Folder function to set the appropriate attribute. That function consults the system policy and sets the attribute that the policy indicates should be used to mark folders with nondefault UI.

    Going back to the original question, then: The difference between Path­Is­System­Folder and checking for folders that are marked hidden+system is that they check different things and have different purposes.

    Function Test
    Path­Is­System­Folder ReadOnly or System
    path is protected operating system folder Hidden and System
Page 5 of 450 (4,498 items) «34567»