如何在 .NET c# 中使用 Win32 GetMonitorInfo()?
我必须实现一个保存窗口最后位置的功能。当应用程序启动时,需要获取并恢复该位置。
现在可能第二台显示器被拆除了。如果最后一个位置位于现在不可见的监视器上(换句话说,保存的坐标位于可见坐标之外),则应捕获这种情况,并将坐标设置为默认位置而不是最后一个位置。
为了检索有关监视器的信息,我需要使用 Win32。对我来说完成这项工作并不容易。
我创建了一个 Helper 类:
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);
任何建议我需要做什么解决这个问题?
I have to implement a feature where the last position of the window is saved. When the application starts up this position needs to be obtained and restored.
Now it could be that a second monitor is dismantled. If the last position is on a now non-visible monitor (in other words the saved coordinates are outside the visible coordinates), this case should be caught and the coordinates shall be set to the default rather than last position.
In order to retrieve the information about monitors I need to use Win32. It is not easy for me to make this work.
I have created a 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);
}
}
And these are my attempts to create the MonitorInfo and Rect classes:
[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;
}
}
I am using this code like this to obtain the visible monitors:
//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);
The last method throws an exception when trying to execute
bool result = GetMonitorInfo(mh, ref mi);
Any suggestions what I need to do to fix this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您的 Rectangle2 应使用
Int32
或仅使用int
,而不是Int64
。更多信息可以在此处找到。它还需要是一个结构,而不是一个类。 MonitorInfo 类也是如此(它应该是一个结构)。我建议尝试上面链接中的版本,或将它们与您的版本进行比较。
Your Rectangle2 should use
Int32
or justint
, notInt64
. More information can be found here.Also it needs to be a struct, not a class. Same goes for your MonitorInfo class (it should be a struct). I'd recommend trying the version from the link above, or compare them with your versions.
由于您使用的是 MonitorInfo 类,而不是结构体,因此您必须指定 [Out] 属性而不是使用 ref 以便封送拆收器正确更新您的类。
你也可以去使用结构和
ref
,那么它看起来像这样:Since you are using the MonitorInfo class, not a struct, you must specify the [Out] attribute and not use ref in order for the marshaler to update your class correctly.
You can also go to using structure and
ref
, then it will look like this:您不应调用本机 API,而应使用
System.Windows.Forms.Screen
。它应该拥有您需要的一切,并且更易于使用。Screen.FromPoint
< /a> 是带有MONITOR_DEFAULTTONEAREST
选项的GetMonitorInfoNow
函数的托管等效项。我只是注意到您没有使用该选项,因此您可能必须编写自己的或使用正确的 P/Invoke 签名。如果您只引用
System.Drawing
和System.Windows.Forms
,那么编写自己的代码应该相当简单。这两者都应该有效:如果您更喜欢自己进行 Win32 调用,则需要调用的函数的正确 P/Invoke 签名(即您从反编译 .Net DLL 中获得的签名)是:
Rather than calling a native API, you should use
System.Windows.Forms.Screen
. It should have everything you need, and be much easier to use.Screen.FromPoint
is the managed equivalent of yourGetMonitorInfoNow
function with theMONITOR_DEFAULTTONEAREST
option. I just noticed you aren't using that option, so you may have to write your own or use the correct P/Invoke signatures.Writing your own should be fairly simple, if you just reference
System.Drawing
andSystem.Windows.Forms
. Both of these should work:If you prefer to make the Win32 calls yourself, the proper P/Invoke signatures (i.e. what you'd get from decompiling the .Net DLL) for the functions you need to call are:
我发现一个不同的是
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 与 [In,Out] 的更多信息,请参见 这个。
I found one different is
public static extern bool GetMonitorInfo(IntPtr hMonitor, [In,Out] MONITORINFO lpmi)
andpublic static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi)
In my case, the ref keywork made the function always return false.
But if remove this keyword or usr [In,Out], it work.
More info about ref vs. [In,Out] on This.