November, 2007

  • The Old New Thing

    Hotkeys involving the Windows logo key are reserved by the system

    • 77 Comments

    Hotkeys involving the Windows logo key are reserved by the system. New ones are added over time. For example, Windows 98 added Win+D to show the desktop. Windows 2000 added Win+U to call up the Utility Manager. Windows XP added Win+B to move keyboard focus to the taskbar notification area. And a whole bunch of new hotkeys were added in Windows Vista, such as Ctrl+Win which is used by speech recognition. (It's also a bit of a pun: "Control Windows", get it?)

    The fact that these hotkeys are reserved is hard to find. It's buried in a bullet point a third of the way down the Guidelines for Keyboard User Interface Design document. It's also highlighted in the WHQL keyboard requirements right in the section title: "Windows Logo Key Support (Reserved for Operating System Use)". But even if you didn't find the documentation (and I don't blame you), the history of Windows clearly indicates that new Windows logo hotkeys arrive on a pretty regular basis. If your program uses one of those hotkeys, there's a good chance it'll conflict with a future version of Windows.

    Why isn't this mentioned in the RegisterHotKey documentation? Perhaps it should. But historically, function documentation merely told what a function does, not how it should be used. How a function should be used is a value judgement, and it was traditionally not the role of function documentation to make value judgements or specify policy. If you go to a programming language's specification document, you'll find a discussion of what the language keywords do, but no guidance as to how you should use them. Like a language specification, function documentation historically limited itself to how something is done, not whether it ought to be done. Recommendations traditionally belong in overviews, white papers, and other guidance documents. After all, the CreateFile documentation doesn't say "Do not create application data in the My Documents folder." The CreateFile function doesn't care; its job is to create files. Whether a file is created in the correct location is a guideline or policy decision at a higher level.

    Note: I don't know whether that is still the position of the function documentation team, that it limit itself to the facts and avoid value judgements and guidance. My goal today is to draw attention to the guidance.

  • The Old New Thing

    The first day at Microsoft: Don't just stand there

    • 30 Comments

    A second installment in a very sporadic series titled The first day at Microsoft, relating stories of funny things that happened on people's first days at Microsoft. Today, we hear from Employee Y:

    One of the things you have to do on your first day is get your photo taken for your identification badge, and I waited in line with the droves of other new college interns starting that same day. As a result, the line for photos extended out of the room into the parking garage. (ID badges are distributed from a room in the basement of one of the parking garages.)

         |-----
         | CAR
    -----+-----
      * * * * * 
         +-----* 
    ID   |      *
    room |----- *
         | CAR  *
    -----|----- *
         | CAR  *
         |----- *
         | CAR  *
         |----- *
    

    As we waited in line, a car pulled up and wanted to park in that empty space near the door that people were blocking. The entire line of college interns just stood and stared in stunned amazement when they realized that the person driving the car was Bill Gates.

    This stalemate lasted a good ten seconds before I spoke up and said, "Hey, everybody. I think Bill is trying to park in that empty space."

    That was enough to snap people out of their frozen state. A hole was made, Bill parked his car, crisis resolved.

    I like to amuse myself by thinking that each of those interns that blocked Bill from getting to the parking space cost the company more money in wasted time than the intern will make all summer.

  • The Old New Thing

    The importance of the FORMAT_MESSAGE_IGNORE_INSERTS flag

    • 16 Comments

    You can use the FormatMessage message with the FORMAT_MESSAGE_FROM_SYSTEM flag to indicate that the message number you passed is an error code and that the message should be looked up in the system message table. This is a specific case of the more general case where you are not in control of the message, and when you are not in control of the message, you had better pass the FORMAT_MESSAGE_IGNORE_INSERTS flag.

    Let's look at what happens when you don't.

    #include <windows.h>
    #include <stdio.h>
    #include <tchar.h>
    
    int __cdecl main(int argc, char **argv)
    {
     TCHAR buffer[1024];
     DWORD dwError = ERROR_BAD_EXE_FORMAT;
     DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
     DWORD dwResult = FormatMessage(dwFlags, NULL, dwError,
                                    0, buffer, 1024, NULL);
     if (dwResult) {
      _tprintf(_T("Message is \"%s\"\n"), buffer);
     } else {
      _tprintf(_T("Failed! Error code %d\n"), GetLastError());
     }
     return 0;
    }
    

    If you run this program, you'll get

    Failed! Error code 87
    

    Error 87 is ERROR_INVALID_PARAMETER. What went wrong? Let's pass the FORMAT_MESSAGE_IGNORE_INSERTS flag to see what the message was. Change the value of dwFlags to

     DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM |
                     FORMAT_MESSAGE_IGNORE_INSERTS;
    

    and run the program again. This time you get

    Message is "%1 is not a valid Win32 application.
    "
    

    Aha, now we see the problem. The message corresponding to ERROR_BAD_EXE_FORMAT contains an insertion %1. If you don't pass the FORMAT_MESSAGE_IGNORE_INSERTS flag, the FormatMessage function will insert the first parameter in the argument list (or argument array). But we didn't pass an argument list, so the function fails.

    Actually, we got lucky. If we had passed an argument list or argument array, the function would have inserted the corresponding string, even if the argument list we passed didn't have a string in the first position.

    If you are not in control of the format string, then you must pass FORMAT_MESSAGE_IGNORE_INSERTS to prevent the %1 from causing trouble. If somebody was being particularly evil, they might decide to give you a format string that contains a %9, which is almost certainly more insertions than you provided. The result is a buffer overflow and probably a crash.

    This may have been obvious to some people, in the same way that you shouldn't pass a string outside your control as the format string to the printf function, but I felt it worth mentioning.

  • The Old New Thing

    What to do when the steering column is stuck and the ignition won't turn

    • 47 Comments

    One evening, I parked pointing downhill and like the book says, I turned my wheels to the right before parking. But I turned it a bit too far, because when I returned to the car and inserted the key into the ignition, the key wouldn't turn.

    The wrong thing to do is to force the key until it breaks.

    Here's the right thing to do:

    Take the steering wheel and turn it left and right. One direction will have a little more play than the other. Grab the wheel with your left hand and apply pressure turning it in the direction with more play while using your right hand to turn the key in the ignition. (Or, if you're like me and can't figure it out, just wobble the wheel back and forth until the key unsticks.)

    Note: I scheduled this item independently of its partner. By an amazing coincidence, both items warn against forcing something until it breaks.

  • The Old New Thing

    When you compress a drive, some files are exempted, but you can force it, and then it's your problem

    • 29 Comments

    On the drive property sheet for an NTFS volume, there is a checkbox called "Compress drive to save disk space." If you check that box, the shell marks the drive as "compress all newly-created files" and also goes through and compresses all the existing files on the drive.

    Well, almost all the file.

    Some files are exempted by default.

    Examples of exempted files are the files involved in booting the system (NTLDR, NTDETECT.COM, HIBERFIL.SYS) and files for which write requests must succeed (PAGEFILE.SYS). (If a file is compressed, then a write to previously-committed file data may fail if the new data does not compress as well as the old data and there is no more disk space.) These files are exempted on all drives, even if they're not your system drive.

    On the other hand, if you right-click one of these exempted files and explicitly compress it, then the shell will compress it (or at least try to). For boot files, this will typically succeed since boot files are used only at boot; once the system is running, they aren't needed any more and therefore there aren't any open handles to the file with restrictive sharing modes.

    Of course, if you do this to your system drive, it won't boot any more. So don't do that.

    Like with many things in the physical world, if you force it too hard, it may break.

  • The Old New Thing

    Why are INI files deprecated in favor of the registry?

    • 139 Comments

    Welcome, Slashdot readers. Remember, this Web site is for entertainment purposes only.

    Why are INI files deprecated in favor of the registry? There were many problems with INI files.

    • INI files don't support Unicode. Even though there are Unicode functions of the private profile functions, they end up just writing ANSI text to the INI file. (There is a wacked out way you can create a Unicode INI file, but you have to step outside the API in order to do it.) This wasn't an issue in 16-bit Windows since 16-bit Windows didn't support Unicode either!
    • INI file security is not granular enough. Since it's just a file, any permissions you set are at the file level, not the key level. You can't say, "Anybody can modify this section, but that section can be modified only by administrators." This wasn't an issue in 16-bit Windows since 16-bit Windows didn't do security.
    • Multiple writers to an INI file can result in data loss. Consider two threads that are trying to update an INI file. If they are running simultaneously, you can get this:
      Thread 1Thread 2
      Read INI file
      Read INI file
      Write INI file + X
      Write INI file + Y
      Notice that thread 2's update to the INI file accidentally deleted the change made by thread 1. This wasn't a problem in 16-bit Windows since 16-bit Windows was co-operatively multi-tasked. As long as you didn't yield the CPU between the read and the write, you were safe because nobody else could run until you yielded.
    • INI files can suffer a denial of service. A program can open an INI file in exclusive mode and lock out everybody else. This is bad if the INI file was being used to hold security information, since it prevents anybody from seeing what those security settings are. This was also a problem in 16-bit Windows, but since there was no security in 16-bit Windows, a program that wanted to launch a denial of service attack on an INI file could just delete it!
    • INI files contain only strings. If you wanted to store binary data, you had to encode it somehow as a string.
    • Parsing an INI file is comparatively slow. Each time you read or write a value in an INI file, the file has to be loaded into memory and parsed. If you write three strings to an INI file, that INI file got loaded and parsed three times and got written out to disk three times. In 16-bit Windows, three consecutive INI file operations would result in only one parse and one write, because the operating system was co-operatively multi-tasked. When you accessed an INI file, it was parsed into memory and cached. The cache was flushed when you finally yielded CPU to another process.
    • Many programs open INI files and read them directly. This means that the INI file format is locked and cannot be extended. Even if you wanted to add security to INI files, you can't. What's more, many programs that parsed INI files were buggy, so in practice you couldn't store a string longer than about 70 characters in an INI file or you'd cause some other program to crash.
    • INI files are limited to 32KB in size.
    • The default location for INI files was the Windows directory! This definitely was bad for Windows NT since only administrators have write permission there.
    • INI files contain only two levels of structure. An INI file consists of sections, and each section consists of strings. You can't put sections inside other sections.
    • [Added 9am] Central administration of INI files is difficult. Since they can be anywhere in the system, a network administrator can't write a script that asks, "Is everybody using the latest version of Firefox?" They also can't deploy scripts that say "Set everybody's Firefox settings to XYZ and deny write access so they can't change them."

    The registry tried to address these concerns. You might argue whether these were valid concerns to begin with, but the Windows NT folks sure thought they were.

    Commenter TC notes that the pendulum has swung back to text configuration files, but this time, they're XML. This reopens many of the problems that INI files had, but you have the major advantage that nobody writes to XML configuration files; they only read from them. XML configuration files are not used to store user settings; they just contain information about the program itself. Let's look at those issues again.

    • XML files support Unicode.
    • XML file security is not granular enough. But since the XML configuration file is read-only, the primary objection is sidestepped. (But if you want only administrators to have permission to read specific parts of the XML, then you're in trouble.)
    • Since XML configuration files are read-only, you don't have to worry about multiple writers.
    • XML configuration files files can suffer a denial of service. You can still open them exclusively and lock out other processes.
    • XML files contain only strings. If you want to store binary data, you have to encode it somehow.
    • Parsing an XML file is comparatively slow. But since they're read-only, you can safely cache the parsed result, so you only need to parse once.
    • Programs parse XML files manually, but the XML format is already locked, so you couldn't extend it anyway even if you wanted to. Hopefully, those programs use a standard-conforming XML parser instead of rolling their own, but I wouldn't be surprised if people wrote their own custom XML parser that chokes on, say, processing instructions or strings longer than 70 characters.
    • XML files do not have a size limit.
    • XML files do not have a default location.
    • XML files have complex structure. Elements can contain other elements.

    XML manages to sidestep many of the problems that INI files have, but only if you promise only to read from them (and only if everybody agrees to use a standard-conforming parser), and if you don't require security granularity beyond the file level. Once you write to them, then a lot of the INI file problems return.

  • The Old New Thing

    Sounds about right: What a geek does with an iPhone in the first two days

    • 21 Comments

    Welcome to Black Friday, the traditional start of the Christmas shopping season. I suspect there will be a bunch of people who are hoping Santa will get them an iPhone. Here's one story of what lies in store.

    One of my friends stood in line on the first day of iPhone availability and got one of the highly-anticipated gadgets. We happened to get together two days later, and of course there was a lot of showing off and playing with the phone's features. But for me what was telling was the part of the phone which shows you the phone usage statistics. After two days, the numbers looked something like this:

    • Data downloaded from the Internet: 500MB
    • Minutes talking on the phone: 2

    Sounds about right.

  • The Old New Thing

    The forgotten common controls: The GetEffectiveClientRect function

    • 10 Comments

    The GetEffectiveClientRect function is another one in the category of functions that everybody tries to pretend doesn't exist. It's not as bad as MenuHelp, but it's still pretty awful.

    The idea behind the GetEffectiveClientRect function is that you have a frame window with a bunch of optional gadgets, such as a status bar or toolbar. The important thing is that these optional gadgets all reside at the borders of the window. In our examples, the toolbar goes at the top and the status bar goes at the bottom. You might also have gadgets on the left and right such as a navigation tree or a preview pane. They can also be stacked up against the border, such as an address bar and a toolbar. The important thing is that all the gadgets go around the border.

    The first parameter to the GetEffectiveClientRect function is the window whose effective client rectangle you wish to compute; no surprises there. The second parameter is a pointer to the rectangle that receives the result; again, hardly surprising. It's that third parameter, the array of integers, that is the weird one.

    The first two integers in the array are ignored. The remainder of the array consists of pairs of nonzero integers; the array is terminated by a pair consisting of zeroes. Of each pair, only the second integer is used; it is the control identifier of a child window of the window you passed in. If that child window is visible (in a special sense I'll explain later), then its window rectangle is subtracted from the parent window's client rectangle. After all the rectangles of visible children are subtracted away, what remains is the effective client rectangle.

    For example, suppose your window's client rectangle is 100×100 and there is a toolbar at (0, 0)–(100, 20) and a status bar at (0, 90)–(100, 100), both visible. The GetEffectiveClientRect starts with the full client rectangle (0, 0)–(100, 100), subtracts the two rectangles corresponding to the toolbar and status bar, resulting in (0, 20)–(100, 90).

    (0, 0) (100, 0)
    toolbar
    (0, 20) (100, 20)
    effective client
    (0, 90) (100, 90)
    status bar
    (0, 100) (100, 100)

    If the control IDs for the toolbar and status bar are 100 and 101, respectively, then the array you need to pass would be { *, *, ¤, 100, ¤, 101, 0, 0 } where * can be anything and ¤ can be any nonzero value.

    Continuing from the above example, if the status bar were hidden, then the effective client rectangle would be (0, 20)–(100, 100) because hidden windows are ignored when computing the effective client rectangle.

    Okay, first question: What is that special sense of visible I mentioned above? I didn't write simply visible because IsWindowVisible reports a window as visible only if the window and all its parents are visible. But all that GetEffectiveClientRect cares about is whether the window is visible in the sense that the WS_VISIBLE style is set. In other words, that the window would be visible if its parent is.

    Why does the GetEffectiveClientRect use this strange definition of visible? Because it wants to make it possible for you to get the effective client rectangle of a window while it is still hidden, the result being the effective client rectangle you would get once the window becomes visible. This is valuable because it allows you to do your calculations "behind the scenes" while the window is still hidden (for example, in your WM_CREATE handler).

    Second question: Why is the integer array so crazy? What's with all the ignored values and the "must be nonzero" values? Why can't it just be the array { 100, 101, 0 }?

    The format of the integer array is the same as the one used by the ShowHideMenuCtl function. The intent was that you could use the same array for both functions. The two functions do work well together: The ShowHideMenuCtl function do the work of letting the user toggle the toolbar and status bar on and off, and GetEffectiveClientRect lets you compute the client rectangle that results.

    That said, the GetEffectiveClientRect function is largely ignored nowadays. It doesn't do anything you couldn't already do yourself, and when you write your own version, you don't need to deal with that crazy integer array.

  • The Old New Thing

    When in doubt, consult the online Magic 8 Ball

    • 13 Comments

    On our team's web site, buried among the other debugging documents, was a page titled simply "Magic 8 Ball"®¹. If you visited it, you got a dark blue circle with a lighter-blue triangle, on which appeared white text with a randomly-chosen message. The messages were things like

    • Memory corruption.
    • Try a newer build.
    • Known bug.
    • Can't connect.
    • Hardware failure.
    • Looks like a X bug. (Where "X" was a random component.)

    It was fun to give the 8-ball a shake, but the real purpose of the 8-ball was to be a link sent out in response to failure reports.

    You see, there was a secret URL for each of the 8-ball's responses, so you could respond to a failure report with something like

    The Magic 8 ball says... http://internalserver/magic8.asp?TWVGS

    and when the person clicked on the link, they got an 8-ball that said "Known bug" or "Memory corruption".

    Some teams liked the 8-Ball's responses so much, they asked us to add new custom messages to the repertoire.

    Anyway, I was reminded of this by the story of Radio8Ball.

    ¹Magic 8-Ball is a registered trademark of Tyco Toys, Inc. When the Tyco scandal hit the airwaves, I always did a double-take before realizing that it was a different company.

    • Tyco International = scandal-plagued multinational conglomerate, cursed by shareholders.
    • Tyco Toys = a division of Mattel, maker of the Magic 8 Ball, beloved by soothsayers.
  • The Old New Thing

    Alternate theories on how Putin can retain power after his second term expires

    • 31 Comments

    Everybody has their own pet theory on how Vladimir Putin can retain power after the end of his second official term. I earlier wrote of one such theory: Change the constitution to permit a third term. In the intervening months, other theories have been put forth. Each one chips away at a different part of the statement that "the president can be elected to at most two consecutive terms":

    • Attack the word the president: Transfer all the powers of the presidency to the prime minister, then have Putin be the next prime minister. There is no limit on how many terms someone can serve as prime minister.
    • Attack the word elected: Since the prime minister is first in line should the president step down, have Putin become the next prime minister and install a crony as president. When the president resigns on his first day, Putin becomes president without being elected.
    • Attack the word two by changing the constitution, as noted already.
    • Attack the word consecutive: Similar to the attack on the word elected, and then point out that since there was an intervening president, Putin's next two terms will not be consecutive with the first two.

    There's also another method that merely plays by a different set of rules: Install a crony as president and pull the strings from backstage.

    As I noted in my earlier entry, I'm much more fascinated by the machinery being put into motion to arrange for Putin to retain power than in whether he actually does.

    On an unrelated note, Mark MacKinnon explains the magic phrase to use as a foreigner when pulled over arbitrarily by the police.

Page 1 of 3 (29 items) 123