Move window when external application's window moves











up vote
3
down vote

favorite
4












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 minimise button.



I can get the Rect representing the "target" process using the following code which I can then pair with an offset to generate the initial position of my overlay.



Get the HWnd IntPtr:



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


Declare the function from user32.dll:



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


And the appropriate struct:



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


And then call it on demand.



However, I would like to avoid constantly polling this value, so I would like to hook into the target application and respond whenever the target window is moved.



Looking around the user32.dll documentation, the only way I can see for doing this is using SetWindowsHookEx(). I'm not entirely sure of how I'd go about intercepting an event from here however.



I believe the target application is built off of WinForms but I cannot be sure. So solutions that let me respond to the target's Move event or directly to some Windows Message would both be useful.



Any ideas on how I can proceed?










share|improve this question


















  • 1




    Consider using SetWinEventHook() instead of SetWindowsHookEx()
    – Remy Lebeau
    Feb 13 at 16:09










  • @Remy Lebeau I was wondering whether you had the chance to test SetWinEventHook() in a context like this. You probably have to register multple (or range of) events to "follow" a window and, AFAIK, SetWinEventHook() has a lot of overhead. Could this cause perceivable lags in the synchronized twin-window movement?
    – Jimi
    Feb 13 at 17:42






  • 1




    The first null indicates that the hook proc is not contained in a .dll, should be IntPtr.Zero. targetPID is the process.Id (I think you mean that). The second null I think is best substituted with GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero), otherwise you'll get the events of all the threads on the current desktop. The last null represents the flags that define the hook position. Since you have IntPtr.Zero as handle, those flags should be WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD.
    – Jimi
    Feb 14 at 2:54








  • 1




    Well, I'm glad you did it. If you want, I can post the methods I use to hook a window with SetWinEventHook(), for comparison. But if you want to post a self-answer, it's ok. Maybe I'll post mine after, for the same reason.
    – Jimi
    Feb 15 at 16:37








  • 1




    Allright then, I'll prepare something. I think it's an interesting matter that you don't see very often.
    – Jimi
    Feb 15 at 16:50















up vote
3
down vote

favorite
4












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 minimise button.



I can get the Rect representing the "target" process using the following code which I can then pair with an offset to generate the initial position of my overlay.



Get the HWnd IntPtr:



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


Declare the function from user32.dll:



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


And the appropriate struct:



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


And then call it on demand.



However, I would like to avoid constantly polling this value, so I would like to hook into the target application and respond whenever the target window is moved.



Looking around the user32.dll documentation, the only way I can see for doing this is using SetWindowsHookEx(). I'm not entirely sure of how I'd go about intercepting an event from here however.



I believe the target application is built off of WinForms but I cannot be sure. So solutions that let me respond to the target's Move event or directly to some Windows Message would both be useful.



Any ideas on how I can proceed?










share|improve this question


















  • 1




    Consider using SetWinEventHook() instead of SetWindowsHookEx()
    – Remy Lebeau
    Feb 13 at 16:09










  • @Remy Lebeau I was wondering whether you had the chance to test SetWinEventHook() in a context like this. You probably have to register multple (or range of) events to "follow" a window and, AFAIK, SetWinEventHook() has a lot of overhead. Could this cause perceivable lags in the synchronized twin-window movement?
    – Jimi
    Feb 13 at 17:42






  • 1




    The first null indicates that the hook proc is not contained in a .dll, should be IntPtr.Zero. targetPID is the process.Id (I think you mean that). The second null I think is best substituted with GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero), otherwise you'll get the events of all the threads on the current desktop. The last null represents the flags that define the hook position. Since you have IntPtr.Zero as handle, those flags should be WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD.
    – Jimi
    Feb 14 at 2:54








  • 1




    Well, I'm glad you did it. If you want, I can post the methods I use to hook a window with SetWinEventHook(), for comparison. But if you want to post a self-answer, it's ok. Maybe I'll post mine after, for the same reason.
    – Jimi
    Feb 15 at 16:37








  • 1




    Allright then, I'll prepare something. I think it's an interesting matter that you don't see very often.
    – Jimi
    Feb 15 at 16:50













up vote
3
down vote

favorite
4









up vote
3
down vote

favorite
4






4





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 minimise button.



I can get the Rect representing the "target" process using the following code which I can then pair with an offset to generate the initial position of my overlay.



Get the HWnd IntPtr:



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


Declare the function from user32.dll:



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


And the appropriate struct:



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


And then call it on demand.



However, I would like to avoid constantly polling this value, so I would like to hook into the target application and respond whenever the target window is moved.



Looking around the user32.dll documentation, the only way I can see for doing this is using SetWindowsHookEx(). I'm not entirely sure of how I'd go about intercepting an event from here however.



I believe the target application is built off of WinForms but I cannot be sure. So solutions that let me respond to the target's Move event or directly to some Windows Message would both be useful.



Any ideas on how I can proceed?










share|improve this question













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 minimise button.



I can get the Rect representing the "target" process using the following code which I can then pair with an offset to generate the initial position of my overlay.



Get the HWnd IntPtr:



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


Declare the function from user32.dll:



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


And the appropriate struct:



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


And then call it on demand.



However, I would like to avoid constantly polling this value, so I would like to hook into the target application and respond whenever the target window is moved.



Looking around the user32.dll documentation, the only way I can see for doing this is using SetWindowsHookEx(). I'm not entirely sure of how I'd go about intercepting an event from here however.



I believe the target application is built off of WinForms but I cannot be sure. So solutions that let me respond to the target's Move event or directly to some Windows Message would both be useful.



Any ideas on how I can proceed?







c# winforms winapi pinvoke user32






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Feb 13 at 12:53









Persistence

1,36921234




1,36921234








  • 1




    Consider using SetWinEventHook() instead of SetWindowsHookEx()
    – Remy Lebeau
    Feb 13 at 16:09










  • @Remy Lebeau I was wondering whether you had the chance to test SetWinEventHook() in a context like this. You probably have to register multple (or range of) events to "follow" a window and, AFAIK, SetWinEventHook() has a lot of overhead. Could this cause perceivable lags in the synchronized twin-window movement?
    – Jimi
    Feb 13 at 17:42






  • 1




    The first null indicates that the hook proc is not contained in a .dll, should be IntPtr.Zero. targetPID is the process.Id (I think you mean that). The second null I think is best substituted with GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero), otherwise you'll get the events of all the threads on the current desktop. The last null represents the flags that define the hook position. Since you have IntPtr.Zero as handle, those flags should be WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD.
    – Jimi
    Feb 14 at 2:54








  • 1




    Well, I'm glad you did it. If you want, I can post the methods I use to hook a window with SetWinEventHook(), for comparison. But if you want to post a self-answer, it's ok. Maybe I'll post mine after, for the same reason.
    – Jimi
    Feb 15 at 16:37








  • 1




    Allright then, I'll prepare something. I think it's an interesting matter that you don't see very often.
    – Jimi
    Feb 15 at 16:50














  • 1




    Consider using SetWinEventHook() instead of SetWindowsHookEx()
    – Remy Lebeau
    Feb 13 at 16:09










  • @Remy Lebeau I was wondering whether you had the chance to test SetWinEventHook() in a context like this. You probably have to register multple (or range of) events to "follow" a window and, AFAIK, SetWinEventHook() has a lot of overhead. Could this cause perceivable lags in the synchronized twin-window movement?
    – Jimi
    Feb 13 at 17:42






  • 1




    The first null indicates that the hook proc is not contained in a .dll, should be IntPtr.Zero. targetPID is the process.Id (I think you mean that). The second null I think is best substituted with GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero), otherwise you'll get the events of all the threads on the current desktop. The last null represents the flags that define the hook position. Since you have IntPtr.Zero as handle, those flags should be WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD.
    – Jimi
    Feb 14 at 2:54








  • 1




    Well, I'm glad you did it. If you want, I can post the methods I use to hook a window with SetWinEventHook(), for comparison. But if you want to post a self-answer, it's ok. Maybe I'll post mine after, for the same reason.
    – Jimi
    Feb 15 at 16:37








  • 1




    Allright then, I'll prepare something. I think it's an interesting matter that you don't see very often.
    – Jimi
    Feb 15 at 16:50








1




1




Consider using SetWinEventHook() instead of SetWindowsHookEx()
– Remy Lebeau
Feb 13 at 16:09




Consider using SetWinEventHook() instead of SetWindowsHookEx()
– Remy Lebeau
Feb 13 at 16:09












@Remy Lebeau I was wondering whether you had the chance to test SetWinEventHook() in a context like this. You probably have to register multple (or range of) events to "follow" a window and, AFAIK, SetWinEventHook() has a lot of overhead. Could this cause perceivable lags in the synchronized twin-window movement?
– Jimi
Feb 13 at 17:42




@Remy Lebeau I was wondering whether you had the chance to test SetWinEventHook() in a context like this. You probably have to register multple (or range of) events to "follow" a window and, AFAIK, SetWinEventHook() has a lot of overhead. Could this cause perceivable lags in the synchronized twin-window movement?
– Jimi
Feb 13 at 17:42




1




1




The first null indicates that the hook proc is not contained in a .dll, should be IntPtr.Zero. targetPID is the process.Id (I think you mean that). The second null I think is best substituted with GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero), otherwise you'll get the events of all the threads on the current desktop. The last null represents the flags that define the hook position. Since you have IntPtr.Zero as handle, those flags should be WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD.
– Jimi
Feb 14 at 2:54






The first null indicates that the hook proc is not contained in a .dll, should be IntPtr.Zero. targetPID is the process.Id (I think you mean that). The second null I think is best substituted with GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero), otherwise you'll get the events of all the threads on the current desktop. The last null represents the flags that define the hook position. Since you have IntPtr.Zero as handle, those flags should be WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD.
– Jimi
Feb 14 at 2:54






1




1




Well, I'm glad you did it. If you want, I can post the methods I use to hook a window with SetWinEventHook(), for comparison. But if you want to post a self-answer, it's ok. Maybe I'll post mine after, for the same reason.
– Jimi
Feb 15 at 16:37






Well, I'm glad you did it. If you want, I can post the methods I use to hook a window with SetWinEventHook(), for comparison. But if you want to post a self-answer, it's ok. Maybe I'll post mine after, for the same reason.
– Jimi
Feb 15 at 16:37






1




1




Allright then, I'll prepare something. I think it's an interesting matter that you don't see very often.
– Jimi
Feb 15 at 16:50




Allright then, I'll prepare something. I think it's an interesting matter that you don't see very often.
– Jimi
Feb 15 at 16:50












2 Answers
2






active

oldest

votes

















up vote
7
down vote













This one shows how to hook a Winform to another process (notepad, in this case) and follow the movements of the process Main Window, to create sort of a toolbar that can interact with the process.



The main API function used is SetWinEventHook()


A visual representation of the results:

enter image description here



The Form class initialization procedure:



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


The support (partial) classes used to reference the Windows API methods:



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





share|improve this answer




























    up vote
    1
    down vote



    accepted










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





    share|improve this answer

















    • 2




      I thought you'ld make it a self-answer. Thanks. I have to say, maybe you're new to Win API, but you've got it pretty good. One thing. In your WinEventDelegate (TargetMoved), it may happen that you get two different window handles (both the Main process and the Main Child Window in sequence). Take a look at what I implemented in the same situation, it might save you some time. For the rest, it's pretty much the same code.
      – Jimi
      Feb 15 at 17:55











    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f48767318%2fmove-window-when-external-applications-window-moves%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    7
    down vote













    This one shows how to hook a Winform to another process (notepad, in this case) and follow the movements of the process Main Window, to create sort of a toolbar that can interact with the process.



    The main API function used is SetWinEventHook()


    A visual representation of the results:

    enter image description here



    The Form class initialization procedure:



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


    The support (partial) classes used to reference the Windows API methods:



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





    share|improve this answer

























      up vote
      7
      down vote













      This one shows how to hook a Winform to another process (notepad, in this case) and follow the movements of the process Main Window, to create sort of a toolbar that can interact with the process.



      The main API function used is SetWinEventHook()


      A visual representation of the results:

      enter image description here



      The Form class initialization procedure:



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


      The support (partial) classes used to reference the Windows API methods:



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





      share|improve this answer























        up vote
        7
        down vote










        up vote
        7
        down vote









        This one shows how to hook a Winform to another process (notepad, in this case) and follow the movements of the process Main Window, to create sort of a toolbar that can interact with the process.



        The main API function used is SetWinEventHook()


        A visual representation of the results:

        enter image description here



        The Form class initialization procedure:



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


        The support (partial) classes used to reference the Windows API methods:



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





        share|improve this answer












        This one shows how to hook a Winform to another process (notepad, in this case) and follow the movements of the process Main Window, to create sort of a toolbar that can interact with the process.



        The main API function used is SetWinEventHook()


        A visual representation of the results:

        enter image description here



        The Form class initialization procedure:



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


        The support (partial) classes used to reference the Windows API methods:



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






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Feb 15 at 17:16









        Jimi

        6,05121033




        6,05121033
























            up vote
            1
            down vote



            accepted










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





            share|improve this answer

















            • 2




              I thought you'ld make it a self-answer. Thanks. I have to say, maybe you're new to Win API, but you've got it pretty good. One thing. In your WinEventDelegate (TargetMoved), it may happen that you get two different window handles (both the Main process and the Main Child Window in sequence). Take a look at what I implemented in the same situation, it might save you some time. For the rest, it's pretty much the same code.
              – Jimi
              Feb 15 at 17:55















            up vote
            1
            down vote



            accepted










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





            share|improve this answer

















            • 2




              I thought you'ld make it a self-answer. Thanks. I have to say, maybe you're new to Win API, but you've got it pretty good. One thing. In your WinEventDelegate (TargetMoved), it may happen that you get two different window handles (both the Main process and the Main Child Window in sequence). Take a look at what I implemented in the same situation, it might save you some time. For the rest, it's pretty much the same code.
              – Jimi
              Feb 15 at 17:55













            up vote
            1
            down vote



            accepted







            up vote
            1
            down vote



            accepted






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





            share|improve this answer












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






            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Feb 15 at 16:54









            Persistence

            1,36921234




            1,36921234








            • 2




              I thought you'ld make it a self-answer. Thanks. I have to say, maybe you're new to Win API, but you've got it pretty good. One thing. In your WinEventDelegate (TargetMoved), it may happen that you get two different window handles (both the Main process and the Main Child Window in sequence). Take a look at what I implemented in the same situation, it might save you some time. For the rest, it's pretty much the same code.
              – Jimi
              Feb 15 at 17:55














            • 2




              I thought you'ld make it a self-answer. Thanks. I have to say, maybe you're new to Win API, but you've got it pretty good. One thing. In your WinEventDelegate (TargetMoved), it may happen that you get two different window handles (both the Main process and the Main Child Window in sequence). Take a look at what I implemented in the same situation, it might save you some time. For the rest, it's pretty much the same code.
              – Jimi
              Feb 15 at 17:55








            2




            2




            I thought you'ld make it a self-answer. Thanks. I have to say, maybe you're new to Win API, but you've got it pretty good. One thing. In your WinEventDelegate (TargetMoved), it may happen that you get two different window handles (both the Main process and the Main Child Window in sequence). Take a look at what I implemented in the same situation, it might save you some time. For the rest, it's pretty much the same code.
            – Jimi
            Feb 15 at 17:55




            I thought you'ld make it a self-answer. Thanks. I have to say, maybe you're new to Win API, but you've got it pretty good. One thing. In your WinEventDelegate (TargetMoved), it may happen that you get two different window handles (both the Main process and the Main Child Window in sequence). Take a look at what I implemented in the same situation, it might save you some time. For the rest, it's pretty much the same code.
            – Jimi
            Feb 15 at 17:55


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f48767318%2fmove-window-when-external-applications-window-moves%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            404 Error Contact Form 7 ajax form submitting

            How to know if a Active Directory user can login interactively

            Refactoring coordinates for Minecraft Pi buildings written in Python