August, 2008

  • The Old New Thing

    It's not Christmas: Nobody enjoys unwrapping your present

    • 142 Comments

    I don't know why it happens, but it happens with disturbing frequency. A customer wants to report a problem, and then illustrate it with a screenshot or two, but instead of attaching the screenshots, they paste the screenshots inside a Word document (and for some reason it's always Word) and then attach the Word document.

    It's not a Christmas present. People aren't going to say "Wow, I wonder what's inside? I'm brimming with anticipation!" They're going to say, "Oh great, I can't even see the screen shot. I have to download the attachment, scan it for viruses, then load it into Word. Oh wait, this is a Word 2007 document and I only have Word 2003; let me run the converter first. Okay good, now I can open the document to see, oh, look, it's a picture." Most people won't bother. And then you're going to wonder why nobody answered your first message.

    If you insist on attaching the pictures, just attach them directly. And use a compressed image format like JPG or PNG, please. Don't send uncompressed screenshots; they are ridiculously huge. Cropping the image to the relevant portion of the screen helps, too. (This is very easy to do with the Snipping Tool.)

    In March of this year, a customer wrote, "I have attached a Word document that describes the problem." (Hey, here's an idea: Why not describe the problem in your email message?)

    The Word document contained a screenshot.

    The screenshot was of an email message.

    The email message contained a screenshot.

    Bonus remark from the customer liaison: "Once you open the document, you may need to zoom it further to read it."

    Wooden table not included.

  • The Old New Thing

    The unwritten rule of riding a Seattle Metro bus

    • 62 Comments

    The Metro King County transit site has all the facts about how to ride the bus, but there's another rule that is applied by convention rather than by any formal codification:

    For some reason, and I see this only in Seattle, it is customary to say Thank you to the bus driver as you get off the bus.

    Tip for new riders: If you aren't familiar with the area, you can ask the bus driver to announce the stop you intend to get off at.

    Bonus tip for bicyclists: There is an experimental program running this summer to allow bicyclists to ride out-of-service buses across the 520 bridge for free. You have to get on at Evergreen Point and get off at Montlake (or vice versa). This is in addition to the existing policy of allow bicyclists to ride out-of-service buses across the bridge (paying the normal fare), eastbound as far as 51st St, assuming the bus is heading that way. (This bonus tip is not that helpful to Microsoft employees, who already get free bus passes and a special bike shuttle, but I figured I'd toss it out there.)

  • The Old New Thing

    Why was the RAM drive removed from Windows 95?

    • 59 Comments

    Commenter Travis Owens asks, "Why was the RAM drive removed from Windows 95?"

    As with many of these types of accusatory questions, this comes with a false hidden assumption, in this case, that the RAM drive was in Windows 95 to begin with.

    Remember that Windows 95 introduced a new driver model, so any RAM drive would have had to be written from scratch. (Well, okay, you could use the old 16-bit RAM drive, but then you'd be going through the real-mode mapper, at which point your performance would go down the tubes.) As you admitted, it was a very niche feature that usually hurts but can sometimes help in very specific circumstances. Which Windows 95 file system feature would you have cut so that the geeks could have their RAM drive? Should we even be showing RAM drives to typical users, who would just turn it on without understanding its consequences, winding up with a computer that ran worse than if it had never been there?

    If you want a RAM drive, you can sit down with the DDK and write one. But the Windows 95 folks decided that adding support for SCSI hard drives would be a more effective use of the time and skills of the Windows 95 storage device driver development team—which, as I recall, consisted of three people.

    Mind you, if you really want your precious RAM drive, I've heard rumors that it's tucked away on your Windows Server 2003 CD under the names RAMDISK.IN_ and RAMDISK.SY_, and that there is a way to add a RAM drive to your Windows PE disk. (And no, I haven't heard any rumors about Windows Server 2008.)

  • The Old New Thing

    I warned you: The dangers of attaching input queues

    • 53 Comments

    Some people didn't take to heart my cautions on the subject of attached input queues, item number five on the list of five things every Win32 programmer should know. And then they find that their application stops responding.

    // Code in italics is wrong
    void TryToStealFocus(HWND hwnd)
    {
      // First try plain SetForegroundWindow
      SetForegroundWindow(hwnd);
      HWND hwndFG = GetForegroundWindow();
      if (hwndFG == hwnd) return;
    
      // That didn't work - if the foreground window belongs
      // to another thread, attach to that thread and try again
      DWORD dwCurrentThread = GetCurrentThreadId();
      DWORD dwFGThread = GetWindowThreadProcessId(hwndFG, NULL);
      if (dwFGThread == dwCurrentThread) return;
    
      AttachThreadInput(dwCurrentThread, dwFGThread, TRUE);
      SetForegroundWindow(hwnd); // hangs here
      AttachThreadInput(dwCurrentThread, dwFGThread, FALSE);
    }
    

    Their customer feedback data shows that this function often hangs at the second call to SetForegroundWindow. My exercise for you is to explain why. (Here's someone else with the same problem.)

    (Note that both of these customers are trying to circumvent the foreground lock timeout so that they can steal focus and shove a dialog box in the user's face.)

  • The Old New Thing

    The implementation of iterators in C# and its consequences (part 2)

    • 49 Comments

    Now that you have the basic idea behind iterators under your belt, you can already answer some questions on iterator usage. Here's a scenario based on actual events:

    I have an iterator that is rather long and complicated, so I'd like to refactor it. For illustrative purposes, let's say that the enumerator counts from 1 to 100 twice. (In real life, of course, the iterator will not be this simple.)

    IEnumerable<int> CountTo100Twice()
    {
     int i;
     for (i = 1; i <= 100; i++) {
      yield return i;
     }
     for (i = 1; i <= 100; i++) {
      yield return i;
     }
    }
    

    As we learned in Programming 101, we can pull common code into a subroutine and call the subroutine. But when I do this, I get a compiler error:

    IEnumerable<int> CountTo100Twice()
    {
     CountTo100();
     CountTo100();
    }
    
    void CountTo100()
    {
     int i;
     for (i = 1; i <= 100; i++) {
      yield return i;
     }
    }
    

    What am I doing wrong? How can I move the "count to 100" into a subroutine and call it twice from the CountTo100Twice function?

    As we saw last time, iterators are not coroutines. The technique above would have worked great had we built iterators out of, say, fibers instead of building them out of state machines. As state machines, all yield return statements must occur at the "top level". So how do you iterate with the help of subroutines?

    You make the subroutine its own iterator and suck the results out from the main function:

    IEnumerable<int> CountTo100Twice()
    {
     foreach (int i in CountTo100()) yield return i;
     foreach (int i in CountTo100()) yield return i;
    }
    
    IEnumerable<int> CountTo100()
    {
     for (i = 1; i <= 100; i++) {
      yield return i;
     }
    }
    

    Exercise: Consider the following fragment:

     foreach (int i in CountTo100Twice()) {
      ...
     }
    

    Explain what happens on the 150th call to MoveNext() in the above loop. Discuss its consequences for recursive enumerators (such as tree traversal).

  • The Old New Thing

    The gradual erosion of the car trip experience

    • 38 Comments

    How will kids today learn to get along with their siblings? I just learned that another of the basic childhood conflict resolution scenarios has disappeared, thanks to the dual-screen DVD player and entertainment system for your car, so each kid can remain content without the burden of having to interact with their annoying brother or sister. The traditional car ride games will slowly fade away, replaced with questions like, "Grandma, where's the Nintendo?"

    Why stop there? Why not just equip the car with tranquilizing gas in the back seat? The kids go in, you knock them unconscious, and you wake them up when you arrive at the destination.

    One of my friends told me that as a child, she objected that her brother was looking out her window, a degree of territoriality I was previously not aware of. Her parents naturally ridiculed her for making such a complaint, and I think she turned out okay.

  • The Old New Thing

    Microspeak: Pencils down

    • 37 Comments

    I'm particularly fascinated by Microspeak terms which nobody actually knows the meaning of. You can defend jargon by saying that it's a shorthand way of talking in order to improve communication, but if nobody actually knows what it means, the in order to improve communication part is completely turned on its head. The Microspeak that allegedly allows people to communicate better ends up making them communicate worse.

    A colleague of mine introduced me to this new type of Microspeak. Our conversation takes place in an impromptu hallway meeting between the development manager and a few members of the team.

    Team member: "What does this mean for our milestone? I'm assuming it means that our features are code complete."

    Development manager: It means you are pencils down.

    Team member: (confused) "What does 'pencils down' mean?"

    — It means your features are done for the milestone.

    "Where did that term come from?"

    — A meeting with «senior executive».

    "Okay, so what does 'done' mean?"

    — It means you are pencils down.

    "What exactly does that mean?"

    — It means you don't have to write any new code for your features.

    "Oh, okay. So it means code complete."

    — No, that means something different.

    "How are they different?"

    — I don't know.

    "Has anyone sent any email defining what 'pencils down' means?"

    — Not that I know of.

    My colleague has yet to find anybody who can provide a definition of the term pencils down.

    This sort of confirms what my colleague Michael Grier mentioned in a comment: The intended purpose for this jargon is not to communicate with the people who work for you but to impress the people you work for.

  • The Old New Thing

    When somebody says a game is like chess, it is usually completely unlike chess

    • 36 Comments

    Chess has become elevated to the gold standard of board games. Everybody wishes their game had the cultural gravity of chess, a game whose mere mention elevates you to the category of deep thinker and profound strategist. (We'll set aside the scrawny glasses-wearing nerd part for now.)

    I've learned that when somebody says a game is like chess, it's pretty much guaranteed that the game in fact bears no resemblance to chess whatsoever.

    I came to this realization when somebody pulled out a game with a strange-looking board with what looked like plastic volcanoes glued to the surface, catwalks, and a lazy susan. Each player was represented by a token, set in its respective starting position. I asked, "Hey, what's this game?"

    The person replied, "It's a lot like chess. First you roll the dice..."

    (Translation from the German in that link above, for those who need some help.)

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    What's with this MSH_MOUSEWHEEL message?

    • 34 Comments

    The hardware folks had this mouse wheel thing they were making, and they needed a way to get applications to support the mouse. Now, one way of doing this was to say, "Well, we'll start selling this wheel mouse, but no applications can use it until the next version of Windows is released, one that supports the wheel." Of course, that would have meant waiting until Windows NT 4 came out, and who know when that would be. Plus it meant that people would have to upgrade Windows in order to take advantage of their fancy new mouse. As you can imagine, they weren't too pleased with the "wait a few years" plan.

    In the interim, they proposed a stopgap mechanism for applications to respond to the mouse wheel. Enter the zmouse.h header file and its MSH_MOUSEWHEEL registered message. When you installed the wheel mouse driver, it listened for wheel events from the hardware and posted this new message when the mouse wheel turned, and applications could just respond to either the WM_MOUSEWHEEL message (if running on a version of Windows that supported the message) or the MSH_MOUSEWHEEL message (if running on an older version of Windows that didn't). Unfortunately, the two messages behave differently, so it's not a simple matter of writing

    if (uMsg == WM_MOUSEWHEEL || uMsg == g_msgWheel) {
     ... do wheel stuff ...
    }
    

    (These next few paragraphs summarize what is already spelled out in MSDN; you can skip them if you already know how the messages work.)

    First, let's look at WM_MOUSEWHEEL. This message is delivered to the window that has focus (in the SetFocus sense). If the window procedure doesn't handle the message and just passes it through to the DefWindowProc function, then the DefWindowProc function forward the message to the window's parent. In this way, the WM_MOUSEWHEEL message automatically "bubbles outward" from the focus window up the parent chain until somebody finally handles the message (or it goes all the way to the top without being handled at all).

    On the other hand, the MSH_MOUSEWHEEL message works from the outside in. It is delivered to the foreground window (in the SetForegroundWindow sense). If the window procedure doesn't want to handle the message, it can forward the message to child windows of its choice, until one of them returns TRUE to indicate that the message was handled, or until no further candidates exist.

    I'll summarize these differences in a table, since people seem to like tables so much.

    WM_MOUSEWHEEL MSH_MOUSEWHEEL
    Propagation direction Inside-out Outside-in
    Propagation mechanism DefWindowProc SendMessage
    Handling Automatic Manual: Application checks return value
    from child to determine what to do next
    Return value if processed Zero TRUE
    Return value if not processed DefWindowProc FALSE

    Notice that WM_MOUSEWHEEL is much simpler, and the inside-out propagation mechanism retains the spirit of other messages such as WM_CONTEXTMENU and WM_SETCURSOR. Why can't MSH_MOUSEWHEEL do it the same way?

    Well, first of all, MSH_MOUSEWHEEL doesn't have the luxury of being able to modify the DefWindowProc function. After all, that's the whole point of introducing the message in the first place, because we're trying to add wheel support to an older operating system that predated mouse wheels. Put in other words, if we could modify DefWindowProc to handle the MSH_MOUSEWHEEL message, then we wouldn't have needed the MSH_MOUSEWHEEL message to begin with; we would've just modified DefWindowProc to handle the WM_MOUSEWHEEL message.

    The argument in the previous paragraph is a frustratingly common one. Given a problem X and a workaround Y, somebody will ask, "Why didn't you use method Z?" If you look at method Z, though, you'll see that it suffers from the exact same problem X.

    Here's a real-world example of the "confused workaround":

    "Since the I-90 bridge is closed, I can't take the 550 bus to get from Bellevue to Safeco Field. Instead, I'm going to take the 230 to Redmond, and then change to the 545."

    — Well, that's silly. Why not take the 245 to Eastgate, and then change to the 554? It's a lot faster.

    "Um, the 554 uses the I-90 bridge, too."

    Okay, so you can't change DefWindowProc, but why not at least propagate the MSH_MOUSEWHEEL from the inside out instead of from the outside in?

    Starting with the focus window assumes you can even find out what the focus window is, but if you had paid attention to the Five Things Every Win32 Programmer Should Know, you would have known that each thread has its own focus window. (Not nitpickily true, but true enough.) Consequently, when the helper program that injects MSH_MOUSEWHEEL messages calls GetFocus, it just gets its own focus window, not the focus window of the thread that controls the foreground window. (Remember, we're talking 1996, long before the GetGUIThreadInfo function was invented. History buffs can find out more from Meet The Inventor of the Mouse Wheel.) Since inside-out was off the table, that pretty much forced outside-in.

    Now that you know how mouse wheel messages work, you can explain the behavior this customer is seeing:

    I'm seeing the WM_MOUSEWHEEL message being delivered to the wrong child window. I have a parent window with two children. Even though I move the mouse pointer over child 1, the WM_MOUSEWHEEL goes to child 2.
  • The Old New Thing

    The implementation of iterators in C# and its consequences (part 1)

    • 33 Comments

    Like anonymous methods, iterators in C# are very complex syntactic sugar. You could do it all yourself (after all, you did have to do it all yourself in earlier versions of C#), but the compiler transformation makes for much greater convenience.

    The idea behind iterators is that they take a function with yield return statements (and possible some yield break statements) and convert it into a state machine. When you yield return, the state of the function is recorded, and execution resumes from that state the next time the iterator is called upon to produce another object.

    Here's the basic idea: All the local variables of the iterator (treating iterator parameters as pre-initialized local variables, including the hidden this parameter) become member variables of a helper class. The helper class also has an internal state member that keeps track of where execution left off and an internal current member that holds the object most recently enumerated.

    class MyClass {
     int limit = 0;
     public MyClass(int limit) { this.limit = limit; }
    
     public IEnumerable<int> CountFrom(int start)
     {
      for (int i = start; i <= limit; i++) {
       yield return i;
      }
     }
    }
    

    The CountFrom method produces an integer enumerator that spits out the integers starting at start and continuing up to and including limit. The compiler internally converts this enumerator into something like this:

     class MyClass_Enumerator : IEnumerable<int> {
      int state$0 = 0;// internal member
      int current$0;  // internal member
      MyClass this$0; // implicit parameter to CountFrom
      int start;      // explicit parameter to CountFrom
      int i;          // local variable of CountFrom
    
      public int Current {
       get { return current$0; }
      }
    
      public bool MoveNext()
      {
       switch (state$0) {
       case 0: goto resume$0;
       case 1: goto resume$1;
       case 2: return false;
       }
    
     resume$0:;
       for (i = start; i <= this$0.limit; i++) {
        current$0 = i;
        state$0 = 1;
        return true;
     resume$1:;
       }
    
       state$0 = 2;
       return false;
      }
      ... other bookkeeping, not important here ...
     }
    
     public IEnumerable<int> CountFrom(int start)
     {
      MyClass_Enumerator e = new MyClass_Enumerator();
      e.this$0 = this;
      e.start = start;
      return e;
     }
    

    The enumerator class is auto-generated by the compiler and, as promised, it contains two internal members for the state and current object, plus a member for each parameter (including the hidden this parameter), plus a member for each local variable. The Current property merely returns the current object. All the real work happens in MoveNext.

    To generate the MoveNext method, the compiler takes the code you write and performs a few transformations. First, all the references to variables and parameters need to be adjusted since the code moved to a helper class.

    • this becomes this$0, because inside the rewritten function, this refers to the auto-generated class, not the original class.
    • m becomes this$0.m when m is a member of the original class (a member variable, member property, or member function). This rule is actually redundant with the previous rule, because writing the name of a class member m without a prefix is just shorthand for this.m.
    • v becomes this.v when v is a parameter or local variable. This rule is actually redundant, since writing v is the same as this.v, but I call it out explicitly so you'll notice that the storage for the variable has changed.

    The compiler also has to deal with all those yield return statements.

    • Each yield return x becomes
       current$0 = x;
       state$0 = n;
       return true;
      resume$n:;
      

      where n is an increasing number starting at 1.

    And then there are the yield break statements.

    • Each yield break becomes
       state$0 = n2;
       return false;
      
      where n2 is one greater than the highest state number used by all the yield return statements. Don't forget that there is also an implied yield break at the end of the function.

    Finally, the compiler puts the big state dispatcher at the top of the function.

    • At the start of the function, insert
      switch (state$0) {
      case 0: goto resume$0;
      case 1: goto resume$1;
      case 2: goto resume$2;
      ...
      case n: goto resume$n;
      case n2: return false;
      }
      

      with one case statement for each state, plus the initial zero state and the final n2 state.

    Notice that this transformation is quite different from the enumeration model we built based on coroutines and fibers. The C# method is far more efficient in terms of memory usage since it doesn't consume an entire stack (typically a megabyte in size) like the fiber approach does. Instead it just borrows the stack of the caller, and anything that it needs to save across calls to MoveNext are stored in a helper object (which goes on the heap rather than the stack). This fake-out is normally quite effective—most people don't even realize that it's happening—but there are places where the difference is significant, and we'll see that shortly.

    Exercise: Why do we need to write state$0 = n2; and add the case n2: return false;? Why can't we just transform each yield break into return false; and stop there?

Page 1 of 4 (37 items) 1234