c# windows user32

c# - Usando SetWindowPos con mĂșltiples monitores



windows user32 (1)

Usando user32.dll y C # escribí el método que se ve a continuación. Usando un identificador de proceso para una ventana, establecerá la posición de la ventana en una ubicación proporcionada {x,y} .

Sin embargo, en un entorno con múltiples monitores, el siguiente código establece la posición de la ventana en el monitor principal, solo. Me gustaría poder seleccionar qué monitor, también.
¿Alguien puede explicar cómo se puede lograr esto usando SetWindowPos o quizás una combinación con otra función user32.dll ?

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

Solución basada en el comentario de Jimi.

Aquí está mi configuración de monitor:

Observe que tengo un monitor secundario a la izquierda de mi monitor principal. Después de leer el enlace del monitor virtual que proporcionó Jimi, descubrí que para mover ventanas al monitor secundario debo usar un valor x negativo porque está a la izquierda del origen del monitor principal (esquina superior izquierda o <0,0>).

Por lo tanto, si quiero establecer la posición de mi ventana en la coordenada <0,0> del monitor secundario, debo SUBREGIR el ancho x del monitor secundario desde el origen del monitor primario, de esta manera:

<0,0> - <1920,0> = <-1920,0>

Ahora, cuando llamo a SetWindowPosition en mi código de cliente, lo llamo así:

SetWindowPosition(Process p, -1920, 0);

Nota: No sé qué haría si los monitores tuvieran diferentes resoluciones. Ese es un tema más complejo y no una pregunta que estoy haciendo. Además, no vi la necesidad de profundizar en el tema, ya que el sencillo ejemplo anterior resolvió todos mis problemas.


Sistema de disposición de pantallas y pantalla virtual.

En un sistema Windows, la pantalla principal (perspectiva de programación) es el dispositivo de visualización que tiene su posición en la esquina superior izquierda establecida en el Point(0,0) .

Esto implica que las pantallas ubicadas a la izquierda de la pantalla principal tendrán coordenadas X negativas (la coordenada Y podría ser negativa si la pantalla está en diseño vertical).
Las pantallas de la derecha tendrán coordenadas X positivas (la coordenada Y podría ser negativa si la pantalla está en diseño vertical).

Aparece a la izquierda de la pantalla principal :
En otras palabras, las pantallas que tienen un origen Point.X negativo Point.X
El origen de Point.X es la suma de todas las Screens[].Width anteriores Screens[].Width , restado de la coordenada de origen de Point.X de la pantalla principal.

Aparece a la derecha de la pantalla principal :
En otras palabras, las pantallas que tienen un origen Point.X positivo Point.X
El origen de Point.X es la suma de todas las Screens[].Width anteriores Screens[].Width , Primaria incluida , agregada a la coordenada Point.X origen de la Pantalla principal.

Nota importante :
Si la aplicación no es DPIAware, todas estas medidas pueden verse comprometidas por la virtualización y el escalamiento automático de DPI que realiza el sistema. Todas las medidas se uniformarán a un valor predeterminado de 96 Dpi: la aplicación recibirá valores escalados. Esto también incluye los valores recuperados de funciones que no son DpiAware Win32 Api. Ver:

Desarrollo de aplicaciones de escritorio High DPI en Windows

Habilite la compatibilidad con todos los sistemas específicos en el archivo app.manifest , sin comentar las secciones requeridas.

Agregue / Descomente las secciones DpiAware y DpiAwareness en el archivo app.manifest .
El modo de conocimiento de Dpi PerMonitorV2 se puede configurar en el archivo app.config (disponible en Windows 10 Creators Edition).

Ejemplo:
Considere un sistema con 3 monitores:

PrimaryScreen (//./DISPLAY1): Width: (1920 x 1080) Secondary Display (Right) (//./DISPLAY2): Width: (1360 x 768) Secondary Display (Left) (//./DISPLAY3): Width: (1680 x 1050) PrimaryScreen: Bounds: (0, 0, 1920, 1080) Left: 0 Right: 1920 Top: 0 Bottom: 1080 Secondary Display (Right): Bounds: (1360, 0, 1360, 768) Left: 1360 Right: 2720 Top: 0 Bottom: 768 Secondary Display (Left): Bounds: (-1680, 0, 1680, 1050) Left: -1680 Right: 0 Top: 0 Bottom: 1050



Si cambiamos, usando el applet del sistema, la referencia de la pantalla principal, configurándola en //./DISPLAY3 , las coordenadas se modificarán en consecuencia:


Pantalla virtual

La pantalla virtual es una pantalla virtual, cuyas dimensiones están representadas por:
Origen : la coordenada de origen de la Screen más a la izquierda
Ancho : la suma de todos los Ancho de Screens .
Altura : la altura de la Screen más alta

Estas medidas son reportadas por SystemInformation.VirtualScreen
SystemInformation.PrimaryMonitorSize notifica el Size pantalla principal
Todas las medidas y posiciones actuales de las Pantallas también se pueden recuperar utilizando Screen.AllScreens e inspeccionando cada //./DISPLAY[N] propiedades.

Usando el ejemplo anterior como referencia, en la primera disposición, los límites de VirtualScreen son:

Bounds: (-1680, 0, 3280, 1080) Left: -1680 Right: 3280 Top: 0 Bottom: 1080

En la segunda disposición, los límites de VirtualScreen son:

Bounds: (0, 0, 4960, 1080) Left: 0 Right: 4960 Top: 0 Bottom: 1080


Posición de la ventana dentro de un área de visualización :

La clase Pantalla ofrece múltiples métodos que se pueden usar para determinar en qué pantalla se muestra actualmente una ventana específica:

Screen.FromControl([Control reference])
Devuelve el objeto Screen que contiene la sección más grande de la referencia de Control especificada.

Screen.FromHandle([Window Handle])
Devuelve el objeto Screen que contiene la sección más grande de Window / Control referenciada por un Handle

Screen.FromPoint([Point])
Devuelve el objeto Screen que contiene un Point específico

Screen.FromRectangle([Rectangle])
Devuelve el objeto Screen que contiene la sección más grande del Rectangle especificado

Screen.GetBounds() (sobrecargado)
Devuelve una estructura de Rectangle que hace referencia a los Límites de pantalla que contienen:
- un Point específico
- Sección más grande del Rectangle especificado.
- Una referencia de Control

Para determinar el //./DISPLAY[N] en el que se muestra el Formulario actual, llame (por ejemplo):

Screen.FromHandle(this);

Para determinar en qué Pantalla se muestra una Forma secundaria:
(Usando las pantallas de muestra en el ejemplo)

form2 = new Form2(); form2.Location = new Point(-1400, 100); form2.Show(); Rectangle screenSize = Screen.GetBounds(form2); Screen screen = Screen.FromHandle(form2.Handle);

screenSize será = a //./DISPLAY3 Bounds.
screen será el objeto Screen que representa las propiedades //./DISPLAY3 .

screen objeto de screen también informará el nombre //./DISPLAY[N] de la Screen en la que se muestra form2 .

Obtenga el controlador hMonitor de un objeto Screen :

El .NET Reference Source muestra que se devuelve hMonitor llamando a [Screen].GetHashCode();

IntPtr monitorHwnd = new IntPtr([Screen].GetHashCode());

O usando las mismas funciones nativas de Win32:

MonitorFromWindow , MonitorFromPoint y MonitorFromRect

[Flags] internal enum MONITOR_DEFAULTTO { NULL = 0x00000000, PRIMARY = 0x00000001, NEAREST = 0x00000002, } [DllImport("User32.dll", SetLastError = true)] internal static extern IntPtr MonitorFromWindow(IntPtr hwnd, MONITOR_DEFAULTTO dwFlags); [DllImport("User32.dll", SetLastError = true)] internal static extern IntPtr MonitorFromPoint([In] POINT pt, MONITOR_DEFAULTTO dwFlags); [DllImport("User32.dll", SetLastError = true)] internal static extern IntPtr MonitorFromRect([In] ref RECT lprc, MONITOR_DEFAULTTO dwFlags);

Obtenga un identificador del contexto de dispositivo de una pantalla :
Un método genérico para recuperar el hDC de cualquier pantalla disponible.

Las coordenadas de la Pantalla o el Dispositivo de Pantalla pueden determinarse utilizando uno de los métodos descritos anteriormente cuando solo se requiere una referencia de Pantalla específica.

La propiedad Screen.DeviceName se puede usar como el parámetro lpszDriver de la función GDI + CreateDC . Devolverá el hDC de la pantalla que Graphics.FromHdc puede usar para crear un objeto Graphics válido que permita pintar en una pantalla específica.

Aquí, asumiendo que al menos dos pantallas están disponibles:

[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Auto)] internal static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData); [DllImport("gdi32.dll", SetLastError = true, EntryPoint = "DeleteDC")] internal static extern bool DeleteDC([In] IntPtr hdc); public static IntPtr CreateDCFromDeviceName(string deviceName) { return CreateDC(deviceName, null, null, IntPtr.Zero); } Screen[] screens = Screen.AllScreens; IntPtr screenDC1 = CreateDCFromDeviceName(screens[0].DeviceName); IntPtr screenDC2 = CreateDCFromDeviceName(screens[1].DeviceName); using (Graphics g1 = Graphics.FromHdc(screenDC1)) using (Graphics g2 = Graphics.FromHdc(screenDC2)) using (Pen pen = new Pen(Color.Red, 10)) { g1.DrawRectangle(pen, new Rectangle(new Point(100, 100), new Size(200, 200))); g2.DrawRectangle(pen, new Rectangle(new Point(100, 100), new Size(200, 200))); } DeleteDC(screenDC1); DeleteDC(screenDC2);