• The Old New Thing

    Why are HANDLE return values so inconsistent?

    • 21 Comments

    If you look at the various functions that return HANDLEs, you'll see that some of them return NULL (like CreateThread) and some of them return INVALID_HANDLE_VALUE (like CreateFile). You have to check the documentation to see what each particular function returns on failure.

    Why are the return values so inconsistent?

    The reasons, as you may suspect, are historical.

    The values were chosen to be compatible with 16-bit Windows. The 16-bit functions OpenFile, _lopen and _lcreat return -1 on failure, so the 32-bit CreateFile function returns INVALID_HANDLE_VALUE in order to facilitate porting code from Win16.

    (Armed with this, you can now answer the following trivia question: Why do I call CreateFile when I'm not actually creating a file? Shouldn't it be called OpenFile? Answer: Yes, OpenFile would have been a better name, but that name was already taken.)

    On the other hand, there are no Win16 equivalents for CreateThread or CreateMutex, so they return NULL.

    Since the precedent had now been set for inconsistent return values, whenever a new function got added, it was a bit of a toss-up whether the new function returned NULL or INVALID_HANDLE_VALUE.

    This inconsistency has multiple consequences.

    First, of course, you have to be careful to check the return values properly.

    Second, it means that if you write a generic handle-wrapping class, you have to be mindful of two possible "not a handle" values.

    Third, if you want to pre-initialize a HANDLE variable, you have to initialize it in a manner compatible with the function you intend to use. For example, the following code is wrong:

    HANDLE h = NULL;
    if (UseLogFile()) {
        h = CreateFile(...);
    }
    DoOtherStuff();
    if (h) {
       Log(h);
    }
    DoOtherStuff();
    if (h) {
        CloseHandle(h);
    }
    
    This code has two bugs. First, the return value from CreateFile is checked incorrectly. The code above checks for NULL instead of INVALID_HANDLE_VALUE. Second, the code initializes the h variable incorrectly. Here's the corrected version:
    HANDLE h = INVALID_HANDLE_VALUE;
    if (UseLogFile()) {
        h = CreateFile(...);
    }
    DoOtherStuff();
    if (h != INVALID_HANDLE_VALUE) {
       Log(h);
    }
    DoOtherStuff();
    if (h != INVALID_HANDLE_VALUE) {
        CloseHandle(h);
    }
    

    Fourth, you have to be particularly careful with the INVALID_HANDLE_VALUE value: By coincidence, the value INVALID_HANDLE_VALUE happens to be numerically equal to the pseudohandle returned by GetCurrentProcess(). Many kernel functions accept pseudohandles, so if if you mess up and accidentally call, say, WaitForSingleObject on a failed INVALID_HANDLE_VALUE handle, you will actually end up waiting on your own process. This wait will, of course, never complete, because a process is signalled when it exits, so you ended up waiting for yourself.

  • The Old New Thing

    Why 16-bit DOS and Windows are still with us

    • 65 Comments
    Many people are calling for the abandonment of 16-bit DOS and 16-bit Windows compatibility subsystems. And trust me, when it comes time to pull the plug, I'll be fighting to be the one to throw the lever. (How's that for a mixed metaphor.)

    But that time is not yet here.

    You see, folks over in the Setup and Deployment group have gone and visited companies around the world, learned how they use Windows in their businesses, and one thing keeps showing up, as it relates to these compatibility subsystems:

    Companies still rely on them. Heavily.

    Every company has its own collection of Line of Business (LOB) applications. These are programs that the company uses for its day-to-day business, programs the company simply cannot live without. For example, at Microsoft two of our critical LOB applications are our defect tracking system and our source control system.

    And like Microsoft's defect tracking system and source control system, many of the LOB applications at major corporations are not commercial-available software; they are internally-developed software, tailored to the way that company works, and treated as trade secrets. At a financial services company, the trend analysis and prediction software is what makes the company different from all its competitors.

    The LOB application is the deal-breaker. If a Windows upgrade breaks a LOB application, it's game over. No upgrade. No company is going to lose a program that is critical to their business.

    And it happens that a lot of these LOB applications are 16-bit programs. Some are DOS. Some are 16-bit Windows programs written in some ancient version of Visual Basic.

    "Well, tell them to port the programs to Win32."

    Easier said than done.

    • Why would a company go to all the effort of porting a program when the current version still works fine. If it ain't broke, don't fix it.
    • The port would have to be debugged and field-tested in parallel with the existing system. The existing system is probably ten years old. All its quirks are well-understood. It survived that time in 1998 when there was a supply chain breakdown and when production finally got back online, they had to run at triple capacity for a month to catch up. The new system hasn't been stress-tested. Who knows whether it will handle these emergencies as well as the last system.
    • Converting it from a DOS program to a Windows program would incur massive retraining costs for its employees ("I have always used F4 to submit a purchase order. Now I have this toolbar with a bunch of strange pictures, and I have to learn what they all mean." Imagine if somebody took away your current editor and gave you a new one with different keybindings. "But the new one is better.")
    • Often the companies don't have the source code to the programs any more, so they couldn't port it if they wanted to. It may use a third-party VB control from a company that has since gone out of business. It may use a custom piece of hardware that they have only 16-bit drivers for. And even if they did have the source code, the author of the program may no longer work at the company. In the case of a missing driver, there may be nobody at the company qualified to write a 32-bit Windows driver. (I know one company that used foot-pedals to control their software.)

    Perhaps with a big enough carrot, these companies could be convinced to undertake the effort (and risk!) of porting (or in the case of lost source code and/or expertise, rewriting from scratch) their LOB applications.

    But it'll have to be a really big carrot.

    Real example: Just this past weekend I was visiting a friend who lived in a very nice, professionally-managed apartment complex. We had occasion to go to the office, and I caught a glimpse of their computer screen. The operating system was Windows XP. And the program they were running to do their apartment management? It was running in a DOS box.

  • The Old New Thing

    The correct order for disabling and enabling windows

    • 42 Comments
    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?

    • 17 Comments
    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?

    • 34 Comments
    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?

    • 26 Comments
    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.

    Why?

    • 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

    • 21 Comments
    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!

    • 14 Comments
    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

    • 8 Comments

    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?

    • 8 Comments
    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.
Page 362 of 388 (3,876 items) «360361362363364»