2011-02-16 9 views
15

que estoy haciendo algo como esto en mi programa:Screen.AllScreen no está dando cuenta el monitor correcto

Int32 currentMonitorCount = Screen.AllScreens.Length; 

if (currentMonitorCount < 2) 
{ 
    //Put app in single screen mode. 
} 
else 
{ 
    //Put app in dual screen mode. 
} 

Es muy importante que es mi aplicación reconoce el número de monitores están conectados actualmente.

Sin embargo, después de conectar/desconectar el monitor un par de veces, Screen.AllScreens.Length siempre devuelve '2'.

Mi monitor sabe que no está conectado (ha ingresado al modo 'ahorro de energía'), y el panel de control sabe que no está conectado (muestra solo un monitor).

Entonces, ¿qué es lo que me falta? ¿Cómo se forma I que solo hay un monitor?

+2

¿Has probado System.Windows.Forms.SystemInformation.MonitorCount? Lo uso en una de mis aplicaciones y hasta ahora ha funcionado bien, pero no he experimentado con desconectar/conectar el monitor mientras mi aplicación se está ejecutando. –

Respuesta

19

He echado un vistazo a la fuente (recuerde que podemos hacer eso usando los servidores de MS Symbol). AllScreens utiliza una API no administrada para obtener las pantallas en el primer acceso , luego almacena el resultado en una variable estática para su uso posterior.

La consecuencia de esto, es que si la cantidad de monitores cambia mientras se ejecuta su programa; luego Screen.AllScreens no recogerá el cambio.

La forma más sencilla de evitar esto sería llamar directamente al unmanaged API. (O podría ser malo y usar el reflejo para establecer el campo estático screens en nulo antes de preguntar. No haga eso).

Editar:

Si sólo necesita saber el conteo, y comprueba si puede utilizar System.Windows.Forms.SystemInformation.MonitorCount (como se sugiere en los comentarios) antes de ir al P/Invoke ruta. Esto llama directamente al GetSystemMetrics, y probablemente esté correctamente actualizado.

Si encuentra que tiene que hacer es utilizar P/Invoke, aquí es un ejemplo completo que muestra el uso de la API no administrada desde C#:

using System; 
using System.Runtime.InteropServices; 

class Program 
{ 
    public static void Main() 
    { 
     int monCount = 0; 
     Rect r = new Rect(); 
     MonitorEnumProc callback = (IntPtr hDesktop, IntPtr hdc, ref Rect prect, int d) => ++monCount > 0;          
     if (EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0)) 
      Console.WriteLine("You have {0} monitors", monCount); 
     else 
      Console.WriteLine("An error occured while enumerating monitors"); 

    } 
    [DllImport("user32")] 
    private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lpRect, MonitorEnumProc callback, int dwData); 

    private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref Rect pRect, int dwData); 

    [StructLayout(LayoutKind.Sequential)] 
    private struct Rect 
    { 
     public int left; 
     public int top; 
     public int right; 
     public int bottom; 
    } 
} 
+0

No estoy seguro de cómo leer esa combinación de Delegado/Linq ... ¿cómo puedo sacar los datos del monitor de esa función? – Nyerguds

+2

Ambas soluciones funcionan bien, pero ¿alguna idea sobre cómo obtener un monitor real cuenta cuando se configura el modo de presentación de presentación para duplicar? Tal vez sea sólo no expuestos, pero en la configuración de pantalla, ventanas sabe cuando un segundo monitor está conectado, incluso en modo duplicado, mientras que ambas soluciones resultan en un conteo de 1. – samuelesque

+0

que necesitaba para detectar cuando el número de monitores de cambios. Uso WndProc y busco WM_DISPLAYCHANGE. Cuando estoy allí, el valor de System.Windows.Forms.Screen.AllScreens es impreciso. Sin embargo, el método EnumDisplayMonitors es perfecto. ¡¡¡Gracias!!! –

6

Basándose en la respuesta anterior por driis, esto es cómo lo manejé Debo señalar que el siguiente código vive en mi archivo Program.cs.

En primer lugar los enlaces a recursos externos y estructuras de datos:

[DllImport("user32")] 
    private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lpRect, MonitorEnumProc callback, int dwData); 

    private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref Rect pRect, int dwData); 

    [StructLayout(LayoutKind.Sequential)] 
    private struct Rect 
    { 
     public int left; 
     public int top; 
     public int right; 
     public int bottom; 
    } 

Ahora crea un objeto simple para contener información del monitor:

public class MonitorInfo 
{ 
    public bool IsPrimary = false; 
    public Rectangle Bounds = new Rectangle(); 
} 

Y un envase para guardar estos objetos:

public static List<MonitorInfo> ActualScreens = new List<MonitorInfo>(); 

y un método para actualizar el contenedor:

public static void RefreshActualScreens() 
    { 
     ActualScreens.Clear(); 
     MonitorEnumProc callback = (IntPtr hDesktop, IntPtr hdc, ref Rect prect, int d) => 
     { 
      ActualScreens.Add(new MonitorInfo() 
       { 
        Bounds = new Rectangle() 
        { 
         X = prect.left, 
         Y = prect.top, 
         Width = prect.right - prect.left, 
         Height = prect.bottom - prect.top, 
        }, 
        IsPrimary = (prect.left == 0) && (prect.top == 0), 
       }); 

      return true; 
     }; 

     EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0); 
    } 

Luego, más tarde en un formulario, Si quería detectar que una pantalla se ha añadido o eliminado ...

private const int WM_DISPLAYCHANGE = 0x007e; 

    protected override void WndProc(ref Message message) 
    { 
     base.WndProc(ref message); 

     if (message.Msg == WM_DISPLAYCHANGE) 
     { 
      Program.RefreshActualScreens(); 
      // do something really interesting here 
     } 
    } 

puede haber algunos errores tipográficos en allí, pero esa es la idea básica. ¡Buena suerte!

0

tuve mirar el código de la clase Screen (en here)

véase la línea 120, Screen.AllScreens utilizan Screen.screens de campo para la temp. Y en mi solución, uso la API de reflexión para cambiar la clase Screen. Estoy limpio Screens.screens antes de recuperar Screen.AllScreen.

typeof(Screen).GetField("screens", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).SetValue(null, null); 
// it is code for clean private field 
+0

Intente aclarar esta respuesta un poco más. – Nae

Cuestiones relacionadas