January, 2009

  • The Old New Thing

    Where does shell.windows.com get information about file extensions, and how do I get in on that action?

    • 46 Comments

    If you double-click a file for which there is no registered handler, Windows will offer to visit the Web service on shell.windows.com to locate a program that can open it. But where does this information come from, and how can you add your program to the database?

    Knowledge Base article Q929149, titled Windows File Association System On-Boarding Process, provides step-by-step instructions on how you can add your file extension.

    If you look at the existing entries on shell.windows.com, most of them have relatively straightforward and neutral descriptions. "This document is a PowerPoint presentation." "This document is a sound file." But there is at least one company that decided to use the file association service for a bit of grandstanding. "Invented by XYZ company and perfected over 15 years, ABC file format lets you capture information from any application, on any computer, and share it with anyone around the world."

    By the way, if the file association Web service offends you, you can disable it.

  • The Old New Thing

    xkcd breaks the news on the new Windows 7 user interface

    • 9 Comments

    Last week, Web comic xkcd covered the new Windows 7 user interface. Unfortunately, they got the wrong operating system. It was Windows XP that had a picture of Hitler (according to a few of our beta testers).

  • The Old New Thing

    How does PostQuitMessage know which thread to post the quit message to?

    • 8 Comments

    Commenter bav016 asks how functions like PostQuitMessage and SetTimer(NULL) know which thread the messages should go to. Unlike some functions such as InvalidateRect which have a window handle parameter that lets you say which window you want to operate on, PostQuitMessage and SetTimer(NULL) don't say which thread the WM_QUIT or WM_TIMER message should go to. How do they decide?

    The messages go to the current thread; that is, they are delivered to the thread that called the function in the first place.

    There are many functions which operate on an implicit message queue, and those cases, they operate on the message queue associated with the thread making the call. If you call GetKeyState you retrieve the calling thread's keyboard state. If you call GetMessage you retrieve messages from the calling thread's message queue. If you call InSendMessage, you are told about the calling thread's message processing state. If you call GetQueueStatus you retrieve information about the calling thread's msesage queue. You get the idea.

    If you want these functions to operate on a thread different from the one that is executing, you'll have to ask that thread to make the call for you.

  • The Old New Thing

    It's surprising how suddenly those new skins started pouring in

    • 37 Comments

    A friend of mine told me a story of a project from over ten years ago. Part of the product design was that it would include a bunch of skins (visual styles). The development team had written up the skinning infrastructure, but the company which was hired to create the actual skins hadn't delivered anything. My friend's assignment was to test the skin-switching interface, but since there were no skins, there was nothing to test.

    My friend was responsible for testing a bunch of other product features, so it's not like the days were spent thumb-twiddling. But eventually, it got to the point where the automated testing for most of the other features was nearly complete, and at the weekly status meetings, my friend would ask the representative from the company that was hired to deliver the skins when they might send over the first two or three. "I don't need all twenty skins, just two or three so I can test the skin-switcher."

    At the meetings, the company representative always answered, "Yes, we know you're waiting for it, but designing these skins is really complicated stuff, and it's pretty slow going."

    My friend wondered, "How hard is this, really?" A visit to the development team quickly produced the same tools and documents that the skin design company was given, and in a few hours on a Saturday, my friend produced a brand new skin. Since my friend was a big Star Trek fan at the time, the homegrown skin was naturally based on the science fiction series, created by surfing the Internet and downloading pictures and sound effects. There was obviously no way the company could include this skin with their product, but at least it was something to test with. The skin-switcher finally had something to switch between: You could have the standard look or the Star Trek skin.

    At the next meeting, my friend once again asked the representative from the design company when they might deliver a skin or two and received the same runaround as usual. "It's a lot harder than we anticipated, but we're getting close, so who knows, you might have something in a few weeks."

    Except this time, my friend had a response for them. "Oh, maybe I can help you guys out. I threw together this skin in just a few hours. Here, let me show you." And then followed a demonstration of the Star Trek theme. It had a lot of rough edges (for example, the sound volumes weren't leveled), but it certainly did the job as a demo.

    As if by magic, just a few days later, the company that had been contracted to produce the skins managed to overcome all the obstacles that had been plaguing them up until now, and a steady stream of new skins started arriving.

  • The Old New Thing

    The programmers don't design skins; they just make skins possible

    • 37 Comments

    Not all skill sets are interchangeable. That's why we have concepts like division of labor and specialization. But it appears that not everybody understands this.

    I was reminded of this topic when I read the reactions to the Microsoft Exchange Team announcing that they had added Xbox and Zune themes to OWA. Many people were shocked, such as Loren, who was furious that "development time" was wasted on something frivolous like new themes when there are so many bugs that need to be fixed. Loren appears to believe that the people who do graphic design and the people who are up to their elbows in the code base fixing bugs and adding features are one and the same, that any time spent building a new visual style is time not spent fixing bugs.

    Generally speaking, programmers don't do the visual design. I mean, these are people who are lucky if they are wearing matching socks when they come to work, if they even remember to wear socks at all. And you want them to design a color scheme? Visual design is done by people trained in the visual arts, people who know about color wheels and lighting effects, people who can argue for days about whether the spacing between two visual elements should be one point or two, about whether a serif or sans-serif font should be used for a particular screen element. What the programmers do is create the infrastructure for skins and themes, but the actual building of the skin or theme is left to the designers. Once the infrastructure is built, the programmers' work is pretty much done, and they can go back to fixing bugs and adding other features, stuff that keeps Loren happy. Of course, the infrastructure for themes and skins needs to be maintained, but the amount of work spent on maintenance is largely independent of how many themes the designers have come up with.

    (Similarly, the office disco party happened to have been the handiwork of the infrared communication team, not the Windows 95 setup team, so any time they wasted on the party had no effect on Windows 95 setup.)

    Mind you, the new skins do create additional work for the testers, who need to write tests to verify that that the new skins don't have bugs like "In the Zune skin, you can't read the subject lines." But presumably they already have a template for testing skins—after all, they have to test all the other skins—so plugging the new skin's parameters into the template is a comparatively low incremental cost.

    So go ahead and enjoy those new themes, comforted in knowing that the effort spent in making those new themes was not at the cost of bug fixing.

    Next time, a story about what happens when a tester needs to test the skinning infrastructure when there are no skins.

  • The Old New Thing

    Raymond misreads flyers: A Taste of WWL

    • 14 Comments

    There were flyers in our building inviting people to attend a food event called A Taste of WWL.

    The letters WWL stand for Windows and Windows Live, but the font they chose for the sign was confusing to me. The capital L looked like a capital I, and I misread the poster as an invitation to attend A Taste of WWI.

    And then I thought, "Who the heck thought World War I was a fun event we'd want to re-experience?"

    One of my colleagues, who also misread the poster, had a much more succinct response: "Mmmm... mustard gas..."

  • The Old New Thing

    When debugging a stack overflow, you want to focus on the repeating recursive part

    • 10 Comments

    When your program breaks into the debugger with a stack overflow, you will get a ridiculously huge stack trace because your program has gone into some sort of recursive death. (This is not a statement of metaphysical certitude, but it is true with very high probability.) But the place where the program crashed is usually not interesting at all. Here's a sample stack trace. (Warning: Ridiculously long stack traces ahead because stack traces from stack overflows are always ridiculously long. Apologies to my blind readership.)

    ntdll!RtlpAllocateHeap+0x394f2
    ntdll!RtlAllocateHeap+0x151
    ntdll!RtlFormatCurrentUserKeyPath+0xfa
    ADVAPI32!BaseRegTranslateToUserClassKey+0xaf
    ADVAPI32!BaseRegOpenClassKeyFromLocation+0xc0
    ADVAPI32!BaseRegGetUserAndMachineClass+0x102
    ADVAPI32!LocalBaseRegQueryValue+0xeb
    ADVAPI32!RegQueryValueExW+0xef
    SHLWAPI!_SHRegQueryValueW+0xfc
    SHLWAPI!SHRegGetValueW+0xca
    PROGRAM!GetPathFromRegistry+0x3d
    PROGRAM!CPluginFinder::GetProviderDLL+0x79
    PROGRAM!CPluginFinder::InitializeProvider+0x22
    PROGRAM!CPluginFinder::Initialize+0xad
    PROGRAM!LookupPluginInfo+0x49
    PROGRAM!CPluginInfo::Create+0x1d4
    PROGRAM!TList<CPluginInfo>::GetInfo+0x6d
    PROGRAM!CPluginInfo::GetInfoTable+0x5d
    PROGRAM!TList<CPluginInfo>::Enumerate+0x83
    PROGRAM!CPluginRepository::GetGUID+0xc0
    PROGRAM!CPrivateNodeInfo::GetPluginInfo+0xdf
    PROGRAM!CPrivateNodeInfo::LoadPlugin+0x7a
    PROGRAM!CPrivateNode::GetChild+0x2e3
    PROGRAM!CPrivateNode::FindChild+0x2be
    PROGRAM!CPrivateNode::FindItem+0x190
    PROGRAM!CPrivateNode::FindChild+0x289
    PROGRAM!CPrivateNode::FindItem+0x190
    PROGRAM!CLocalNode::FindItem+0xca
    PROGRAM!CCompoundTreeNode::FindItem+0x70
    PROGRAM!CCompoundTreeNode::FindChild+0xaf
    PROGRAM!CCompoundTreeNode::FindItem+0x55
    PROGRAM!FindTreeItem+0x78
    PROGRAM!CToolbarCommand::Initialize+0x6c
    PROGRAM!CompoundMenu_InitMenu+0x1d2
    PROGRAM!CItemMenu::InitMenu+0x4e0
    PROGRAM!InvokeViaContextMenu+0xce
    PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e
    PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97
    PROGRAM!CToolbarSite::SendToToolbar+0x66
    PROGRAM!CToolbarSite::OnAction+0x1ff
    PROGRAM!CToolbarSite::HandleMessage+0xaa
    PROGRAM!CSite::HandleMessage+0x61
    PROGRAM!CMainWindow::WindowProc+0xc92
    PROGRAM!CWindow::WindowProc+0x91
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!SendMessageWorker+0x64a
    USER32!SendMessageW+0x5b
    comctl32!CReBar::_WndProc+0x1b5
    comctl32!CReBar::s_WndProc+0x4a
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!SendMessageWorker+0x64a
    USER32!SendMessageW+0x5b
    comctl32!CToolbar::TBOnLButtonUp+0x181
    comctl32!CToolbar::ToolbarWndProc+0xed1
    comctl32!CToolbar::s_ToolbarWndProc+0xd6
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!CallWindowProcAorW+0xdb
    USER32!CallWindowProcW+0x18
    comctl32!CallOriginalWndProc+0x1d
    comctl32!CallNextSubclassProc+0x8d
    comctl32!DefSubclassProc+0x7c
    PROGRAM!DefSubclassProc+0x56
    PROGRAM!CToolbar::WindowProc+0x142
    PROGRAM!CCustomizableToolbar::WindowProc+0xb3
    PROGRAM!CWindowSubclass::SubclassWndProc+0xeb
    comctl32!CallNextSubclassProc+0x8d
    comctl32!MasterSubclassProc+0xe1
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!DispatchMessageWorker+0x389
    PROGRAM!MsgWaitForCompletion+0xe0
    PROGRAM!AsyncFinishCall+0x22
    PROGRAM!SynchronousCallService+0x48a
    PROGRAM!GetItemDescriptionFromServer+0x49c
    PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f
    PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127
    PROGRAM!CTreeItem::GetDescriptionWorker+0x198
    PROGRAM!CTreeItem::GetDescription+0x188
    PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90
    PROGRAM!CTreeItemWrapper::GetDescription+0x20b
    PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2
    PROGRAM!CItemPropertiesMenu::Execute+0xe7
    PROGRAM!CompoundMenu_DispatchCommand+0x108
    PROGRAM!CItemMenu::Execute+0x29c
    PROGRAM!CCompoundMenu::ExecuteDirect+0x308
    PROGRAM!CCompoundMenu::Execute+0xf4
    PROGRAM!CompoundMenu_DispatchCommand+0x108
    PROGRAM!CItemMenu::Execute+0x29c
    PROGRAM!InvokeViaContextMenu+0x11c
    PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e
    PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97
    PROGRAM!CToolbarSite::SendToToolbar+0x66
    PROGRAM!CToolbarSite::OnAction+0x1ff
    PROGRAM!CToolbarSite::HandleMessage+0xaa
    PROGRAM!CSite::HandleMessage+0x61
    PROGRAM!CMainWindow::WindowProc+0xc92
    PROGRAM!CWindow::WindowProc+0x91
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!SendMessageWorker+0x64a
    USER32!SendMessageW+0x5b
    comctl32!CReBar::_WndProc+0x1b5
    comctl32!CReBar::s_WndProc+0x4a
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!SendMessageWorker+0x64a
    USER32!SendMessageW+0x5b
    comctl32!CToolbar::TBOnLButtonUp+0x181
    comctl32!CToolbar::ToolbarWndProc+0xed1
    comctl32!CToolbar::s_ToolbarWndProc+0xd6
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!CallWindowProcAorW+0xdb
    USER32!CallWindowProcW+0x18
    comctl32!CallOriginalWndProc+0x1d
    comctl32!CallNextSubclassProc+0x8d
    comctl32!DefSubclassProc+0x7c
    PROGRAM!DefSubclassProc+0x56
    PROGRAM!CToolbar::WindowProc+0x142
    PROGRAM!CCustomizableToolbar::WindowProc+0xb3
    PROGRAM!CWindowSubclass::SubclassWndProc+0xeb
    comctl32!CallNextSubclassProc+0x8d
    comctl32!MasterSubclassProc+0xe1
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!DispatchMessageWorker+0x389
    PROGRAM!MsgWaitForCompletion+0xe0
    PROGRAM!AsyncFinishCall+0x22
    PROGRAM!SynchronousCallService+0x48a
    PROGRAM!GetItemDescriptionFromServer+0x49c
    PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f
    PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127
    PROGRAM!CTreeItem::GetDescriptionWorker+0x198
    PROGRAM!CTreeItem::GetDescription+0x188
    PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90
    PROGRAM!CTreeItemWrapper::GetDescription+0x20b
    PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2
    PROGRAM!CItemPropertiesMenu::Execute+0xe7
    PROGRAM!CompoundMenu_DispatchCommand+0x108
    PROGRAM!CItemMenu::Execute+0x29c
    PROGRAM!CCompoundMenu::ExecuteDirect+0x308
    PROGRAM!CCompoundMenu::Execute+0xf4
    PROGRAM!CompoundMenu_DispatchCommand+0x108
    PROGRAM!CItemMenu::Execute+0x29c
    PROGRAM!InvokeViaContextMenu+0x11c
    PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e
    PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97
    PROGRAM!CToolbarSite::SendToToolbar+0x66
    PROGRAM!CToolbarSite::OnAction+0x1ff
    PROGRAM!CToolbarSite::HandleMessage+0xaa
    PROGRAM!CSite::HandleMessage+0x61
    PROGRAM!CMainWindow::WindowProc+0xc92
    PROGRAM!CWindow::WindowProc+0x91
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!SendMessageWorker+0x64a
    USER32!SendMessageW+0x5b
    comctl32!CReBar::_WndProc+0x1b5
    comctl32!CReBar::s_WndProc+0x4a
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!SendMessageWorker+0x64a
    USER32!SendMessageW+0x5b
    comctl32!CToolbar::TBOnLButtonUp+0x181
    comctl32!CToolbar::ToolbarWndProc+0xed1
    comctl32!CToolbar::s_ToolbarWndProc+0xd6
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!CallWindowProcAorW+0xdb
    USER32!CallWindowProcW+0x18
    comctl32!CallOriginalWndProc+0x1d
    comctl32!CallNextSubclassProc+0x8d
    comctl32!DefSubclassProc+0x7c
    PROGRAM!DefSubclassProc+0x56
    PROGRAM!CToolbar::WindowProc+0x142
    PROGRAM!CCustomizableToolbar::WindowProc+0xb3
    PROGRAM!CWindowSubclass::SubclassWndProc+0xeb
    comctl32!CallNextSubclassProc+0x8d
    comctl32!MasterSubclassProc+0xe1
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!DispatchMessageWorker+0x389
    PROGRAM!MsgWaitForCompletion+0xe0
    PROGRAM!AsyncFinishCall+0x22
    PROGRAM!SynchronousCallService+0x48a
    PROGRAM!GetItemDescriptionFromServer+0x49c
    PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f
    PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127
    PROGRAM!CTreeItem::GetDescriptionWorker+0x198
    PROGRAM!CTreeItem::GetDescription+0x188
    PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90
    PROGRAM!CTreeItemWrapper::GetDescription+0x20b
    PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2
    PROGRAM!CItemPropertiesMenu::Execute+0xe7
    PROGRAM!CompoundMenu_DispatchCommand+0x108
    PROGRAM!CItemMenu::Execute+0x29c
    PROGRAM!CCompoundMenu::ExecuteDirect+0x308
    PROGRAM!CCompoundMenu::Execute+0xf4
    PROGRAM!CompoundMenu_DispatchCommand+0x108
    PROGRAM!CItemMenu::Execute+0x29c
    PROGRAM!InvokeViaContextMenu+0x11c
    PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e
    PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97
    PROGRAM!CToolbarSite::SendToToolbar+0x66
    PROGRAM!CToolbarSite::OnAction+0x1ff
    PROGRAM!CToolbarSite::HandleMessage+0xaa
    PROGRAM!CSite::HandleMessage+0x61
    PROGRAM!CMainWindow::WindowProc+0xc92
    PROGRAM!CWindow::WindowProc+0x91
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!SendMessageWorker+0x64a
    USER32!SendMessageW+0x5b
    comctl32!CReBar::_WndProc+0x1b5
    comctl32!CReBar::s_WndProc+0x4a
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!SendMessageWorker+0x64a
    USER32!SendMessageW+0x5b
    comctl32!CToolbar::TBOnLButtonUp+0x181
    comctl32!CToolbar::ToolbarWndProc+0xed1
    comctl32!CToolbar::s_ToolbarWndProc+0xd6
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!CallWindowProcAorW+0xdb
    USER32!CallWindowProcW+0x18
    comctl32!CallOriginalWndProc+0x1d
    comctl32!CallNextSubclassProc+0x8d
    comctl32!DefSubclassProc+0x7c
    PROGRAM!DefSubclassProc+0x56
    PROGRAM!CToolbar::WindowProc+0x142
    PROGRAM!CCustomizableToolbar::WindowProc+0xb3
    PROGRAM!CWindowSubclass::SubclassWndProc+0xeb
    comctl32!CallNextSubclassProc+0x8d
    comctl32!MasterSubclassProc+0xe1
    USER32!UserCallWinProcCheckWow+0x1ad
    USER32!DispatchMessageWorker+0x389
    PROGRAM!MsgWaitForCompletion+0xe0
    PROGRAM!AsyncFinishCall+0x22
    PROGRAM!SynchronousCallService+0x48a
    PROGRAM!GetItemDescriptionFromServer+0x49c
    PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f
    PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127
    PROGRAM!CTreeItem::GetDescriptionWorker+0x198
    PROGRAM!CTreeItem::GetDescription+0x188
    PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90
    PROGRAM!CTreeItemWrapper::GetDescription+0x20b
    PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2
    PROGRAM!CItemPropertiesMenu::Execute+0xe7
    PROGRAM!CompoundMenu_DispatchCommand+0x108
    PROGRAM!CItemMenu::Execute+0x29c
    PROGRAM!CCompoundMenu::ExecuteDirect+0x308
    PROGRAM!CCompoundMenu::Execute+0xf4
    PROGRAM!CompoundMenu_DispatchCommand+0x108
    PROGRAM!CItemMenu::Execute+0x29c
    PROGRAM!InvokeViaContextMenu+0x11c
    PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e
    PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97
    

    If you go hunting through your defect tracking database trying to see whether this is a known issue or not, a search for the top functions on the stack is unlikely to find anything interesting. That's because stack overflows tend to happen at a random point in the recursion; each stack overflow looks superficially different from every other one even if they are the same stack overflow.

    Suppose you're singing the song Frère Jacques, except that you sing each verse a few tones higher than the previous one. Eventually, you will reach the top of your singing range, and precisely where that happens depends on where your vocal limit lines up against the melody. In the melody, the first three notes are each a new "record high" (i.e., the notes are higher than any other note sung so far), and new record highs appear in the three notes of the third measure, and a final record high in the second note of the fifth measure.

    If the melody represented a program's stack usage, a stack overflow could possibly occur at any of those five locations in the program's execution. In other words, the same underlying runaway recursion (musically represented by an ever-higher rendition of the melody) can manifest itself in five different ways. The "recursion" in this analogy was rather quick, just eight bars before the loop repeated. In real life, the loop can be quite long, leading to dozens of potential points where the stack overflow can manifest itself.

    If you are faced with a stack overflow, then, you want to ignore the top of the stack, since that's just focusing on the specific note that exceeded your vocal range. You really want to find the entire melody, since that's what's common to all the stack overflows with the same root cause.

    To do this, look for the part of the stack trace that repeats itself. That's the pattern that is causing the problem, the stack overflow melody, you might say. (And if you do say it, everybody will look at you funny since it's just a dumb analogy I made up on the spot.) Let's take another look at that stack trace above.

    ntdll!RtlpAllocateHeap+0x394f2 ntdll!RtlAllocateHeap+0x151 ntdll!RtlFormatCurrentUserKeyPath+0xfa ADVAPI32!BaseRegTranslateToUserClassKey+0xaf ADVAPI32!BaseRegOpenClassKeyFromLocation+0xc0 ADVAPI32!BaseRegGetUserAndMachineClass+0x102 ADVAPI32!LocalBaseRegQueryValue+0xeb ADVAPI32!RegQueryValueExW+0xef SHLWAPI!_SHRegQueryValueW+0xfc SHLWAPI!SHRegGetValueW+0xca PROGRAM!GetPathFromRegistry+0x3d PROGRAM!CPluginFinder::GetProviderDLL+0x79 PROGRAM!CPluginFinder::InitializeProvider+0x22 PROGRAM!CPluginFinder::Initialize+0xad PROGRAM!LookupPluginInfo+0x49 PROGRAM!CPluginInfo::Create+0x1d4 PROGRAM!TList<CPluginInfo>::GetInfo+0x6d PROGRAM!CPluginInfo::GetInfoTable+0x5d PROGRAM!TList<CPluginInfo>::Enumerate+0x83 PROGRAM!CPluginRepository::GetGUID+0xc0 PROGRAM!CPrivateNodeInfo::GetPluginInfo+0xdf PROGRAM!CPrivateNodeInfo::LoadPlugin+0x7a PROGRAM!CPrivateNode::GetChild+0x2e3 PROGRAM!CPrivateNode::FindChild+0x2be PROGRAM!CPrivateNode::FindItem+0x190 PROGRAM!CPrivateNode::FindChild+0x289 PROGRAM!CPrivateNode::FindItem+0x190 PROGRAM!CLocalNode::FindItem+0xca PROGRAM!CCompoundTreeNode::FindItem+0x70 PROGRAM!CCompoundTreeNode::FindChild+0xaf PROGRAM!CCompoundTreeNode::FindItem+0x55 PROGRAM!FindTreeItem+0x78 PROGRAM!CToolbarCommand::Initialize+0x6c PROGRAM!CompoundMenu_InitMenu+0x1d2 PROGRAM!CItemMenu::InitMenu+0x4e0 PROGRAM!InvokeViaContextMenu+0xce PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97 PROGRAM!CToolbarSite::SendToToolbar+0x66 PROGRAM!CToolbarSite::OnAction+0x1ff PROGRAM!CToolbarSite::HandleMessage+0xaa PROGRAM!CSite::HandleMessage+0x61 PROGRAM!CMainWindow::WindowProc+0xc92 PROGRAM!CWindow::WindowProc+0x91 USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CReBar::_WndProc+0x1b5 comctl32!CReBar::s_WndProc+0x4a USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CToolbar::TBOnLButtonUp+0x181 comctl32!CToolbar::ToolbarWndProc+0xed1 comctl32!CToolbar::s_ToolbarWndProc+0xd6 USER32!UserCallWinProcCheckWow+0x1ad USER32!CallWindowProcAorW+0xdb USER32!CallWindowProcW+0x18 comctl32!CallOriginalWndProc+0x1d comctl32!CallNextSubclassProc+0x8d comctl32!DefSubclassProc+0x7c PROGRAM!DefSubclassProc+0x56 PROGRAM!CToolbar::WindowProc+0x142 PROGRAM!CCustomizableToolbar::WindowProc+0xb3 PROGRAM!CWindowSubclass::SubclassWndProc+0xeb comctl32!CallNextSubclassProc+0x8d comctl32!MasterSubclassProc+0xe1 USER32!UserCallWinProcCheckWow+0x1ad USER32!DispatchMessageWorker+0x389 PROGRAM!MsgWaitForCompletion+0xe0 PROGRAM!AsyncFinishCall+0x22 PROGRAM!SynchronousCallService+0x48a PROGRAM!GetItemDescriptionFromServer+0x49c PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127 PROGRAM!CTreeItem::GetDescriptionWorker+0x198 PROGRAM!CTreeItem::GetDescription+0x188 PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90 PROGRAM!CTreeItemWrapper::GetDescription+0x20b PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2 PROGRAM!CItemPropertiesMenu::Execute+0xe7 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!CCompoundMenu::ExecuteDirect+0x308 PROGRAM!CCompoundMenu::Execute+0xf4 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!InvokeViaContextMenu+0x11c PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97 PROGRAM!CToolbarSite::SendToToolbar+0x66 PROGRAM!CToolbarSite::OnAction+0x1ff PROGRAM!CToolbarSite::HandleMessage+0xaa PROGRAM!CSite::HandleMessage+0x61 PROGRAM!CMainWindow::WindowProc+0xc92 PROGRAM!CWindow::WindowProc+0x91 USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CReBar::_WndProc+0x1b5 comctl32!CReBar::s_WndProc+0x4a USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CToolbar::TBOnLButtonUp+0x181 comctl32!CToolbar::ToolbarWndProc+0xed1 comctl32!CToolbar::s_ToolbarWndProc+0xd6 USER32!UserCallWinProcCheckWow+0x1ad USER32!CallWindowProcAorW+0xdb USER32!CallWindowProcW+0x18 comctl32!CallOriginalWndProc+0x1d comctl32!CallNextSubclassProc+0x8d comctl32!DefSubclassProc+0x7c PROGRAM!DefSubclassProc+0x56 PROGRAM!CToolbar::WindowProc+0x142 PROGRAM!CCustomizableToolbar::WindowProc+0xb3 PROGRAM!CWindowSubclass::SubclassWndProc+0xeb comctl32!CallNextSubclassProc+0x8d comctl32!MasterSubclassProc+0xe1 USER32!UserCallWinProcCheckWow+0x1ad USER32!DispatchMessageWorker+0x389 PROGRAM!MsgWaitForCompletion+0xe0 PROGRAM!AsyncFinishCall+0x22 PROGRAM!SynchronousCallService+0x48a PROGRAM!GetItemDescriptionFromServer+0x49c PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127 PROGRAM!CTreeItem::GetDescriptionWorker+0x198 PROGRAM!CTreeItem::GetDescription+0x188 PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90 PROGRAM!CTreeItemWrapper::GetDescription+0x20b PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2 PROGRAM!CItemPropertiesMenu::Execute+0xe7 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!CCompoundMenu::ExecuteDirect+0x308 PROGRAM!CCompoundMenu::Execute+0xf4 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!InvokeViaContextMenu+0x11c PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97 PROGRAM!CToolbarSite::SendToToolbar+0x66 PROGRAM!CToolbarSite::OnAction+0x1ff PROGRAM!CToolbarSite::HandleMessage+0xaa PROGRAM!CSite::HandleMessage+0x61 PROGRAM!CMainWindow::WindowProc+0xc92 PROGRAM!CWindow::WindowProc+0x91 USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CReBar::_WndProc+0x1b5 comctl32!CReBar::s_WndProc+0x4a USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CToolbar::TBOnLButtonUp+0x181 comctl32!CToolbar::ToolbarWndProc+0xed1 comctl32!CToolbar::s_ToolbarWndProc+0xd6 USER32!UserCallWinProcCheckWow+0x1ad USER32!CallWindowProcAorW+0xdb USER32!CallWindowProcW+0x18 comctl32!CallOriginalWndProc+0x1d comctl32!CallNextSubclassProc+0x8d comctl32!DefSubclassProc+0x7c PROGRAM!DefSubclassProc+0x56 PROGRAM!CToolbar::WindowProc+0x142 PROGRAM!CCustomizableToolbar::WindowProc+0xb3 PROGRAM!CWindowSubclass::SubclassWndProc+0xeb comctl32!CallNextSubclassProc+0x8d comctl32!MasterSubclassProc+0xe1 USER32!UserCallWinProcCheckWow+0x1ad USER32!DispatchMessageWorker+0x389 PROGRAM!MsgWaitForCompletion+0xe0 PROGRAM!AsyncFinishCall+0x22 PROGRAM!SynchronousCallService+0x48a PROGRAM!GetItemDescriptionFromServer+0x49c PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127 PROGRAM!CTreeItem::GetDescriptionWorker+0x198 PROGRAM!CTreeItem::GetDescription+0x188 PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90 PROGRAM!CTreeItemWrapper::GetDescription+0x20b PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2 PROGRAM!CItemPropertiesMenu::Execute+0xe7 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!CCompoundMenu::ExecuteDirect+0x308 PROGRAM!CCompoundMenu::Execute+0xf4 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!InvokeViaContextMenu+0x11c PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97 PROGRAM!CToolbarSite::SendToToolbar+0x66 PROGRAM!CToolbarSite::OnAction+0x1ff PROGRAM!CToolbarSite::HandleMessage+0xaa PROGRAM!CSite::HandleMessage+0x61 PROGRAM!CMainWindow::WindowProc+0xc92 PROGRAM!CWindow::WindowProc+0x91 USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CReBar::_WndProc+0x1b5 comctl32!CReBar::s_WndProc+0x4a USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CToolbar::TBOnLButtonUp+0x181 comctl32!CToolbar::ToolbarWndProc+0xed1 comctl32!CToolbar::s_ToolbarWndProc+0xd6 USER32!UserCallWinProcCheckWow+0x1ad USER32!CallWindowProcAorW+0xdb USER32!CallWindowProcW+0x18 comctl32!CallOriginalWndProc+0x1d comctl32!CallNextSubclassProc+0x8d comctl32!DefSubclassProc+0x7c PROGRAM!DefSubclassProc+0x56 PROGRAM!CToolbar::WindowProc+0x142 PROGRAM!CCustomizableToolbar::WindowProc+0xb3 PROGRAM!CWindowSubclass::SubclassWndProc+0xeb comctl32!CallNextSubclassProc+0x8d comctl32!MasterSubclassProc+0xe1 USER32!UserCallWinProcCheckWow+0x1ad USER32!DispatchMessageWorker+0x389 PROGRAM!MsgWaitForCompletion+0xe0 PROGRAM!AsyncFinishCall+0x22 PROGRAM!SynchronousCallService+0x48a PROGRAM!GetItemDescriptionFromServer+0x49c PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127 PROGRAM!CTreeItem::GetDescriptionWorker+0x198 PROGRAM!CTreeItem::GetDescription+0x188 PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90 PROGRAM!CTreeItemWrapper::GetDescription+0x20b PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2 PROGRAM!CItemPropertiesMenu::Execute+0xe7 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!CCompoundMenu::ExecuteDirect+0x308 PROGRAM!CCompoundMenu::Execute+0xf4 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!InvokeViaContextMenu+0x11c PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97

    Once you get past the initial turmoil, the stack trace settles down into a nice repeating pattern consisting of the same 53 functions over and over again. Identifying the start of the repeating pattern isn't important, because the starting point will be different for each crash, in the same way that the precise note which exceeds your singing range varies from crash to crash. When I go looking for the repeating pattern, I ignore the first hundred or so functions in the stack trace. That usually takes me well past the momentary weirdness at the top of the stack and dumps me straight into the repeating part.

    Once you've identified the repeating part, pick a function from it that is somewhat unusual and search for it in your defect database. In our example, SendMessageW would probably be a bad choice, since sending a message is a pretty common operation in most Windows programs. I would go with CTreeItem::GetDescriptionFromServer.

    It so happens that a query for all defects that involve the function CTreeItem::GetDescriptionFromServer turned up the following stack trace:

    ntdll!RtlpAllocateHeap+0x33 ntdll!RtlAllocateHeap+0x151 ntdll!RtlDebugAllocateHeap+0xcd ntdll!RtlpAllocateHeap+0x39592 ntdll!RtlAllocateHeap+0x151 PROGRAM!CopyString+0x24 PROGRAM!CopyDirectoryName+0x11 PROGRAM!GetItemLongPath+0xe PROGRAM!CPrivateNode::GetSourceLongPath+0x6d PROGRAM!CPrivateNode::GetSourcePath+0x57 PROGRAM!CPrivateNode::GetSource+0x123 PROGRAM!GetDownloadSource+0x23 PROGRAM!GetCustomizedButtonSource+0xcc PROGRAM!CCustomizableToolbar::IsWarningNeeded+0x69 PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x1b6 PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97 PROGRAM!CToolbarSite::SendToToolbar+0x66 PROGRAM!CToolbarSite::OnAction+0x1ff PROGRAM!CToolbarSite::HandleMessage+0xaa PROGRAM!CSite::HandleMessage+0x61 PROGRAM!CMainWindow::WindowProc+0xc92 PROGRAM!CWindow::WindowProc+0x91 USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CReBar::_WndProc+0x1b5 comctl32!CReBar::s_WndProc+0x4a USER32!UserCallWinProcCheckWow+0x1ad USER32!SendMessageWorker+0x64a USER32!SendMessageW+0x5b comctl32!CToolbar::TBOnLButtonUp+0x181 comctl32!CToolbar::ToolbarWndProc+0xed1 comctl32!CToolbar::s_ToolbarWndProc+0xd6 USER32!UserCallWinProcCheckWow+0x1ad USER32!CallWindowProcAorW+0xdb USER32!CallWindowProcW+0x18 comctl32!CallOriginalWndProc+0x1d comctl32!CallNextSubclassProc+0x8d comctl32!DefSubclassProc+0x7c PROGRAM!DefSubclassProc+0x56 PROGRAM!CToolbar::WindowProc+0x142 PROGRAM!CCustomizableToolbar::WindowProc+0xb3 PROGRAM!CWindowSubclass::SubclassWndProc+0xeb comctl32!CallNextSubclassProc+0x8d comctl32!MasterSubclassProc+0xe1 USER32!UserCallWinProcCheckWow+0x1ad USER32!DispatchMessageWorker+0x389 PROGRAM!MsgWaitForCompletion+0xe0 PROGRAM!AsyncFinishCall+0x22 PROGRAM!SynchronousCallService+0x48a PROGRAM!GetItemDescriptionFromServer+0x49c PROGRAM!CTreeItem::GetDescriptionFromServer+0x15f PROGRAM!CTreeItem::TryGetDescriptionFromServer+0x127 PROGRAM!CTreeItem::GetDescriptionWorker+0x198 PROGRAM!CTreeItem::GetDescription+0x188 PROGRAM!CTreeItemWrapper::GetDescriptionWorker+0x90 PROGRAM!CTreeItemWrapper::GetDescription+0x20b PROGRAM!CItemPropertiesMenu::RefreshProperties+0xf2 PROGRAM!CItemPropertiesMenu::Execute+0xe7 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!CCompoundMenu::ExecuteDirect+0x308 PROGRAM!CCompoundMenu::Execute+0xf4 PROGRAM!CompoundMenu_DispatchCommand+0x108 PROGRAM!CItemMenu::Execute+0x29c PROGRAM!InvokeViaContextMenu+0x11c PROGRAM!CCustomizableToolbar::TrySimpleCommand+0x23e PROGRAM!CCustomizableToolbar::OnCommand+0x102
    PROGRAM!CToolbar::OnAction+0x97

    Yup, there's that recurring 53-function cycle again. The initial part of the stack trace is different, of course, but the important part is right there. This is another manifestation of the same underlying bug.

    Moral of the story: When studying a stack overflow, the stragglers at the top of the stack are the least important functions. You really want the meaty bit in the middle.

  • The Old New Thing

    Microspeak: Learnings

    • 22 Comments

    If things you teach are teachings, then things you learn must be learnings, right? Good Microspeak citations for this word are hard to find since the word is rarely used in a sentence; it's just a heading in a slide presentation. I found dozens of presentations that had a slide titled Learnings from XYZ, or, for those who want to sound really fancy, Key Learnings from XYZ, but very few actual sentences. Here are two:

    Alice will send an email to Bob with regards to any learnings from the XYZ program being incented to do ABC.
    What are our key learnings for this project?

    There's that word key again, along with a surprise appearance of incent.

    And that second citation barely counts since it's really just a prose version of the slide heading!

  • The Old New Thing

    Not my finest hour: Misreading a product label

    • 26 Comments

    I had finished some store-bought soup and thought to myself, "That was a pretty good soup. What brand was it? I'll buy it again."

    I went to my recycle bin to fish out the aseptic box that the soup came in, and looked for the brand name. And I found it: dnos.

    I thought to myself, "That's a strange name for a soup company."

  • The Old New Thing

    Even if you have code to handle a message, you're allowed to call DefWindowProc, because you were doing that anyway after all

    • 16 Comments

    Just because you write case WM_SOMETHING: doesn't mean that you have to handle all possible parameters for the WM_SOMETHING message. You're still allowed to call the DefWindowProc function. After all, that's what you did when you didn't have a case WM_SOMETHING: statement in the first place.

    switch (uMsg) {
    case WM_CHAR:
        OnChar(...);
        return 0;
    
    default:
        return DefWindowProc(...);
    }
    

    The above code fragment doesn't handle the WM_SOMETHING message at all. Suppose the WM_SOMETHING message uses the wParam parameter to specify what type of something occurred, and you only want to override the default processing in the case where wParam has the value of 4. What do you do with the other values?

    switch (uMsg) {
    case WM_CHAR:
        OnChar(...);
        return 0;
    
    case WM_SOMETHING:
        if (wParam == 4) { DoSomething4(...); }
        else ... ????? ...
        return 0;
    
    default:
        return DefWindowProc(...);
    }
    

    If the value is 4, then you do your special "something 4" processing, but what about all the other values? How do you handle them?

    Well, think about it: How did you handle them before? The original code, before you added a WM_SOMETHING handler, was equivalent to this:

    switch (uMsg) {
    case WM_CHAR:
        OnChar(...);
        return 0;
    
    case WM_SOMETHING:
        return DefWindowProc(...);
    
    default:
        return DefWindowProc(...);
    }
    

    In the original code, since there was no explicit handler for the WM_SOMETHING message, control is transferred to the default case handler, which just calls the DefWindowProc function. If you really want to, you can expand the case out a bit more:

    switch (uMsg) {
    case WM_CHAR:
        OnChar(...);
        return 0;
    
    case WM_SOMETHING:
        if (wParam == 4) return DefWindowProc(...);
        else return DefWindowProc(...);
    
    default:
        return DefWindowProc(...);
    }
    

    Because if the wParam is 4, the original code just called DefWindowProc. And if the wParam was something other than 4, the original code still just called DefWindowProc.

    Of course, I expanded the block in precisely this way so it matches up with the case we started writing when we decided to handle the WM_SOMETHING method. Written out this way, it becomes obvious what to write for the question marks.

    switch (uMsg) {
    case WM_CHAR:
        OnChar(...);
        return 0;
    
    case WM_SOMETHING:
        if (wParam == 4) { DoSomething4(...); }
        else return DefWindowProc(...);
        return 0;
    
    default:
        return DefWindowProc(...);
    }
    

    Just because you have a case WM_SOMETHING statement doesn't mean you have to handle all the cases; you can still call DefWindowProc for the cases you don't want to handle.

    Armed with this information, you can help commenter Norman Diamond handle the VK_F10 key in his WM_SYSKEYDOWN message handler without having to "start handling a bunch of keys that really are system keys, that I didn't want to bother with."

Page 3 of 4 (34 items) 1234