February, 2013

  • The Old New Thing

    Microspeak: bubble up

    • 12 Comments

    Bubble up is the name of a soft drink, but at Microsoft, it means something else. (Remember, Microspeak is not just terms used exclusively within Microsoft, but also terms used at Microsoft more often than in the general population.)

    To bubble up information is to expose the information at a higher reporting level. For example, you might have a local team report that goes into detail over all the work items the team is responsible for and the corresponding status of each item. The data from this report may bubble up into a group report, which summarizes the work item status across all teams.

    As another example, if there is an error condition in a particular item, the error may be reported on the status page for that item, and it may bubble up to the status page for the container as well (meaning that the information will be reported there, too).

    Information can bubble up in other less formal ways. For example, your manager may say, "That's important. "I'll make sure to bubble that information up in our weekly status meeting," which is a jargony way of saying, "That's important. I'll make sure to present that information in our weekly status meeting."

    The implied metaphor is bubbles rising to the surface of a liquid, with the suggestion that reaching the top of the liquid is a good thing in some way or other. (Even though people usually don't pay attention to the bubbles at the top, and once they get there, they tend to just hang around for a while, and then pop. A rather ignominious end to a bubble, if you ask me.)

    The concept of information flowing to higher levels is used even in the HTML DOM specification: Event bubbling is the term used to describe how an event travels from an element to its parents.

  • The Old New Thing

    I speak German better in my dream than I do in real life

    • 21 Comments

    I dreamed that I was at a large trade show, where everybody had cleared the center of the main floor to make room for an impressive real-time holography demo.

    After the demo was over, everybody moved their chairs back, but the German delegation had difficulty returning their chairs to the exact position they took them from, because everybody else (not being German) just put the chairs in rows without regard for their original row and seat number.

    Next came dinner, and I had to help interpret for a German attendee (who in retrospect may have been Angela Merkel with dark hair) who had taken two appetizers and no main course (which was allowed, but it meant that she didn't get a main course). I got stuck translating the word "dessert" but I guessed.

    When I woke up, I looked up the word, and it turns out that dream-me had guessed correctly. So I can speak German better in my dreams than in real life.

  • The Old New Thing

    Display control buttons on your taskbar preview window

    • 15 Comments

    Today's "Little Program" displays a button on the taskbar preview window. For now, the button increments a number, because incrementing numbers is so retro.

    Welcome, visitor number  0 0 3 1 4

    Start with the program from last week and make these changes:

    int g_iCounter;
    
    void
    PaintContent(HWND hwnd, PAINTSTRUCT *pps)
    {
      RECT rc;
      GetClientRect(hwnd, &rc);
      LOGFONTW lf = { 0 };
      lf.lfHeight = rc.bottom - rc.top;
      wcscpy_s(lf.lfFaceName, L"Verdana");
      HFONT hf = CreateFontIndirectW(&lf);
      HFONT hfPrev = SelectFont(pps->hdc, hf);
      wchar_t wszCount[80];
      swprintf_s(wszCount, L"%d", g_iCounter);
      DrawTextW(pps->hdc, wszCount, -1, &rc,
                DT_CENTER | DT_VCENTER | DT_SINGLELINE);
      SelectFont(pps->hdc, hfPrev);
      DeleteObject(hf);
    }
    

    That's an awful lot of typing just to print a big number on the screen.

    #define IDC_INCREMENT 100
    
    void CreateThumbBarButtons(HWND hwnd)
    {
      THUMBBUTTON rgtb[1];
      rgtb[0].iId = IDC_INCREMENT;
      rgtb[0].hIcon = g_hicoAlert;
      rgtb[0].dwFlags = THBF_ENABLED;
      rgtb[0].dwMask = THB_ICON | THB_TOOLTIP | THB_FLAGS;
      wcscpy_s(rgtb[0].szTip, L"Increment the value");
      ITaskbarList3Ptr sptb3;
      sptb3.CreateInstance(CLSID_TaskbarList);
      sptb3->ThumbBarAddButtons(hwnd, 1, rgtb);
    }
    

    We define only one thumbbar button, and out of laziness, I just reuse that alert icon.

    void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
    {
      switch (id) {
      case IDC_INCREMENT:
        ++g_iCounter;
        InvalidateRect(hwnd, nullptr, TRUE);
        break;
      }
    }
    

    When the button is pressed, we increment the counter and invalidate our window so we redraw with the new counter.

        // HANDLE_MSG(hwnd, WM_CHAR, OnChar);
        HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
    
        default:
          if (uiMsg != 0 && uiMsg == g_wmTaskbarButtonCreated) {
            CreateThumbBarButtons(hwnd);
          }
          break;
    

    Okay, run the program, and then hover over the taskbar button so that the preview window appears. Hey, look, there's an alert icon button under the thumbnail.

    Click it.

    Boom, the number increments.

    That's why I chose a huge font to draw the number: So it's big enough that you can see the number in the thumbnail.

  • The Old New Thing

    Debug session: Why is an LPC server not responding?

    • 16 Comments

    A particular scenario was hanging, and the team responsible for the scenario debugged it to the point where they saw that their X component was waiting for their Y component, which was waiting for Explorer, so they asked for help chasing the hang into Explorer.

    The team was kind enough to have shared what they've learned so far:

    kd> !alpc /m 9c14d020
    
    Message 9c14d020
      MessageID             : 0x0274 (628)
      CallbackID            : 0xCCA5 (52389)
      SequenceNumber        : 0x00000016 (22)
      Type                  : LPC_REQUEST
      DataLength            : 0x0094 (148)
      TotalLength           : 0x00AC (172)
      Canceled              : No
      Release               : No
      ReplyWaitReply        : No
      Continuation          : Yes
      OwnerPort             : 82bb9db8 [ALPC_CLIENT_COMMUNICATION_PORT]
      WaitingThread         : 834553c0
      QueueType             : ALPC_MSGQUEUE_MAIN
      QueuePort             : 84646730 [ALPC_CONNECTION_PORT]
      QueuePortOwnerProcess : 846209c0 (explorer.exe)
      ServerThread          : 00000000 <-----------------------
      QuotaCharged          : No
      CancelQueuePort       : 00000000
      CancelSequencePort    : 00000000
      CancelSequenceNumber  : 0x00000000 (0)
      ClientContext         : 02a56b80
      ServerContext         : 00000000
      PortContext           : 0701ea20
      CancelPortContext     : 00000000
      SecurityData          : 962f89b8
      View                  : 00000000
    kd> !process 846209c0 0
    PROCESS 846209c0  SessionId: 1  Cid: 0804    Peb: 7fbac000  ParentCid: 0724
        DirBase: 3e546380  ObjectTable: 97195300  HandleCount: 1041.
        Image: explorer.exe
    

    Yikes, there is no thread signed up to service the request.

    I don't know much about ALPC, but I can fumble around. Fortunately, this is debugging and not rocket surgery, so you still get full points if you stumble across the answer by guessing.

    I decided to start guessing by looking at what the !alpc command can tell me.

    kd> !alpc -?
    
      !alpc /m MessageAddress
        Dumps the message at the specified address.
    
      !alpc /p PortAddress
        Dumps the port at the specified address.
    

    Well, I already saw what the result was for dumping the message, so I may as well dump the port.

    kd> !alpc /p 84646730
    
    ...
      8 thread(s) are registered with port IO completion object:
        THREAD 84658d40  Cid 0804.0888  Teb: 7fa7e000 Win32Thread: 8214a748 WAIT
        THREAD 8466a040  Cid 0804.08c4  Teb: 7fa74000 Win32Thread: 8214c800 WAIT
        THREAD 84659a00  Cid 0804.08ec  Teb: 7fa72000 Win32Thread: 82158d08 WAIT
        THREAD 8466c8c0  Cid 0804.08f0  Teb: 7fa6e000 Win32Thread: 82160420 WAIT
        THREAD 84671040  Cid 0804.0910  Teb: 7fa68000 Win32Thread: 8217c4e8 WAIT
        THREAD 8460d180  Cid 0804.099c  Teb: 7fa5e000 Win32Thread: 820bad08 WAIT
        THREAD 834278c0  Cid 0804.0c80  Teb: 7fa6b000 Win32Thread: 820b9620 WAIT
        THREAD 8345ad40  Cid 0804.0da0  Teb: 7fba9000 Win32Thread: 821c6d08 WAIT
    ...
    

    So it looks like there are eight threads signed up to process events on this port. (Is that what this means? I don't know, but I'm going to assume that it does, because this is debugging. Debugging is an exercise in optimism.) Let's see what they're doing.

    kd> .thread 84658d40;k
    Implicit thread is now 84658d40
      *** Stack trace for last set context - .thread/.cxr resets it
    ChildEBP RetAddr
    940ef394 80f1505f nt!KiSwapContext+0x19
    940ef3d0 80f184e0 nt!KiSwapThread+0x34b
    940ef3fc 80f163fc nt!KiCommitThreadWait+0x26f
    940ef46c 80f4d2df nt!KeWaitForSingleObject+0x459
    940ef4b4 80e20838 nt!KiSchedulerApc+0x298
    940ef4c8 00000000 hal!KfLowerIrql+0x2c
    
    [the others look the same]
    

    Well, I don't know what they're doing, but it looks like they're waiting for something. But one of the threads looks different:

    kd> .thread 84671040;k
    Implicit thread is now 84671040
      *** Stack trace for last set context - .thread/.cxr resets it
    ChildEBP RetAddr
    9415f864 80f1505f nt!KiSwapContext+0x19
    9415f8a0 80f184e0 nt!KiSwapThread+0x34b
    9415f8cc 80eb3d6e nt!KiCommitThreadWait+0x26f
    9415f934 810c0527 nt!KeWaitForMultipleObjects+0x4e3
    9415fbe4 810c0703 nt!ObWaitForMultipleObjects+0x2fd
    9415fd38 80ef113c nt!NtWaitForMultipleObjects+0xca
    9415fd38 77945e04 nt!KiFastCallEntry+0x12c
    07e2f1c4 779437f6 ntdll!KiFastSystemCallRet
    07e2f1c8 7515c136 ntdll!NtWaitForMultipleObjects+0xa
    07e2f34c 77752658 KERNELBASE!WaitForMultipleObjectsEx+0xee
    07e2f368 777fbe60 KERNEL32!WaitForMultipleObjects+0x19
    07e2f3d4 777fc5de KERNEL32!WerpReportFaultInternal+0x1a3
    07e2f3e8 777df654 KERNEL32!WerpReportFault+0x6d
    07e2f3f4 751e517c KERNEL32!BasepReportFault+0x19
    07e2f490 77a0f95a KERNELBASE!UnhandledExceptionFilter+0x1e0
    07e2f4a0 77a0fd4d ntdll!TppExceptionFilter+0x1b
    07e2f4b4 77a1c66b ntdll!TppWorkerpInnerExceptionFilter+0x13
    07e2fb34 77753278 ntdll!TppWorkerThread+0xa6092
    07e2fb40 779761a6 KERNEL32!BaseThreadInitThunk+0xe
    07e2fb80 77976152 ntdll!__RtlUserThreadStart+0x4a
    07e2fb90 00000000 ntdll!_RtlUserThreadStart+0x1c
    

    Ah, well that explains why Explorer isn't responding: It crashed on an unhandled exception! Windows Error Reporting is busy trying to generate a report.

    Now to see what the crash was. I don't know for sure, but I'm pretty confident that one of the parameters to Basep­Report­Fault is an EXCEPTION_POINTERS. Why am I confident of that? Because it would be hard to report the fault without it!

    kd> dd 07e2f3f4 l4
    07e2f3f4  07e2f490 77a0f95a 07e2f4e8 00000001
              ChildEBP RetAddr  Param1
    
    kd> dd 07e2f4e8 l2
    07e2f4e8  07e2f620 07e2f63c 
                     ^ ^
       ExceptionRecord ContextRecord
    kd> .cxr 0x07e2f63c
    eax=00000000 ebx=0451e2f8 ecx=e2af034f edx=77945e00 esi=00000000 edi=0451e2e0
    eip=1df7fc6a esp=07e2f920 ebp=07e2f938 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
    contoso!ContosoPower::Disconnect+0xdd:
    001b:1df7fc6a 8b08  mov ecx,dword ptr [eax] ds:0023:00000000=????????
    

    Aha, Explorer crashed due to a null pointer crash in the Contoso­Power::Disconnect function.

    Passing the buck onward to Contoso, the report back was that this was a known issue, and a hotfix was available.

  • The Old New Thing

    If you can't find the function, find the caller and see what the caller jumps to

    • 17 Comments

    You're debugging a program and you want to set a breakpoint on some function, say, netapi32!Ds­Address­To­Site­NameW, but when you execute the bp netapi32!Ds­Address­To­Site­NameW command in the debugger, the debugger says that there is no such function.

    The Advanced Windows Debugging book says that the bp command should set a breakpoint on the function, but the debugger says that the symbol cannot be found. I used the x netapi32!* command to see that the debugger did find a whole bunch of symbols, and it says that the symbols were loaded (from the public symbol store), but netapi32!Ds­Address­To­Site­NameW isn't among them. The MSDN documentation says that Ds­Address­To­Site­NameW is in the netapi32.dll, but it's not there! I can't believe you guys stripped that function out of the symbol file, since it's a function that people will want to set a breakpoint on.

    Okay, first let's rule out the conspiracy theory. The symbols were not stripped from the public symbols. And even if they were, that shouldn't stop you, because after all, the loader has to be able to find the function when it loads your program, so it's gotta be obtainable even without symbols.

    Don't be helpless. You already have the tools to figure out where the function is.

    Just write a program that calls the function, then load it into the debugger and see what the destination of the call instruction is. You don't even have to pass valid parameters to the function call, since you're never actually executing the code; you're just looking at it.

    And hey looky-here, you already have a program that calls the function: The program you're trying to debug! So let's see where it goes.

    0:001>u contoso!AwesomeFunction
    ...
    00407352 call [contoso!__imp__DsAddressToSiteNameW (0040f104)]
    ...
    0:001>u poi 0040f104
    logoncli!DsAddressToSiteNameW:
    7f014710 push ebp
    7f014711 mov esp, ebp
    ...
    

    There you go. The code for the function is in logoncli.dll.

    What happened? How did you end up in logoncli.dll?

    What you saw was the effect of a DLL forwarder. The code for the function Ds­Address­To­Site­NameW doesn't live in netapi32.dll. Instead, netapi32.dll has an export table entry that says "If anybody comes to me asking for Ds­Address­To­Site­NameW, send them to logoncli!Ds­Address­To­Site­NameW instead."

    Officially, the function is in netapi32.dll for linkage purposes, but internally the function has been forwarded to another DLL for implementation. It's like a telephone call-forwarding service for DLL functions, except that instead of forwarding telephone calls, it forwards function calls. You publish a phone number in all your marketing materials, and behind the scenes, you set up the number to forward to the phone of the person responsible for sales. That way, if that person quits, or the responsibility for selling the product changes, you can just update the call-forwarding table, and all the calls get routed to the new person.

    That's what happenned here. The MSDN phone book lists the function as being in netapi32.dll, and whenever a call comes in, it gets forwarded to wherever the implementation happens to be. And the implementation has moved around over time, so you should continue calling netapi32!Ds­Address­To­Site­NameW and let the call-forwarding do the work of getting you to the implementation.

    Don't start calling logoncli directly, thinking that you're cutting out the middle man, or in a future version of Windows, your program may start failing with a "This number is no longer in service" error, like calling the direct office number for the previous sales representative, only to find that he left the company last month.

  • The Old New Thing

    How do I launch a file as if it were a text file, even though its extension is not .txt?

    • 18 Comments

    You might have a program that generates log files or other text content with an extension other than .txt. You naturally might want to open these documents in the user's default text editor.

    You might decide to ask the Windows developer support team, "How can I figure out what program is the handler for text files?" The idea being that once you get this program name, you can then run it yourself, with the document on the command line. And you would also be running into the trap of looking for the answer to a question rather than a solution to a problem.

    For one thing, the default handler for the file type might require special command line parameters, parameters which you won't get if you merely get the executable path. For example, on Windows 7, the default command line for JPG files is %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Windows Photo Viewer\PhotoViewer.dll", ImageView_Fullscreen %1, and if you merely asked for the executable, all you would get back would be rundll32.exe, and trying to execute rundll32.exe Boats.jpg doesn't get you very far. You lost all the command line arguments.

    For another thing, the default handler for the file type might not even be a command line. It might be an old program that uses DDE. Or the handler might be a drop target. Or it could be an IContext­Menu or an IExecute­Command. In these cases, there is no command line in the first place, so asking for the command line template is meaningless.

    But we saw the answer to this question before, just in a different guise. The lpClass member of the SHELL­EXECUTE­INFO lets you open a file as if it were another type of file. In that article, somebody was passing a class when they didn't mean to; here, we're passing it on purpose.

  • The Old New Thing

    What's the story of the onestop.mid file in the Media directory?

    • 45 Comments

    If you look in your C:\Windows\Media folder, you'll find a MIDI file called onestop. What's the story behind this odd little MIDI file? Aaron Margosis considers this file a security risk because "if an attacker can cause that file to be played, it will cause lasting mental pain and anguish to everybody within earshot."

    Despite Wikipedia's claims[citation needed], the file is not an Easter Egg. The file was added in in Windows XP with the comment "Add cool MIDI files to replace bad old ones." So as bad as onestop is, the old ones must have been even worse!

    Okay, but why were they added?

    For product support.

    The product support team wants at least one MIDI file present on the system by default for troubleshooting purposes. That way, problems with MIDI playback can be diagnosed without making the customer go to a Web page and download a MIDI file. When asked why the song is so awful, the developer who added the file explained, "Believe it or not, OneStop is 'less bad' than the ones that it replaced. (Dance of the Sugar Plum Fairy, etc.)" Another reason for replacing the old MIDI file is that the new one exercises more instruments.

    The song was composed by David Yackley.

    On the other hand, we lost clock.avi.

  • The Old New Thing

    Let me take this shortcut across Lake Saskatchewan

    • 18 Comments

    I dreamed that I had to drive from Los Angeles to Berkeley to catch my flight home. To do this, I used a product from a local startup company: A computerized map of the Bay Area.

    There were two routes across Lake Saskatchewan. The southern route is highway I-70, and the northern route is an elevated highway that goes right past the head of the Canadian Statue of Freedom, a 300-foot-tall statue carved from a single rock. This shows that computer nerds are horrible cartographers: Berkeley has an airport, Los Angeles is on the peninsula just south of San Francisco, and the Bay Area is in Canada.

    Sure, this is an absurd dream, but you gotta admit, that shortcut across Lake Saskatchewan would be pretty awesome.

  • The Old New Thing

    Display an overlay on the taskbar button

    • 20 Comments

    Today's "Little Program" displays an overlay on the taskbar button. I've seen some people call this a "badge", but "overlay" is the official term.

    Start with our scratch program and make the following changes:

    #include <comip.h>
    #include <comdef.h>
    #include <shlobj.h>
    #include <shellapi.h>
    
    _COM_SMARTPTR_TYPEDEF(ITaskbarList3, __uuidof(ITaskbarList3));
    

    I decided to shake things up and use a different smart pointer library: com_ptr_t. (That'll teach you to complain that I don't use a smart pointer library in my samples. Now you get to complain that I use the wrong smart pointer library.)

    HICON g_hicoAlert;
    UINT g_wmTaskbarButtonCreated;
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
      g_hicoAlert = LoadIcon(nullptr, IDI_EXCLAMATION);
      g_wmTaskbarButtonCreated = RegisterWindowMessage(
                                  TEXT("TaskbarButtonCreated"));
      return TRUE;
    }
    

    Our overlay icon is the system exclamation point icon. I chose this because I'm lazy.

    bool g_fHasOverlay = false;
    
    void UpdateOverlayIcon(HWND hwnd)
    {
      HICON hicon = g_fHasOverlay ? g_hicoAlert : nullptr;
      PCWSTR pszDescription = g_fHasOverlay ?
                            L"Attention required" : nullptr;
      ITaskbarList3Ptr sptb3;
      sptb3.CreateInstance(CLSID_TaskbarList);
      sptb3->SetOverlayIcon(hwnd, hicon, pszDescription);
    }
    
    void OnChar(HWND hwnd, TCHAR ch, int cRepeat)
    {
      if (ch == ' ') {
        g_fHasOverlay = !g_fHasOverlay;
        UpdateOverlayIcon(hwnd);
      }
    }
    
        HANDLE_MSG(hwnd, WM_CHAR, OnChar);
    
        default:
          if (uiMsg != 0 && uiMsg == g_wmTaskbarButtonCreated) {
            UpdateOverlayIcon(hwnd);
          }
          break;
    

    A real program would have error checking, of course.

    Press the space bar, and the overlay will be toggled on and off.

    If you're really clever, you might generate your overlay icons on the fly, say, if you wanted to report the number of unread messages or something.

    I've heard that there's one program out there that abuses the ITaskbar­List3::Set­Progress­State method by changing its progress state repeatedly, causing its taskbar button to cycle through different colors to get the user's attention.

    Just a reminder: The user interface guidelines say that the way to get the user's attention is to flash your taskbar button. Various parts of the system understand this convention and respond to it. (For example, the taskbar will temporarily unhide if a button starts flashing, and accessibility tools know how to signal the flash state to the user.) As always, the shell reserves the right to block this sort of abusive behavior in the future, just like it has done with abusive notification icons.

  • The Old New Thing

    The Essential Guide To Dim Sum

    • 4 Comments

    The folks over at BuzzFeed Food have put together this marvelous Essential Guide To Dim Sum. The field guide includes pictures of dim sum offerings, organized by food preparation style, then sub-categorized by physical appearance. Each dish is accompanied by its name in three languages (English, Mandarin Chinese, and Cantonese), along with symbolic information similar to a clothing care tag that encodes information like recommended method of eating.

    Save some radish cakes for me.

    Via Tony Chor.

Page 2 of 3 (29 items) 123