I have a WIN32 SW which the UI was designed in 96 DPI, so when user changes the windows DPI from 96 to 120 or bigger, the UI will be wrong. I want to know if there is API to force my SW to display the UI with 96DPI.
Starting with Windows Vista, scaling for DPI is supposed to happen automatically. I don't have any direct experience to know how well it works, but here's the page that explains how to turn it off:
http://msdn.microsoft.com/en-us/library/ms701681(VS.85).aspx
You can also add an appcompat key for your application. The place for this in the registry is:
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
That is the per-user settings, there is the same key in HKEY_LOCAL_MACHINE, but of course that is a system setting and will require elevated privileges to write to. Adding a key like so:
"C:\path\to\app.exe"="HIGHDPIAWARE"
Will enable that compatibility flag for your program, which will turn off DPI scaling. This is for Vista+.
SetProcessDPIAware is also an option, but be aware there is a danger of a race condition, according to the documentation.
There is no API to force your app to show at 96DPI. The DPI is a device setting and cannot be controlled per application.
If you can change your program, you can scale your UI to look properly on high DPI though. You need to call GetDeviceCaps; more specificaly, you need to calculate the X and Y scale using the number returned for LOGPIXELSX and LOGPIXELSY. Something like this:
HDC hdc;
double m_dDPIScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
double m_dDPIScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
Related
Is it possible some how, to change the DPI scaling setting in kiosk-mode Windows 10 LTSC ?
In the kiosk-mode windows 10,the UWP application is set up to 150%, so UI controls are really large.
I have to say that in UWP apps, it is not possible to change the scale size of the system. My suggestion is that you might need to scale the controls in your app based on the system scale value to make your app looks better.
You could use the following code to get the current scale size:
DisplayInformation information = DisplayInformation.GetForCurrentView();
var factor = information.ResolutionScale;
Then you could apply a transform for the controls in your app to make them look better. For example using the ScaleTransform
My application happens to be written in Python using pygame, which wraps SDL, but I'm imagining that this is probably a more-general question to do with the Windows API.
In some of my Python applications, I want pixel-for-pixel control under Windows 10 even at high resolutions. I want to be able to ensure, for example, that if my Surface Pro 3 has a native resolution of 2160x1440, then I can enter full-screen mode with those dimensions and present a full-screen image of exactly those dimensions.
The barrier to this is "DPI scaling". By default, under Windows' Settings -> Display, the value of "Change the size of text, apps, and other items" is "150% (Recommended)" and the result is that I only see 2/3 of my image. I have discovered how to fix this behaviour...
systemwide, by moving that slider down to 100% (but that's undesirable for most other applications)
just for python.exe and pythonw.exe, by going to those executables' "Properties" dialogs, Compatibility tab, and clicking "Disable display scaling on high DPI settings". I can do this for me alone, or for all users. I can also automate this process by setting the appropriate keys in the registry programmatically. Or via .exe.manifest files (which also seems to require a global setting change, to prefer external manifests, with possible side-effects on other applications).
My question is: can I do this from inside my program on a per-launch basis, before I open my graphics window? I, or anyone using my software, won't necessarily want this setting enabled for all Python applications ever—we might want it just when running particular Python programs. I'm imagining there might be a winapi call (or failing that something inside SDL, wrapped by pygame) that could achieve this, but so far my research is drawing a blank.
Here's the answer I was looking for, based on comments by IInspectable and andlabs (many thanks):
import ctypes
# Query DPI Awareness (Windows 10 and 8)
awareness = ctypes.c_int()
errorCode = ctypes.windll.shcore.GetProcessDpiAwareness(0, ctypes.byref(awareness))
print(awareness.value)
# Set DPI Awareness (Windows 10 and 8)
errorCode = ctypes.windll.shcore.SetProcessDpiAwareness(2)
# the argument is the awareness level, which can be 0, 1 or 2:
# for 1-to-1 pixel control I seem to need it to be non-zero (I'm using level 2)
# Set DPI Awareness (Windows 7 and Vista)
success = ctypes.windll.user32.SetProcessDPIAware()
# behaviour on later OSes is undefined, although when I run it on my Windows 10 machine, it seems to work with effects identical to SetProcessDpiAwareness(1)
The awareness levels are defined as follows:
typedef enum _PROCESS_DPI_AWARENESS {
PROCESS_DPI_UNAWARE = 0,
/* DPI unaware. This app does not scale for DPI changes and is
always assumed to have a scale factor of 100% (96 DPI). It
will be automatically scaled by the system on any other DPI
setting. */
PROCESS_SYSTEM_DPI_AWARE = 1,
/* System DPI aware. This app does not scale for DPI changes.
It will query for the DPI once and use that value for the
lifetime of the app. If the DPI changes, the app will not
adjust to the new DPI value. It will be automatically scaled
up or down by the system when the DPI changes from the system
value. */
PROCESS_PER_MONITOR_DPI_AWARE = 2
/* Per monitor DPI aware. This app checks for the DPI when it is
created and adjusts the scale factor whenever the DPI changes.
These applications are not automatically scaled by the system. */
} PROCESS_DPI_AWARENESS;
Level 2 sounds most appropriate for my goal although 1 will also work provided there's no change in system resolution / DPI scaling.
SetProcessDpiAwareness will fail with errorCode = -2147024891 = 0x80070005 = E_ACCESSDENIED if it has previously been called for the current process (and that includes being called by the system when the process is launched, due to a registry key or .manifest file)
I'm using accessibility with the AccessibleObjectFromPoint function, and I'd like it to work correctly on a per-monitor DPI environment. Unfortunately, I can't get it to work. I tried many things, and the situation for now is:
My app is marked as per-monitor-DPI-aware in the manifest. (True/PM)
I use GetCursorPos and then AccessibleObjectFromPoint.
How can the problem be reproduced:
Have two monitors, one with 100% DPI, the other with 125%.
Run Chrome on the 125% monitor.
Use AccessibleObjectFromPoint on one of the tab names, it won't work.
It works with some apps (DPI-aware, it seems, like explorer), but doesn't work with others. I tried several relevant functions, such as GetPhysicalCursorPos and PhysicalToLogicalPointForPerMonitorDPI, but nothing works.
It's worth noting that Microsoft's inspect.exe works as expected.
I’ve been struggling with this exact same problem for several weeks and can now tell you my findings. Unfortunately I can’t give you more than a hint of code, because the project I am working on, is proprietary.
The issue started at Windows 8.1. The problem did not exist on Windows 7 or Vista, because AccessibleObjectFromPoint always used raw physical coordinates, as documented here: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317984(v=vs.85).aspx .
“Microsoft Active Accessibility does not use logical coordinates. The following methods and functions either return physical coordinates or take them as parameters.” This has not been true since Windows 8.1.
AccessibleObjectFromPoint now uses a flawed calculation that cannot always find the correct window for reasons similar to my question here: High DPI scaling, mouse hooks and WindowFromPoint .
My findings lead me to one conclusion: The API is broken. This does not mean it is not possible though.
Possible solutions I have partially tested that seem to work follow.
Prerequisites are that you
1/. Make your process per monitor DPI aware, NOT USING THE MANIFEST (more on that later).
2/. Determine the hWnd of the window you want to query (WindowFromPoint() variants)
3/. Determine the monitor DPI of the queried hWnd
4/. Determine the DPI of your process
5/. Determine the DPI of the queried hWnd
6/. Determine the monitor origin and offset for the queried hWnd (MonitorFromWindow() and GetMonitorInfo() )
Next, depends on your platform
Windows 10.0.14393+
Write a function that finds the IAccessible (AccessibleObjectFromWindow() ) from the top level window, and then recursively call IAccessible::accHitTest until you reach the bottom-most IAccessible and perhaps ChildID data. Return that as if you would call AccessibleObjectFromPoint.
To call it successfully, you will need to scale the (x,y) co-ordinates into the scale system of the queried hWnd, using the DPIs and co-ordinates fetched in the list above. Watch out for systems where monitors are not the same size or if monitors are partially offset, or above and below.
And now for the important part for 10.0.14393 – Set your thread to the same DPI_AWARENESS_CONTEXT of the hWnd you are querying. Now call your new function. Now revert your thread to monitor DPI aware, and voila, it works, even if the window is not maximised. This is why you must not use the manifest.
If you are on Windows 8.1 to 10.0.10586 you have a tougher task.
Instead of calling accHitTest, as above, you have to recursively call AccessibleChildren and iterate the call IAccessible::accLocation to determine if your test point is within each child. This is tricky and starts to get really messy when you get to e.g. combo boxes in products like Office, which is only system DPI aware.
That’s all I can give you for now.
To do it successfully on multi-platform (mine has to work from Vista to Windows-Current) the only really safe bet is to write a wrapper DLL in C++ that can determine at runtime which OS it is on and change code path accordingly. The reason you want to do it in C++ is to avoid passing IAccessible objects across the .Net/unmanaged marshalling boundary. You can call IUnknown::Release on objects you don’t need to return n the unmanaged side. You can do it all in .Net, but it will be slow.
P.S. also watch out for Chrome returning infinite trees where parents are children of their parents, some snity checks are required. Also, Chrome does not return accRole correctly, and will give you HTML tags instead of VT_I4.
Good luck
A fairly workable solution is as follows, in your IAccessible recursive function:
Use getwindowrect to capture the physical right on main window
Use accChild.accLocation in loop to capture left and Width on each Object
Add this simple test
If l > rct2r.Right And l > arrIACC.x2 Then
arrIACC.x2 = l + w
End If
if dpi = 100 then no Object is furter out than physical right
if dpi > 100 then closebutton is...x pix offset
Use the difference to rescale all values you are in use of Width
arrIACC.w1 = CInt(((-rct2r.Left + arrIACC.w1) / arrIACC.x2) * rct2r.Right)
This solution is from an Excel plugin I have developed, I was testing the Width of the quick access toolbar qat and my result was +- 5 pixels regardless of any DPI.
My application is a plug-in developed in VC++ (win32). The solutions I could find didn't work for me.
I have two options :
To disable DPI changes for my DLL plugin. That means my plug-in will not be affected with DPI changes.
To scale all the controls according to the DPI change.
Please help.
Thanks.
1. Disable DPI affect on my application UI when some one re-configures the DPI. Help required : If possible, how to do that pro-grammatically.
This is not possible. Adrian already told you that. DPI is a user setting, not an application setting. If the user wants to change their DPI, they can do so. They can also apply backwards-compatibility hacks that turn off high DPI for a particular application, but that functionality is not available to applications. As the developer, you're expected to make sure that you support high DPI environments, not disable it.
The only thing you can do as an app developer is to fail to indicate that your application is DPI-aware. This is roughly equivalent to walking around with an "IDIOT" sign taped to your forehead. You might find that people are slightly more accommodating of your shortcomings, but they won't have much respect for you and probably won't care to hang out with you very often. Windows will do much the same thing: it will hide the truth from you about the user's actual DPI settings (you can't handle the truth) and instead it will scale up your interface automatically to match. The effect is an ugly one.
Of course, you also say that you're creating a plug-in DLL, which changes things. DLLs can't alter the DPI-awareness of the entire process because that is determined by the host application. If the host application indicates that it is high DPI aware, then your DLL must also be high DPI aware. This is an all or nothing setting, just like the "IDIOT" sign.
2. Scale the UI of my application according to the DPI change. Help required : How to determine the current DPI and how to scale the UI accordingly? Is it necessary to scale every component or any other way to scale them automatically.
In order to correctly determine the current DPI settings, you need to indicate to Windows that you're not an idiot your application is high DPI aware. As discussed above, if you do not do this, Windows will assume that you cannot handle the truth and lie to you. But as we also established above, this probably does not apply to you because you're creating a DLL that runs in the context of another process that is already establishing itself as high DPI aware.
So, what you need to do is determine the DPI scale factor. You need that to scale your user interface elements accordingly when running in high DPI environments. The baseline DPI setting is taken to be 96. The following code scales a SIZE structure (that defines a width and height) relative to that baseline:
void ScaleDpi(SIZE& size)
{
// Determine the current screen DPI.
const HDC hDC = GetDC(NULL);
const int dpiX = GetDeviceCaps(hDC, LOGPIXELSX);
const int dpiY = GetDeviceCaps(hDC, LOGPIXELSY);
ReleaseDC(NULL, hDC);
// Perform the scaling.
size.cx = MulDiv(size.cx, dpiX, 96);
size.cy = MulDiv(size.cy, dpiY, 96);
}
However, if you follow good design practices, you may not need to scale every control manually. When defining windows and dialogs in a resource file (e.g., using the Visual Studio Dialog Editor), you specify the layout in logical units (known as DLUs, or dialog units). These units are independent of any particular DPI setting and are therefore preferable to using something like pixels. A simple dialog with a few controls therefore requires no special scaling effort. You'll only need to perform manual scaling if you're creating and/or laying out controls at run-time.
You'll find more tips and techniques on how to write high DPI aware applications on MSDN, in the Writing High-DPI Win32 Applications article. And I don't link this just to say RTFM—it's actually extremely helpful, and I highly recommend reading it all.
The folks who make the RealWorld Icon and Cursor editors also maintain a helpful article on their site: DPI-aware applications in Windows Vista and Windows 7.
How can I have a vb6 program which opens correctly in 1280*1024 but when switched to other resolutions say 640*480 i can only see half of the screen. how to re-size my vb6 program so that it automatically fits in any screen resolution?
You need to use the Screen object, this will always give you the current resolution in pixels:
Dim screenwidth,screenheight As Single
screenwidth = Screen.Width \ Screen.TwipsPerPixelX
screenheight = Screen.Height \ Screen.TwipsPerPixelY
Usually a Form amenable to resizing has controls that lend themselves to a "flow" layout. Often this is something like a TextBox, grid control, etc. that supports scrollbars. You shrink/grow such controls as required after allocating positions for (i.e. moving) the fixed-size elements like buttons and such.
For a busy Form with lots of fixed size controls that isn't "document oriented" there is no set answer. Sometimes creating a scrollable Form makes sense but usually it doesn't.
Some people try to resize "fixed" elements, change fonts sizes, etc. This can produce results of mixed quality though, sometimes good and sometimes not.
Considerations about the Form size are best made up front as part of the design process. For some applications it might be better to decide on a minimum supported Form size. In other cases you may have to break things up with dialog Forms or tab controls.
There's no easy way to do this in VB6, like there is in .Net. You have to manually resize everything in the form's Resized event handler based on the new form's client size. It's a pain, and a huge mess, but it's the only way to do it.
Correction: There's never only one way to do things, but I've been programming VB6 for several years, and usually just writing it into the Resize handler is straightforward enough, and I haven't found any good way to do it other than that.
Have you tried any 3rd party tools for doing this? Here's (a free) one that seems to work :-
ActiveResize Control Lite - I created a quick project to try it and it does what it says on the tin!
The lite version has some limitions such as number of forms in project, number of controls on form etc. You can also buy a Standard or Professional version if you need more functionality.
I know we've spent countless hours trying to implement our own resizing code only to remove it all and fix the location of most controls, move a few to make it look better and limit the min/max functionality of the form - none of which give a nice user experience. If we needed to do it again I probably use this control (or a similar one) just for the time savings.
I use ComponentOne SizerOne
The C1Elastic control allow from resizing and maintain the aspect ratio, resizing the inside controls on the setting you defined.
It's not free, but it payed itself with all the time I saved.
Form1.Height = Screen.Height
Form1.Width = Screen.Width
This code sets form size according to screen resolution.
"ActiveResize Control Lite" ActiveX tool is limited to 20 controls per form.
Once we know the screen resolution, there are a number of things you can do.
• The easiest solution would be do design different form to accommodate the four most popular monitor resolutions – 640 x 480, 800 x 600, 1024 x 768, and 1600 x 1200.
• Alternatively, we could write code that dynamically resizes and relocates every control on the form, based on the screen resolution – not an easy undertaking!
• Third party controls that resize the controls based on the screen resolution are quite effective. On the whole, though, it's better to just avoid this kind of problem, if you can. For example, see Creating Flexible Forms in Visual Basic (Flexi-Forms) at codeguru.com
To auto fit screen resolution you need to download an active x, drag it on your conform.
Search for "veg gold vb6.0 screen Resize".