0

I try to grab specific windows on Windows 10. I read some articles from MSDN to get familiar with APIs. My goal is to grab some certain windows, even if there are some windows on top of them (equivalent to OS X CGWindowList API). So if there are 2 windows: A and B, and windows B partially overlaps window A, I would like to be able to capture window A content, without capturing window B that partially covers window it.

According to this link, there are 5 different ways to capture the screen, if I understood them correct, most of them can capture only some regions on the screen, i.e. they don't distinguish between windows. The only API which allows to grab specific windows is "old standby, GDI".

I tried to acquire windows' device contexts using GetWindowDC() function, create a compatible bitmap and then use bit block transfer (BitBlt()). However, it seems that it does not always work as expected.

I've noticed several problems on Windows 10 (did not test on other operating systems):

  1. Window's title bar usually is not captured. I tried to open Notepad and capture the window, but it was not fully captured, part of the scroll bar was not captured as well as a title bar. I tried to capture child windows of Notepad, but it did not work as expected, moreover some child windows are seem to have coordinates which seem to be wrong (the msctls_statusbar32msctls_statusbar32 child window of Notepad had the width which was 3 times bigger than the actual width of the window).
  2. Some apps are not captured at all. For instance applications like "Photos", "Calc", "Settings" are not captured with that approach, when I try to capture them I get a black bitmap. There should be an API which allows capturing such windows, for instance TeamViewer is able to capture those Windows. It seems that all such windows are rendered by ApplicationFrameHost.exe process.

Does anyone know how to solve those issues?

Daniel
  • 635
  • 1
  • 5
  • 22
  • What would you like to have where B covers A? The content of A? AFAIK (hope I'm not wrong) when the content is dynamic, it's not getting updated, in other words if you have something like a timer which is covered by another window, the time "displayed" in the back is not automatically updated (Windows detects that the area is not visible and doesn't update it). – CristiFati Jul 26 '16 at 12:01
  • If capturing Notepad fails, you are using the wrong coordinates (either in your call to `BitBlt` or when creating the required offscreen bitmap). You cannot capture *Modern UI* applications. There is no meaningful device context associated with the host window. Those applications render directly to DirectX surfaces. I don't know how to solve this issue (unless you have infinite amounts of time at your disposal). – IInspectable Jul 26 '16 at 12:32
  • @CristiFati, it depends. I tried to capture `cmd.exe` window and I was able to see window updates during capturing, even though some other window was placed on top of it. – Daniel Jul 26 '16 at 12:51
  • @andlabs, is it better than using DC and `BitBlt()`? The thing is that `PrintWindow()` works but only on such applications as Notepad, it does not capture "Photo", "Calc", "Settings" apps. – Daniel Jul 26 '16 at 12:52
  • @IInspectable, do I have to capture child windows when I perform window capturing? I am able to grab Notepad window, however it does not capture everything, i.e. I can see "File" menus, I can see the content of the window, but the scrollbar and the titlebar of the window are not captured unforunately. As for Modern UI application, I also thought that there is no API for that, however TeamViewer application somehow captures them. – Daniel Jul 26 '16 at 12:54
  • There is a long history of people asking this question that date back to at least 2011. The simple answer is, no, in all that time no one has solved the issues in a general way. The windows drawing model simply does not support the arbitrary capture of window contents that are not visible, because how those contents are being stored off screen is entirely up to the application and the framework it is using. – Chris Becke Jul 26 '16 at 13:02
  • TeamViewer (or Citrix Delivery Agent for example) grab the visible screen. That's a very different scenario from capturing invisible portions. `GetWindowDC` returns a device context for the entire window (including child windows, borders, and the window caption). If you cannot capture the entire window, there is something wrong with your code. As for capturing *Modern UI* windows, I guess that [DXGI](https://msdn.microsoft.com/en-us/library/windows/desktop/hh404534.aspx) allows you to get at the information, even if not a documented scenario. – IInspectable Jul 26 '16 at 13:11
  • @IInspectable, I've just tried the latest TeamViewer, I tried to capture Modern UI windows, which were placed behind other windows and TeamViewer was able to capture them, there was an animation running on those windows and it was also visible to me (so I could see how the window is updated), so I think they can grab them somehow. As for DXGI, thanks for the link, do you know any examples / samples which show how to use that API for capturing? Seems like it's not documented (at least I have not found anything). – Daniel Jul 26 '16 at 13:40
  • @IInspectable, "GetWindowDC returns a device context for the entire window (including child windows, borders, and the window caption). If you cannot capture the entire window, there is something wrong with your code." -- For some reason it does not capture window borders and the window title, if I use `PrintWindow()` instead of `BitBlt()`, it wors, but `BitBlt()` does not capture the title bar for some reason. My pipeline is: `GetWindowDC` -> `GetWindowRect` -> `CreateCompatibleDC` -> `CreateCompatibleBitmap` -> `BitBlt` -> `GetDIBits`. – Daniel Aug 02 '16 at 09:43
  • @IInspectable, and also for some reason, my image is a little bit scaled, so the bitmap height and width is equal to the window geometry, but the "content" of that bitmap is a little bit scaled, because of that I cannot see the bottom and the right parts of the captured window. – Daniel Aug 02 '16 at 09:47
  • @IInspectable, another one strange thing is that sometimes it captures windows which are placed behind that window. So for instance when I tried to capture the terminal window, instead of capturing the title bar, it captured part of the window which was placed behind it. – Daniel Aug 02 '16 at 10:24
  • 1
    Sounds like you are ignoring DPI settings (see [Writing DPI-Aware Desktop and Win32 Applications](https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266.aspx)). Also have a look at [this Q&A](http://stackoverflow.com/q/30323426/1889329). – IInspectable Aug 02 '16 at 10:25
  • @IInspectable, thanks for the hint, you were right. I changed the function I used to retrieve the window's geometry though (from `GetWindowRect()` to `DwmGetWindowAttribute()`, I request `DWMA_EXTENDED_FRAME_BOUNDS`), and now it works even if I run it on a different DPI settings. Is it a safe approach? Should I do any additional computation based on the current DPI values? I've noticed from from documentation that their approach is to resize the UI elements (and fonts) according to DPI settings every time they receive an event from OS that the DPI has changed. – Daniel Sep 02 '16 at 08:09

0 Answers0