How to use Win32 GetMonitorInfo () in .NET C #?

I need to implement a function in which the last position of the window is saved. When the application starts, this position must be obtained and restored.

Now it may happen that the second monitor is dismantled. If the last position is on an invisible monitor (in other words, the stored coordinates are outside the visible coordinates), this case should be captured, and the coordinates should be set by default, and not in the last position.

To get information about monitors, I need to use Win32. It’s not easy for me to do this job.

I created a CLASS 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); } } 

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 use this code to do this to get 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?

+6
source share
3 answers

Instead of calling your own API, you should use System.Windows.Forms.Screen . It should have everything you need and will be much easier to use.

Screen.FromPoint is the managed equivalent of your GetMonitorInfoNow function with the MONITOR_DEFAULTTONEAREST option. I just noticed that you are not using this 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 and System.Windows.Forms . Both should work:

 static Screen ScreenFromPoint1(Point p) { System.Drawing.Point pt = new System.Drawing.Point((int)pX, (int)pY); return Screen.AllScreens .Where(scr => scr.Bounds.Contains(pt)) .FirstOrDefault(); } static Screen ScreenFromPoint2(Point p) { System.Drawing.Point pt = new System.Drawing.Point((int)pX, (int)pY); var scr = Screen.FromPoint(pt); return scr.Bounds.Contains(pt) ? scr : null; } 

If you prefer to make Win32 calls yourself, then the appropriate P / Invoke signatures (i.e. what you get from the DLL.Net decompilation) for the functions you need to call:

  [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; } 
+8
source

Your Rectangle2 should use Int32 or just int , not Int64 . More information can be found here .

It should also be a structure, not a class. The same goes for your MonitorInfo class (this should be a structure). I would recommend trying the version from the link above or comparing it with your versions.

+1
source

I found one other
public static extern bool GetMonitorInfo(IntPtr hMonitor, [In,Out] MONITORINFO lpmi) and
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi)

In my case, the ref ref function made the function always return false.
But if you delete this keyword or usr [In, Out], it will work.

More info on ref vs [In, Out] on this .

0
source

Source: https://habr.com/ru/post/890056/


All Articles