C# 桌面windows集合更改时的pinvoke异步委托?
我有一个C程序,可以轮询EnumDesktopWindows集合的更改。如果用户关闭或打开窗口,轮询例程会检测到这一点,并将可用窗口的更新列表发送给另一个.Net windows窗体项目。但是,我不喜欢轮询方法。我希望对EnumDesktopWindows的任何更改都会触发一个事件,以便异步响应更改 我能想出的最好办法就是你在下面看到的。我尝试了Scott C.的建议,从控制台窗口执行,但没有成功 当前,当Windows窗体加载这是一个Windows窗体应用程序时,您在下面看到的内容捕获CreateWnd=3。但是,它不会全局捕获:它只捕获当前运行的可执行文件中的窗口事件。如果有人有鹰眼,并且能够发现如何在全球范围内捕获此代码,我将给出答案 尝试一下;首先创建一个Windows窗体应用程序项目,并将以下代码添加到Form1.cs中。您需要向名为lstLog的窗体中添加一个列表框才能正确编译C# 桌面windows集合更改时的pinvoke异步委托?,c#,pinvoke,user32,C#,Pinvoke,User32,我有一个C程序,可以轮询EnumDesktopWindows集合的更改。如果用户关闭或打开窗口,轮询例程会检测到这一点,并将可用窗口的更新列表发送给另一个.Net windows窗体项目。但是,我不喜欢轮询方法。我希望对EnumDesktopWindows的任何更改都会触发一个事件,以便异步响应更改 我能想出的最好办法就是你在下面看到的。我尝试了Scott C.的建议,从控制台窗口执行,但没有成功 当前,当Windows窗体加载这是一个Windows窗体应用程序时,您在下面看到的内容捕获Crea
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/Invoke并注册WH_CBT事件。一旦你们收到这些信息,你们就可以用代码来监听信息。它将在创建或销毁新窗口时通知您 使用在中定义的类型
我想您可以使用带有WH_CBT钩子类型的SetWindowsHookx来完成这项工作。我刚刚修改了我的问题,并用我迄今为止最好的尝试进行了更新。谢谢Scott。我仍在寻找答案。@Hans-所以不要使用SetWindowsHookEx?我要试一试。我是这方面的新手。@sapbucket您的问题是hModule需要指向包含_callbackDelegate函数的程序集。user32.dll不包含您的函数,这就是它不工作的原因。尝试在测试控制台应用程序中执行此操作,然后再尝试在单元测试中运行它。此外,您的单元测试程序将在事件触发之前退出并关闭。Hans Passat解释说,hModule不在低级库中使用,传入任何有效的内容都可以。这就是我坚持使用LoadLibrary的原因。汉斯错了吗?不管怎样,我都不会成功。这只适用于低级钩子。CBT钩子并不是低级的,模块句柄是非常重要的。@Hans-我发现一个线程表明我试图做的事情不受.Net支持;我相信你的意见——这条线正确吗?没错,是的。不能将C代码注入另一个进程。好了,Scott的代码只会告诉你你自己的应用程序中的windows被创建和关闭的情况。这不是程序员通常想要的,尤其不是在控制台模式的应用程序中:这就是我发布替代方案的原因。需要消息循环,因此避免使用控制台模式应用程序。
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);
}
}