2

I want to take a screenshot of some specific window (e.g calculator).

Here is the code I have written according to this discussion:

// Get the window handle of calculator application.
HWND hWnd = ::FindWindow(0, _T("Calculator"));
RECT r;
GetWindowRect(hWnd, &r);
int x[2]; int y[2];
x[0] = r.top;  x[1] = r.bottom;
y[0] = r.left; y[1] = r.right;

HDC     hScreen = GetWindowDC(hWnd);
HDC     hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, y[1] - y[0], x[1] - x[0]);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BitBlt(hDC, 0, 0, y[1] - y[0], x[1] - x[0], hScreen, y[0], x[0], SRCCOPY);

Afterwards, I save the bitmap as a .bmp image.

The result has correct size and position of the calculator window but the resulting bmp is all black.

I tried to screenshot the full desktop and then cut the calculator part and that worked. But I want to be able to make a screenshot of the window even if it is minimized or covered by another window.

Any ideas why this code is not working or is there any other way to do it?

Thanks.

Community
  • 1
  • 1
mbaros
  • 825
  • 8
  • 31
  • What about some error checking code? – zett42 May 11 '17 at 22:52
  • _even if it is minimized or covered by another window_ -- won't work using `BitBlt()`. AFAIK this is reliably possible only using undocumented DWM API to access the shared surface of the window. – zett42 May 11 '17 at 22:57
  • 1
    "*even if it is minimized*" - minimized windows cant be captured this way. But you can restore the window temporarily (either offscreen, or with an alpha translucency of 1, so the user doesn't see it), capture it, and re-minimize it. – Remy Lebeau May 12 '17 at 00:21
  • 1
    Also, in Win7+, apps can opt-out of allowing their Windows to be captured, via [`SetWindowDisplayAfffinity()`](https://msdn.microsoft.com/en-us/library/windows/desktop/.aspx). – Remy Lebeau May 12 '17 at 00:28
  • 1
    @zett42: For minimized windows you can use the [DWM Thumbnail API](https://msdn.microsoft.com/en-us/library/windows/desktop/aa969541.aspx). – IInspectable May 12 '17 at 06:53
  • @IInspectable it doesn't allow one to capture into a bitmap, so not really usable for screenshots. [See this answer](http://stackoverflow.com/a/2946848/7571258). – zett42 May 12 '17 at 07:45
  • @zett42: The DWM Thumbnail API allows you to render a hidden/obscured/minimized window into a device context **you** control. Transforming pixel data under your control into any given file format is trivial. – IInspectable May 12 '17 at 08:49
  • @IInspectable Nope, it only allows you to pass a window handle as the target, not a device context. It doesn't even render into the device context of that window. – zett42 May 12 '17 at 10:00
  • @zett42: A window handle to a window **you** control. Retrieving a device context for a window you own is a safe and supported operation. – IInspectable May 12 '17 at 11:15
  • @IInspectable have you even looked at the answer I linked above? _the contents of the thumbnail are never blitted onto the DC of your window. Instead, the DWM composition engine will render the thumbnail directly over the contents of your window when the desktop is presented._ – zett42 May 12 '17 at 11:30
  • So, in general I should forget this option and concentrate on getting it through DWM? Actually, this option will also work for me: Can I check if the current window is the active one, and if yes then take the image, if not then do nothing? This way I don't have to care about when it is minimized or covered? The idea is to have a video capture of some window, so my main task is to not capture other window-s in the video – mbaros May 12 '17 at 12:17
  • @RemyLebeau can you hint how to do it? – mbaros May 12 '17 at 12:34
  • for some windows this code work well (how minimum for client area and DC) but for appcontainer windows this not worked. really your window is not in *calculator* but in another process - *ApplicationFrameHost.exe* and this is empty frame. (class *ApplicationFrameWindow*) even no caption. it childs already in *calculator* - `Windows.UI.Core.CoreWindow, Calculator` and `ApplicationFrameTitleBarWindow` but for this windows also not worked.. – RbMm May 12 '17 at 12:37
  • 1
    @mbaros: to capture a minimized window, I call `SystemParametersInfo(SPI_SETANIMATION)` to turn off window animations, enable `WS_EX_LAYERED` on the window if needed, apply an alpha of 1 to the window using `SetWindowLayeredAttributes()`, restore the window with `ShowWindow(SW_RESTORE)`, repaint the window with `InvalidateRect()` and `UpdateWindow()`, grab the window bitmap (`WM_PRINT`, `BitBlt()`, etc), minimize the window with `ShowWindow(SW_MINIMIZE)`, reset the window alpha to 255, remove `WS_EX_LAYERED` if needed, and enable window animations if needed. – Remy Lebeau May 12 '17 at 16:47
  • 1
    @zett42: That's true, I had overlooked that 'detail'. Still, another idea I haven't evaluated: Create a window with the `WS_EX_NOREDIRECTIONBITMAP` [extended window style](https://msdn.microsoft.com/en-us/library/windows/desktop/ff700543.aspx), and set up your own flip chain (as explained under [Windows with C++ : High-Performance Window Layering Using the Windows Composition Engine](https://msdn.microsoft.com/magazine/dn745861.aspx)). I'll see if I can look into this, if no one else does. – IInspectable May 13 '17 at 10:57
  • @IInspectable sounds interesting, thanks for that link. BTW, I played around a little bit with the DWM thumbnail API. You actually *can* `BitBlt()` from a thumbnail target window, but only if you use the screen device context (`GetDC(0)`) as the source. The drawback is that you may capture other stuff that is in front of your window. – zett42 May 13 '17 at 11:18
  • @RemyLebeau The method that you proposed can be used only for capturing a single frame. If you wan to capture a stream of frame the result is flickering between two windows and it is basically equivalent to restore the window, capture, and minimize the window. This is at least the behaviour on Windows 10. – Bemipefe Mar 29 '19 at 15:30

2 Answers2

1

A method for consideration is CreateForWindow.

Another angle, given the target window is movable, is relocating it to the top left corner of the current desktop. Perform a capture of the entire screen, and then, given you know the dimensions of the window, crop it to those.
This has a better chance of success provided security programs can be suspended or terminated, the target window isn't cloaked, or composited in the way as discussed in the above comments, or when Aero can be temporarily disabled.

Laurie Stearn
  • 959
  • 1
  • 13
  • 34
-1

The easiest way to do that is using PrintWindow.

Here's some examples :

  • If you want to draw the whole window (with the frame), just do it this way : PrintWindow(calculatorHwnd, destHwnd, 0);
  • If you only want to capture the client area of a window, here's the way to go : PrintWindow(calculatorHwnd, destHwnd, PW_CLIENTONLY);
winapiwrapper
  • 125
  • 1
  • 12