May, 2008

  • The Old New Thing

    Behind the Scenes at Mythbusters does not include experiments or explosions

    • 14 Comments

    Mythbusters is going on the road. Well, the show isn't actually going on the road. Just the two hosts. In Behind the Scenes at Mythbusters, they go on stage to tell stories about the myth busting process, but they won't conduct any actual experiments or blow anything up. (And therefore the reason for going to the show is what, exactly?)

    Oops, the Portland people forgot to edit the ad copy. It still says "A hilarious evening with Jamie Heyneman and Adam Savage" even though the show is in the afternoon.

    Tickets for the Seattle and Portland shows go on sale today. (Fans in San Diego will have to wait until September.)

  • The Old New Thing

    Why are some GDI functions named ExtXxx instead of XxxEx?

    • 21 Comments

    By convention, an enhanced version of a function Xxx is called XxxEx, but there are many GDI functions that don't follow this conventions, most notably ExtTextOut, which should have been named TextOutEx under the XxxEx convention. Why don't the GDI functions follow that convention?

    Because they were named before the XxxEx convention was established.

    Nothing nefarious, just an artifact of history.

  • The Old New Thing

    The sad demise of whimsical teasing in Comic Chat

    • 34 Comments

    Internet Explorer 3 came with the IRC client Comic Chat, a product from the research division. And it's not surprising that a program as goofy as Comic Chat would put something goofy in the default profile. If you didn't set a profile when you created your character, it defaulted to "This person is too lazy to create a profile entry."

    Of course, it wasn't long before people complained that the text was snotty. So much for trying to be funny.

  • The Old New Thing

    India Calling: Call centers from the Indian point of view

    • 27 Comments

    In the United States, the growing relocation of call centers to India has been the source of much hand-wringing, but for every action there is an equal and opposite reaction: What effect is this having on India?

    The primetime soap opera India Calling looks at the effect of a 600,000-person industry, the eyebrows raised when neighbors see a young single woman heading to work at night and returning in the morning, the social change this new affluence brings, and the stress of a job that consists in large part of talking to rude, angry Americans. Welcome to Bollywood.

  • The Old New Thing

    Reading a contract from the other side: SHSetInstanceExplorer and SHGetInstanceExplorer

    • 27 Comments

    Shell extensions that create worker threads need to call the SHGetInstanceExplorer function so that Explorer will not exit while the worker thread is still running. When your worker thread finishes, you release the IUnknown that you obtained to tell the host program, "Okay, I'm done now, thanks for waiting."

    You can read this contract from the other side. Instead of thinking of yourself as the shell extension running inside a host program, think of yourself as the host program that has a shell extension running inside of it. Consider a simple program that displays the properties of a file, or at least tries to:

    #include <windows.h>
    #include <shellapi.h>
    #include <tchar.h>
    
    int __cdecl _tmain(int argc, TCHAR **argv)
    {
      SHELLEXECUTEINFO sei = { sizeof(sei) };
      sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_INVOKEIDLIST;
      sei.nShow = SW_SHOWNORMAL;
      sei.lpVerb = TEXT("properties");
      sei.lpFile = TEXT("C:\\Windows\\Explorer.exe");
      ShellExecuteEx(&sei);
      return 0;
    }
    

    Oh dear! When you run this program, nothing happens. Well, actually, something did happen, but the program exited too fast for you to see it. To slow things down, add the line

      MessageBox(NULL, TEXT("Waiting"), TEXT("Title"), MB_OK);
    

    right before the return 0. Run the program again, and this time the properties dialog appears, as well as the message box. Aha, the problem is that our program is exiting while the property sheet is still active. (Now delete that call to MessageBox before something stupid happens.)

    The question now is how to know when the property sheet is finished so we can exit. That's where SHSetInstanceExplorer comes in. The name "Explorer" in the function name is really a placeholder for "the host program"; it just happens to be called "Explorer" because the function was written from the point of view of the shell extension, and the host program is nearly always Explorer.exe.

    In this case, however, we are the host program, not Explorer. The SHSetInstanceExplorer lets you register a free-threaded IUnknown that shell extensions can obtain by calling SHGetInstanceExplorer. Following COM reference counting conventions, the SHGetInstanceExplorer performs an AddRef() on the IUnknown that it returns; the shell extension's worker thread performs the corresponding Release() when it is finished.

    All that is required of the IUnknown that you pass to SHSetInstanceExplorer is that it be free-threaded; in other words, that it support being called from multiple threads. This means managing the "process reference count" with interlocked functions rather than boring ++ and -- operators. Of course, in practice, you also need to tell your main program "Okay, all the shell extensions are finished; you can exit now" when the reference count drops to zero.

    There are many ways to accomplish this task. Here's one that I threw together just now. I didn't think too hard about this class; I'm not positioning this as the best way of implementing it, or even as a particularly good one. The purpose of this article is to show the principle behind process references. Once you understand that, you are free to go ahead and solve the problem your own way. But here's a way.

    #include <shlobj.h>
    
    class ProcessReference : public IUnknown {
    public:
      STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
      {
        if (riid == IID_IUnknown) {
          *ppv = static_cast<IUnknown*>(this);
          AddRef();
          return S_OK;
        }
       *ppv = NULL; return E_NOINTERFACE;
      }
    
      STDMETHODIMP_(ULONG) AddRef()
        { return InterlockedIncrement(&m_cRef); }
    
      STDMETHODIMP_(ULONG) Release()
      {
        LONG lRef = InterlockedDecrement(&m_cRef);
        if (lRef == 0) PostThreadMessage(m_dwThread, WM_NULL, 0, 0);
        return lRef;
      }
    
      ProcessReference()
        : m_cRef(1), m_dwThread(GetCurrentThreadId())
        { SHSetInstanceExplorer(this); }
    
      ~ProcessReference()
      {
        SHSetInstanceExplorer(NULL);
        Release();
    
        MSG msg;
        while (m_cRef && GetMessage(&msg, NULL, 0, 0)) {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
        }
      }
    
    private:
      LONG m_cRef;
      DWORD m_dwThread;
    };
    

    The idea behind this class is that the main thread (and only the main thread) creates it on the stack. When constructed, the object registers itself as the "process IUnknown"; any shell extensions that call SHGetInstanceExplorer will get a pointer to this object. When the object is destructed, it unregisters itself as the process reference (to avoid dangling references) and waits for the reference count to drop to zero. Notice that the Release method posts a dummy thread message so that the "waiting for the reference count to go to zero" message loop will wake up.

    In a sense, this is backwards from the way normal COM objects work, which operate under the principle of "When the reference count drops to zero, the object is destructed." We turn it around and code it up as "when the object is destructed, it waits for the reference count to drop to zero." If you wanted to do it the more traditional COM way, you could have the main thread go into a wait loop and have the object's destructor signal the main thread. I did it this way because it makes using the class very convenient.

    Now that we have a process reference object, it's a simple matter of adding it to our main thread:

    int __cdecl _tmain(int argc, TCHAR **argv)
    {
      ProcessReference ref;
      ...
    

    With this modification, the program displays the property sheet and patiently waits for the property sheet to be dismissed before it exits.

    Exercise: Explain how the object behaves if we initialized the reference count to zero and deleted the call to Release in the destructor.

    Bonus reading: Do you know when your destructors run? Part 2.

  • The Old New Thing

    The cultural axes of punctuality and waiting in line

    • 58 Comments

    Even ten minutes after my conference talk was scheduled to begin, people were still wandering into the hall. My host explained that in Portugal, nothing starts on time. (This Web site goes so far as to say that arriving a half hour to 45 minutes late is customary.)

    I asked whether the Portuguese form orderly lines when waiting for things. The answer: No. They just form a crowd.

    One would expect that punctuality and waiting in orderly lines would be correlated traits in a culture, since they both reflect a degree of attention to detail and order, but there are exceptions.

    In Taiwan, people form very orderly lines. For example, lanes are painted on subway platforms for people to stand in, and everybody waits their turn in line to get on. On the other hand, punctuality is not a Taiwanese trademark.

    In the United States, waiting in line is taken for granted, but that doesn't always align with punctuality. It is important to be on time for business meetings, but the rules for social gatherings are more complex.

    • If invited to meet someone at a neutral location, you are expected to be there on time, and if you are more than five or ten minutes late, you should call the other party to let them know; otherwise they may abandon waiting for you.
    • If you are invited to dinner at someone's house, then up to about ten minutes tardiness is excusable, but under no conditions should you arrive early.
    • If invited to a party, you should arrive ten to fifteen minutes after the announced start time. The term for this is fashionably late. Again, under no circumstances should you arrive early.

    Is there a correlation between punctuality and waiting in line in your country?

  • The Old New Thing

    You can't give away something that isn't yours

    • 13 Comments

    This rule of real life applies to code as well. If something doesn't belong to you, then you can't give it away.

    For example, functions like SetCliboardData and SetWindowRgn take ownership of the item that you pass it. In SetClipboardData's case, the memory block you pass as the hMem parameter becomes the property of the clipboard. For SetWindowRgn it's the hRgn that becomes the property of the window manager. In both cases, you are giving control of the item to another component, but in order to do that legitimately, it must be yours to give away.

    This statement may be obvious, but it's surprising how many people fail to grasp its consequences. For example, you can't do this:

    // error checkin removed to improve readability
    SetClipboardData(CF_TEXT, hMem);
    // Code in italics is wrong
    SetClipboardData(RegisterClipboardData(CFSTR_SHELLURL), hMem);
    

    Once you call SetClipboardData the first time, the memory now belongs to the clipboard; it's not yours again. When you call SetClipboardData a second time, you're giving it away again, but the second time, it's not your memory any more.

    In other words: After the first call to SetClipboardData, the memory belongs to the clipboard. The second call is trying to give away something that you no longer own.

    It reminds me of a news story I read some time ago. In the United States, the winner of a major lottery often gets a choice between a series of payments over several years or a lump sum. Somebody won the lottery and agreed to sell the annuity to a company in exchange for a lump sum. (Presumably, this company offered a lump sum larger than the one offered by the lottery.) And then when the lottery asked whether to disburse the winnings in the form of an annuity or a lump sum, the winner opted for the lump sum. Dude, you can't take the lump sum. You already sold the revenue stream to that other company. That's not your money any more.

  • The Old New Thing

    Welcome to the International Bank of Raymond

    • 24 Comments

    Over a decade ago, one of my colleagues (whom I'll call "Ralph" for the purpose of this discussion) told me that he briefly ran an international bank.

    Well, not really.

    Ralph's first roommate was another Microsoft employee, somewhat of the stereotypical computer nerd in the sense that he could write code like a maniac but didn't have a good grasp on mundane life skills like, say, cashing paychecks (this was the days before direct deposit), paying bills, or opening glass doors before walking through. Ralph helped out his roommate by managing his finances for him, depositing his roommate's paychecks, paying his bills, loaning him cash (which he didn't have since he forgot to cash his paychecks). Opening doors, on the other hand, was still his own problem.

    In exchange for this high level of personal finance management, Ralph charged a "generous" service fee, calling himself The International Bank of Ralph or IBOR. (Pronounced eye-bore.)

    This is all a rather lengthy lead-in to the story of the International Bank of Raymond, which rather confusingly also goes by the name IBOR. But the International Bank of Raymond is a much more customer-oriented bank than the International Bank of Ralph. For example, a friend of mine wanted to buy a Suburban, and after negotiating with his wife, they arrived at the following agreement: He could get a Suburban, but only if it were yellow and cost no more than $5000. His wife figured these restrictions would delay the acquisition of the Suburban for a long time, possibly indefinitely. What she didn't count on, however, was that within a week, my friend would see a yellow Suburban with a For Sale sign in the parking lot of a local grocery store, with an asking price of... $5000! The owner asked to be paid in cash, and my friend didn't have that much money readily available, so he turned to the International Bank of Raymond. He borrowed $5000 from me to buy the Suburban (mind you, I don't keep that much cash on hand either, but I did have it readily available in a bank account), and then a week later, he repaid the loan.

    Turnabout is fair play. My friend's wife wanted to take him on a surprise trip. Had she bought the airplane tickets herself, she would have risked her husband noticing it on her bank statement. To avoid this problem, she established a line of credit at the International Bank of Raymond: I bought the tickets for her, and she repaid me after they returned from their vacation.

    How does one establish an account at the International Bank of Raymond? Well, first, you have to be a good personal friend of Raymond for several years...

  • The Old New Thing

    What does TranslateAccelerator do?

    • 13 Comments

    For some reason, there appears to be some confusion over what TranslateAccelerator does. It's very simple, and it's all spelled out in the documentation. You give it a message, and if the message is a keypress that matches an entry in the accelerator table, the corresponding WM_COMMAND or WM_SYSCOMMAND message is sent to the window you said you are translating messages for.

    One point of confusion is which window receives the translated message. Is it the window in the MSG structure or the window passed as the first parameter to TranslateAccelerator? This confusion shouldn't last long, though, because of the two options, one of them raises more questions than it resolves.

    • If it went to the window in the MSG structure, then there would be no need for a hWnd parameter to TranslateAccelerator, since it wouldn't be used for anything.
    • If it went to the window in the MSG structure, there wouldn't be much need to have a TranslateAccelerator function anyway, because even without it, the message would have gone to the window in the MSG structure anyway.

    If the message matches an accelerator, the WM_COMMAND or WM_SYSCOMMAND message goes to the window you passed as the first parameter to TranslateAccelerator.

    Once you understand this, you can answer the next question:

    My program has two dialogs containing tabs, both running on the same UI thread. I want to make sure that the Ctrl+Tab hotkey switches between the tabs on the correct dialog. I've already created an accelerator table, but how do I decide which window to pass as the first parameter to TranslateAccelerator? Right now, I'm using GetForegroundWindow().

    Well, first off, GetForegroundWindow() is completely wrong, since the foreground window need not be one of your two dialogs. The user may be working with Notepad, and now you sent a WM_COMMAND to Notepad with one of your private command codes, a command code that has a completely different meaning to Notepad.

    Okay, back to the question. How do you know which window to pass as the first parameter to TranslateAccelerator? Well, it's the window that you want the WM_COMMAND message to go to: You want it to go to the dialog that contains the window that the user typed Ctrl+Tab into. You can determine this window by looking at the MSG structure, since keyboard messages are delivered to the window with keyboard focus.

    if (IsChild(hwnd1, msg.hwnd))
        TranslateAccelerator(hwnd1, hAccel, &msg);
    else if (IsChild(hwnd2, msg.hwnd))
        TranslateAccelerator(hwnd2, hAccel, &msg);
    

    The window handle in the MSG structure tells you which window the user typed the key into; if that window is part of the first dialog, then translate the message and send any translated message to the first dialog. Otherwise, check the same thing with the second dialog.

    Easy as pie.

  • The Old New Thing

    Microspeak: On-board (verb)

    • 36 Comments

    Here are a few citations. On a list of activities:

    • On-board a new team member.

    Presumably they mean bring on board. What makes this particularly interesting is that they didn't convert a noun to a verb; they converted a prepositional phrase to a verb, demonstrating once again the malleability of the English language.

    Here's a snippet from a blog post which seems to use the same meaning, but dispensing with the hyphen:

    Over the past 4 weeks, we have been onboarding customers slowly.

    On the other hand, there are usages whose intended meaning I can't quite figure out. Some titles from documents I don't have access to:

    How to On-Board Tools on the Extranet
    On-Boarding Kit

    And a subsection from an old document:

    On-Board Schedule

    MilestoneTarget DateStatus
    Attend a client planning meeting Dec. 2005Complete
    Frooble analysisMeeting daily with ABC team to map out migration
    On-Board to client devFeb. 2006
    Client devTBD
    On-Board to client testTBD
    Client test completeTBD
    On-Board to DEFTBD
    DEF sign-off by GHITBD
    File migrationTBD
    Go-LiveTBD

    ABC, DEF, and GHI were TLAs I did not understand. Frooble is a made-up word substituting for the actual word in the schedule. (And yes, "Go-Live" is a noun.)

    As a final example, there is somebody at Microsoft whose official job title is Senior Onboarding Manager.

    If you can figure out what on-board means, you're smarter than me.

    Pre-emptive clever comment: Verbing weirds language.

Page 1 of 4 (39 items) 1234