C# – Screen capture with Vista DWM (Shared Direct3D Surface)

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.

Vista DWM project download
Vista DWM project download

46 thoughts on “C# – Screen capture with Vista DWM (Shared Direct3D Surface)”

  1. 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?

    1. 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

      1. 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.

        1. 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?

          1. Some more info…

            I modified the call to CreateTexture so that it was getting (uint)Format.X8R8G8B8 instead of format = 0. It’s now failing at DataStream stream = Surface.ToStream(_systemMemorySurface, ImageFileFormat.Bmp, region); and all the debugging output shows is a failed assertion with no details.

          2. 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

          3. 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

          4. 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

          5. 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

  2. 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

    1. 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

      1. 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

      2. 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!

        1. 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

          1. 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.

  3. 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.

    1. 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:

      public class MainClass
      
          // Declare external functions.
          [DllImport("user32.dll")]
          private static extern IntPtr GetForegroundWindow();
      
          public static void Main() {
              // Obtain the handle of the current foreground window.
              IntPtr handle = GetForegroundWindow();
      
              Bitmap bitmap;
              bitmap = Direct3DCapture.CaptureWindow(handle);
      
              bitmap.Save(@"D:test2.jpg");
          }
      }
      

      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)

  4. 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?

  5. 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?

    1. 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

      1. Hey Justin,
        Any chance to get this full-screen D3D hooking example even if it is not cleaned up yet?

        Thanks in advance,
        Guy.

          1. 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

  6. 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?

    1. 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

  7. 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.

    1. 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

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.