Accessing Monitor Information with C#, Part 2: Getting a Monitor associated with a Window Handle
You are here:
Home
/
Technical Babble
/ Accessing Monitor Information with C#, Part 2: Getting a Monitor associated with a Window Handle
Categories:
.Net
,
C#
,
Monitors
,
Native Methods
,
Unmanaged Code
,
WPF
by Joshua Arzt
This is the second of a 2-part series on Accessing Monitor Information with C#.
The series will include the following:
Part 1: Getting Monitor Handles
Part 2: Getting a Monitor associated with a Window Handle
Like I said in
Part 1
this uses Native Methods so don’t act surprised if I drop a few in this post.
Note:
if easily startled by unmanaged code, please seek the help of a medical professional. Otherwise, read on.
So, to continue, we are going to start by using our
GetMonitors
method to grab the current set of Monitors associated with our computer:
private static IList<MonitorInfoWithHandle> _monitorInfos;
/// <summary>
/// Gets the monitors.
/// </summary>
/// <returns></returns>
public static MonitorInfoWithHandle[] GetMonitors()
// New List
_monitorInfos = new List<MonitorInfoWithHandle>();
// Enumerate monitors
NativeMonitorHelper.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnum, IntPtr.Zero);
// Return list
return _monitorInfos.ToArray();
Now, we won’t be using this method directly in this post, but you should observe the results to understand which handles are associated with each monitor on your machine. For this purposes of this exercise, we will be looking from the
process-side
and determining which monitor handle it relates to.
So, to continue we look to the native method
EnumWindows
:
EnumWindows
Enumerates all top-level windows on the screen by passing the handle to each window, in turn, to an application-defined callback function.
EnumWindows
continues until the last top-level window is enumerated or the callback function returns false.
EnumWindowsProc
An application-defined callback function used with the
EnumWindows
or
EnumDesktopWindows
function. It receives top-level window handles. The WNDENUMPROC type defines a pointer to this callback function. EnumWindowsProc is a placeholder for the application-defined function name.
/// <summary>
/// EnumWindows Processor (delegate)
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="lParam">The l parameter.</param>
/// <returns></returns>
public delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lParam);
/// <summary>
/// Enums the windows.
/// </summary>
/// <param name="enumWindowsProcessorDelegate">The enum windows processor delegate.</param>
/// <param name="lParam">The l parameter.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool EnumWindows(EnumWindowsProc enumWindowsProcessorDelegate, IntPtr lParam);
The key to using this method is to provide it a delegate which will act on each instance discovered and help us extract the results.
To get the monitor related to a window handle we need to first get the
RECT
the window occupies with
GetWindowRect
.
GetWindowRect
Retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.
/// <summary>
/// Gets the rectangle representing the frame of a window.
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="rectangle">The rectangle.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr windowHandle, ref RECT rectangle);
Next we need to derive the monitor from the RECT found in our last operation and we will do that using
MonitorFromRect
:
MonitorFromRect
The
MonitorFromRect
function retrieves a handle to the display monitor that has the largest area of intersection with a specified rectangle.
/// <summary>
/// Monitors from rect.
/// </summary>
/// <param name="rectPointer">The RECT pointer.</param>
/// <param name="flags">The flags.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromRect([In] ref RECT rectPointer, uint flags);
So, now let’s put that all together with an example:
#region Members
private const uint MONITOR_DEFAULTTONEAREST = 0x00000002;
private IList<WindowAndMonitorHandle> _windowAndMonitorHandles;
#endregion
#region Methods
/// <summary>
/// Retrieves a list of all main window handles and their associated process id's.
/// </summary>
/// <returns></returns>
public static WindowAndMonitorHandle[] GetWindowAndMonitorHandles()
// new list
_windowAndMonitorHandles = new List<WindowAndMonitorHandle>();
// Enumerate windows
WindowHelper.EnumWindows(EnumTheWindows, IntPtr.Zero);
// Return list
return _windowAndMonitorHandles.ToArray();
/// <summary>
/// Enumerates through each window.
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="lParam">The l parameter.</param>
/// <returns></returns>
private static bool EnumTheWindows(IntPtr windowHandle, IntPtr lParam)
// Get window area
var rect = new RECT();
MonitorHelper.GetWindowRect(windowHandle, ref rect);
// Get current monitor
var monitorHandle = MonitorHelper.MonitorFromRect(ref rect, MONITOR_DEFAULTTONEAREST);
// Add to enumerated windows
_windowAndMonitorHandles.Add(new WindowAndMonitorHandle(windowHandle, monitorHandle));
return true;
#endregion
#region Native Methods
/// <summary>
/// EnumWindows Processor (delegate)
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="lParam">The lparameter.</param>
/// <returns></returns>
public delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lParam);
/// <summary>
/// Enums the windows.
/// </summary>
/// <param name="enumWindowsProcessorDelegate">The enum windows processor delegate.</param>
/// <param name="lParam">The lparameter.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool EnumWindows(EnumWindowsProc enumWindowsProcessorDelegate, IntPtr lParam);
/// <summary>
/// Gets the rectangle representing the frame of a window.
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="rectangle">The rectangle.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr windowHandle, ref RECT rectangle);
/// <summary>
/// Monitors from rect.
/// </summary>
/// <param name="rectPointer">The RECT pointer.</param>
/// <param name="flags">The flags.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromRect([In] ref RECT rectPointer, uint flags);
#endregion
/// <summary>
/// A simple class to group our handles.
/// </summary>
/// <returns></returns>
public class WindowAndMonitorHandle
public IntPtr WindowHandle { get; }
public IntPtr MonitorHandle { get; }
public WindowAndMonitorHandle(IntPtr windowHandle, IntPtr monitorHandle)
WindowHandle = windowHandle;
MonitorHandle = monitorHandle;
Does it seem like a lot of work? Yes, definitely. With native methods,
PAIN = GAIN
. Don’t concern yourself with how much code it takes, but focus on what you are actually gaining. If you compare the monitor handle for each
WindowAndMonitorHandle
with what you returned from
GetMonitors
you should immediately be able to relate a window handle to a monitor.
So, until next time…
Like this:
Like
Loading...
Related
Thanks a lot. But in order for this example to work, you have to remove ‘WindowHelper.’ in line 20. Other than that, really helpful.
Accessing Monitor Information with C#, Part 2: Getting a Monitor associated with a Window Handle
Accessing Monitor Information with C#, Part 1: Getting Monitor Handles
Trap when Alt+Tab is pressed in a WPF Application