Move window when external application's window moves

前端 未结 2 1733
死守一世寂寞
死守一世寂寞 2020-11-29 06:18

I\'ve got an always on-top application (basically a status display) that I want to follow around another program and always sit just to the left of the minimize button.

2条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-11-29 06:57

    Thanks to @Jimi for his help here. The following method worked.

    First, store a reference to the target process:

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

    Then get the handle to the main window:

    IntPtr _tagetHWnd = _target.MainWindowHandle;
    

    Then initialise the hook:

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

    Where SetWinEventHook is declared as such:

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

    And the constants involved are:

    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;
    

    Then in my TargetMoved method I get check the new Window location and move my overlay.

    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);
    }
    

    Where GetWindowRect() is defined by:

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

    And Rect is defined by:

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

    So when you put it all together, the entire class now looks like this:

    using System;
    using System.Diagnostics;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using 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
        }
    }
    

提交回复
热议问题