我必须实现一个保存窗口最后位置的功能.当应用程序启动时,需要获取并恢复该位置.
现在可能会拆除第二台显示器.如果最后一个位置在现在不可见的监视器上(换句话说,保存的坐标在可见坐标之外),则应捕获此情况并将坐标设置为默认位置而不是最后位置.
为了检索有关监视器的信息,我需要使用Win32.我不容易做这项工作.
我创建了一个Helper CLass:
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",ExactSpelling = true)] private static extern UInt32 MonitorFromPoint(Point pt,UInt32 dwFlags); [DllImport("user32.dll",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和Rect类:
[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);
有什么建议我需要做些什么来解决这个问题?
解决方法
您应该使用
System.Windows.Forms.Screen
而不是调用本机API.它应该包含您需要的所有内容,并且更易于使用.
Screen.FromPoint
是具有MONITOR_DEFAULTTONEAREST选项的GetMonitorInfoNow函数的托管等效项.我刚刚注意到你没有使用该选项,所以你可能必须自己编写或使用正确的P / Invoke签名.
如果您只引用System.Drawing和System.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签名(即您从反编译.Net 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; }