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

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.

Related

How to render child window with Direct2D in native desktop Windows application?

I have a desktop application where all windows (HWND) render itself with Direct2D 1.1. My question is how to do it more correctly?
Should each window has its own Direct2D device context derived from one Direct2D device? In this case, I cannot render transparent content on a child window without additional tricks (I have to change target on parent window’s context, render parent window to Direct2D bitmap and then draw this bitmap on child’s target).
May be it is better to have one Direct2D device context where all windows render itself? I believe DirectComposition works in a similar way. Unfortunately, I cannot use it because I target Windows 7.
You're asking a question whose answer will be very application specific. I recommend avoiding the whole problem of trying to get HWNDs to render with transparency amongst each other, especially if you're throwing Direct2D into the mix. There is just too much pain in that direction. Every version of Windows that you support will have different bugs that you'll be constantly bumping into and grasping at workarounds for.
Case in point: For the v4.0 release of Paint.NET, I converted all text rendering to DirectWrite, and almost all UI controls to use Direct2D. The image thumbnail control at the top of the window (the MDI selector) is using Direct2D for rendering but it also has to compose on top of what's behind it. And it has to play nice with glass on Win7 (it looks great though!). The code for this is awful, tricky, barely maintainable, and it seems to bump into a different rendering bug on every release of Windows: 7, 7 SP1, 8, 8.1, and 10 all behave slightly differently! It's really annoying to test, too; it's the only reason I have to set up and maintain VMs for every version of Windows that I support (other than the installer and updater). Windows 7 worked fine, then 7 SP1 added a bug which required some tuning to how I filled the alpha channel. Windows 8 has flickering when you resize the window unless I do a certain hack, but 8.1 works fine. 10 then has its own flickering bug if software rendering is used. Remote Desktop breaks things in its own way. Then you also have to worry about High Contrast, and whether DWM is enabled/disabled if you're supporting Windows 7. They all behave differently and it's really really painful.
Anyway. What you seem to really need is a UI system like WPF or XAML which doesn't use anything other than a top-level HWND container. At that point you're custom rendering everything and doing your own hit-testing and input routing (and accessibility and all sorts of other things), so it's not a small task.
Regarding the "how to do it more correctly" question and cardinality of device and device context: Have you thought about just using ID2D1Factory::CreateHWNDRenderTarget or ID2D1Factory::CreateDCRenderTarget ? They return ID2D1RenderTarget but you can call QueryInterface to cast them to ID2D1DeviceContext (this fact is missing from the docs but is also clearly intentional). This should simplify working with Direct2D and HWNDs quite a bit. This is what I do in Paint.NET: I still use an HWND for each control, but each control is using its own HWND or DC render target. If you're willing to poke around with Reflector or ILSpy, checkout Direct2DControl and Direct2DControlHandler in the Paint.NET DLLs.
Also, be careful about using more than 1 hardware accelerated HWND render target. You don't want to get into a weird area where every Direct2D-based UI control is waiting on VSync. Using D2D1_PRESENT_OPTIONS_IMMEDIATELY when creating the HWND render target should help. DWM already handles VSync, so you should be fine to tell Direct2D to ignore it unless you're doing some rather specific stuff with animations and timers.

How to change window style/theme in c++

I need an approach how to customize the window style like Steam or Spotify. I don't want to have the boring Windows (or any other OS) look.
Can that be done with Qt or the WinApi? Do I need DirectX or OpenGL for doing that?
Best Regards
Yes, you can do it with the WinAPI. The primary message you care about is WM_NCPAINT. That's what's sent when the non-client areas of a window (e.g., the frame and title bar) need to be painted.
Note that by default, rendering of the non-client areas is handled by the desktop window manager (DWM). If you're going to handle your own drawing, you usually want to explicitly enable/disable that with DwmSetWindowAttribute. Otherwise, you can get strange symptoms (e.g., if try to switch back to "stock" frames and such, it may not "notice" and start drawing your frame again).
Microsoft has an article on MSDN with more details on doing this in conjunction with DWM.

Draw OpenGL on the windows desktop without a window

I've seen things like this and I was wondering if this was possible, say I run my application
and it will show the render on whatever is below it.
So basically, rendering on the screen without a window.
Possible or a lie?
Note: Want to do this on windows and in c++.
It is possible to use your application to draw on other application's windows. Once you have found the window you want, you have it's HWND, you can then use it just like it was your own window for the purposes of drawing. But since that window doesn't know you have done this, it will probably mess up whatever you have drawn on it when it tries to redraw itself.
There are some very complicated ways of getting around this, some of them involve using windows "hooks" to intercept drawing messages to that window so you know when it has redrawn so that you can do your redrawing as well.
Another option is to use clipping regions on a window. This can allow you to give your window an unusual shape, and have everything behind it still look correct.
There are also ways to take over drawing of the desktop background window, and you can actually run an application that draws animations and stuff on the desktop background (while the desktop is still usable). At least, this was possible up through XP, not sure if it has changed in Vista/Win7.
Unfortunately, all of these options are too very complex to go in depth without more information on what you are trying to do.
You can use GetDesktopWindow(), to get the HWND of the desktop. But as a previous answer says (SoapBox), be careful, you may mess up the desktop because the OS expects that it owns it.
I wrote an open source project a few years ago to achieve this on the desktop background. It's called Uberdash. If you follow the window hierarchy, the desktop is just a window in a sort of "background" container. Then there is a main container and a front container. The front container is how windows become full screen or "always on top." You may be able to use Aero composition to render a window with alpha in the front container, but you will need to pass events on to the lower windows. It won't be pretty.
Also, there's a technology in some video cards called overlays/underlays. You used to be able to render directly to an overlay. Your GPU would apply it directly, with no interference to main memory. So even if you took a screen capture, your overlay/underlay would not show up in the screen cap. Unfortunately MS banned that technology in Vista...

How to draw text on the desktop in Windows?

How Would I go about placing text on the windows desktop? I've been told that GetDesktopWindow() is what I need but I need an example.
I'm assuming your ultimate goal is displaying some sort of status information on the desktop.
You will have to do either:
Inject a DLL into Explorer's process and subclass the desktop window (the SysListView32 at the bottom of the Progman window's hierarchy) to paint your text directly onto it.
Create a nonactivatable window whose background is painted using PaintDesktop and paint your text on it.
First solution is the most intrusive, and quite hard to code, so I would not recommend it.
Second solution allows the most flexibility. No "undocumented" or reliance on a specific implementation of Explorer, or even of just having Explorer as a shell.
In order to prevent a window from being brought to the top when clicked, you can use the extended window style WS_EX_NOACTIVATE on Windows 2000 and up. On downlevel systems, you can handle the WM_MOUSEACTIVATE message and return MA_NOACTIVATE.
You can get away with the PaintDesktop call if you need true transparency by using layered windows, but the concept stays the same. I wrote another answer detailing how to properly do layered windows with alpha using GDI+.
Why not just draw the text in the desktop wallpaper image file?
This solution would be feasible if you don't have to update the information too often and if you have a wallpaper image.
One can easily use CImage class to load the wallpaper image, CImage::GetDC() to obtain a device context to draw into, then save the new image, and finally update the desktop wallpaper to the new image.
i haven't tried but i assume you could do the following:
use GetDesktopWindow to retrieve the handle of the desktop window
use SetWindowLong to point the windows message handler to your own procedure
in your proc, process the WM_PAINT message (or whatever) and draw what you need.
in your proc, call the original message handler (as returned by SetWindowLong).
not 100% sure it will work, but seems like it should as this is the normal way to subclass a window.
-don
If your intent is to produce something like the Sidebar, you probably just want to create one or more layered windows. That will also allow you to process mouse clicks and other normal sources of input, and if you supply the alpha channel information, Windows will make sure that your window is drawn properly at all times. If you don't want the window to be interactive, use appropriate styles (such as WS_EX_NOACTIVATE) like Koro suggests.

Simulate fullscreen

I've seen an application that simulates a fullscreen application by removing the title bar and the window borders. I've done some research and found getWindowLongPtr() for that.
Now my question: How can I find and identify the application and get the appropriate window handle? How can I distinguish multiple instances of the application (running from different locations on disc)?
Just to make "simulate" more precise. If you make an application go fullscreen and you click on a different monitor, it minimizes itself. If the application runs in a window and you click on a different monitor, the window is not changed. If you remove the borders of the window and position it on the left or right monitor, you can still work with the other monitor without minimizing the application. Still it looks like the application running fullscreen on one of the monitors.
As an example: you can set Eve (www.eveonline.com) to fullscreen and windowed mode. In fullscreenmode you can not click on a second monitor without Eve minimizing itself. In window mode you can. There are tools like evemover that allow you to setup your window on one monitor, looking like fullscreen, but being in window mode. That's what I want to archieve. Evemover actually provides some of it's source code, that's why I know that removing the border and setting the position is done using the Win32-API with setWindowLongPtr and setWindowPos.
Many applications use divergent and confusing applications of the phrase "fullscreen".
A fullscreen application simply - occupies the full screen area.
DirectX applications can request a fullscreen exclusive mode. The advantage of this mode to DirectX applications is, with exclusive access to the (full) screen they are then allowed to change the resolution, bit depth etc, as well as gain access to vertical sync synchronized hardware buffering where the screen surface is 'flipped' between display intervals so that 'tearing' does not occur.
Anyway, the windows desktop understands 'fullscreen windows' - windows that occupy the full area of a monitor and have no non client elements. When windows like this are created, things like desktop gadgets and task bars automatically hide themselves. Modern games have come to call this mode 'fullscreen windowed'.
Back to your question: 'FindWindow' is the API used to discover other applications windows. Getting the path to the application that created the window is much harder. GetWindowThreadProcessId can get you the process id of the owning process. OpenProcess will get you a handle that you can pass to QueryFullProcessImageName (implemented on Vista and above) to get the full path to the process.
I think you are refering to applications like window aggregators, that 'plug in' to the system and act from outside the application.
Look at the code for the freeware app PuttyCM (for aggregating Putty (SSH) shell windows as tabs). IIRC, it ensures that the Window pointer passed to the application has the flags already set.
On applications running from different places, you will probably need some way of identifying it - registry entries / install log etc.