当桌面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窗体项目。您应该看到它检测到一个正在创建的窗口,即您刚刚执行的那个窗口。
如果你想被推送通知而不是拉取通知,这并不太难。您需要做的是P/InvokeSetWindowsHookEx
并注册WH_CBT
事件。一旦收到这些信息,您就可以收听带有HCBT_CREATEWND
或HCBT_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);
}
}