Picture in Picture uses Windows Vista's new Live Thumbnail feature to display the window you specify. The problem is, after you minimize the window you are watching, the thumbnail is no longer live. Some users might not understand why this is happening, so I decided to display a little message when this happens.
To do this, PiP needs to know when a window has been minimized, even if that window isn't one created by my application. The solution was to use system hooks. I decided on Chris Wilson's fantastic Global CBT Hook library.
I won't explain how to use Chris' library, as his article does a fine job of that. However, I will show you some edits that need to be made and how to use parts of the library related directly to detecting window minimization. I will provide a small sample at the end of the article that contains everything I've talked about.
There are many ways to capture a window's minimization event. You can catch the SC_MINIMIZE syscommand. The problem that I ran into with that, however, is there's no way to know which window the message originated from. Had I used this method, the user would get a message when they minimized ANY window, rather than just the one PiP was watching. Another way, and the way I was hoping to use, is to catch WM_SIZE. However, Chris' library seems to have a problem with GetMsg, as it only fires twice and never again.
I finally settled on HCBT_MINMAX. This message is sent when a window is about to be minimized or maximized. But, since I didn't want to display a message when the window is maximized, I needed to extract the low-order word of lParam.
But, wait! Chris' library doesn't provide lParam in the CBT.MinMax callback! For this, I needed to make some edits to his C# wrapper (GlobalHooks.cs). At the top, where the delegates are defined, create a new delegate called ExtendedWindowEventHandler:
public delegate void ExtendedWindowEventHandler(IntPtr Handle, int lParam);
Now, find the CBTHook class. Where the events are defined, change
WindowEventHandler MinMax;
to
ExtendedWindowEventHandler MinMax;
While still in the CBTHook class, locate the ProcessWindowMessage method. Find the block that looks like this:
else if (m.Msg == _MsgID_CBT_MinMax)
{
if (MinMax != null)
MinMax(m.WParam);
}
And change MinMax(m.WParam) to MinMax(m.WParam, m.LParam.ToInt32()). Now just edit your event handler code to reflect the changes. You should now be able to use lParam:
int lowOrder = (lParam & 0x0000FFFF);
Using the above line, the variable, lowOrder, will contain which operation the window is performing. Since I only want to catch when the window is minimizing, the only values I need to worry about are 2 and 6 (SW_SHOWMINIMIZED and SW_MINIMIZE, respectively):
int SW_SHOWMINIMIZED = 2;
int SW_MINIMIZE = 6;
Why am I checking for both of them? Well, some applications work differently than others. For example, while Pidgin sent SW_MINIMIZE, Firefox sent SW_SHOWMINIMIZED. In order to handle all cases, you should check for both values.
Now just put it all together in the callback for CBT.MinMax:
// Extract the low-order word from lParam. This tells us the operation
// the window is performing
int lowOrder = (lParam & 0x0000FFFF);
// Check for both SW_MINIMIZE and SW_SHOWMINIMIZED, since some applications
// send different messages than others
if (lowOrder == SW_MINIMIZE || lowOrder == SW_SHOWMINIMIZED) {
// Add the window to the log
listBox1.Items.Add(Handle.ToString() + ": " + getWindowTitle(Handle));
}
Neat, huh? Here's the full sample: CBT Minimize Sample
Enjoy!