How to capture a Direct3D/WPF/DWM window into a bitmap? - windows

The normal way to capture an image of a window is to call:
HDC SharedWndDC = GetWindowDC(SharedWnd);
BitBlt(BitmapDC, 0, 0, width, height, SharedWndDC, 0, 0, SRCCOPY /* |CAPTUREBLT */);
ReleaseDC(SharedWnd, SharedWndDC);
Which on a system running DWM, nicely grabs just the window in question, even if it's being overlapped by other windows or partly off the screen, or whatnot.
But, it doesn't work right on some windows (presumably those that use WPF), and does funny things with the glass areas. Basically, the GDI capture doesn't work on non-GDI things.
I understand what I really want is to grab the Direct3D "back buffer" or "front buffer", but the numerous examples I've seen for that are for capturing the entire screen or desktop. Not knowing Direct3D, I can't find an example or simple statement of how to obtain the proper object for an existing window, which I could then grab the buffers from.
Can some kind soul at least show that missing piece?
—John

I don't believe there is a way to capture the front buffer contents without capturing the whole screen, and the back buffer is private to the application running Direct3D. The front buffer's content can only be seen when the graphics card actually presents the data to the monitor. With GDI, Windows has access to the front and back buffers, because it is managing them itself. But with Direct3D (and I'm presuming WPF), this is no longer the case. Windows does not have access to the buffers, and as such, can't obtain the data unless the application gives it to Windows, which is impossible (there's no message defined to do so).
EDIT:
This looks like a dead question, but I'll add this anyway in case someone else comes along looking for answers. The aforementioned issues still apply to getting the window contents programmatically. However, it is possible in Windows to get the contents of a single window by utilizing the shortcut ALT+Print Screen when the window you want to capture has the keyboard focus (a.k.a. it's the active window). This may or may not help you, but it does capture the contents of just the window in question, even with DirectX stuff.

Related

What context should I refer to so I am able to BitBlt on top of any application or window? Is there a "general" context which refer to display?

So I`m very new to win32ui, basically just starting. I was once using BitBlt wit python win32api module and as far as I remember to draw on top of display (so any application - if they are opened) I had to get specific context handle. But my memory is hazy on whether it simply was NULL or was it some specific context? Null doesn't seem to work, so I wonder how to obtain that general context? I really want to avoid to create fully transparent un blocking window.
The GetDC API allows you to get a device context for any given window. Alternatively,
If [hWnd] is NULL, GetDC retrieves the DC for the entire screen.
You can use the device context for the entire screen to read from, reliably (with restrictions). Rendering into a device context for a window you do not own won't be reliable, though. While it won't fail straight away, the window owner can overwrite your rendering at any point. There's no way for you to even be notified about this.
If you need to render on top of the screen you will have to create a top-most (transparent) window yourself, and use its device context. Make sure you ask the question: What if two programs did this?

DirectX11 Swapchain and window losing fullscreen status

I just stumbled on this little annoying behavior, while adding full screen support on a sample program.
Creating a full screen window works, but as soon as I move any window (from another application) on the output that contains my fullscreen window, it automatically switches back to windowed.
Is there any way to prevent this behavior (so full screen window do not go back to windowed)?
As a reference, this is a small standalone example (so problem can be replicated easily).
Also if that is useful, I'm running on Windows 8.1.
I already tried to change WindowAssociationFlags and SwapChainFlags, both with no success, same as using FlipSequential instead of Discard
SharpDX.DXGI.Factory2 factory = new SharpDX.DXGI.Factory2();
SharpDX.DXGI.Adapter adapter = factory.GetAdapter(0);
var renderForm1 = new RenderForm("Form 1");
factory.MakeWindowAssociation(renderForm1.Handle, SharpDX.DXGI.WindowAssociationFlags.IgnoreAll);
Device device = new Device(adapter, DeviceCreationFlags.BgraSupport);
SharpDX.DXGI.SwapChainDescription sd = new SharpDX.DXGI.SwapChainDescription()
{
BufferCount = 2,
ModeDescription = new SharpDX.DXGI.ModeDescription(0, 0, new SharpDX.DXGI.Rational(50, 1), SharpDX.DXGI.Format.R8G8B8A8_UNorm),
IsWindowed = true,
OutputHandle = renderForm1.Handle,
SampleDescription = new SharpDX.DXGI.SampleDescription(1,0),
SwapEffect = SharpDX.DXGI.SwapEffect.Discard,
Usage = SharpDX.DXGI.Usage.RenderTargetOutput,
Flags = SharpDX.DXGI.SwapChainFlags.None
};
var swapChain1 = new SharpDX.DXGI.SwapChain(factory, device, sd);
renderForm1.Left = 1922; //Just hardcoded here to move window to second screen
renderForm1.Width = 1920;
renderForm1.Height = 1080;
renderForm1.FormBorderStyle = FormBorderStyle.None;
swapChain1.SetFullscreenState(true, null);
swapChain1.ResizeBuffers(2, 1920, 1080, SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.DXGI.SwapChainFlags.AllowModeSwitch);
var resource = Texture2D.FromSwapChain<Texture2D>(swapChain1, 0);
var renderView = new RenderTargetView(device, resource);
RenderLoop.Run(renderForm1, () =>
{
device.ImmediateContext.ClearRenderTargetView(renderView, new SharpDX.Color4(1, 0, 0, 1));
swapChain1.Present(1, SharpDX.DXGI.PresentFlags.None);
});
Edit:
I also tried a c++ sample (just taken DirectX11 basic tutorial from Microsoft and added full screen switch), this leads to the same behavior, so this is not a SharpDX specific issue.
I looked at the message loop, and once this occurs, first fullscreen mode is changed back to windowed, and I receive a WM_DISPLAYCHANGE message).
This sounds like expected behavior. If you have a full screen 'exclusive' mode swapchain and the associated window loses focus, the system automatically switches the application out of full screen mode back to windowed mode by design.
With a single monitor, it mostly works as long as you have your applications' window sized to fill the display. Users can't use the mouse to change focus of your window, and it requires something like ALT+TAB to switch focus.
With multiple monitors, it's a real problem. If you click on another window on another display, your app loses focus and the full screen mode is again switched out. There are also limitations that prevent you from setting full screen 'exclusive' mode on more than one monitor.
Furthermore, on Windows Vista or later the notion of 'exclusive' mode is an illusion: the GPU is always shared anyhow. The 'focus' application gets priority whether it is a full screen or a windowed swap chain.
For a Windows desktop apps you have three choices for a full screen style experience:
Use the traditional full screen 'exclusive' mode with a window sized to fill the display, along with setting the display mode which may not be what the user has set for Windows generally. Here you have IsWindowed = false.
You set the window size to fill the full display (i.e. maximized). You can use windows styles to ensure that the window has no frame which results in a full screen style experience (WS_POPUP). Here you have IsWindowed = true, and you should be sure to set DXGI_MWA_NO_ALT_ENTER to avoid allowing DXGI to try to take you to use the 1 case.
You can do the same as 2 with IsWindowed = true and the borderless window sized to match the screen, but you change the display mode to something other than the system default. This is commonly referred to as 'fake full screen'. The display mode gets changed back whenever you exit the application.
1 has all has all the problems with multi-tasking and focus we just described. 2 and 3 allow system notifications and other pop-ups to show up over the game and not force a mode switch. 2 and 3 also work a lot better in multi-monitor setups where you can play your game on one display and use other apps on another display. For multi-tasking most people to prefer a classic window style with a frame border.
Windows Store UWP notions of full screen mode is basically like 2 above. You can't change the display mode with a UWP.
Debugging a full-screen setup is quite challenging. With multiple monitors, 2 and 3 can work with your debugger on the other screen. For true full-screen exclusive mode, really the only option is to use remote debugging from another PC.
Another issue with 1 and 3 is that you can set the display mode to something that won't sync with the display leaving the user with a system with no UI and no way to exit. Ideally with the right driver setup, the DXGI enumeration list does not contain unsupported modes, but it is something to be aware of. For this reason, your UI for selecting a display mode should have a timeout and you should make sure there's a reasonable way to abort the application with the keyboard if the display mode fails to sync at some point in the future. Using the existing display mode as we do in 2 above is always the safest option.
The main reason to use full screen exclusive mode (1) above is to try to get 'flip' rather than 'blit' of the backbuffer/frontbuffer. For most modern systems, this is a negligible performance difference. The other reason to go through the pain of using it is for SLI/Crossfire multi-GPU rendering going to a single display. There are a number of other optimizations required to really make that scenario work, and it's pretty niche. You should seek out the vendor optimization guides for the details.
Most modern games default to using fake full screen rather than full screen 'exclusive' mode. They offer the ability to use a true windowed mode as many users want to be able to multi-task while playing (like looking up hints online, use IM or external voice chat, etc.). AAA Windows desktop games that want to support tuned high-performance gaming for SLI/Crossfire will offer a full screen 'exclusive' mode, but this requires some work to get working fully and entails more work than just some DXGI code.
See DXGI Overview and DirectX Graphics Infrastructure (DXGI): Best Practices
After several attempts and trials, here are the different workarounds I used, none are ideal but all are somehow better than getting a mode change.
1/Force cursor in the middle of the full screen window, with a keyboard shortcut to get control again.
This is not ideal since we can't really do anything while our part is running, but at least prevents accidental "disaster click". It does not prevent keyboard interaction either.
2/Use a DX9 renderer with a shared texture.
DX9 Swapchain can have it's parent window set to desktop, so it does not lose focus when moving to something else.
Having a focused window on top show little borders visible while moving it, but that is a bit more acceptable than losing everything.
Not future proof but guess will stay actual for a while.
3/Stay on Windows 7 and Disable DWM Service:
Doesn't work in Windows 8 anymore, but in my use case since most media companies I work for are still on Windows 7, it stays a valid solution for at least 5 to 10 years.
4/Force the DX11 Window on foreground
Basically continuously call SetForegroundWindow to avoid another window to take focus.
5/Prevent mode switch at presentation level.
Since on my application I got access to when presentation occurs, I use the following routine (before to call Present)
-Get Foreground window handle (using GetForegroundWindow), If Foreground handle is our fullscreen window, just call Present as usual.
If Foreground handle is not our fullscreen window, perform the following. Please note that visibility check is not needed, since even an invisible overlapping window will also cause a full screen loss! (seriously, this is just so bad...)
-Verify if our foreground window overlaps with the monitor:
Call GetWindowRect to get the bounds, and perform intersection with the monitor location.
Alternatively, call Present on the swapchain with the DXGI_PRESENT_TEST flag. If a window is overlapping, the Present call will return DXGI_STATUS_OCCLUDED
If a window overlaps, either Hide it or move it in another monitor (anywhere so it does not overlap):
ShowWindow and SetWindowPos are aperfect fit for this task.
Repeat that Test present call in a loop until it doesn't return the occluded status (this is important, since windows might not have processed the messages immediately); Once occluded flag is gone, call Present as usual.
There is a way to prevent DXGI from automatically leaving fullscreen mode when your process loses focus, though I must warn, it is a bit hackish.
Basically DXGI calls GetForegroundWindow() and checks if the returned window is yours.
If not, it switches off the fullscreen mode.
So if you hook/redirect this function to your own replacement, that always returns your window (regardless of whether it has the focus or not) - that will get the job done.
Here is a simple code that does that. It is for 64-bit mode and assumes that you NEVER need to call the real function, so it simply overwrites its start with a jump instruction to your replacement:
HWND WINAPI get_our_window()
{
return our_window;
}
void disable_automatic_leaving_fullscreen_on_lost_focus()
{
// get the address of GetForegroundWindow
char *p = (char *)GetProcAddress(GetModuleHandleA("user32.dll"), "GetForegroundWindow");
// make the function code writable
DWORD old;
VirtualProtect(p, 12, PAGE_EXECUTE_WRITECOPY, &old);
// overwrite the function start:
// mov rax, <address_of_GetOurWindow>
p[0] = 0x48, p[1] = 0xB8, *(void **)(p + 2) = (void *)get_our_window;
// jmp rax
p[10] = 0xFF, p[11] = 0xE0;
}
This code is only for demonstration.
If you need to retain the ability to call the true function, then you have to hook it in a different, more complicated way, but this is a separate subject

OpenGL on a secondary display

I have a Windows 7 system, a regular monitor as the primary display (serving as a desktop, etc.), and an additional screen attached to the same graphics card.
I want to write a program that takes control of the secondary display and uses it for fullscreen OpenGL rendering. I tried to enumerate displays with EnumDisplaySettings, pick the secondary display, create a device context associated with the display, set the pixel format on the DC, and create a WGL context associated with it. I can get this far without errors, but then the call to wglMakeCurrent fails for no apparent reason (return value is 0, GetLastError() is 0, and OpenGL does not function.)
The only way I could get it to work is to extend the desktop onto the secondary display (manually, from Windows display settings), create a window and move it onto the secondary display. Which is tolerable but undesirable (I don't want the secondary display to interfere with the desktop. For example, in this setup, I can move the mouse cursor from the desktop into the secondary display.) Is there a way to avoid this?
More generally, in order to get OpenGL to work on a display, do I need (1) to have the display attached to the desktop (or "a" desktop?), and/or (2) to have a window of my own on that display?
P.S. It seems that I might be able to get this to work with a third-party library such as glfw3, but I don't want extra baggage (I don't need 90% of functionality of glfw3) and I'd prefer to get this done directly through native API calls if possible.
Unfortunately the Windows graphics driver model does not allow to use displays independently. You will have to extend the desktop to the second display and create a fullscreen window on it. When it comes to constraining the mouse, the usual way is to hook into the system mouse events and whenever the mouse pointer is moved into the secondary screen remove it back to the primary screen.

Garbage image from xcomposite pixmap

I'm using XComposite extension to get contents of the windows running under a sort of window manager that I develop. I found that in many cases if I try to get contents of the window using pixmap created with XCompositeNameWindowPixmap shortly after it was mapped and redirected I get garbage image from the pixmap. Same call a little bit later gives perfectly valid image of the window.
My assumption is that it takes some time to initially populate the pixmap. Unfortunately, I'm not able to quantify how much more time I have to wait before I can get correct image.
Is there any way to determine if composited pixmap is ready to be used? Or is there anything else that might be causing this weird effect?
What you're experiencing is, that after redirecting a window the program has to redraw the window's contents, to those are not initially available right after redirection.
This is where the Damage extension enters the stage, which allows clients to inform other clients, that their window's contents have been updated.

Painting data from device context

I've just got a fresh device context (DC):
GetDC(someForeignHwnd)
Most normal people now want to paint on this. I don't. I want to display the context in my own program. Or duplicate, I wouldn't even mind the window I stole the context from beeing empty.
In my case, I want it in a TPanel in Delphi, but anything else helping me understanding goes.
Afterwards, I'll probably find the DC invalid by the time I get to display it.
My main problem is: Showing the content of another window in my own. But that isn't important. First of all, I want to know how these DC are of any use. Can I do something like the following?
Canvas.Draw(0, 0, MyNewDC);
The answer can be in Java, C, or Pascal. Is it just not possible or just a stupid idea?
While it's possible to use a device context that you retrieve via GetDC() as the SOURCE for BitBlt(), etc., you will likely not get the results that you're looking for. When you call GetDC() for a specific window, Windows essentially returns a device context for the screen, but with a clipping region set to exclude any portions of the screen where the window is not visible. For example, if there happens to be another window overlapping the source window, the portion of the source window that is covered is clipped from the device context. Therefore, you can only "retrieve" the bits that are actually visible.
You may have better luck sending a WM_PRINT or WM_PRINTCLIENT message to the window. However, not all windows respond to these messages, so this isn't a universal solution.

Resources