June, 2012

  • The Old New Thing

    Don't be helpless: What might be the reason for a "Path not found" error?

    • 74 Comments

    Internally at Microsoft, we have a programmer's tool which I will call Program Q. On the peer-to-peer mailing list for Program Q, somebody asked the following question:

    When I try to do a q edit template template_name, instead of opening an editor window where I can modify the template, I get the following error:

    Error opening for write:
    C:\Users\Waldo\AppData\Local\Temp\2\t144t4.tmp
    The system cannot find the path specified.
    

    Can you help resolve this error?

    Okay, there is already everything you need in the error message. The program even converted the error number to error text for you. You just have to read it and think about what it's telling you.

    The file is C:\Users\Waldo\AppData\Local\Temp\2\t144t4.tmp. Therefore the path is C:\Users\Waldo\AppData\Local\Temp\2. I leave you to determine the next step in the investigation.

    That was apparently not enough of a nudge in the right direction.

    While the error message does say "The system cannot find the path specified," the fact remains that I did not specify a path at all. The path in the error message is completely unknown to me. I could try to navigate to that path in Windows Explorer, but I doubt that this has anything to do with resolving the problem.

    Normally, I get an editor window that lets me edit the template, but instead I get this strange error message which I've never seen before.

    I did not try to navigate to the path mentioned in the error message simply because the mentioned Temp folder C:\Users\Waldo\AppData\Local\Temp is completely empty!

    The helplessness is so thick you can cut it with a knife! I also find it astonishing that the person thinks that verifying whether the path can be found is totally unrelated to resolving a "Path not found" error.

    Don't forget, this is a programmer's tool. One should assume that the people who use it have some level of technical skill!

    Okay, first we have a "Path not found" error, and there is a fully-qualified file name whose path couldn't be found. First thing to check is whether the path really exists. From the most recent reply, one can see that the answer is "No, it does not exist." The 2 subdirectory is missing from the Temp directory.

    Okay, so we verified that the error message is valid. The next thing to determine is where the program got this path from. The person already recognized that it was the Temp directory, and it shouldn't be a huge deductive leap to determine that the path probably came from the TEMP or TMP environment variable.

    The observation that the Temp directory is completely empty suggests that the person, in an obsessive-compulsive fit, deleted everything from the Temp directory, including the 2 subdirectory. Too bad that their TEMP environment variable still contained a reference to it.

    As a result, any program that wants to create a temporary file will try to create it in a directory that doesn't exist. Result: "Path not found."

    The fix: Re-create the 2 subdirectory that you mistakenly deleted. (And yes, this fixed the problem.)

    It somehow seemed completely surprising to this person that a "Path not found" error could possibly mean that a path couldn't be found.

  • The Old New Thing

    You still need the "safe" functions even if you check string lengths ahead of time

    • 69 Comments

    Commenter POKE53280,0 claims, "If one validates parameters before using string functions (which quality programmers should do), the 'safe' functions have no reason to exist."

    Consider the following function:

    int SomeFunction(const char *s)
    {
      char buffer[256];
      if (strlen(s) ≥ 256) return ERR;
      strcpy(buffer, s);
      ...
    }
    

    What could possibly go wrong? You check the length of the string, and if it doesn't fit in the buffer, then you reject it. Therefore, you claim, the strcpy is safe.

    What could possibly go wrong is that the length of the string can change between the time you check it and the time you use it.

    char attack[512] = "special string designed to trigger a "
                       "buffer overflow and attack your machine. [...]";
    
    void Thread1()
    {
     char c = attack[256];
     while (true) {
      attack[256] ^= c;
     }
    }
    
    void Thread2()
    {
     while (true) {
      SomeFunction(attack);
     }
    }
    

    The first thread changes the length of the string rapidly between 255 and 511, between a string that passes validation and a string that doesn't, and more specifically between a string that passes validation and a string that, if it snuck through validation, would pwn the machine.

    The second thread keeps handing this string to Some­Function. Eventually, the following race condition will be hit:

    • Thread 1 changes the length to 255.
    • Thread 2 checks the length and when it reaches attack[256], it reads zero and concludes that the string length is less than 256.
    • Thread 1 changes the length to 511.
    • Thread 2 copies the string and when it reaches attack[256], it reads nonzero and keeps copying, thereby overflowing its buffer.

    Oops, you just fell victim to the Time-of-check-to-time-of-use attack (commonly abbreviated TOCTTOU).

    Now, the code above as-written is not technically a vulnerability because you haven't crossed a security boundary. The attack code and the vulnerable code are running under the same security context. To make this a true vulnerability, you need to have the attack code running in a lower security context from the vulnerable code. For example, if the threads were running user-mode code and Some­Function is a kernel-mode function, then you have a real vulnerability. Of course, if Some­Function were at the boundary between user-mode and kernel-mode, then it has other things it needs to do, like verify that the memory is in fact readable by the process.

    A more interesting case where you cross a security boundary is if the two threads are running code driven from an untrusted source; for example, they might be threads in a script interpreter, and the toggling of attack[256] is being done by a function on a Web page.

    // this code is in some imaginary scripting language
    
    var attack = new string("...");
    procedure Thread1()
    {
     var c = attack[256];
     while (true) attack[256] ^= c;
    }
    
    handler OnClick()
    {
     new backgroundTask(Thread1);
     while (true) foo(attack);
    }
    

    When the user clicks on the button, the script interpret creates a background thread and starts toggling the length of the string under the instructions of the script. Meanwhile, the main thread calls foo repeatedly. And suppose the interpreter's implementation of foo goes like this:

    void interpret_foo(const function_args& args)
    {
     if (args.GetLength() != 1) wna("foo");
     if (args.GetArgType(0) != V_STRING) wta("foo", 0, V_STRING);
     char *s = args.PinArgString(0);
     SomeFunction(s);
     args.ReleasePin(0);
    }
    

    The script interpreter has kindly converted the script code into the equivalent native code, and now you have a problem. Assuming the user doesn't get impatient and click "Stop script", the script will eventually hit the race condition and cause a buffer overflow in Some­Function.

    And then you get to scramble a security hotfix.

  • The Old New Thing

    Now that Windows makes it harder for your program to block shutdown, how do you block shutdown?

    • 62 Comments

    Up until Windows XP, applications could intercept the WM_QUERY­END­SESSION message and tell Windows, "No, don't shut down." If they were polite about it, they would also inform the user which application blocked system shutdown and why. And if they were really polite about it, they would even provide a way for the user to say, "I don't care; shut down anyway."

    As I noted some time ago, Windows Vista made it harder for applications to block shutdown. Applications are given two seconds to clean up, and then it's game over.

    Okay, now the game of walls and ladders continues. The power management folks created an escape hatch for applications which are doing things like burning a CD or controlling an industrial lathe, where shutting down the machine may not be in the user's best interest. (The user ends up with a coaster or a factory on fire.) But since they created the escape hatch, they get to control the keys to the hatch, too.

    The Shutdown­Block­Reason­Create function lets you register your application window with a custom message that is displayed to the user when they try to shut down the computer. When the danger-time is over, you call Shutdown­Block­Reason­Destroy to say that the coast is clear and shutdown is once again permitted.

    Mind you, these blocks are merely advisory. If users really want to create a coaster or burn down their factory, they can click Force shut down. One nice thing about making Windows responsible for the warning message is that if multiple applications want to block shutdown, all of them can be displayed in a single dialog, and the user only needs to click Force shut down once.

    Further guidance on system shutdown and the use of these functions can be found in the Application Shutdown Changes in Windows Vista document, which was the source material for most of this blog entry.

  • The Old New Thing

    Why do you have to wait for Windows Error Reporting to check for solutions before it restarts the application?

    • 47 Comments

    Leo Davidson wonders why you have to wait for Windows Error Reporting to check for solutions before it restarts the application. Why not do the two in parallel?

    Well, for one thing, when the application restarts, it might freak out if it sees a dead copy of itself. I know for sure that I would freak out if I woke up one morning and saw my own dead body lying next to me.

    While Windows Error Reporting is checking for a solution, it still has access to the carcass of the crashed application, because it may need to refer to it in order to answer follow-up questions from the server. ("Hey, was version 3.14 of PI.DLL loaded into the process when it crashed? If so, then I may have an idea what went wrong.") And so that, if you ask it to submit the crash to Microsoft, it can grab the information it needs in order to generate the crash report.

    Now suppose you start up a new copy of the application right away. If the application is a single-instance program, it will look around for another copy of itself, and hey look, it'll find its own lifeless body in the middle of the computer version of an autopsy. It will then try to send messages to the dead program, saying, "Hey, the user wants to open document X; could you do that for me?" And it won't get a response because, well, the program is dead. It's never going to respond.

    Some programs don't even try to pass information along. They just find the existing copy of the program, and call Set­Foreground­Window on its main window, thereby switching to it. Of course, what they tried to do was switched to a crashed program.

    Even worse, what if the second copy of the program tries to extract information from the existing copy of itself? If the existing copy crashed, it's highly likely that the crash was caused by corruption in the program's internal data structures. When the second copy tries to extract the corrupted data, it may itself crash. Immediately launching the replacement program creates a very quickly-growing pile of dead programs, and your screen basically gets spammed with Windows Error Reporting dialogs faster than you can click OK.

    The crashed program has effectively launched a denial of service attack against itself.

    Before trying to start the program again, Windows makes sure that the previous copy has received a proper burial. Because few programs are prepared to see their own cadaver.

    Bonus chatter: Another common scenario is that the program crashes at startup. Automatically restarting the program would just launch another copy that immediately crashes. Again, you get into the situation where you get a dozen copies of the program launched per second, all of which immediately crash.

  • The Old New Thing

    Globalization quiz: In honor of, well, that's part of the quiz

    • 46 Comments
    The Corporate Citizenship Tools; Microsoft Local Language Program Web site contains a map of the world, coded by region. There was a bug on the map. See if you can spot it:

      Asia
      Europe
      Middle East & Africa
      North America
      South America
      South Pacific

    After I pointed out the error, they fixed the map on their Web page, so no fair clicking through to the Local Language Program Web page and comparing the pictures!

    Non-useful hint: I chose the publication date of this quiz in honor of the answer.

    Bonus chatter: Inside the answer.

  • The Old New Thing

    How did my hard drive turn into a TARDIS?

    • 44 Comments

    A customer observed that the entry for a network drive looked liked this in My Computer, well, except that there was a network drive icon instead of ASCII art.

    O Public (\\server) (S:)
     
     
    3.81TB free of 2.5GB

    How is it possible for a 2.5GB drive to have 3.81TB free?

    While there have certainly been examples of Explorer showing confusing values the reason for the strange results was, at least this time, not Explorer's fault.

    This particular network drive actually reported (via Get­Disk­Free­Space­Ex) that it had more free space than drive space. Explorer is dutifully reporting the information it was given, because it doesn't try to second-guess the file system. If a network drive wants to report that it is a TARDIS, then it's a TARDIS.

  • The Old New Thing

    How does Explorer determine the delay between clicking on an item and initiating an edit?

    • 34 Comments

    Ian Boyd wants to know why the specific value of 500ms was chosen as the edit delay in Windows Explorer.

    Because it's your double-click time.

    Since the double-click action (execute) is not an extension of the single-click action (edit), Explorer (and more generally, list view) waits for the double-click timeout before entering edit mode so it can tell whether that first click was really a single-click on its own or a single-click on the way to a double-click.

    If the timeout were shorter than the double-click time, then double-clicking an item would end up having the first click trigger edit mode and the second click selecting text inside the editor.

    (If the timeout were longer, then everything would still work, but you would just have to wait longer.)

    Ian says, "Through my testing it does not appear linked to configurable double-click timeout." My guess is that Ian changed the double-click timeout not by calling Set­Double­Click­Time but by whacking a registry value directly. The values in the registry are loaded and cached at logon; you can update them all you want at runtime, nobody will care.

  • The Old New Thing

    Eventually the window manager simply says that enough is enough

    • 34 Comments

    Many window manager operations are recursive, and eventually the window manager will simply say that enough is enough when the recursion goes too deep. We've seen this when you nest windows more than 50 levels deep or nest menus more than 25 levels deep, for example. (Note also that these limits may change in the future, so don't rely on being able to walk right up to the edge. Those values came from 32-bit Windows XP; I don't know if the limits have been dropped even further in subsequent versions of Windows, and I'm not sufficiently motivated to find out.)

    A customer had some code which installed a message hook, and they found that the message hook was not called consistently. They tracked it down to another component in their application, and that component also installed a message hook. The contact actually came from the developer who maintained the other component: Did I write my message hook incorrectly? Am I accidentally messing up other message hooks in the system?

    The developer included their hook-management code, and it didn't look obviously wrong. All code paths eventually called Call­Next­Hook­Ex, so there shouldn't be any hook-loss.

    The customer was kind enough to include a copy of their program with instructions on how to trigger the problem, and stepping through the hook code quickly revealed the source of the problem. Maybe you can see it too. Here's a stack trace when the end of the hook chain is reached:

    ChildEBP RetAddr
    0011cdc4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011cdf8 768231eb user32!DispatchHookA+0x104 
    0011ce38 76824260 user32!CallHookWithSEH+0x21 
    0011ce6c 773e642e user32!__fnHkINLPMSG+0x71 
    0011ceb0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011ceb4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011ced8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011cf14 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011cf48 768231eb user32!DispatchHookA+0x104 
    0011cf88 76824260 user32!CallHookWithSEH+0x21 
    0011cfbc 773e642e user32!__fnHkINLPMSG+0x71 
    0011d000 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011d004 76844787 user32!NtUserCallNextHookEx+0xc 
    0011d028 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011d064 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011d098 768231eb user32!DispatchHookA+0x104 
    0011d0d8 76824260 user32!CallHookWithSEH+0x21 
    0011d10c 773e642e user32!__fnHkINLPMSG+0x71 
    0011d150 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011d154 76844787 user32!NtUserCallNextHookEx+0xc 
    0011d178 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011d1b4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011d1e8 768231eb user32!DispatchHookA+0x104 
    0011d228 76824260 user32!CallHookWithSEH+0x21 
    0011d25c 773e642e user32!__fnHkINLPMSG+0x71 
    0011d2a0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011d2a4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011d2c8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011d304 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011d338 768231eb user32!DispatchHookA+0x104 
    0011d378 76824260 user32!CallHookWithSEH+0x21 
    0011d3ac 773e642e user32!__fnHkINLPMSG+0x71 
    0011d3f0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011d3f4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011d418 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011d454 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011d488 768231eb user32!DispatchHookA+0x104 
    0011d4c8 76824260 user32!CallHookWithSEH+0x21 
    0011d4fc 773e642e user32!__fnHkINLPMSG+0x71 
    0011d540 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011d544 76844787 user32!NtUserCallNextHookEx+0xc 
    0011d568 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011d5a4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011d5d8 768231eb user32!DispatchHookA+0x104 
    0011d618 76824260 user32!CallHookWithSEH+0x21 
    0011d64c 773e642e user32!__fnHkINLPMSG+0x71 
    0011d690 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011d694 76844787 user32!NtUserCallNextHookEx+0xc 
    0011d6b8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011d6f4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011d728 768231eb user32!DispatchHookA+0x104 
    0011d768 76824260 user32!CallHookWithSEH+0x21 
    0011d79c 773e642e user32!__fnHkINLPMSG+0x71 
    0011d7e0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011d7e4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011d808 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011d844 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011d878 768231eb user32!DispatchHookA+0x104 
    0011d8b8 76824260 user32!CallHookWithSEH+0x21 
    0011d8ec 773e642e user32!__fnHkINLPMSG+0x71 
    0011d930 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011d934 76844787 user32!NtUserCallNextHookEx+0xc 
    0011d958 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011d994 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011d9c8 768231eb user32!DispatchHookA+0x104 
    0011da08 76824260 user32!CallHookWithSEH+0x21 
    0011da3c 773e642e user32!__fnHkINLPMSG+0x71 
    0011da80 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011da84 76844787 user32!NtUserCallNextHookEx+0xc 
    0011daa8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011dae4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011db18 768231eb user32!DispatchHookA+0x104 
    0011db58 76824260 user32!CallHookWithSEH+0x21 
    0011db8c 773e642e user32!__fnHkINLPMSG+0x71 
    0011dbd0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011dbd4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011dbf8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011dc34 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011dc68 768231eb user32!DispatchHookA+0x104 
    0011dca8 76824260 user32!CallHookWithSEH+0x21 
    0011dcdc 773e642e user32!__fnHkINLPMSG+0x71 
    0011dd20 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011dd24 76844787 user32!NtUserCallNextHookEx+0xc 
    0011dd48 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011dd84 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011ddb8 768231eb user32!DispatchHookA+0x104 
    0011ddf8 76824260 user32!CallHookWithSEH+0x21 
    0011de2c 773e642e user32!__fnHkINLPMSG+0x71 
    0011de70 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011de74 76844787 user32!NtUserCallNextHookEx+0xc 
    0011de98 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011ded4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011df08 768231eb user32!DispatchHookA+0x104 
    0011df48 76824260 user32!CallHookWithSEH+0x21 
    0011df7c 773e642e user32!__fnHkINLPMSG+0x71 
    0011dfc0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011dfc4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011dfe8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011e024 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011e058 768231eb user32!DispatchHookA+0x104 
    0011e098 76824260 user32!CallHookWithSEH+0x21 
    0011e0cc 773e642e user32!__fnHkINLPMSG+0x71 
    0011e110 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011e114 76844787 user32!NtUserCallNextHookEx+0xc 
    0011e138 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011e174 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011e1a8 768231eb user32!DispatchHookA+0x104 
    0011e1e8 76824260 user32!CallHookWithSEH+0x21 
    0011e21c 773e642e user32!__fnHkINLPMSG+0x71 
    0011e260 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011e264 76844787 user32!NtUserCallNextHookEx+0xc 
    0011e288 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011e2c4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011e2f8 768231eb user32!DispatchHookA+0x104 
    0011e338 76824260 user32!CallHookWithSEH+0x21 
    0011e36c 773e642e user32!__fnHkINLPMSG+0x71 
    0011e3b0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011e3b4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011e3d8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011e414 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011e448 768231eb user32!DispatchHookA+0x104 
    0011e488 76824260 user32!CallHookWithSEH+0x21 
    0011e4bc 773e642e user32!__fnHkINLPMSG+0x71 
    0011e500 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011e504 76844787 user32!NtUserCallNextHookEx+0xc 
    0011e528 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011e564 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011e598 768231eb user32!DispatchHookA+0x104 
    0011e5d8 76824260 user32!CallHookWithSEH+0x21 
    0011e60c 773e642e user32!__fnHkINLPMSG+0x71 
    0011e650 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011e654 76844787 user32!NtUserCallNextHookEx+0xc 
    0011e678 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011e6b4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011e6e8 768231eb user32!DispatchHookA+0x104 
    0011e728 76824260 user32!CallHookWithSEH+0x21 
    0011e75c 773e642e user32!__fnHkINLPMSG+0x71 
    0011e7a0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011e7a4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011e7c8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011e804 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011e838 768231eb user32!DispatchHookA+0x104 
    0011e878 76824260 user32!CallHookWithSEH+0x21 
    0011e8ac 773e642e user32!__fnHkINLPMSG+0x71 
    0011e8f0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011e8f4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011e918 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011e954 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011e988 768231eb user32!DispatchHookA+0x104 
    0011e9c8 76824260 user32!CallHookWithSEH+0x21 
    0011e9fc 773e642e user32!__fnHkINLPMSG+0x71 
    0011ea40 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011ea44 76844787 user32!NtUserCallNextHookEx+0xc 
    0011ea68 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011eaa4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011ead8 768231eb user32!DispatchHookA+0x104 
    0011eb18 76824260 user32!CallHookWithSEH+0x21 
    0011eb4c 773e642e user32!__fnHkINLPMSG+0x71 
    0011eb90 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011eb94 76844787 user32!NtUserCallNextHookEx+0xc 
    0011ebb8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011ebf4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011ec28 768231eb user32!DispatchHookA+0x104 
    0011ec68 76824260 user32!CallHookWithSEH+0x21 
    0011ec9c 773e642e user32!__fnHkINLPMSG+0x71 
    0011ece0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011ece4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011ed08 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011ed44 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011ed78 768231eb user32!DispatchHookA+0x104 
    0011edb8 76824260 user32!CallHookWithSEH+0x21 
    0011edec 773e642e user32!__fnHkINLPMSG+0x71 
    0011ee30 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011ee34 76844787 user32!NtUserCallNextHookEx+0xc 
    0011ee58 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011ee94 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011eec8 768231eb user32!DispatchHookA+0x104 
    0011ef08 76824260 user32!CallHookWithSEH+0x21 
    0011ef3c 773e642e user32!__fnHkINLPMSG+0x71 
    0011ef80 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011ef84 76844787 user32!NtUserCallNextHookEx+0xc 
    0011efa8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011efe4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011f018 768231eb user32!DispatchHookA+0x104 
    0011f058 76824260 user32!CallHookWithSEH+0x21 
    0011f08c 773e642e user32!__fnHkINLPMSG+0x71 
    0011f0d0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011f0d4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011f0f8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011f134 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011f168 768231eb user32!DispatchHookA+0x104 
    0011f1a8 76824260 user32!CallHookWithSEH+0x21 
    0011f1dc 773e642e user32!__fnHkINLPMSG+0x71 
    0011f220 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011f224 76844787 user32!NtUserCallNextHookEx+0xc 
    0011f248 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011f284 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011f2b8 768231eb user32!DispatchHookA+0x104 
    0011f2f8 76824260 user32!CallHookWithSEH+0x21 
    0011f32c 773e642e user32!__fnHkINLPMSG+0x71 
    0011f370 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011f374 76844787 user32!NtUserCallNextHookEx+0xc 
    0011f398 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011f3d4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011f408 768231eb user32!DispatchHookA+0x104 
    0011f448 76824260 user32!CallHookWithSEH+0x21 
    0011f47c 773e642e user32!__fnHkINLPMSG+0x71 
    0011f4c0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011f4c4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011f4e8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011f524 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011f558 768231eb user32!DispatchHookA+0x104 
    0011f598 76824260 user32!CallHookWithSEH+0x21 
    0011f5cc 773e642e user32!__fnHkINLPMSG+0x71 
    0011f610 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011f614 76844787 user32!NtUserCallNextHookEx+0xc 
    0011f638 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011f674 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011f6a8 768231eb user32!DispatchHookA+0x104 
    0011f6e8 76824260 user32!CallHookWithSEH+0x21 
    0011f71c 773e642e user32!__fnHkINLPMSG+0x71 
    0011f760 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011f764 76844787 user32!NtUserCallNextHookEx+0xc 
    0011f788 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011f7c4 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011f7f8 768231eb user32!DispatchHookA+0x104 
    0011f838 76824260 user32!CallHookWithSEH+0x21 
    0011f86c 773e642e user32!__fnHkINLPMSG+0x71 
    0011f8b0 768447aa ntdll!KiUserCallbackDispatcher+0x2e 
    0011f8b4 76844787 user32!NtUserCallNextHookEx+0xc 
    0011f8d8 6fbdb1e0 user32!CallNextHookEx+0x71 
    0011f914 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011f948 768231eb user32!DispatchHookA+0x104 
    0011f988 76824260 user32!CallHookWithSEH+0x21 
    0011f9bc 773e642e user32!__fnHkINLPMSG+0x71 
    0011f9d0 00030000 ntdll!KiUserCallbackDispatcher+0x2e 
    0011fa28 6fbdb1e0 0x30000
    0011fa64 16846e6e contoso!ContosoWindowEventHook+0x11b 
    0011fa98 768231eb user32!DispatchHookA+0x104 
    0011fad8 76824260 user32!CallHookWithSEH+0x21 
    0011fb0c 773e642e user32!__fnHkINLPMSG+0x71 
    0011fb20 00030000 ntdll!KiUserCallbackDispatcher+0x2e 
    0011fb7c 768292a9 0x30000
    0011fba8 6b2ce010 user32!PeekMessageW+0xfb 
    

    As you can see, a third component in their application installed at least thirty-five hooks. After the thirty-fifth hook, the window manager stepped in and said, "That's it, I'm cutting you off."

    Now, the limit isn't actually thirty-five. The window manager keeps dispatching hooks until the kernel stack starts running low, and then it gives up. This happens with a lot of recursive algorithms: The window manager plays the game for a while, but when it looks like you're about to bluescreen, it stops short and says, "Okay, I'm not going to do that any more."

    The developers now got to take their problem to the developer responsible for the Contoso component, and figure out why it's installing so many hooks. Maybe that component could try to consolidate identical hooks. Or maybe it's a leak. They never did report back (not that I was expecting them to).

    Bonus chatter: Why is hook dispatch done recursively? Shouldn't it be done iteratively?

    Remember that windows hooks came from 16-bit Windows, where economy was paramount. And the existing Call­Next­Hook pattern was preserved, though it changed to Call­Next­Hook­Ex, where you pass the hook handle directly instead of its address. One advantage of the Call­Next­Hook­Ex model over an iterative model is that explicitly forwarding to the previous hook lets you do work on the back end. I.e., you can forward the call down the chain, and then do something when control returns. This is the sort of thing you probably use a lot when you subclass a window or override a method in a derived class and call the base class from your override.

  • The Old New Thing

    Microspeak: Turds

    • 29 Comments

    In Microspeak, a turd is a graphics glitch which results in old pixels appearing on the screen when they shouldn't. Perhaps they are not being erased properly, or instead of being erased, they move to an unwanted location. The source of the problem could be failing to mark a region for redrawing after something changed, or it could be that the drawing code ran into a problem and failed to draw over the old pixels. The problem might be that the code whose job it is to remove the object from the screen ran into a problem and didn't actually remove it, resulting in an on-screen object that nobody really is keeping track of. Whatever the reason, these "junk pixels" go by the name turds.

    For non-English speakers: turd is a boorish term for dung.

    The term turds are more generally applied to objects that don't serve any purpose but never got cleaned up. For example, they might be files and registry keys that didn't get deleted when you uninstalled an application, or user accounts for employees who left the company years ago, or C++ objects that some code forgot to delete. If you upgrade your dogfood machine from build to build, there may be files left over from earlier builds that aren't being used by the product any more. A build system may create temporary files that only get erased up when you do a "make clean".

    Note that this particular term turd is not formal Microspeak. You won't find it in a specifications document. But you may encounter it in a bug report or hear it in a casual discussion. Personally, I don't use the term. For the graphics glitches, I prefer to use the word artifact. The leftover stuff that didn't get cleaned up I simply call dirt.

    Bonus chatter: There was one project that actually tried to introduce the word turd as a formal technical term:

    If a transaction has been superseded by another transaction, a marker is left behind in the original transaction record which redirects the resolver to the new transaction. This marker is known as the Transaction Under Redirection Descriptor (TURD).

    I bet the developer who came up with that spent way too much time making up turd-related jokes in other parts of the specification.

  • The Old New Thing

    Thanks for reminding me what to do when the elevator is out of order

    • 27 Comments

    Every few years, the building maintenance people have to perform tests on the elevators to ensure they meet safety regulations. And the real estate department sends out the usual notice informing the building occupants that the elevators in the building will be taken out of service at various times during the day. They were kind enough to include the following advice:

    If an elevator is non-responsive and/or has out of order signage posted, please use another available elevator.

    One of my colleagues sarcastically remarked, "Wow, thank goodness they sent that email. I'd have no idea what to do had I seen a non-responsive elevator with an 'out of order' sign posted on it."

Page 1 of 3 (25 items) 123