January, 2011

  • The Old New Thing

    Solutions that require a time machine: Making applications which require compatibility behaviors crash so the developers will fix their bug before they ship

    • 76 Comments

    A while ago, I mentioned that there are many applications that rely on WM_PAINT messages being delivered even if there is nothing to paint because they put business logic inside their WM_PAINT handler. As a result, Windows sends them dummy WM_PAINT messages.

    Jerry Pisk opines,

    Thanks to the Windows team going out of their way not to break poorly written applications developers once again have no incentive to clean up their act and actually write working applications. If an application requires a dummy WM_PAINT not to crash it should be made to crash as soon as possible so the developers go in and fix it before releasing their "code".

    In other words, Jerry recommends that Microsoft use the time machine that Microsoft Research has been secretly perfecting for the past few years. (They will sometimes take it out for a spin and fail to cover their tracks.)

    In 1993, Company X writes a program that relies on WM_PAINT messages arriving in a particular order relative to other messages. (And just to make things more interesting, in 1994, Company X goes out of business, or they discontinue the program in question, or the only person who understands the code leaves the company or dies in a plane crash.)

    In 1995, changes to Windows alter the order of messages, and in particular, WM_PAINT messages are no longer sent under certain circumstances. I suspect that the reason for this is the introduction of the taskbar. Before the taskbar, minimized windows appeared as icons on your desktop and therefore received WM_PAINT messages while minimized. But now that applications minimize to the taskbar, minimized windows are sent off screen and never actually paint. The taskbar button does the job of representing the program on the screen.

    Okay, now let's put Jerry in charge of solving this compatibility problem. He recommends that instead of sending a dummy WM_PAINT message to these programs to keep them happy, these programs should instead be made to crash as soon as possible, so that the developers can go in and fix the problem before they release the program.

    In other words, he wants to take the Microsoft Research time machine back to 1993 with a beta copy of Windows 95 and give it to the programmers at Company X and tell them, "Your program crashes on this future version of Windows that doesn't exist yet in your time. Fix the problem before you release your code. (Oh, and by the way, the Blue Jays are going to repeat.)"

    Or maybe I misunderstood his recommendation.

  • The Old New Thing

    Some remarks on VirtualAlloc and MEM_LARGE_PAGES

    • 41 Comments

    If you try to run the sample program demonstrating how to create a file mapping using large pages, you'll probably run into the error ERROR_NOT_ALL_ASSIGNED (Not all privileges or groups referenced are assigned to the caller) when calling Adjust­Token­Privileges. What is going on?

    The Adjust­Token­Privileges enables privileges that you already have (but which are masked). Sort of like how a super hero can't use super powers while disguised as a normal mild-mannered citizen. In order to enable the Se­Lock­Memory­Privilege privilege, you must already have it. But where do you get it?

    You do this by using the group policy editor. The list of privileges says that the Se­Lock­Memory­Privilege corresponds to "Lock pages in memory".

    Why does allocating very large pages require permission to lock pages in memory?

    Because very large pages are not pageable. This is not an inherent limitation of large pages; the processor is happy to page them in or out, but you have to do it all or nothing. In practice, you don't want a single page-out or page-in operation to consume 4MB or 16MB of disk I/O; that's a thousand times more I/O than your average paging operation. And in practice, the programs which use these large pages are "You paid $40,000 for a monster server whose sole purpose is running my one application and nothing else" type applications, like SQL Server. Those applications don't want this memory to be pageable anyway, so adding code to allow them to be pageable is not only a bunch of work, but it's a bunch of work to add something nobody who uses the feature actually wants.

    What's more, allocating very large pages can be time-consuming. All the physical pages which are involved in a very large page must be contiguous (and must be aligned on a large page boundary). Prior to Windows XP, allocating a very large page can take 15 seconds or more if your physical memory is fragmented. (And even machines with as much as 2GB of memory will probably have highly fragmented physical memory once they're running for a little while.) Internally, allocating the physical pages for a very large page is performed by the kernel function which allocates physically contiguous memory, which is something device drivers need to do quite often for I/O transfer buffers. Some drivers behave "highly unfavorably" if their request for contiguous memory fails, so the operating system tries very hard to scrounge up the memory, even if it means shuffling megabytes of memory around and performing a lot of disk I/O to get it. (It's essentially performing a time-critical defragmentation.)

    If you followed the discussion so far, you'll see another reason why large pages aren't paged out: When they need to be paged back in, the system may not be able to find a suitable chunk of contiguous physical memory!

    In Windows Vista, the memory manager folks recognized that these long delays made very large pages less attractive for applications, so they changed the behavior so requests for very large pages from applications went through the "easy parts" of looking for contiguous physical memory, but gave up before the memory manager went into desperation mode, preferring instead just to fail. (In Windows Vista SP1, this part of the memory manager was rewritten so the really expensive stuff is never needed at all.)

    Note that the MEM_LARGE_PAGES flag triggers an exception to the general principle that MEM_RESERVE only reserves address space, MEM_COMMIT makes the memory manager guarantee that physical pages will be there when you need them, and that the physical pages aren't actually allocated until you access the memory. Since very large pages have special physical memory requirements, the physical allocation is done up front so that the memory manager knows that when it comes time to produce the memory on demand, it can actually do so.

  • The Old New Thing

    How do you obtain the icon for a shortcut without the shortcut overlay?

    • 12 Comments

    The easy one-stop-shopping way to get the icon for a file is to use the SHGet­File­Info function with the SHGFI_ICON flag. One quirk of the SHGet­File­Info function is that if you pass the path to a shortcut file, it will always place the shortcut overlay on the icon, regardless of whether you passed the SHGFI_ADD­OVERLAYS flag. (Exercise: What is so special about the shortcut overlay that makes it exempt from the powers of the SHGFI_ADD­OVERLAYS flag? The information you need is on the MSDN page for SHGet­File­Info, though you'll have to apply some logic to the sitaution.)

    I'm using SHGet­File­Info to get the icon of a file to display in my application. When the file is a shortcut, rather than displaying the exe icon with a link overlay (as in SHGFI_LINK­OVERLAY) I'd like to display the original exe icon. Is there a way to do this with SHGet­File­Info? Thanks,

    First, correcting a minor error in the question: The icon for a shortcut is, by default, the icon for the shortcut target, but it doesn't have to be. The IShell­Link::Set­Icon­Location method lets you set the icon for a shortcut to anything you like. (This is the method used when you click Change Icon on the shortcut property page.)

    Anyway, the SHGet­File­Info function gets the icon first by asking the shell namespace for the icon index in the system imagelist, and then converting that imagelist/icon index into a HICON. If you want to change the conversion, you can just ask SHGet­File­Info to stop halfway and then finish the process the way you like.

    HICON GetIconWithoutShortcutOverlay(PCTSTR pszFile)
    {
     SHFILEINFO sfi;
     HIMAGELIST himl = reinterpret_cast<HIMAGELIST>(
      SHGetFileInfo(pszFile, 0, &sfi, sizeof(sfi),
                    SHGFI_SYSICONINDEX));
     if (himl) {
      return ImageList_GetIcon(himl, sfi.iIcon, ILD_NORMAL);
     } else {
      return NULL;
     }
    }
    

    Of course, if you're going to be doing this for a lot of files, you may want to just stop once you have the imagelist and the index, using Image­List_Draw to draw the image when necessary, instead of creating thousands of little icons.

  • The Old New Thing

    Microspeak: Leverage

    • 25 Comments

    At Microsoft, leverage is not a term of physics whereby a force can be magnified by the application of mechanical advantage. It is also not a term of finance whereby the power of a small amount of money can be magnified by the assumption of debt. In fact, at Microsoft, the word leverage isn't even a noun. It is a verb: to leverage, leverages, leveraging, leveraged, has leveraged. Oh, and it has nothing to do with magnification.

    Here are some citations:

    How do I leverage a SiteMap?
    Allow advertising partners to leverage this resource for providing targeted advertising links.
    Leverage existing design to power other designs
    Do we have documents on how Windows 95 can leverage Windows 2000 Active Directory?

    At Microsoft, to leverage means to take advantage of, or in many cases, simply to use.

    Verbal use of the word leverage appears to be popular outside of Microsoft as well, such as this headline from eWeek: How to Leverage IT to Speed RandD Innovation.

    But can you do this: At Microsoft you can leverage people.

    Alice, does Bob perform any component-Foo testing? Charles can be leveraged to actually execute tests if Donald can drive him with these asks. Let me know.

    That snippet was a whirlwind of Microspeak, with the passive form of the verb to leverage, plus to drive and the plural noun asks.

  • The Old New Thing

    Why does the name of my TEMP directory keep changing?

    • 25 Comments

    A customer liaison contacted the shell team with the following request:

    Subject: Support case: 069314718055994

    On two of my customer's machines, he's finding that if he opens %TEMP% from the Start menu, it opens C:\Users\username\AppData\Local\Temp\1, C:\Users\username\AppData\Local\Temp\2, and so on. Each time the user logs off and back on, the number increments. The number resets after each reboot. Why are we seeing these folders being created under Temp? This does not appear to be the default behavior. What would cause the operating system to create these folders?

    The customer rebuilt one of the affected machines, and the behavior went away. However, the customer claims that both machines were working fine before, and then this problem suddenly started. Therefore, the customer is afraid that the problem will come back in the future. Any pointers in solving this mystery would be very much appreciated.

    It's not clear why this question was directed at the shell team, since Explorer doesn't set your TEMP directory. (In general, a lot of random questions come to the shell team not because they are shell questions but because people don't know where else to turn. Since the shell presents the desktop, and the desktop is on the screen when the problem occurs, maybe it's a shell issue!)

    The question was redirected to the Remote Desktop team, since it is Remote Desktop that creates these subdirectories off the TEMP directory. And from there, psychic powers predicted that the problem lay in the Administrative Templates\Windows Components\Terminal Services\Temporary folders group policy. If you don't select Do not use temporary folders per session, then these TEMP subdirectories are created. (Yet another of those crazy negative checkboxes.) There is also a knowledge base article describing the registry keys behind these group policies.

    The customer liaison responded cryptically,

    Thanks. I tested the policies and it is the one that creates the folder.

    Not sure what this means for solving the customer's problem, but that was the last we heard from the customer liaison, so I guess this policy was enough to give the customer a nudge in the right direction.

  • The Old New Thing

    There's a default implementation for WM_SETREDRAW, but you might be able to do better

    • 15 Comments

    If your window doesn't have a handler for the WM_SET­REDRAW message, then Def­Window­Proc will give you a default implementation which suppresses WM_PAINT messages for your window when redraw is disabled, and re-enables WM_PAINT (and triggers a full repaint) when redraw is re-enabled. (This is internally accomplished by making the window pseudo-invisible, but that's an implementation detail you shouldn't be concerned with.)

    Although the default implementation works fine for simple controls, more complex controls can do better, and in fact they should do better, because that's sort of the point of WM_SET­REDRAW.

    The intended use for disabling redraw on a window is in preparation for making large numbers of changes to the window, where you don't want to waste time updating the screen after each tiny little change. For example, if you're going to add a hundred items to a list box, you probably want to disable redraw while adding the items so you don't have to suffer through 100 screen refreshes when only one is enough. You've probably seen the programs that forget to suppress redraw when filling a large list box: The application freezes up except for a list box whose scroll bar starts out with a big thumb that slowly shrinks as the number of items increases.

    I say that this is sort of the point of WM_SET­REDRAW for a complex control, because if you have a simple control (like a button), there isn't much in the way of "bulk updates" you can perform on it, so there isn't much reason for anybody to want to disable redraw on it anyway. The types of windows for which people want to disable redraw are the types of windows that would benefit most from a custom handler.

    For example, the list view control has a custom handler for WM_SET­REDRAW which sets an internal redraw has been disabled flag. Other parts of the list view control check this flag and bypass complex screen calculations if is set. For example, when you add an item to a list view while redraw is disabled, the list view control doesn't bother recalculating the new scroll bar position; it just sets an internal flag that says, "When redraw is re-enabled, don't forget to recalculate the scroll bars." If the list view is in auto-arrange, it doesn't bother rearranging the items after each insertion or deletion; it just sets an internal flag to remember to do it when redraw is re-enabled. If you have a regional list view, it doesn't bother recalculating the region; it just sets a flag. And when you finally re-enable drawing, it sees all the little Post-It note reminders that it left lying around and says, "Okay, let's deal with all this stuff that I had been putting off." That way, if you add 100 items, it doesn't perform 99 useless scroll bar calculations, 99 useless auto-arrange repositionings, and create, compute, and then destroy 99 regions. Since some of these calculations are O(n), deferring them when redraw is disabled improves the performance of inserting n items from O() to O(n).

    Moral of the story: If you have a control that manages a large number of sub-items, you should have a custom WM_SET­REDRAW handler to make bulk updates more efficient.

    Bonus chatter: Note that using Lock­Window­Update as a fake version of WM_SET­REDRAW does not trigger these internal optimizations. Abusing Lock­Window­Update gets you the benefit of not repainting, but you still have to suffer through the various O() calculations.

  • The Old New Thing

    The 2011/2012 Seattle Symphony subscription season at a glance

    • 15 Comments

    Every year, I put together a little pocket guide to the Seattle Symphony subscription season for my symphony friends to help them decide which ticket package they want. As before, you might find it helpful, you might not, but either way, you're going to have to suffer through it. Here's the at-a-glance season guide for the first season with newly-appointed music director Ludovic Morlot. (Full brochure [pdf].)

    Week Program Comments 21 13 7A
    7B
    7C
    7D
    7E
    7F
    7G 4A BW MM RH BS SS
    09/22
    2011
    Zappa: Dupree's Paradise
    Dutilleux: The Tree of Dreams
    Beethoven: Symphony #3
    Nervous
    Nervous
    Awesome
                           
    09/29
    2011
    Stravinsky: The Rite of Spring
    Gershwin: An American in Paris
    Varèse: Amériques
    Polarizing
    Awesome
    Nervous
                       
     


     
    10/06
    2011
    Liszt: Von der Wiege bis zum Grabe
    Mahler: Kindertotenlieder
    Rachmaninov: Symphonic Dances
    Nervous
    Okay
    Excellent
                           
    10/20
    2011
    Mozart: Symphony #25, K173dB
    Haydn: Cello Concerto in C
    Schubert: Symphony #5
    Awesome
    Excellent
    Excellent
                           
    10/28
    2011
    Bach: Harpsichord Concerto in D minor, BWV 1052
    Webern: Symphony Op. 21
    Stravinsky: Dumbarton Oaks
    Bach: Brandenburg Concerto #5
    Excellent
    Nervous
    Good
    Excellent
                           
    11/03
    2011
    Schumann: Violin Concerto
    Bruckner: Symphony #7
    Excellent
    Nervous
                           
    11/10
    2011
    Poulenc: Gloria
    Rachmaninov: Rhapsody on a Theme of Paganini
    Copland: Symphony #3
    Okay
    Awesome
    Okay
                           
    11/17
    2011
    Britten: Canadian Carnival
    Knussen: Violin Concerto
    Bedford: Outblaze the Sky
    Britten: Four Sea Interludes and Passacaglia (Peter Grimes)
    Good
    Nervous
    Wildcard
    Good
                           
    12/28
    2011
    Humperdinck: Hansel und Gretel excerpts
    Beethoven: Symphony #9
    Wildcard
    Awesome
                           
    01/05
    2012
    Weber: Overture Der Freischütz
    Stravinsky: Capriccio for Piano and Orchestra
    Mozart: Piano Concert #20, K466
    Adams: Doctor Atomic Symphony
    Excellent
    Okay
    Awesome
    Nervous
                           
    01/13
    2012
    Bach: Orchestral Suite #2
    CPE Bach: Kiev Concerto
    JC Bach: Keyboard Concerto
    Bach: Brandenburg Concerto #2
    Excellent
    Excellent
    Excellent
    Awesome
                           
    01/19
    2012
    Tchaikovsky: Souvenir de Florence
    Vasks: Distant Light
    Mendelssohn: Symphony #4 Italian
    Excellent
    Nervous
    Awesome
                           
    01/26
    2012
    Muhly: New Work (World Premiere)
    Chopin: Piano Concerto #2
    Sibelius: Symphony #2
    Wildcard
    Excellent
    Excellent
                           
    02/09
    2012
    Stravinsky: Scherzo fantastique
    Jolivet: Concertino for Trumpet
    Haydn: Symphony #93
    Mussorgsky: Pictures at an Exhibition
    Okay
    Nervous
    Excellent
    Awesome
                           
    02/16
    2012
    Schubert/Berio: Rendering
    Beethoven: Piano Concerto #2
    Schumann: Symphony #4
    Wildcard
    Excellent
    Excellent
                           
    02/22
    2012
    Vivaldi: Winter and Summer (4 Seasons)
    Mozart: Symphony #38 Prague
    Beethoven: Symphony #7
    Awesome
    Awesome
    Awesome
                           
    03/01
    2012
    Mozart: Piano Concerto #6, K238
    Haydn: Symphony #6 Le matin
    Mozart: Piano Concerto #27, K595
    Excellent
    Excellent
    Awesome
                       

     
     
    03/09
    2012
    L. Mozart: Symphony in G, G. 16
    Bach: Brandenburg Concerto #3
    Bach: Sinfonia in G minor, Op. 6, No. 6
    Mozart: Viola Concerto (orig. Clarinet concerto)
    Good
    Awesome
    Good
    Excellent
                           
    03/15
    2012
    Debussy: Prelude to the Afternoon of a Fawn
    Dutilleux: Tout un monde lointain
    Ravel: La Valse
    Excellent
    Nervous
    Excellent
                           
    03/22
    2012
    Schubert: Rosamunda excerpts
    Janáček: Taras Bulba
    Brahms: Violin Concerto
    Good
    Okay
    Awesome
                           
    04/05
    2012
    Mozart: Overture Marriage of Figaro
    Mozart: Piano Concerto #24, K491
    Mahler: Symphony #1
    Awesome
    Awesome
    Excellent
                           
    04/12
    2012
    Rouse: Infernal Machine
    Dvořák: Violin Concerto
    Tchaikovsky: Symphony #4
    Nervous
    Excellent
    Awesome
                       

     
     
    04/19
    2012
    Dutilleux: Symphony #1
    Imeri: Za Simon
    Ravel: Piano Concerto in G
    Dukas: The Sorceror's Apprentice
    Nervous
    Wildcard
    Excellent
    Awesome
                           
    04/26
    2012
    Prokofiev: Piano Concerto #3
    Shostakovich: Symphony #8
    Awesome
    Polarizing
                           
    05/03
    2012
    Mozart: Divertimento in F, K138
    Mozart: Violin Concerto #2, K211
    Vieuxtemps: Violin Concerto #5 Gretry
    Schubert: Symphony #3
    Awesome
    Excellent
    Okay
    Excellent
                           
    05/15
    2012
    Hindemith: Kammermusik #1
    Bartók: Bluebeard's Castle
    Okay
    Wildcard
                           
    06/07
    2012
    R. Strauss: Don Juan
    Korngold: Violin Concerto
    Turina: Danzas Fantásticas
    Rimsky-Korsakov: Capriccio espagnol
    Excellent
    Okay
    Good
    Excellent
                           
    06/14
    2012
    Bernstein: Candide Overture
    Ives: Symphony #2
    Rachmaninov: Piano Concerto #3
    Excellent
    Polarizing
    Excellent
                           
    06/21 Berlioz: The Damnation of Faust Excellent                        
    07/12
    2012
    R. Strauss: Also Sprach Zarathustra fanfare
    Ligeti: Atmosphères
    Matthews: Pluto
    Holst: The Planets (with video)
    Excellent
    Polarizing
    Wildcard
    Awesome
                           
    Week Program Comments 21 13 7A
    7B
    7C
    7D
    7E
    7F
    7G 4A BW MM RH BS SS

    Legend:

    21Masterworks 21-concert series (Choice of Thursdays or Saturdays)
    13Masterworks 13-concert series (Choice of Thursdays or Saturdays)
    7AMasterworks 7-concert series A (Thursdays)
    7BMasterworks 7-concert series B (Saturdays)
    7CMasterworks 7-concert series C (Thursdays)
    7DMasterworks 7-concert series D (Saturdays)
    7EMasterworks 7-concert series E (Thursdays)
    7FMasterworks 7-concert series F (Saturdays)
    7GMasterworks 7-concert series G (Sunday afternoons)
    4AMasterworks 4-concert series A (Friday afternoons)
    BWBaroque & Wine Series (Choice of Fridays or Saturdays)
    MMMainly Mozart (Choice of Thursdays or Saturdays)
    RHRush Hour (Fridays)
    BS Beyond the Score multimedia lecture-concert (Sunday afternoons)
    SSSymphony Specials (Various evenings)

    For those not familiar with the Seattle Symphony ticket package line-ups: Most of the ticket packages are named Masterworks nX where n is the number is the number of concerts in the package, and the letter indicates which variation. Ticket packages have been combined if they are identical save for the day of the week. For example, 7C and 7D are the same concerts; the only difference is that 7C is for Thursday nights, while 7D is for Saturday nights. The Beyond the Score concerts focus on only one of the pieces.

    Changes from last season:

    • The Masterworks 20 series has been expanded to 21 concerts.
    • The Masterworks 10A and 10B series have been dropped.
    • The Masterworks 4B series was renamed 4A. (There used to be 4A and 4B, then 4A was dropped last year leaving just 4B. Then I guess they figured having a 4B without a 4A was confusing, so they renamed 4B to 4A.)
    • The Thursday Rush Hour series has been dropped.

    This chart doesn't include "one-off" concert series such as the Visiting Orchestras or Distinguished Artists series. A "one-off" series is a concert series which shares no concerts with any other series. (Baroque & Wine and Symphony Specials are grandfathered in; I'll probably omit them in future years.)

    The comments column very crudely categorizes the works to assist my less-classically-aware friends. This is, of course, a highly subjective rating system, but I tried to view each piece from the ears of my symphony friends. Thus, I rated downward pieces that I personally like but which others might not and rated up pieces that I may not find musically satisfying but which nevertheless tend to be crowd-pleasers.

    These predictions have, of course, proven wrong in the past.

    Here's what the comments mean. Note that they do not indicate whether the piece is significant in a musicological sense; they're just my guess as to whether my friends are going to like it. (For example, I know that my friends hate minimalism, so I rated the Adams down even though I myself might enjoy it. They also aren't big fans of Bruckner.)

    • Awesome: Guaranteed crowd-pleaser.
    • Excellent: You will definitely like this piece.
    • Good: You will probably like this piece.
    • Okay: You may like this piece.
    • Nervous: I have a bad feeling about this one.
    • Polarizing: Some people will love it; others will hate it.
    • Wildcard: I have no idea what will happen.

    In many cases, I am not familiar with the piece and am basing my evaluation on what I know about the composer (or am just guessing).

  • The Old New Thing

    Modality, part 9: Setting the correct owner for modal UI, practical exam

    • 22 Comments

    Here's a question that came from a customer. You can answer it yourself based on what you know about modal UI. (If you're kind of rusty on the topic, you can catch up here.) I've left in some irrelevant details just to make things interesting.

    Our program launches a helper program to display an Aero Wizard to guide the user through submitting a service request.

    theWiz.DoModal(hwndMainFrame);
    

    There are no EnableWindow calls leading up to or returning from this call. the DoModal handles things nicely.

    When the user clicks "Cancel" to cancel the service request, we use a TaskDialog so we can get the new look for our confirmation message box. The task dialog setup goes like this:

    TASKDIALOGCONFIG config = { 0 };
    config.cbSize = sizeof(config);
    config.hwndParent = hwndMainFrame;
    

    When the user clicks "Yes" to cancel, then another window instead of our frame becomes active.

    On a hunch, I replaced the task dialog with a Win32 message box

    MessageBox(hwndMainFrame, ...);
    

    and bingo, we get the correct behavior. When our wizard exits, the main frame receives focus.

    I believe that the "automatic" modal behavior that comes with DoModal() that takes care of disabling and reenabling the main frame is somehow getting short-circuited by using TaskDialog from inside our PSM_QUERYCANCEL message handler.

    Right now, we've switched to MessageBox, but we would much prefer to use the task dialog.

    Although it's not common, it is legal to have a window's parent or owner belongs to another thread or process. But it definitely makes things a bit more tricky to manage because it attaches the input queues of the two threads, and you now have two threads coöperating to manage a single window hierarchy.

    Is the cross-process window hierarchy a contributing factor to the problem, or is it just a red herring?

  • The Old New Thing

    How to turn off the exception handler that COM "helpfully" wraps around your server

    • 37 Comments

    Historically, COM placed a giant try/except around your server's methods. If your server encountered what would normally be an unhandled exception, the giant try/except would catch it and turn it into the error RPC_E_SERVERFAULT. It then marked the exception as handled, so that the server remained running, thereby "improving robustness by keeping the server running even when it encountered a problem."

    Mind you, this was actually a disservice.

    The fact that an unhandled exception occurred means that the server was in an unexpected state. By catching the exception and saying, "Don't worry, it's all good," you end up leaving a corrupted server running. For example:

    HRESULT CServer::DoOneWork(...)
    {
     CWork *pwork = m_listWorkPending.RemoveFirst();
     if (pwork) {
       pwork->UpdateTimeStamp();
       pwork->FrobTheWidget();
       pwork->ReversePolarity();
       pwork->UnfrobTheWidget();
       m_listWorkDone.Add(pwork);
     }
     return S_OK;
    }
    

    Suppose there's a bug somewhere that causes pwork->Reverse­Polarity() to crash. Maybe the problem is that the neutrons aren't flowing, so there's no polarity to reverse. Maybe the polarizer is not property initialized. Whatever, doesn't matter what the problem is, just assume there's a bug that prevents it from working.

    With the global try/except, COM catches the exception and returns RPC_E_SERVERFAULT back to the caller. Your server remains up and running, ready for another request. Mind you, your server is also corrupted. The widget never got unfrobbed, the timestamp refers to work that never completed, and the CWork that you removed from the pending work list got leaked.

    But, hey, your server stayed up.

    A few hours later, the server starts returning E_OUTOFMEMORY errors (because of all the leaked work items), you get errors because there are too many outstanding frobs, and the client hangs because it's waiting for a completion notification on that work item that you lost track of. You debug the server to see why everything is so screwed up, but you can't find anything wrong. "I don't understand why we are leaking frobs. Every time we frob a widget, there's a call to unfrob right after it!"

    You eventually throw up your hands in resignation. "I can't figure it out. There's no way we can be leaking frobs."

    Even worse, the inconsistent object state can be a security hole. An attacker tricks you into reversing the polarity of a nonexistent neutron flow, which causes you to leave the widget frobbed by mistake. Bingo, frobbing a widget makes it temporarily exempt from unauthorized polarity changes, and now the bad guys can change the polarity at will. Now you have to chase a security vulnerability where widgets are being left frobbed, and you still can't find it.

    Catching all exceptions and letting the process continue running assumes that a server can recover from an unexpected failure. But this is absurd. You already know that the server is unrecoverably toast: It crashed!

    Much better is to let the server crash so that the crash dump can be captured at the point of the failure. Now you have a fighting chance of figuring out what's going on.

    But how do you turn off that massive try/except? You didn't put it in your code; COM created it for you.

    Enter IGlobal­Options: Set the COMGLB_EXCEPTION_HANDLING property to COMGLB_EXCEPTION_DONOT_HANDLE, which means "Please don't try to 'help' me by catching all exceptions. If a fatal exception occurs in my code, then go ahead and let the process crash." In Windows 7, you can ask for the even stronger COMGLB_EXCEPTION_DONOT_HANDLE_ANY, which means "Don't even try to catch 'nonfatal' exceptions."

    Wait, what's a 'fatal' exception?

    A 'fatal' exception, at least as COM interprets it, is an exception like STATUS_ACCESS_VIOLATION or STATUS_ILLEGAL_INSTRUCTION. (A complete list is in this sample Rpc exception filter.) On the other hand a 'nonfatal' exception is something like a C++ exception or a CLR exception. You probably want an unhandled C++ or CLR exception to crash your server, too; after all, it would have crashed your program if it weren't running as a server. Therefore, my personal recommendation is to use COMGLB_EXCEPTION_DONOT_HANDLE_ANY whenever possible.

    "That's great, but why is the default behavior the dangerous 'silently swallow exceptions' mode?"

    The COM folks have made numerous attempts to change the default from the dangerous mode to one of the safer modes, but the application compatibility consequences have always been too great. Turns out there are a lot of servers that actually rely on COM silently masking their exceptions.

    But at least now you won't be one of them.

  • The Old New Thing

    Why didn't they use the Space Shuttle to rescue the Apollo 13 astronauts?

    • 44 Comments

    Many decisions make sense only in the context of history.

    Much like the moviegoers who were puzzled why NASA didn't just use the Space Shuttle to rescue the Apollo 13 astronauts, computer users of today, when looking back on historical decisions, often make assumptions based on technology that didn't exist.

    Consider, for example, pointing out that the absence of a console subsystem in Windows 3.1 was no excuse for not porting the ipconfig program as a character-mode application. "Sure maybe you didn't have a console subsystem, but why not just use the DOS box?"

    The MS-DOS prompt is a virtual machine running a copy of MS-DOS. Since it's a virtual machine, as far as the MS-DOS prompt is concerned, it's just running all by its happy self on a dedicated computer running MS-DOS. In reality, of course, it's running inside a simulator being controlled by Windows, but the point of the simulation is so that old applications can continue to run even though they think they're running under MS-DOS.

    "There wasn't any security in place with Win 3.1, so any program run from a DOS box should have been able to affect anything on the system."

    Since the MS-DOS prompt ran in a virtual machine, everything it did was under the supervision of the virtual machine manager. If it tried to access memory it didn't have permission to access, an exception would be raised and handled by the virtual machine manager. If it tried to execute a privileged instruction, an exception would be raised, and the virtual machine manager would step in with a "Nope, I'm not going to let you do that" and terminate the virtual machine. In a sense, programs running in the MS-DOS prompt actually ran with more protection and isolation than Windows applications running on the desktop, because Windows created a whole separate universe for each MS-DOS prompt.

    One of the consequences of virtualization is that programs running in the MS-DOS prompt are plain old MS-DOS applications, not Windows applications. There is no Windows API in MS-DOS, so there is no Windows API in the MS-DOS prompt either. (It's like running Windows inside a virtual machine on your Linux box and wondering why your Windows program can't call XCreateWindow. It can't call XCreateWindow because that's a function on the host system, not in the virtual machine.)

    Okay, but let's suppose, just for the sake of argument, that somebody poked a hole in the virtual machine and provided a way for MS-DOS programs to call WinSock APIs.

    You still wouldn't want ipconfig to be an MS-DOS program.

    Recall that Windows 3.1 ran in one of two modes, either standard mode or enhanced mode. Standard mode is the version designed for the 80286 processor. It didn't have virtual memory or support for virtual machines. When you ran an MS-DOS prompt, standard mode Windows would freeze all your Windows programs and effectively put itself into suspended animation. It then ran your MS-DOS program (full-screen since there was no Windows around to put it in a window), and when your MS-DOS program exited, Windows would rouse itself from its slumber and bring things back to the way they were before you opened that MS-DOS prompt.

    It would kind of suck if getting your computer's IP address meant stopping all your work, shutting down Windows (effectively), and switching the video adapter into character mode, just so it could print 16 characters to the screen.

    "Well, who cares about standard mode Windows any more? Let's say that it only works in enhanced mode. Enhanced mode can multi-task MS-DOS prompts and run them in a window."

    Recall that the minimum memory requirements for Windows 3.1 in enhanced mode was 1664KB of memory. Given that each MS-DOS box took up about 1MB of memory, you're saying that displaying 16 characters of information is going to consume over half of your computer's memory?

    "Okay, helpdesk wants to know my IP address so they can troubleshoot my computer. In order to do that, I have to run this program, but first I need to save all my work and exit all my programs in order to free up enough memory to run the program they want me to run."

    Better to just write a simple Windows application.

    Bonus commentary: 640k asks, "Why wasn't winipcfg called ipconfig?"

    Right. "Let's have two completely different and incompatible programs with the same name." See how far you get with that.

Page 1 of 3 (22 items) 123