I am using the code below to capture a screenshot of a window using bltblt. However the titlebar appears completely black in the captured screenshot. I am running the code on Windows 8.1. Is there a way i can correctly capture the title bar.
// Retrieve the handle to a display device context for the sourceWindow
hdcScreen = GetDC(ss);
// Retrieve the handle to a display device context for the dest window
hdcWindow = GetDC(hWnd);
//Get the client area for size calculation
RECT rcClient;
GetWindowRect(ss, &rcClient);
if (!BitBlt(hdcWindow,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcScreen,
0, 0,
SRCCOPY|CAPTUREBLT))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
EDIT:
The window i am displaying the screenshot in would cover the entire desktop and will be constantly updating the screenshot of the window just behind it. Also, the window displaying the screenshot will always be the topmost window.
The information you want is not all available from the window DC. Themes get painted over the top.
If you want an exactly visual representation you need to find the screen coordinates of the window (or part of it) and then blit from the screen DC.
If the window is not displayed, you may have an insurmountable problem. As far as I know the theme (since at least Windows Vista) is not part of the Window DC but is painted over the top using non-GDI techniques. The GDI simply does not have the capabilities to paint sophisticated blends and transparency effect. Until Windows 8 it was still possible to select the old classic themes but now they're gone. You may find that the title bar simply isn't painted in the NCPAINT handler any more.
Related
I am creating a dialog with a tab control. Each tab should show different set of controls, so I have created child dialog boxes in resource editor to behave like pages.
I have used instructions from this post to do this.
In resource editor I made dialog boxes without border, set their styles to Child, removed system menu, and I have set flags Control and Control Parent to true.
In my child dialog box procedures I have handled WM_INITDIALOG by adding EnableThemeDialgTexture(handleOfmyDialog, ETDT_ENABLETAB); and returning TRUE. No WM_ERASEBKGND, WM_PAINT or WM_CTLCOLORDLG have been overridden.
In main dialog box that contains tab control, I have created "child dialogs" with CreateDialog function, and have used MoveWindow to properly position them.
I did not use EndDialog to destroy "child dialogs" on IDCANCEL or WM_CLOSE, I think that they will get destroyed automatically.
I have used Visual Studio 2013 on Windows 8.1 to do all this.
There seems to be no problem on Windows 7 and Windows 8.1, but maybe my eyes are playing tricks with me, since the background color of the tab control is similar to the default background color of the dialog box. The problem is best seen on Windows XP, as shown on the picture below:
How can I make background color of "child dialogs" ( and their child controls like checkbox/trackbar/radio button/static control ) to be transparent ( match the background color of tab control )?
Thank you.
This is a pretty straight-forward problem. You can't see the mistake on later Windows version because they no longer use a gradient for the "texture". EnableThemeDialogTexture() worked just fine, your dialog certainly has the same texture as your tabcontrol. The brush origin starts at the upper-left corner of the dialog. Like it does for the tabcontrol. But the dialog is not positioned correctly, now the gradients are mis-aligned and the dialog no longer blends.
You need to move the dialog so it is located correctly inside the tab page area. The relevant line of code from the MSDN article:
// Size the dialog box.
SetWindowPos(hwndDlg, NULL,
0, 0, // <=== here!
rcTab.right + cyMargin + (2 * GetSystemMetrics(SM_CXDLGFRAME)),
rcTab.bottom + rcButton.bottom + (2 * cyMargin)
+ (2 * GetSystemMetrics(SM_CYDLGFRAME))
+ GetSystemMetrics(SM_CYCAPTION),
SWP_NOMOVE | SWP_NOZORDER);
Positioned at (0, 0) in the client area of the tabcontrol, now the gradients align.
Hans’ observation is right, but with the wrong conclusions.
Indeed, EnableThemeDialogTexture() worked: There clearly is a gradient on the background of the Slider control. And indeed it does not line up with the tab control’s background.
However, this is not an alignment problem. The gradient you see on the Slider control is the correct gradient according to EnableThemeDialogTexture(). The gradient on the background is actually the wrong one. You can clearly see it with enhanced contrast – the background gradient is blocky and coarse, while the Slider’s gradient is perfectly fine.
I observed this exact behavior when the main window had the WS_CLIPCHILDREN style set while the Z order was wrong (tab above child). Move the child dialog boxes to the top of the Z order via SetWindowPos(child, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE) and it should align perfectly and not be blocky any more.
I have developed an application to display jpeg images. It can display 4 images, one in each quadrant of the screen. It uses 4 windows for that. The windows have no border (frame) nor titlebar.
When a new image is loaded, the window size is adjusted for the new image and then the image is displayed.
Especially when the window is made larger, there is often a flicker. With my eyes to slits, it seems that the old contents is moved when resizing before the new contents is displayed.
I consulted many resources and used all tricks:
the window has only style CS_DBLCLKS (no CS_HREDRAW or CS_VREDRAW);
the background brush is NULL;
WM_ERASEBKGND returns 1;
WM_NCPAINT returns 0;
WM_NCCALCSIZE tells to align to the side not moved (can you tell it to discard the client area?);
WM_WINDOWPOSCHANGING returns 0;
SetWindowPos has flags SWP_NOCOPYBITS | SWP_DEFERERASE | SWP_NOREDRAW | SWP_NOSENDCHANGING.
Still, the flicker (or the contents move) occurrs when resizing the window. What I want is to:
SetWindowPos to new size and position;
InvalidateRect (hWnd, NULL, FALSE);
UpdateWindow(hWnd);
without any painting, background erasing or content moving until WM_PAINT.
WM_PAINT does:
hDC= BeginPaint (hWnd, &ps);
hMemDC= CreateCompatibleDC (hDC);
hOldBitmap = SelectObject (hMemDC, hNewBitmap);
BitBlt (hDC,...,hMemDC,0,0,SRCCOPY);
SelectObject (hMemDC, hOldBitmap);
DeleteDC (hMemDC);
EndPaint (hWnd, &ps);
Can anyone tell me if/where I make a mistake that causes the old content of the window to be moved?
Hardware etc: Windows 7 on HP Elitebook Core7 64 bits with NVIDIA Quadro K1000m driver 9.18.13.3265 (updated to 341.44).
UPDATE (Jul '17)
I have seen the behavior of the pogram also on another Windows computer (Windows 8/10). It does not seem to be the NVIDIA display driver.
The behavior is the most visible when resizing a window tiled to the centre of the screen (right bottom = w/2, h/2) to the left or left upper corner (0, 0).
I may have problems with the calculations for the WM_NCCALCSIZE message to tell Windows not to do anything. Could anyone give an example calculation for my purpose? See also How do I force windows NOT to redraw anything in my dialog when the user is resizing my dialog?
You have an impressive list of anti-flickering tricks :-)
I don't know if this is of importance (since it depends on how your tool windows are created and espacially if they are child windows to a common parent):
Try setting window style WS_CLIPCHILDREN in the parent window of the tool windows (if there is one).
If not set the parent window will erase it's (entire) background and then forward the paint messages to the child windows which will cause flickering. If WS_CLIPCHILDREN is set the parent window does nothing to the client area occupied by child windows. As a result the area of child windows isn't drawn twice and there is no chance for flickering.
This is a theory more than an answer:
By default, in modern Windows, your window is just a texture on the video card, and the desktop window manager is mapping that to a rectangle on the screen. You've seem to have done everything necessary to make sure that texture gets updated in one fell swoop.
But when you resize the window, perhaps the desktop compositor immediately updates its geometry, causing the (still unchanged) texture to be appear in the new position on the screen. It's only later, when you do the paint, that the texture is updated.
You can test this theory by temporarily turning off desktop compositing. On Windows 7, you navigate to System Properties, choose the Advanced tab, under Performance choose Settings..., on the Visual Effects tab, deselect the "Enable desktop composition" setting. Then try to reproduce the problem. If it goes away, then that supports (but doesn't absolutely prove) my theory.
(Remember to re-enable compositing, since that's how most of your users will be running most of the time.)
If the theory is true, it seems the goal is to get to the paint as soon possible after the window resize. If the window of time is small enough, then both could happen within a monitor refresh cycle and there would be no flicker.
Ironically, your efforts to eliminate flicker may be working against you here, since you've intentionally suppressed the invalidation and redraw that would normally result from SetWindowPos until you do it manually at a later step.
A debugging tip: Try introducing delays (e.g., Sleep(1000);) at key points in the process so you can see whether the resize and redraw are actually rendering on screen as two distinct steps.
You have indeed got a nice set of tricks.
First I can suggest a few variants on the existing tricks that might help especially on XP/Vista/7, and second I want to mention where the persistent flicker you see on Win8/10 is likely coming from and some tricks to reduce that.
First, despite advice to the contrary in other OP posts, you may find that if you set the CS_HREDRAW|CS_VREDRAW window styles that you avoided before, it actually eliminates the BitBlt that is done inside the internal SetWindowPos Windows does during window resizing (but---and this is the confusing part---on Windows 8/10 you will still see flicker from another source...more on that below).
If you don't want to include CS_HREDRAW|CS_VREDRAW, you can also intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing.
Or, you could add to your WM_NCCALCSIZE trick by having it return a set of values that will tell Windows to just BitBlt 1 pixel on top of itself so that even if the BitBlt does happen, it doesn't do anything visibly:
case WM_NCCALCSIZE:
{
RECT ocr; // old client rect
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new window rect
// np->rgrc[1] is old window rect
// np->rgrc[2] is old client rect
ocr = np->rgrc[2];
}
else
{
RECT *r = (RECT *)lParam;
// *r is window rect
}
// first give Windoze a crack at it
lRet = DefWindowProc(hWnd, uMsg, wParam, lParam);
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new client rect computed
// np->rgrc[1] is going to be dst blit rect
// np->rgrc[2] is going to be src blit rect
//
ncr = np->rgrc[0];
RECT &dst = np->rgrc[1];
RECT &src = np->rgrc[2];
// FYI DefWindowProc gives us new client rect that
// - in y
// - shares bottom edge if user dragging top border
// - shares top edge if user dragging bottom border
// - in x
// - shares left edge if user dragging right border
// - shares right edge if user dragging left border
//
src = ocr;
dst = ncr;
// - so src and dst may have different size
// - ms dox are totally unclear about what this means
// https://learn.microsoft.com/en-us/windows/desktop/
// winmsg/wm-nccalcsize
// https://learn.microsoft.com/en-us/windows/desktop/
// api/winuser/ns-winuser-tagnccalcsize_params
// - they just say "src is clipped by dst"
// - they don't say how src and dst align for blt
// - resolve ambiguity
// essentially disable BitBlt by making src/dst same
// make it 1 px to avoid waste in case Windoze still does it
dst.right = dst.left + 1;
dst.bottom = dst.top + 1;
src.right = dst.left + 1;
src.bottom = dst.top + 1;
lRet = WVR_VALIDRECTS;
}
else // wParam == 0: Windoze wants us to map a single rect w->c
{
RECT *r = (RECT *)lParam;
// *r is client rect
}
return lRet;
So that's all very nice, but why does Windows 8/10 look so bad?
Apps under Windows 8/10 Aero don't draw directly to the screen, but rather draw to offscreen buffers that are then composited by the evil DWM.exe window manager. DWM actually adds another layer of BitBlt-type behavior on top of the existing legacy XP/Vista/7 BitBlt behavior.
And the DWM blit behavior is even more crazy because they don't just copy the client area, but they actually replicate pixels at the edges of your old client area to make the new one. Unfortunately, making DWM not do its blit is much harder than just passing some extra flags.
I don't have a 100% solution, but I hope the above info will help, and please see this Q&A for a timing trick you can use to reduce the chances of the DWM-layer blit happening to your app:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Just in addition to your list of tricks. I have the same problem with flicker on my Dell XPS notebook while resizing window using left/top edge. I've tried all the tricks you mentioned. As far as I understand window border is drawn in GPU and the window content is prepared in GDI subsystem and transferred to video memory for window composition (DWM introduced in Windows 8.1). I tried to remove GDI rendering completely (setting WS_EX_NOREDIRECTIONBITMAP style) which makes window without any content, and then create content surface directly using DXGI subsystem (using CreateSwapChainForComposition, there are few examples on how to do this). But the problem remains. There is still a lag between rendering a window frame and resizing/displaying content surface.
However it may solve your problem, as you don't have the border and you will not notice this lag. But you will have full control over window repaint and it will be made on GPU side.
I am working on a DX11 game, and I want to clip the cursor during fullscreen mode to the fullscreen window. I use this method
void MyClass::_SetupCursor( BOOL bFullscreen ) {
// Clip cursor if requested
if( bFullscreen ) {
if(m_bShowCursorWhenFullscreen) {
ShowCursor(m_bShowCursorWhenFullscreen);
}
if(m_bClipCursorWhenFullscreen) {
// Confine cursor to full screen window
RECT windowRect;
GetWindowRect( m_hWnd, &windowRect );
ClipCursor( &windowRect );
}
}
else {
ShowCursor( TRUE );
ClipCursor( NULL );
}
}
However, when I am in fullscreen mode with 2 monitors, I can still move the mouse over to the other monitor. With resolution set to 2048x1152 in fullscreen mode, I get the window rectangle as 1360x768, and that is what it gets clipped to. I confirm that it is clipped using GetClippedRect.
So I have two questions:
1) Why isn't the mouse getting clipped to the monitor my window is in?
2) Why is the window rectangle measured as 1360x768 when I know for a fact the monitor is 2048x1152, and I have the resolution set to 2048x1152?
It turns out that for ClipCursor to work, you must have all your DX11 buffers and your window size correct. I found this out by running my application in fullscreen first, without toggling into it, and ClipCursor worked just fine, even with multiple monitors. For more information on when ClipCursor will fail, check out my other question on stackoverflow: Why is D3D10SDKLayers.dll loaded during my DX11 game? .
ClipCursor will fail ever time the situations i describe in that question arise. Also, in response to my 2nd question, the window size is incorrect because of the situation I describe in the linked question.
Unfortunately according to a comment on the documentation (by a user) it appears that this does not work for multimonitor setups. You may want to develop a method that will reposition the mouse when it goes off screen, turns off the rendring for it, then turn it back on when you move the cursor back to the window (to detect if the mouse moves off the window or not, there is windows messages for that).
I have a GUI application that is using GStreamer to capture video from capture cards, and then play the video. The audio and video streams are sent to GStreamer, and GStreamer automatically opens its own window to play the video. Once the video window is open, I need to take the video window and remove the border and set the window size and position and make my GUI window the parent of that window so that it will be "anchored" to my GUI window.
Since I know the name of the video window I am using FindWindow() to get an HWND handle to the window. I am then passing that HWND to SetWindowPos() as follows SetWindowPos(VideoWindow, GUIWindow, GUIWindowLeft, GUIWindowTop, 640, 360, SWP_SHOWWINDOW). Then I set the parent of the video window SetParent(VideoWindow, GUIWindow).
When I start my application, for a very brief moment it looks like my window is being resized and placed correctly but then the window returns to its default position (almost like it is just neglecting that SetWindowPos() was even called). Is there an obvious reason for why this happens? I am new to window manipulation so it is very possible I am making a simple mistake, but it does not make since why my window would be positioned correctly for a very brief moment but then move back to default position.
This is because the SWP_SHOWWINDOW or SWP_HIDEWINDOW is set, the window won't be moved or resized (see SetWindowPos documentation). Seems a little strange. Try using a different flag.
From the docs:
If the SWP_SHOWWINDOW or SWP_HIDEWINDOW flag is set, the window cannot be moved or sized.
I've got an application window in which I'm adding the WS_THICKFRAME style and I have removed the WS_CAPTION style. When the window maximizes, I want to hide the WS_THICKFRAME, but retain the Aero-Snap feature, so I have altered my handler for WS_NCCALCSIZE to return an inflated rect with respect to the size of the window borders.
That is, the important part of the WS_NCCLIENTSIZE handler code looks like this:
...
CRect rc( lpncsp->rgrc[0] );
if (IsZoomed())
{
int borderSize = GetSystemMetrics(SM_CYSIZEFRAME);
rc.InflateRect(borderSize,topOff+borderSize,borderSize,borderSize);
}
else
rc.InflateRect(0,topOff+0,0,0);
lpncsp->rgrc[0] = rc;
...
That code effectively makes the WS_THICKFRAME hidden.
Only problem is that when the window loses focus or regains focus (while maximized) the WS_THICKFRAME gets drawn within the boundary. Is there a message in which I can return the Inflated rect back or at least re-adjust the window size to hide the WS_THICKFRAME again when the window focus is set/unset?
Yeah, that won't work. Implement a message handler for WM_GETMINMAXINFO to allow the borders of the window to fall off the screen. Beware that if you didn't set the linker's /SUBSYSTEM option to say that your program is made for Vista or Win7 (version 6,0) then Aero will lie to you when you use GetWindowRect(). The value you get back is based on thin (legacy) borders.