Holy cow, I wrote a book!
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.
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.