-
-
Notifications
You must be signed in to change notification settings - Fork 222
User32.GetMonitorInfo() has the wrong signature. #558
Comments
I see that our definition of it is: Lines 1814 to 1816 in bb42d00
This is wrong because the Friendly attribute indicates the second parameter is only an @HavenDV, I haven't tested your proposed fix, but I would be surprised if that worked because you are passing a copy of the struct as the second parameter instead of a pointer to the struct as the header file and documentation indicate. |
Confirmed: |
This allows callers of the friendly overloads to initialize the `cbSize` field as required. Fixes #558
I think it's a little more complicated. And as far as I know [Out] might be important: P.S. What's interesting is that if I pass your structure to the ref method, then no error occurs. Unfortunately the DeviceName is of type char *, so I cannot use that. P.S.S. I found a difference that requires the [Out] attribute instead of the ref. I used a class, not a struct. var info = new User32.MONITORINFOEX
{
cbSize = Marshal.SizeOf(typeof(User32.MONITORINFOEX)),
};
GetMonitorInfo(hDesktop, ref info); Class usage(In this case, the use of [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
internal class MonitorInfoEx
{
public uint cbSize = (uint)Marshal.SizeOf(typeof(MonitorInfoEx));
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] szDevice = new char[32];
}
[DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GetMonitorInfo(
IntPtr hmonitor,
[In, Out] MonitorInfoEx info);
var info = new MonitorInfoEx();
GetMonitorInfo(hDesktop, info); |
|
It's not that complicated, ref works just fine with structs, but only if it's defined correctly. Preferably do not use class for interop calls, although it might seem to work it causes a lot of headaches. I myself have a convenience method on every struct for creating, like here: https://github.com/dapplo/Dapplo.Windows/blob/master/src/Dapplo.Windows.User32/Structs/MonitorInfoEx.cs#L75 It's important to know that the DllImports here are trying to create as less overhead as possible, converting from a native char * to a .NET string costs time and memory, even marshalling this... The idea is only convert when something is used, which is more or less up to you. I add convenience methods for dealing with most things like having a char * like here: https://github.com/dapplo/Dapplo.Windows/blob/master/src/Dapplo.Windows.User32/Structs/MonitorInfoEx.cs#L60 I created my project Dapplo.Windows a bit before this one, and it has a completely different focus (more modern & functional usage of Win32 APIs). I planned to refactor to use this with the great work of @AArnott and all contributors "under the covers", and remove all the overhead which is done double. Unfortunately I lack time 🤷♂️ Btw. You can read about interop best practices here: https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices |
Thanks for the explanation. I do not use classes in Interop interactions, but it was interesting to know about their use and the [Out] attribute. By the way, it seems to me that you are very experienced in WinAPI, perhaps you can answer my question, which I could not find on the Internet - User32.GetCursorPos sometimes (1-5%) returns a value that seems to take into account some factor, like DPI ... This happens inside a transparent WPF window when the Rectangle is created using mouse coordinates. |
@HavenDV Maybe this is on a system with displays which have different DPI settings? I don't recall any Greenshot issues which confirm such a thing, but let me check and get back to you. |
Apologies in advance for spamming this issue.
Yes, this happens on a triple screen system (1 HD 100% scale, 2 Primary 4K 150% scale, 3 HD 150%). P.S. The User32.GetCursorPos values depend on the window under the cursor.
P.S.S
P.S.S.S
|
That is so recognizable, tracking an issue like that... The odd things I have seen in my time supporting and building Greenshot 😁 Just in case: Your application is correctly set to multi-monitor DPI Aware? From my experience is working with transparent windows very tricky, events seem to pass through, this might explain why you suddenly get information from another window. Try changing the alpha to not 100% transparent, I know this might not be what you want but just humor me! Another thing I have seen, is that some applications create & destroy windows all the time (yes really!!) In the Win32 world, I sometimes have to come up with weird hacks, to make things work... 😞 |
No it isn't.
Why not? All you need to do is We generally avoid using marshal attributes in our structs because that causes .NET to see them as managed types and that severely limits where and how they can be used.
ah, ok that teaches me something. I have never tried using classes for interop when the native layer uses structs, but what you say makes sense.
I guess so, since the ctor on your class runs first and can initialize the field. But on a struct in C#, the default ctor cannot be defined so the user has to initialize it themselves. To make this easier we usually define a more later... |
Some interop calls do very particular memory management that .NET marshaling cannot deal with as well, which is another reason we prefer non-managed/marshaled structs in general. |
|
In the end, I just started using LowLevelMouseHook instead of GetCursorPos and everything works as it should. |
I'm not sure if that is supported with pinvoke (this) but I have you covered with Dapplo.Windows.Input 😉
|
User32.GetCursorInfo definitively should be a ref, it needs a initialized struct. |
Ah, I see my mistake. I'll send another PR to fix |
Correct code contains [In, Out] instead out keyword.
Tested on Windows 10 latest version.
The text was updated successfully, but these errors were encountered: