外部应用程序窗口移动时移动窗口

我有一个总是在顶部的应用程序(基本上是一个状态显示),我想跟随另一个程序,并始终坐在最小化按钮的左侧。

我可以使用以下代码获取表示“目标”过程的Rect ,然后我可以将其与偏移量配对以生成叠加层的初始位置。

获取HWnd IntPtr:

 private IntPtr HWnd = Process.GetProcessesByName("targetapplication")[0].MainWindowHandle; 

user32.dll声明函数:

 [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); 

和适当的struct

 [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } 

然后按需调用它。

但是,我想避免不断轮询这个值,所以我想挂钩到目标应用程序并在目标窗口移动时响应。

查看user32.dll文档,我能看到的唯一方法是使用SetWindowsHookEx() 。 但是,我并不完全确定如何从这里拦截一个事件。

我相信目标应用程序是基于WinForms构建的,但我无法确定。 因此,让我响应目标的Move事件或直接响应某些Windows消息的解决方案都很有用。

关于如何进行的任何想法?

这个展示了如何将Winform挂钩到另一个进程(在本例中为记事本)并遵循进程主窗口的移动,以创建可以与进程交互的工具栏。

使用的主要API函数是SetWinEventHook()

结果的直观表示:

在此处输入图像描述

Form类初始化过程:

 public partial class Form1 : Form { private IntPtr NotepadhWnd; private IntPtr hWinEventHook; private Process Target; private WinApi.RECT rect = new WinApi.RECT(); protected Hook.WinEventDelegate WinEventDelegate; public Form1() { InitializeComponent(); WinEventDelegate = new Hook.WinEventDelegate(WinEventCallback); try { Target = Process.GetProcessesByName("notepad").FirstOrDefault(p => p != null); if (Target != null) { NotepadhWnd = Target.MainWindowHandle; uint TargetThreadId = Hook.GetWindowThread(NotepadhWnd); if (NotepadhWnd != IntPtr.Zero) { hWinEventHook = Hook.WinEventHookOne(Hook.SWEH_Events.EVENT_OBJECT_LOCATIONCHANGE, WinEventDelegate, (uint)Target.Id, TargetThreadId); rect = Hook.GetWindowRect(NotepadhWnd); this.Location = new Point(rect.Right, rect.Top); } } } catch (Exception ex) { //ErrorManager.Logger(this, this.InitializeComponent(), ex.HResult, ex.Data, DateTime.Now); throw ex; } } protected void WinEventCallback(IntPtr hWinEventHook, Hook.SWEH_Events eventType, IntPtr hWnd, Hook.SWEH_ObjectId idObject, long idChild, uint dwEventThread, uint dwmsEventTime) { if (hWnd == NotepadhWnd && eventType == Hook.SWEH_Events.EVENT_OBJECT_LOCATIONCHANGE && idObject == (Hook.SWEH_ObjectId)Hook.SWEH_CHILDID_SELF) { WinApi.RECT rect = Hook.GetWindowRect(hWnd); this.Location = new Point(rect.Right, rect.Top); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { Hook.WinEventUnhook(hWinEventHook); } private void Form1_Shown(object sender, EventArgs e) { if (Target == null) { this.Hide(); MessageBox.Show("Notepad not found!", "Target Missing", MessageBoxButtons.OK, MessageBoxIcon.Hand); this.Close(); } else { this.Size = new Size(50, 140); } } 

用于引用Windows API方法的支持(部分)类:

 public class WinApi { [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } } public class Hook { public static long SWEH_CHILDID_SELF = 0; //SetWinEventHook() flags public enum SWEH_dwFlags : uint { WINEVENT_OUTOFCONTEXT = 0x0000, // Events are ASYNC WINEVENT_SKIPOWNTHREAD = 0x0001, // Don't call back for events on installer's thread WINEVENT_SKIPOWNPROCESS = 0x0002, // Don't call back for events on installer's process WINEVENT_INCONTEXT = 0x0004 // Events are SYNC, this causes your dll to be injected into every process } //SetWinEventHook() events public enum SWEH_Events : uint { EVENT_MIN = 0x00000001, EVENT_MAX = 0x7FFFFFFF, EVENT_SYSTEM_SOUND = 0x0001, EVENT_SYSTEM_ALERT = 0x0002, EVENT_SYSTEM_FOREGROUND = 0x0003, EVENT_SYSTEM_MENUSTART = 0x0004, EVENT_SYSTEM_MENUEND = 0x0005, EVENT_SYSTEM_MENUPOPUPSTART = 0x0006, EVENT_SYSTEM_MENUPOPUPEND = 0x0007, EVENT_SYSTEM_CAPTURESTART = 0x0008, EVENT_SYSTEM_CAPTUREEND = 0x0009, EVENT_SYSTEM_MOVESIZESTART = 0x000A, EVENT_SYSTEM_MOVESIZEEND = 0x000B, EVENT_SYSTEM_CONTEXTHELPSTART = 0x000C, EVENT_SYSTEM_CONTEXTHELPEND = 0x000D, EVENT_SYSTEM_DRAGDROPSTART = 0x000E, EVENT_SYSTEM_DRAGDROPEND = 0x000F, EVENT_SYSTEM_DIALOGSTART = 0x0010, EVENT_SYSTEM_DIALOGEND = 0x0011, EVENT_SYSTEM_SCROLLINGSTART = 0x0012, EVENT_SYSTEM_SCROLLINGEND = 0x0013, EVENT_SYSTEM_SWITCHSTART = 0x0014, EVENT_SYSTEM_SWITCHEND = 0x0015, EVENT_SYSTEM_MINIMIZESTART = 0x0016, EVENT_SYSTEM_MINIMIZEEND = 0x0017, EVENT_SYSTEM_DESKTOPSWITCH = 0x0020, EVENT_SYSTEM_END = 0x00FF, EVENT_OEM_DEFINED_START = 0x0101, EVENT_OEM_DEFINED_END = 0x01FF, EVENT_UIA_EVENTID_START = 0x4E00, EVENT_UIA_EVENTID_END = 0x4EFF, EVENT_UIA_PROPID_START = 0x7500, EVENT_UIA_PROPID_END = 0x75FF, EVENT_CONSOLE_CARET = 0x4001, EVENT_CONSOLE_UPDATE_REGION = 0x4002, EVENT_CONSOLE_UPDATE_SIMPLE = 0x4003, EVENT_CONSOLE_UPDATE_SCROLL = 0x4004, EVENT_CONSOLE_LAYOUT = 0x4005, EVENT_CONSOLE_START_APPLICATION = 0x4006, EVENT_CONSOLE_END_APPLICATION = 0x4007, EVENT_CONSOLE_END = 0x40FF, EVENT_OBJECT_CREATE = 0x8000, // hwnd ID idChild is created item EVENT_OBJECT_DESTROY = 0x8001, // hwnd ID idChild is destroyed item EVENT_OBJECT_SHOW = 0x8002, // hwnd ID idChild is shown item EVENT_OBJECT_HIDE = 0x8003, // hwnd ID idChild is hidden item EVENT_OBJECT_REORDER = 0x8004, // hwnd ID idChild is parent of zordering children EVENT_OBJECT_FOCUS = 0x8005, // hwnd ID idChild is focused item EVENT_OBJECT_SELECTION = 0x8006, // hwnd ID idChild is selected item (if only one), or idChild is OBJID_WINDOW if complex EVENT_OBJECT_SELECTIONADD = 0x8007, // hwnd ID idChild is item added EVENT_OBJECT_SELECTIONREMOVE = 0x8008, // hwnd ID idChild is item removed EVENT_OBJECT_SELECTIONWITHIN = 0x8009, // hwnd ID idChild is parent of changed selected items EVENT_OBJECT_STATECHANGE = 0x800A, // hwnd ID idChild is item w/ state change EVENT_OBJECT_LOCATIONCHANGE = 0x800B, // hwnd ID idChild is moved/sized item EVENT_OBJECT_NAMECHANGE = 0x800C, // hwnd ID idChild is item w/ name change EVENT_OBJECT_DESCRIPTIONCHANGE = 0x800D, // hwnd ID idChild is item w/ desc change EVENT_OBJECT_VALUECHANGE = 0x800E, // hwnd ID idChild is item w/ value change EVENT_OBJECT_PARENTCHANGE = 0x800F, // hwnd ID idChild is item w/ new parent EVENT_OBJECT_HELPCHANGE = 0x8010, // hwnd ID idChild is item w/ help change EVENT_OBJECT_DEFACTIONCHANGE = 0x8011, // hwnd ID idChild is item w/ def action change EVENT_OBJECT_ACCELERATORCHANGE = 0x8012, // hwnd ID idChild is item w/ keybd accel change EVENT_OBJECT_INVOKED = 0x8013, // hwnd ID idChild is item invoked EVENT_OBJECT_TEXTSELECTIONCHANGED = 0x8014, // hwnd ID idChild is item w? test selection change EVENT_OBJECT_CONTENTSCROLLED = 0x8015, EVENT_SYSTEM_ARRANGMENTPREVIEW = 0x8016, EVENT_OBJECT_END = 0x80FF, EVENT_AIA_START = 0xA000, EVENT_AIA_END = 0xAFFF } //SetWinEventHook() Object Ids public enum SWEH_ObjectId : long { OBJID_WINDOW = 0x00000000, OBJID_SYSMENU = 0xFFFFFFFF, OBJID_TITLEBAR = 0xFFFFFFFE, OBJID_MENU = 0xFFFFFFFD, OBJID_CLIENT = 0xFFFFFFFC, OBJID_VSCROLL = 0xFFFFFFFB, OBJID_HSCROLL = 0xFFFFFFFA, OBJID_SIZEGRIP = 0xFFFFFFF9, OBJID_CARET = 0xFFFFFFF8, OBJID_CURSOR = 0xFFFFFFF7, OBJID_ALERT = 0xFFFFFFF6, OBJID_SOUND = 0xFFFFFFF5, OBJID_QUERYCLASSNAMEIDX = 0xFFFFFFF4, OBJID_NATIVEOM = 0xFFFFFFF0 } private static SWEH_dwFlags WinEventHookInternalFlags = SWEH_dwFlags.WINEVENT_OUTOFCONTEXT | SWEH_dwFlags.WINEVENT_SKIPOWNPROCESS | SWEH_dwFlags.WINEVENT_SKIPOWNTHREAD; public delegate void WinEventDelegate(IntPtr hWinEventHook, SWEH_Events eventType, IntPtr hwnd, SWEH_ObjectId idObject, long idChild, uint dwEventThread, uint dwmsEventTime); public static IntPtr WinEventHookRange(SWEH_Events _eventFrom, SWEH_Events _eventTo, WinEventDelegate _delegate, uint idProcess, uint idThread) { new UIPermission(UIPermissionWindow.AllWindows).Demand(); return UnsafeNativeMethods.SetWinEventHook(_eventFrom, _eventTo, IntPtr.Zero, _delegate, idProcess, idThread, WinEventHookInternalFlags); } public static IntPtr WinEventHookOne(SWEH_Events _event, WinEventDelegate _delegate, uint idProcess, uint idThread) { new UIPermission(UIPermissionWindow.AllWindows).Demand(); return UnsafeNativeMethods.SetWinEventHook(_event, _event, IntPtr.Zero, _delegate, idProcess, idThread, WinEventHookInternalFlags); } public static bool WinEventUnhook(IntPtr hWinEventHook) { return UnsafeNativeMethods.UnhookWinEvent(hWinEventHook); } public static uint GetWindowThread(IntPtr hWnd) { new UIPermission(UIPermissionWindow.AllWindows).Demand(); return UnsafeNativeMethods.GetWindowThreadProcessId(hWnd, IntPtr.Zero); } public static WinApi.RECT GetWindowRect(IntPtr hWnd) { WinApi.RECT rect = new WinApi.RECT(); bool _result = SafeNativeMethods.GetWindowRect(hWnd, ref rect); return rect; } } [SuppressUnmanagedCodeSecurityAttribute] internal static class SafeNativeMethods { [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hWnd, ref WinApi.RECT lpRect); } [SuppressUnmanagedCodeSecurityAttribute] internal static class UnsafeNativeMethods { [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr voidProcessId); [DllImport("user32.dll", SetLastError = false)] public static extern IntPtr SetWinEventHook(Hook.SWEH_Events eventMin, Hook.SWEH_Events eventMax, IntPtr hmodWinEventProc, Hook.WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, Hook.SWEH_dwFlags dwFlags); [DllImport("user32.dll", SetLastError = false)] public static extern bool UnhookWinEvent(IntPtr hWinEventHook); } 

感谢@Jimi在这里的帮助。 以下方法有效。

首先,存储对目标进程的引用:

 Process _target = Process.GetProcessesByName("target")[0]; 

然后获取主窗口的句柄:

 IntPtr _tagetHWnd = _target.MainWindowHandle; 

然后初始化钩子:

 SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, TargetMoved, (uint)_foxview.Id, GetWindowThreadProcessId(_foxview.MainWindowHandle, IntPtr.Zero), WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD); 

SetWinEventHook声明如下:

 [DllImport("user32.dll")] private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); 

而涉及的常数是:

 private const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B; private const int HT_CAPTION = 0x2; private const uint WINEVENT_OUTOFCONTEXT = 0x0000; private const uint WINEVENT_SKIPOWNPROCESS = 0x0002; private const uint WINEVENT_SKIPOWNTHREAD = 0x0001; private const int WM_NCLBUTTONDOWN = 0xA1; 

然后在我的TargetMoved方法中,我检查新的Window位置并移动我的叠加层。

 private void TargetMoved(IntPtr hWinEventHook, uint eventType, IntPtr lParam, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { Rect newLocation = new Rect(); GetWindowRect(_foxViewHWnd, ref newLocation); Location = new Point(newLocation.Right - (250 + _currentUser.Length * 7), newLocation.Top + 5); } 

GetWindowRect()定义于:

 [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowRect(IntPtr hWnd, ref Rect lpRect); 

Rect的定义是:

 [StructLayout(LayoutKind.Sequential)] private struct Rect { public readonly int Left; public readonly int Top; public readonly int Right; public readonly int Bottom; } 

所以当你把它们放在一起时,整个类现在看起来像这样:

使用系统; 使用System.Diagnostics; 使用System.Drawing; 使用System.Runtime.InteropServices; 使用System.Windows.Forms; 使用UserMonitor;

 namespace OnScreenOverlay { public partial class Overlay : Form { #region Public Fields public const string UserCache = @"redacted"; #endregion Public Fields #region Private Fields private const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B; private const uint WINEVENT_OUTOFCONTEXT = 0x0000; private const uint WINEVENT_SKIPOWNPROCESS = 0x0002; private const uint WINEVENT_SKIPOWNTHREAD = 0x0001; private readonly Process _foxview; private readonly IntPtr _foxViewHWnd; private readonly UserMon _monitor; private string _currentUser; #endregion Private Fields #region Public Constructors public Overlay() { InitializeComponent(); _target= Process.GetProcessesByName("target")[0]; if (_foxview == null) { MessageBox.Show("No target detected... Closing"); Close(); } _targetHWnd = _target.MainWindowHandle; InitializeWinHook(); StartPosition = FormStartPosition.Manual; Location = new Point(Screen.PrimaryScreen.Bounds.Left + 20, Screen.PrimaryScreen.Bounds.Bottom - 20); ShowInTaskbar = false; _monitor = new UserMon(UserCache); _monitor.UserChanged += (s, a) => { _currentUser = a.Value; if (pictBox.InvokeRequired) { pictBox.Invoke((MethodInvoker)delegate { pictBox.Refresh(); }); } }; _currentUser = _monitor.GetUser(); } #endregion Public Constructors #region Private Delegates private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); #endregion Private Delegates #region Private Methods [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowRect(IntPtr hWnd, ref Rect lpRect); [DllImport("user32.dll", SetLastError = true)] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId); [DllImport("user32.dll")] private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); private void InitializeWinHook() { SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, TargetMoved, (uint)_foxview.Id, GetWindowThreadProcessId(_foxview.MainWindowHandle, IntPtr.Zero), WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD); } private void Overlay_FormClosing(object sender, FormClosingEventArgs e) { _monitor.Dispose(); } private void pictBox_Paint(object sender, PaintEventArgs e) { using (Font myFont = new Font("Arial", 8)) { e.Graphics.DrawString($"User: {_currentUser}", myFont, Brushes.LimeGreen, new Point(2, 2)); } } private void TargetMoved(IntPtr hWinEventHook, uint eventType, IntPtr lParam, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { Rect newLocation = new Rect(); GetWindowRect(_foxViewHWnd, ref newLocation); Location = new Point(newLocation.Right - (250 + _currentUser.Length * 7), newLocation.Top + 5); } #endregion Private Methods #region Private Structs [StructLayout(LayoutKind.Sequential)] private struct Rect { public readonly int Left; public readonly int Top; public readonly int Right; public readonly int Bottom; } #endregion Private Structs } }