July, 2004

  • The Old New Thing

    How does Add/Remove Programs get the size and other information?

    • 120 Comments

    If the program doesn't provide this information itself, Add/Remove Programs is forced to guess.

    The problem is that there is no "obvious" way to map an entry in the Add/Remove Programs list to an actual program. Each entry in the list, for those who care about such things, comes from the HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall registry key. The only mandatory properties for an uninstallable program are the DisplayName and the UninstallPath. Everything else is optional.

    Let's suppose Add/Remove Programs is given a program registration like this:

    HKEY_LOCAL_MACHINE\
     Software\
      Microsoft\
       Windows\
        CurrentVersion\
         Uninstall\
          SomeProgram
           DisplayName=REG_SZ:"Awesome Program for Windows"
           UninstallPath=REG_SZ:"C:\WINDOWS\uninstall.exe -SomeParameters"
    

    In order to get the "Last Used" and "Frequency" values, Add/Remove Programs needs to know the name of the EXE so it can ask the Start menu "Hey, how often did the user run this program, and when was the last time it happened?"

    Notice that there are no clues in the registration above as to the identity of this EXE file.

    So Add/Remove Programs starts guessing. It goes through all the programs on your Start menu and compares their names with the display name of the uninstallable item. It looks for Start menu items which share at least two words with the words in the DisplayName.

    For example, if there were a Start menu item called "Pretty Decent Windows Program", this would count as a two-word match ("Windows" and "Program").

    It then takes the one with the most matches and decides, "Okay, I guess this is it." Suppose for the sake of illustration that the best match is indeed "Pretty Decent Windows Program.lnk", which is a shortcut to "C:\Program Files\LitWare\Decent Program\Decent.exe". Add/Remove Programs would decide that "Awesome Program for Windows" should get the icon for "Pretty Decent Windows Program.lnk", that the frequency of use and most-recently-used information for "C:\Program Files\LitWare\Decent Program\Decent.exe" will be displayed for "Awesome Program for Windows".

    But wait, there's more. There's also the program size. Add/Remove Programs looks in your "Program Files" directory for directories whose names share at least two words in common with the DisplayName. The best match is assumed to be the directory that the program files are installed into. The sizes are added together and reported as the size of "Awesome Program for Windows".

    A program can add some properties to its registration to avoid a lot of this guessing. It can set an EstimatedSize property to avoid making Add/Remove Programs guess how big the program is. It can also set a DisplayIcon property to specify which icon to show for the program in the list.

    But if a program omits all of these hints, the guess that Add/Remove Programs ends up making can often be ridiculously wide of the mark due to coincidental word matches. In my experience, Spanish suffers particularly badly from this algorithm, due to that language's heavy use of prepositions and articles (which result in a lot of false matches).

    Yes, this is all lame, but when you are forced to operate with inadequate information, lame is the best you can do.

    [July 15 2004: Emphasizing that this is done only if the program fails to provide the information itself. For some reason, a lot of people who link to this page fail to notice that little detail.]

  • The Old New Thing

    Why do some process stay in Task Manager after they've been killed?

    • 35 Comments

    When a process ends (either of natural causes or due to something harsher like TerminateProcess), the user-mode part of the process is thrown away. But the kernel-mode part can't go away until all drivers are finished with the thread, too.

    For example, if a thread was in the middle of an I/O operation, the kernel signals to the driver responsible for the I/O that the operation should be cancelled. If the driver is well-behaved, it cleans up the bookkeeping for the incomplete I/O and releases the thread.

    If the driver is not as well-behaved (or if the hardware that the driver is managing is acting up), it may take a long time for it to clean up the incomplete I/O. During that time, the driver holds that thread (and therefore the process that the thread belongs to) hostage.

    (This is a simplification of what actually goes on. Commenter Skywing gave a more precise explanation, for those who like more precise explanations.)

    If you think your problem is a wedged driver, you can drop into the kernel debugger, find the process that is stuck and look at its threads to see why they aren't exiting. You can use the !irp debugger command to view any pending IRPs to see what device is not completing.

    After all the drivers have acknowledged the death of the process, the "meat" of the process finally goes away. All that remains is the "process object", which lingers until all handles to the process and all the threads in the process have been closed. (You did remember to CloseHandle the handles returned in the PROCESS_INFORMATION structure that you passed to the CreateProcess function, didn't you?)

    In other words, if a process hangs around after you've terminated it, it's really dead, but its remnants will remain in the system until all drivers have cleaned up their process bookkeeping, and all open handles to the process have been closed.

  • The Old New Thing

    Querying information from an Explorer window

    • 37 Comments

    Sometimes software development is inventing new stuff. But often, it's just putting together the stuff you already have. Today's puzzle is one of the latter type of problem.

    Given a window handle, you can you determine (1) whether it is an Explorer window, and if so (2) what folder it is viewing, and (3) what item is currently focused.

    This is not an inherently difficult task. You just have to put together lots of small pieces.

    Start with the ShellWindows object which represents all the open shell windows. You can enumerate through them all with the Item property. This is rather clumsy from C++ because the ShellWindows object was designed for use by a scripting language like JScript or Visual Basic.

     IShellWindows *psw;
     if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,
                                    IID_IShellWindows, (void**)&psw))) {
      VARIANT v;
      V_VT(&v) = VT_I4;
      IDispatch  *pdisp;
      BOOL fFound = FALSE;
      for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK;
           V_I4(&v)++) {
        ...
        pdisp->Release();
      }
      psw->Release();
     }
    

    From each item, we can ask it for its window handle and see if it's the one we want.

       IWebBrowserApp *pwba;
       if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) {
         HWND hwndWBA;
         if (SUCCEEDED(pwba->get_HWND((LONG_PTR*)&hwndWBA)) &&
           hwndWBA == hwndFind) {
           fFound = TRUE;
           ...
         }
         pwba->Release();
       }
    

    Okay, now that we have found the folder via its IWebBrowserApp, we need to get to the top shell browser. This is done by querying for the SID_STopLevelBrowser service and asking for the IShellBrowser interface.

           IServiceProvider *psp;
           if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) {
             IShellBrowser *psb;
             if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,
                                  IID_IShellBrowser, (void**)&psb))) {
               ...
               psb->Release();
             }
             psp->Release();
           }
    

    From the IShellBrowser, we can ask for the current shell view via the QueryActiveShellView method.

               IShellView *psv;
               if (SUCCEEDED(psb->QueryActiveShellView(&psv))) {
                 ...
                 psv->Release();
               }
    

    Of course, what we really want is the IFolderView interface, which is the automation object that contains all the real goodies.

                 IFolderView *pfv;
                 if (SUCCEEDED(psv->QueryInterface(IID_IFolderView,
                                                   (void**)&pfv))) {
                   ...
                   pfv->Release();
                 }
    

    Okay, now we're golden. What do you want to get from the view? How about the location of the IShellFolder being viewed. To do that, we need to use IPersistFolder2::GetCurFolder. The GetFolder method will give us access to the shell folder, from which we ask for IPersistFolder2. (Most of the time you want the IShellFolder interface, since that's where most of the cool stuff hangs out.)

                   IPersistFolder2 *ppf2;
                   if (SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2,
                                                (void**)&ppf2))) {
                     LPITEMIDLIST pidlFolder;
                     if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder))) {
                       ...
                       CoTaskMemFree(pidlFolder);
                     }
                     ppf2->Release();
                   }
    

    Let's convert that pidl into a path, for display purposes.

                       if (!SHGetPathFromIDList(pidlFolder, g_szPath)) {
                         lstrcpyn(g_szPath, TEXT("<not a directory>"), MAX_PATH);
                       }
                       ...
    

    What else can we do with what we've got? Oh right, let's see what the currently-focused object is.

                       int iFocus;
                       if (SUCCEEDED(pfv->GetFocusedItem(&iFocus))) {
                         ...
                       }
    

    Let's display the name of the focused item. To do that we need the item's pidl and the IShellFolder. (See, I told you the IShellFolder is where the cool stuff is.) The item comes from the Item method (surprisingly enough).

                         LPITEMIDLIST pidlItem;
                         if (SUCCEEDED(pfv->Item(iFocus, &pidlItem))) {
                           ...
                           CoTaskMemFree(pidlItem);
                         }
    

    (If we had wanted a list of selected items we could have used the Items method, passing SVGIO_SELECTION.)

    After we get the item's pidl, we also need the IShellFolder:

                           IShellFolder *psf;
                           if (SUCCEEDED(ppf2->QueryInterface(IID_IShellFolder,
                                                              (void**)&psf))) {
                             ...
                             psf->Release();
                           }
    

    Then we put the two together to get the item's display name, with the help of the GetDisplayNameOf method.

                             STRRET str;
                             if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem,
                                                       SHGDN_INFOLDER,
                                                       &str))) {
                               ...
                             }
    

    We can use the helper function StrRetToBuf to convert the kooky STRRET structure into a boring string buffer. (The history of the kooky STRRET structure will have to wait for another day.)

                               StrRetToBuf(&str, pidlItem, g_szItem, MAX_PATH);
    

    Okay, let's put this all together. It looks rather ugly because I put everything into one huge function instead of breaking them out into subfunctions. In "real life" I would have broken things up into little helper functions to make things more manageable.

    Start with the scratch program and add this new function:

    #include <shlobj.h>
    #include <exdisp.h>
    
    TCHAR g_szPath[MAX_PATH];
    TCHAR g_szItem[MAX_PATH];
    
    void CALLBACK RecalcText(HWND hwnd, UINT, UINT_PTR, DWORD)
    {
     HWND hwndFind = GetForegroundWindow();
     g_szPath[0] = TEXT('\0');
     g_szItem[0] = TEXT('\0');
    
     IShellWindows *psw;
     if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,
                                    IID_IShellWindows, (void**)&psw))) {
      VARIANT v;
      V_VT(&v) = VT_I4;
      IDispatch  *pdisp;
      BOOL fFound = FALSE;
      for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK;
           V_I4(&v)++) {
       IWebBrowserApp *pwba;
       if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) {
         HWND hwndWBA;
         if (SUCCEEDED(pwba->get_HWND((LONG_PTR*)&hwndWBA)) &&
           hwndWBA == hwndFind) {
           fFound = TRUE;
           IServiceProvider *psp;
           if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) {
             IShellBrowser *psb;
             if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,
                                  IID_IShellBrowser, (void**)&psb))) {
               IShellView *psv;
               if (SUCCEEDED(psb->QueryActiveShellView(&psv))) {
                 IFolderView *pfv;
                 if (SUCCEEDED(psv->QueryInterface(IID_IFolderView,
                                                   (void**)&pfv))) {
                   IPersistFolder2 *ppf2;
                   if (SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2,
                                                (void**)&ppf2))) {
                     LPITEMIDLIST pidlFolder;
                     if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder))) {
                       if (!SHGetPathFromIDList(pidlFolder, g_szPath)) {
                         lstrcpyn(g_szPath, TEXT("<not a directory>"), MAX_PATH);
                       }
                       int iFocus;
                       if (SUCCEEDED(pfv->GetFocusedItem(&iFocus))) {
                         LPITEMIDLIST pidlItem;
                         if (SUCCEEDED(pfv->Item(iFocus, &pidlItem))) {
                           IShellFolder *psf;
                           if (SUCCEEDED(ppf2->QueryInterface(IID_IShellFolder,
                                                              (void**)&psf))) {
                             STRRET str;
                             if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem,
                                                       SHGDN_INFOLDER,
                                                       &str))) {
                               StrRetToBuf(&str, pidlItem, g_szItem, MAX_PATH);
                             }
                             psf->Release();
                           }
                           CoTaskMemFree(pidlItem);
                         }
                       }
                       CoTaskMemFree(pidlFolder);
                     }
                     ppf2->Release();
                   }
                   pfv->Release();
                 }
                 psv->Release();
               }
               psb->Release();
             }
             psp->Release();
           }
         }
         pwba->Release();
       }
        pdisp->Release();
      }
      psw->Release();
     }
     InvalidateRect(hwnd, NULL, TRUE);
    }
    

    Now all we have to do is call this function periodically and print the results.

    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
        SetTimer(hwnd, 1, 1000, RecalcText);
        return TRUE;
    }
    
    void
    PaintContent(HWND hwnd, PAINTSTRUCT *pps)
    {
      TextOut(pps->hdc, 0, 0, g_szPath, lstrlen(g_szPath));
      TextOut(pps->hdc, 0, 20, g_szItem, lstrlen(g_szItem));
    }
    

    We're ready to roll. Run this program and set it to the side. Then launch an Explorer window and watch the program track the folder you're in and what item you have focused.

    Okay, so I hope I made my point: Often, the pieces you need are already there; you just have to figure out how to put them together. Notice that each of the pieces is in itself not very big. You just had to recognize that they could be put together in an interesting way.

    Exercise: Change this program so it takes the folder and switches it to details view.

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

  • The Old New Thing

    Why can't you trap TerminateProcess?

    • 25 Comments

    If a user fires up Task Manager and clicks "End Task" on your program, Windows first tries to shut down your program nicely, by sending WM_CLOSE messages to GUI programs and CTRL_CLOSE_EVENT events to console programs. But you don't get a chance to intercept TerminateProcess. Why not?

    TerminateProcess is the low-level process killing function. It bypasses DLL_PROCESS_DETACH and anything else in the process. Once you kill with TerminateProcess, no more user-mode code will run in that process. It's gone. Do not pass go. Do not collect $200.

    If you could intercept TerminateProcess, then you would be escalating the arms race between programs and users. Suppose you could intercept it. Well, then if you wanted to make your program unkillable, you would just hang in your TerminateProcess handler!

    And then people would ask for "a way to kill a process that is refusing to be killed with TerminateProcess," and we'd be back to where we started.

    Tomorrow: About those processes that don't go away even though you've killed them. They're really dead, but they won't go away.

  • The Old New Thing

    Why does icon text get a solid background if drop shadows are disabled?

    • 49 Comments

    A commenter asks why icon label have "those ugly color boxes" when there is a background image.

    The answer: Because the alternative would be worse.

    Imagine if there were no solid background between the text and the background image. You would end up with text against an unpredictable background, which doesn't help readability.

    Take the default background for Windows XP: There are some places that are very light and other places that are very dark. No matter what color you pick for the icon text, it will look bad in one or the other place. If you decide to use white, then the text becomes unreadable in the clouds. If you decide to use black, then the text becomes unreadable in the shadows.

    You lose either way.

    The solution is to intercede a contrasting color to ensure that the text is readable. If your video card is powerful enough, the contrasting color is used only just around the strokes of text themselves, which lends a shadow-effect. If shadows are not enabled, then a solid block of contrast is used.

    (And for those of you who say, "Use white in the dark places and black in the light places," what if there is a section of the wallpaper that has a dark area right next to a light area, and the text covers both?)

  • The Old New Thing

    How do you detect "Large Fonts"?

    • 58 Comments

    When people ask, "How do I detect Large Fonts", they aren't really asking how to detect Large Fonts specifically. Rather, Large Fonts is just the most common manifestation of "unusual DPI".

    Windows uses a nominal DPI of 96 pixels per inch. In other words, if Windows wants to draw a line that is one inch long, it draws 96 pixels. Of course, the physical length of this line depends on your screen resolution and the size of your monitor. The value of 96 is merely nominal.

    You can change this DPI setting from the Display control panel, either by choosing Large Fonts, or by choosing a custom font size. Standard size is 96DPI. Large is 120DPI. Custom is, well, custom.

    DPI higher than 96 will become more and more prevalent as LCD technology improves.

    Programs can query the DPI setting by asking GetDeviceCaps for the LOGPIXELSX of the screen DC.

    int GetScreenDPI()
    {
      HDC hdcScreen = GetDC(NULL);
      int iDPI = -1; // assume failure
      if (hdcScreen) {
        iDPI = GetDeviceCaps(hdcScreen, LOGPIXELSX);
        ReleaseDC(NULL, hdcScreen);
      }
      return iDPI;
    }
    

    The code above assumes that pixels are square, which is true of most modern devices. (You can choose an odd screen resolution and get non-square pixels, but most people avoid such resolutions.) Back in the old days, there were many devices with non-square pixels. For example, the EGA video adapter had pixels which were 1.33 times as tall as they were wide.

    For nonsquare-pixel devices, the values of the LOGPIXELSX and LOGPIXELSY metrics will be different. On an EGA, if the value of the LOGPIXELSX metric were 96, then the LOGPIXELSY metric would be 72, since there are only 72 vertical pixels per inch. Similarly, the ASPECTX, ASPECTY and ASPECTXY values for nonsquare-pixel devices will be somewhat interesting as well, as this diagram demonstrates:

    36 27 45

    The ASPECTX is 27 and the ASPECTY is 36, representing the 4:3 ratio of vertical to horizontal, and the ASPECTXY is 45, representing the hypotenuse.

  • The Old New Thing

    Watch out for those sample URLs

    • 29 Comments

    When writing documentation, one often has need to come up with a sample URL to illustrate some point or other. When you do, make sure the sample URL is under your control.

    I remember a Windows beta that used the sample URL http://www.xxxxx.com/ in a dialog box. You can imagine where that actually goes.

    This web site uses www.wallyworld.com as a sample URL. Perhaps they didn't realize that it's a gay porn site.

    (Raymond's strange dream story: One night I dreamt that I found a web site that had a complete Dilbert archive, and for some reason the name of the site was "Wally World". In the morning, I checked out the site and was in for a big surprise...)

    So play it safe. When you need a sample URL, don't just make something up. If you do, odds are good that somebody is going to rush in and register it. Make your sample URLs point back to your company's home page, or use http://www.example.com, which the IANA has reserved for use in sample URLs. If that's too dorky, you can always go out and register the domain you want to use as your sample, so that nobody else can sneak in and steal it. (This does have the problem of incurring renewal fees.)

  • The Old New Thing

    What is the DS_CONTROL style for?

    • 16 Comments

    The DS_CONTROL dialog box style indicates that the dialog being created is being used as the child of another dialog rather than being a top-level dialog in and of itself.

    Nested dialogs are hardly a scary sight. You see them in property sheets, for example. Each page on a property sheet is a separate dialog; they all live inside the outer dialog, the property sheet itself. Nested dialogs are sometimes used in the common file dialogs: You can see one in action if you do a Save As from Notepad. The extra options at the bottom come from a nested dialog.

    When you set the DS_CONTROL style on a dialog template (or set the WS_EX_CONTROLPARENT extended style on a regular window), a bunch of new rules kick in.

    First of all, the WS_CAPTION and WS_SYSMENU styles in your dialog template are ignored. Because you're a child window now, not a top-level window, so you don't get a caption bar or a system menu. (The caption bar and system menu come from the outer window.)

    Next, the dialog navigation functions like GetNextDlgTabItem will recurse into windows marked WS_EX_CONTROLPARENT when they inspect the controls on a dialog box (in GetNextDlgTabItem's case, because it is looking for a control to tab to). Without the extended style, the control search treats the embedded dialog box as one giant control rather than as a container for other controls.

    When you create a dialog with the DS_CONTROL style, you invariably use one of the creation functions like CreateDialogParam rather than one of the dialog box functions like DialogBoxParam, because the modal loop is controlled by the outer dialog, not the inner one.

    The recursive behavior is important to know in order to avoid sending the dialog manager into an infinite loop. When you ask GetNextDlgTabItem to look for the previous item, what it does is take the starting control, then walk through the controls on the dialog until it comes back to the starting point, at which point it returns the one it saw before that one. If you forget to mark your dialog as DS_CONTROL, and focus started out in the sub-dialog, then the control enumeration will not recurse into the sub-dialog and consequently the starting point will never be found. The dialog manager will just keep looping, hunting for that starting-point control and never finding it.

    (This problem exists even without DS_CONTROL. If you start out on a disabled or invisible control, then the walk through the controls will again never find the starting point, since disabled and invisible controls are skipped over when tabbing through a dialog.)

  • The Old New Thing

    The evolution of mascara in Windows UI

    • 86 Comments

    The "look" of the Windows user interface has gone through fashion cycles.

    In the beginning, there was Windows 1.0, which looked very flat because screen resolutions were rather low in those days and color depth was practically nonexistent. If you had 16 colors, you were doing pretty good. You couldn't afford to spend very many pixels on fluff like borders, and shadows were out of the question due to lack of color depth.

    The "flat look" continued in Windows 2.0, but Windows 3.0 added a hint of 3D (notice the beveling in the minimize/maximize buttons and in the toolbar buttons on the help window).

    Other people decided that the 3D look was the hot new thing, and libraries sprung up to add 3D shadow and outlining effects to nearly everything. The library CTL3D.DLL started out as just an Excel thing, but it grew in popularity until it became the "standard" way to make your dialog boxes "even more 3D".

    Come Windows 95, even more of the system had a 3D look. Notice the beveling along the inside edge of the panes in the Explorer window. Furthermore, 3D-ness was turned on by default for all programs that marked themselves as "4.0"; i.e., programs that were designed for Windows 95. For programs that wanted to run on older versions of Windows as well, a new dialog style DS_3DLOOK was added, so that they could indicate that they wanted 3D-ization if available.

    And if the 3D provided by Windows 95 by default wasn't enough, you could use CTL3D32.DLL to make your controls even more 3D. By this point, things started getting really ugly. Buttons on dialog boxes had so many heavy black outlines that it started to look like a really bad mascara job.

    Fortunately, like many fashions that get out of hand, people realized that too much 3D is not a good thing. User interfaces got flatter. Instead of using 3D effects and bold outlines to separate items, subtler dividers were used. Divider lines became more subdued and sometimes disappeared entirely.

    Microsoft Office and Microsoft Money were two programs that embraced the "less is more" approach. In this screenshot from Microsoft Money, observe that the beveling is gone. There are no 3D effects. Buttons are flat and unobtrusive. The task pane separates itself from the content pane by a simple gray line and a change in background shade. Even the toolbar has gone flat. Office 2000 also went largely flat, though some 3D effects linger, in the grooves and in the scrollbars (not visible in picture).

    Windows XP jumped on the "flat is good" bandwagon and even got rid of the separator line between the tasks pane and the contents pane. The division is merely implied by the change in color. "Separation through juxtaposition."

    Office XP and Outlook 2003 continue the trend and flatten nearly everything aside from the scrollbar elements. Blocks of color are used to separate elements onscreen, sometimes with the help of simple outlines.

    So now the pendulum of fashion has swung away from 3D back towards flatness. Who knows how long this school of visual expression will hold the upper hand. Will 3D return with a vengeance when people tire of the starkness of the flat look?

  • The Old New Thing

    Disabling the program crash dialog

    • 48 Comments

    If you don't want your program to display the standard crash dialog, you can disable it by setting the SEM_NOGPFAULTERRORBOX flag in the process error mode.

    The simple-minded way is just to do

    SetErrorMode(SEM_NOGPFAULTERRORBOX);
    

    but this overwrites the previous error mode rather than augmenting it. In other words, you inadvertently turned off the other error modes!

    Unfortunately, there is no GetErrorMode function, so you have to do a double-shuffle.

    DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
    SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
    

    This sets the new error mode (possibly disabling some other error modes that had been set) and saves the previous mode. Then it sets the error mode the right way, adding the SEM_NOGPFAULTERRORBOX flag to the existing error modes.

    Merging with existing error modes is important. For example, the previous error mode may have included SEM_NOALIGNMENTFAULTEXCEPT. If you casually turned that off, then the program would not longer receive automatic alignment fault fixups and will start crashing.

    (But if you read the documentation, you'll see that SEM_NOALIGNMENTFAULTEXCEPT is special: The system won't let you turn it off once it's been turned on. Why? Because too many people were already making this mistake. I remember before this special rule was introduced. Programs were crashing left and right because they didn't do the double-set shuffle above; as a result, they started crashing on alignment faults. So the special rule had to be added. Welcome to the world of compatibility, where it is the operating system's duty to cover for other people's mistakes. Mind you, the design of the SetErrorMode function makes this mistake a very easy one to commit.)

    Note that the error mode is a process-wide setting, not a per-thread setting. This means that manipulating the process error mode is not something you should do lightly, since it may have unintended consequences for other threads (which you might not have control over). For safety's sake, a program should set its error mode when it starts up and not mess with it thereafter.

    Of course, if you disable the crash dialog, then you also miss out on the opportunity to retrieve crash reports collected by Windows Error Reporting so you can see where your program is crashing in the real world.

Page 1 of 4 (33 items) 1234