2

I'm trying to stick my form to a window of another application (let's say Microsoft Outlook). When I move the Outlook window, my form should still stick at the right-hand side of it.

At the moment, I'm monitoring Outlook's position in a while(true) loop (with a sleep()) and adjusting my form's position to it.

Here are two problems:

  • If the sleep() duration is too short, it takes much performance to check Outlook's position and to adjust my form that often.
  • If the sleep() duration is too long, my form is too slow in adjusting to Outlook (it lags).

Isn't there a native solution for this?

halloei
  • 1,892
  • 1
  • 25
  • 45
  • Don't do your adjustment in a while loop with a sleep : use a timer. Also, test if location is unchanged before setting the property. – Graffito Aug 05 '15 at 12:33
  • You can do it with SetWinEventHook(), EVENT_OBJECT_LOCATIONCHANGE notification. – Hans Passant Aug 05 '15 at 12:47
  • Possible duplicate of [Move window when external application's window moves](https://stackoverflow.com/questions/48767318/move-window-when-external-applications-window-moves) – Martin Schneider Apr 17 '19 at 09:04
  • @MA-Maddin Your linked question is rather a duplicate of mine, as my question is over 2 years older. – halloei Apr 17 '19 at 09:45
  • @halloei that's completely right and did it the other way first, but then found this Meta QA: [Should I vote to close a duplicate question, even though it's much newer, and has more up to date answers?](https://meta.stackexchange.com/q/147643/344075) – Martin Schneider Apr 17 '19 at 10:06

3 Answers3

2

you have to get a hook on the process and listen to event

this should give you a good starting point

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private const uint WINEVENT_OUTOFCONTEXT = 0x0000;
        private const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B;

        private const uint EVENT_SYSTEM_MOVESIZESTART = 0x000A;
        private const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Width = 100;
            this.Height = 100;
            this.TopMost = true;
            int processId = Process.GetProcessesByName("OUTLOOK")[0].Id;

            //this will also be triggered by mouse moving over the process windows
            //NativeMethods.SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);

            NativeMethods.SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
        }

        private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
        {
            Rect move = new Rect();

            if (eventType == EVENT_SYSTEM_MOVESIZESTART)
            {
                NativeMethods.GetWindowRect(hwnd, ref move);

                Debug.WriteLine("EVENT_SYSTEM_MOVESIZESTART");
            }
            else if (eventType == EVENT_SYSTEM_MOVESIZEEND)
            {
                NativeMethods.GetWindowRect(hwnd, ref move);

                Debug.WriteLine("EVENT_SYSTEM_MOVESIZEEND");
            }

            this.Left = move.Left;
            this.Top = move.Top;
        }
    }

    public struct Rect
    {
        public int Left { get; set; }
        public int Top { get; set; }
        public int Right { get; set; }
        public int Bottom { get; set; }
    }

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

        public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

        [DllImport("user32.dll")]
        public static extern bool GetWindowRect(IntPtr hwnd, ref Rect rectangle);
    }
}
Fredou
  • 19,848
  • 10
  • 58
  • 113
  • Is there a way to listen to RESIZING event? For example, if user is dragging the Outlook form down by the top border, your form will reposition itself only after resize is done, so there will be a noticeable lag. – Victor Zakharov Dec 28 '15 at 16:11
1

You could use any of the windows hook functions WH_GETMESSAGE, WH_GETMESSAGERET, WH_SYSMSGFILTER or WH_MSGFILTER.

In this case you would be interested in WM_MOVE and WM_MOVING, where WM_MOVE is sent after the window is moved (aka, done moving), and WM_MOVING is sent while the window is moved (and so you will get alot of those).

Start here with reading:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644959(v=vs.85).aspx

Mark Jansen
  • 1,491
  • 12
  • 24
0

The answer from Fredou was allmost the answer.

I used it in my application, but got an exception "callbackoncollecteddelegate". To make it work its neccessary to attach the WinEventProc on a property.

...
private NativeMethods.WinEventDelegate winEventProc;
private void Form1_Load(object sender, EventArgs e)
    {
        this.Width = 100;
        this.Height = 100;
        this.TopMost = true;
        int processId = Process.GetProcessesByName("OUTLOOK")[0].Id;

        //this will also be triggered by mouse moving over the process windows
        //NativeMethods.SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
        this.winEventProc = new NativeMethods.WinEventDelegate(WinEventProc);

        NativeMethods.SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, this.winEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
    }
...

See CallbackOnCollectedDelegate at Application.Run(new Form1())

Community
  • 1
  • 1
Gideon Mulder
  • 344
  • 3
  • 6