当桌面windows集合更改时,pinvoke异步委托

本文关键字:pinvoke 异步 桌面 windows 集合 | 更新日期: 2023-09-27 18:20:37

我有一个C#程序,用于轮询EnumDesktopWindows集合的更改。如果用户关闭或打开一个窗口,轮询例程会检测到这一点,并将可用窗口的更新列表发送到另一个.Net窗口窗体项目。但是,我不喜欢这种投票方式。我更希望对EnumDesktopWindows的任何更改都会触发一个事件,以便异步响应更改。

我能想出的最好的办法就是你在下面看到的。我尝试了Scott C.的建议,从控制台窗口执行,但没有成功。

当前,当加载Windows窗体(这是一个Windows窗体应用程序)时,您在下面看到的捕获CreateWnd=3。但是,它不会全局捕获:它只捕获当前运行的可执行文件中的窗口事件。如果有人有敏锐的眼光,能够发现如何在全球范围内捕获代码,我将给出答案。

尝试一下;首先创建一个Windows窗体应用程序项目,并将以下代码添加到Form1.cs(您需要向名为lstLog的窗体添加ListBox才能正确编译)

using System;
using System.Windows.Forms;
namespace Utilities
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            var gwh = new GlobalWindowHook();
            gwh.WindowCreated += onWindowCreated;
        }
        private void onWindowCreated()
        {
            lstLog.Items.Add("window creation event detected.");
        }
    }
}

在同一个名为GlobalWindowHook.cs的项目中创建一个类文件,并复制粘贴以下内容:

using System;
using System.Runtime.InteropServices;
namespace Utilities
{
    internal class GlobalWindowHook
    {
        private delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll")]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
        public enum HookType
        {
            WH_JOURNALRECORD = 0,
            WH_JOURNALPLAYBACK = 1,
            WH_KEYBOARD = 2,
            WH_GETMESSAGE = 3,
            WH_CALLWNDPROC = 4,
            WH_CBT = 5,
            WH_SYSMSGFILTER = 6,
            WH_MOUSE = 7,
            WH_HARDWARE = 8,
            WH_DEBUG = 9,
            WH_SHELL = 10,
            WH_FOREGROUNDIDLE = 11,
            WH_CALLWNDPROCRET = 12,
            WH_KEYBOARD_LL = 13,
            WH_MOUSE_LL = 14
        }
        public enum HCBT
        {
            MoveSize = 0,
            MinMax = 1,
            QueueSync = 2,
            CreateWnd = 3,
            DestroyWnd = 4,
            Activate = 5,
            ClickSkipped = 6,
            KeySkipped = 7,
            SysCommand = 8,
            SetFocus = 9
        }
        private IntPtr hhook = IntPtr.Zero;
        public GlobalWindowHook()
        {
            hook();
        }

        ~GlobalWindowHook()
        {
            unhook();
        }

        public void hook()
        {
            IntPtr hInstance = LoadLibrary("User32");
            hhook = SetWindowsHookEx(HookType.WH_CBT, hookProc, hInstance, 0);
        }
        public void unhook()
        {
            UnhookWindowsHookEx(hhook);
        }
        public IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam)
        {
            if (code != (int) HCBT.CreateWnd && code != (int) HCBT.DestroyWnd)
                return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
            //Do whatever with the created or destroyed window.
            return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
        }

        [DllImport("user32.dll")]
        private static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadId);
        [DllImport("user32.dll")]
        private static extern bool UnhookWindowsHookEx(IntPtr hInstance);
        [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr LoadLibrary(string fileName);
    }
}

执行以上步骤后,执行windows窗体项目。您应该看到它检测到一个正在创建的窗口,即您刚刚执行的那个窗口。

当桌面windows集合更改时,pinvoke异步委托

如果你想被推送通知而不是拉取通知,这并不太难。您需要做的是P/InvokeSetWindowsHookEx并注册WH_CBT事件。一旦收到这些信息,您就可以收听带有HCBT_CREATEWNDHCBT_DESTROYWND代码的信息。这将在创建或销毁新窗口时通知您。

使用在pinvoke.net 中定义的类型

class HookingClass
{
    private delegate IntPtr CBTProc(HCBT nCode, IntPtr wParam, IntPtr lParam);
    private readonly CBTProc _callbackDelegate;
    public HookingClass()
    {
         _callbackDelegate = CallbackFunction;
    }
    private IntPtr _hook;
    private void CreateHook()
    {
        using (Process process = Process.GetCurrentProcess())
        using (ProcessModule module = process.MainModule)
        {
            IntPtr hModule = GetModuleHandle(module.ModuleName);
            _hook = SetWindowsHookEx(HookType.WH_CBT, _callbackDelegate, hModule, 0);
            if(_hook == IntPtr.Zero)
                throw new Win32Exception(); //The default constructor automatically calls Marshal.GetLastError()
        }
    }
    private void Unhook()
    {
        var success = UnhookWindowsHookEx(_hook);
        if(!success)
            throw new Win32Exception();
    }
     private IntPtr CallbackFunction(HCBT code, IntPtr wParam, IntPtr lParam)
     {
         if (code != HCBT.CreateWnd && code != HCBT.DestroyWnd) 
         {         
             return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
         }
         //Do whatever with the created or destroyed window.
         return CallNextHookEx(IntPtr.Zero, (int)code, wParam, lParam);
     }
}