• The Old New Thing

    2007 year-end link clearance

    • 5 Comments

    A few random links that I've collected over the last six months.

    And then the obligatory plug for my column in TechNet Magazine, which, despite the fact that Microsoft's name is on the magazine cover, does not establish the official Microsoft position on anything.

  • The Old New Thing

    How did the invalid floating point operand exception get raised when I disabled it?

    • 23 Comments

    Last time, we learned about the dangers of uninitialized floating point variables but left with a puzzle: Why wasn't this caught during internal testing?

    I dropped a hint when I described how SNaNs work: You have to ask the processor to raise an exception when it encounters a signaling NaN, and the program disabled that exception. Why was an exception being raised when it had been disabled?

    The clue to the cause was that the customer that was encountering the crash reported that it tended to happen after they printed a report. It turns out that the customer's printer driver was re-enabling the invalid operand exception in its DLL_PROCESS_ATTACH handler. Since the exception was enabled, the SNaN exception, which was previously masked, was now live, and it crashed the program.

    I've also seen DLLs change the floating point rounding state in their DLL_PROCESS_ATTACH handler. This behavior can be traced back to old versions of the C runtime library which reset the floating point state as part of their DLL_PROCESS_ATTACH; this behavior was corrected as long ago as 2002 (possibly even earlier; I don't know for sure). Obviously that printer driver was even older. Good luck convincing the vendor to fix a bug in a driver for a printer they most likely don't even manufacture any more. If anything, they'll probably just treat it as incentive for you to buy a new printer.

    When you load external code into your process, you implicitly trust that the code won't screw you up. This is just another example of how a DLL can inadvertently screw you up.

    Sidebar

    One might argue that the LoadLibrary function should save the floating point state before loading a library and restore it afterwards. This is an easy suggestion to make in retrospect. Writing software would be so much easier if people would just extend the courtesy of coming up with a comprehensive list of "bugs applications will have that you should protect against" before you design the platform. That way, when a new class of application bugs is found, and they say "You should've protected against this!", you can point to the list and say, "Nuh, uh, you didn't put it on the list. You had your chance."

    As a mental exercise for yourself: Come up with a list of "all the bugs that the LoadLibrary function should protect against" and how the LoadLibrary function would go about doing it.

  • The Old New Thing

    Points are earned by programs, not by shortcuts

    • 63 Comments

    The first subtlety of the basic principle that determines which programs show up in the Start menu is something you may not have noticed when I stated it:

    Each time you launch a program, it "earns a point", and the longer you don't launch a program, the more points it loses.

    Notice that the rule talks about programs, not shortcuts.

    The "points" for a program are tallied from all the shortcuts that exist on the All Programs section of the Start menu. Many programs install multiple shortcuts, say one to the root of the All Programs menu and another to a deep folder. It doesn't matter how many shortcuts you have; if they all point to the same program, then it is that program that earns the points when you use any of the shortcuts.

    One the Start menu decides that a program has earned enough points to make it to the front page, it then has to choose which shortcut to use to represent that program. This is an easy decision if there's only one shortcut. If there are multiple shortcuts to the same program, then the most-frequently-used shortcut is selected as the one to appear on the front page of the Start menu.

    If you paid really close attention, you may have noticed a subtlety to this subtlety. We'll take that up next time.

    Please hold off your questions until the (two-week!) series is complete, because I suspect a later entry will answer them. (This series is an expansion upon the TechNet column on the same topic. If you've read the TechNet article, then a lot of this series will be review.)*

    Footnotes

    *I wrote this last time, but that didn't stop people from asking questions anyway. I don't expect it'll work today either, but who knows, maybe you'll surprise me.

  • The Old New Thing

    How can I find all objects of a particular type?

    • 28 Comments

    More than one customer has asked a question like this:

    I'm looking for a way to search for all instances of a particular type at runtime. My goal is to invoke a particular method on each of those instances. Note that I did not create these object myself or have any other access to them. Is this possible?

    Imagine what the world would be like if it were possible.

    For starters, just imagine the fun you could have if you could call typeof(Secure­String).Get­Instances(). Vegas road trip!

    More generally, it breaks the semantics of App­Domain boundaries, since grabbing all instances of a type lets you get objects from another App­Domain, which fundamentally violates the point of App­Domains. (Okay, you could repair this by saying that the Get­Instances method only returns objects from the current App­Domain.)

    This imaginary Get­Instances method might return objects which are awaiting finalization, which violates one of the fundamental assumptions of a finalizer, namely that there are no references to the object: If there were, then it wouldn't be finalized! (Okay, you could repair this by saying that the Get­Instances method does not return objects which are awaiting finalization.)

    On top of that, you break the syncRoot pattern.

    class Sample {
     private object syncRoot = new object();
     public void Method() {
      lock(syncRoot) { ... };
     }
    }
    
    If it were possible to get all objects of a particular class, then anybody could just reach in and grab your private sync­Root and call Monitor.Enter() on it. Congratuations, the private synchronization object you created is now a public one that anybody can screw with, defeating the whole purpose of having a private syncRoot. You can no longer reason about your syncRoot because you are no longer in full control of it. (Yes, this can already be done with reflection, but at least when reflecting, you know that you're grabbing somebody's private field called sync­Root, so you already recognize that you're doing something dubious. Whereas with Get­Instances, you don't know what each of the returned objects is being used for. Heck, you don't even know if it's being used! It might just be garbage lying around waiting to be collected.)

    More generally, code is often written on the expectation that an object that you never give out a reference to is not accessible to others. Consider the following code fragment:

    using (StreamWriter sr = new StreamWriter(fileName)) {
     sr.WriteLine("Hello");
    }
    

    If it were possible to get all objects of a particular class, you may find that your customers report that they are getting an Object­Disposed­Exception on the call to Write­Line. How is that possible? The disposal doesn't happen until the close-brace, right? Is there a bug in the CLR where it's disposing an object too soon?

    Nope, what happened is that some other thread did exactly what the customer was asking for a way to do: It grabbed all existing Stream­Writer instances and invoked Stream­Writer.Close on them. It did this immediately after you constructed the Stream­Writer and before you did your sr.Write­Line(). Result: When your sr.Write­Line() executes, it finds that the stream was already closed, and therefore the write fails.

    More generally, consider the graffiti you could inject into all output files by doing

    foreach (StreamWriter sr in typeof(StreamWriter).GetInstances()) {
     sr.Write("Kilroy was here!");
    }
    

    or even crazier

    foreach (StringBuilder rb in typeof(StringBuilder).GetInstances()) {
     sb.Insert(0, "DROP TABLE users; --");
    }
    

    Now no String­Builder is safe—the contents of any String­Builder can be corrupted at any time!

    If you could obtain all instances of a type, the fundamental logic behind computer programming breaks down. It effectively becomes impossible to reason about code because anything could happen to your objects at any time.

    If you need to be able to get all instances of a class, you need to add that functionality to the class itself. (GC­Handle or Weak­Reference will come in handy here.) Of course, if you do this, then you clearly opted into the "anything can happen to your object at any time outside your control" model and presumably your code operates accordingly. You made your bed; now you get to lie in it.

    (And I haven't even touched on thread safety.)

    Bonus reading: Questionable value of SyncRoot on Collections.

  • The Old New Thing

    Common gotchas when writing your own p/invoke

    • 21 Comments

    If you're looking to get into some p/invoke action, you'd be well-served to check out the pinvoke wiki to see if somebody else has done it too. If what you need isn't there, you may end up forced to write your own, and here are some gotchas I've seen people run into:

    • C++ bool and Win32 BOOLEAN are not the same as C# bool (aka System.Boolean). In Win32, BOOL is a 4-byte type, and BOOLEAN is a 1-byte type. [See also MadQ's remarks about VARIANT_BOOL.] Meanwhile, C++ bool is not standardized by Win32, so the size will vary based on your compiler, but most compilers use a 1-byte value. And then C# is even weirder: The bool is a 1-byte type, but it marshals as a 4-byte type by default.
    • Win32 char is not the same as C# char (aka System.Char). In C#, char is a Unicode character (two bytes), whereas in C/C++ under Win32 it is an ANSI character (one byte).
    • Win32 long is not the same as C# long (aka System.Int64). In C#, long is 64-bit value, whereas in C/C++ under Win32 it is a 32-bit value.
    • If memory is allocated and freed across the interop boundary, make sure both sides are using the same allocator. It is my understanding that the CLR uses CoTaskMemAlloc/CoTaskMemFree by default. If your Win32 function doesn't use CoTaskMemAlloc, you'll have to teach the CLR which allocator you really want.
    • When laying out structures, you have to watch out for alignment.

    That last one is particularly gnarly on 64-bit systems, where alignment requirements are less forgiving than on x86. The structure declarations on pinvoke.net tend to ignore 64-bit issues. For example, the declaration of the INPUT structure (as of this writing—it's a wiki so it's probably changed by the time you read this) reads as follows:

    [StructLayout(LayoutKind.Explicit)]struct INPUT {
      [FieldOffset(0)] int type;
      [FieldOffset(4)] MOUSEINPUT mi;
      [FieldOffset(4)] KEYBDINPUT ki;
      [FieldOffset(4)] HARDWAREINPUT hi;
    }
    

    This structure layout is correct for 32-bit Windows, but it's incorrect for 64-bit Windows.

    Let's take a look at that MOUSEINPUT structure, for starters.

    typedef struct tagMOUSEINPUT {
        LONG    dx;
        LONG    dy;
        DWORD   mouseData;
        DWORD   dwFlags;
        DWORD   time;
        ULONG_PTR dwExtraInfo;
    } MOUSEINPUT, *PMOUSEINPUT, FAR* LPMOUSEINPUT;
    

    In 64-bit Windows, the LONG and DWORD members are four bytes, but the dwExtraInfo is a ULONG_PTR, which is eight bytes on a 64-bit machine. Since Windows assumes /Zp8 packing, the dwExtraInfo must be aligned on an 8-byte boundary, which forces four bytes of padding to be inserted after the time to get the dwExtraInfo to align properly. And in order for all this to work, the MOUSEINPUT structure itself must be 8-byte aligned.

    Now let's look at that INPUT structure again. Since the MOUSEINPUT comes after the type, there also needs to be padding between the type and the MOUSEINPUT to get the MOUSEINPUT back to an 8-byte boundary. In other words, the offset of mi in the INPUT structure is 8 on 64-bit Windows, not 4.

    Here's how I would've written it:

    // This generates the anonymous union
    [StructLayout(LayoutKind.Explicit)] struct INPUT_UNION {
      [FieldOffset(0)] MOUSEINPUT mi;
      [FieldOffset(0)] KEYBDINPUT ki;
      [FieldOffset(0)] HARDWAREINPUT hi;
    };
    
    [StructLayout(LayoutKind.Sequential)] struct INPUT {
      int type;
      INPUT_UNION u;
    }
    

    I introduce a helper structure to represent the anonymous union that is the second half of the Win32 INPUT structure. By doing it this way, I let somebody else worry about the alignment, and it'll be correct for both 32-bit and 64-bit Windows.

    static public void Main()
    {
      Console.WriteLine(Marshal.OffsetOf(typeof(INPUT), "u"));
    }
    

    On a 32-bit system, this prints 4, and on a 64-bit system, it prints 8. The downside is that you have to type an extra u. when you access the mi, ki or hi members.

    input i;
    i.u.mi.dx = 0;
    

    (I haven't checked what the PInvoke Interop Assistant comes up with for the INPUT structure.)

  • The Old New Thing

    When should your destructor be virtual?

    • 20 Comments

    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 {
    public:
     void f();
     virtual void vf();
    };
    
    class Derived : public Sample {
    public:
     void f();
     void vf();
    }
    
    void function()
    {
     Derived d;
     Sample* p = &d;
     p->f();
     p->vf();
    }
    

    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;
    public:
     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

    The program doesn't have to be run from the Start menu to earn Start menu points

    • 29 Comments

    There's a second subtlety to the basic principle that determines which programs show up in the Start menu:

    Each time you launch a program, it "earns a point", and the longer you don't launch a program, the more points it loses.

    Since programs earn points and not shortcuts, a program can earn points even if you don't use the Start menu to run it.

    In usability studies, we often see people who run programs by digging through their Program Files directory until they find an icon that looks promising and then double-click it. If there is a shortcut on the All Programs section of the Start menu that points to the same program, then that shortcut will eventually work its way onto the front page, assuming the user runs the program often enough.

    This is why you will see a program appear on the front page of the Start menu even though you never ran it from the Start menu. The program earned points because you ran the program manually, or because you opened a document that is associated with that program. Promoting a program run this way helps users realize that they can run Backgammon from the Start menu instead of having to open My Computer, then click on my C drive, then click on Program Files, then MSN Gaming Zone, then Windows, and then double-click the icon with the strange name bckgzm. I've seen usability sessions where the users did this repeatedly, and they considered it perfectly normal, albeit frustrating. "Computers are so hard to use."

    Next time, we'll look at how the pin list influences the list of frequently-used programs.

  • The Old New Thing

    Sensor development kits were flying off the shelves

    • 21 Comments

    After the Sensor and Location Platform PDC presentation, people were stopping by the booth and grabbing sensor hardware and development kits like they were candy. Then again, to geeks, this stuff is candy.

    (And technically, they weren't flying off shelves. They were flying out of bins. Well, and technically they weren't flying either.)

    Other notes from the last day of the 2008 PDC:

    • PDC content honcho Mike Swanson announced that PDC 2008 sessions and keynotes are now up for download, and will be available indefinitely. Available in Silverlight for live streaming, or in iTunes format, WMV, high-quality WMV (this one includes video of the speakers), and Zune for offline viewing. In response, Nigel Parker created the PDC session firehose.
    • Sometimes, when you make the sign too big, nobody can see it. Someone came up to me asking where the Azure booth was. I pointed at the insanely huge banner. "Probably somewhere over there."
    • The reason why room 406A is on the third floor but you press 2 to get there: The room numbers at the convention center are numbered based on room clusters. The 100's in one cluster, the 400's are in another cluster, etc. The "4" in 406 doesn't mean fourth floor; it means fourth cluster. And why do you push 2 to get to the third floor? Because Level 1 is a split level which occupies two floors. Therefore, in the elevator, there are two buttons to get to Level 1, depending on whether you want the upper half or lower half. Cluster 4 is on Level 2, so the button for that is 3.
    • A video of carrots. If you had come to my PDC talk, you'd understand. (The carrot joke elicited a mild chuckle from the main room, but I'm told that in the overflow room it totally killed. I'm not sure what that says about people in the overflow room. It really wasn't that great a joke, people.)
    • At Ask the Experts, I started out at one of the Windows 7 tables, and we speculated as to what the most obscure technical question would be. One of my colleagues suggested that it would be a BitLocker question. And what would you know, the very first person to come to our table had a BitLocker question. Later in the evening, I moved to another Windows 7 table and posed the same question. At the second table, we figured that the most obscure technical question would be about the dispatcher spinlock. About fifteen minutes later, somebody stopped by with a question about the dispatcher spinlock. I decided to stop playing this game. It was too much like a Twilight Zone episode.
    • One attendee was shy about speaking English (not being a native speaker) and came up with a clever solution: The attendee walked up to our table, said, "I have some questions, can you read them?" and then opened a laptop computer where a Word document awaited with the four questions already typed up in a large font so everybody could read them. We answered the questions, and everybody seemed pleasantly surprised with how well this approach worked.
    • At the end of the conference the Channel 9 folks held a giveaway for their giant beanbags. Like anybody would be able to take a giant beanbag home on the airplane. (Second prize: Two beanbags!)
    • James Senior has the PDC by the numbers. I have trouble believing the number of bananas consumed, though. Twenty-five bananas in four days? Perhaps we should start calling James Monkey Boy.
  • The Old New Thing

    Why does a new user get stuff on their Start menu right off the bat?

    • 34 Comments

    In the initial designs for the Start menu, the list of most-frequently-used programs on the Start menu would be completely empty the first time you opened it. This was perfectly logical, since you hadn't run any programs at all yet, so nothing was frequently-used because nothing had been used at all! Perfectly logical and completely stupid-looking.

    Imagine the disappointment of people who just bought a computer. They unpack it, plug everything in, turn it on, everything looks great. Then they open the Start menu to start using their computer and they get... a blank white space. "Ha-ha! This computer can't do anything! You should have bought a Mac!"

    (In usability-speak, this is known as "the cliff": You're setting up a new computer, everything looks like it's going great, and then... you're staring at a blank screen and have no idea what to do next. The learning curve has turned into a precipice.)

    The original design attempted to make this initially-blank Start menu less stark by adding text that said, roughly, "Hey, sure, this space is blank right now, but as you run programs, they will show up here, trust me."

    Great work there. Now it's not stupid any more. Now it's stupid and ugly.

    It took a few months to figure out how to solve this problem, and ultimately we decided upon what you see in Windows XP: For brand new users, we create some "artificial points" so that the initial Start menu has a sampling of fun programs on it. The number of artificial points is carefully chosen so that they are enough points to get the programs onto the Start menu's front page, but not so many points that they overwhelm the "real" points earned by programs users run themselves. (I believe the values were chosen so that a user needs to run a program only twice to get it onto the front page on the first day.)

    Note that these "artificial points" are not given if the user was upgraded from Windows 2000. In that case, the points that the Windows 2000 Start menu used for Intellimenus were used to seed the Windows XP point system. In that way, the front page of the Start menu for an upgraded uses already reflects the programs that the user ran most often on Windows 2000.

    In the initial release of Windows XP, the "artificial points" were assigned so that the first three of the six slots on the most-frequently-used programs list were chosen by Windows and the last three by the computer manufacturer. If your copy of Windows XP was purchased at retail instead of preinstalled by the computer manufacturer, or if the computer manufacturer declined to take advantage of the three slots offered to it (something that never happened in practice), then Windows took two of the three slots that had been offered to the computer manufacturer, leaving the last slot blank. That way, the very first program you ran showed up on your Start menu immediately.

    In Windows XP Service Pack 1, the assignment of the six slots changed slightly. Two of the slots were assigned by Windows, one by the United States Department of Justice, and the last three by the computer manufacturer. (Again, if you bought your copy of Windows XP at retail, then two of the computer manufacturer slots were assigned by Windows and the last was left blank.)

  • The Old New Thing

    More Start menu fine-tuning: Choosing a better representative for a frequently-run program

    • 10 Comments

    If you paid really close attention to the way a representative shortcut is selected for a program, you may have noticed a problem with it. Here's the rule again:

    If there are multiple shortcuts to the same program, then the most-frequently-used shortcut is selected as the one to appear on the front page of the Start menu.

    Suppose there are two shortcuts to Notepad on the All Programs section of the Start menu, one is the standard Notepad shortcut that comes with Windows, and the other is a shortcut whose command line is notepad.exe C:\Program Files\LitWare Inc\Release Notes.txt. Now suppose the user opens a text document on the desktop. Notepad runs, it "earns a point", and suppose that this gives Notepad enough points to appear on the front page of the Start menu. Which Notepad shortcut do we show?

    According to the rule stated above, we will choose either the standard Notepad shortcut or the LitWare Release Notes shortcut, depending on which one you've run most frequently. If it's the latter, then you'll have the puzzling result that opening a text document on the desktop causes the LitWare Release Notes shortcut to show up on the front page of the Start menu. It's perfectly logical and completely baffling at the same time.

    In Windows Vista, another tweak was added to the algorithm by which a shortcut is chosen to represent a program on the front page of the Start menu: If the user hasn't run any of a program's shortcuts from the Start menu, a shortcut that doesn't have any command line parameters is preferred over one that does.

    This tweak causes the Start menu to favor the standard Notepad shortcut over the LitWare Release Notes shortcut. It also means that, for example, a shortcut to Litware.exe is preferred over a shortcut of the form Litware.exe -update.

    Note: I was not present at the Windows Vista Start menu design meetings, so I have no insight into the rationale behind its design. Sorry.

Page 8 of 436 (4,356 items) «678910»