February, 2004

  • The Old New Thing

    So what's to do in Sweden?

    • 104 Comments

    Here is where Raymond gets to abuse his power as a blogger to get some free travel advice.

    I will likely travel to Sweden in mid-March, with a whopping total of five months of Swedish under my belt. I'm sure I will embarrass myself horribly, but that's sort of the point, after all.

    The question is, "So what's to do in Sweden?" I was thinking of flying in to Stockholm, spending the first day recovering from jet lag, then spending maybe a week exploring whatever there is to see there. (Well, I'll actually be staying with a friend in Uppsala, but there appears to be regular train service to/from Stockholm.) Definitely hit Vasamuseet, Nobelmuseet, and Gamla Stan. I'm sure there's other stuff too.

    Then I figure I'd hop a train to Göteborg and spend the remaining three days there doing, um, I have no idea. But I'm told it's a nice city.

    Yes, this plan means that I miss out on Gotland, Skåne, the -Köppings, and all the northern bits. But then again, you can't see a whole country in just ten days.

    So am I nuts?

  • 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

    The arms race between programs and users

    • 69 Comments

    There is a constant struggle between people who write programs and the people who actually use them. For example, you often see questions like, "How do I make my program so the user can't kill it?"

    Now, imagine if there were a way to do this. Ask yourself, "What would the world be like if this were possible?"

    Well, then there would be some program, say, xyz.exe, that is unkillable. Now suppose you're the user. There's this program xyz.exe that has gone haywire, so you want to exit it. But it won't let you exit. So you try to kill it, but you can't kill it either.

    This is just one of several arms races that you can imagine.

    • "I don't want anybody to kill my process." vs. "How do I kill this runaway process?"
    • "I want to shove this critical dialog in the user's face." vs. "How do I stop programs from stealing focus?"
    • "I don't want anybody to delete this file." vs. "How do I delete this file that refuses to be deleted?"
    • "How do I prevent this program from showing up in Task Manager?" vs. "How can I see all the programs that are running on my computer?"

    Eventually you have to decide which side wins, and Windows has decided to keep users in control of their own programs and data, and keep administrators in control of their own computer. So users can kill any process they want (given sufficient privileges), they can stop any program from stealing focus, and they can delete any file they want (again, given sufficient privileges).

    Programs can try to make themselves more difficult to kill (deny PROCESS_TERMINATE access, deny PROCESS_CREATE_THREAD access so people can't CreateRemoteThread(EndProcess), deny PROCESS_VM_WRITE so people can't scribble into your stack and make you doublefault, deny PROCESS_SUSPEND_RESUME so they can't suspend you), but eventually you just can't stop them from, say, elevating to Debug privilege, debugging your process, and moving EIP to "ExitProcess".

    Notice that you can kill CSRSS.EXE and WINLOGON.EXE if you like. Your computer will get very angry at you, but you can do it. (Save you work first!)

    Another useful question to ask yourself: "What's to prevent a virus from doing the same thing?" If there were a way to do these things, then a virus could take advantage of them and make itself invisible to Task Manager, undeletable, and unkillable. Clearly you don't want that, do you?

  • 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

    The social skills of a thermonuclear device

    • 48 Comments

    Somebody@somewhere.else described me as having the social skills of a thermonuclear device. I don't remember the incident in question, but I'll have to accept that it happened.

    I have a very low tolerance for laziness. If you come to me for help, I expect you to have done your homework. (Though I try to scale my expectations to your position. If you're in a coding position then you had better know how to use a debugger. If you're in a testing position, then you had better know at least how to install the debugger. This is considered part of the standard skill set for a tester.)

    But the absolute worst thing you can do is start a conversation by saying, "Hey, Raymond, since you know everything, can you do this for me...?"

  • The Old New Thing

    The correct order for disabling and enabling windows

    • 42 Comments
    If you want to display modal UI, you need to disable the owner and enable the modal child, and then reverse the procedure when the modal child is finished.

    And if you do it wrong, focus will get all messed up.

    If you are finished with a modal dialog, your temptation would be to clean up in the following order:

    • Destroy the modal dialog.
    • Re-enable the owner.

    But if you do that, you'll find that foreground activation doesn't go back to your owner. Instead, it goes to some random other window. Explicitly setting activation to the intended owner "fixes" the problem, but you still have all the flicker, and the Z-order of the interloper window gets all messed up.

    What's going on?

    When you destroy the modal dialog, you are destroying the window with foreground activation. The window manager now needs to find somebody else to give activation to. It tries to give it to the dialog's owner, but the owner is still disabled, so the window manager skips it and looks for some other window, somebody who is not disabled.

    That's why you get the weird interloper window.

    The correct order for destroying a modal dialog is

    • Re-enable the owner.
    • Destroy the modal dialog.

    This time, when the modal dialog is destroyed, the window manager looks to the owner and hey this time it's enabled, so it inherits activation.

    No flicker. No interloper.
  • 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

    The layout of a COM object

    • 39 Comments

    The Win32 COM calling convention specifies the layout of the virtual method table (vtable) of an object. If a language/compiler wants to support COM, it must lay out its object in the specified manner so other components can use it.

    It is no coincidence that the Win32 COM object layout matches closely the C++ object layout. Even though COM was originally developed when C was the predominant programming language, the designers saw fit to "play friendly" with the up-and-coming new language C++.

    The layout of a COM object is made explicit in the header files for the various interfaces. For example, here's IPersist from objidl.h, after cleaning up some macros.

    typedef struct IPersistVtbl
    {
        HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
            IPersist * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void **ppvObject);
    
        ULONG ( STDMETHODCALLTYPE *AddRef )(
            IPersist * This);
    
        ULONG ( STDMETHODCALLTYPE *Release )(
            IPersist * This);
    
        HRESULT ( STDMETHODCALLTYPE *GetClassID )(
            IPersist * This,
            /* [out] */ CLSID *pClassID);
    
    } IPersistVtbl;
    
    struct IPersist
    {
        const struct IPersistVtbl *lpVtbl;
    };
    

    This corresponds to the following memory layout:

    p    lpVtbl    QueryInterface
    AddRef
    Release
    GetClassID

    What does this mean?

    A COM interface pointer is a pointer to a structure that consists of just a vtable. The vtable is a structure that contains a bunch of function pointers. Each function in the list takes that interface pointer (p) as its first parameter ("this").

    The magic to all this is that since your function gets p as its first parameter, you can "hang" additional stuff onto that vtable:

    p    lpVtbl    QueryInterface
    ...
    other stuff
    ...
    AddRef
    Release
    GetClassID

    The functions in the vtable can use offsets relative to the interface pointer to access its other stuff.

    If an object implements multiple interfaces but they are all descendants of each other, then a single vtable can be used for all of them. For example, the object above is already set to be used either as an IUnknown or as an IPersist, since IUnknown is a subset of IPersist.

    On the other hand, if an object implements multiple interfaces that are not descendants of each other, then you get multiple inheritance, in which case the object is typically laid out in memory like this:

    p    lpVtbl    QueryInterface (1)
    q    lpVtbl    QueryInterface (2) AddRef (1)
    ...
    other stuff
    ...
    AddRef (2) Release (1)
    Release (2) ...
    ...

    If you are using an interface that comes from the first vtable, then the interface pointer is p. But if you're using an interface that comes from the second vtable, then the interface pointer is q.

    Hang onto that diagram, because tomorrow we will learn about those mysterious "adjustor thunks".

  • The Old New Thing

    Why are RECTs endpoint-exclusive?

    • 38 Comments

    Endpoint-exclusive RECTs and lines are much easier to work with.

    For example, the width of a rectangle is right - left, and its height is bottom - top. If rectangles were endpoint-inclusive, then there would be annoying +1's everywhere.

    End-point exclusive rectangles also scale properly.

    For example, suppose you have two rectangles (0,0)-(100,100) and (100,100)-(200,200). These two rectangles barely touch at the corner. Now suppose you magnify these rectangles by 2, so they are now (0,0)-(200,200) and (200,200)-(400,400). Notice that they still barely touch at the corner. Also the length of each edge doubled from 100 to 200.

    Now suppose endpoints were inclusive, so the two rectangles would be (0,0)-(99,99) and (100,100)-(199,199). Now when you double them, you get (0,0)-(198,198) and (200,200)-(398,398). Notice that they no longer touch any more because (199,199) is missing. Note also that the length of the side of the square is now 199 pixels instead of 200.

    Similar problems occur if you need to do subpixel computations.

    "But that's silly -- who ever does magnification or subpixel computations?"

    Well, magnification is used more than you think. In addition to the obvious things like zooming in/out, it's also used in printing (since printers are 300dpi but the screen is usually much lower resolution) and in GDI mapping (ScaleWindowExtEx, StretchBlt). And subpixel computations are used in anti-aliasing.

    With apologies to Alvy Ray, I think the best way to interpret this is to view pixels as living between coordinates, not at them. For example, here's a picture of the pixel that lives between (10,10) and (11,11). (In other words, this pixel is the rectangle (10,10)-(11,11).)

      10 11
    10      
    11      
           

    With this interpretation, the exclusion of the endpoint becomes much more natural. For example, here's the rectangle (10,10)-(13,12):

      10 11 12 13
    10          
    11          
    12          
               

    Observe that this rectangle starts at (10,10) and ends at (13,12), just like its coordinates say.

  • The Old New Thing

    Why can't I put hotlinks in notification icon balloon tips?

    • 34 Comments
    The short answer: "Because there is no NIF_PARSELINKS flag."

    The long answer:

    When balloon tips were first developed, there was no ability to embed links. Consequently, programs were free to put insecure text in balloon tips, since there was no risk that they would become "live". So, for example, a virus scanner might say "The document 'XYZ' has been scanned and found to be free of viruses."

    Now suppose hotlinks were supported in balloon tips. Look at how this can be exploited: I can write a web page that goes

    <TITLE>&lt;A HREF="file:C:\Windows\system32\format.com?C:"&gt;
    Party plans&lt;/A&gt;</TITLE>
    
    I then rename the file to "Party plans.html", attach it to some email, and send it to you.

    You download the message and since you are a cautious person, you ask your virus scanner to check it out. The balloon appears:

    Virus scan complete ×
    The document 'Party plans' has been scanned
    and found to be free of known viruses.

    "Oh, how convenient," you say to yourself. "The virus scanner even included a hotlink to the document so I can read it."

    And then you click on it and your hard drive gets reformatted.

    "So why don't you add a NIF_PARSELINKS flag, so people who want to enable hotlinks in their balloon tips can do so, and still remain compatible with people who wrote to the old API?"

    (I've heard of one person trying to pass a TTF_PARSELINKS flag in the NOTIFYICONDATA.uFlags member and wondering why it wasn't working. I hope it's obvious to everybody why this had no chance of working.)

    Because that would just be passing the buck. Anybody who used this proposed flag would then have to be extra-careful not to put untrusted links in their balloon tips. Most people would just say, "Wow! A new flag! That's awesome!" and start using it without considering the serious security implications. Then somebody can trick the program into putting untrusted text into a balloon tip and thereby exploit the security hole.

    "Aw, come on, who would be so stupid as to write code without considering all the security implications?"

    I hope that was a joke question.

    The best way to make sure things are secure is to make it impossible to be insecure.

Page 1 of 4 (32 items) 1234