March, 2005

  • The Old New Thing

    Why does the debugger show me the wrong virtual function?


    Pointers to virtual functions all look basically the same and therefore, as we learned last time, all end up merged into a single function. Here's a contrived example:

    class Class1
     virtual int f1() { return 0; }
     virtual int f2() { return 1; }
    class Class2
     virtual int g1() { return 2; }
     virtual int g2() { return 3; }
    int (Class1::*pfn1)() = Class1::f2;
    int (Class2::*pfn2)() = Class2::g2;

    If you take a look at pfn1 and pfn2 you'll see that the point to the same function:

    0:000> dd pfn1 l1
    01002000  010010c8
    0:000> dd pfn2 l1
    01002004  010010c8
    0:000> u 10010c8 l2
    010010c8 8b01     mov     eax,[ecx]           ; first vtable
    010010ca ff6004   jmp     dword ptr [eax+0x4] ; second function

    That's because the virtual functions Class1::f2 and Class2::g2 are both stored in the same location relative to the respective object pointer: They are the second entry in the first vtable. Therefore, the code to call those functions is identical and consequently has been merged by the linker.

    Notice that the function pointers are not direct pointers to the concrete implementations of Class1::f2 and Class2::g2 because the function pointer might be applied to a derived class which override the virtual function:

    class Class3 : public Class1
     virtual int f2() { return 9; }
    Class3 c3;
    (c3.*pfn1)(); // calls Class3::f2

    Applying the function pointer invokes the function on the derived class, which is the whole point of declaring the function Class1::f2 as virtual in the first place.

    Note that the C++ language explicitly states that the result of comparing non-null pointers to virtual member functions is "unspecified", which is language-standards speak for "the result not only depends on the implementation, but the implementation isn't even required to document how it arrives at the result."

  • The Old New Thing

    Why does the debugger show me the wrong function?


    Often you'll be minding your own business debugging some code, and you decide to step into one function and the debugger shows that you're in some other function. How did that happen?

    class Class1
     int *GetQ() { return q; }
     int *p;
     int *q;
    class Class2
     virtual int GetValue() { return value; }
     int value;

    You then step through code that does something like this:

    int Whatever(Class2 *p)
     return p->GetValue();

    And when you step into the call to p->GetValue() you find yourself in Class1::GetQ. What happened?

    What happened is that the Microsoft linker combined functions that are identical at the code generation level.

    ?GetQ@Class1@@QAEPAHXZ PROC NEAR    ; Class1::GetQ, COMDAT
      00000 8b 41 04         mov     eax, DWORD PTR [ecx+4]
      00003 c3               ret     0
    ?GetQ@Class1@@QAEPAHXZ ENDP         ; Class1::GetQ
    ?GetValue@Class2@@UAEHXZ PROC NEAR  ; Class2::GetValue, COMDAT
      00000 8b 41 04         mov     eax, DWORD PTR [ecx+4]
      00003 c3               ret     0
    ?GetValue@Class2@@UAEHXZ ENDP       ; Class2::GetValue

    Observe that at the object code level, the two functions are identical. (Note that whether two functions are identical at the object code level is highly dependent on which version of what compiler you're using, and with which optimization flags. Identical code generation for different functions occurs with very high frequency when you use templates.) Therefore, the linker says, "Well, what's the point of having two identical functions? I'll just keep one copy and use it to stand for both Class1::GetQ and Class2::GetValue."

    0:000> u Class1::GetQ
    010010d6 8b4104           mov     eax,[ecx+0x4]
    010010d9 c3               ret
    0:000> u Class2::GetValue
    010010d6 8b4104           mov     eax,[ecx+0x4]
    010010d9 c3               ret

    Notice that the two functions were merged: The addresses are identical. That one fragment of code merely goes by two names. Therefore, when the debugger sees that you've jumped to 0x010010d6, it doesn't know which of the names it should use, so it just picks on.

    That's why it looks like you jumped to the wrong function.

    To disable what is called "identical COMDAT folding", you can pass the /OPT:NOICF flag to the linker.

  • The Old New Thing

    Psychic debugging: Why your expensive four-processor machine is ignoring three of its processors


    On one of our internal mailing lists, someone was wondering why their expensive four-processor computer appeared to be using only one of its processors. From Task Manager's performance tab, the chart showed that the first processor was doing all the work and the other three processors were sitting idle. Using Task Manager to set each process's processor affinity to use all four processors made the computer run much faster, of course. What happened that messed up all the processor affinities?

    At this point, I invoked my psychic powers. Perhaps you can too.

    First hint: My psychic powers successfully predicted that Explorer also had its processor affinity set to use only the first processor.

    Second hint: Processor affinity is inherited by child processes.

    Here was my psychic prediction:

    My psychic powers tell me that

    1. Explorer has had its thread affinity set to 1 proc....
    2. because you previewed an MPG file...
    3. whose decoder calls SetProcessAffinityMask in its DLL_PROCESS_ATTACH...
    4. because the author of the decoder couldn't fix his multiproc bugs...
    5. and therefore set the process thread affinity to 1 to "fix" the bugs.

    Although my first psychic prediction was correct, the others were wide of the mark, though they were on the right track and successfully guided further investigation to uncover the culprit.

    The real problem was that there was a third party shell extension whose authors presumably weren't able to fix their multi-processor bugs, so they decided to mask them by calling the SetProcessAffinityMask function to lock the current process (Explorer) to a single processor. Woo-hoo, we fixed all our multi-processor bugs at one fell swoop! Let's all go out and celebrate!

    Since processor affinity is inherited, this caused every program launched by Explorer to use only one of the four available processors.

    (Yes, the vendor of the offending shell extension has been contacted, and they claim that the problem has been fixed in more recent versions of the software.)

  • The Old New Thing

    Confusion over whether you have Windows XP SP1 or SP2


    Some support people have asked me why the "About" dialog seems to be kind of schizophrenic as to whether a machine has Windows XP SP1 or SP2.

    About Windows

    Microsoft® Windows
    Version 5.1 (Build 2600.xpsp2.040919-1003 : Service Pack 1)
    Copyright© 1981-2001 Microsoft Corporation

    Why does the version string say "xpsp2" and then "Service Pack 1"? Is this machine running SP1 or SP2?

    It's running Service Pack 1. The build number string is a red herring.

    Why does the build number string say "xpsp2" when the computer is running SP1?

    Because Windows XP Service Pack 2 was a victim of changing circumstances.

    After Service Pack 1 shipped, there was no indication that Service Pack 2 was going to be anything other than "just another service pack": A cumulative update of the fixes that had been issued since the release of Service Pack 1. Therefore, the release team created a new project, called it "xpsp2" and when a fix needed to be made to Service Pack 1, they made it there. It was called "xpsp2" because the assumption was that when the time came to release Service Pack 2, they would just take all the fixes they had been making to Service Pack 1 and call that Service Pack 2. In other words, "fixes to Service Pack 1" and "working on Service Pack 2" were the same thing.

    Of course, things changed, and a "new" Service Pack 2 project was created for the "real" Service Pack 2 changes, leaving the old "xpsp2" project to be merely the place where Service Pack 1 fixes were developed.

    Yes, it's confusing. We're kind of embarrassed by the whole project naming fiasco. That's what happens when plans take a radical change after work has already started.

    Anyway, there you have it, the long and boring story of why fixes for Service Pack 1 have "xpsp2" in their build string.

  • The Old New Thing

    Your exception handler can encounter an exception


    Consider the following code, written in C# just for kicks; the problem is generic to any environment that supports exception handling.

    void ObliterateDocument()
     try {
      try {
      } finally {
     } finally {

    Some time later, you find yourself facing an assertion failure from document.Destroy() claiming that you are destroying the document while there are still active plugins. But there is your call to document.DestroyPlugins(), and it's in a finally block, and the whole point of a finally block is that there is no way you can escape without executing it.

    So why didn't document.DestroyPlugins() execute?

    Because your exception handler itself encountered an exception.

    The exception handler is not active during its own finally clause. As a result, if an exception is thrown during document.Close(), the exception handler search begins at the block outside the finally block.

    (That the exception handler is not active during its own finally clause should be obvious. It would mean that if an exception were to occur during the finally clause, the program would go into an infinite loop. And it also wouldn't be possible to rethrow a caught exception; your throw would end up caught by yourself!)

    In this case, the exception was caught by some outer caller, causing the remainder of the first finally block to be abandoned. The other finally blocks do run since they contain the one that died.

    (This bug also exists in the proposed alternative to error-checking code posted by an anonymous commenter.)

  • The Old New Thing

    Competing to be the worst-dressed couple in America


    The U.S. cable network TLC is putting on a special episode of What Not to Wear devoted to identifying the worst-dressed couple in America. It so happens that one of my friends knows one of the finalists, so we'll be rooting for them. Or is it against them? Are you supposed to hope that your favorite is in fact the worst-dressed? Or should you be relieved that they're only "sort of badly dressed but at least not the worst I've seen"?

    Yes, the show is a rip-off of the original BBC show. It's interesting how we yanks look to our British neighbors as examples of quality television (What Not to Wear, Changing Rooms, The Office) and bemoan how U.S. television producers can only come up with drivel. Meanwhile, the Brits look right back (Hill Street Blues, Cheers, 24) and bemoan the same thing about their own television industry.

    The other guy's stuff is always better.

  • The Old New Thing

    The great Alaskan ice sculpture


    NPR interviewed John Reeves, the artist behind a 160-foot-tall mountain of ice in Alaska. The man has a down-home aw-shucks kind of demeanor that I found quite charming.

    I'm a middle-aged guy that has a lot of time in the winter and a little bit of extra money to play with, so my hobby was to see how big an ice hill I could grow. I started last year as just a way to amuse myself just to see what would happen if you left the water running all winter, and last year's model was so interesting and so much fun that this year I decided to continue doing it.

    Well, a long winter in Alaska will do that to you, I guess.

    Yes it will. A long, cold, dark... lengthy, cold... did I mention cold?

  • The Old New Thing

    Windows NT Security in Theory and Practice


    Today, I'm not writing anything new. Instead, I'm referring you to the series of articles by Ruediger Asche starting with Windows NT Security in Theory and Practice. These articles are quite old but the principles are still sound. Just bear in mind that the newer stuff won't be covered.

  • The Old New Thing

    Windows are not cheap objects


    Although Windows is centered around, well, windows, a window itself is not a cheap object. What's more, the tight memory constraints of systems of 1985 forced various design decisions.

    Let's take for example the design of the list box control. In a modern design, you might design the list box control as accepting a list of child windows, each of which represents an entry in the list. A list box with 20,000 items would have 20,000 child windows.

    That would have been completely laughable in 1985.

    Recall that Windows was built around a 16-bit processor. Window handles were 16-bit values and internally were just near pointers into a 64K heap. A window object was 88 bytes (I counted), which means that you could squeeze in a maximum of 700 or so before you ran out of memory. What's more, menus hung out in this same 64K heap, so the actual limit was much lower.

    Even if the window manager internally used a heap larger than 64K (which Windows 95 did), 20,000 windows comes out to over 1.5MB. Since the 8086 had a maximum address space of 1MB, even if you devoted every single byte of memory to window objects, you'd still not have enough memory.

    Furthermore, making each list box item a window means that every list box would be a variable-height list box, which carries with it the complexity of managing a container with variable-height items. This goes against two general principles of API design: (1) simple things should be simple, and (2) "pay-for-play", that if you are doing the simple thing, you shouldn't have to pay the cost of the complex thing.

    Filling a list box with actual windows also would have made the "virtual list box" design significantly trickier. With the current design, you can say, "There are a million items" without actually having to create them.

    (This is also why the window space is divided into "client" and "non-client" areas rather than making the non-client area consist of little child windows.)

    To maintain compatibility with 16-bit Windows programs (which still run on Windows XP thanks to the WOW layer), there cannot be more than 65536 window handles in the system, because any more than that would prevent 16-bit programs from being able to talk meaningfully about windows. (Once you create your 65537'th window, there will be two windows with the same 16-bit handle value, thanks to the pigeonhole principle.)

    (And yes, 16/32-bit interoperability is still important even today.)

    With a limit of 65536 window handles, your directory with 100,000 files in it would be in serious trouble.

    The cost of a window object has grown over time, as new features get added to the window manager. Today it's even heftier than the svelte 88 bytes of yesteryear. It is to your advantage not to create more windows than necessary.

    If your application design has you creating thousands of windows for sub-objects, you should consider moving to a windowless model, like Internet Explorer, Word, list boxes, treeview, listview, and even our scrollbar sample program. By going windowless, you shed the system overhead of a full window handle, with all the baggage that comes with it. Since window handles are visible to all processes, there is a lot of overhead associated with centrally managing the window list. If you go windowless, then the only program that can access your content is you. You don't have to worry about marshalling, cross-process synchronization, Unicode/ANSI translation, external subclassing, hooks... And you can use a gigabyte of memory to keep track of your windowless data if that's what you want, since your windowless controls don't affect any other processes. The fact that window handles are accessible to other processes imposes a practical limit on how many of them can be created without impacting the system as a whole.

    I believe that WinFX uses the "everything on the screen is an element" model. It is my understanding that they've built a windowless framework so you don't have to. (I'm not sure about this, though, not being a WinFX person myself.)

  • The Old New Thing

    Dot-Con Job: How InfoSpace took its investors for a ride


    The Seattle Times ran an excellent series last week on the rise and fall of InfoSpace and its charismatic leader, Naveen Jain, who at one point even used the phrase "cult leader" to refer to himself.

    To set the tone, and perhaps to serve as a reference while you read the series, here's a list of reported Infospace earnings per share (EPS), both pro-forma and following Generally Accepted Accounting Principles (GAAP), as reported in their SEC filings and press releases, illustrated with quotes from contemporary press releases (attributed to Naveen Jain unless otherwise noted).

    Period Pro-forma GAAP Quote from press release Remarks
    1999 Q1   -3¢ We executed flawlessly on our plan. Our performance this quarter is a clear demonstration of our undisputed position as a leader in providing Internet infrastructure services.  
    1999 Q2 +1¢
    −8¢ This demonstrates that growth and profitability are not mutually exclusive. Achieving profitability two quarters ahead of Wall Street's expectations demonstrates the strength of our business model. This press release begins the policy of not even mentioning GAAP results.
    1999 Q3 +6¢
    +2¢ We had a totally awesome quarter.  
    1999 Q4 +10¢
    InfoSpace is now synonymous with wireless Internet services. They did not appear to file a 10-Q with the SEC, so I couldn't find their contemporary GAAP EPS. A year later they claimed it was −3¢.
    2000 Q1 +1¢ −38¢ InfoSpace is leading the convergence of the two fastest growing industry segments in history—wireless and the Internet—creating a new industry: the wireless Internet.  
    2000 Q2 −1¢ −14¢ Today marks another historic milestone in the history of the rapid evolution of InfoSpace.  
    2000 Q3 +1¢ −30¢ This was an excellent quarter for InfoSpace, as we continued to build upon our market leadership in the globally-expanding wireless sector. Quote comes from Arun Sarin, CEO.
    2000 Q4 +4¢ −17¢ InfoSpace continues to expand its relationships and deliver value to wireless carriers proven by the significant revenue growth in our wireless business and the more than 1.5 million wireless subscribers.  
    2001 Q1 −2¢ −37¢ InfoSpace continues to demonstrate its strength and ability to generate new business and pursue favorable market opportunities.  
    2001 Q2 +1¢ −22¢ Our return to pro-forma profitability this quarter reconfirms the strength of our business model. I like how they're proud that they are profitable "once you ignore all that accounting stuff".
    2001 Q3 −3¢ −63¢ InfoSpace's performance this quarter underscores our continuing success in focusing on our high growth areas of wireless and merchant.  
    2001 Q4 −2¢ −32¢ We are feeling better about our near and long-term prospects and believe we have turned a corner in our business. In response to the Enron scandal, GAAP numbers are once again being reported.
    2002 Q1 −2¢ −77¢ Results this quarter demonstrate our ability to continue executing on our strategy.  
    2002 Q2 −2¢ −4¢ We are pleased to post another quarter of encouraging results.  
    2002 Q3 −0.2¢ −8.6¢ I'm pleased to report that our focus on profitability is paying off.  
    2002 Q4 +0.5¢ −20.7¢ Our team made significant progress this past year aligning costs with revenues. Quote comes from Jim Voelker, CEO.

    The stock underwent a 10-1 reverse split in September 2002; values have been adjusted to pre-split values for comparison purposes.

    Some boxes list two numbers. The top number is the value reported at the time of the press release. The bottom number is the value reported the following year. For example, in 1999 Q2, the press release claimed that they earned 1¢/share pro forma, but one year later, in the 2000 Q2 filing, they reported a 2¢ loss per share for 1999 Q2. [Years fixed, 10pm.] Why change the amount? Because it makes the 2000 Q2 results look better when compared to the "same period last year". I have no idea which set of numbers (if any!) is correct.

Page 2 of 4 (34 items) 1234