January, 2010

  • The Old New Thing

    How do I suppress full window drag/resize for just one window?

    • 12 Comments

    A customer asked,

    Is there a way to turn off Full Window Drag on a single window? I have a resizable control that I would like not update itself while resizing.

    It so happens that I wrote a sample program ages ago to illustrate how to do this. You can find it in the Platform SDK under winui\fulldrag. The source code is also reproduced in this Knowledge Base article.

    In addition to deferring painting, you may also want to defer layout if layout is expensive for your window.

  • The Old New Thing

    Microsoft phenomenon: The annual award that winds up being awarded only once

    • 19 Comments

    The Grammy Awards will be handed out this upcoming weekend, an annual award that seems to have survived.

    A not uncommon phenomenon at Microsoft is the annual award that winds up being awarded only once. Because all the excitement is in the announcement, not in the actual award.

    Every year, we want to uniquely call out and recognize a set of people. I'm proud to kick off the XYZ Awards, which we will be given every year, starting this year, which recognize employees who best represent ABC and DEF.

    The XYZ Awards were indeed handed out that first year with great pomp and circumstance.

    And were never heard from again.

    This is a special case of the more general phenomenon of the introduction of some undertaking to great fanfare, only to have it quietly fade away into obscurity without any formal announcement that it had ended. One might cynically observe that the likelihood of this happening to your project increases the more dramatically it is introduced. If it's just called a program, then it might survive. If it's called an initiative, then you might want to hold off on ordering new business cards for a while. And if it's called a bold new initiative, then you'll want to spend some time freshening your résumé, because your project is doomed.

    Update since people seem to be missing the point: I'm not talking about marketing campaigns. Those are meant to die out eventually. I'm talking about stuff like The Council for Programming Excellence (which meets only once) or The DTI Program (which has a kickoff meeting and then nothing).

  • The Old New Thing

    What idiot would hard-code the path to Notepad?

    • 46 Comments

    There seemed to be a great deal of disbelief that anybody would hard-code the path to Notepad.

    Here's one example and here's another.

    There's a large class of problems that go like this:

    I'm running Program X, and when I tell it to view the error log, I get this error message: CreateProcess of "C:\Windows\Notepad.exe errorlog.txt" failed: error 2: The system cannot find the file specified. What is wrong and how do I fix it?

    Obviously, the file C:\Windows\Notepad.exe is missing. But how can that be? Well, Windows Server 2008 bit the bullet and removed one of the copies of Notepad. Once you learn this, troubleshooting the above problem becomes a simple exercise in psychic debugging.

    My psychic powers tell me that you're running Windows Server 2008. The Notepad program no longer exists in the Windows directory; it's now in the system directory. Find the setting for your program that lets you change the program used for viewing error logs and tell it to use C:\Windows\System32\Notepad.exe.

    Of course, this tip works only if the program permits you to change the program used for viewing error logs. If they hard-code the path, then you'll have to find some other workaround. (For example, you might try using the CorrectFilePaths shim.)

  • The Old New Thing

    If you are trying to understand an error, you may want to look up the error code to see what it means instead of just shrugging

    • 64 Comments

    A customer had a debug trace log and needed some help interpreting it. The trace log was generated by an operating system component, but the details aren't important to the story.

    I've attached the log file. I think the following may be part of the problem.

    [07/17/2005:18:31:19] Creating process D:\Foo\bar\blaz.exe
    [07/17/2005:18:31:19] CreateProcess failed with error 2
    

    Any ideas?

    Thanks,
    Bob Smith
    Senior Test Engineer
    Tailspin Toys

    What struck me is that Bob is proud of the fact that he's a Senior Test Engineer, perhaps because it makes him think that we will take him more seriously because he has some awesome title.

    But apparently a Senior Test Engineer doesn't know what error 2 is. There are some error codes that you end up committing to memory because you run into them over and over. Error 32 is ERROR_SHARING_VIOLATION, error 3 is ERROR_PATH_NOT_FOUND, and in this case, error 2 is ERROR_FILE_NOT_FOUND.

    And even if Bob didn't have error 2 memorized, he should have known to look it up.

    Error 2 is ERROR_FILE_NOT_FOUND. Does the file D:\Foo\bar\blaz.exe exist?

    No, it doesn't.

    -Bob

    Bob seems to have shut off his brain and decided to treat troubleshooting not as a collaborative effort but rather as a game of Twenty Questions in which the person with the problem volunteers as little information as possible in order to make things more challenging. I had to give Bob a nudge.

    Can you think of a reason why the system would be looking at D:\Foo\bar\blaz.exe? Where did you expect it to be looking for blaz.exe?

    This managed to wake Bob out of his stupor, and the investigation continued. (And no, I don't remember what the final resolution was. I didn't realize I would have to remember the fine details of this support incident three years later.)

  • The Old New Thing

    Microspeak: Zap

    • 23 Comments

    You may hear an old-timer developer use the verb zap.

    That proposed fix will work. Until everybody gets the fix, they can just zap the assert.

    The verb to zap means to replace a breakpoint instruction with an appropriate number of NOP instructions (effectively ignoring it).

    The name comes from the old Windows 2.x kernel debugger. (Actually, it may be even older, but that's as far back as I was able to trace it.) The Z (zap) command replaces the current instruction with a NOP if it is an int 3 (the x86 single-byte breakpoint instruction), or replaced the previous instruction with NOPs if it is an int 1 (the x86 two-byte breakpoint instruction).

    This operation was quite common back in the days when lots of code was written in assembly language. A technique used by some teams was to insert a hard-coded breakpoint (called a TRAP) into every code path of a function. Here's an example (with comments and other identifying characteristics removed and new ones made up):

    xyz8:   mov     bl,[eax].xyz_State
            cmp     bl,XYZSTATE_IGNORE
            TRAPe
            je      short xyz10     ; ignore this one
            or      bl,bl
            TRAPe
            je      short xyz11     ; end of table
    
            mov     bh,[eax].xyz_Flags
            test    bh,XYZFLAGS_HIDDEN
            TRAPz
            jz      short xyz10     ; skip - item is hidden
            test    bh,XYZFLAGS_MAGIC
            TRAPe
            je      short gvl10     ; skip - not the magic item
            TRAP
            bts     [esi].alt_flags,ALTFLAGS_SEENMAGIC
            TRAPc
            jc      short xyz10     ; weird - we shouldn't have two magic items
    

    There were a variety of TRAP macros. Here we see the one plain vanilla TRAP and a bunch of fancy traps which trigger only when certain conditions are met. For example, TRAPc traps if the carry is set. Here's its definition:

    TRAPc   MACRO
            local   l
            jnc     short l
            int     3
    l:
            ENDM
    

    Hardly rocket science.

    When you became the person to trigger a particular code path for the first time, you would trigger the trap, and you either stepped through the code yourself or (if you weren't familiar with the code) contacted the author of the code to verify that the code successfully handled this "never seen before" case. When sufficiently satisfied that a code path operated as expected, the developer removed the corresponding TRAP from the source code.

    Of course, most TRAPs are removed before the code gets checked in, but the ones related to error handling or recovering from data corruption tend to remain (such as here, where we inserted a TRAP when we encounter two magic items, which is theoretically impossible).

    When you trigger one trap, you usually trigger it a lot, and you usually trigger a lot of related traps as well. The Z command was quite handy at neutering each one after you checked that everything was working. You zapped the trap.

    That's why old-timers refer to patching out a hard-coded breakpoint as zapping, even though the zap command hasn't existed for over a decade.

    Update: As far as I can tell, the earlier uses of the word zap referred to patching binaries, not for removing hard-coded breakpoints after they stopped in the debugger.

  • The Old New Thing

    Why doesn't the window manager have a SetClipboardDataEx helper function?

    • 29 Comments

    Jonathan Wilson asks why the clipboard APIs still require GlobalAlloc and friends. Why is there not a SetClipboardDataEx or something that does what SetClipboardData does but without needing to call GlobalAlloc?

    Okay, here's your function:

    HANDLE SetClipboardDataEx(UINT uFormat, void *pvData, DWORD cbData)
    {
        if (uFormat == CF_BITMAP ||
            uFormat == CF_DSPBITMAP ||
            uFormat == CF_PALETTE ||
            uFormat == CF_METAFILEPICT ||
            uFormat == CF_DSPMETAFILEPICT ||
            uFormat == CF_ENHMETAFILE ||
            uFormat == CF_DSPENHMETAFILE ||
            uFormat == CF_OWNERDISPLAY) {
            return NULL; // these are not HGLOBAL format
        }
    
        HANDLE hRc = NULL;
        HGLOBAL hglob = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT,
                                    cbData);
        if (hglob) {
            void *pvGlob = GlobalLock(hglob);
            if (pvGlob) {
                CopyMemory(pvGlob, pvData, cbData);
                GlobalUnlock(hglob);
                hRc = SetClipboardData(uFormat, hglob);
            }
            if (!hRc) {
                GlobalFree(hglob);
            }
        }
        return hRc;
    }
    

    Whoop-dee-doo.

    Historically, Windows doesn't go out of its way to include functions like this because you can easily write them yourself, or you can at least find a framework library that did it for you. Windows focused on doing the things that only Windows could do, providing you the building blocks with which you can create your own programs.

    Besides, the classic clipboard is so old-school. The OLE clipboard provides a much richer interface, where you can generate data dynamically (for example as a stream) and expose it in formats other than just a chunk of bytes. Since SetClipboardData is old-school, if the window manager folks had written a function like SetClipboardDataEx, people would instead have asked the not unreasonable question, "Why did you bother to write a function that provides no essential new functionality to an old interface that was supplanted over a decade ago?"

  • The Old New Thing

    During process termination, the gates are now electrified

    • 24 Comments

    It turns out that my quick overview of how processes exit on Windows XP was already out of date when I wrote it. Mind you, the information is still accurate for Windows XP (as far as I know), but the rules changed in Windows Vista.

    What about critical sections? There is no "Uh-oh" return value for critical sections; EnterCriticalSection doesn't have a return value. Instead, the kernel just says "Open season on critical sections!" I get the mental image of all the gates in a parking garage just opening up and letting anybody in and out.

    In Windows Vista, the gates don't go up. Instead they become electrified!

    If during DLL_PROCESS_DETACH at process termination on Windows Vista you call EnterCriticalSection on a critical section that has been orphaned, the kernel no longer responds by just letting you through. Instead, it says, "Oh dear, things are in unrecoverably bad shape. Best to just terminate the process now." If you try to enter an orphaned critical section during process shutdown, the kernel simply calls TerminateProcess on the current process!

    It's sort of like the movie Speed: If the thread encounters a critical section that causes it to drop below 50 miles per hour, it blows up.

    Fortunately, this error doesn't change the underlying analysis of How my lack of understanding of how processes exit on Windows XP forced a security patch to be recalled.

    But it also illustrates how the details of process shutdown are open to changes in the implementation at any time, so you shouldn't rely on them. Remember the classical model for how processes exit: You cleanly shut down all your worker threads, and then call ExitProcess. If you don't follow that model (and given the current programming landscape, you pretty have no choice but to abandon that model, what with DLLs creating worker threads behind your back), it's even more important that you follow the general guidance of not doing anything scary in your DllMain function.

  • The Old New Thing

    Historically, Windows didn't tend to provide functions for things you can already do yourself

    • 62 Comments

    Back in the old days, programmers were assumed to be smart and hardworking. Windows didn't provide functions for things that programs could already do on their own. Windows worried about providing functionality for thing that programs couldn't do. That was the traditional separation of responsibilities in operating systems of that era. If you wanted somebody to help you with stuff you could in principle do yourself, you could use a runtime library or a programming framework.

    You know how to open files, read them, and write to them; therefore, you could write your own file copy function. You know how to walk a linked list; the operating system didn't provide a linked list management library. There are apparently some people who think that it's the job of an operating system to alleviate the need for implementing them yourself; actually that's the job of a programming framework or tools library. Windows doesn't come with a finite element analysis library either.

    You can muse all you want about how things would have been better if Windows had had an installer library built-in from the start or even blame Windows for having been released without one, but then again, the core unix operating system doesn't have an application installer library either. The unix kernel has functions for manipulating the file system and requesting memory from the operating system. Standards for installing applications didn't arrive until decades later. And even though such standards exist today (as they do in Windows), there's no law of physics preventing a vendor from writing their own installation program that doesn't adhere to those standards and which can do anything they want to the system during install. After all, at the end of the day, installing an application's files is just calling creat and write with the right arguments.

    Commenter Archangel remarks, "At least if the ACL route had been taken, the installers would have had to be fixed - and fixed they would have been, when the vendors realised they didn't run on XP."

    These arguments remind me of the infamous "Step 3: Profit" business plan of the Underpants Gnomes.

    • Step 1: Require every Windows application to adhere to new rules or they won't run on the next version of Windows.
    • ...
    • Step 3: Windows is a successful operating system without applications which cause trouble when they break those rules.

    It's that step 2 that's the killer. Because the unwritten step 2 is "All applications stop working until the vendors fix them."

    Who's going to fix the the bill-printing system that a twelve-year-old kid wrote over a decade ago, but which you still use to run your business. (I'm not making this up.) What about that shareware program you downloaded three years ago? And it's not just software where the authors are no longer available. The authors may simply not have the resources to go back and update every single program that they released over the past twenty years. There are organizations with thousands of install scripts which are used to deploy their line-of-business applications. Even if they could fix ten scripts a day, it'd take them three years before they could even start thinking about upgrading to the next version of Windows. (And what about those 16-bit applications? Will they have to be rewritten as 32-bit applications? How long will that take? Is there even anybody still around who understands 16-bit Windows enough to be able to undertake the port?)

  • The Old New Thing

    The wrong way to determine the size of a buffer

    • 62 Comments

    A colleague of mine showed me some code from a back-end program on a web server. Fortunately, the company that wrote this is out of business. Or at least I hope they're out of business!

    size = 16384;
    while (size && IsBadReadPtr(buffer, size)) {
        size--;
    }
    
  • The Old New Thing

    The hardest part of writing the video game Monty Python's Complete Waste of Time

    • 17 Comments

    Many years ago, I happened to have lunch with one of the programmers who worked on the video game Monty Python's Complete Waste of Time (read a review). This program was notable in many ways, most geekily that it was brought on board the Mir space station by astronaut Michael Foale.

    Anyway, during the course of lunch, I learned something unusual:

    "The hardest part of writing that program? Synchronizing the farts [sounds] to the video."

    Vaguely related: How the Space Shuttle and International Space Station use Outlook.

    Not really related: Trailer for IMAX Hubble 3D movie.

Page 1 of 4 (32 items) 1234