I need the virtual screen (physical, not logical) to make an adjustment so to pass it on ClipCursor. (screen rectangle)
In below sample there are two approaches.
1. Using GetSystemMetrics
2. Using GetClipBox on hDCScreen
I tested them with 2 monitors. Both ways return correctly (possible) negative values etc. and it seems to work also with what ClipCursor expects.
However, I read that GetSystemMetrics don't work if the calling thread is per monitor aware and also some posts in the wild that SM_CXVIRTUALSCREEN returns 'bad' values for large screens etc.
I did the test with a manifest and also messed with the DPI settings either way on the two monitors and the sizes keeps being correct. (?)
Thanks
Code:
Option Explicit
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hWnd As Long, ByVal hDC As Long) As Long
Private Declare Function GetClipBox Lib "gdi32" (ByVal hDC As Long, ByRef lpRect As RECT) As Long
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Const SM_XVIRTUALSCREEN As Long = 76
Private Const SM_YVIRTUALSCREEN As Long = 77
Private Const SM_CXVIRTUALSCREEN As Long = 78
Private Const SM_CYVIRTUALSCREEN As Long = 79
Private Sub Command1_Click()
Test1
Test2
End Sub
Private Sub Test1()
' Get virtual screen from GetSystemMetrics.
' Any DPI issues ? It seems to be physical always and not logical.
Debug.Print "1", "Left: " & GetSystemMetrics(SM_XVIRTUALSCREEN), "Right: " & (GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN))
Debug.Print "1", "Top: " & GetSystemMetrics(SM_YVIRTUALSCREEN), "Bottom: " & (GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN))
End Sub
Private Sub Test2()
' This seems to be an easy approach. Better than enumerating all the monitors and get physical pixel sizes ?
' Is this 'safe' ?
Dim hDCScreen As Long
hDCScreen = GetDC(0)
If hDCScreen <> 0 Then
Dim RC As RECT
GetClipBox hDCScreen, RC
Debug.Print "2", "Left: " & RC.Left, "Right: " & RC.Right
Debug.Print "2", "Top: " & RC.Top, "Bottom: " & RC.Bottom
ReleaseDC 0, hDCScreen
End If
End Sub
Last edited by Krool; Nov 24th, 2021 at 02:52 PM.
Reason: Thank you Elroy
Re: Question about GetSystemMetrics and VIRTUALSCREEN
Enumerating all the monitors and getting their physical pixel sizes would be difficult for several reasons. First, I wouldn't even begin to know how the monitors were actually physically arranged within Display Settings, and how to actually "get" that information. They may be horizontally arranged, vertically arranged, or a combination of the two:
I've never played around with what you're trying to do, but I'll take your code and try it with different DPI settings and see what it does.
CORRECTION: I was making a call to SetProcessDpiAwarenessContext(Context_PerMonitorAwareV2) in an IDE add-in that I had forgotten about.
Last edited by Elroy; Nov 26th, 2021 at 12:45 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
I'm not sure I know how to make sense of that. It looks like test#1 is reporting "physical" across both monitors' width and the height for the larger monitor. Test#2 is reporting the correct width, but the height for the smaller monitor.
EDIT: The more I think about it, maybe test#1 is reporting the "full" virtual screen, and test#2 is reporting the "viewable" virtual screen. I'll play around more with that idea.
CORRECTION: I was making a call to SetProcessDpiAwarenessContext(Context_PerMonitorAwareV2) in an IDE add-in that I had forgotten about.
Last edited by Elroy; Nov 26th, 2021 at 12:45 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
My best guess right now is that width is always the total, but height is viewable for test#2.
CORRECTION: I was making a call to SetProcessDpiAwarenessContext(Context_PerMonitorAwareV2) in an IDE add-in that I had forgotten about.
Last edited by Elroy; Nov 26th, 2021 at 12:45 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: Question about GetSystemMetrics and VIRTUALSCREEN
Ok, this is weird. I put my configuration back to this:
With monitor #2 still at 150% scaling.
And I also compiled things. The first (left) one is in the IDE and the second (right) one is compiled:
And just an FYI, nothing is manifested.
CORRECTION: I was making a call to SetProcessDpiAwarenessContext(Context_PerMonitorAwareV2) in an IDE add-in that I had forgotten about.
I've attached your "modified" project to this post, just modified to report in the form, rather than the Immediate window.
Last edited by Elroy; Nov 26th, 2021 at 12:46 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: Question about GetSystemMetrics and VIRTUALSCREEN
Say Krool, also, you declared ReleaseDC in your project, but you didn't call it. I just spotted that. It's NOT fixed in the project attached to post #5 either, so beware.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
An HD monitor next to a QHD monitor, primary is the second one and that's why the negative Left above I guess.
DPI is 125% system-wide (both monitors) but this seems not to matter in this test.
cheers,
</wqw>
Weird about the negative value. I guess because my #1 monitor is disabled and my #2 monitor is on the left, I didn't get negative numbers. I can easily test that though.
Yep, that's totally it (this test just in IDE):
CORRECTION: I was making a call to SetProcessDpiAwarenessContext(Context_PerMonitorAwareV2) in an IDE add-in that I had forgotten about.
Last edited by Elroy; Nov 26th, 2021 at 12:46 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
(*) "Physical" are not truly physical but what is selected by the user as the resolution of the monitor.
I changed the second monitor setting to 1920x1024 and the result changed to:
I need the virtual screen (physical, not logical) to make an adjustment so to pass it on ClipCursor. (screen rectangle)
[...]
However, I read that GetSystemMetrics don't work if the calling thread is per monitor aware and also some posts in the wild that SM_CXVIRTUALSCREEN returns 'bad' values for large screens etc.
I did the test with a manifest and also messed with the DPI settings either way on the two monitors and the sizes keeps being correct. (?)
Anyway, I don't understand why you need to explicitly confine the mouse to the virtual screen... isn't supposed that the mouse is already confined by Windows to the virtual screen and cannot got beyond its bounds?
(Or perhaps you want to leave some space at the sides?)
Last edited by Eduardo-; Nov 25th, 2021 at 01:44 AM.
Re: Question about GetSystemMetrics and VIRTUALSCREEN
Ive recently had a project where i had to dive deep into displays and monitors. heres some things i learned.
The virtual screen is always the height of the tallest monitor + the width of all monitors combined.
The SM_XVIRTUALSCREEN and SM_YVIRTUALSCREEN are the upper left X-Y coords of the virtual screen and are used for navigating
the virtual screen only, they should not be used in calculating the size of the virtual screen.
Your code only works because those values return 0, if they had a negative value it would skew your numbers heavily.
The reason those values arent always 0 is because the primary monitor's X-Y are 0 and if the user has a monitor
to the left of the primary monitor the values will be negative.
I have a module with all my screen code that may be useful to you, I only coded what I needed and more could be added.
I made a small example program of how you can use the code. This is my monitor setup shown below. Keep in mind some users can have a monitor
in portrait mode also.
here is the example code with the module included.
EDIT: Fixed the taskbar exclusion error, and added VirtualScreenLeft and VirtualScreenTop, and fixed a bug in drawing the virtual screen...
Last edited by vbLewis; Nov 25th, 2021 at 08:57 PM.
Re: Question about GetSystemMetrics and VIRTUALSCREEN
I just noticed all the Height values are 40 pixels short, not sure why that is...
EDIT: the code above excludes the taskbar which is what i needed for my purposes, to be correct it should include the task bar.
To make that code correct for the whole screen you should change the height, width, left, top to use rcMonitor and not rcWork
in the EnumMonitorCallback function.
Re: Question about GetSystemMetrics and VIRTUALSCREEN
Originally Posted by vbLewis
Your code only works because those values return 0, if they had a negative value it would skew your numbers heavily.
The reason those values arent always 0 is because the primary monitor's X-Y are 0 and if the user has a monitor
to the left of the primary monitor the values will be negative.
I want a RECT structure.
For the Right member I use currently below value, which is correct also for negative values. Or what you mean ?