Using user32.dll
and C# I wrote the method that you see below. Using a process handle for a window, it will set the window position at a provided {x,y}
location.
However, in a multi-monitored environment the code below sets the window position to the primary monitor, only. I would like to be able to select which monitor, too.
Can someone please explain how this can be accomplished using SetWindowPos
or perhaps a combination with another user32.dll
function?
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOZORDER = 0x0004;
private const int SWP_SHOWWINDOW = 0x0040;
public static void SetWindowPosition(Process p, int x, int y)
{
IntPtr handle = p.MainWindowHandle;
if (handle != IntPtr.Zero)
{
SetWindowPos(handle, IntPtr.Zero, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
}
}
Solution based upon Jimi's comment.
Here is my monitor configuration:
Observe that I have a secondary monitor to the left of my primary monitor. After reading the Virtual Monitor link that Jimi provided I discovered that to move windows to the secondary monitor that I must use a negative x-value because it is left of the primary monitor's origin (top left corner, or <0,0>).
Therefore, if I want to have my window position set to the secondary monitor's <0,0> coordinate, that I must SUBTRACT the x-width of the secondary monitor from the origin of the primary monitor, like this:
<0,0> - <1920,0> = <-1920,0>
Now, when I call SetWindowPosition in my client code, I call it like this:
SetWindowPosition(Process p, -1920, 0);
Note: I do not know what you would do if the monitors have different resolutions. That is a more complex topic and not a question that I am asking. Also, I did not see a need to explore deeper in to the topic as the simple example above solved all of my issues.
System Displays disposition and VirtualScreen
In a Windows System, the Primary Screen (programming perspective) is the Display device which has its upper left corner position set at
Point(0,0)
.This implies that the Displays positioned on the left of the Primary Screen, will have negative
X
coordinates (theY
coordinate could be negative if the Display is in Portrait layout).The Displays on the right will have positive
X
coordinates (theY
coordinate could be negative if the Display is in Portrait layout).Displays on the Left of the Primary Screen:
In other words, Displays that have a negative
Point.X
originThe
Point.X
origin is the sum of all of the precedingScreens[].Width
, subtracted from thePoint.X
origin coordinate of the Primary Screen.Displays on the Right of the Primary Screen:
In other words, Displays that have a positive
Point.X
originThe
Point.X
origin is the sum of all of the precedingScreens[].Width
, Primary included, added to the originPoint.X
coordinate of the Primary Screen.Important note:
If the application is not DPIAware, all these measures can be compromised by the virtualization and automatic DPI Scaling performed by the System. All measures will be uniformed to a default 96 Dpi: the applicaton will receive scaled values. This also includes the values retrieved from non-DpiAware Win32 Api functions. See:
High DPI Desktop Application Development on Windows
Enable support for all targeted Systems in the
app.manifest
file, uncommenting the required sections.Add/Uncomment the DpiAware and DpiAwareness sections in the
app.manifest
file.The PerMonitorV2 Dpi Awareness mode can be set in the
app.config
file (available from Windows 10 Creators Edition).Example:
Consider a System with 3 Monitors:
If we change, using the System applet, the Primary Screen reference, setting it to
\\.\DISPLAY3
, the coordinates will be modified accordingly:Virtual Screen
The Virtual Screen is a virtual display, which dimensions are represented by:
Origin: the origin coordinate of the left-most
Screen
Width: the sum of all the
Screens
Widths.Height: the Height of the highest
Screen
These measure are reported by SystemInformation.VirtualScreen
The Primary Screen
Size
is reported by SystemInformation.PrimaryMonitorSizeAll the Screens current measures and position can also be retrieved using Screen.AllScreens and inspecting each
\\.\DISPLAY[N]
properties.Using the preceding example as reference, in the first disposition, the
VirtualScreen
bounds are:In the second disposition, the
VirtualScreen
bounds are:Window Position inside a Display area:
The Screen class offers multiple methods that can be used to determine in which screen a specific window is currently displayed:
Screen.FromControl([Control reference])
Returns the
Screen
object that contains the largest section of the specifiedControl
reference.Screen.FromHandle([Window Handle])
Returns the
Screen
object that contains the largest section of the Window\Control referenced by anHandle
Screen.FromPoint([Point])
Returns the
Screen
object that contains a specificPoint
Screen.FromRectangle([Rectangle])
Returns the
Screen
object that contains the largest section of the specifiedRectangle
Screen.GetBounds()
(overloaded)Returns a
Rectangle
structure that references the Screen Bounds that contain:- a specific
Point
- largest section of the specified
Rectangle
- A
Control
referenceTo determine the
\\.\DISPLAY[N]
in which the current Form is shown, call (for example):To determine in which Screen a secondary Form is shown:
(Using the sample Displays in the example)
screenSize
will be = to\\.\DISPLAY3
Bounds.screen
will be theScreen
object representing the\\.\DISPLAY3
properties.screen
object will also report the\\.\DISPLAY[N]
name of theScreen
in whichform2
is shown.Obtain the
hMonitor
Handle of a Screen object:The .NET Reference Source show that the
hMonitor
is returned calling[Screen].GetHashCode();
Or using the same native Win32 functions:
MonitorFromWindow, MonitorFromPoint and MonitorFromRect
Obtain a Handle of the device context of a Screen:
A generic method to retrieve the hDC of any Display available.
The Screen coordinates or Screen Device can determined using one of the methods previously described when only a specific Screen reference is required.
The Screen.DeviceName property can be used as the
lpszDriver
parameter of GDI's CreateDC function. It will return the hDC of the display that Graphics.FromHdc can use to create a valid Graphics object, which will allow to paint on a specific screen.Here, assuming at least two Displays are available: