[英]Listening to another window resize events in 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
}
我建議你使用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);
另請參見: 事件掛鈎
WH_CALLWNDPROC
掛鈎可能就足夠了,這將允許您監視發往目標窗口的所有消息。
您是否在詢問如何使用C#創建全局鈎子,或者您是否樂意在C ++中創建鈎子然后與.NET交互? 第二個選擇是我要去的路線。
基本上我的頭腦,我會做的是以下
1-在C中創建全局鈎子,並將函數導出到InstallHook
和UninstallHook
,可以使用Interop從C#應用程序調用。 InstallHook在你的C#應用程序中占用了一個窗口。
2-如果您感興趣的消息(如WM_SIZE
,安裝的掛鈎功能會將自定義消息發布到InstallHook
調用中提供的C#窗口。
3-在C#應用程序中,從鈎子接收已發布消息的窗口將覆蓋WndProc以處理自定義消息。
這是一種方法的概述。
我在使用的一些代碼中遇到了同樣的問題,並發現我無法在進程中注入托管的.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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.