When I was researching for this article, I noticed some passing references to the Vista DWM (Desktop Window Manager) and the ability to use it to capture images of windows — DWM is the technology behind Windows Aero and its window switching features like Flip3D. I filed this away as something to look into at a later date.
Update: 2010-03-29: An example of hooking the Direct3D API directly in C#: http://spazzarama.com/2010/03/29/screen-capture-with-direct3d-api-hooks/
Well I finally got back to it and found this great article by Jeremiah Morrill, including an example project for capturing Direct3D/WPF windows using the DWM and streaming it to video. The main advantage of using this approach over my previous article is that the Direct3D/WPF window being captured does not need to be visible – it can be behind another window, or even partially off screen – but not minimized.
The example project allows you to save a video of the window to WMV or stream it over the network. Note: although the video is not of the original resolution/quality, the actual bitmap captured is – and you can grab this instead if you need a full quality image/video.
My initial tests showed that this technique does not work for full-screen Direct3D applications. I would be interested to see if anyone else had any luck.
Update: April 11th 2009
I have now prepared a cut down C# sample project for capturing the contents of a WPF / Direct3D window to a Bitmap based on Jeremiah’s code.
Sample project attached: http://cid-da49be3d7591a40a.skydrive.live.com/embedrowdetail.aspx/.Public/Spazzarama/Source/VistaDWMScreenCaptureExample.zip
Getting an ArgumentNullException at Texture.FromPointer(…) right after the comment “Hack because SlimDX does not let you specify…”
Looks like devEx.CreateTexture isn’t setting the value of pTexture
I’ve tried against a few DX9 sample applications from the DirectX SDK as well as a couple others (WoW…)
Any Tips?
Hi Sean,
I would enable the DirectX debug information and see if something useful comes up in there. I agree that it seems that the following call isn’t working for you:
devEx.CreateTexture((int)size.Width, (int)size.Height, 1, 1, format, 0, out pTexture, ref pSharedHandle);
DirectX 9 Tutorials – Debug info contains some useful tips of setting up logging of DirectX debug information.
Also can you confirm that the hWnd is set correctly, and that the screen you are attempting to capture is not in fullscreen mode?
Cheers,
J
Tried this on another machine, as well as a brand new install of Windows 7 – No dice, sill get the error at the same place.
hWnd is set correctly, the screen definately is not in fullscreen mode. Composition is on.
I’m having the same problem on Windows 7 64-bit. Unfortunately, I can’t get any meaningful debugging output to expound on this problem.
Has anyone found a solution?
Some more info…
I modified the call to CreateTexture so that it was getting
(uint)Format.X8R8G8B8
instead offormat = 0
. It’s now failing atDataStream stream = Surface.ToStream(_systemMemorySurface, ImageFileFormat.Bmp, region);
and all the debugging output shows is a failed assertion with no details.Hi Travis,
I’m setting up a Windows 7 64-bit environment soon so will test it out and see how I go.
I have also been working on a Direct3D hooking example – which I have fully working (to capture full-screen direct3D), just need to tidy it up ready for posting. Time has been short however so this probably will take a little before I post the solution.
Cheers,
J
Hey Justin,
Any news on getting this to work on Win7? Have you been able to repro the CreateTexture() error on your environment? I am stuck with the same issue and very interested in solving it.
Thanks!
Jeff
Hi Jeff,
I haven’t got around to it yet – but my recent upgrade is going to force my hand soon.
I’ll update you on progress.
Cheers,
J
Btw Travis – did you try this with DWM composition disabled?
See here: http://spazzarama.wordpress.com/2009/09/30/enable-or-disable-dwm-composition-aero/
Cheers,
J
Hi Travis / Sean,
I have taken a look at the Windows 7 solution, and have determined that it is not working as a result of changes to the undocumented API’s in dwmapi.dll.
The number of parameters for the dll export at ordinal #100 has changed, if the index of the method didn’t change as well.
I have taken a look however my assembler skills have never been that great. Therefore I’m going to abandon supporting capturing via the DWM shared surface in Windows 7, and instead focus on the Direct3D hooking approach.
Cheers,
J
Any chance you know of a method that works with DirextX 11, Justin?
No idea at this time. If you give it a go I would be interested to know how you go.
Cheers,
J
Hello Justin,
do you have any idea why the capturing only works on WPF/Direct3d windows?
And is there any possibility to grab “native” windows to develop an application like Flip3d?
Greetings,
Michael
Hi Michael,
I’m not sure of all the mechanics of the Vista DWM, but the shared surface approach used here is specifically for capturing WPF/Direct3D based application (which both use a DirectX device I believe). You can use BitBlt to capture native windows reliably, or if you are using .NET 3.5 SP1 you can use the GDI32 equivalent (MS fixed a HDC leak in 3.5 SP1) – however both these methods will fail to capture hidden windows.
I should have examples of both lying around here which I will try to hunt down for you.
Cheers,
J
Oh, okay.
I thought every window is renderd to a DirectX surface when aero is enabled. For doing an own composing of the Windows I hoped to get access to these surfaces. I think bitblt will therefore not work for my tasks.
Thank you very much for your reply,
Greetings, Michael
Hello Justin,
Great code and article, thank you!
Do you have an idea or advice how to determine whether a window is D3D/WPF or “native” one? To choose which method of capturing to use: through Direct3D or through BitBlt?
I am working on creating an universal algorithm to capture any window, but am stuck at determining which approach to use for a particular window.
Thanks!
Hi Igor,
Thanks for your comment.
You could check if the process has one of the DirectX DLLs loaded (e.g. d3d9.dll) to determine if it is a D3D app, and possibly a similar approach might work for WPF.
I’m not sure how you could do it based upon the window handle (which is probably the ideal solution)
Cheers,
J
Hello Justin,
Thanks for your suggestion. Though an app may be using DirectX DLLs for some purposes and not using D3D/WPF windows. For instance: as I use your sample code, I link to d3d9.dll, but my GUI is “native”…
Anyway thanks.
Hey Justin,
I’m having a problem while using you’r class.
I made this code:
Bitmap bitmap;
IntPtr iptr = new IntPtr();
bitmap = Direct3DCapture.CaptureWindow(iptr);
bitmap.Save(“D://test2.jpg”);
And after I compile it (when the console window appear) it opens a “Send / Don’t Send” Message box,
and write an error in the console window:
Unhandled Exception: SlimDX.Direct3D9.Direct3D9Exception: D3DERR_INVALIDCALL: In
valid call (-2005530516)
at SlimDX.Result.Throw[T](Object dataKey, Object dataValue)
at SlimDX.Result.Record[T](Int32 hr, Boolean failed, Object dataKey, Object d
ataValue)
at SlimDX.Direct3D9.Device..ctor(Direct3D direct3D, Int32 adapter, DeviceType
deviceType, IntPtr controlHandle, CreateFlags createFlags, PresentParameters pr
esentParameters)
at PrintScreen.Direct3DCapture.CaptureRegionDirect3D(IntPtr handle, Rectangle
region) in C:Documents and SettingsEladMy DocumentsVisual Studio 2008Proje
ctsPrintScreenPrintScreenDirect3DCapture.cs:line 61
at PrintScreen.PrintScreen.Main(String[] args) in C:Documents and SettingsE
ladMy DocumentsVisual Studio 2008ProjectsPrintScreenPrintScreenProgram.cs:
line 15
Do you have any idea why?
Thanks in addition,
Elad.
Hi Elad,
I think that you are not passing in a valid handle to the window you want to capture.
You will need to do something more like the following:
Note: take a look at the other window finding methods in user32.dll for obtaining the handle (e.g. FindWindow: http://www.pinvoke.net/default.aspx/user32.FindWindow)
Hi,
I have used the sample program provided and it works fine in windowed mode. Does any one knows how to pass a pointer to desktop in windows VISTA as i wanted to capture the whole screen rather than a window?
Hi Abdul,
Here is an example of capturing the entire screen in Vista/Win7 using two methods: Win32 GDI and .NET Graphics.CopyFromScreen.
I have not tried either in regards to Direct3D applications however:
http://blog.bobcravens.com/2009/04/26/FastestScreenCaptureUsingCVISTAVsWIN7.aspx
Cheers,
J
Do this work with fullscreen direct3d applications?
My problem is that when I use win vista/7 with AA activated, I get a blank screen when capping with GDI. So I’m looking for a more generic way.
Want to get snapshots, so isn’t crucial like video capture.
Can anyone help me?
No this doesn’t work with full screen Direct3D applications. To do this you will need to perform Direct3D hooking: see http://nexe.gamedev.net/directKnowledge/ for some pointers.
I haven’t performed the hooking method yet – if you are willing to share your results I would be interested to see them.
Cheers,
J
I have a Direct3D hooking example working here now, for capturing fullscreen Direct3D applications – will be working on a post in the next few weeks/months when time permits after I have cleaned up the code a bit.
Cheers,
J
Hey Justin,
Any chance to get this full-screen D3D hooking example even if it is not cleaned up yet?
Thanks in advance,
Guy.
I’ll send it thru to ur email address later assuming it is correct
Can I get it too? Please.
That’s been sent. Sorry for the delay on getting the next post up – been a bit busy – i’ll get around to it I’m sure!
Cheers,
J
Justin,
thank you for your efforts.
Can you send it to me also?
Thanks and bye!
Stefano
Hi Stefano,
This is available in the following post:
http://spazzarama.wordpress.com/2010/03/29/screen-capture-with-direct3d-api-hooks/
Cheers,
J
hi Justin
i want to use this program in xp .but i have this exception :
Direct3D 9 was not found. Reinstalling DirectX may fix the problem.
can i use this in xp or its just for vista?
and i have to install directx sdk to run this program?
This particular example will only work on Vista.
Take a look at the DirectX 9 API hooking example on this blog for how to do it for DirectX 9 regardless of platform.
Cheers,
J
thx
but i cant use that.!!!
i want to capture from hidden forms.exactly like this program.
DirectX 9 API hooking example do this??!!
i realy need that.tnx alot.
Yes you certainly can do that using a DirectX 9 API hook. If it is a DirectX application that is.
Otherwise I’m not sure of a method that will work for you on XP.
Cheers,
J
tnx justin
and i dont know that i have to install directx sdk to run this program?
No you do not need the DirectX SDK for this particular sample.
It does only run on Windows Vista however.
Cheers,
J
I found MagSetImageScalingCallback() based dwm capture code.
http://mosax.sakura.ne.jp/fswiki.cgi?page=SCFH+DSF+Dev
Thanks for that, the Magnification API looks to be a great option for screen capture.
http://msdn.microsoft.com/en-us/library/ms692162(v=vs.85).aspx
Cheers,
J
Hey Justin,
I am window development engineer, recently i got a task to capture the font surface image of a hide window that runing on window 7 os, I got some acknowledge about dwm from msdn, but i am not full got learn it.
I want go ahead to learn it, form http://cid-da49be3d7591a40a.skydrive.live.com/embedrowdetail.aspx/.Public/Spazzarama/Source/VistaDWMScreenCaptureExample.zip,but i cannot to get the example package. can help me?
I don’t think I have that example any longer. It only works in Vista and isn’t really suitable. Take a look at the DWM thumbnail API perhaps.