我要实现在窗口的最后一个位置保存功能。当应用程序启动时该位置需要获取和恢复。
现在,它可能是第二个显示器被拆除。如果最后的位置是现在不可见的显示器上(即保存的坐标是可见的坐标外),这种情况下应该被捕获的坐标,应设置为默认值,而不是最后的位置。
为了检索显示器我需要使用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);
我需要做的任何建议,以解决这一问题?
而不是调用本地API,你应该使用System.Windows.Forms.Screen
。它应该有你需要的一切,并且更容易使用。
Screen.FromPoint
与GetMonitorInfoNow
选项您MONITOR_DEFAULTTONEAREST
功能的管理等同。我只注意到你不使用该选项,所以您可能需要编写自己的或使用正确的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签名(即你从反编译的净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;
}
你Rectangle2应该使用Int32
或只是int
,不Int64
。更多信息可以发现here。
此外,它需要一个结构,而不是一类。这同样适用于您的monitorInfo的类(它应该是一个结构)。我建议从链接尝试上述版本,或将它们与你的版本进行比较。
我发现了一个不同的是,
public static extern bool GetMonitorInfo(IntPtr hMonitor, [In,Out] MONITORINFO lpmi)
和
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi)
在我的情况下,裁判keywork所做的功能总是返回false。 但是,如果删除此关键字或USR [输入,输出],它的工作。
关于裁判对[输入,输出]上This更多信息。