• The Old New Thing

    Bad version number checks

    • 33 Comments
    Version numbers. Very important. And so many people check them wrong.

    This is why Windows 95's GetVersion function returned 3.95 instead of 4.0. A lot of code checked the version number like this:

      UINT Ver = GetVersion();
      UINT MajorVersion = LOBYTE(uVer);
      UINT MinorVersion = HIBYTE(uVer);
      if (MajorVersion < 3 || MinorVersion < 10) {
       Error("This program requires Windows 3.1");
      }
    

    Now consider what happens when the version number is reported as 4.0. The major version check passes, but the minor version check fails since 0 is less than 10.

    This bug was so rife that we gave up shimming every app that had the problem and just decided, "Fine. If anybody asks, say that the Windows version is 3.95."

    I suspect this is also why DirectX always reports its version as 4.x.

  • The Old New Thing

    Stories of going through airport security

    • 13 Comments
    I went through security three times at Seattle-Tacoma International Airport before my flight to Newark.

    My original flight was cancelled due to inclement weather in Newark, so I get rescheduled onto another flight that arrived three hours later. I thought to myself, "That's strange. Both flights are going to Newark. It's not like the weather in Newark is nicer on the second flight."

    Not surprisingly, after about an hour, the second flight was also cancelled.

    Anyway, when going through security the first time, I spotted the monitor on one of the X-ray machines and was somewhat amused to see the Windows 98 boot screen. Windows 98: Not dead yet. (Actually, this was probably Windows 98 Embedded. I remember having to debug a problem with Windows 95 Embedded just last year. The product key validation algorithm was failing because the "issued date" in the product key was failing a sanity check and being rejected as "ridiculously out of range".) I decided not to take a picture, though. Security people get really nervous when you take their picture...

    While going through security the second time, they decided that my knitting needles may be a problem. This was the first time anybody had given me trouble over my needles. Eventually they decided that I wasn't going to attack somebody by poking them with a knitting needle, but not before doing a more detailed search of my bag and my person. (Mind you, they were Number 2 needles, pointier than your average knitting needle.)

    On my return from Newark to Seattle, the security person also made note of my knitting needles, but only to compliment me on my hobby.

  • The Old New Thing

    TEXT vs. _TEXT vs. _T, and UNICODE vs. _UNICODE

    • 40 Comments
    So what's with all these different ways of saying the same thing?

    There's actually a method behind the madness.

    The plain versions without the underscore affect the character set the Windows header files treat as default. So if you define UNICODE, then GetWindowText will map to GetWindowTextW instead of GetWindowTextA, for example. Similarly, the TEXT macro will map to L"..." instead of "...".

    The versions with the underscore affect the character set the C runtime header files treat as default. So if you define _UNICODE, then _tcslen will map to wcslen instead of strlen, for example. Similarly, the _TEXT macro will map to L"..." instead of "...".

    What about _T? Okay, I don't know about that one. Maybe it was just to save somebody some typing.

  • The Old New Thing

    Improbable Research comes to Seattle

    • 1 Comments
    The lunatics behind The Annals of Improbable Research and The Ig Nobel Prize will be in Seattle tomorrow night, Feburary 13. The meeting schedule lists the AIR presentation as "8:00PM-10:30PM, Special Event: Annals of Improbable Research (open to all registrants), Sheraton Hotel, Third Floor, Metropolitan Ballroom". The AIR folks said "Open to the public"; maybe they won't be checking for an AAAS membership card at the door.
  • The Old New Thing

    Sure, we do that

    • 50 Comments
    The DirectX video driver interface for Windows 95 had a method that each driver exposed called something like "DoesDriverSupport(REFGUID guidCapability)" where we handed it a capability GUID and it said whether or not that feature was supported.

    There were various capability GUIDs defined, things like GUID_CanStretchAlpha to ask the driver whether it was capable of stretching a bitmap with an alpha channel.

    There was one driver that returned TRUE when you called DoesDriverSupport(GUID_XYZ), but when DirectDraw tried to use that capability, it failed, and in a pretty spectacular manner.

    So one of the DirectDraw developers called the vendor and asked them, "So does your card do XYZ?"

    Their response: "What's XYZ?"

    Turns out that their driver's implementation of DoesDriverSupport was something like this:

    BOOL DoesDriverSupport(REFGUID guidCapability)
    {
      return TRUE;
    }
    

    In other words, whenever DirectX asked, "Can you do this?" they answered, "Sure, we do that," without even checking what the question was.

    (The driver must have been written by the sales department.)

    So the DirectDraw folks changed the way they queried for driver capabilities. One of the developers went into his boss's office, took a network card, extracted the MAC address, and then smashed the card with a hammer.

    You see, this last step was important: The GUID generation algorithm is based on a combination of time and space. When you ask CoCreateGuid to create a new GUID, it encodes the time of your request in the first part of the GUID and information that uniquely identifies your machine (the network card's MAC address, which is required to be unique by the standards that apply to network card).

    By smashing the network card with a hammer, he prevented that network card from ever being used to generate a GUID.

    Next, he added code to DirectDraw so that when it starts up, it manufactures a random GUID based on that network card (which - by its having been destroyed - can never be validly created) and passes it to DoesDriverSupport. If the driver says, "Sure, we do that", DirectDraw says, "Aha! Caught you! I will not believe anything you say from now on."
  • The Old New Thing

    Dunkin Donuts vs. Krispy Kreme

    • 70 Comments
    Having grown up on the east coast, I imprinted on Dunkin Donuts. Once a month we would stop at DD on the way home and buy a shoebox of doughnuts. Toasted coconut and butternut, those were my favorites.

    Ironically, Dunkin Donuts is really a coffee shop disguised as a doughnut shop. (Doughnuts account for only 20% of their sales; coffee 50%.)

    So during my travels through Manhattan, I walked past one of the twenty-five zillion Dunkin Donuts stores there and popped in for a toasted coconut doughnut. One bite and I was a little kid again.

    Some people say that DD's doughnuts are awful, but that's pretty much irrelevant to me by now. It's all about the memories that are invoked.

    And besides, those people are wrong. I don't understand the appeal of KK donuts. They have no flavor; it's just sugar.

  • The Old New Thing

    Answer to exercise: Pointer to member function cast

    • 3 Comments

    Yesterday's exercise asked you to predict and explain the codegen for the following fragment:

    class Base1 { int b1; void Base1Method(); };
    class Base2 { int b2; void Base2Method(); };
    class Derived : public Base1, Base2
      { int d; void DerivedMethod(); };
    class Derived2 : public Base3, public Derived { };
    
    void (Derived::*pfnDerived)();
    void (Derived2::*pfnDerived2();
    
    pfnDerived2 = pfnDerived;
    

    Well, the codegen might go something like this:

      mov  ecx, pfnDerived[0]       ; ecx = address
      mov  pfnDerived2[0], ecx
    
      mov  ecx, pfnDerived2[4]      ; ecx = adjustor
      add  ecx, sizeof(Base3)       ; adjust the adjustor!
      mov  pfnDerived2[4], ecx
    

    Let's use one of our fancy pictures:

    p
    Base3::b3
    q
    Base2::b2
    Base1::b1
    Derived::d

    Just for fun, I swapped the order of Base1 and Base2. There is no requirement in the standard about the order in which storage is allocated for base classes, so the compiler is completely within its rights to put Base2 first, if it thinks that would be more efficient.

    A pointer to member function for class Derived expects the "this" pointer to be at "q". So when we have a "p", we need to add sizeof(Base3) to it to convert it to "q", on top of whatever other adjustment the original function pointer wanted. That's why we add sizeof(Base3) to the existing adjustor to make a new combined adjustor.

  • The Old New Thing

    Orkut's privacy policy and terms of service

    • 23 Comments
    It was bound to happen sooner or later. I was invited to join Orkut. But before clicking Submit, I always read the fine print: their Terms of Service and their Privacy Policy. (Oh great, you have to have scripting enabled just to read their Terms of Service and Privacy Policy!)

    Notice, for example, the terms for changes to their terms of service:

    We also reserve the right to modify these Terms of Service from time to time without notice. You are responsible for regularly reviewing these Terms of Service so that you will be apprised of any changes.

    (Emphasis mine.) Notice that they do not say that they will notify you when the Terms of Service change. It is your responsibilty to check the Terms of Service. So tomorrow, they could quietly amend their Terms of Service to read, "By agreeing to these Terms of Service, you also agree to pay orkut.com a fee of $50 per day in perpetuity, and you grant that orkut.com or its agents are authorized to use physical force or threats of force to compel such payment," and it is your responsibility to notice this.

    And even if you do manage to notice this, their termination clause says

    Once your membership terminates, you will have no right to use the orkut.com service. Our proprietary rights, disclaimer of warranties, indemnities, limitations of liability and miscellaneous provisions shall survive any termination of your membership.

    Suppose you alertly notice that they changed their Terms of Service and you quickly contact them to cancel your membership. Does that relieve you of your $50/day habit? Nope. The $50/day fee survives termination of the membership. Even though you lose your right to the benefits of membership, they retain the rights to exploit your membership (that you don't have any more but are still paying for).

    Of course, any interpretation of the above paragraph is meaningless since they can totally write their Terms of Service at any time and hold you to the rewritten version.

    What about the privacy policy?

    We reserve the right to transfer your personal information in the event of a transfer of ownership of orkut.com, such as acquisition by or merger with another company. In such an event, orkut.com will notify you before information about you is transferred and becomes subject to a different privacy policy.

    Note that there is no way to opt out of being subjected to a different privacy policy. So Orkut could be bought by vast-left-wing-conspiracy.com, whose privacy policy reads, "We reserve the right to use all information gathered about you, both aggregate and personally identifiable, for any means whatsoever, without compensation or recourse. The terms of this policy remain in effect even after membership is cancelled."

    And now vast-left-wing-conspiracy.com can sell your name to hate groups and you can't do anything about it.

    I find it interesting that there are no provisions in the Privacy Policy for changes to the Privacy Policy.

  • The Old New Thing

    I think this counts as having come full circle

    • 4 Comments
    First, ABBA rises to stardom in their native Sweden with Ring, Ring. They then win the Eurovision Song Contest with Waterloo, which is also recorded in English, French, German, and probably Spanish.

    Twenty-five years later, the English-language musical Mamma-Mia premieres in London and subsequently spreads through large portions of the world not yet civilized enough to ban fluorescent pink tuxedos or platform shoes.

    And now, the musical is being translated into Swedish and auditions are being taken for a scheduled opening in Stockholm on 12 February 2005.

  • The Old New Thing

    Pointers to member functions are very strange animals

    • 19 Comments

    Pointers to member functions are very strange animals.

    Warning: The discussion that follows is specific to the way pointers to member functions are implemented by the Microsoft Visual C++ compiler. Other compilers may do things differently.

    Well, okay, if you only use single inheritance, then pointers to member functions are just a pointer to the start of the function, since all the base classes share the same "this" pointer:

    class Simple { int s; void SimpleMethod(); };
    class Simple2 : public Simple
      { int s2; void Simple2Method(); };
    class Simple3 : public Simple2
      { int s3; Simple3Method(); };
    
    p
    Simple::s
    Simple2::s2
    Simple3::s3

    Since they all use the same "this" pointer (p), a pointer to a member function of Base can be used as if it were a pointer to a member function of Derived2 without any adjustment necessary.

    The size of a pointer-to-member-function of a class that uses only single inheritance is just the size of a pointer.

    But if you have multiple base classes, then things get interesting.

    class Base1 { int b1; void Base1Method(); };
    class Base2 { int b2; void Base2Method(); };
    class Derived : public Base1, Base2
      { int d; void DerivedMethod(); };
    
    p
    Base1::b1
    q
    Base2::b2
    Derived::d

    There are now two possible "this" pointers. The first (p) is used by both Derived and Base1, but the second (q) is used by Base2.

    A pointer to a member function of Base1 can be used as a pointer to a member function of Derived, since they both use the same "this" pointer. But a pointer to a member function of Base2 cannot be used as-is as a pointer to a member function of Derived, since the "this" pointer needs to be adjusted.

    There are many ways of solving this. Here's how the Visual Studio compiler decides to handle it:

    A pointer to a member function of a multiply-inherited class is really a structure.

    Address of function
    Adjustor
    The size of a pointer-to-member-function of a class that uses multiple inheritance is the size of a pointer plus the size of a size_t.

    Compare this to the case of a class that uses only single inheritance.

    The size of a pointer-to-member-function can change depending on the class!

    Aside: Sadly, this means that Rich Hickey's wonderful technique of Callbacks in C++ Using Template Functors cannot be used as-is. You have to fix the place where he writes the comment

    // Note: this code depends on all ptr-to-mem-funcs being same size
    

    Okay, back to our story.

    To call through a pointer to a member function, the "this" pointer is adjusted by the Adjustor, and then the function provided is called. A call through a function pointer might be compiled like this:

    void (Derived::*pfn)();
    Derived d;
    
    (d.*pfn)();
    
      lea  ecx, d       ; ecx = "this"
      add  ecx, pfn[4]  ; add adjustor
      call pfn[0]       ; call
    

    When would an adjustor be nonzero? Consider the case above. The function Derived::Base2Method() is really Base2::Base2Method() and therefore expects to receive "q" as its "this" pointer. In order to convert a "p" to a "q", the adjustor must have the value sizeof(Base1), so that when the first line of Base2::Base2Method() executes, it receives the expected "q" as its "this" pointer.

    "But why not just use a thunk instead of manually adding the adjustor?" In other words, why not just use a simple pointer to a thunk that goes like this:

    Derived::Base2Method thunk:
        add ecx, sizeof(Base1)  ; convert "p" to "q"
        jmp Base2::Base2Method  ; continue
    

    and use that thunk as the function pointer?

    The reason: Function pointer casts.

    Consider the following code:

    void (Base2::*pfnBase2)();
    void (Derived::*pfnDerived)();
    
    pfnDerived = pfnBase2;
    
      mov  ecx, pfnBase2            ; ecx = address
      mov  pfnDerived[0], ecx
    
      mov  pfnDerived[4], sizeof(Base1) ; adjustor!
    

    We start with a pointer to a member function of Base2, which is a class that uses only single inheritance, so it consists of just a pointer to the code. To assign it to a pointer to a member function of Derived, which uses multiple inheritance, we can re-use the function address, but we now need an adjustor so that the pointer "p" can properly be converted to a "q".

    Notice that the code doesn't know what function pfnBase2 points to, so it can't just replace it with the matching thunk. It would have to generate a thunk at runtime and somehow use its psychic powers to decide when the memory can safely be freed. (This is C++. No garbage collector here.)

    Notice also that when pfnBase2 got cast to a pointer to member function of Derived, its size changed, since it went from a pointer to a function in a class that uses only single inheritance to a pointer to a function in a class that uses multiple inheritance.

    Casting a function pointer can change its size!

    I bet that you didn't know that before reading this entry.

    There's still an awful lot more to this topic, but I'm going to stop here before everybody's head explodes.

    Exercise: Consider the class

    class Base3 { int b3; void Base3Method(); };
    class Derived2 : public Base3, public Derived { };
    
    How would the following code be compiled?
    void (Derived::*pfnDerived)();
    void (Derived2::*pfnDerived2();
    
    pfnDerived2 = pfnDerived;
    

    Answer to appear tomorrow.

Page 403 of 427 (4,268 items) «401402403404405»