【发布时间】:2014-10-29 15:42:05
【问题描述】:
我有一个 C# 程序来轮询对 EnumDesktopWindows 集合的更改。如果用户关闭或打开一个窗口,轮询例程会检测到这一点并将可用窗口的更新列表发送到另一个 .Net windows 窗体项目。但是,我不喜欢轮询方法。我希望对 EnumDesktopWindows 的任何更改都会触发一个事件,以便异步完成对更改的响应。
我能想到的最好的就是你在下面看到的。我尝试了 Scott C. 从控制台窗口执行的建议,但没有奏效。
目前,您在下面看到的内容在 Windows 窗体加载时捕获 CreateWnd=3(这是一个 Windows 窗体应用程序)。但是它不会全局捕获:它仅从当前运行的可执行文件中捕获窗口事件。如果有人有鹰眼并且可以发现如何在全球范围内捕获此代码,我将奖励答案。
尝试一下;首先创建一个Windows Forms应用项目,在Form1.cs中添加如下代码(需要在名为lstLog的Form中添加一个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 窗体项目。您应该会看到它检测到正在创建一个窗口,即您刚刚执行的那个。
【问题讨论】:
-
我认为你可以使用
SetWindowsHookEx()和WH_CBT钩子类型来做到这一点。 -
我刚刚修改了我的问题,并用我迄今为止的最佳尝试对其进行了更新(谢谢 Scott)。我仍在寻求答案。
-
@Hans - 所以不要使用 SetWindowsHookEx?我会试一试。我是这方面的新手。