February, 2004

  • The Old New Thing

    The correct order for disabling and enabling windows

    If you want to display modal UI, you need to disable the owner and enable the modal child, and then reverse the procedure when the modal child is finished.

    And if you do it wrong, focus will get all messed up.

    If you are finished with a modal dialog, your temptation would be to clean up in the following order:

    • Destroy the modal dialog.
    • Re-enable the owner.

    But if you do that, you'll find that foreground activation doesn't go back to your owner. Instead, it goes to some random other window. Explicitly setting activation to the intended owner "fixes" the problem, but you still have all the flicker, and the Z-order of the interloper window gets all messed up.

    What's going on?

    When you destroy the modal dialog, you are destroying the window with foreground activation. The window manager now needs to find somebody else to give activation to. It tries to give it to the dialog's owner, but the owner is still disabled, so the window manager skips it and looks for some other window, somebody who is not disabled.

    That's why you get the weird interloper window.

    The correct order for destroying a modal dialog is

    • Re-enable the owner.
    • Destroy the modal dialog.

    This time, when the modal dialog is destroyed, the window manager looks to the owner and hey this time it's enabled, so it inherits activation.

    No flicker. No interloper.
  • The Old New Thing

    Why do timestamps change when I copy files to a floppy?

    Floppy disks use the FAT filesystem, as do DOS-based and Windows 95-based operating systems. On the other hand, Windows NT-based systems (Windows 2000, XP, 2003, ...) tend to use the NTFS filesystem. (Although you can format a drive as FAT on Windows NT-based systems, it is not the default option.)

    The NTFS and FAT filesystems store times and dates differently. Note, for example, that FAT records last-write time only to two-second accuracy. So if you copy a file from NTFS to FAT, the last-write time can change by as much as two seconds.

    Why is FAT so much lamer than NTFS? Because FAT was invented in 1977, back before people were worried about such piddling things like time zones, much less Unicode. And it was still a major improvement over CP/M, which didn't have timestamps at all.

    It is also valuable to read and understand the consequences of FAT storing filetimes in local time, compared to NTFS storing filetimes in UTC. In addition to the Daylight Savings time problems, you also will notice that the timestamp will appear to change if you take a floppy across timezones. Create a file at, say, 9am Pacific time, on a floppy disk. Now move the floppy disk to Mountain time. The file was created at 10am Mountain time, but if you look at the disk it will still say 9am, which corresponds to 8am Pacific time. The file travelled backwards in time one hour. (In other words, the timestamp failed to change when it should.)

  • The Old New Thing

    Why can't I put hotlinks in notification icon balloon tips?

    The short answer: "Because there is no NIF_PARSELINKS flag."

    The long answer:

    When balloon tips were first developed, there was no ability to embed links. Consequently, programs were free to put insecure text in balloon tips, since there was no risk that they would become "live". So, for example, a virus scanner might say "The document 'XYZ' has been scanned and found to be free of viruses."

    Now suppose hotlinks were supported in balloon tips. Look at how this can be exploited: I can write a web page that goes

    <TITLE>&lt;A HREF="file:C:\Windows\system32\format.com?C:"&gt;
    Party plans&lt;/A&gt;</TITLE>
    I then rename the file to "Party plans.html", attach it to some email, and send it to you.

    You download the message and since you are a cautious person, you ask your virus scanner to check it out. The balloon appears:

    Virus scan complete ×
    The document 'Party plans' has been scanned
    and found to be free of known viruses.

    "Oh, how convenient," you say to yourself. "The virus scanner even included a hotlink to the document so I can read it."

    And then you click on it and your hard drive gets reformatted.

    "So why don't you add a NIF_PARSELINKS flag, so people who want to enable hotlinks in their balloon tips can do so, and still remain compatible with people who wrote to the old API?"

    (I've heard of one person trying to pass a TTF_PARSELINKS flag in the NOTIFYICONDATA.uFlags member and wondering why it wasn't working. I hope it's obvious to everybody why this had no chance of working.)

    Because that would just be passing the buck. Anybody who used this proposed flag would then have to be extra-careful not to put untrusted links in their balloon tips. Most people would just say, "Wow! A new flag! That's awesome!" and start using it without considering the serious security implications. Then somebody can trick the program into putting untrusted text into a balloon tip and thereby exploit the security hole.

    "Aw, come on, who would be so stupid as to write code without considering all the security implications?"

    I hope that was a joke question.

    The best way to make sure things are secure is to make it impossible to be insecure.

  • The Old New Thing

    What's so special about the desktop window?

    The window returned by GetDesktopWindow() is very special, and I see people abusing it all over the place.

    For example, many functions in the shell accept a window handle parameter to be used in case UI is needed. IShellFolder::EnumObjects, for example.

    What happens if you pass GetDesktopWindow()?

    If UI does indeed need to be displayed, you hang the system.


    • A modal dialog disables its owner.
    • Every window is a descendant of the desktop.
    • When a window is disabled, all its descendants are also disabled.

    Put this together: If the owner of a modal dialog is the desktop, then the desktop becomes disabled, which disables all of its descendants. In other words, it disables every window in the system. Even the one you're trying to display!

    You also don't want to pass GetDesktopWindow() as your hwndParent. If you create a child window whose parent is GetDesktopWindow(), your window is now glued to the desktop window. If your window then calls something like MessageBox(), well that's a modal dialog, and then the rules above kick in and the desktop gets disabled and the machine is toast.

    So what window do you pass if you don't have a window?

    Pass NULL. To the window manager, a parent of NULL means "Create this window without an owner." To the shell, a UI window of NULL typically means "Do not display UI," which is likely what you wanted anyway.

    Be careful, though: If your thread does have a top-level unowned window, then creating a second such window modally will create much havoc if the user switches to and interacts with the first window. If you have a window, then use it.

  • The Old New Thing

    Invalid thread and process IDs

    Perhaps you want a value to use as a sentinel, which you want to be guaranteed is never a valid thread ID or process ID. What values can you use?

    Nothing is explicitly written about this topic, but you can put on your logic cap and figure it out.

    If you need an invalid thread ID, you can use zero. How do you know that zero is an invalid thread ID? It's implied by SetWindowsHookEx: Notice that if you pass zero, then the hook is installed into all threads on the current desktop. This implies that zero is not itself a valid thread ID.

    This sentinel value is confirmed by GetThreadID, which uses zero as its error return value.

    Similarly, if you need an invalid process ID, you can use (DWORD)-1. This comes from AllowSetForegroundWindow: The value ASFW_ANY has special meaning, which implies that it is never a valid process ID.

    Or, you can use zero as your invalid process ID, since that is the error value returned by GetProcessId and GetProcessIdOfThread.

  • The Old New Thing

    And we will build a processor with... ONE MILLION TRANSISTORS!

    A friend of mine decided to recaption this picture which is in the rotation on Intel's Hardware Design Home Page:

    "And we will build a processor with... ONE MILLION TRANSISTORS!"

    Who knew that Dr. Evil designed CPUs, too!

  • The Old New Thing

    Raymond's comment policy


    Okay, I was hoping it wasn't going to be needed but it takes only one bad apple...

    Here are the ground rules.

    • I reserve the right to edit, delete, or ignore any comment.
    • If I edit your comment in any significant way, I promise to make that fact clear in the edit. (Exception: Broken links and typos will be repaired without comment.)

    Things that increase the likelihood that your comment will be edited or deleted:

    • Offensive or abusive language or disrespectful behavior. (Insults count as disrespectful behavior. Examples of insulting words/phrases: "moron", "designed without adult supervision".)
    • Misrepresentation. (I.e., claiming to be somebody you're not.) If you don't want to use your real name, that's fine, as long as your "handle" isn't offensive, abusive, or misrepresentative.
    • Comment spamming. This includes posting multiple comments in rapid succession, even if they are different. If you are prone to spurts of creativity, collect them into batches and post them as a single comment.
    • Comments that are off-topic, particular when the discussion turns into a shouting match.
    • Comments that attempt to "out" a company/program/person. E.g., trying to guess the identity of a program whose name I did not reveal.
    • More generally, comments that identify a specific company, program, or person. You can talk about a program, but you have to call it "Program X" or something like that. The purpose is to discuss problems and solutions, not to assign blame and ridicule.
    • Comments that expose me to potential legal action. Examples: Disclosing any company's trade secrets, posting copyrighted source code, violating Microsoft's company guidelines.
    • Giving yourself a star. (This is another case of mispresentation.)

    If a wave of comment spam is under way, I may choose to moderate or even close comments until the problem subsides.

    More rules may be added later, but I hope it won't be necessary.

    Disclaimer: All postings are provided "AS IS" with no warranties and confer no rights. Opinions expressed are those of the respective authors. More legal stuff here.

    [31 May 2004: Add exceptions for broken link repair.

    2 Dec 2004: Add disclaimer and exception for fixing typos.

    13 Dec 2004: Add remark on temporary closure of comments during spam attacks.

    15 Mar 2005: Add remark for off-topic comments.

    20 Mar 2007: No "outing".

    25 Dec 2008: No disrespectful behavior.

    12 March 2009: Examples of insults.

    8 July 2010: Self-starring.]

  • The Old New Thing

    Why do I get a QueryInterface(IID_IMarshal) and then nothing?

    A common problem when trying to get your new COM object off the ground is that you can't seem to be able to get it created. The object comes out of the class factory, and it gets a QueryInterface for IMarshal, and then nothing. What's going on?

    This is a sure sign that you didn't register your CLSID properly; most likely you forgot to set your threading model properly. (And if you fail to specify a threading model at all, then you get the dreaded "main" threading model.)

    If somebody tries to create a COM object from a thread whose model is incompatible with the threading model of the COM object, then a whole bunch of marshalling stuff kicks in. And if the marshalling stuff isn't there, then COM can't use your object.

    There is a long and very technical article in MSDN on COM threading models which has lots of scary-looking diagrams and tables. In particular, the second scary table in the "In-process servers: (almost) totally dependent on their clients" chapter lists all the combinations of thread models with object threading models, and what COM tries to do in each case.

    In particular, notice that if you have a (mistakenly marked) "main"-threaded object and somebody on any thread other than the main thread tries to create it, marshalling will try to kick in.

    So watch those threading models. The failure modes when you get them wrong are quite baffling.
  • The Old New Thing

    Sorry the posts are late lately

    Somehow the server is in a wonky state and my autopilot script that posts a new article at 7 every morning is getting error 500 back (internal server error). So I'm posting manually for the nonce. Expect erratic posting times until the problem is resolved.
  • The Old New Thing

    No code is an island

    Norman Diamond noted in a comment that on Windows 2003 Server, the Display Adapter Troubleshooting slider still lists "full acceleration" as the recommended setting even though the default for Server is "full minus one".

    This is one of those "Oh, that's an easy change" bugs. The discussion probably went like this:

    Some guy whose idea this was: "For stability reasons, we want to lower the default video acceleration for Server a notch. Dear Video Setup team, can you do that for us?"

    Video Setup team: "Sure thing, that's no problem. The default setting is all done by us; it should not have any impact on anybody else. We'll just do it and be done with it."

    Guy: "Sweet. Thanks."

    And bingo, the default video acceleration dropped to one notch below full on Server, and everyone was happy.

    Except that there's this text tucked away in the Display control panel that has the word "(recommended)" next to "full acceleration". That didn't get updated. Oops. (I wouldn't be surprised if there is also some help text that didn't get updated for this change.)

    No code is an island.

    So when you complain, "Aw come on, it's a one-line change. What's taking so long?" think about the little video acceleration slider.
Page 1 of 4 (32 items) 1234