How do you scale the title bar on a DPI aware win application? - c++

I am making my app dpi-aware per monitor by setting <dpiAware>True/PM</dpiAware> in the manifest file. I can verify with process explorer that this is indeed working or by calling GetProcessDpiAwareness.
This is all working fine and I can scale anything in the client area fine in my code. However, my only problem is that if I drag my app from a system-dpi monitor to a non-system dpi monitor, the title bar and any system menu would either become too big or too small. This isn't the case for most built-in apps (e.g. calc, edge browser, etc..) so there must be away to scale it properly. Does anyone how the devs at MS did this?
The screenshot below should explain my problem better. Also notice, that the padding between the close, min, and max button is different when it's scaled (96dpi).
Sample app I'm attaching a very simple app that is per-monitor dpi aware.

The Windows 10 Anniversary Update (v1607) has added a new API you must call to enable DPI scaling of the non-client areas: EnableNonClientDpiScaling. This function should be called, when WM_NCCREATE is received. The message is sent to the window's procedure callback during window creation.
Example:
case WM_NCCREATE:
{
if (!EnableNonClientDpiScaling(hWnd))
{
// Error handling
return FALSE;
}
return DefWindowProcW(...);
}
If the application's DPI-awareness context is DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, then calling EnableNonClientDpiScaling should be omitted, as it won't have any effect, although the function will still return successfully.
From the documentation:
Non-client scaling for top-level windows is not enabled by default. You must call this API to enable it for each individual top-level window for which you wish to have the non-client area scale automatically. Once you do, there is no way to disable it. Enabling non-client scaling means that all the areas drawn by the system for the window will automatically scale in response to DPI changes on the window. That includes areas like the caption bar, the scrollbars, and the menu bar. You want to call EnableNonClientDpiScaling when you want the operating system to be responsible for rendering these areas automatically at the correct size based on the API of the monitor.
See this blog post for additional information about DPI scaling changes in Windows 10 AU.

Does anyone how the devs at MS did this?
This has a pretty disappointing answer. Using Alin Constantin's WinCheat and inspecting the top-level window of Calculator, I see a window size of 320x576, and a client size that is also 320x576.
In other words, Microsoft entirely avoids the problem by suppressing the non-client area of the window, putting everything in the client area instead. Making this work well for you may involve custom drawing of the title bar.
Something worth noting is that Calculator and e.g. Windows Explorer don't use the same colour for the title bars. Calculator doing custom drawing of the title bar would explain that perfectly.

UPDATE:
It is enough to add new <dpiAwarness> declaration to manifest to solve all this mess. Example is here.
Remnants of former investigations (obsolete):
More investigations on the subject.
System setup: two monitors, one is 96 dpi another one is 267 dpi (Microsoft Surface 4).
Testing window is moved to secondary 96 dpi monitor:
Here is rendering (wrong, IMO) with <dpiAware>true/pm</dpiAware> in manifest:
Note huge size of caption bar and wrong sizes of window icons.
And here is correct rendering using <dpiAware>true</dpiAware>
And I suspect that MSDN documentation is plainly misleading about values of PROCESS_DPI_AWARENESS. I do not see any differences in messages and styles between <dpiAware>true</dpiAware> and <dpiAware>true/pm</dpiAware>. The later one just makes caption larger. In both case application receives WM_DPICHANGED message while moving between monitors with different DPI.
Sigh.

The documentation says:
Note that the non-client area of a per monitor–DPI aware application is not scaled by Windows, and will appear proportionately smaller on a high DPI display.
The Microsoft apps that you link to deal with this by removing the non-client area and making the client area cover the entire window.

Related

DPI Scaling with windows-generated dialogues in C++?

I'm trying to properly DPI scale an application in C++ and I'm having trouble getting this to work with the File Picker window created from calling OPENFILENAMEW from commdlg.h.
I'm using three monitors: two with 1.0 dpi and one with 2.5 dpi. For me, the file picker only opens with 1.0 DPI regardless of what window my application is in. So when I drag the file picker to the 2.5 dpi monitor, the window is so small is hard to read. I can only get it to scale with 2.5 dpi when I disconnect the other monitors. I looked at the documentation for OPENFILENAMEW and there is a flag to allow the dialogue to resize manually but that's about it.
It has to register the dpi at some point to scale but I just can't find it.
Does anyone know how to do this?
Enabling per monitor DPI awareness in Manifest settings didn't solve this completely but it did lead me to the answer I was looking for! So the issue that persisted was that once the file-picker window was created, it kept it's DPI scale from its original window even after moving it to a window with different DPI.
Apparently the options in Manifest don't support this and neither does the SetProcessDpiAwareness function in the shellscaling api, which can be used to set that Manifest setting programmatically.
However, the SetProcessDpiAwarenessContext from winuser.h has one more option that the others don't: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. This can only be used on windows machines with the Creators update (named Redstone 2) and you can check that to do DPI scaling right when you can, and wrong but as good as possible when you can't:
if (IsWindowsVersionOrGreater(HIBYTE(NTDDI_WIN10_RS2), LOBYTE(NTDDI_WIN10_RS2), 0)) {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
else {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
}
And this works!

MFC picture control changes size when DPI awareness disabled or running on Win7

I made an MFC app for my friend using VS2015 in Win10. It looks like this, which is exactly the same as in resource editor.
.
But when he ran the app on his computer in Win7, the Bitmap image in Picture Control enlarges and covers up some text boxes below, which looks like this.
.
After I searched and realized it may be related with DPI awareness. I disabled DPI-Awareness in property page of Manifest Tool and rebuilt. The same thing happened even when it runs in Win10.
Could someone help me explain the cause of this and find a solution to fix the size of the image control? Thanks.
The main problem is that a dialog from a resource is always measured in DLUs.
And DLUs are calculated from the size of the font, that is used for the dialog.
See this article how dialog base units are calculated.
Now you have a static picture control that is sized in DLUs. The bitmap is just scaled in pixels and is never resized, when you assign it to a static dialog control. And because the real size of the static control depends on the used font, you get different layouts for your dialog and your bitmap.
And because just the font changes when you choose no DPI awareness and because the font changes from windows version to windows version your dialog always look different.
Advice: Paint you picture your own and stretch it accordingly.
Also this stackoverflow question is nice documents and shows the effect of DLUs.
And here some code for auto sizeing picture controls.
An auto-sizing bitmap picture control
A simple image preview class using GDI+
CxImage
Normally, I prefer to keep control in my hand by using SetWindowPos() to set the size of image I want in different situations. You can use below two lines to control/set position and size of your image.
Assume ID of the Picture Control is IDC_STATIC2 then you can use like:
CStatic * pStatic = (CStatic *) GetDlgItem(IDC_STATIC2);
pStatic->SetWindowPos(NULL,20,20,50,50,0);

Detect when window gets overlapped by another window from the same or different process

Background
We are running our application in XenDesktop mode and our window shows some real time information. But if some other application is also launched in that XenDekstop and that application is overlapping our window then we want to stop rendering. And once its moved out of our window then we want to start rendering again. Unfortunately, right now these kind of notifications are not supported by Citrix.
Question
How can we detect when a part or the whole of the application window has been overlapped by other windows, and also detect when that's no longer the case?
I found the WindowFromPoint family of functions when Googling, however, that is not practical for my purpose because I'd need to keep polling all co-ordinates that my window covers.
Bonus points: For a start, it's enough if I can just detect when such overlapping occurs. However, if I can detect exactly which area(s) of my window is/are covered that would be great.
There is no such API function. And usually the it isn't needed. WM_PAINT cares for itself.
If you get a WM_PAINT message you receive a region and a update rectangle of the area that needs a repaint. But it is a rectangle only, no complex region. Also there is a clipping region too.
But it should be possible to calculate the region by yourself. If we are talking about a top level window.
Create a rectangular region that is consists of your window rect
Walk all top level windows from back to front
Ignore all windows until you find your top level window
For each visible top level window create a rectangular region and XOR it with your current one.
Should be easy with GetWindow GW_HWNDNEXT
The resulting region is what you are searching for.
Again: There is no such function or message that determine, that is fired or can be executed to find such overlapping. There is no need for such an information. The system cares for itself with the appropriate WM_PAINT message. If an area is covered. There is no need for an action. If an area is uncovered WM_PAINT gets fired.
I think you should be able to get this kind of information when processing the WM_PAINT message, since normally the clipping region would be set accordingly. Calls to the RectVisible() function should tell you, for any part of your window, whether it "should be painted" (and so, whether it was just uncovered).
Despite this is not a solution to the OP's problem, I want to remark that once an overlapping window reveals part of your window (and also if you drag more area of your window back to screen), you will get a WM_ERASEBKGND message before the WM_PAINT.

How to design a dialog with 640*480 which can be used on 800*600 resolution?

I should design a dialog with size 640*480 ,which can be used on 800*600 resolution.
At present my dialog size is 411*292 ,look wise this itself looks good enough ,but actually I was
asked to design dialog with the size I mentioned above.I tried that also but that dialog is too biger
than my earlier dialog of 411*292 size.
while using in 800*600 resolution or my dialog seems to be bigger and not able to see some of my controls
This is size of my dialog,
IDD_DIALOG_MYPAGE DIALOGEX 0, 0, 411, 292
can anyone please let me know how to design a dialog with 640*480 (which should not be bigger).
And how can I make my application to fit to any resolution so that all the controls on the dialog should be visible.
Dialogs are scaled in Dialog Units and Dialog Units scale upon the current UI settings and depend upon the System font. So if the user selects a larger UI representation your Dialog will grow too.
Best advise 1 I could give: Size all you controls by youself. Pick a font you like. Set it to the Control, calculate the positions ofthe new controls and use SetWindowPos/MoveWindow.
Only in this case you have full Control.
You may also use a fixed font size in the Dialog resource, but alos this font scales upon the DPI untis selected for the Screen...
So best advise 2 I could give: Scale upon the DPI/UI Settings and Show the dialog n a size that the user wants too, thats what a normal dialog will do...
You're in for a world of pain. If I understand correctly, you're being asked to make a dialog that will always be 640*480 pixels. It seems like whoever is asking hasn't kept up with UI development since 1995. 'Pixels' are meaningless in 2014. First, the units in resource files are in 'DLUs', 'Dialog Length Units'. See e.g. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/17/74811.aspx and support.microsoft.com/kb/125681 for some starters on how to convert one to the other. However, with high DPI displays these methods are insufficient. Now it doesn't just depend on the size of the system font any more, but also on the settings the user has selected for how big to display 'things' on various monitors, whether to differentiate between monitors at all, etc. See for a start http://msdn.microsoft.com/en-us/library/windows/desktop/dn469266%28v=vs.85%29.aspx . But beware, because there are updates to these API's - e.g. Win7, Win8 and Win8.1 all have new features in this regards.
All of this of course doesn't help you. It's not terribly hard to make a dialog that will fit on 640x480 and up; just make it as small as possible. But this part: "And how can I make my application to fit to any resolution so that all the controls on the dialog should be visible." is really difficult. Does 'any' mean 'also smaller than 640x480'? And does 'fit' mean that it should look 'native' also on higher resolutions? Should the dialog be resizable? You can either go with the simple and robust approach suggested by xMRi above, or you will have to ask whoever is writing your spec to be more clear about what they want, keeping in mind all the things I outlined above.

C++ - How to screen-capture, except for some windows

Situation: I have a software that performs screen sharing over the Internet, where one user acts as a presenter, and other users act as viewers/attendees.
Besides the presentation windows, the presenter also has a set of NON-SHARING-WINDOWS that appear on the screen (a button bar for start sharing/stop sharing/etc., a Skype window etc.).
The presenter can configure from the setup of the screen sharing software to make these NON-SHARING-WINDOWS invisible (i.e. they will not appear in the screen sharing that is being sent to the attendees, but the window content behind them will appear in the screenshot).
The screenshots are sent at approximately 10 frames-per-second, or faster.
Question: how can I programmatically capture the screen, except for these NON-SHARING-WINDOWS windows?
Notes:
Because of the higher frames-per-second value, I cannot minimize/maximize/set alpha for these windows, because then the windows will flicker. The application is written in Win32 C++.
I would use layered windows, but because of the Windows 7 Desktop Composition feature, this is not usable out-of-the-box (and in Windows 8, you cannot use DwmEnableComposition anymore to temporarily and programmatically disable composition)
I could use the layered window approach for Windows XP/2000/7 etc., and a different approach for Windows 8 (if there is one), though I would prefer a single process that works on all systems
I could also try to "compose" the screenshots by capturing individual images (of the desktop, the windows that need to be captured) and using their z-index to create the final image, but because of the required frames-per-second value, this process would be too slow.
In windows even the desktop is considered a window and has its own HWND.
It seems however, not easily possible to only copy the "wallpaper" on its own.
So i basically see two ways to do that.
1. Copy the entire desktop e.g. BitBlt(GetWindowDC(GetDesktopWindow()),...)
OR
Use GetWindow and traverse the window list in backward direction starting from the Desktop-Window whose HWND you just can determine with GetDesktopWindow(), Like this:
// paint on a black DC
hwnd=GetDesktopWindow()
while (hwnd = GetWindow(hwnd, GW_HWNDPREV))
{
// is this window not shared? continue
// else bitblt it into our dc
}
Hope i gave some inspiration :-)
If someone knows a way how to copy ONLY the desktop without its child windows please let me know.
You can use Magnifier API.
There is a function in magnifier API that allows you to exclude specific windows from your target window (your window with 1x magnification where magnifier renders).
You can set this window to full screen and make it transparent and then use PrintWindow function.
The function: https://learn.microsoft.com/en-us/windows/desktop/api/magnification/nf-magnification-magsetwindowfilterlist
Sample projects:
https://www.codeproject.com/Articles/607288/Screenshot-using-the-Magnification-library
https://code.msdn.microsoft.com/windowsdesktop/Magnification-API-Sample-14269fd2
I'm aware this question is pretty old, but I ran into the same problem and it was very, very hard to find any information at all regarding this.
Since Windows 10 version 2004 (build 10.0.19041), the SetWindowDisplayAffinity API has been expanded to include a flag called WDA_EXCLUDEFROMCAPTURE (0x00000011). This will remove the window from images captured with BitBlt
The window is displayed only on a monitor. Everywhere else, the window does not appear at all.
One use for this affinity is for windows that show video recording controls, so that the controls are not included in the capture.
Introduced in Windows 10 Version 2004. See remarks about compatibility regarding previous versions of Windows.
For versions before 2004, it will use the existing WDA_MONITOR flag.
I have tested this with a screen capture of the desktop and I am unsure what would happen if you were to use a window DC.
So I guess a possible solution would be:
// get window handle
hWnd = (...)
BOOL result = SetWindowDisplayAffinity(m_hWnd, WDA_EXCLUDEFROMCAPTURE);
// do bitblt stuff
mabye you can use Magnification API, even Microsoft said The MagImageScalingCallback function is deprecated in Windows 7 and later, and should not be used in new applications. There is no alternate functionality., but it still work on Windows 10;
Here is the overview of this API : https://learn.microsoft.com/en-us/previous-versions/windows/desktop/magapi/magapi-intro
The sample code of Microsoft is here : https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Magnification
If you want to get the screenshot rgb data, you can use this api MagSetImageScalingCallback to set callback of Magnifier window, every time you use MagSetWindowSource or InvalidRect of magnifer window, this callback function MagImageScalingCallback will be called, so you can get screenshot rgb data here.
I think that to limit the capture content within a big window will be more simple. otherwise you will need to cut some windows from the screen capture.