tags:

views:

227

answers:

2

So, I thought this would be simple and, well, I was wrong. Here is a simplified description of the problem:

I am writing a small application for our manufacturing folks that will grab a screenshot of the entire desktop as well as the foreground window when they click the application's icon in the system tray. Currently, I am using the Win32 method "GetforegroundWindow" in the MouseMove event of the NotifyIcon to save the foreground window handle and take the screenshot in the Click event.

This does work sometimes, but if I click the icon very quickly I actually capture the task bar instead of the foreground window. I am not sure why this is happening (I do understand that the task bar is a window, I don't understand why sometimes it seems to have focus in MouseMove before I have clicked), and I have had little luck using the EnumWindows method as well, likely because I do not completely understand how it works.

It would seem that, if I were able to get the z position of each window using only the window handle, this would be an easy problem to solve using EnumWindows. I have not found a method to do that however.

So, I ask you guys; how would you write a method to locate the foreground window reliably, given that it may not have focus at the time? Either my google-fu is failing me or the information on this is sparse. Thanks in advance.

+1  A: 

The taskbar is as valid a foreground window as any. When you click it, it will temporarily be the foreground window. And if you click Start and press Escape for example, it will be the foreground window until you click off of it.

You can probably use GetWindow with HWND_NEXT passing in the window handle of the taskbar.

Nevermind, since the taskbar is a topmost window, GetWindow (or GetNextWindow, etc) will operate differently. I would suggest revisiting the EnumWindows solution which is probably your best bet.

Josh Einstein
Right, I understand that the task bar is a window, but that is exactly what I am trying to avoid. I need to get the hWnd of the logical foreground window. I will try passing HWND_NEXT and see if that always returns what I am after. Thanks.
Ed Swangren
Of course, now I need to be able to find the handle to the taskbar =P. that is probably defined somewhere for me, I'll look (I know the desktop is 0).
Ed Swangren
Well actually you already have the handle at that point. You just need to use GetClassName to determine if that handle is the taskbar or not. If you have Spy++ installed, you can use that to find the class name. I don't know it off hand. http://msdn.microsoft.com/en-us/library/ms633582(VS.85).aspx
Josh Einstein
But remember, I do not always get the taskbar, it only occurs when I click the icon very quickly. Anyhow, this should work, and I am sure there is a simple way to identify the task bar. Thanks again.
Ed Swangren
Right... what I mean is once you have the handle that may or may not be the taskbar, call GetClassName. If it matches the taskbar's class name, then use GetWindow with HWND_NEXT.
Josh Einstein
Oh, der, of course =). Thanks.
Ed Swangren
Unfortunately, using GW_HWNDNEXT does not work. The next window in z-order is the desktop minus the taskbar, and subsequent calls return nothing. I am still playing around with it, but no solution yet.
Ed Swangren
Gah! I think I know why. The docs suggest a topmost window only returns the next topmost window. Gotta think about this...
Josh Einstein
Yep, I am reading the same thing =). Hopefully our combined brain power can figure this out, I know of other programs that do the same thing.
Ed Swangren
If EnumWindows provides its results in Z-order (can't tell from the docs) then that's really the best way to go. You could check for the taskbar in your EnumWindowsProc. http://pinvoke.net/default.aspx/user32/EnumWindows.html
Josh Einstein
I will give it a go, let's see.
Ed Swangren
A: 

If the form you want the snapshot of is the same as the form linked to the task bar, you really do not need to use GetforegroundWindow. Just use the Form.hWnd and pass that into the function getting the snapshot. You may need to make it the top window by calling

[DllImport( "user32.dll" )]
public static extern IntPtr SetForegroundWindow( IntPtr hWnd );

or

[DllImport( "user32.dll" )]
public static extern bool BringWindowToTop( HandleRef hWnd );

If you want the whole desktop, then you probably just need to put in a Thread.Sleep to make sure the foreground window has had enough to come to the top before getting the desktop snapshot.

putting the src from my comment here for better printing

[DllImport( "user32.dll" )]
public static extern IntPtr GetForegroundWindow();

[DllImport( "user32.dll" )] 
public static extern IntPtr GetActiveWindow(); 

// this or the next line not both
IntPtr curWindow = GetActiveWindow(); 
IntPtr curWindow = GetForegroundWindow(); 

BringWindowToTop( window ); 
System.Threading.Thread.Sleep( 500 ); 

Where the thread spleep gives the window enough time to come to the top of the Z order.

JDMX
It isn't, I need to grab an arbitrary window that is on top. Thanks though.
Ed Swangren
Think of a screenshot program that just grabs the topmost window, whatever that may be. The desktop is easy and that part works fine currently.
Ed Swangren
I would also add that neither GetActiveWindow or GetForegroung window will work. As I said in my initial post, the window will not have focus.
Ed Swangren