Detect active window changed using C# without poll

2019-01-04 09:06发布

How might one invoke a callback whenever the current active window changes. I've seen how it might be done using CBTProc. However, global events aren't easy to hook into with managed code. I'm interested in finding a way that doesn't require polling. I'd prefer an event driven approach.

Regards

3条回答
霸刀☆藐视天下
2楼-- · 2019-01-04 09:23

You can use SetWinEventHook and listen for the EVENT_SYSTEM_FOREGROUND event. Use the WINEVENT_OUTOFCONTEXT flag to avoid the global-hook problem.

查看更多
贪生不怕死
3楼-- · 2019-01-04 09:43

I know this thread is old, but for sake of future use: when running the code you'll notice a crash after a while. This is caused from the line in the Form constructor:

public Form1()
    {
        InitializeComponent();
        WinEventDelegate dele = new WinEventDelegate(WinEventProc);//<-causing ERROR
        IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
    }

Instead of the above make the following modification:

public Form1()
        {
            InitializeComponent();
            dele = new WinEventDelegate(WinEventProc); 
            IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
        }
WinEventDelegate dele = null;

..works now as expected!

查看更多
叼着烟拽天下
4楼-- · 2019-01-04 09:48

Create a new windows forms project, add a textbox, make it multiline, and set the textbox Dock property to fill, name it Log and paste in the following code (you'll need to add System.Runtime.InteropServices to your usings)...

    WinEventDelegate dele = null;

    public Form1()
    {
        InitializeComponent();
        dele = new WinEventDelegate(WinEventProc);
        IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
    }

    delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

    [DllImport("user32.dll")]
    static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

    private const uint WINEVENT_OUTOFCONTEXT = 0;
    private const uint EVENT_SYSTEM_FOREGROUND = 3;

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

    [DllImport("user32.dll")]
    static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

    private string GetActiveWindowTitle()
    {
        const int nChars = 256;
        IntPtr handle = IntPtr.Zero;
        StringBuilder Buff = new StringBuilder(nChars);
        handle = GetForegroundWindow();

        if (GetWindowText(handle, Buff, nChars) > 0)
        {
            return Buff.ToString();
        }
        return null;
    }

    public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
    {
        Log.Text += GetActiveWindowTitle() + "\r\n";
    } 
查看更多
登录 后发表回答