2011-06-08 117 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