1

I need to get the handle to the current/previously active window on my desktop. Basically I am working on a c# Windows Forms application that can take screen shots. I am using the GetForegroundWindow PInvoke to get this handle, but if I call my code from a menu click, the active window seems to be set to that of the menu itself so the image produced is a small black rectangle.

The application runs in the systray with a context menu. I have also implemented global hot keys and these work fine, but when I use the context menu with the mouse, the above scenario happens.

How to I keep track of the previously active window handle? Bear in mind my application runs as a tray icon so overriding wndproc never fires. I have tried a NativeForm and MessageOnly form also, but these never fire unless the application has focus.

Are we down to global hooks and external dll's? Surely there has to be a simple way to solve this issue? (any yes, I do need to implement menu items as well as keyboard shortcuts for a particular usability scenario.

So, how do I keep track of the current / previous active window on my desktop from a c# app that has no window itself and has no focus?

Many thanks for any help

Kieran
  • 186
  • 2
  • 13

2 Answers2

0

Suppose you use ContextMenuStrip for your tray menu:

IntPtr lastHandle;
public IntPtr GetForegroundWin(){
   IntPtr hwnd = GetForegroundWindow();
   if(hwnd != contextMenuStrip1.Handle) lastHandle = hwnd; 
   return lastHandle;      
}
//Add a timer
Timer t = new Timer();
t.Interval = 1;
t.Tick += (s,e) => {
    GetForegroundWin();
};//Then you can get the foreground Handle by lastHandle
t.Start();//this timer will run as long as your application runs.

OK, without using timer, we still have another choice using SetWinEventHook. This function can help you hook some callback to catch some events including active window change event. Here is the link for you to learn more: Detect active window changed using C# without polling

Here is the code for the solution without using timer (polling):

//Must add using System.Runtime.InteropServices;
public partial class Form1 : Form
{
    [DllImport("user32")]
    private static extern IntPtr SetWinEventHook(int minEvent, int maxEvent, IntPtr hModule, WinEventProcDelegate proc, int procId, int threadId, int flags);
    private delegate void WinEventProcDelegate(IntPtr hHook, int ev, IntPtr hwnd, int objectId, int childId, int eventThread, int eventTime);
    private void WinEventProc(IntPtr hHook, int ev, IntPtr hwnd, int objectId, int childId, int eventThread, int eventTime)
    {
        if(hwnd != contextMenuStrip1.Handle) lastHandle = hwnd;
    }
    public Form1()
    {
        InitializeComponent();            
        //EVENT_SYSTEM_FOREGROUND = 3
        //WINEVENT_OUTOFCONTEXT = 0
        SetWinEventHook(3, 3, IntPtr.Zero, WinEventProc, 0, 0, 0);                                                                      
    }
    IntPtr lastHandle;
}
//You can access the lastHandle to get the current active/foreground window. This doesn't require GetForegroundWindow()
Community
  • 1
  • 1
King King
  • 61,710
  • 16
  • 105
  • 130
  • Hi KK< I am using a ContextMenuStrip for my tray menu. I have tried your code in it's menu_click event but it still doesn't work. Can you tie your solution together for me? How and Where would I use your GetForegroundWin() method ? – Kieran Aug 16 '13 at 20:30
  • @user2026382 whenever you need to get the `Handle` of the foreground window you can call the method `GetForegroundWin` instead of the method `GetForegroundWindow`. The return `Handle` will be of the foreground window except the ContextMenuStrip window. – King King Aug 16 '13 at 20:32
  • Yes that's what I'm doing but its not working. I just tried it in the traymenu opening event, and even then its not working. Lets say I have notepad open for example, I click on notepad and its now the active window. But as soon as I click on my tray app, notepad loses focus, its no longer the active window. I am using your method and passing lasthandle into my screen capture method, but I still get back a small back rectangle as the image. – Kieran Aug 16 '13 at 20:38
  • @user2026382 you may need a timer to check the foreground window, see my updated code. – King King Aug 16 '13 at 20:41
  • Oh lord I don't want to start using a timer, hammering my app every millisecond or whatever, there has to be a better solution. I just need to know what was the last active window before I clicked on my tray icon. – Kieran Aug 16 '13 at 20:44
  • @user2026382 I've written an application of this kind (not snapshot but had also to track the last active window) and I also used a timer, it worked OK. The `GetForegroundWindow()` doesn't consume much calculation. There won't any other solution if you stick to `GetForegroundWindow`. – King King Aug 16 '13 at 20:51
  • Your last sentence suggests there are alternatives to GetForegroundWindow ?? Does anyone else have any ideas? – Kieran Aug 16 '13 at 20:56
  • @user2026382 not really, I don't know other choices, in fact we have to be notified if the active/foreground window is changed (some kind of event) but we don't have such event. That's why we have to `probe` to update the last active window using `GetForegroundWindow`. `Win32` doesn't supports `event`. There is another choice for you is `hook into system globally`, however it's not easy to do (even using some library like `EasyHook`) and it's also expensive in slowing down the system performance (a bit or very slightly but it does affect). – King King Aug 16 '13 at 21:00
  • @user2026382 I've found a helpful link for you, it would be the choice you want. – King King Aug 16 '13 at 21:15
0

I have found a much simpler solution and while it's a bit of a hack, it works beautifully, reliably and costs nothing. What I do is, from my menu click event, I show a form, then immediately hide the form. This has the effect of taking focus away from the taskbar back to the desktop. I show my form, then when I close it, the previously selected window regains its focus. Then my GetForegroundWindow gets it's handle.

My app, actually has a status form that can update the user during lengthy processes, so I can show it with a "capturing, please wait" message or something. Or I can simply flash it up on screen so fast that it cannot be really seen at all. Then I close it, thread sleep(100) and carry on with the screen capture. Works like a charm

        if (_runMode == RunMode.Tray)
        {
            FormStatus f = new FormStatus();
            f.Show();
            f.Close();
            Thread.Sleep(100);
        }

        ScreenCapture.CaptureActiveWindow(filename);
Kieran
  • 186
  • 2
  • 13