如何在中使用Win32 GetMonitorInfo().NET c#
本文关键字:GetMonitorInfo NET Win32 | 更新日期: 2023-09-27 17:58:52
我必须实现一个保存窗口最后位置的功能。当应用程序启动时,需要获取并恢复此位置。
现在可能是第二个监视器被拆除了。如果最后一个位置在现在不可见的监视器上(换句话说,保存的坐标在可见坐标之外),则应捕捉这种情况,并将坐标设置为默认位置,而不是最后一个。
为了检索有关监视器的信息,我需要使用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", 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和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);
有什么建议我需要做些什么来解决这个问题吗?
与其调用本机API,不如使用System.Windows.Forms.Screen
。它应该有你需要的一切,并且更容易使用。
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;
}
我发现一个不同的是public static extern bool GetMonitorInfo(IntPtr hMonitor, [In,Out] MONITORINFO lpmi)
和public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi)
在我的例子中,ref键使函数总是返回false
但是,如果删除这个关键字或usr[In,Out],它就会起作用。
更多关于ref vs.[In,Out]的信息。
Rectangle2应该使用Int32
或仅使用int
,而不是Int64
。更多信息可以在这里找到。
此外,它需要是一个结构,而不是一个类。MonitorInfo类也是如此(它应该是一个结构)。我建议您尝试上面链接中的版本,或者将它们与您的版本进行比较。
由于您使用的是MonitorInfo类,而不是结构,因此必须指定[Out]属性而不使用ref,封送拆收器才能正确更新您的类。
[DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GetMonitorInfo(
IntPtr hmonitor,
[In, Out] MonitorInfo info);
你也可以使用结构和ref
,然后它会看起来像这样:
[DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GetMonitorInfo(
IntPtr hmonitor,
ref MonitorInfoEx info);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
internal struct MonitorInfoEx
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] szDevice;
}
var info = new MonitorInfoEx
{
cbSize = (uint)Marshal.SizeOf(typeof(MonitorInfoEx)),
};
GetMonitorInfo(hDesktop, ref info);