February, 2006

  • The Old New Thing

    Raymond, you so write like a girl

    • 21 Comments

    During the Windows 95 project, we had a super-sized whiteboard in the hallway outside the build lab in order to keep track of the most critical bugs that were blocking the release of the build. I remember one day I was walking past the board, and two of my colleagues were particularly interested in one of the bugs. Its current status had recently been updated to something like "Problem understood, fix coming, ETA 2pm." But they weren't as interested in the bug itself as in the identity of the person who made the update.

    Janice asked Rachel, "Do you know who wrote that?"

    "No, but it's clearly a woman's handwriting."

    "Obviously, but who could it have been? I would have guessed Laura, but I know her writing and that's not it."

    "Ahem," I interjected. "I wrote that."

    An awkward silence.

    "Oh, it's very nice handwriting, really."

    "Yes, very graceful."

    Looking back at my penmanship through the years, I think that era was my peak. It has been declining steadily ever since. Sometimes I stop to try to recover some of its former glory, but at best it's just holding its ground.

    (While you're checking out TechNet Magazine, why not drop into the current issue's Blog Tales written by our own Betsy Aoki.)

  • The Old New Thing

    Ry Jones seems particularly amused by things I've written

    • 16 Comments

    I can't believe he saves this stuff.

  • The Old New Thing

    Changing a window class affects all windows which belong to that class

    • 12 Comments

    Sometimes you need to state the obvious, and that's fine. You can learn a lot from the obvious. For example, the first topic in my PDC talk consisted of simply stating the obvious.

    Occasionally, when you state the obvious, you have to follow up by stating the obvious.

    When I pointed out that the effect of SetCursor lasts only until the next SetCursor, one commenter proposed as an alternative solution to the problem of how to prevent the cursor from reverting to the arrow,

    What about calling SetClassLongPtr() with the GCLP_HCURSOR param? Is this an acceptable alternative, or does this have issues of its own?

    Being busy is a state of a particular window instance, not a state of the window class. Therefore, the setting of the cursor must be done by the window instance, not by the window class. If you change a window class, then it affects all windows which belong to that class. In particular, changing the class cursor changes the cursor for all windows that belong to the class.

    In other words, consider the case where you have two windows on the screen that belong to the window class. One of the windows becomes busy, so you change the class cursor to an hourglass. When the user moves the mouse over the window that is busy processing, the cursor turns to an hourglass, as you would expect. But when the user moves the mouse over the window that is not doing any processing, the cursor will turn to an hourglass there also, even though that window is perfectly fine.

    It is important to understand the scope of what one is changing. Don't make a global change to solve a local problem.

  • The Old New Thing

    The Top Gear Winter Olympics 2006

    • 28 Comments

    I know nothing about cars. Driving fast scares me. But I still enjoy the British car show Top Gear. The hosts clearly enjoy driving, and they don't hold back in their commentary. ("This was the first seven-seat 4×4 designed by someone who had children, not by an engineer who read about them in a book.") Last week was their Winter Olympics Episode, and it did not disappoint. They drove a biathlon (with Jeremy of course cheating unabashedly). They raced a Jaguar against a speed skater. Plenty of other events. But the highlight was sending a Mini down the ski jump. Watch it. (Warning: One-hour video.)

  • The Old New Thing

    Why can't I disable the Cancel button in a wizard?

    • 40 Comments

    The PropSheet_SetWizButtons macro lets you manipulate many of the buttons on a wizard, but the Cancel button remains elusive. Why can't you disable the Cancel button or the "X" button?

    Because our users tell us they don't like it.

    Observation of users in our labs and interviews with them reveal that wizards that disable the Cancel button cause them stress and frustration. Put yourself in their shoes: You've started some operation, the wizard asks you a few questions, you answer them, and then, uh-oh, it asks a question you can't answer, or you realize that you don't want to do it after all because the wizard told you that completing the task will have some undesired side-effects or because one of the steps is taking too long. You want to cancel out of the wizard. But the wizard won't let you. "Ha-ha. Now I'm going to force you to do what I tell you! You're trapped, I say, trapped! Mwaaa-ha-ha-ha-ha!"

    Don't do that to your users. It only upsets them. Let them cancel and live another day.

  • The Old New Thing

    That $9 you got from the PayPal settlement? Taxable income.

    • 12 Comments

    If you're like many people, you signed up for the PayPal class action lawsuit and got around $9 for filing a claim against the Statutory Damage Fund. My read of IRS publication 4345 says that this constitutes taxable income. Note: This is just my personal opinion. Consult with your tax advisor before taking action. As for me, I reported it. Given my previous experience with the IRS, I'm willing to fork over the $3 to avoid getting audited.

  • The Old New Thing

    Enumerating threads in a process

    • 11 Comments

    The tool helper library is sort of the black sheep of Win32. It grew out of the 16-bit TOOLHELP library, which provided services for system debugging tools to do things like take stack traces and enumerate all the memory in the system. The original incarnation of Win32 didn't incorporate it; it wasn't until Windows 95 that a 32-bit version of the tool helper library sort of got bolted onto the side of Win32.

    Disowned or not, the functions are still there, so let's give them a spin.

    #include <stdio.h>
    #include <windows.h>
    #include <tlhelp32.h>
    
    int __cdecl main(int argc, char **argv)
    {
     HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
     if (h != INVALID_HANDLE_VALUE) {
      THREADENTRY32 te;
      te.dwSize = sizeof(te);
      if (Thread32First(h, &te)) {
       do {
         if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
                          sizeof(te.th32OwnerProcessID)) {
           printf("Process 0x%04x Thread 0x%04x\n",
                 te.th32OwnerProcessID, te.th32ThreadID);
         }
       te.dwSize = sizeof(te);
       } while (Thread32Next(h, &te));
      }
      CloseHandle(h);
     }
     return 0;
    }
    

    Running this program prints a list of all the threads in the system (or at least all the ones you have access to). This is particularly straightforward, the only subtlety being the strange check that the size returned by the Thread32First function is large enough to emcompass the th32OwnerProcessID field that we need. This complexity is necessary due to the somewhat unorthodox way that the Thread32First and Thread32Next functions check structure sizes.

    That's what happens when you're the black sheep of the Win32 API.

  • The Old New Thing

    Games give you hand-eye coordination and spatial intelligence together with map-reading skills

    • 16 Comments

    Australian comedy group Tripod performs a song that I'm sure describes none of my readers in any way whatsoever. (Courtesy of my good friend The Knitty Professor.)

  • The Old New Thing

    The performance cost of reading a registry key

    • 48 Comments

    The registry is a convenient place to record persistent cross-process data in a uniform and multi-thread-safe manner. It roams with the user if you store it in HKEY_CURRENT_USER, and individual keys can be secured (even on systems that use FAT, which doesn't otherwise support security).

    But that doesn't mean that it's free.

    The cost of opening a key, reading a value, and closing it is around 60,000 to 100,000 cycles (I'm told). And that's assuming the key you're looking for is in the cache. If you open the key and hold it open, then the act of reading a value costs around 15,000 to 20,000 cycles. (These numbers are estimates for Windows XP; actual mileage may vary.)

    Consequently, you shouldn't be reading a registry key in your inner loop. Not only does it cost you CPU time at query time, but the constant hammering of the registry means that the data structures used by the registry to locate and store your key (including the entry in the registry cache) are kept in the system working set. Don't read a registry key on every mouse move; read the value once and cache the result. If you need to worry about somebody changing the value while your program is running, you can establish a protocol for people to follow when they want to change a setting. Windows, for example, uses functions such as SystemParametersInfo to manipulate settings that are normally cached rather than read from the registry each time they are needed. Calling the update function both updates the registry and the in-memory cache. If you can't establish a mechanism for coordinating changes to the setting, you can set a change notification via the RegNotifyChangeKeyValue function so that you are notified when the value changes.

    Whenever possible, optimize for the common case, not the rare case. The common case is that the registry value hasn't changed. By using a notification mechanism, you move the cost of "But what if the value changed?" out of your inner loop and into code that doesn't execute most of the time. (Remember, the fastest code is code that never runs.)

    Of course, you don't want to burn a thread waiting on the notification event. I use the thread pool. The RegisterWaitForSingleObject function lets you tell the thread pool, "Hey, could you call me when this object is signalled? Thanks." The thread pool then does the work of combining this with all the other handles it has been asked to wait for into a giant WaitForMultipleObjects call. That way, one thread can handle multiple waits.

    One caveat to heed with the RegNotifyChangeKeyValue function is that the notification has thread affinity! If the thread that calls the RegNotifyChangeKeyValue function exits, the notification is raised. This means that you shouldn't call the function from a thread pool thread, since the system will destroy threads in the thread pool when the work list goes idle and their presence is no longer needed. If you mess up and call it from a thread pool thread, you'll find that the event keeps firing spuriously as the thread pool cleanup code runs, making the cure as bad as the disease! Instead, you should create the wait from a persistent thread (say, the thread that actually cares about the value!) and register the wait there. When the event fires on the thread pool, handle the change, then ask your persistent thread to start a new cycle of RegNotifyChangeKeyValue. That way, the event is always associated with your persistent thread instead of with a transient thread pool thread.

  • The Old New Thing

    In pursuit of the message queue

    • 16 Comments

    In 16-bit Windows, every thread (or "task" as it was called then) had a message queue, end of story. In the transition to 32-bit Windows, this model broke down because Win32 introduced the concepts of "worker threads" and "console applications", neither of which had much need for messaging. Creating a queue for every thread in the system would have been quite a waste, so the window manager deferred creating the input queue for a thread until that thread actually needed an input queue. That way, threads that didn't use the GUI didn't have to pay for something they weren't using. But once you send a message or peek for a message or create a window or do anything else that requires a message queue, *poof* a message queue would be created just in time to accomodate your request. As far as you knew, the queue was always there.

    The create-on-demand queue model worked out great: Queues were created only when they were needed, threads that didn't need message queues didn't get one, and nobody knew the difference. There was only one catch: PostThreadMessage. When I started writing this entry, I was going to write that the behavior of the PostThreadMessage function is a mistake. Instead of failing if the thread doesn't have a queue, it should have preserved the conceit that the queue was there all along by creating the queue on demand. But thinking about it more (as the writing process forces you to do), I've now convinced myself that the current design is correct, even though it violates the "as far as you can tell, the queue is always there" principle.

    The PostThreadMessage function is peculiar among all the other queue-related functions in that it operates on the queue of another thread that may not already have a queue. All the other queue functions operate on the queue of the thread making the call or operate on the queue of a thread that is known to have a queue (because it created a window, for example). As a result, a thread is in control of whether it gets a message queue or not. If PostThreadMessage created a queue on demand, this would allow one process to start creating queues in other processes without those other processes knowing about it. It could then start filling that message queue with thousands upon thousands of posted messages, and the victim thread would have no idea not only that it had a message queue, but also that the message queue that somebody else created was full of unprocessed messages! No thread would be able to defend itself from this sort of attack.

    Making the PostThreadMessage an exception to the "as if there always were a thread queue" rule keeps a thread in control of its own queue destiny.

Page 1 of 4 (34 items) 1234