2011-06-08 118 views
6

我必须实现保存窗口最后位置的功能。当应用程序启动时,需要获取并恢复此位置。如何在.NET c#中使用Win32 GetMonitorInfo()?

现在可能是第二台显示器被拆除。如果最后一个位置在现在不可见的监视器上(换句话说,保存的坐标在可见坐标之外),则应该捕获这种情况,并将坐标设置为默认位置而不是最后位置。

为了检索关于显示器的信息,我需要使用Win32。我做这件事并不容易。

我创建了一个助手类:

public static class DisplayHelper 
    { 
     private const int MONITOR_DEFAULTTONEAREST = 2; 

     [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 
     public static extern int GetSystemMetrics(int nIndex); 

     [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 
     private static extern UInt32 MonitorFromPoint(Point pt, UInt32 dwFlags); 

     [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 
     private static extern bool GetMonitorInfo(UInt32 monitorHandle, ref MonitorInfo mInfo); 


     public static void GetMonitorInfoNow(MonitorInfo mi, Point pt) 
     { 
      UInt32 mh = MonitorFromPoint(pt, 0); 
      mi.cbSize = (UInt32)System.Runtime.InteropServices.Marshal.SizeOf(typeof(MonitorInfo)); 
      mi.dwFlags = 0; 
      bool result = GetMonitorInfo(mh, ref mi); 

     } 
    } 

而这些是我尝试创建monitorInfo的和矩形类:

[StructLayout(LayoutKind.Sequential)] 
    public class MonitorInfo 
    { 
     public UInt32 cbSize; 
     public Rectangle2 rcMonitor; 
     public Rectangle2 rcWork; 
     public UInt32 dwFlags; 

     public MonitorInfo() 
     { 
      rcMonitor = new Rectangle2(); 
      rcWork = new Rectangle2(); 

      cbSize = (UInt32)System.Runtime.InteropServices.Marshal.SizeOf(typeof(MonitorInfo)); 
      dwFlags = 0; 
     } 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public class Rectangle2 
    { 
     public UInt64 left; 
     public UInt64 top; 
     public UInt64 right; 
     public UInt64 bottom; 

     public Rectangle2() 
     { 
      left = 0; 
      top = 0; 
      right = 0; 
      bottom = 0; 
     } 
    } 

我使用这个代码,这样获得的可视监控:

//80 means it counts only visible display monitors. 
int lcdNr = DisplayHelper.GetSystemMetrics(80); 
var point = new System.Drawing.Point((int) workSpaceWindow.Left, (int) workSpaceWindow.Top); 
MonitorInfo monitorInfo = new MonitorInfo(); 
DisplayHelper.GetMonitorInfoNow(monitorInfo, point); 

最后一个方法在尝试执行时会引发异常

bool result = GetMonitorInfo(mh, ref mi); 

任何建议,我需要做什么来解决这个问题?

回答

8

与其调用本机API,您应该使用System.Windows.Forms.Screen。它应该有你需要的一切,并且更容易使用。

Screen.FromPointGetMonitorInfoNow函数的管理等效函数,其中MONITOR_DEFAULTTONEAREST选项。我只注意到你没有使用这个选项,所以你可能必须自己编写或使用正确的P/Invoke签名。

如果你只是参考System.DrawingSystem.Windows.Forms,编写你自己的应该很简单。这两个应该工作:

static Screen ScreenFromPoint1(Point p) 
{ 
    System.Drawing.Point pt = new System.Drawing.Point((int)p.X, (int)p.Y); 
    return Screen.AllScreens 
        .Where(scr => scr.Bounds.Contains(pt)) 
        .FirstOrDefault(); 
} 

static Screen ScreenFromPoint2(Point p) 
{ 
    System.Drawing.Point pt = new System.Drawing.Point((int)p.X, (int)p.Y); 
    var scr = Screen.FromPoint(pt); 
    return scr.Bounds.Contains(pt) ? scr : null; 
} 

如果你喜欢使Win32调用自己,适当的P/Invoke签名(即你会从反编译的净DLL获得),你需要调用的函数是:

[DllImport("User32.dll", CharSet=CharSet.Auto)] 
    public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out]MONITORINFOEX info); 
    [DllImport("User32.dll", ExactSpelling=true)] 
    public static extern IntPtr MonitorFromPoint(POINTSTRUCT pt, int flags); 

    [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto, Pack=4)] 
    public class MONITORINFOEX { 
     public int  cbSize = Marshal.SizeOf(typeof(MONITORINFOEX)); 
     public RECT rcMonitor = new RECT(); 
     public RECT rcWork = new RECT(); 
     public int  dwFlags = 0; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] 
     public char[] szDevice = new char[32]; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public struct POINTSTRUCT { 
     public int x; 
     public int y; 
     public POINTSTRUCT(int x, int y) { 
      this.x = x; 
      this.y = y; 
     } 
    } 

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

感谢Gabe,我忘记提及我的应用程序是用WPF编写的。理论上,我将无法访问System.Windows.Forms.Screen,除非它也适用于WPF? – Houman 2011-06-08 13:41:22

+0

@Kave:只需添加一个对System.Windows.Forms.dll的引用,就可以在WPF中使用它。 – Gabe 2011-06-08 13:43:39

+0

@Gabe - 这可能会更容易,但我不确定是否会为任何优化加载一个类/方法的5MB程序集。更不用说它的依赖关系;-) – CodeNaked 2011-06-08 13:55:12

0

你的矩形2应该使用Int32或者只是int而不是Int64。更多的信息可以在here找到。

此外它需要是一个结构,而不是一个类。你的MonitorInfo类也是一样的(它应该是一个结构体)。我建议从上面的链接尝试版本,或者将它们与您的版本进行比较。

+0

没有什么区别。它仍然崩溃。 – Houman 2011-06-08 13:44:29

+1

@Kave - 已更新的答案。 – CodeNaked 2011-06-08 13:47:09