Move window when external application's window moves
up vote
3
down vote
favorite
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
|
show 9 more comments
up vote
3
down vote
favorite
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
1
Consider usingSetWinEventHook()
instead ofSetWindowsHookEx()
– Remy Lebeau
Feb 13 at 16:09
@Remy Lebeau I was wondering whether you had the chance to testSetWinEventHook()
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 beIntPtr.Zero
.targetPID
is the process.Id (I think you mean that). The second null I think is best substituted withGetWindowThreadProcessId(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 haveIntPtr.Zero
as handle, those flags should beWINEVENT_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 withSetWinEventHook()
, 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
|
show 9 more comments
up vote
3
down vote
favorite
up vote
3
down vote
favorite
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
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
c# winforms winapi pinvoke user32
asked Feb 13 at 12:53
Persistence
1,36921234
1,36921234
1
Consider usingSetWinEventHook()
instead ofSetWindowsHookEx()
– Remy Lebeau
Feb 13 at 16:09
@Remy Lebeau I was wondering whether you had the chance to testSetWinEventHook()
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 beIntPtr.Zero
.targetPID
is the process.Id (I think you mean that). The second null I think is best substituted withGetWindowThreadProcessId(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 haveIntPtr.Zero
as handle, those flags should beWINEVENT_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 withSetWinEventHook()
, 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
|
show 9 more comments
1
Consider usingSetWinEventHook()
instead ofSetWindowsHookEx()
– Remy Lebeau
Feb 13 at 16:09
@Remy Lebeau I was wondering whether you had the chance to testSetWinEventHook()
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 beIntPtr.Zero
.targetPID
is the process.Id (I think you mean that). The second null I think is best substituted withGetWindowThreadProcessId(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 haveIntPtr.Zero
as handle, those flags should beWINEVENT_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 withSetWinEventHook()
, 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
|
show 9 more comments
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:
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);
}
add a comment |
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
}
}
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 yourWinEventDelegate
(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
add a comment |
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:
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);
}
add a comment |
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:
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);
}
add a comment |
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:
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);
}
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:
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);
}
answered Feb 15 at 17:16
Jimi
6,05121033
6,05121033
add a comment |
add a comment |
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
}
}
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 yourWinEventDelegate
(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
add a comment |
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
}
}
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 yourWinEventDelegate
(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
add a comment |
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
}
}
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
}
}
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 yourWinEventDelegate
(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
add a comment |
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 yourWinEventDelegate
(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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
1
Consider using
SetWinEventHook()
instead ofSetWindowsHookEx()
– 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 withGetWindowThreadProcessId(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 haveIntPtr.Zero
as handle, those flags should beWINEVENT_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