May, 2004

  • The Old New Thing

    When should your destructor be virtual?


    When should your C++ object's destructor be virtual?

    First of all, what does it mean to have a virtual destructor?

    Well, what does it mean to have a virtual method?

    If a method is virtual, then calling the method on an object always invokes the method as implemented by the most heavily derived class. If the method is not virtual, then the implementation corresponding to the compile-time type of the object pointer.

    For example, consider this:

    class Sample {
     void f();
     virtual void vf();
    class Derived : public Sample {
     void f();
     void vf();
    void function()
     Derived d;
     Sample* p = &d;

    The call to p->f() will result in a call to Sample::f because p is a pointer to a Sample. The actual object is of type Derived, but the pointer is merely a pointer to a Sample. The pointer type is used because f is not virtual.

    On the other hand, the call to The call to p->vf() will result in a call to Derived::vf, the most heavily derived type, because vf is virtual.

    Okay, you knew that.

    Virtual destructors work exactly the same way. It's just that you rarely invoke the destructor explicitly. Rather, it's invoked when an automatic object goes out of scope or when you delete the object.

    void function()
     Sample* p = new Derived;
     delete p;

    Since Sample does not have a virtual destructor, the delete p invokes the destructor of the class of the pointer (Sample::~Sample()), rather than the destructor of the most derived type (Derived::~Derived()). And as you can see, this is the wrong thing to do in the above scenario.

    Armed with this information, you can now answer the question.

    A class must have a virtual destructor if it meets both of the following criteria:

    • You do a delete p.
    • It is possible that p actually points to a derived class.

    Some people say that you need a virtual destructor if and only if you have a virtual method. This is wrong in both directions.

    Example of a case where a class has no virtual methods but still needs a virtual destructor:

    class Sample { };
    class Derived : public Sample
     CComPtr<IStream> m_p;
     Derived() { CreateStreamOnHGlobal(NULL, TRUE, &m_p); }
    Sample *p = new Derived;
    delete p;

    The delete p will invoke Sample::~Sample instead of Derived::~Derived, resulting in a leak of the stream m_p.

    And here's an example of a case where a class has virtual methods but does not require a virtual destructor.

    class Sample { public: virtual void vf(); }
    class Derived : public Sample { public: virtual void vf(); }
    Derived *p = new Derived;
    delete p;

    Since the object deletion occurs from the pointer type that matches the type of the actual object, the correct destructor will be invoked. This pattern happens often in COM objects, which expose several virtual methods corresponding to its interfaces, but where the object itself is destroyed by its own implementation and not from a base calss pointer. (Notice that no COM interfaces contain virtual destructors.)

    The problem with knowing when to make your destructor virtual or not is that you have to know how people will be using your class. If C++ had a "sealed" keyword, then the rule would be simpler: If you do a "delete p" where p is a pointer to an unsealed class, then that class needs have a virtual destructor. (The imaginary "sealed" keyword makes it explicit when a class can act as the base class for another class.)

  • The Old New Thing

    Extending the Internet Explorer context menu


    In a comment, Darrell Norton asked for a "View in Mozilla" option for Internet Explorer.

    You can already do this.

    Internet Explorer's context menu extension mechanism has been in MSDN for years. Let me show you how you can create this extension yourself.

    First, create the following registry key:

    [HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\View in Mozilla]

    Of course, you need to change C:\some\path\to to an actual path.

    How did I know to do this? Because steps 1, 2 and 3 in the "Implementation Steps" section tell me (1) what key to create, (2) what to set the default value to, and (3) what to set Contexts to. I chose a Context value of 1, which means "Default".

    Okay, now to write the script ViewInMozilla.htm. Well, the documentation says that I can access context from the menuArguments property of the external object. So let's start with that.


    Okay, let's run this puppy. Launch IE, right-click on a blank space in the web page, select "View in Mozilla", and you get...


    Woo-hoo! This is a major accomplishment: Something happened at all. Doing things in small steps makes it easy to identify where a problem is. If we had run full steam ahead to completion and then it didn't work, we wouldn't have known whether it was due to a bug in the script, a bad registration, a bad filename...

    Now that I have the menu arguments, I can use that to suck out information about the item that the context menu applies to. Let's try this:


    Woo-hoo, now it gives me the URL. Almost there. All that's left to do is to run a program with that URL as the command line parameter.

    var shell = new ActiveXObject("WScript.Shell");"mozilla \"" + external.menuArguments.document.URL + "\"");

    Mission accomplished.

    Now you too can create Internet Explorer context menu extensions.

    In fact, go ahead and do it, since Darrell asked for it: Create an Internet Explorer context menu extension that operates on anchors and opens the linked-to page in Mozilla.

    (Bonus: Tony Schreiner cooked up something similar for zooming.)

  • The Old New Thing

    You can do anything at, anything at all


    Zombo has been around for many years, and yet I still find it hilarious (requires Flash).

    I just went back to check, and alas the introduction actually ends. But fortunately, they made it even cooler by having a text-only version. (Still requires sound.)

  • The Old New Thing

    When you change the insides, nobody notices


    I find it ironic when people complain that Calc and Notepad haven't changed. In fact, both programs have changed. (Notepad gained some additional menu and status bar options. Calc got a severe workover.)

    I wouldn't be surprised if these are the same people who complain, "Why does Microsoft spend all its effort on making Windows 'look cool'? They should spend all their efforts on making technical improvements and just stop making visual improvements."

    And with Calc, that's exactly what happened: Massive technical improvements. No visual improvement. And nobody noticed. In fact, the complaints just keep coming. "Look at Calc, same as it always was."

    The innards of Calc - the arithmetic engine - was completely thrown away and rewritten from scratch. The standard IEEE floating point library was replaced with an arbitrary-precision arithmetic library. This was done after people kept writing ha-ha articles about how Calc couldn't do decimal arithmetic correctly, that for example computing 10.21 - 10.2 resulted in 0.0100000000000016.

    (These all came from people who didn't understand how computers handle floating point. I have a future entry planned to go into floating point representations in more detail.)

    Today, Calc's internal computations are done with infinite precision for basic operations (addition, subtraction, multiplication, division) and 32 digits of precision for advanced operations (square root, transcendental operators).

    Try it: 1 / 3 * 10000000000 - 3333333333 =. The result is one third exactly. Type 1/x - 3 = and you get zero back. (Of course, if you don't believe that, then repeat the sequence "* 10000000000 - 3333333333 =" until you're bored and notice that the answer always comes back as 0.33333333333333333333333333333333. If it were fixed-precision, then the 3's would eventually stop coming.)

    Thirty-two positions of precision for inexact results not good enough? The Power Calculator PowerToy uses the same arithmetic engine as Calc and lets you crank the precision to an unimaginable 512 digits.

    Anyway, my point is that - whether you like it or not - if you don't change the UI, nobody notices. That's so much effort is spent on new UI.

  • The Old New Thing

    What is the difference between Minimize All and Show Desktop?


    The keyboard shortcut for "Minimize All" is ÿ+M and The keyboard shortcut for "Show Desktop" is ÿ+D. How are they different?

    "Minimize All" is easier to describe. It minimizes all the windows that support the "Minimize" command. You can minimize a window by selecting "Minimize" from its System menu, or by clicking the 0 button in the title bar. So "Minimize All" is effectively the same as going to each window that is open and clicking the Minimize button. If there is a window that doesn't have a Minimize button, then it is left alone.

    "Show Desktop" takes "Minimize All" one step further. After minimizing all the windows that can be minimized, it then takes the desktop and "raises" it to the top of the window stack so that no other windows cover it. (Well, okay, topmost windows continue to cover it.)

    So "Show Desktop" manages to get a few more windows out of your way than "Minimize All".

    Note, however, that when you return the desktop to its normal state (either by selecting "Show Open Windows" or just by switching to another window), all the un-minimizeable windows come back because the desktop has "lowered" itself back to the bottom of the window stack.

  • The Old New Thing

    How does the desktop choose the icon label color?


    Many system colors come in pairs, one for background and one for foreground. For example, COLOR_MENU and COLOR_MENUTEXT; COLOR_WINDOW and COLOR_WINDOWTEXT; COLOR_HIGHLIGHT and COLOR_HIGHLIGHTTEXT. More on these colors in a future blog entry.


    How does the desktop choose the icon label color?

    It's not very exciting.

    The COLOR_DESKTOP color is studied to see if it is mostly dark or mostly light. If mostly dark, then the icon text is white. If mostly light, then the icon text is black.

    This works great if you don't have a wallpaper. If you do, then the desktop color doesn't typically correspond to the dominant color in your wallpaper, which may lead to a poor choice of black vs. white.

    To remedy this, you can manually set the desktop color by going to the Display control panel and going to the Desktop tab. Change the desktop color to something dark to get white icon text and to something light to get black icon text.

  • The Old New Thing

    How do you undo Minimize All and Show Desktop?


    It seems it's not well-known how to undo a Minimize All or a Show Desktop, two operations which I discussed briefly yesterday.

    To undo a Minimize All, you can right-click the taskbar and select "Undo Minimize All", or at least you used to be able to do that. Minimize All is no longer on the menus, presumably because it ended up redundant with Show Desktop.

    So after you do your ÿ+M to Minimize All, you can type ÿ+Shift+M to Undo Minimize All.

    To undo a Show Desktop, just click Show Desktop again. If you used the keyboard accelerator ÿ+D, just press it a second time.

  • The Old New Thing

    Why is the default 8-bit codepage called "ANSI"?


    Reader Ben Hutchings wanted to know why the 8-bit codepage is called "ANSI" when it isn't actually ANSI.

    But instead of saying, "Oh well, some things mortals were never meant to know," he went and dug up the answer himself.

    A quick Google for Windows ANSI misnomer found me exactly what I was looking for [pdf]:

    "The term "ANSI" as used to signify Windows code pages is a historical reference, but is nowadays a misnomer that continues to persist in the Windows community. The source of this comes from the fact that the Windows code page 1252 was originally based on an ANSI draft, which became ISO Standard 8859-1. However, in adding code points to the range reserved for control codes in the ISO standard, the Windows code page 1252 and subsequent Windows code pages originally based on the ISO 8859-x series deviated from ISO. To this day, it is not uncommon to have the development community, both within and outside of Microsoft, confuse the 8859-1 code page with Windows 1252, as well as see "ANSI" or "A" used to signify Windows code page support.
  • The Old New Thing

    Why does my hard drive light flash every few second?


    Back in Windows 95, people would notice that their hard drive light would blink every few seconds. What's that all about?

    Actually, it wasn't the hard drive light after all.

    Windows 95 was polling your CD-ROM drive to see whether you had inserted a new CD. Some computers wired up the "hard drive light" not to the hard drive but rather to the SCSI and/or IDE bus. So the light didn't indicate hard drive activity necessarily. It turned on for any bus activity.

    Fortunately, motherboard manufacturers discovered their mistake and nowadays you don't find any of them that miswire the hard drive access light.

    Or do you? I keep my computer under my desk so I never see the hard drive light anyway. I'm just surmising that in the past seven years, motherboard manufacturers have gotten their act together.

  • The Old New Thing

    There are two types of scrollbars


    Remember that there are two types of scrollbars.

    One is the standalone scrollbar control. This one has its own window handle, and consequently can be given focus and all those other fun things you can do with window handles. To manipulate them, pass the handle to the scrollbar control to the appropriate scrollbar function (SetScrollInfo, for example) and pass SB_CTL as the nBar parameter to indicate that you have a scrollbar control.

    The other type is the horizontal or vertical scrollbar (or both) attached to a window by virtue of having the WS_HSCROLL and/or WS_VSCROLL style. These are nonclient scrollbars and are not controls. They are just decorations added to some other window. You can't give them focus since they aren't windows in their own right. To manipulate them, pass the handle to the containing window to the appropriate scrollbar function and pass SB_HORZ or SB_VERT as the nBar parameter to indicate that you want to manipulate the nonclient horizontal or vertical scrollbar.

    I'm writing this down since some people seem to miss the distinction between these two cases.

Page 1 of 5 (47 items) 12345