I have two monitors, one is laptop screen(125% scale, Physical 1366x768), another is an extend(175% scale, , Physical 1920x1080). In maniefst, I set dpiaware:
In result, laptop screen is dpiaware, extend is not dpiaware.
If the app's Form placement is crossing two monitors by careful adjustment, the app will automatically turn into non-dpiaware. As we know, in non-dpiaware app, some APIs like GetWindowRect return 'false' rectangle. So checking whether the app is dipaware status is crucial. How to check? Any notifications are available in main Form when app is turning into non-dipaware or dipaware?
Re: how do we know App is dpiAware applied currently?
Being DPI aware, but not per-monitor aware, means you are "System DPI Aware". If your app moves to another monitor that has a different DPI setting than the one your app loaded up at, virtualization occurs.
Virtualization will scale your form to the other DPI. In fact, even on the same monitor (let's say a single-monitor system for simplicity), your form scaling may be virtualized. That can happen when, while the app is loaded, the users changes their DPI settings.
If you are not per-monitor aware, most Windows APIs will scale values to your awareness, be it either not aware (always 96 DPI) or system aware (whatever DPI app loaded into).
If your app is per-monitor aware, you get WM_DPIChanged notifications from Windows. If not, no notifications because the system scales the app for you. When per-monitor aware, the system doesn't scale much for you at all. Per MSDN, you may get that message even if not per-monitor aware. But the use of the word "may" indicates that it shouldn't be relied on.
This message is only relevant for PROCESS_PER_MONITOR_DPI_AWARE applications or DPI_AWARENESS_PER_MONITOR_AWARE threads. It may be received on certain DPI changes if your top-level window or process is running as DPI unaware or system DPI aware, but in those situations it can be safely ignored.
Edited: in the codebank, I have a project that I hoped might start some conversation on per-monitor awareness. Regardless, the documentation within that project has some really useful stuff that may shed some light?
Last edited by LaVolpe; Aug 4th, 2020 at 08:30 PM.
Insomnia is just a byproduct of, "It can't be done"
Re: how do we know App is dpiAware applied currently?
Originally Posted by LaVolpe
If your app is per-monitor aware, you get WM_DPIChanged notifications from Windows. If not, no notifications because the system scales the app for you. When per-monitor aware, the system doesn't scale much for you at all. Per MSDN, you may get that message even if not per-monitor aware. But the use of the word "may" indicates that it shouldn't be relied on.
Understand. I shift to other solutions.
I noticed the Aero issue on Win10 that the Form placement can't be automatically align w/o hard code by 8 pixels offset for SizableToolWindow FormBorderStyle.
On Win7, there's Aero theme as well, why don't have issue w/o offset?
Code:
standard code not considering Aero borders
Form2.Left = Form1.Left + Form1.Width
Form2.Top = Form1.Top
Form2.Width = Form1.Width
Form2.Height = Form1.Height
Edited:
are there solid functions to identify we are on either win7 or win10 so that I can use hard code offset for win10 only?
Last edited by DaveDavis; Aug 4th, 2020 at 08:28 PM.
Re: how do we know App is dpiAware applied currently?
There are ways to determine if you are on any operating system. Though without a manifest declaring you are at least Win8.1 compatible, Windows may lie to you when on Win10 and say you are on Vista for example. If manifest entries exist, then GetVersionEx will return the correct version. The entries below are in order: Vista, Win7, Win8, WIn8.1, Win10
Other than manifesting with above entries, I remember seeing a post by Bonnie West that seemed to be a workaround of the Windows O/S version lie, but wouldn't know where it is without spending some searching time -- I'll leave that to you
As for your Aero issue, maybe you can post a sample project, including a res file with manifest (if it applies) that helps explain what you are attempting. I am not clear on what you are doing. Along with that project, maybe an example (text or screenshot) of what is expected and what is actually happening?
Hardcoding offsets is generally not a good solution. Things can change down the road that make those offsets break what they were suppose to fix.
Insomnia is just a byproduct of, "It can't be done"
Re: how do we know App is dpiAware applied currently?
Originally Posted by LaVolpe
As for your Aero issue, maybe you can post a sample project, including a res file with manifest (if it applies) that helps explain what you are attempting. I am not clear on what you are doing. Along with that project, maybe an example (text or screenshot) of what is expected and what is actually happening?
Hardcoding offsets is generally not a good solution. Things can change down the road that make those offsets break what they were suppose to fix.
I stripped down the demo.
Basically, Form2 should be stick to right side of Form1 on Win10 using the simple code.
Code:
Sub Main()
'standard code not considering Aero borders
Form2.Left = Form1.Left + Form1.Width '- 8 * 2 * Screen.TwipsPerPixelX
Form2.Top = Form1.Top
Form2.Width = Form1.Width
Form2.Height = Form1.Height
Form1.Show
Form2.Show
End Sub
Re: how do we know App is dpiAware applied currently?
Well, that is specific enough. I'll check back tomorrow morning to see if you have this resolved then. I won't have access to a VB box until then.
In the mean time, is there any change if you show the forms first, before aligning them? Even if that isn't what you want to do? Reason I ask is that it is possible the form's size and location isn't completely finalized until it is first shown.
Insomnia is just a byproduct of, "It can't be done"
Re: how do we know App is dpiAware applied currently?
Originally Posted by LaVolpe
In the mean time, is there any change if you show the forms first, before aligning them? Even if that isn't what you want to do? Reason I ask is that it is possible the form's size and location isn't completely finalized until it is first shown.
Tried but result is still the same.
Code:
Sub Main()
Form1.Show
Form2.Show
'standard code not considering Aero borders
Form2.Left = Form1.Left + Form1.Width '- 8 * 2 * Screen.TwipsPerPixelX
Form2.Top = Form1.Top
Form2.Width = Form1.Width
Form2.Height = Form1.Height
'Form1.Show
'Form2.Show
End Sub
Re: how do we know App is dpiAware applied currently?
Oh, one more question that will help me & others if they play with this in a bit. Is this a problem in just specific DPIs? or is it also a problem in standard/default 96 DPI (100%)?
Insomnia is just a byproduct of, "It can't be done"
Re: how do we know App is dpiAware applied currently?
Originally Posted by LaVolpe
Oh, one more question that will help me & others if they play with this in a bit. Is this a problem in just specific DPIs? or is it also a problem in standard/default 96 DPI (100%)?
I have tested on multiple DPIs surely including standard/default 96 DPI (100%), it is the same problem on Win10.
Initially, I found this issue on .NET, then VB6 also got problem. It is due to Aero theme on Win10 (window is shrinked in 4 sides by 8 pixels, but still occupied the space).
Last edited by DaveDavis; Aug 4th, 2020 at 11:07 PM.
Re: how do we know App is dpiAware applied currently?
Originally Posted by DaveDavis
Initially, I found this issue on .NET, then VB6 also got problem. It is due to Aero theme on Win10 (window is shrinked in 4 sides by 8 pixels, but still occupied the space).
Almost sounds like Win10 includes the shadowing whereas earlier versions did not?
I'd be glad to take a look-see tomorrow after I get off work & get some sleep. Until then... maybe someone else will chime in with a solution that does not rely on hardcoding magic-number offsets.
Insomnia is just a byproduct of, "It can't be done"
Re: how do we know App is dpiAware applied currently?
Originally Posted by LaVolpe
maybe someone else will chime in with a solution that does not rely on hardcoding magic-number offsets.
Computing DwmGetWindowAttribute and GetWindowRect will give the 'magic-number' but they are mad if no DpiAware and Permonitor DpiAwareness applied in manifest.
Last edited by DaveDavis; Aug 5th, 2020 at 01:18 AM.
Re: how do we know App is dpiAware applied currently?
Want to see something neat?
Add the following to each form (just for testing)
Code:
Private Const DWMNCRP_DISABLED = 1
Private Const DWMNCRP_ENABLED = 2
Private Const DWMWA_NCRENDERING_POLICY = 2
Private Sub Form_Load()
Call DwmSetWindowAttribute(hWnd, DWMWA_NCRENDERING_POLICY, DWMNCRP_DISABLED, 4)
End Sub
Now run your test project in Win10
Win10 keeps the form's size as reported. However, its rendering of the non-client area is different in some ways. It almost lops off everything around the client area and replaces it with shadows.
Edited. Remove those test lines above and now try this:
-- in your bas module, add these
Code:
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function DwmGetWindowAttribute Lib "dwmapi.dll" (ByVal hWnd As Long, ByVal dwAttribute As Long, ByRef pvAttribute As Any, ByVal cbAttribute As Long) As Long
Const DWMWA_EXTENDED_FRAME_BOUNDS = 9
and now tweak your sub Main
Code:
Dim wRect As RECT
DwmGetWindowAttribute Form1.hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, wRect, 16
Form2.Left = Form1.Left + (wRect.Right - wRect.Left) * Screen.TwipsPerPixelX
Form2.Top = Form1.Top
Form2.Width = Form1.Width
Form2.Height = Form1.Height
Last edited by LaVolpe; Aug 5th, 2020 at 02:45 AM.
Insomnia is just a byproduct of, "It can't be done"
Re: how do we know App is dpiAware applied currently?
Originally Posted by LaVolpe
Want to see something neat?
Add the following to each form (just for testing)
Code:
Private Const DWMNCRP_DISABLED = 1
Private Const DWMNCRP_ENABLED = 2
Private Const DWMWA_NCRENDERING_POLICY = 2
Private Sub Form_Load()
Call DwmSetWindowAttribute(hWnd, DWMWA_NCRENDERING_POLICY, DWMNCRP_DISABLED, 4)
End Sub
Now run your test project in Win10
Win10 keeps the form's size as reported. However, its rendering of the non-client area is different in some ways. It almost lops off everything around the client area and replaces it with shadows.
Edited. Remove those test lines above and now try this:
-- in your bas module, add these
Code:
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function DwmGetWindowAttribute Lib "dwmapi.dll" (ByVal hWnd As Long, ByVal dwAttribute As Long, ByRef pvAttribute As Any, ByVal cbAttribute As Long) As Long
Const DWMWA_EXTENDED_FRAME_BOUNDS = 9
and now tweak your sub Main
Code:
Dim wRect As RECT
DwmGetWindowAttribute Form1.hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, wRect, 16
Form2.Left = Form1.Left + (wRect.Right - wRect.Left) * Screen.TwipsPerPixelX
Form2.Top = Form1.Top
Form2.Width = Form1.Width
Form2.Height = Form1.Height
No, they are still seperated.
Look simple, but it is complicated~
Last edited by DaveDavis; Aug 5th, 2020 at 04:14 AM.
Re: how do we know App is dpiAware applied currently?
Have to Show both Forms at first then do twice computes, and need to apply DpiAware in manifest. Twice showing cause crazy flashing at slow machine, I can see them movement. So, this solution is not applicable.
Code:
Option Explicit
'declarations for Function BorderSize
Public Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function IsThreadDesktopComposited Lib "user32" () As Long
Private Declare Function DwmGetWindowAttribute Lib "dwmapi.dll" _
(ByVal hWnd As Long, ByVal dwAttribute As Long, ByRef pvAttribute As Any, ByVal cbAttribute As Long) As Long
Private Const DWMWA_EXTENDED_FRAME_BOUNDS = 9&
Private Declare Function GetWindowRect Lib "user32" (ByVal hWnd As Long, lpRect As RECT) As Long
Private Declare Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, _
ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, _
ByVal hInstance As Long, lpParam As Any) As Long
Private Declare Function DestroyWindow Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, _
ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, _
ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function GetClientRect Lib "user32" (ByVal hWnd As Long, lpRect As RECT) As Long
Private Declare Function MoveWindowA Lib "user32" Alias "MoveWindow" (ByVal hWnd As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long
Private Const GWL_STYLE As Long = (-16&)
Private Const WS_THICKFRAME As Long = &H40000
Private Const WS_VISIBLE = &H10000000
Private Const GWL_EXSTYLE = -20&
Private Const WS_EX_TOOLWINDOW = &H80
Private Const WS_EX_DLGMODALFRAME = &H1&
Private Const SWP_FRAMECHANGED = &H20
Private Const SWP_NOZORDER = &H4
Private Const SWP_NOMOVE = 2
Private Const SWP_NOSIZE = 1
Sub Main()
'Initialization done we can start doing useful stuff
' Goal - Place Sizable Form2 to Right of Fixed Form1
'standard code not considering Aero borders
' Form2.Left = Form1.Left + Form1.Width '+ 2 * BorderSize(Form2).Right
' Form2.Top = Form1.Top
' Form2.Width = Form1.Width
' Form2.Height = Form1.Height
'turns into the following:
Dim Form1Right As Long: Form1Right = BorderSize2(Form1).Right
Dim Form2Left As Long: Form2Left = BorderSize2(Form2).Left
Form2.Left = Form1.Left + Form1.Width + Form1Right + Form2Left
Form2.Top = Form1.Top '- BorderSize(Form1).Top
Form2.Width = Form1.Width '+ BorderSize(Form1).Left * 2
Form2.Height = Form1.Height '+ BorderSize(Form1).Bottom + BorderSize(Form1).Top
Form1.Show
Form2.Show
End Sub
Private Function BorderSize2(Frm As Form) As RECT
Dim lret&, TempWindowHwnd&
Dim AeroRect As RECT, TempWindowRect As RECT, FinalBorderSize As RECT
TempWindowHwnd& = CreateWindowEx(0&, "button", "", WS_VISIBLE Or 113770496, 0, 0, 100&, 100&, Frm.hWnd, 0&, App.hInstance, 0&)
lret = SetWindowLong(TempWindowHwnd, GWL_STYLE, GetWindowLong(TempWindowHwnd, GWL_STYLE) Xor WS_THICKFRAME)
'change to sizable Tool window and loop
lret = SetWindowLong(TempWindowHwnd, GWL_EXSTYLE, GetWindowLong(TempWindowHwnd, GWL_EXSTYLE) Or WS_EX_TOOLWINDOW)
lret = GetWindowRect(TempWindowHwnd, TempWindowRect)
lret = SetWindowPos(TempWindowHwnd, 0&, 0&, 0&, 0&, 0&, SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER Or SWP_FRAMECHANGED)
On Error Resume Next 'API below is not supported in XP and may cause error when called so catch that to keep use under XP sweet
lret = DwmGetWindowAttribute(TempWindowHwnd, DWMWA_EXTENDED_FRAME_BOUNDS, AeroRect, LenB(AeroRect))
With FinalBorderSize
.Left = (TempWindowRect.Left - AeroRect.Left) * Screen.TwipsPerPixelX
.Top = (TempWindowRect.Top - AeroRect.Top) * Screen.TwipsPerPixelY
.Right = (AeroRect.Right - TempWindowRect.Right) * Screen.TwipsPerPixelX
.Bottom = (AeroRect.Bottom - TempWindowRect.Bottom) * Screen.TwipsPerPixelY
End With
DestroyWindow TempWindowHwnd
BorderSize2 = FinalBorderSize
End Function
Option Explicit
Private Enum BOOL
FALSE_
TRUE_
End Enum
#If False Then
Dim FALSE_, TRUE_
#End If
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function DwmGetWindowAttribute Lib "dwmapi.dll" (ByVal hWnd As Long, ByVal dwAttribute As Long, ByRef pvAttribute As Any, ByVal cbAttribute As Long) As Long
Private Declare Function DwmIsCompositionEnabled Lib "dwmapi.dll" (ByRef pfEnabled As BOOL) As Long
Private Declare Function GetWindowRect Lib "user32.dll" (ByVal hWnd As Long, ByRef lpRect As RECT) As BOOL
Private Declare Function MoveWindow Lib "user32.dll" (ByVal hWnd As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, Optional ByVal bRepaint As BOOL = TRUE_) As BOOL
Private Sub Main()
Const DWMWA_EXTENDED_FRAME_BOUNDS = 9&, S_OK = 0&
Dim IsCompEnabled As BOOL, R1 As RECT, R2 As RECT, R3 As RECT
Form1.Show
Form2.Show
On Error Resume Next 'Try calling DwmIsCompositionEnabled
Select Case DwmIsCompositionEnabled(IsCompEnabled) = S_OK And IsCompEnabled = TRUE_
Case True
On Error GoTo 0
If DwmGetWindowAttribute(Form1.hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, R1, LenB(R1)) = S_OK Then
If DwmGetWindowAttribute(Form2.hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, R2, LenB(R2)) = S_OK Then
If GetWindowRect(Form2.hWnd, R3) Then
MoveWindow Form2.hWnd, _
R1.Right + (R3.Left - R2.Left), _
R1.Top + (R3.Top - R2.Top), _
(R1.Right - R1.Left) - ((R3.Left - R2.Left) + (R2.Right - R3.Right)), _
(R1.Bottom - R1.Top) - ((R3.Top - R2.Top) + (R2.Bottom - R3.Bottom))
End If
End If
End If
Case Else
Err = vbObjectError
End Select
If Err Then 'DWM is either unavailable or disabled so there's
On Error GoTo 0 'no need to compensate for bogus coordinates
If GetWindowRect(Form1.hWnd, R1) Then
MoveWindow Form2.hWnd, R1.Right, R1.Top, R1.Right - R1.Left, R1.Bottom - R1.Top
End If
End If
End Sub
Last edited by DaveDavis; Aug 5th, 2020 at 04:38 AM.
Re: how do we know App is dpiAware applied currently?
Originally Posted by DaveDavis
I am not so convinced to manifest a component. Normal practice is that App should take care of manifest.
I don't think that telling the clients that they must manifest their program for DPI aware per monitor for your component to work would be a good idea, since they would have to do a lot and hard work to handle how all the forms are shown since the system would not take care of the issue any more.
Re: how do we know App is dpiAware applied currently?
Originally Posted by Eduardo-
I don't think that telling the clients that they must manifest their program for DPI aware per monitor for your component to work would be a good idea, since they would have to do a lot and hard work to handle how all the forms are shown since the system would not take care of the issue any more.
I understand your points. I follow MS .NET practice that component has no manifest option.
Re: how do we know App is dpiAware applied currently?
Originally Posted by DaveDavis
No, they are still seperated.
Not when I tested it otherwise I wouldn't have posted it. In fact, I found it to be visually ugly. The shadow from the borders made it appear like a solid fuzzy black line overlapping Form1 when Form2 was in the foreground. Tomorrow morning I can post a screenshot, but you should be able to reproduce the results if using your test project from post #5
However, I tested it with a manifested IDE (for common controls) @ 100% DPI
Insomnia is just a byproduct of, "It can't be done"
Re: how do we know App is dpiAware applied currently?
Originally Posted by LaVolpe
Not when I tested it otherwise I wouldn't have posted it. In fact, I found it to be visually ugly. The shadow from the borders made it appear like a solid fuzzy black line overlapping Form1 when Form2 was in the foreground. Tomorrow morning I can post a screenshot, but you should be able to reproduce the results if using your test project from post #5
However, I tested it with a manifested IDE (for common controls) @ 100% DPI
With 96Dpi@100%, I only see normal nice appearence, though there's a black line at Top of Form2 when Form2 was in the foreground.
To ensure it is a solid solution, we should do comprehensive test, please consider:
1. Form1 & Form2 has different Scale mode
2. Form1 & Form2 has different BorderStyle and MaxButton/MinButton.
3. DpiAware (System) Only
4. DpiAwareness Per monitor (System & Monitors)
6. No DpiAware (manifest) at all
7. Different Dpi Scale factor
8. Multi-monitors
9. OS (Win10,Win7,WinXP...)
Last edited by DaveDavis; Aug 5th, 2020 at 07:23 PM.
Re: how do we know App is dpiAware applied currently?
Originally Posted by LaVolpe
I am assuming you are not just aligning forms, you are aligning some component? What is that? A form inside a ocx or dll? Any external window?
It is just 8 pixels offset, but it is hard to get this magic number considering 9 conditions I list above!
At this moment, it is just aligning forms, but it could be a component such as a custom dropdown using a Form as container in Grid or Listview control.
Last edited by DaveDavis; Aug 5th, 2020 at 08:01 PM.
Re: how do we know App is dpiAware applied currently?
Originally Posted by DaveDavis
It is just 8 pixels offset, but it is hard to get this magic number considering 9 conditions I list above!
Quite confirmed that the magic number offset in pixels are borderwidth which is vary from OS, theme, FormBorderStyle,screen Dpi Scale and DpiAware setting. But luckily we can retrieve from few APIs: