C# – Screen capture with Direct3D 9 API Hooks

Since investigating screen capture techniques for Direct3D 9 applications a year ago I have wanted to look into hooking the Direct3D 9 API to utilise the much faster GetBackBuffer for my screen captures. Well here it is at last – a mostly managed C# solution providing easy and safe hooking of the Direct3D 9 API thanks to EasyHook, supporting both 32-bit and 64-bit.

Update: 14th March 2011 – here is a new version that supports Direct3D 10 and 11: http://spazzarama.com/2011/03/14/c-screen-capture-and-overlays-for-direct3d-9-10-and-11-using-api-hooks/

Update: 16th April 2010 – updated code to handle fullscreen applications in a more stable manner (issue was around user inputs and Window’s getting confused about which application/window should be in the foreground). Also fixed bug where the width + height of the region to capture were needlessly being adjusted for the x1,y1,x2,y2 format of RECT – SlimDX now marshal’s the Rectangle to a RECT with this conversion for us.

Skip to download.

Overview

I have tried two approaches to hooking the Direct3D Device: “Hooking and Interface Wrapping” and “Hooking VTable function addresses”. My final implementation uses the later as it is the simpler and more effective approach for my needs.

Both approaches use “DLL injection” to directly access the address space of the target application:

DLL injection is a technique used to run code within the address space of another process by forcing it to load a dynamic-link library. (http://en.wikipedia.org/wiki/DLL_injection)

Both approaches also use function hooking, or API hooks (often referred to as a detour after the Microsoft Detours library http://research.microsoft.com/en-us/projects/detours/):

Function hooking is implemented by changing the very first few code instructions of the target function to jump to an injected [piece of] code. (http://en.wikipedia.org/wiki/Hooking)

Hooking and Interface Wrapping

This approach involves first hooking the “Direct3DCreate9” method within d3d9.dll so that we can return our own custom IDirect3D9 instance wrapping the original IDirect3D9 instance within an “Interface Wrapper” class. This allows us to intercept the call to IDirect3D9.CreateDevice that creates the IDirect3DDevice9 and in turn wrap the IDirect3DDevice9 within another “Interface Wrapper” thus providing interception for all the interface members.

An Interface Wrapper works like so:

The wrapper class implements an interface and takes an object implementing this interface as a constructor parameter. Each implemented method of the wrapper then calls the corresponding method of this original object allowing additional logic to be included before and after the original object is called.

This allows you to easily intercept and respond to each of the methods exposed by the interface. However there is a lot of extra code that needs to be created as you have to fully duplicate the original interface and in every method call the original wrapped object.

To work effectively with Direct3D this approach also requires that the host application be running and hook the “Direct3DCreate9” within the target application before the target application has finished initialising its usage of Direct3D (Direct3DCreate9 is called right at the start of any Direct3D9 application, and usually only once). This introduces potential timing issues, and does not allow you to hook a target application that is already running (a minor inconvenience really).

I stumbled across this approach to “hooking” the Direct3D API thanks to the “Direct3D StarterKit v3.0” at forums over at http://www.gamedeception.net/. There is plenty of other interesting information for dealing with Direct3D there.

Hooking VTable function addresses

This approach involves using the Virtual Method Table (VTable) of an IDirect3DDevice9 instance to determine the address of each of its virtual methods so that we are then able to install a detour around any of those that we wish to hook.

VTable is a mechanism used in a programming language to support dynamic dispatch (or run-time method binding). This supports the inheritance hierarchy whereby a virtual function points to different implementations for each sub-class (http://en.wikipedia.org/wiki/Virtual_method_table).

To access the VTable requires that we first create a temporary IDirect3DDevice9 instance within the injected code.  It is then simply a matter of obtaining a pointer to the VTable and iterating through to retrieve the method pointers.

To retrieve each of the function addresses requires that you know the order of the functions in the class (see d3d9.h in the DirectX SDK) and their parameters. Because they are functions of an object, each has an additional parameter representing “this” that you need to take into consideration when hooking.

The pros for this method are that the target application does not have to be started after the host application; the hooks can be installed at any time – and more importantly only a very small amount of native C/C++ code is required (to do the VTable address lookups), allowing the majority to be implemented in C# (It’s probably possible to do the vtable lookup directly in C# but I haven’t tried).

A con of this approach is that you have to create a temporary Direct3D device to be able to get a pointer to the VTable, which may or may not impact upon the target application (I have yet to experience any ill effects).

The provided solution here was implemented using this method after having already completed an example using the first approach. When preparing the code for this post I decided it was far too complicated and pursued the VTable route.

Stuff you need first

Some things you’ll need:

  1. Microsoft Visual Studio 2008: C++ and C# environment (install the optional 64-bit C++ compiler if required)
  2. Microsoft DirectX SDK (February 2010): headers, type libraries, examples for all DirectX versions. This is required to build the C++ helper DLL.
    http://msdn.microsoft.com/en-us/directx/aa937788.aspx
  3. EasyHook (2.6 Stable): EasyHook is an open source library released under the LGPL that supports extending (hooking) unmanaged code (APIs) with pure managed ones, from within a fully managed environment like C# using Windows 2000 SP4 and later (including 64-bit XP/Vista/Win7/Server2008).
    http://easyhook.codeplex.com/
  4. SlimDX (February 2010): SlimDX is an open source managed framework for the DirectX API supporting all manner of DirectX both 32 and 64-bit. See http://slimdx.org/features.php for the complete list. Download the “End User Runtime” or if you want the samples grab the Developer SDK: http://www.slimdx.org/download.php
  5. A Brain: needless to say all of the above is fairly useless if you don’t bring one of these along for the ride. It might also be useful if your brain has a working knowledge of C#, understands some basics of Direct3D programming and at least knows what an API hook is and why you might want to use one in the context of Direct3D applications.

Implementation

The first thing we need to do is implement the MarshalByRefObject class (referred to as the client/server interface object or simply interface) to be used by the host application and client/injected assembly to communicate via an IPC channel. We also need to create the client assembly that will be injected into the target application, all with the help of EasyHook.

For my example I want the injected assembly to take a copy of the back buffer during EndScene when requested to do so by the host application and convert it to a Bitmap object.

The marshalled object will do three things:

  1. Send a request for a screenshot from the host process to the injected assembly.
  2. Return the screenshot result from the injected assembly back to the host process.
  3. Provide a method by which the injected assembly can ping the host process and know that it needs to continue checking for screenshot requests or not.
  4. Allow debug messages from the injected assembly back to the host process.

The injection assembly will be the client for the IPC server. Using EasyHook we will inject this into the target application and call its “Run” method. This is where hooks are initialised, and a loop entered until the host application closes the IPC channel or the target process closes.

Getting the Function Addresses

The main difficulty in hooking DirectX is that the methods are not exposed in any way that allows us to get the addresses in a straight forward manner. Instead you either need to determine the addresses and hard-code them (they differ from platform to platform and version to version however), or use the VTable to lookup the addresses dynamically at runtime (which is what we will do here).

[BEGIN: AMATEURISH C++ CODE]

Here is my C++ code to retrieve the 119 addresses of IDirect3DDevice9 from the VTable and place them in an array for later retrieval:

// This will be an array of (uintptr_t*)
uintptr_t** g_deviceFunctionAddresses;
// There are 119 functions defined in the IDirect3DDevice9 interface
// (including our 3 IUnknown methods QueryInterface, AddRef, and Release)
const int interfaceMethodCount = 119;

// ...The Windows and Direct3D code to get a pointer to
// the D3D Device goes here...

// retrieve a pointer to the VTable
uintptr_t* pInterfaceVTable = (uintptr_t*)*(uintptr_t*)pd3dDevice;
g_deviceFunctionAddresses = new uintptr_t*[interfaceMethodCount];

// Retrieve the addresses of each of the methods
// (note first 3 are IUnknown methods)
// See d3d9.h IDirect3DDevice9 to see the list of methods,
// the order they appear there is the order they appear in
// the VTable, 1st one is index 0 and so on.
for (int i=0; i<interfaceMethodCount; i++) {
	g_deviceFunctionAddresses[i] = (uintptr_t*)pInterfaceVTable[i];
}

To later retrieve the address for hooking is simply a matter of returning the appropriate methods’ index in the VTable:

uintptr_t* APIENTRY GetD3D9DeviceFunctionAddress(short index)
{
	if (g_deviceFunctionAddresses) {
		return g_deviceFunctionAddresses[index];
	} else {
		return 0;
	}
}

[END: AMATEURISH C++ CODE]

If you wish to hardcode the offsets then you can determine the offsets to use as follows:

	// Determine the offset for the current platform if hardcoding addresses
	g_deviceFunctionAddresses[index] - GetModuleHandle("d3d9.dll");

You can then simply add that offset against GetModuleHandle(“d3d9.dll”) in your application and bypass the need for a C++ helper dll. The D3DHelper.dll in the attached solution logs these offsets to a log file.

The EndScene Hook

Ok now that the unmanaged code is out of the way, we are able to hook the EndScene function using EasyHook like so:

// 42 – IDirect3DDevice9.EndScene
LocalHook Direct3DDevice_EndSceneHook = LocalHook.Create(
    GetD3D9DeviceFunctionAddress(42), // This is the C++ helper method we implemented above - hardcoded addresses could be used instead
    new Direct3D9Device_EndSceneDelegate(EndSceneHook),
    this);

Within EndSceneHook we are now able to copy the BackBuffer into our own IDirect3DSurface9 with the following:

/// <summary>
/// Hook for IDirect3DDevice9.EndScene
/// </summary>
/// <param name="devicePtr">Pointer to the IDirect3DDevice9 instance. Note: object member functions always pass "this" as the first parameter.</param>
/// <returns>The HRESULT of the original EndScene</returns>
/// <remarks>Remember that this is called many times a second by the Direct3D application - be mindful of memory and performance!</remarks>
int EndSceneHook(IntPtr devicePtr)
{
    using (Device device = Device.FromPointer(devicePtr))
    {
        // If you need to capture at a particular frame rate, add logic here decide whether or not to skip the frame
        try
        {
            // Is there a screenshot request? If so lets grab the backbuffer
            lock (_lockRenderTarget)
            {
                if (_screenshotRequest != null)
                {
                    DateTime start = DateTime.Now;
                    try
                    {
                        // First ensure we have a Surface to the render target data into
                        if (_renderTarget == null)
                        {
                            // Create offscreen surface to use as copy of render target data
                            using (SwapChain sc = device.GetSwapChain(0))
                            {
                                _renderTarget = Surface.CreateOffscreenPlain(device, sc.PresentParameters.BackBufferWidth, sc.PresentParameters.BackBufferHeight, sc.PresentParameters.BackBufferFormat, Pool.SystemMemory);
                            }
                        }

                        using (Surface backBuffer = device.GetBackBuffer(0, 0))
                        {
                            // Create a super fast copy of the back buffer on our Surface
                            device.GetRenderTargetData(backBuffer, _renderTarget);

                            // We have the back buffer data and can now work on copying it to a bitmap

                            // NOTE: originally I had tried calling ProcessRequest in a separate
                            // thread, however I ran into stability issues resulting in
                            // corrupt images or memory violation issues. Therefore ProcessRequest
                            // is called direct.
                            // ProcessRequest is also the slowest part of the EndScene hook.
                            ProcessRequest();
                        }
                    }
                    finally
                    {
                        // We have completed the request - mark it as null so we do not continue to try to capture the same request
                        // Note: If you are after high frame rates, consider implementing buffers here to capture more frequently
                        //         and send back to the host application as needed. The IPC overhead significantly slows down
                        //         the whole process if sending frame by frame.
                        _screenshotRequest = null;
                    }
                    DateTime end = DateTime.Now;
                    _lastScreenshotTime = (end - start);
                }
            }
        }
        catch
        {
            // If there is an error we do not want to crash the hooked application, so swallow the exception
            // TODO: log any exceptions
        }

        // EasyHook has already repatched the original EndScene - so calling EndScene against the SlimDX device will call the original
        // EndScene and bypass this hook. EasyHook will automatically reinstall the hook after this hook function exits.
        return device.EndScene().Code;
    }
}

The ProcessRequest method copies the data from our copy of the back buffer into a stream that we can now transfer back to the host process within a separate thread:

/// <summary>
/// Copies the _renderTarget surface into a stream and starts a new thread to send the data back to the host process
/// </summary>
void ProcessRequest()
{
    if (_screenshotRequest != null)
    {
        // We need to convert Width and Height to x2, and y2 respectively, because
        // SlimDX Surface.ToStream(...) expects the rectangle to have x1,y1,x2,y2 not x1,y1,width,height
        Rectangle region = new Rectangle(_screenshotRequest.RegionToCapture.Location, _screenshotRequest.RegionToCapture.Size);
        region.Width += region.X;
        region.Height += region.Y;

        // Prepare the parameters for RetrieveImageData to be called in a separate thread.
        RetrieveImageDataParams retrieveParams = new RetrieveImageDataParams();

        // After the Stream is created we are now finished with _renderTarget and have our own separate copy of the data,
        // therefore it will now be safe to begin a new thread to complete processing.
        // Note: RetrieveImageData will take care of closing the stream.
        // Note 2: Surface.ToStream is the slowest part of the screen capture process - the other methods
        //         available to us at this point are _renderTarget.GetDC(), and _renderTarget.LockRectangle/UnlockRectangle
        if (_screenshotRequest.RegionToCapture.Width == 0)
        {
            // The width is 0 so lets grab the entire window
            retrieveParams.Stream = Surface.ToStream(_renderTarget, ImageFileFormat.Bmp);
        }
        else if (_screenshotRequest.RegionToCapture.Height > 0)
        {
            retrieveParams.Stream = Surface.ToStream(_renderTarget, ImageFileFormat.Bmp, region);
        }

        if (retrieveParams.Stream != null)
        {
            // _screenshotRequest will most probably be null by the time RetrieveImageData is executed
            // in a new thread, therefore we must provide the RequestId separately.
            retrieveParams.RequestId = _screenshotRequest.RequestId;

            // Begin a new thread to process the image data and send the request result back to the host application
            Thread t = new Thread(new ParameterizedThreadStart(RetrieveImageData));
            t.Start(retrieveParams);
        }
    }
}

To ensure that _renderTarget is always in the correct format we also have to hook IDirect3DDevice9.Reset so that we can dispose of _renderTarget and ensure it is recreated with the correct parameters (e.g. to handle switching between windowed and full screen).

/// <summary>
/// Reset the _renderTarget so that we are sure it will have the correct presentation parameters (required to support working across changes to windowed/fullscreen or resolution changes)
/// </summary>
/// <param name="devicePtr"></param>
/// <param name="presentParameters"></param>
/// <returns></returns>
int ResetHook(IntPtr devicePtr, ref D3DPRESENT_PARAMETERS presentParameters)
{
    using (Device device = Device.FromPointer(devicePtr))
    {
        PresentParameters pp = new PresentParameters()
        {
            AutoDepthStencilFormat = (Format)presentParameters.AutoDepthStencilFormat,
            BackBufferCount = presentParameters.BackBufferCount,
            BackBufferFormat = (Format)presentParameters.BackBufferFormat,
            BackBufferHeight = presentParameters.BackBufferHeight,
            BackBufferWidth = presentParameters.BackBufferWidth,
            DeviceWindowHandle = presentParameters.DeviceWindowHandle,
            EnableAutoDepthStencil = presentParameters.EnableAutoDepthStencil,
            FullScreenRefreshRateInHertz = presentParameters.FullScreen_RefreshRateInHz,
            Multisample = (MultisampleType)presentParameters.MultiSampleType,
            MultisampleQuality = presentParameters.MultiSampleQuality,
            PresentationInterval = (PresentInterval)presentParameters.PresentationInterval,
            PresentFlags = (PresentFlags)presentParameters.Flags,
            SwapEffect = (SwapEffect)presentParameters.SwapEffect,
            Windowed = presentParameters.Windowed
        };
        lock (_lockRenderTarget)
        {
            if (_renderTarget != null)
            {
                _renderTarget.Dispose();
                _renderTarget = null;
            }
        }
        // EasyHook has already repatched the original Reset so calling it here will not cause an endless recursion to this function
        return device.Reset(pp).Code;
    }
}

Running It

The following assemblies must be registered within the GAC before hooking will work:

  1. SlimDX.dll
  2. EasyHook.dll
  3. The assembly containing your IPC object (i.e. ScreenshotInterface in ScreenshotInject.dll)
  4. Also put the assembly that will be injected to the target application, in my case the same as the IPC assembly (i.e.ScreenshotInject.dll)

OR: EasyHook has a handy method for doing this at runtime if your host application is running as an Administrator, e.g.:

// Note: EasyHook.dll will be automatically added to GAC if not already when using the following method
Config.Register(
  "My Injection assemblies",
  "Assembly1.dll",
  "Assembly2.dll");

You need to have the EasyHook binaries within the same directory as your applications executable (see the attached solutions bin directory).

I prefer to use the following post build command to register my injection assembly in the GAC, at least while debugging. It requires that you are running Visual Studio as an Administrator:

"C:Program FilesMicrosoft SDKsWindowsv6.0ABingacutil.exe" /i "$(TargetPath)"

You should already have the Direct3D SDK. You may need to adjust the locations in the D3DHelper DLL project (I installed it to “C:DirectX SDK“). The examples included in the SDK are handy for testing (e.g. checking that you have not tied up any Direct3D resource after everything closes down and so on). If you are on a 64-bit OS, the SDK includes 64-bit examples so you can test if you can’t find another 64-bit Direct3D application. You will notice that if you exit the target application first there is a Surface object that Direct3D samples complain about not having been freed correctly – if you close the host application first you will not get this warning.

64-bit Support

The .NET assemblies all support 64-bit as they are compiled for “Any CPU”. The only difference is that you need to compile the helper C++ dll in 64-bit for it to work when hooking 64-bit applications (other than the Direct3D SDK example I have not come across any yet).

Improvements, Considerations and What Next?

If you are looking to capture frames for a movie, do the buffering in the injected assembly, I believe you will be able to achieve fairly high frame rates. Then return the buffer to the host process as needed.

For completeness you may want to investigate an alternative to SlimDX Surface.ToStream – this utilises the underlying D3DXSaveSurfaceToFileInMemory. Instead you could try Surface.LockRectangle and read the pixels directly. I have yet to try this and compare performance.

Important!: Remember that your injected assembly is running in the target application. Handle ALL your exceptions and cleanup after yourself. Be a good citizen and don’t cause any unnecessary interruption to the target process. Direct3D hooking involves functions that are called many many times per second, so keep this in mind at all times!

Note that I pass around a byte[] for my IPC not a Bitmap object, this is because the Bitmap class does not support MarshalByRefObject.

If you want to implement in-game overlays, simply prepare everything within EndScene, and add it to the scene as necessary before passing on the request to the “real” EndScene method.

What about Direct3D 10/11? I believe a similar process will work fine, and will be preparing an updated example to support all current versions in the future.

Further Reading

I recommend taking a look through the EasyHook documentation and examples to get a better understanding of how it hangs together.

The SlimDX documentation and examples will be handy if you want to implement any additional features such as in-game overlays.

Greg over at Ring3 Circus has some great articles that have helped with my understanding of DLL injection and Direct3D hooking: http://www.ring3circus.com/category/gameprogramming/

The forums over at gamedeception.net have been a great learning resource. You will have to register to view the posts and download the example source code (all C++): http://forum.gamedeception.net/forum.php

As always searching on Google found plenty of useful information too numerous to remember or list here.

Feedback

I have spent some time trying to get this right so please leave a comment if you can think of any improvements or have any other feedback.

I am always interested in hearing about how this has been useful in your own solutions. You can use the contact page or leave a comment here if you would like to share.

Download

C# Direct3D 9 Hook source download (revision 2 – 2010-04-16) 

The original version of the source code can still be found here.

115 thoughts on “C# – Screen capture with Direct3D 9 API Hooks”

    1. Hello from AceticSoft.com MMO Development Community! We Appreciate your work and postings and have donated to your cause. Thank you. 🙂

  1. Hey Justin,
    This may be a silly question but I really don’t know the answer :>
    Will this code work also on OpenGL APIs?

    1. Hi,
      This example will only work with Direct3D. I don’t really know much about the OpenGL API’s but I guess you could try the same approach if the equivalent methods are available in that API

      Cheers, J

  2. I was just wondering why those dlls must be registered in the GAC? That seems like it adds a lot of complication to the install when the hook is released. Is there an easier way to deal with that? I am kind of familiar with the GAC but maybe not as well as I should be.

    1. Hi Joey, the assemblies will be run in the context of the target process and therefore need to be on the search path of that process i.e. same directory as target process executable or GAC. The EasyHook Config class provides a way to register these temporarily if running as an administrator if you don’t want register them as part of the installation process.
      Cheers
      J

      1. Ok so putting that in the same directory would work then? I would rather not have to register with GAC. Makes things more complicated I guess. Hmm thats more reasonable.

  3. Very nice work! This article has helped me better understand easyhook and what can be done with it. However, there are some strange irregularities when using the default project.

    If I start up world of warcraft or oblivion in fullscreen mode, alt-tab causing them to minimize, then press inject, the target process is raised to be the active window, but isn’t selected so keys and mouse movements are passed through the game. If you however alt-tab at this point and luckily select the game as the alt-tab target commands will be passed properly if you however do not land on the game, the screen locks up and renders what ever was on screen at the moment of trying to tab over and over. At this point pressing alt+enter to shift from fullscreen to windowed and then once more to back to fullscreen will allow you to properly alt tab.

    Sorry if that is hard to understand, do you have any insight into why this might be, and how it could be corrected?

    1. Hi,

      I’ve taken a look at it and reproduced the problem. I’m testing out code that correctly sets the foreground window to the targeted application which fixes the problem here, however I want to further investigate the cause before updating the project download.

      My guess is that the native code that creates a temporary window and then destroys it could be the problem. Over the next few days I’ll test it out with hard coded addresses / using the existing HWND of the target application.

      Cheers,
      J

    2. Hi,

      The native dll that creates an IDirect3DDevice9 is what causes this behaviour when hooking a fullscreen application.

      I have confirmed that using hard-coded address offsets, or determining them in a separate process will fix the problem.

      I have uploaded an updated copy of the source code that alleviates the issue by restoring the fullscreen target after injection programmatically – instead now only the Alt+Tab doesn’t work immediately after injection for a fullscreen application. Pressing Alt+Enter to switch to windowed will then fix the issue.

      See bottom of post for new link to source.

      Cheers,
      J

  4. Hi Justin,

    I can use this code for my Ambilight program. This program capture the screen trought the FrontBuffer of DirectX. But games with AA on and also Windows Media Center wouldn’t work with the frontbuffer. The only way to do this is trought hooking and read the backbuffer. That’s why this article is interesting.

    But what I have tried so far. It didn’t work with Windows Media Center. Please can you have a look to it?

    Many thanks,

    Gerrit

    1. Hi Gerrit,

      I haven’t tried anything with Windows Media Center yet, but will try to take a look in the next few weeks. I will let you know if I find anything.

      Cheers,
      J

      1. Hi Justin,

        I have now a capture from Windows Media Center. I had made the following changes (Thanks to Armageddon_2k and KDVken):

        First I replace the creation of _renderTarget, under the device.GetRenderTaget(0), because to get the correct size. Windows Media Centers device is 16 by 16 px, and will give some error’s, if you copy a surface from 1000 by 500 to 16 by 16. The correctsize wil come from renderTargetTemp.Description.Width.

        Second change is to create a new Surface whitout MultiSample and copy from our rendertarget to our new surface. This is for capture the screen for games with AA on. (http://leenux.org.uk/2009/11/02/d3d-screenshots-with-anti-aliasing-quickly/)

        Third changes (for me important), is that the device.StretchRectangle wil resize the screencapture. In my program I must keep an image of 10 by 10 pixels. So not the whole image will go back from the GPU to the CPU.

        The changed source can you found here: http://gathering.tweakers.net/forum/list_message/34384265#34384265

        1. Thanks for posting back your findings Gerrit!

          I’ll look at trying out the AA solution you posted when I get some spare time.

          I really need to put together an updated version (including the C# method for retrieving VTable addresses and DirectX 10 support) when I find some spare time 🙂

          Cheers,
          J

        2. Hey Gerrit,

          I just wanted to do the same thing! Did you finish your ambilight application yet? I would appreciate it very much if you could share your source/app. I just figured out the same problems like you mentioned here.

          thanks!
          daniel

  5. It’s brilliant I found this just as I’ve come to need it!

    I was wondering (or asking before I try) – you know how you’re using a helper DLL to make a duff device that in turn gets the virtual addresses? What would happen if I just created said duff device through SlimDX and took the Device.InternalPointer?

    The CLR loads PInvoke libraries through the same mechanism the native victim of a hook does. Right? So this should actually work, shouldn’t it?

    1. It worked!


      /* RenderForm and Device construction omitted... */
      List id3dvt = new List();
      IntPtr rp = device.ComPointer;
      IntPtr vt = Marshal.ReadIntPtr(rp);
      for (int i = 0; i < 119; i++)
      id3dvt.Add(Marshal.ReadIntPtr(vt, i * 4));

      It’s that easy!

      1. Thanks for your input Ben that is a much simpler solution to getting the function pointers. I’ll go ahead and update the example to include this in the next few weeks.

        Cheers,
        J

  6. Hi,

    I’ve spent a bit of time trying to get this to work on my machine but I’m stuck. I’m not getting any errors, but I’m not getting any screenshots either.

    I’ve stepped through the code and used file.appendalltext to understand the flow of the injected code, and what Ive determined is this:

    EndSceneHook is never getting called.

    I’ve tested it with vlc, windows media player, and another directx media player, and the result is the same for each.

    Probably I’ve forgotten some elementary step, but I can’t determine what it could be. Any suggestions would be helpful.

    Thanks

    1. Hi,

      Firstly it would be worth you trying it with a DirectX 9 game to see if it works there – just for a sanity check.

      If that works, then I would suggest you confirm what version of DirectX the applications you are trying to hook are using – this example will only work with DirectX 9 at the moment.

      Alternatively, try hooking a number of other IDirect3DDevice9 methods, e.g. IDirect3DDevice9::Present.

      Also what version of Windows are you running?

      Cheers,
      J

    2. Hi,

      Are you running the example as an Administrative user and are you adding the assemblies to the GAC manually or relying on the example to do the temporary install?

      Cheers,
      J

  7. Hi Justin,

    Thanks for your reply. I’m on Windows 7, am admin, and relying on example to temporary install assemblies in the GAC.

    Can you recommend any games or applications you know the method works with, so I can test? (Preferably something I can get a demo copy of)

    How would I find out what version of Directx an external process is using? I tried an Api monitor but it doesn’t show any obvious version signs. Perhaps I need to look for some specific api calls which are a signiture of different directx versions?

    I have very little directx experience unfortunately.

    Thanks for your help,
    Seabhcan

      1. Turns out its using ddraw.dll, not d3d9.dll. I guess my problem has changed to “How to hook to ddraw.dll”. Back to google!

        Thanks for your help.

        Seabhcan

  8. Hi Justin,

    Thanks a ton. This is very helpful.

    However, I can’t get the TestScreenshot example to run properly. I registered SlimDX.dll, EasyHook.dll, and ScreenshotInjection.dll with GAC using gacutil.exe. When I try to Debug it in VS, the DX9 game crashes when RemoteHooking.inject() is called and I get the following:

    “STATUS_INTERNAL_ERROR: C++ completion routine has returned success but didn’t raise the remote event. (Code: 255)”

    I also tried running the .bat script included in the example, and also tried the Auto Register option.

    Appreciate the help.

    Jon

    1. Hi Jon,

      Can you try debugging with VS running as an Administrator? I did find that debugging didn’t work for me at one point, and then I started using a later version of EasyHook and it worked again.

      If that fails, start using the poor-man’s debugger and output debug information into a text file. Check if it gets into the Run method of the injected assembly or not…

      What game is it and does it have a free trial or something? If so I could possibly take a look for you.

      Cheers,
      J

  9. Hi Justin,

    Thanks for your work. It’s a big help to me!
    But I’ve got some strange problems.

    I tested the code with some of the samples in DXSDK and it worked very well yesterday, but today it crashes with the following message while calling the RemoteHooking.inject():

    “Unknown error code (-1073741502): The user defined managed entry point failed in the target process. Make sure that EasyHook is registered in the GAC. Refer to event logs for more information. (Code: 13)”

    I debugged with VS running as an Administrator and didn’t change any settings so I wonder why this happens?I also tried running the InstallEasyHookGAC.bat script and the Auto Register option, but the error message continues.

    Appreciate the help.

    RickLee

      1. Did you mean the EasyHook.dll and others in /bin ? I didn’t change them, and I’m sure that they are the same as they were in the packet which you offered. I just compiled the project with no changes and it works well in the first day but crashes in the second day, that made me crazy!

        btw: I delete everything and recompile the code from the very beginning, everything back to normal now but I wonder if there would be the same problem tomorrow.

        1. Hi RickLee,

          Not sure, I’ve only had those errors happen when I’ve either not been running as Administrator, or didn’t have the EasyHook stuff in the correct spot. Sounds like you had both of those covered.

          I’m glad a recompile worked for you.

          I doubt you will end up with the same problem – I run it on a regular basis without any issues.

          Cheers,
          J

          1. Hi Justin,
            Lucky that the problem never appeared again since the recomplie. I read the code these days and now I have a new question.

            I changed the Surface.ToStream to _renderTarget.GetDC() for a faster speed, according to your proposal. I tried to build a bitmap directly with the DC returned, but it didn’t work, so I had to create a Graphics object with Graphics.FromHdc(_renderTarget.GetDC()), then copy it to a bitmap and passed it back, but this take even more time than Surface.ToStream. Do you have any suggestions? How did you implement it?
            Appreciate for your help.

            btw:I also tried the LockRectangle method and it was a little faster indeed, but not very much. I wonder if there are other methods

          2. Hi RickLee,

            The method you see in the post is what I have used.

            I did begin to look at buffering using multiple surfaces, but didn’t get very far as what I had was all I needed for my purposes.

            What I was planning was to use GetRenderTarget into an array of surfaces, processing each on a separate thread (i.e. try to get ProcessRequest to work on a separate thread – passing the index of the surface to be processed).

            Also if you do not need the full resolution, Gerrit has posted a link to a solution he derived from this that uses device.StretchRectangle – a smaller surface means less data to copy from video memory to system memory which will speed things up.

            I hope this helps, and please keep me updated on how you go 🙂

            Cheers,
            J

  10. so how do can i take screenshot using DirectX in C# from zero?
    i cant believe this whole code should be written..
    PLEASE help me..
    here or in my e-mail (without spaces)
    roy_barina @ hotmail.com

    1. Hi Roy,

      You will need to download the sample linked at the bottom of the article. This has everything ready to go for you.

      Cheers,
      J

        1. Hi Roy,

          It takes a screenshot of a single DirectX window, not the whole desktop. But it can do that for fullscreen DirectX windows.

          Cheers,
          J

          1. This is my first time using VC++..
            i need to compile the helper in VC++ then in VC# add it as reference?
            they both(VC++ and VC#) are shouting at me.. :

          2. Hi Roy,

            You can do it all in C#, there’s a comment above by Ben that has a small snippet of code that does the VTable address lookups. Otherwise, I’ll try to dig up my current version and post that portion of code.

            Cheers,
            J

          3. Here is C# code to retrieve the address pointers:

                // First we need to determine the function address for IDirect3DDevice9
                Device device;
                List<IntPtr> id3dDeviceFunctionAddresses = new List<IntPtr>();
                using (Direct3D d3d = new Direct3D())
                {
                    using (device = new Device(d3d, 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters() { BackBufferWidth = 1, BackBufferHeight = 1 }))
                    {
                        IntPtr realPointer = device.ComPointer;
                        IntPtr vTable = Marshal.ReadIntPtr(realPointer);
                        for (int i = 0; i < 119; i++)
                            id3dDeviceFunctionAddresses.Add(Marshal.ReadIntPtr(vTable, i * IntPtr.Size));
                    }
                }
            
          4. where is that should go..?
            i tried something and this:

                                // Inject DLL into target process
                                RemoteHooking.Inject(
                                    process.Id,
                                    InjectionOptions.Default,
                                    "ScreenshotInject.dll", // 32-bit version (the same because AnyCPU) could use different assembly that links to 32-bit C++ helper dll
                                    "ScreenshotInject.dll", // 64-bit version (the same because AnyCPU) could use different assembly that links to 64-bit C++ helper dll
                                    // the optional parameter list...
                                    ChannelName // The name of the IPC channel for the injected assembly to connect to
                                    );

            throws me an DllNotFoundException
            i can load my whole code to gitHub so you can see..
            :S

          5. Sounds to me like the ScreenshotInject.dll is either not built, or not registered in the GAC. I have found on a 64-bit Windows 7 machine you have to register in the GAC and then restart the machine for it to correctly find the assembly while injecting.

            If you want me to look at the code then feel free to load it to gitHub or similar… Otherwise send me an email via the contact page and I’ll reply from there.

            Cheers,
            J

          6. Hi Justin,
            I’ve tried this piece of code but it retrieves a complete different result from d3dhelper.dll and the hook failed. Is there any other thing I need to pay attention to?

  11. Thanks Justin. This post was really helpfull. Nice use of Remoting 🙂

    For people who want to use this to capture a video, they might want to look into int EndSceneHook(IntPtr devicePtr) and put a StretchRectangle in there to scale down the image to a smaller resolution (using GPU rather than CPU). Might also be usefull for people who want to use this to implement AmbiLight for games, since the image can be mipmapped to 2×1 pixel (depending on howmany leds you want to control), which would yield the average pixel colors if MIPMAP is used.

  12. Thanks, Justin! This is exactly what I needed to help me. If only I could get it to work…For me, it appears that the Run() of the injected ScreenShotInject.dll never executes, so it never gets to the DirectX parts…do you know why this is?

    1. Hi Cecil,

      I would try the following:

      1) register EasyHook and the Screenshot assemblies into the GAC
      2) reboot your machine
      3) try again

      I have found on 64-bit systems the registration of the assemblies in the GAC is not always immediately visible, especially if running the app as Administrator.

      Hopefully this helps. If not, could it be that you are trying to hook a DirectX 10/11 app not a DirectX 9 one? Although you would still at least get to the Run() method.

      Also the debugger is sometimes unable to step through the Run() method in the target process. It’s best to only attach to the application being hooked, and set the break point then. Because it is in the GAC I find it is a little difficult to get debugging at times.

      Cheers,
      J

  13. Great code! very well done! I’m trying to use this with a C++ program, get you point me in the right direction? Any chance of creating a TestScreenshot program in C++? I could post a sample of this if you can help me out. Thanks,

    Phil

  14. Hello Justin,

    Thanks for the great code!

    I have a question for you:
    As you are using EndScene for hooking, the first image of the target window(app) can be obtained only the first time it gets redrawn, and if no changes occur in the window for a long time, then getting first image takes a while.

    I need to connect to the app and get the first image as it is before EndScene gets called, or may be invalidate window/force EndScene. Is that possible in D3D?

    Thanks. in advance for your help!

    1. Hi Igor,

      If you can get a reference to the device you can then retrieve the Front or Back buffers. In this case you probably want to grab the front buffer.

      To get the device reference you might need to experiment with which other D3D methods are being called more regularly and hook one of them.

      Cheers,
      J

      1. Hi Justin,

        Thanks for your answer.
        I got your idea that I need to access front buffer of a device by hooking some other Direct3D method. However in the case where the target application is simply WPF app which is idle, no Direct3D method is called before some change in visual state of it happens.

        My target app screenshots of which I need to get is WPF so what I decided to do is to basing on your code in the injected code call Application.Current.MainWindow.InvalidateVisual() using Application.Current.Dispatcher and delegate. But althout WPF documentation says that InvalidateVisual forces redraw=>I expected EndScene call, it didn’t work. May be you have some thoughts, why? I know you worked in the area of grabbing WPF/Direct3D windows a lot.

        Thanks!

        1. You could perhaps try to do something like the following: http://www.grumpydev.com/2009/01/03/taking-wpf-screenshots/

          Using RenderTargetBitmap on a WPF visual element is the regular way of grabbing something in WPF (when it’s your app) – perhaps that will work from your injected assembly also.

          It isn’t very fast tho, taking between 200-500ms.

          Also if you save to a PNG or another format that stores the alpha, you can capture the window preserving any transparencies.

          Cheers,
          J

  15. I’m trying this with Skype, I’m working on a project to capture the video when Skype is not on the foreground.
    The TestScreenshot application just hangs after “Inject”…

    Can this be used to capture the video from Skype?
    If yes, could you please give me a hint on how I get the TestScreenshot application to work with Skype?

    Thanks,
    Frank.

    1. Hi Frank, no this will only work for directx 9 applications. You would need to hook directshow or dwm or possibly something else to do this.

      I haven’t hooked any of these except dwm, and that was only for vista.

  16. Great Job ! thanks to share it.
    From my side, injected dll crash when hooked app is fullscreen.
    When windowsed, no problem.

    i gently modified example to diagnose the problem:
    When i press inject, it waits intil target is in foreground, then, and only then, it calls inject.

    And the exception occurs (with no doubt) when it try to build the endscene hook “Direct3DDevice_EndSceneHook = LocalHook.Create(…”:

    System.ArgumentException: STATUS_INVALID_PARAMETER_1: Invalid entry point. (Code: 127)
    à EasyHook.LocalHook.Create(IntPtr InTargetProc, Delegate InNewProc, Object InCallback)
    à ScreenshotInject.ScreenshotInjection.Run(IContext InContext, String channelName)

    Looks like VTable is not available, or something like that when target game is fullscreened. I don’t figure out how to know if target is plenty displayed when Hook is builded.

    Any idea how to fix this?
    about witch entry point is it talking about?
    Just to confirm : Target uses DirectX9.

    1. Hi Giova,

      I haven’t experienced this problem, usually if it fails for me, it will fail in both Windowed and Fullscreen.

      Have you checked that the EndSceneHook address that is discovered in the VTable is the same in both windowed / fullscreen? I can’t think why that would differ or fail for that matter just because it is fullscreen and not windowed.

      The error appears to occur within EasyHook, have you checked that it really has d3d9.dll loaded when in fullscreen? Perhaps do a get module address on it to confirm?

      Another thing that might be occurring is that the application you are hooking is implementing some anti-hooking capability when in fullscreen. But it really does sound like it can’t find the method address where it is expecting it…

      Let me know how you go, and hopefully this helps.

      Cheers,
      J

      1. Hi Justin, and thanks for your fast answer.
        Well, i really don’t think that game implement any anti-hooking feature, that is a cheap game, mainly for single player….

        Anyway, i went deeper into my diagnostic by adding some add_log() into D3DHelper.

        the problem occurs into GetD3D9DeviceFunctionAddress func, on that line :

        FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, dwBehaviorFlags, &d3dpp, &pd3dDevice )

        i’ve checked, both if windowsed or full screen: dxBehaviorFlags == 40, and of course hWnd change every times, but is never equal to 0.
        more over &d3dpp is never equal to 0 too.

        pD3D is of course not null in any case

        pD3D->GetAdapterDisplayMode and pD3D->GetDeviceCaps seems not failing

        So i have absolutly no idea of what is going wrong…
        Do you have any suggestion?

        1. ok, perhaps i found where the problem is.

          into msdn, regarding IDirect3D9::CreateDevice Method
          it says pretty interresting things about hFocusWindow parameter :
          quote:
          “•For full-screen mode, the window specified must be a top-level window.
          •For windowed mode, this parameter may be NULL only if the hDeviceWindow member of pPresentationParameters is set to a valid, non-NULL value.

          but into your code, this parameter value seems to comes from a fake window :

          HWND hWnd = CreateWindowA(“BUTTON”, “Temporary Window”, WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, dllHandle, NULL);

          isn’t it?

          if i’m on the right way, i’m wondering how this should works for you in fullscreen mode. But i’m pretty noob, so probably i’m missing something…

          Now perhaps solution would be to get the hooked app’s wHnd, problem is for me that i’m very very noob with cpp.

          i’ll try adding a parameter to GetD3D9DeviceFunctionAddress signature, so injected dll should pass window’s handle.

          what do you think about it?

          thanks again for your help.

          1. ok, i’ve partially fixed the problem.

            FYI CreateDevice was returning Device lost
            So i changed its device type parameter to D3DDEVTYPE_NULLREF and now it works.

            I said partially fixed because game crashes if i do Alt+Enter or Alt+Tab twice (when the game is going to be redisplayed)

            Interresting fact is the we can create a nullref device to get VTable adress. But now i have to discover why game doesn’t appreciate display switching.

            I know you wrote few things regarding this issue, i’ll tell you if i discover something interresting. While this any tips/suggestion is wellcome.

            Cheers.

          2. Hi Giova,

            Looks like you have mostly sorted it out, that’s great.

            I have actually rewritten my CPP code in C# instead, based on a comment from Ben.

            e.g. at the start of the Run() method of the screenshot injection class:

                // First we need to determine the function address for IDirect3DDevice9
                Device device;
                List<IntPtr> id3dDeviceFunctionAddresses = new List<IntPtr>();
                using (Direct3D d3d = new Direct3D())
                {
                    using (device = new Device(d3d, 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters() { BackBufferWidth = 1, BackBufferHeight = 1 }))
                    {
                        IntPtr realPointer = device.ComPointer;
                        IntPtr vTable = Marshal.ReadIntPtr(realPointer);
                        for (int i = 0; i < 119; i++)
                            id3dDeviceFunctionAddresses.Add(Marshal.ReadIntPtr(vTable, i * 4));
                    }
                }
            

            When I was first developing for this post I also hooked the device Reset function to handle switching between Windowed/Fullscreen and this has always worked well for me since. Perhaps there is something else I have missed…

            Good luck! And keep me posted – feel free to use the contact page if you want me to try and take a look at the specific game or anything.
            J

          3. Thanks Justin, i sent you an email.

            Regarding your last modification, i’m probably wrong but, i’m wondering if it should works with a 64bits machine?

            Should we do something like this instead?

            int ptrSize = 4;
            if (RemoteHooking.IsX64Process())
            ptrSize = sizeof(IntPtr);
            […]
            id3dDeviceFunctionAddresses.Add(Marshal.ReadIntPtr(vTable, i * ptrSize));

  17. Hey man 🙂
    Very nice articel.
    Can you say me, how I can use this example to draw something in the game with D3D ?
    You know, a simple rectangle or a font 🙂

  18. Hi Justin,i have downloaded Direct3DHook_2011-03-14.zip and according to your post to run the TestScreenshot, it can display the GUI,i use ParallaxOcclusionMapping.exe for example which is belong to directx sample,input ParallaxOcclusionMapping
    into the EXE name of direct3d application ,then click inject,the program is died.i dont know why ? can you tell me when i enter the GUI ,how can i use the GUI to screeen capture

    1. Hi Justin,i found someone have the same question with me ,you suggest them to ensure “register EasyHook and the Screenshot assemblies into the GAC”,i want to know how to register?i am sorry i am a beginner,Look forward to your reply

      1. now ,i can register the dll file to GAC,but there still have some problems.i write them problems in the comment in your another logs .thanks for your help

  19. Hi Justin, thanks for this write-up! I’m assuming this will only work for applications that use Direct3D, right? I’m trying to investigate what the different possibilities are for recording a screen capture from an application. The first approach I’ve seen, which CamStudioPro, Camtasia etc seem to be using is a ‘front-buffer’ capture, which is directly from the screen, so it won’t capture from the application window if it’s moved, minimized, etc. Then I stumbled on this post and it seems to be what I’m looking for, which is being able to capture the application window whether it’s minimized, moved, or whatever. I noticed that you’re hooking into the target app in order to do this. Do you know if the Windows API provides any facilities to capture from the application without needing to hook into it? It sounds like your approach will only work when apps are using the direct3D API, is that right? Do you know if it’s even possible to capture from any app, minimized or not? Thanks for your time and I would love to hear any thoughts you have on this.

    1. Hi Peterson,

      It isn’t possible to capture from a minimised application, but you can for a window that is behind another, or partially off screen.

      The only way I know to do it in the windows API for non-Direct3D applications, that still allows you to capture background windows, is via the DWM. However it is not possible to capture a full size image this way without using undocumented API’s (you will find an example that works in Vista on this blog). To date I have not tried in great deal to get this working for Windows 7, but I do know this is possible (here is a C++ demo: http://www.youtube.com/watch?v=G75WKeXqXkc).

      Good luck,
      J

      1. ah, saw your reply after I had posted my other question below, thanks I’ll look into that, and I hope you can help me understand how to use your hook app working that I mentioned below. Thanks!

  20. Hi again, I’m trying to run your latest Direct3DHook test app with a DirectX 11 Tutorial sample app 04. I was a little unclear on how exactly to use it, so I dropped the directX exe ‘Tutorial04.exe’ into the bin dir for the the Direct3DHook app, then I’m launching Tutorial04.exe, and while it’s running, I’m launching Direct3DHook, and supplying ‘Tutorial04.exe’ and clicking inject. When I step through in Debug, the method call GetProcessByName(textBox1.text) keeps returning an empty list. In the process list for Windows, I can see Tutorial04.exe running. Am I misunderstanding something? It looks like the hook app wants the target app already running, right? Any help is appreciated!

    1. Maybe I should also mention that SlimDX.dll, EasyHook.dll, ScreenshotInject.dll are all registered with the GAC.

      1. Just figured it out! It didn’t like the file extension .exe, so I just added ‘Tutorial04’ and it worked:-)

  21. Hi
    Thank you for your great article !
    I have a problem: when i playing a game, i want to create an application running in background to draw a simple text over screen (f.e. time, key pressed etc…)
    Using GDI+ is impossible, how can i do it with hooking Directx ? Can you give me any examples if it is possible ? Thank so much !

  22. Hey Justin,
    My problem is of a different kind, not related to your code. I was wondering if you could provide any inputs on how a screen capturing software works, like from where does it pick the video signal & take the screen shots. Earlier I thought that data(screenshots) was being picked up from the system clipboard by a screen capturing software, but then when I disabled the system clipboard & ran the screen capture software, it was still picking up the data & making a video. Can you provide some help regarding this? I want to prevent screen capturing.

    1. Hi Vinay,

      In my opinion there is no 100% sure fire way to prevent a determined person / application from being able to take a screen shot. The techniques shown here are grabbing the screenshot from the Direct3D API. You can implement logging that allows you to see what is accessing the various API’s that can be used for screen capturing (GDI+ / DirectX / etc) and with a great deal of injection experience you could possibly be able to make it difficult to take screenshots by injecting into the various API’s and preventing them from doing what they would normally do. But it would be difficult to do this without impacting system stability, and I think there will always be some way to work around it.

      Stopping the basic Print Screen / Alt + Print Screen would be quite easy however by creating a global keyboard hook and preventing those particular key combinations, but I don’t think you are going to be able to prevent applications from using other techniques.

      Cheers,
      J

  23. Hi!

    Do you know a way to determine the current yaw and pitch in a directx-egoshooter via hook, ie “externally”?

    Best

  24. Hi!

    Is it possible to modify your sample project to hook into DrawText and other similar DX functions?

    1. Hi John,

      Yes it is possible. You need to create a new delegate for the appropriate method – just follow what was done for the EndScene hook of Direct3D 9.

      Cheers,
      J

      1. How do I get the correct function index?
        How was the enum Direct3DDevice9FunctionOrdinals formed?

        1. By reviewing the DirectX SDK header file for the relevant interface. The order that they appear in the *.h file is the order you want to index them by (0-based).

          Cheers,
          J

  25. Hello Justin ,
    I am trying to intercept IE10’s text via D3D10.can you tell me accurate approach for hooking which interface’s function in which i get text or glyphs.
    Thanks in advance.

    1. Hi Sam,

      I have no idea how IE10 renders its text, but for D3D10 I guess you could try taking a look at the ID3DX10Font interface..

      You would have to instantiate an instance of the interface and then implement hooks to see if it is appropriate.

      Also take a look at this post for more up to date examples.

      Cheers,
      J

    1. Hey Marc, are you trying with the updated code that also supports d3d 10/11? That project is on GitHub and doesn’t require using the GAC.

      Cheers,
      J

      1. Yeah, I just found out your updated-updated project on GitHub. But am I misunderstanding something, because it only worked after I registered “Capture.dll”, “EasyHook.dll” and “SharpDX.dll” on the GAC. Otherwise EasyHook keep saying it’s can’t find a specific DLL or another.

          1. Hello Justin,
            Trying to run your example as a learning exercise and having a similar problem to this poster. I am using the latest version of your example, and EasyHook 2.7. I have tried with and without the GAC registration for EasyHook and ScreenshotInject and keep getting the same error:

            Additional information: STATUS_INVALID_PARAMETER_4: The given 32-Bit library does not exist! (Code: 2)

            Right now I show SlimDX in GAC_64 – do I need to remove that and find slimdx.dll to place in the app folder? Seen this kind of thing before?

            Thanks for the intriguing work here.

            (error details follow)
            ‘TestScreenshot.vshost.exe’ (Managed (v4.0.30319)): Loaded ‘C:\Users\ts\Documents\hooktest\bin\TestScreenshot.exe’, Symbols loaded.
            ‘TestScreenshot.vshost.exe’ (Managed (v4.0.30319)): Loaded ‘C:\Users\ts\Documents\hooktest\bin\ScreenshotInject.dll’, Symbols loaded.
            ‘TestScreenshot.vshost.exe’ (Managed (v4.0.30319)): Loaded ‘C:\Users\ts\Documents\hooktest\bin\EasyHook.dll’
            ‘TestScreenshot.vshost.exe’ (Managed (v4.0.30319)): Loaded ‘C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll’, Skipped loading symbols. Module is optimized and the debugger option ‘Just My Code’ is enabled.
            A first chance exception of type ‘System.ArgumentException’ occurred in mscorlib.dll
            An unhandled exception of type ‘System.ArgumentException’ occurred in mscorlib.dll
            Additional information: STATUS_INVALID_PARAMETER_4: The given 32-Bit library does not exist! (Code: 2)

          2. In reference to my last post – I just realized I hadn’t installed the x86 32-bit SlimDX runtime, do did that. Now it shows up in GAC_32. Same problem persists.

          3. Thanks, I will give that a shot. By the way, do you know how to tell which library is not found when this error is spit out? I haven’t been able to figure out if it’s talking about EasyHook, ScreenShotInject, SlimDX, or what…

            Additional information: STATUS_INVALID_PARAMETER_4: The given 32-Bit library does not exist! (Code: 2)

  26. Is it possible to capture entire desktop using this technique? Basically, I want to hook into global DirectX calls, so that entire desktop (DWM) rendering is captured by my software and later displayed remotely.

  27. Hello,
    Thanks for a great post, you really helped me get to where I am right now a lot quicker. I got a bug that I need to ask about tho.

    I am building the app using C++ not C# and I am using CreateTemporaryThread instead of easyhook. I am currently using the app on Direct X 9 samples. I am facing a problem with the C++ code you got up there, the thread just gets stuck in an infinite loop in

    if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
    dwBehaviorFlags, &d3dpp, &pd3dDevice ) ) )

    I tried the exact same code in a different project without any injection or anything and it works just fine. However after being injected in any other DX9 application it freezes over there.

    The sequence that happens in my code is this:
    The dll is injected.
    It starts in DllMain and afterwards t goes straight to the function where it gets the address of the VMTable.
    Creates a D3D9 object.
    Creates a window exactly like you have it in the code.
    Initializes the device data.
    The remote thread freezes at the createdevice function.

    What am I missing here?

    Thank you,

    Sherif

    1. Maybe try with the D3D9 debug layer enabled and see if anything sticks out. I remember having to use D3DDEVTYPE_NULLREF or D3DDEVTYPE_SW in the past although I can’t remember the circumstances, it is worth a try however.

      1. I tried NULLREF and REF and they both also freeze in an infinite loop. SW requires you to pre-specify a SoftwareDevice with IDirect3D9::RegisterSoftwareDevice.

        I will try the D3D9 debug layer and get back to you.

        Thanks for the quick reply.

      2. With D3D9 debug layer being used and the debug output level set to high and with maximum validation the thread still freezes at CreateDevice.

        When I am checking “Break on D3D9 Error” the thread breaks.

        I have got a question, when you are using EasyHook, the function that gets the vtable and creates the dummy device, does it run on a secondary thread or on the main thread of the application?

      3. The problem was in that I was calling CreateDevice function from DllMain which according to MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/dn633971%28v=vs.85%29.aspx) can’t be done, anything that might have threads crossing will cause deadlock.

        So I followed the solution mentioned here: http://stackoverflow.com/questions/1162050/createremotethread-loadlibrary-and-postthreadmessage-whats-the-proper-ipc-me/1163681#1163681 and that solved it.

        Thanks again Justin.

        Sherif

  28. Hello,

    One more question, when the app I am hooking to is in full screen the device creation fails with debug messages saying:

    Direct3D9: (INFO) :======================= Hal HWVP device selected

    Direct3D9: (INFO) :HalDevice Driver Style b

    Direct3D9: (WARN) :Hwnd 00320462 no good: Different Hwnd (0014069a) already set for Device
    Direct3D9: (ERROR) :SetCooperativeLevel returned failure. CreateDevice/Reset Failed
    Direct3D9: (ERROR) :Failed to initialize primary swapchain
    Direct3D9: (ERROR) :Failed to initialize Framework Device. CreateDeviceEx Failed.

    Direct3D9: :intra-thread window unhook, letting window proc do it
    The thread ‘Win32 Thread’ (0x167c) has exited with code 0 (0x0).
    The thread ‘Win32 Thread’ (0x1614) has exited with code 0 (0x0).
    The thread ‘Win32 Thread’ (0x1bc8) has exited with code 0 (0x0).
    D3D9 Helper: IDirect3D9::CreateDevice failed: D3DERR_INVALIDCALL

    I am unable to reach the cause of this error. I tried multiple different methods and I still reach it, weird thing about it is I used your C++ code for device creation and I still get it.

    Any ideas?

    1. My apologies. I changed so much trying to fix the bug that I had in DllMain that I wasn’t using Display mode for back buffer format, which caused this error. It all works now.

      Again thank you and sorry for the spam.

      Sherif

  29. Is there any way to do this on a wpf uielement (usercontrol) like RenderTargetBitmap does which is way to slow ? Basically not capturing the screen but the user control off screen.

    1. If it uses D3D to render you could yes. I’m not sure whether that element does so or not, you might be better off capturing region of screen (BitBlt / GDI+) for this one.

  30. Hi, Exelect work!! and thanks for share this, i have one question because i’m not an expert on this. How i can capture the screen if the process id have 2 windows screen, i try to do, but ever i get only the main screen, but i don’t know how to get the second screen. i know the HWND id of the second screen but i don’t know where i can use…
    Thanks in advance for your answere, and sorry for my bad english…

    1. Haven’t tested this but I’m fairly certain you just pass through the 2nd hwnd instead of the main. Check for where I pass through the main window handle in the example and replace with the desired handle.

Leave a Reply

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