Holy cow, I wrote a book!
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.
HANDLE
NULL
CreateThread
INVALID_HANDLE_VALUE
CreateFile
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.
OpenFile
_lopen
_lcreat
-1
(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.
CreateMutex
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); }
h
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.
GetCurrentProcess()
WaitForSingleObject
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.
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.
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:
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
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.
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 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><A HREF="file:C:\Windows\system32\format.com?C:"> Party plans</A></TITLE>
You download the message and since you are a cautious person, you ask your virus scanner to check it out. The balloon appears:
"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?"
The best way to make sure things are secure is to make it impossible to be insecure.
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?
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.
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.
Who knew that Dr. Evil designed CPUs, too!
Okay, I was hoping it wasn't going to be needed but it takes only one bad apple...
Here are the ground rules.
Things that increase the likelihood that your comment will be edited or deleted:
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.]
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.