October, 2007

  • The Old New Thing

    The best way to process Unicode input is to make somebody else do it

    • 23 Comments

    Andrew M asks via the Suggestion Box:

    I was hoping you could address how to properly code Unicode character input. It seems like a lot of applications don't support it correctly.

    I'm not sure I understand the question, but the answer is pretty easy: Don't do it!

    Text input is hard. It should be left to the professionals. This means you should use controls such as the standard edit control and the rich edit control. Properly converting keystrokes to characters involves not just the shift state, but the management of various input method editors, some of which are quite complicated. For example, the IME Pad lets the user draw a Chinese character with the mouse (or if you're lucky, the stylus), and then it will take the result and try to figure out which character you were trying to write and generate the appropriate Unicode character.

    Other IMEs will generate provisional conversions of phonetic text into Unicode characters, and as more input is received, they can go back and revise their previous guesses based on subsequent input. You definitely don't want to get involved in this. Just leave it to the professionals.

    Postscript: For those who have never used a phonetic IME, here's how a hypothetical English phonetic IME might work. Let's pretend there's an English phonetic keyboard with keys labeled with various phonemes. (Instead of IPA characters, I will use traditional American phonetics.)

    You type Result
    әUh
    tUt
    ĕA te
    nA 10
    shA 10 sh
    әA 10 sha
    nA tension
    oA tension o
    lAttention all

    Notice how the IME keeps updating its guess as to what you're trying to type as better information becomes available. The text is underlined since it is all provisional. During the input, you can hit the left-arrow to go back to any part of the provisional text, hit the down-arrow, and see a list of alternatives, at which point you can override the guess with the correct answer. For example, if you really wanted to write "A tension all", you would arrow back to the word "Attention", hit the down-arrow, and select "A tension" from the menu. Eventually, you reach the end of a phrase or sentence, look over the provisional text, and after making any necessary corrections, you hit Enter, at which point the text is committed into the edit control and a new string of provisional text begins.

  • The Old New Thing

    Other problems traced to violating COM single-threaded apartment rules in the shell

    • 45 Comments

    Probably the biggest category of problems that can be traced to violating COM single-threaded apartment rules in the shell is using an object from the wrong thread. Of course, nobody admits to doing this up front, They just report that the shell is broken.

    We can't enumerate the items on the desktop any more. We take the pointer returned by SHGetDesktopFolder and call IShellFolder::EnumObjects, but no objects come out. This code used to work on Windows XP.

    There isn't enough information to diagnose the problem, and if you just do what they claim doesn't work, you find that it works:

    #include <windows.h>
    #include <ole2.h>
    #include <shlobj.h>
    #include <shlwapi.h>
    #include <stdio.h>
    #include <tchar.h>
    
    INT __cdecl
    _tmain(
        INT iArgc,
        __in_ecount(iArgc) PTSTR ppszArgv[]
        )
    {
     if (SUCCEEDED(CoInitialize(NULL))) {
      IShellFolder *psf;
      if (SUCCEEDED(SHGetDesktopFolder(&psf))) {
       IEnumIDList *peidl;
       if (SUCCEEDED(psf->EnumObjects(NULL, SHCONTF_FOLDERS |
                             SHCONTF_NONFOLDERS, &peidl)) && peidl) {
        LPITEMIDLIST pidl;
        while (peidl->Next(1, &pidl, NULL) == S_OK) {
         STRRET str;
         if (SUCCEEDED(psf->GetDisplayNameOf(pidl,
                                             SHGDN_NORMAL, &str))) {
          TCHAR sz[MAX_PATH];
          if (SUCCEEDED(StrRetToBuf(&str, pidl, sz, MAX_PATH))) {
           _tprintf(TEXT("%s\n"), sz);
          }
         }
         ILFree(pidl);
        }
       }
       psf->Release();
      }
      CoUninitialize();
     }
     return 0;
    }
    

    When given this simple program that does what they claim doesn't work, the customer explained that they cache the desktop folder. It works for a while, and then stops working. The code is complicated, so they haven't been able to isolate the problem yet. They did find that if they didn't cache the pointer and just called SHGetDesktopFolder each time they needed it, then they didn't have the problem.

    I never got a confirmation, but I'm pretty sure that they are violating COM apartment threading model rules and obtaining the desktop folder obtained on one thread, then using it on another. Apartment model rules specify that you must use an object on the same thread that created it. If you want to use it on another thread, you have to use a helper function like CoMarshalInterThreadInterfaceInStream. If you just dive in and use it on another thread (known informally as "smuggling"), then all sorts of strange things happen. In this case, the folder can't enumerate objects any more.

    Moral of the story: Stick to the rules for COM objects. If you don't, you may get away with it for a little while, but someday your sins may catch up to you.

  • The Old New Thing

    Win32 user interface work is inherently single-threaded

    • 53 Comments

    At the end of the day, there's a window handle in there.

    As we saw a few years ago, windows have thread affinity. You can push work to a background thread all you want, but once you send a message to the window (or do something that results in a message being sent to the window), you've given control to the UI thread.

    Since the shell is all about user interfaces, the shell naturally expects that all threads that use it are single-threaded apartments. Historically, however, you sort of got away with violating this rule because older versions of the shell used their own custom fake version of COM rather than using the official one in OLE32.DLL. As a result, programs that broke the rule on apartment threading often managed to get away with it because the shell assumed everybody was doing the right thing. (The Internet Explorer team faced a similar problem with browser plug-ins.) If you tried to use an object on the wrong thread, nobody would step in and try to stop you. Mind you, you might corrupt memory or crash, but that was your own fault. Nobody said this was going to be easy.

    As the shell gradually switched to using real COM instead of fake COM, and as new features were added to the shell which relied more and more heavily on callers following the rules for apartment threading, programs that had skirted the rules started running into problems. If you got away with it on earlier versions of Windows and the problem was severe, there was a good chance the shell would have to do some re-architecting to allow your dodgy code to keep working.

    A member of the COM team explained that COM assumes fundamentally that multi-threaded apartments are UI-free. No UI means no need to pump messages. If you initialize COM in MTA mode on a thread, you'd better not do any UI or your thread will stop responding to messages whenever COM needs to talk to another thread.

  • The Old New Thing

    The wrong way to check whether the mouse buttons have been swapped

    • 39 Comments

    Back in the late 1990's, the window manager team received a bug that said that sometimes the mouse button state got messed up and the computer acted as if the buttons were stuck down. Further investigation revealed that it occurred only when one particular program was running, and only if the user had enabled mouse button swapping.

    The reason is that the program in question detected whether the mouse buttons were swapped with a function like this:

    // do not use this function
    BOOL AreMouseButtonsSwapped()
    {
     BOOL fWasSwapped = SwapMouseButton(FALSE);
     if (fWasSwapped) SwapMouseButton(TRUE);
     return fWasSwapped;
    }
    

    The SwapMouseButton function changes the button swap state and returns the old state. The way the program checked whether the buttons were swapped was by unswapping the buttons and using the return value to determine what the previous setting was, then re-swapping the buttons if the previous setting was "Yes, they were swapped."

    If you started with the buttons swapped, running this function created a tiny window where the buttons were momentarily unswapped. And if you were unlucky enough to click the mouse during this window of vulnerability, the program saw one mouse button go down and a different button come up! Even though it was the same physical button each time, it was a different logical button, since the meanings of the buttons had changed.

    The correct way of detecting whether mouse buttons are swapped is just to ask non-intrusively.

    BOOL AreMouseButtonsSwapped()
    {
     return GetSystemMetrics(SM_SWAPBUTTON);
    }
    
  • The Old New Thing

    How do I delay the automatic logon process?

    • 62 Comments

    To solve some problems you need to place one foot outside the box.

    We have a number of kiosk machines that are networked wirelessly. Each machine is configured with automatic logon so that things return to normal after power is restored after an outage. The problem is that the wireless switch takes a long time to recover from a power failure, so when the kiosk machines try to log on, they can't. We have to go around to all the machines and manually log them on after waiting a few minutes for the switch to get itself back up. Is there a way we can delay the automatic logon or convince automatic logon to pause and retry?

    Your first reaction may be to write a custom logon provider or otherwise control the GINA. But there's a much lower-tech solution.

    Go to your boot.ini file (or if you're using Windows Vista, use bcdedit) to increase the boot menu timeout. The timeout value in boot.ini can go as high as 11 million seconds (about four months). If your wireless switch takes more than four months to get itself into a ready state, then you've got worse problems.

  • The Old New Thing

    Nicolas Bourbaki, perhaps the biggest inside joke in mathematics

    • 7 Comments

    If you've studied advanced mathematics, you've certainly read or at least heard about the books and papers of the great French mathematician Nicolas Bourbaki, whose works in the 1930's set out a rigorous formulation of modern mathematics. Except he never actually existed. The name was created as a front for a group of renegade French mathematicians who were reacting to Poincaré's intuitive approach.

    In modern graph theory, I'm told that it is fashionable to publish one's results under false names. For example, the graph theorist G. W. Peck was actually a collaboration of the mathematicians Ronald Graham, Douglas West, George Purdy, the legendary Paul Erdős, Fan Chung, and Daniel Kleitman. (Take the initials of the members' last names.) I learned from Dr. West that the group did not include him originally, and they were prepared to publish under the name "G. Peck", listing the author's address as Hollywood, California. With the addition of a new member, the name changed to "G. W. Peck", which to the group's pleasant surprise happened to be the name of a United States Congressman from the 19th century, which gave them the opportunity to include a photograph and a short biography that ended with something like "After retiring from Congress, Peck returned to his first love: graph theory."

  • The Old New Thing

    Why aren't shortcuts as easy as unix links?

    • 66 Comments

    Commenter dingo asks, "Why are shortcuts so hard to manipulate? Why can't they be as easy as unix links?"

    Well, if you want something like unix links, then you can just create a hard link. Creating them is about the same difficulty (CreateHardLink vs link) and manipulating them is the same since you don't actually manipulate a hard link. You just use it like a regular file (since a regular file is a hard link).

    If you want something like unix symbolic links, then you can create an NTFS junction, such as this one that mounts a drive into a directory. (I'm told that Windows Vista expands the repertoire of symbolic links as well.)

    But neither of these features is available on FAT (or CD-ROMs or Novell Netware or email), which meant that Windows 95 couldn't use them. Last year I discussed in some detail why shortcuts are files. Maybe that's what your question is really about.

  • The Old New Thing

    Not the best way to start out a form letter

    • 17 Comments

    Special Welcome Back Offer From the President of The Wall Street Journal Online

    Dear Do Not Edit These Fields,

    My name is Todd Larsen and I'm the president of THE WALL STREET JOURNAL ONLINE...

    Pre-emptive snarky comment: I got a lame form letter from Microsoft once.

  • The Old New Thing

    No good deed goes unpunished: Free code samples

    • 36 Comments

    There's no such thing as a free lunch. Or free code either.

    Many years ago, I wrote some samples for the SDK as a favor, and at each major SDK release, I am reminded that no good deed goes unpunished.

    I can tell when a major SDK release is coming, because I get a piece of mail that goes something like this:

    Hi. You are on file as the owner of at least one SDK sample. Please fill out the following gazillion-item checklist for each sample. If you don't do this by (some date), your sample will be removed from the SDK.

    Yup, it's another episode of "You're not my manager."

    This gazillion-item checklist goes something like this:

    • Start with a clean install of Windows XP with all service packs and patches.
    • Install Visual Studio Super Awesome Edition.
    • Install the latest build of the Platform SDK from this internal web site.
    • Launch Visual Studio and change the following settings: ...
    • Open your sample in Visual Studio with the following settings: ...
    • Compile and run it, confirm that it builds with no warnings or errors and that it runs successfully.
    • Repeat with Windows XP 64-bit Edition.
    • Go to this internal web site and install additional stuff.
    • Run additional tests X, Y and Z and ensure that your sample passes them all.
    • ... etc ...

    It's the gift that punishes the giver. And not just once, but repeatedly, with no end in sight. If I'd know that writing an SDK sample would be so much recurring hassle, I might not have done it.

    Every time this happens, I have to remind the SDK folks that if they want people to do something as a favor to them, they have to make it easy. This means setting up machines with Windows XP and Windows XP 64-bit edition, installing Visual Studio Super Awesome Edition and all the additional tools, and letting people use Remote Desktop to connect to them, compile their samples, and run tests X, Y and Z.

    Each time I recommend that they do this, the response is "Hey, that's a good idea." It's like they forget that they did this same thing last time.

  • The Old New Thing

    Unlikely excuses: A faulty microchip

    • 22 Comments

    Last year, a talking action figure was discovered to utter a curse word. A spokesperson for the store said that the problem might be a faulty microchip.

    Huh? What microchips fail by saying curse words?

    I mean, I can see the voice chip failing by generating static or chopping up the audio so as to become unintelligible, but what are the odds that a chip will just happen to fail by reassembling the audio to form a popular curse word?

    Mind you, the problem may still end up having a benign explanation. Apparently, the recording is supposed to be of the word "stop". Maybe the recording quality is so poor that people can mis-hear it as another word. But that's not the same as a faulty microchip.

Page 2 of 4 (33 items) 1234