听另一个窗口调整C#中的事件大小

我正在实现一个小应用程序(观察者),需要将自己“附加”到另一个窗口的底部(观察到)。 后者不是应用程序内部的窗口。

此时我通过获取窗口的hWnd并在线程中定期查询观察窗口的位置来解决,相应地移动观察者窗口。

然而,这是一个非常不优雅的解决方案。 我想做的是听取观察窗口的resize事件,以便观察者只在必要时做出反应。

我假设我应该使用一个钩子,我找到了很多方法,但是我对C WinAPI缺乏了解阻碍了我需要创建哪个钩子以及如何(pinvoke / parameters / etc)。

我很确定这是非常简单的,你们中的一些熟悉C / C ++和WinAPI就可以随时得到答案;)

谢谢

扩展Chris Taylor的答案:您可以使用包含Hook类的ManagedWinApi ,而不是自己进行本机互操作。

编辑:使用ManagedWinApi。 你代码中的某个地方:

 Hook MyHook = new Hook(HookType.WH_CALLWNDPROC, false, false); MyHook.Callback += MyHookCallback; MyHook StartHook(); 

对于回调,引用CallWndProc和CWPSTRUCT :

 private static int MyHookCallback(int code, IntPtr wParam, IntPtr lParam, ref bool callNext) { if (code >= 0) { // You will need to define the struct var message = (CWPSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPSTRUCT)); // Do something with the data } return 0; // Return value is ignored unless you set callNext to false } 

WH_CALLWNDPROC挂钩可能就足够了,这将允许您监视发往目标窗口的所有消息。

您是否在询问如何使用C#创建全局钩子,或者您是否乐意在C ++中创建钩子然后与.NET交互? 第二个选择是我要去的路线。

基本上我的头脑,我会做的是以下

1-在C中创建全局挂钩,并将函数导出到InstallHookUninstallHook ,可以使用Interop从C#应用程序调用。 InstallHook在C#应用程序中占用了一个窗口。

2-安装了挂钩函数将自定义消息发布到InstallHook调用中提供的C#窗口,如果有你感兴趣的消息,如你的情况下的WM_SIZE

3-在C#应用程序中,从钩子接收已发布消息的窗口将覆盖WndProc以处理自定义消息。

这是一种方法的概述。

我建议你使用WinEvents:

 public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); [DllImport("user32.dll")] public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); 

另请参见: 事件挂钩

我在使用的一些代码中遇到了同样的问题,并发现我无法在进程中注入托管的.DLL。

不想编写一个使用SetWindowsHook的C ++非托管DLL,我选择了SetWinEventHook的组合,它在窗口启动时发出信号并结束一个move事件,一个Timer在窗口移动时轮询窗口,使其看起来跟随窗口当它移动。

这是一个可以做到这一点的(非常简化的)类版本(只需用代码替换TODO来移动窗口或引发事件)。

 public class DockingHelper { private readonly uint m_processId, m_threadId; private readonly IntPtr m_target; // Needed to prevent the GC from sweeping up our callback private readonly WinEventDelegate m_winEventDelegate; private IntPtr m_hook; private Timer m_timer; public DockingHelper(string windowName, string className) { if (windowName == null && className == null) throw new ArgumentException("Either windowName or className must have a value"); m_target = FindWindow(className, windowName); ThrowOnWin32Error("Failed to get target window"); m_threadId = GetWindowThreadProcessId(m_target, out m_processId); ThrowOnWin32Error("Failed to get process id"); m_winEventDelegate = WhenWindowMoveStartsOrEnds; } [DllImport("user32.dll", SetLastError = true)] private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); [DllImport("user32.dll", SetLastError = true)] private static extern bool UnhookWinEvent(IntPtr hWinEventHook); [DllImport("user32.dll")] private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", SetLastError = true)] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); private void ThrowOnWin32Error(string message) { int err = Marshal.GetLastWin32Error(); if (err != 0) throw new Win32Exception(err, message); } private RECT GetWindowLocation() { RECT loc; GetWindowRect(m_target, out loc); if (Marshal.GetLastWin32Error() != 0) { // Do something useful with this to handle if the target window closes, etc. } return loc; } public void Subscribe() { // 10 = window move start, 11 = window move end, 0 = fire out of context m_hook = SetWinEventHook(10, 11, m_target, m_winEventDelegate, m_processId, m_threadId, 0); } private void PollWindowLocation(object state) { var location = GetWindowLocation(); // TODO: Reposition your window with the values from location (or fire an event with it attached) } public void Unsubscribe() { UnhookWinEvent(m_hook); } private void WhenWindowMoveStartsOrEnds(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { if (hwnd != m_target) // We only want events from our target window, not other windows owned by the thread. return; if (eventType == 10) // Starts { m_timer = new Timer(PollWindowLocation, null, 10, Timeout.Infinite); // This is always the original position of the window, so we don't need to do anything, yet. } else if (eventType == 11) { m_timer.Dispose(); m_timer = null; var location = GetWindowLocation(); // TODO: Reposition your window with the values from location (or fire an event with it attached) } } [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left, Top, Right, Bottom; } private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); }