C# – Screen capture and Overlays for Direct3D 9, 10 and 11 using API Hooks

So it’s been almost a year and I have finally got around to finishing a new version of my screen capture project that supports Direct3D 9, 10, and 11! This solution still uses SlimDX for the Direct3D API wrapper along with EasyHook to perform the remote process hooking and IPC between the host process and target process.

Some of the changes since the previous version:

  1. 100% C# implementation
  2. Added Direct3D 10 and 11 support
  3. Capturing multi-sampled/anti-aliased images (for 10 & 11) is supported
  4. Re-organised code making it easier to support multiple D3D versions
  5. Implemented a new and improved test bed application
  6. Provided example overlays for D3D 9 and 10
  7. Improved debug messaging from Target application to Host (mostly removed when compiled with “Release” configuration)

Update 2012-04-14: code now hosted on Github

Prerequisites

Like the previous post you need the SlimDX June 2010 SDK or Runtime, and it is useful to have the DirectX SDK handy to try screenshots on their samples.

The download already includes the EasyHook binaries, but if you want to download them yourself you can find them at CodePlex here.

C# only Implementation

Previously I had a C++ helper DLL to get the VTable addresses.

This has been replaced with the following C# implementation:

        protected IntPtr[] GetVTblAddresses(IntPtr pointer, int numberOfMethods)
        {
            List vtblAddresses = new List();

            IntPtr vTable = Marshal.ReadIntPtr(pointer);
            for (int i = 0; i < numberOfMethods; i++)
                vtblAddresses.Add(Marshal.ReadIntPtr(vTable, i * IntPtr.Size)); // using IntPtr.Size allows us to support both 32 and 64-bit processes

            return vtblAddresses.ToArray();
        }

Adding Direct3D 10 and 11 Support

Direct3D 10 and Direct3D 11 have a completely different rendering pipeline compared to Direct3D 9, making use of the DirectX Graphics Infrastructure (DXGI). We now hook the IDXGISwapChain.Present method to capture in D3D 10 & 11.

The capture method used in Direct3D 10 and 11 is also more thread-safe allowing us to extract data from the resulting texture on a background thread. This reduces the impact to the frame rate.

We are also able to copy only the region of the backbuffer that we are actually interested in – making the capture of smaller regions quicker as less data needs to be copied to system memory. This also will make capturing a sequence of frames for use in video production much easier.

Note: I believe that D3D9Ex (Vista+ shared surfaces – e.g. DWM) also uses the DXGI Present but haven’t looked into this in greater detail as the EndScene hook in Direct3D 9 achieves what we are after also.

Support capturing Multi-Sampled (anti-aliased) Images

Direct3D 10 and 11 both provide the ResolveSubresource method (Direct3D10.Device.ResolveSubresource, and Direct3D11.Device.ImmediateContext.ResolveSubresource), that makes it easy to resolve a multi-sampled texture down into a single sample for copying into a Bitmap.

D3D 10 code:

    // If texture is multisampled, then we can use ResolveSubresource to copy it into a non-multisampled texture
    Texture2D textureResolved = null;
    if (texture.Description.SampleDescription.Count > 1)
    {
        this.DebugMessage("PresentHook: resolving multi-sampled texture");
        // texture is multi-sampled, lets resolve it down to a single sample
        textureResolved = new Texture2D(texture.Device, new Texture2DDescription()
        {
            CpuAccessFlags = CpuAccessFlags.None,
            Format = texture.Description.Format,
            Height = texture.Description.Height,
            Usage = ResourceUsage.Default,
            Width = texture.Description.Width,
            ArraySize = 1,
            SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0), // Ensure single sample
            BindFlags = BindFlags.None,
            MipLevels = 1,
            OptionFlags = texture.Description.OptionFlags
        });
        // Resolve into textureResolved
        texture.Device.ResolveSubresource(texture, 0, textureResolved, 0, texture.Description.Format);
    }

Direct3D 9 and 10 Overlays

I have implemented two sample overlays for Direct3D 9 and 10.

The D3D 9 example displays the frame rate and draws a box with a cross in the middle to identify the region that was captured – this fades after 1sec.

#region Example: Draw Overlay (after screenshot so we don't capture overlay as well)

#region Draw fading lines based on last screencapture request
if (_lastRequestTime != null && _lineVectors != null)
{
    TimeSpan timeSinceRequest = DateTime.Now - _lastRequestTime.Value;
    if (timeSinceRequest.TotalMilliseconds < 1000.0)
    {
        using (Line line = new Line(device))
        {
            _lineAlpha = (float)((1000.0 - timeSinceRequest.TotalMilliseconds) / 1000.0); // This is our fade out
            line.Antialias = true;
            line.Width = 1.0f;
            line.Begin();
            line.Draw(_lineVectors, new SlimDX.Color4(_lineAlpha, 0.5f, 0.5f, 1.0f));
            line.End();
        }
    }
    else
    {
        _lineVectors = null;
    }
}
#endregion

#region Draw frame rate
using (SlimDX.Direct3D9.Font font = new SlimDX.Direct3D9.Font(device, new System.Drawing.Font("Times New Roman", 16.0f)))
{
    if (_lastFrame != null)
    {
        font.DrawString(null, String.Format("{0:N1} fps", (1000.0 / (DateTime.Now - _lastFrame.Value).TotalMilliseconds)), 100, 100, System.Drawing.Color.Red);
    }
    _lastFrame = DateTime.Now;
}
#endregion

#endregion

The D3D 10 example simply displays the current date and time:

#region Example: Draw overlay (after screenshot so we don't capture overlay as well)

using (Texture2D texture = Texture2D.FromSwapChain(swapChain, 0))
{
    if (_lastFrame != null)
    {
        FontDescription fd = new SlimDX.Direct3D10.FontDescription()
        {
            Height = 16,
            FaceName = "Times New Roman",
            IsItalic = false,
            Width = 0,
            MipLevels = 1,
            CharacterSet = SlimDX.Direct3D10.FontCharacterSet.Default,
            Precision = SlimDX.Direct3D10.FontPrecision.Default,
            Quality = SlimDX.Direct3D10.FontQuality.Antialiased,
            PitchAndFamily = FontPitchAndFamily.Default | FontPitchAndFamily.DontCare
        };

        using (Font font = new Font(texture.Device, fd))
        {
            DrawText(font, new Vector2(100, 100), String.Format("{0}", DateTime.Now), new Color4(System.Drawing.Color.Red));
        }
    }
    _lastFrame = DateTime.Now;
}

#endregion

Unfortunately Direct3D 11 does not provide any support for Direct2D / Fonts for our overlay, so you would need to create a D3D 10 device with a shared texture, and blend this into the D3D 11 backbuffer (see http://forums.create.msdn.com/forums/t/38961.aspx and http://www.gamedev.net/topic/547920-how-to-use-d2d-with-d3d11/).

Issues

  1. I have found that often a target 64-bit process will hang if your host application (e.g. the test bed) closes before the target process, I could not determine what the cause was, but I’m guessing it has something to do with EasyHook  (unconfirmed)
  2. No example overlay for Direct3D 11 yet
  3. If you try to inject / capture using the wrong Direct3D version you could crash your host or target application
  4. Load test in D3D11 would be slow if host application (e.g. test bed) does not have focus – not sure where the delay is there

Some Numbers

Direct3D 9 – SDK Sample Blobs (640×480)

  • Time spent in render pipeline ~8ms
  • Total time to request and retrieve a capture ~50ms

Direct3D 10 – SDK Sample CubeMapGS (32-bit) (640×480)

  • Time spent inside render pipeline ~1-2ms
  • Total time to copy backbuffer, copy result into system memory and send back response ~70ms
  • Total time to request and retrieve a capture ~100-130ms

Direct3D 11 – SDK Sample SubD11 (640×480)

  • Time spent inside render pipeline ~1-2ms
  • Total time to copy backbuffer, copy result into system memory and send back response ~70-80ms
  • Total time to request and retrieve a capture ~150-200ms

Examples

Direct3D 9: SDK Sample Blobs

D3D9 Blobs Overlay and Capture

Direct3D 10: SDK Sample CubeMapGS

D3D10 CubeMapGS Overlay
D3D10 CubeMapGS Capture

Direct3D 11: SDK Sample SubD11

D3D11 SubD11 Capture

Download

You can access the source on Github here

It’s also recommended you read the previous post

240 thoughts on “C# – Screen capture and Overlays for Direct3D 9, 10 and 11 using API Hooks

  1. Excuse my silly question but do you think it can capture the whole desktop ? I just come to the illution that the dwm.exe or somewhat is a d3d10.1 application (in some way) with the responsibility to draw the screen display frame buffer…in win7. it’s not correct, or not?may i have your op?

    1. Not a silly question at all.

      My guess is that yes this would be possible, somehow :)

      I have created a quick Direct3D 10.1 version of the hook and disabled the bring to front and can successfully hook the DWM process, however as yet I have no images captured.

      I’m going to take a closer look at the source code in the following link (a DWM Win 7 Hack demo) and see what methods they are hooking in the 10.1 device. http://www.youtube.com/watch?v=1lLgUqGjDBk

      Not sure when I will get to it due to current busyness tho.

      Cheers,
      J

      1. Hell,
        I’d very interested in desktop capture using this method as well. I’m investigating low-latency desktop capture like this – lots of exciting possibilities…

      2. Hello, Not sure if you check this but im using your class and im trying to save the captured data to video however there isnt a function to get a continuus stream so im having to to constantly call getscreenshot which is an issue because the frame rate is all messed up and it causes the program to crash. Is there a way i could record the data thats captured? Thanks Reece.

        1. Yes you can do this by modifying the injected code to implement additional logic during frame capture (i.e. within the Present hook).

          As you have found, capturing frames and passing along an IPC channel for the purpose of real-time video creation is far too resource intensive.

  2. Hi there,

    I tried to change your code, so I can hook the DirectXDevice9::Present function, but if i try this, the injected program always crashes. The reason for catching Present is because there might be multiple EndScenes before the final scene is rendered, so this seems to be the way to go.

    I’m pretty stumped here, even an empty function just returning the return value of Device.Present leads to a disaster.

    Did you try something similiar yourself or do you have any hints for me?

    1. Hi Michael,

      Yes I have been able to hook Present. Can you post the code you are using including method signature for Present?

      Does it crash when trying to hook, or when the hooked method is called?

      Cheers,
      J

      1. Hi Justin,

        thanks for your fast answer. I posted the relevant code parts here: http://nopaste.info/bbc64b7673.html

        I forgot to include the delegate, but it corresponds to the function definition, which I just checked.

        It seems to crash after the hook is called. I also tried to just return 0 from the hook, but the result is the same.

        1. Hi Michael,

          I’m pretty sure you need to be using Int32 in your RECT structure not Int64 (check pinvoke.net or SlimDX has a wrapper/converter), everything else looks good to me.

          Let me know how you go. I’m not on my dev machine and I’m in the middle of moving it to a new machine so can’t test right now.

          Cheers,
          J

          1. After I had some problem yesterday with my assembly not being updated in the GAC, this is what I’m confronting today:

            STATUS_INTERNAL_ERROR: Unknown error in injected assembler code. (Code: 5)

            This also happens with your unmodified code and is an exception thrown by RemoteHook.Inject.

            Damn it!

          2. Hi MIchael,

            When you are starting to have issues with GAC etc, I usually try to find a sample (e.g. Direct3D SDK sample) that I can copy to the bin directory of my project (or copy all my binaries to the destination application directory), and run without having to install anything other than the EasyHook assembly to the GAC. Otherwise you can spend quite a bit of time debugging issues that are due to out of date assemblies and so on.

            It is also much easier to attach to the target process for debugging if the injected assembly is not in the GAC.

            Good luck. If you continue to have issues you can contact me via the contact page and subsequently email through a project or something.

            Cheers,
            J

          3. Hi Michael,

            Try the following:

                    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
                    delegate int Direct3D9Device_PresentDelegate(IntPtr device, ref RECT sourceRect, ref RECT destRect, IntPtr destWindowOverride, IntPtr pRgnData);
            
                    int PresentHook(IntPtr devicePtr, ref RECT sourceRect, ref RECT destRect, IntPtr destWindowOverride, IntPtr pRgnData)
                    {
                        using (Device device = Device.FromPointer(devicePtr))
                        {
                            //this.DebugMessage("PresentHook");
                            using (Region region = Region.FromHrgn(pRgnData))
                            {
                                return device.Present(Rectangle.FromLTRB(sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom), Rectangle.FromLTRB(destRect.left, destRect.top, destRect.right, destRect.bottom), destWindowOverride, region).Code;
                            }
                        }
                    }
            

            Cheers,
            J

          4. Hey Justin,

            I’ve been trying to recored the present ex hook to no avail I wondered if you had tips or ideas on how to do it it’s been bugging the hell out of me for days!

            Thanks!

  3. If I inject it, the app stucks.
    It seems that he cant find the process, but the process is open and I’ve written the right name( Wow.exe).
    He is hanging in the !newInstanceFound loop.

          1. After chatting with Justin, we have found the problem.
            You must type the process name without extensions.
            So not Wow.exe but Wow.

  4. I simply try ti run TestScreenShot.exe as administrator and it fails all the time. It hangs and the window title shows “Not responding”.
    I am sure I installed Easy Hook, and SlimX.
    What I need to do to get this sample to work?

    Do you have another sample that allows TEXT output on the screen?

    I appreciate your work, effort and help.
    Thanks.

    1. 1. Make sure you don’t have “.exe” in the process name.
      2. Make sure the ScreenshotInject.dll is also registered in the GAC. I also sometimes have to reboot my machine after installing in GAC then attempt to hook.

      The DirectX 9 version has text output (frames per second). Otherwise check SlimDX examples on their website, you might find some useful bits on using SlimDX to render text there.

      Good luck!
      J

    2. i have the same question with you ,i want to know how to solve this problem if you have correct it.thank you for your reply

  5. hi,Justin.
    before i run this program,i registered the easyhook.dll,screenshotinject.dll into GAC.but the slimdx.dll couldnt registered ,its give the message “Unknown option:filesslimdx”.i have install the slimdx SDK and slimdx.dll exsist in the install directory.

    then i reboot my machine,and run your program use ParallaxOcclusionMapping.exe for example ,the UI only display messages as below:

    2904:DXHookD3D9: Hook: Device created
    2904:DXHookD3D9: Hook: Before device creation
    2904:DXHookD3D9: Hook: Begin
    2904:64-bit Process: False
    2904:DLL Injection succeeded

    why 64-bit process :false,and how can continue to use your program,look farward to your reply

    1. Hi Chen,

      That is all working fine, it just means that the ParallaxOcclusionMapping.exe is a 32-bit application.

      Try taking a screenshot from there, and let me know if the overlay is appearing for you.

      If it doesn’t work from here, try the other D3D version options.

      Cheers,
      J

      1. i run this program in two conditions .one is win 7 ,in this condition the program works fine and i can run directx sdk samples in Direct3D10.another condition is window xp professional 2002,in this conditions there have some problems as follows:
        when i click the inject button ,its display an error dialog ,the title is “a client process (3868) has reported an error…”,the content is “system.runtime.interopservices.sehexception:
        in createdxgifactory(_guid*,void**)
        in slimdx.dxgi.factory..ctor()
        in screenshotinject.dxhookd3d10,hook()”
        directx sdk samples in Direct3D10 didnt work,it shows “Could not initialize direct3d 10.this application requires a direct3d 10 class device (hardware or reference rasterizer)running on windows vista(or later)
        Is my os version wrong?
        Only os version is different in This two condition

          1. hi justin,thank you very much to anser my question patiencly,now i have changed my os to win7,but when i clicked the inject button ,it shows that “shadowmap,exe have already stopped work”,another examples have the same conditions ,i am comfused.i installed Microsoft DirectX SDK (June 2010),and my graphics is intel g41 express chipset

          2. Hi Chen, I’m not really sure what is happening here, I can’t remember getting that message at all. When you run shadowmap.exe without trying to inject is it working correctly? Perhaps you could use the contact page to send through PM details or so that I can request some screenshots.

            Cheers,
            J

          3. hi justin,now the progarm can work now,i install the related softwares again.so it can work.thanks for guiding

    1. I think it’s possible, but you would need to do a fair amount of memory checking, or debugging using something like OllyDbg to determine where you might be able to hook to get this information. I don’t really have the skills or experience in this area, so if you’re interested in this type of thing you should take a look over at http://gamedeception.net

      Cheers,
      J

  6. Is the captured screen shots supposed to appear on the dialog box itself? Or it is saved in a file? If on a file, where should I find it? I was able to run the injection successfully, however I never seen the captured screen images yet.
    Thanks.

    1. It appears in the dialog box itself.

      Play with the different DirectX versions, and if that still doesn’t get you anywhere you will be able to remotely debug the injected process and add breakpoints in the Screenshot.dll project (just untick “Just My Code” in the debugger settings).

      Process explorer (http://technet.microsoft.com/en-us/sysinternals/bb896653) is a great tool for checking which D3D dll’s are being used.

      Cheers,
      J

  7. Hi

    I’m looking for a good method of capturing screen with a great/best as possible performance from MPC-HC latest one version (where is a problem to capture screen from .NET – blank screen on EVR surface) or from older one 1.2.1008.0 where I’m able to capture from EVR surface even GetBackBuffer(1), .NET and GDI32.

    Unfortunatelly in Windows 7 (only, Windows Vista works fine!) with two displays connected I cannot use GetBackBuffer method anymore. Reason? When MPC-HC 1.2.1008.0 is playing video on a primary screen through EVR DX Fullscreen surface it works fine, but when it is playing this movie a on a right, second screen (secondary-extended desktop), than I see some strange on a captured image – video picture from player is shifted to the right with width from primary screen (but in real video is located on a second screen). I know it’s hard to understand, but let me desribe:

    Primary Secondary
    1680 1920
    ——- ———-
    #### xxxxxxx->shifted by 1680
    ——- ———-
    1050 1080

    But DX GetBackBuffer captures picture from 2nd monitor shifted by 1680px to the right so it’s something like that:
    http://qnapclub.pl/qnas/epiLightDX/Windows_7/bug.jpg

    I’m working on some DIY Ambilight project and I’m still looking for best way to capture screen. I see you are best of best guys in a screen capturing so I would like to get advise from you guys or even some perfect source code to do this magic trick :)

    Thanks and Best regards

    1. Btw – there are some other comments in this blog somewhere of some Ambilight stuff, they had a URL to their modified screen capture taking into consideration anti-aliasing as well. Also they were simply scaling the image down to approx. a 4×4 pixel image which made it super fast (since it was only for ambilight).

      The URL is: http://gathering.tweakers.net/forum/list_message/34384265#34384265 (site is in Dutch I think) and you are looking for posts by Gerrit.

  8. Thank you for an answer.
    “Try specifying the coordinates for the capture with this in mind, does it then work correctly?”
    Correctly for a first few seconds (~10sec.) only or shorten when LB mouse clicks on some Window(/Explorer) which will shift video image.

    Do you understand Dutch? Can you explain me to understand what’s different between these two:
    http://gathering.tweakers.net/forum/list_message/34384265#34384265
    and
    http://gathering.tweakers.net/forum/list_message/34390719#34390719

    Do you know if there is a full working source corde to capture the screen (even to capture one frame) by this method? I would like to see proper use of this method. I’m convinced that you are an expert in screen capturing with .NET C#, but I do not want to ask you for detailed help in this issue and I don’t want to waste your time, because that blog and published news in it are truely amazing! I’m really impressed. Respect bro!

    Thanks once again!

    1. Use google translate or something similar to translate them. I don’t think there is any real functional difference between the two. The second one recreates the render target each time which would probably mean no other resizing checks are required in the Device.Reset.

      Both of those links are modifications of the Direct3D 9 hooking I have here on this site.

      I might take a look at MPC-HC myself and see what’s going on. But not sure when I will get around to it.

      Cheers,
      J

    2. Hi,

      I have taken a look and the black area you are referring to is probably because the device created by the MPC-HC app is of that size and displaying black around the video it is playing back.

      In my tests at fullscreen on my second display it would capture fine. The capture would only include the actual video picture even though the video playback had a little extra black due to different aspect ratio of video to screen.

      In windowed mode there is a small black area captured at the bottom of the screen (behind where the video playback controls appear). Or if I have the window sized incorrectly for the image aspect ratio the black would appear elsewhere as appropriate.

      In windowed mode you will find that it is capturing the entire image including black, so you would need to crop out the black area, unfortunately this is just the way the MPC-HC app is rendering to the DirectX surface.

      In fullscreen you shouldn’t have any issues – or at least I could not duplicate the same thing you say you are experiencing in fullscreen.

      Cheers,
      J

  9. I was getting GAC issues as well, downloading the latest EasyHook binaries from EasyHooks website fixed the issue for me. Just letting anyone else having issues know.

  10. Thank you Justin for everything, but I spent last 20 hours with non-sleep and just looking and trying to make screen capture working through GetBackBuffor, but unfortunatelly output is alwayas BLACK.

    If anyone would like to check this:
    http://pastebin.com/8D8nGqu2

    Please! Thanks!
    Going get some rest now :)

    1. Hi,

      You cannot capture the backbuffer from outside of the process that creates the Direct3D device (unless it does some stuff with cooperative mode).

      This is why it is always black for you in that sample code. My code gets around this by hooking the target process and running the capture from within the same process.

      Try this: copy the TestScreenshot.exe along with the rest of the bin folder of this sample into the directory that has mpc-hc64.exe (or mpc-hc.exe I guess if 32-bit). Then try to run it, type the executable name in (without .exe) in the screenshot test bed and it should capture fine for you.

      Good luck!
      J

  11. Hi, Justin
    Please forgive me to disturb you again
    are you sure this program can work fine in windows xp.

    i want to work this in windows xp.but always if fails,i mean the captured image didnt display in the pictureBox1,

    i debug it ,i find the code behind “id3dDeviceFunctionAddresses.AddRange(GetVTblAddresses(device.ComPointer, D3D9_DEVICE_METHOD_COUNT));” didnt be implemented. i add try catch ,i display DXHookD3D9: D3DERR_INVALIDCALL: Invalid call (-2005530516).i dont know why?can you give me some information?

    i know it can work in my computer if i install windows 7.

    1. My guess is that the error is actually around the creation of the temporary device to then get the method addresses from (e.g. incorrect parameters or something).

      The best thing to do is to install the DirectX SDK and enable the debug mode in the DirectX control panel included in the SDK. This will allow you to enable tracing and be able to see the actual errors that are occurring under the hood.

      Cheers,
      J

  12. Hi Justin S.

    Well your method really works. I see you have a lot of experience and passion to create the way to capture screen from application. Can you tell us what is the reason why you do that? You helped me a lot…!!! in DIY AmbiLight! Thanks for that.

    But I like the way how “Taksi” application works. No needed to inject Hook before surface is created. How this works?

    Also I have tried your method with the game Starcraft II which uses DX9. When I set in options of the game “Fullscreen”, then your method does not work. But when I have changed to “Windowed (FullScreen)” then it works. So is there a method to make it working also for DX Fullscreen?

    Thanks and Best regards!

    1. Hi silek65,

      The main reason was because I saw it as a challenge, and wanted to solve the puzzle. But since them I have been more interested in looking at doing game overlays, and also I would like to try a DIY AmbiLight project of my own. Most importantly it is something people have found useful and I’m happy to continue working on it while that is the case.

      I’m not sure I understand your question about “Taksi”, the solution on this blog allows you to inject at any time rather than only at the start of the application.

      Fullscreen to windowed should also work fine – check if anti-aliasing is enabled in fullscreen and disable it perhaps that is the issue? Also try injecting when it is already in fullscreen and see if that works, it could be that the DeviceReset code is not getting called correctly on change of the device dimensions.

      Glad you found this useful, that’s why it is here.

      Cheers,
      J

  13. Hi Justin :)

    Okay so let me write all steps I’m doing and can’t get Inject working while the app is already run.
    (OS: Win7 x64)
    1) Started Screenshot Hook app
    2) Started mpc-hc.exe (1.2.1008.0) x86
    3) Loading and Starts playing movie
    (FIY Movie is created on a window handle called: “MPC D3D FullScreen”, rect: [1920 x 1080], (1680,0)-(3600,1080), class: Afx:00400000:b:00010021:00000006:00000000)
    4) Switched back to Inject Hook app and entered:
    EXE Name: mpc-hc
    Direct3D 9 selected
    Auto register GAC checked
    5) Pushed Inject button and log is:
    4500:DXHookD3D9: Hook: Device created
    4500:DXHookD3D9: Hook: Before device creation
    4500:DXHookD3D9: Hook: Begin
    4500:64-bit Process: False
    4500:DLL Injection succeeded

    Q1) And I cannot inject it while movie is played. So what’s wrong?

    Q2) Sometimes I see another MPC-HC surface blink for a 1 frame shot… normally it is capturing clean movie surface, but sometimes I see a fast blink with content from whole display surface including subtitles. I have also observed that this happens only when CPU is overload much – no issue while >70% of CPU resources are free.

    Q3) May you suggest me a way to make a fast blur GetBackBuffer D3Dv9 surface? I was trying to find some help on a Web, maybe sample code but unfortunatelly I did not find it.

    [quote]also I would like to try a DIY AmbiLight project of my own[/quote]
    GREAT! THAT’S AWESOME NEWS! Let me say something about my software project named epiLight for MoMoLight compatible controllers (S/W at begining based on some old source BobLight Windows source which is not actually used anymore – first time used C# – thank god for it)

    I’m implementic your fast method of screen capturing into my old-based software epiLight and I’m fixing some buggy like-a-video-processing issues to make software easier to use and manage
    …and to bring a user totally new dimension of a movie view in his own home cinema. This is done by new True-Cinema +Expansion mode which analyse input “image”/colours and dynamicly changes voodoo settings to best one for currently displayed picture.

    When epiLight software will be ready, then as always I’ll post all C# sources on a AmbiLight4PC forum – doing this to keep project alive. – where a lot of other DIY AmbiLight users are not doing that! WRRR! and this does NOT help to the project!

    Hope you’ll also enjoy it!
    You need to know that you are the GUY, who made revolutionary STEP for a DIY AmbiLight!

    Here are some detailed words about my software:
    epiLight is only one available app which supports MoMoLight V3 protocol with extra data: inverted checksum to prevent “blank blinks” on some cheap RS232USB adapters.
    It is compatible backward – 3rd party MoMoLight v1/v2 compatible software works fine on a v3 FW:
    (software sends init v3 string, device checksum flag is switched on and from now unit is expects new data format from a software)
    v2: R1R2R3G1G2G3B1B2B3
    v3: $R1R2R3G1G2G3B1B2B3CHKS [inverted: FF FF - checksum]

    ..

    Anyway in same time I’m working on a totally new app called AmbiLED, when I’m going to use your code and I guess I’ll ask you for some help
    Bellow is some my feature/working note – I didn’t read it yet …
    http://pastebin.com/wpqfH6Wq

    Hope to see you ASAP with AmbiLighters :)

    1. Q1) I can’t see what’s wrong there, it is a bit weird what is happening. Maybe the creation of the temporary device/surface is causing the issue but I really don’t know at the moment

      Q2) Again not sure ( i haven’t played with MPC-HC a great deal, but from what I had already seen it had a few weird things going on with view port placement that we discussed previously, so maybe some more such things?)

      Q3) Yes, you can scale the surface down to a 4×4 pixel image using DX (or similar), and this will give you the colour averages etc.. (is this what you are after?) This is how some other AmbiLight users used this code.

      Cheers,
      J

  14. Q3) Well, regarding to the solution with 4×4 sufrace resize I’m not really happy with it.
    Ambi is producing a lot of unwanted blinks.
    But when I’m resizing surface to, let say 192×108 and then saved into unsafe bitmap to perform faster GetPixel operation, then my average colour is more precise then mentioned 4×4 solution.

    I think D3D9 uses some fast method to perform resize operation. Looking onto output surface which is really sharpen, it seems to me that resize is done on not all pixels from original image. I believe it uses some step on pixels to perform it faster.

    I don’t know… I just would like to try a method like that:
    1) Resize > 192×108
    2) Blur with radius 15px
    3) (eventually resize again) Average GetPixels

    1. silek,

      Any luck with your implementation? Would really like to see the source code. Checked most of your posts in ambilight4pc forums, but all the download links for binaries and source are dead. Have a custom LED hardware I want to use against it. Curious how you’re handling the resize, blur, and get average pixels like you talked about above.

      1. Hi there! Sorry for the VERY late reply. I had to stop the project for a while…
        Too bad I didn’t published the source code before. I have done this just right now. So we lost few months with the project.

        I just get an email from a guy who asked me for a source code… So I just zipped all the stuff and sent him and also want to publish it here… …

        Source code of the project you can download after you read the short note I wrote: http://qnapclub.pl/qnas/AmbiLED/software/AmbiLED/20110903/IMPORTANT_NOTE.txt
        (Read before downloading or browsing zipped files)

        Source code you can find in the directory I have all files: http://qnapclub.pl/qnas/AmbiLED/
        path: /software/AmbiLED/20110903/AmbiLEDd.NETv3.src.zip

        1. Hey Silas,

          Good to see ur back around. Since you were last about a friend of mine and I have also started a small ambilight clone project on GitHub (using Arduino for the hardware), I’ll let you know when more is posted on this – we have a working prototype haven’t yet refactored the project for source release in GitHub. I’ll check out your project as well though, I don’t want to reinvent the wheel :)

          Cheers,
          Justin

          1. That’s perfect news!
            But honestly I’m little bit dissapointed, because of platform you choose… ofcourse Arduino platform is very popular. But I was thinking that maybe you’ll go another way and you’ll take a try on a platform from Microsoft .NET Gadgeteer – http://www.netmf.com/gadgeteer/ – seems it fit my requirements enough! Someday I need to try it.

            Anyway I’m waiting for even drafts of your project…

            Also I would like to say something about the code I have provided. Well :) Frankly speaking it’s a third version of the Ambilight module software controller and I need to say, that on the first one I was learning .NET platform and C# – previously I never had a chance to play with this platform. So I know that this code is full of mess and contains a lot of lamy and nooby calls.

            From this time I have spent some weeks learning C#… So now with a little bit better knowledge and experience in AmbiLight project I would like to share with you my opinnions and maybe you’ll find something interesting solution there… Please read them… BTW: Sorry for pure english I’m little bit tired while writing this…
            (let’s start with basics things…)
            1) Many DIY AmbiLight users complained about the lack of dynamic channels support. So I can only suggest you to create channel data collection. Each object should contain at least:
            – source location (screen point/100%);
            – some flags defining priority point: middle of the screen – not so important, near to bounds of the screen – important. Let’s call this for fun “smooth offset” or Gradient Importance… bleh what’s that? :>
            – current RGB values;
            – and a list of undefinied count of previous RGB values objects for next cycles work (that will clear when collecting new data will be triggered from finished Smooth-loop or from Aggresive Scene Changes Detector)

            2) Support for external inputs from other software…
            [not so important at software development time, but later will be helpful to win the market with this software]
            (in this feature I think there needs to be possibility to map by a user inputs to specified channel outputs and smart communication between X app with unknown data source and Y module with unknown LED channels)

            3) It’s very cool when software could work like a winamp – with plugins supports for inputs, outputs and maybe other …

            4) (this one will be very hard to maintance and hard to code, but…) Revolutionary in DIY AmbiLight projects that guarantees success and hope it will invite more people to the project is support for profiles created for each application – !and now attention! – with managed and definied by user a way of data interpretation and calculation for channels output values
            In this I mean, that a user can decide about each mathematical function position in the colour processing list but simple Blocks drag&drop on the application GUI.
            If the module (mathematical functions) requires another data inputs, then user must connect it from another module on the calculation work diagram.
            Module can be everything, eg.
            – parametr container (int, boolean, current RGB array, …)
            – HSV or HSL of current/previous[x] colour,
            – Nght switcher trigger
            – Expand color module with external switch (eg. from Night switcher) to change multipier to 3x during low room lightness – at night (with value changed only at begining of working state to prevent LED intensity changes – during the movie) and 3x multipier for daytime as default
            – trigger: aggressive scene change detection
            – Gamma correction (def 0.5; LCD/TV color != LED stripes color);
            – Compensation (non-white wall behind TV);
            – Color smoothing+Aggressive attacks;
            – Scene colour expand (with mentioned day/night presets)

            … I can help with that. This could be nice tool. But if we go so far, then in same time we can bring to the user empty modules with possibility to enter mathematical function by himself… And everyone one will be able to build AmbiLight color calculation method…

            .. That last point… Is it really necessary? Maybe you do not want to go this way… But shorty let me tell you story about AmbiLight DIY status…
            Tere was a lot of custom and different DIY projects with unique software.
            This is a time where is a chance to built universal software controller with non-limitted input data processing methods.
            I think AmbiLight DIY projects are dieing… Most of other software projects works terrible, some of them had flickering and some of them long time waiting for colour change and other one works like a Quake 2 on 486DX2… So this is a chance to keep this project alive and bring to the world best AmbiLight ever and even better then Philips.

            And btw… before I’ll go to the next and last point, I can only say that last modules conception comes when I saw FPS output when you bring us DX Hook method :) Let me explain you why…
            5) When you managed a way to hook DX9 and I have implement it in the software, then all I did in previous releases of AmbiLight was wrong and not properly working after this. Software was displaying color changes so fast…
            (btw I’ll forgot later, so… capturing images through DX hook method while 24Hz Refresh rate on a display device is set and movie is 23.978fps (RefreshRate=FramePerSecond) produces slower image on screen with few images per seconds only)
            Anyway at 60Hz refresh rate it was displaying so fast… 1:1 that I didn’t expected while writing smoothing calculations… So I was needed to rewrite all the stuff.

            And I found solution that was best for eye with all movie effects on a lightness effects. I think it’s is currently the best available for DIY AmbiLight. Calculation are done in this order:
            1) Input RGB values
            2) Expand colour values with multipier 3x — important it prevent from darkness low light flickering, and this will be reduced later by Gamma
            3) When aggressive attack is set 64(one RGB value difference from previous 64 (eg. R value) triggers cycle from the begining and apply current colour configuration
            4) Smooth colour radius is calculated this way: [cut from my code] http://pastebin.com/rWLEzKSp
            5) Then output RGB value goes to Gamma (0.5) – settings depends on RGB colours reproduction by AmbiLight from TV at middle colour value (128)

            And even I think this way of getting smooth colors with all movie effects replaced from screen onto AmbiLight is currently the best – flash blinkings, etc… run oldie movie Armageddon in HD – it’s one of best to perform AmbiLight tests especially at the scene moment when they countdown to the launch …
            .. is the best available becuase colour changes are easy to view at day time and night time, then I think these calculation should go away and we should take a try on the new mathematical way to reproduce perfect colours on the AmbiLight.

            I think it’s the time to bring something newer and better using more advanced mathematical functions like “Least-Squares Polynomial Approximation”, please take a look on this:
            http://www.chem.uoa.gr/applets/AppletPoly/Appl_Poly2.html – take a look on a java applet on the right side
            http://www.devx.com/enterprise/Article/44408
            –> http://www.devx.com/enterprise/Article/44408/0/page/2
            … … … and the Smooth radius (points number) (= RGB values to calculate) can be fixed number of RGB values…
            or better way can various decreasing and increasing from acceleration and deacceleration: http://stackoverflow.com/questions/4739064/acceleration-deceleration-ratio-equivalent-with-keyframe
            and this can be done from the current and previous RGB.Color -> HSV.V difference.

            I’m think that this method of smoothing colours could be best ever…
            That’s why I would like to see a software with customizable mathematical operations.

            So what you think about that?
            Sorry for the long post – I didn’t expected that it will be so long.

            Have a nice weekend!

          2. Hey Silas,

            Although we haven’t yet gone to the level of detail you have for the various algorithms, I think we have the same goals in mind.

            We actually have a FezSpider here for the .NET Gadgeteer setup, we just wanted to get the Arduino implementation finished first before we implement that one. We actually are using a stream to USB serial so any hardware solution that can read the stream will work. At the moment we are using a string of 50 “smart” LEDs, but the number and placement is configurable.

            We plan to make a pluggable architecture, but again it is early days and we just want to get it working first. This will include plugins for capture method, output to hardware etc… Your thoughts on some post processing plugins are good too, we hadn’t yet thought this through.

            We are also looking at implementing a Linux version via Mono. Of course we will not be using the DirectX capture, but will be looking at the various options available in Linux (specifically Ubuntu).

            I admire your passion on this topic, and hope we can collaborate more on it. I will let you know when our Git repository is ready.

            Cheers,
            J

          3. Hi Silas,

            Just wanted to let you know that the starting point for our Ambilight type project is up: https://github.com/frozenpickle/afterglow and http://afterglow.frozenpickle.com/

            It provides a flexible system for driving an ambilight system – regardless of what lighting system or capturing approach you use. It currently has a very primitive plug-in system to allow custom capturing methods / colour extraction / colour post processing (e.g. smoothing / gamma correction and so on) and custom outputs (e.g. arduino / preview or in future gadgeteer or other – you could even create one that creates background sounds based on colour if it was what you wanted).

            It is still early days – but you are more than welcome to take a look. At the moment you probably would require assistance to setup the software – it has a primitive preview output so that you can try it out without any hardware.

            Cheers,
            J

            p.s. I haven’t completed the DirectX capture plugin so it is not committed yet :)

  15. Hi Justin!
    At first thank you for this nice program.

    I want to make an overlay for a directx9 game.
    It works fine if the game is in windowed-mode, but if I want to inject in the game in full screen the injected dll sucks at creating the device.
    But if I inject in windowed-mode and switch in the game to full screen it works.
    Did you have a solution?

    Thanks
    Martin

    1. Hi Martin,

      Yes – I noticed I have made a mistake in the creation of the device. If you instead use something like the following in the creation of the device it should work for you.

      DXHookD3D9.cs:
      using (device = new Device(d3d, 0, DeviceType.NullReference, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters() { BackBufferWidth = 1, BackBufferHeight = 1 }))

      DXHookD3D10.cs:
      using (SlimDX.Direct3D10.Device device = new Device(factory.GetAdapter(0), DriverType.Null, DeviceCreationFlags.None ))

      DXHookD3D11.cs:
      SlimDX.Result result = SlimDX.Direct3D11.Device.CreateWithSwapChain(
      DriverType.Null,

      ….

      update: 2012-04-10: See below comment for what has to be changed in DXHookD3D10/11

      1. Hi Justin
        I have tested it on directx9 and it works.
        Thank you for your fast and good solution.

        Martin

        PS: If someone has Problems with the GAC-Auto register he should use .NET-Framework 3.5, because EasyHook has sometimes Problems with .NET-Framework 4.0.

      2. Hi Justin,

        First of all thank you for providing such an great C# code on how to do screenshot/capture and overlay in D3D 9/10/11 . It is very helpful for my work. I need to implement an overlay for Direct3D(9/10/11) game applications. It works fine if the game is in windowed-mode, but I want to inject DLL in the full screen game and make an overlay work for game in the full-screen mode.

        If I injected DLL into Windowed mode, switched from Windowed to fullscreen mode for D3D 9, it works. But if I injected DLL into full-screen game, I got error message:Device lost.

        If I injected DLL into full-screen game for D3D 10/11, the injection is successful, the game changed from fullscreen to windowed mode automatically. The overlay is still working. When I switched the game from windowed to fullscreen mode, the game crashed.

        All testing above set DeviceType as Hardware

        In order to make an overlay work for the full-screen game, I follow your instruction to change DeviceType as DeviceType.NullReference(D3D 9) or DeviceType.Null(D3D 10/11), then tested the program again.

        For D3D 9, injection of DLL into fullscreen game works, but no overlay presents. API hooking doesn’t work.
        For D3D 10/11, injection of DLL into fullscreen game makes full-screen game application crash.

        My platform: Win 7/ 64bit laptop, D3D engine version–9/10/11
        Could you help me to confirm if it is possible to make an overlay work for full-screen game in three D3D version(9/10/11)?
        If yes, what is the possible mistakes in my program based on your experience? what is the correct direction or solution for me to fix the bugs ?

        I really need your help to continue the work.

        Jianming

        1. Hi Jianming,

          For D3D10/11 change the DeviceType back to what it was before then do the following changes to PresentHook to make fullscreen capture work:

          DXHookD3D10.cs (change start of PresentHook):

                  private SwapChain _swapChain;
                  private IntPtr _swapChainPointer;
          
                  /// <summary>
                  /// Our present hook that will grab a copy of the backbuffer when requested. Note: this supports multi-sampling (anti-aliasing)
                  /// </summary>
                  /// <param name="swapChainPtr"></param>
                  /// <param name="syncInterval"></param>
                  /// <param name="flags"></param>
                  /// <returns>The HRESULT of the original method</returns>
                  int PresentHook(IntPtr swapChainPtr, int syncInterval, SlimDX.DXGI.PresentFlags flags)
                  {
                      if (swapChainPtr != _swapChainPointer)
                      {
                          _swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr);
                      }
                      SwapChain swapChain = _swapChain;
                      //using (SlimDX.DXGI.SwapChain swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr))
                      {
          

          DXHookD3D11.cs (change start of PresentHook):

                  private SwapChain _swapChain;
                  private IntPtr _swapChainPointer;
          
          
                  /// <summary>
                  /// Our present hook that will grab a copy of the backbuffer when requested. Note: this supports multi-sampling (anti-aliasing)
                  /// </summary>
                  /// <param name="swapChainPtr"></param>
                  /// <param name="syncInterval"></param>
                  /// <param name="flags"></param>
                  /// <returns>The HRESULT of the original method</returns>
                  int PresentHook(IntPtr swapChainPtr, int syncInterval, SlimDX.DXGI.PresentFlags flags)
                  {
                      if (swapChainPtr != _swapChainPointer)
                      {
                          _swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr);
                      }
                      SwapChain swapChain = _swapChain;
          
                      //using (SlimDX.DXGI.SwapChain swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr))
                      {
          
          1. Justin,

            I have made a progress for the injection of the full-screen game.Thank you very much.
            But the problem still existed for switching between full-screen and windows mode. When
            I switched from fullscreen to windowed mode, the game was crashed by the injected DLL.
            The reason is from the release of reference counts for SwapChain object or its pointer.
            SlimDX doesn’t provide a good method to release the reference counts/resources automatically.
            I try the methods of Dispose() and Marshal.Release(). But both couldn’t keep the game not crashing by SwapChain object..
            Do you have any suggestion for solving the probelm(Release COM reference counts/resources correctly).
            Regards,

          2. Hi Jianming,

            Try keeping an additional reference to the swap chain and see if you no longer get any crashes, if that is the case it probably means that SlimDX is causing the COM object to release before the main application is finished with it, which I guess would also mean SlimDX isn’t incrementing the usage count when wrapping the COM object initially. Anyway try this first to see how it behaves.

            Cheers,
            J

          3. Hi Jianming,

            I have just done a test with the CubeMapGS Direct3D 10 sample, and using the test capture project I was able to hit the Load Test, and switch to and from fullscreen without any issues.

            Could you confirm it works for you with the D3D SDK samples?

            Cheers,
            Justin

  16. Hi Justin,

    Good job! I am interested in this tool and make some experiments on it. But I met a problem similar to Michael. When I hook the Present() method in an x64 D3D application, the target is crashed and the injection is never invoked (Program Files (x86)Microsoft DirectX SDK (June 2010)SamplesC++Direct3DBinx64Instancing.exe). But it works well on x86 D3D application if I use the commented “return device.Present().Code” instead of the uncommented piece of code. Any idea on this?

    Source code: http://nopaste.info/398612bf51.html

    1. Hi Superymk,

      Do other hooks work? E.g. try to hook the EndScene method.

      Also try to confirm where it crashes, is it while it is creating the hook, or is it within the new Present method? Output to a debug log or something to confirm.

      Cheers,
      J

      1. If I remove the Present() hook, then other hooks work pretty well. I’ll try to debug it today. Thank you very much!

        1. If that’s the case, it sounds like the parameters being sent to the SlimDX Present call could be the issue. I would take a look at them carefully and see if there is something amiss there.

          Cheers,
          J

          1. Thank you very much. I’ll send you the debug information I get. I use Win7 x64 Ultimate. The version of D3d9.dll is 6.1.7600.16385. Though it works well when using the commented “return device.Present().Code” for x86 applications, I think it will lead to performance degradation.

          2. When hooking on the Present() method (the same code for Michael) in an x64 application, the target process crashed. The output of Direct3DHook is:

            3704:DXHookD3D9: Hook: End
            3704:DXHookD3D9: Insert Present Hook
            3704:DXHookD3D9: Hook: Device created
            3704:DXHookD3D9: Hook: Before device creation
            3704:DXHookD3D9: Hook: Begin
            3704:64-bit Process: True
            3704:DLL Injection succeeded

          3. For the issue on Win7 x64, it seems to be something wrong occurs in EasyHook. I try to read the disassembly code and find that a jmp is placed in the beginning of Present(). But the PresentHook() function is never called.

  17. Justin,

    First off thanks for putting this together, I’ve been looking for a way to do this and came to know this is not an easy task.

    I’m able to run your code just fine, however coming across a scenario where after hooking into a full screen directx app I’m no longer able to ALT+TAB out at all. Even Ctl+Del or Win key do not work. It seems that that window is stuck in foreground no matter what after hooking.

    Were you experiencing the same thing? Is there any way to address this?

    Thanks in advance.

    1. Hi Alex,

      Yes I have experienced this, and had found a way around it by changing foreground focus within the host application while hooking. That mechanism should still be there…

      Not that this helps you out much, but to fix it manually exit fullscreen with Alt+Enter and re-enter again, that used to sort it out for me.

      I’m starting to have vague recollections of sorting out some fullscreen issues in my local version of the code, not sure that it was related to this issue tho. I’ll wait and see how you go.

      Cheers,
      J

  18. Fix to capture dx9 with multisample, just replace de using for this one:

    using (Surface backBuffer = device.GetBackBuffer(0, 0))
    {
    if (backBuffer.Description.MultisampleType != MultisampleType.None)
    {
    Surface multiSampleBuffer = Surface.CreateRenderTarget(device, backBuffer.Description.Width, backBuffer.Description.Height, backBuffer.Description.Format, MultisampleType.None, 0, false);

    device.StretchRectangle(backBuffer, multiSampleBuffer, TextureFilter.Linear);

    device.GetRenderTargetData(multiSampleBuffer, _renderTarget);
    }
    else
    {
    // 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
    ProcessRequest();
    }

  19. I’m having a problem with your samples. They work fine until I resize the window (not fullscreen, just drag it a little larger), then the FPS counter disappears and screen capture stops working; it’s as if it “loses” the EndScene hook.

    I was trying it against the various D3D9 examples in the DirectX SDK. Any thoughts?

    Also, would there be a large benefit to caching/storing off the device and not creating it on every EndScene call?

    1. Hi Randell,

      I’ll take a look at why it isn’t working on resize for you. I seem to remember fixing something here ages ago.

      We aren’t creating a new device in each EndScene, just a new reference to it in SlimDX. There is potentially a performance improvement by caching it, but you would have to also be careful to check that the IntPtr of the SlimDX device wrapper still matches the one being passed in with the call to EndScene, and of course clean it up properly. I’m guessing that any performance improvement will be fairly minimal, but that still might make a difference for something that is called so frequently.

      Cheers,
      J

  20. Hey Just, I would appreciate if you could help me with a small code here. I need help updating the text of the GUI via a request from the form. I cant seem to figure it as the whole request method is a bit to complicated for me. I simply want to type in a textbox “hi” and when clicking a button on the form send a request through the screenshotmanager.cs file and have the program change the font text to hi. I know this is possible and probably very easy, I just cant seem to figure out how to do this. Thanks!

    1. Hi Peter,

      The easiest way I think would be to modify the ScreenshotInterface.ScreenshotInterface class, to include a property that is set in your form such as “DisplayText”, and a method such as “GetDisplayText()” (it will most likely work just with a property with a getter and setter actually) that is called from within the ScreenshotInjection.cs Run(..) method (within the while loop).

      You will probably need to use a lock around the read and write of the string property.

      I hope that helps out.

      Cheers,
      J

  21. Just got this to work in my XNA game I’m developing. Had to run as administrator, set auto register GAC, and put the name of the application in without extensions. It displayed FPS and took a nice picture.

  22. I`m a newbie to programming. Just started to play with VS 2010 tried opening and playing around with dx sdk samples. Checked out nvidia`s parallel nsight 2.1. Ultimately what I am looking for is checking out geometry and textures of a new d3d11 game Battlefield 3. Would there be any chance to put a step by step guide on using your method?

    Thank you

  23. I am a newbie to programming. Just started playing with VS 2010.
    I am interested in geometry and textures of a d3d11 game Battlefield 3.
    Any chance of step by step tut on using you method?

    Thanks

    1. Hi Dainius,

      If you follow through the previous post on hooking Direct3D 9, and then apply what you learn there to this post with Direct3D 10 you should be able to find your way into the depths of hooking the Direct3D pipeline. I’m no expert at programming for Direct3D and there are plenty of other resources out there that will teach you what you need to know on that matter.

      Cheers,
      J

      1. Hey,
        Thanks for the input, I am working on it.
        Btw as I have mentioned above I tried Nvidia`s Nsight 2.1. And this application does part of the job I need. It injects graphics analyzing tools in to the d3d11 game and you can capture and examine a lot of things(which most of them I don`t understand ;] ) it displays captured geometry and textures. So my next question is… is there a way to save assets displayed in the Nsight`s viewport. I mean 3d geometry to a usable format and textures to image files with transparency channels and all. Like I said I am total newbie at VS and it looks like a totally new world to me so please don laugh if I am not getting the main concepts of the program ;]]]

        Thanks again for your time ;]

  24. Hi Justin!

    I would like to use your project, to create an overlay for a directx9 game.
    It all works fine, but sometimes i have problems to uninstall the hook and the overlay doesn’t disappear.
    Do you have a solution or does anyone have the same problem?

    Thanks
    Martin

    1. Hi Martin,

      I have seen this happen although it is usually with a 64-bit process. Probably the best approach would be to send a flag that stops the overlay showing before attempting to uninstall the hook (i.e. the overlay checks the flag each time before doing what you need to do show it). At least this way the overlay will not be shown if the hook uninstall fails.

      I don’t have any solution to the hook uninstall I’m sorry.

      Cheers,
      J

      1. Hi Justin,
        Thanks for your fast answer.
        I have a 32-bit-OS.
        I allready made something like a flag, but also if i stop drawing the overlay, it doesn’t disappears all times (but mostly).
        I only asked, because i want to know if there is a better solution, but so I will use the old solution.
        Thank you!

        Martin

  25. Thank you for sharing this code. Very tricky stuff and I haven’t come across anything that works nearly as well.

    But can anyone think of a straightforward way to *programatically* figure out which version of directx a target process is using — so that it does not have to be manually set by user?

    1. Hi mouser,

      Yes, I do this in my local version now. Here is a snippet of what I do (you will notice I no longer pass the directx version from the host application).

      public void Run(
                  RemoteHooking.IContext InContext,
                  String channelName)
      {
          // NOTE: We are now already running within the target process
          try
          {
              _interface.OnDebugMessage(RemoteHooking.GetCurrentProcessId(), "DLL Injection succeeded");
      
              bool isX64Process = RemoteHooking.IsX64Process(RemoteHooking.GetCurrentProcessId());
              _interface.OnDebugMessage(RemoteHooking.GetCurrentProcessId(), "64-bit Process: " + isX64Process.ToString());
      
              IntPtr d3D9Loaded = IntPtr.Zero;
              IntPtr d3D10Loaded = IntPtr.Zero;
              IntPtr d3D10_1Loaded = IntPtr.Zero;
              IntPtr d3D11Loaded = IntPtr.Zero;
              IntPtr d3D11_1Loaded = IntPtr.Zero;
              int delayTime = 100;
              int retryCount = 0;
              while (d3D9Loaded == IntPtr.Zero &amp;&amp; d3D10Loaded == IntPtr.Zero &amp;&amp; d3D11Loaded == IntPtr.Zero)
              {
                  retryCount++;
                  d3D9Loaded = GetModuleHandle("d3d9.dll");
                  d3D10Loaded = GetModuleHandle("d3d10.dll");
                  d3D10_1Loaded = GetModuleHandle("d3d10_1.dll");
                  d3D11Loaded = GetModuleHandle("d3d11.dll");
                  d3D11_1Loaded = GetModuleHandle("d3d11_1.dll");
                  Thread.Sleep(delayTime);
      
                  if (retryCount * delayTime &gt; 5000)
                  {
                      _interface.OnDebugMessage(RemoteHooking.GetCurrentProcessId(), "Unsupported Direct3DVersion, or Direct3D DLL not loaded within 5 seconds.");
                      return;
                  }
              }
      
              Direct3DVersion version = Direct3DVersion.Unknown;
              if (d3D9Loaded != IntPtr.Zero)
              {
                  version = Direct3DVersion.Direct3D9;
              }
              else if (d3D10Loaded != IntPtr.Zero)
              {
                  version = Direct3DVersion.Direct3D10;
              }
              else if (d3D10_1Loaded != IntPtr.Zero)
              {
                  version = Direct3DVersion.Direct3D10_1;
              }
              else if (d3D11Loaded != IntPtr.Zero)
              {
                  version = Direct3DVersion.Direct3D11;
              }
              else if (d3D11_1Loaded != IntPtr.Zero)
              {
                  version = Direct3DVersion.Direct3D11_1;
              }
      
              switch (version)
              {
                  case Direct3DVersion.Direct3D9:
                      _directXHook = new DXHookD3D9(_interface);
                      break;
                  case Direct3DVersion.Direct3D10:
                      _directXHook = new DXHookD3D10(_interface);
                      break;
                  //case Direct3DVersion.Direct3D10_1:
                  //    _directXHook = new DXHookD3D10_1(_interface);
                  //    return;
                  case Direct3DVersion.Direct3D11:
                      _directXHook = new DXHookD3D11(_interface);
                      break;
                  //case Direct3DVersion.Direct3D11_1:
                  //    _directXHook = new DXHookD3D11_1(_interface);
                  //    return;
                  default:
                      _interface.OnDebugMessage(RemoteHooking.GetCurrentProcessId(), "Unsupported Direct3DVersion");
                      return;
              }
      
      1. ah well i guess my code enumerating all modules (including taking a module Snapshot to handle win32 processes not found by the Processes class, that was the painful part), trying to guess the directx version based on which directx modules got loaded — is no longer need. your solution looks better. thanks for sharing that.

  26. A more serious showstopper.

    Martin in one of the comments above mentioned that the process fails on full screen directx.

    In your reply you suggested a fix:
    “DXHookD3D9.cs:
    using (device = new Device(d3d, 0, DeviceType.NullReference, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters() { BackBufferWidth = 1, BackBufferHeight = 1 }))

    DXHookD3D10.cs:
    using (SlimDX.Direct3D10.Device device = new Device(factory.GetAdapter(0), DriverType.Null, DeviceCreationFlags.None ))

    DXHookD3D11.cs:
    SlimDX.Result result = SlimDX.Direct3D11.Device.CreateWithSwapChain(
    DriverType.Null,”

    I have tried this, and while it does indeed fix Directx 9 — it seems to completely break capture on Directx 10 and 11 (i.e. not only doesnt it let fullscreen grab work, but windowed grab also seems to fail).

    This code is so close to working perfectly, but the fullscreen bug is a show stopper. Any ideas how to get it to work ? (ps. using latest SlimDx build from jan 2012).

    Again, fantastic work on this stuff.

  27. Hi Justin, first of all thanks for providing such an excellent c# code example on how to do this. I’ve actually been able to hook into a full screen 3rd PC game and display FPS (it was really cool to finally see this work!).

    I have a Win 7 64 PC, and I’m running into a snag though regarding the DX9 registration and hook processes:

    Config.Register(“ScreenshotInjector”, “MyAssembly.dll”);
    ….
    // Inject DLL into target process
    RemoteHooking.Inject(
    process.Id,
    InjectionOptions.Default,
    “MyAssembly.dll”,
    “MyAssembly.dll”,
    _channelName,
    );

    I’ve only been able to register and hook to my dll when the dll is referenced from within my VS project and output to its bin directory. I can’t seem to get this working when the dll files are moved to my application directory. I’m running as Admin, and I get the error “The system cannot find the file specified”, however, the dll is actually there. Basically, could you provide a detailed breakdown of how the Config.Register and RemoteHooking.Inject processed work in relation to the GAC, please? I apologize if this is a very noobish question.

    1. Hi Mike,

      I find the auto register to GAC to be a finicky thing…

      To have it working all you need is all the EasyHook stuff in the application directory along with your injection assembly. However I have also had hair pulling moments where it just won’t work for some reason – often a reboot would sort it for me. In the end I register them in the GAC permanently instead (except while debugging).

      When I’m debugging I copy the application I am injecting into my build output directory so everything works nicely without having to worry about getting the new assembly into the GAC each time. I also change the startup project to my injection assembly and configure the debug settings to start the application I will be injecting. I then run my TestScreenshot app to perform the injection – this gives you immediate debugging access (from the start of the “Run” function).

      I hope this helps… I am planning a new version that will hopefully streamline this all a little – but it is a little ways off yet.

      Cheers,
      J

      1. Hi Justin, I’m planning on writing a small program to capture a portion of a game’s buffer and overlay it (partially transparent and much, much larger) on the main game window. Is this article what I should reference in my attempting this, or am I in the wrong neck of the woods?

        1. Hi Devon,

          Yes, this example code will help you along the way. The only part not covered is displaying the captured image as an overlay, but everything up to the capture of it is covered.

          Cheers,
          J

          1. Thanks Justin, I finally was able to get everything working correctly. Very cool indeed, and I look forward to your streamlined version when it’s ready.

  28. Hey Justin,

    This is a great project and I’m excited to see any continued development.
    I having a problem taking screenshots in fullscreen wtih DX9 games (I have not tried DX 10 or 11).
    I always receive this exception:
    10044:DXHookD3D9: EndSceneHook: Exeception: SlimDX.Direct3D9.Direct3D9Exception: D3DERR_INVALIDCALL: Invalid call (-2005530516)

    I had found a very similar problem on gamedev here:
    http://www.gamedev.net/topic/543847-slimdx-handling-lost-devices-solved/

    But I was not able to come to any sort of fix. As previously mentioned in these comments, I did set DeviceType.NullReference when creating my device but that does not seem to make a difference.

    Any thoughts or suggestions?

    Cheers and thank you for sharing this great project.

    1. Hi Dan,

      Could you send me an email with your Direct3D9 hook code, and also let me know what game/application you are attempting to capture from. Also is it any DirectX 9 application that the error happens in? I’ll try to catch you on skype or chat to further troubleshoot.

      Thanks!
      J

  29. Hello Justin,

    My colleague pointed out to me that I was not removing my original ScreenshotInject.dll and registering the new ScreenshotInject.dll in the GAC. I did that, and things begin to work just fine.

    I now need to see if it is possible to tune this for performance. My same colleague mentioned above wrote a small modification to write uncompressed BMP files from the frame captures to disk. It takes 20-60 seconds depending on the computer we use to grab 100 frames, so for video we are looking at .6 – 5 FPS.

    If you are available on Skype to chat about that, and potentially how to make full screen capture work in DX 10 and DX 11 as well, I’d love to talk about it.

    Thank you for the communication and nice project.

    1. Dan,

      For DX9 you will want to offload the frame to a queue for background processing as soon as you have copied the frame from the DX surface. If you are saving the image to a file, DX9 includes a surface save to file method that will hopefully reduce the number of times you are copying the buffer around (in SlimDX I think this is Surface.ToFile).

      For DX10/11 you are able to copy the contents of a texture in another thread so you can create multiple textures and alternate which you are capturing into. You then queue each one up for background processing – you will have to create some logic to determine when to create more textures, and optimize how many frames/sec you are able to process.

      The process of copying the image data over IPC back to the host process can be quite expensive, so you might want to implement as much as possible within the injected DLL.

      Also the timing and image capturing logic should be implemented within the injected assembly (within the capture hook even) rather than relying on a request over IPC, the loop while waiting for image requests from the host process can be expensive (in time if not processing) – the sleep in this loop could then be increased to 500ms or so.

      I hope this helps.
      Cheers,
      J

  30. Justin:

    I have a question about the cleanup for DXHookD3D11/10. I injected the DLL into the target process and implemented an overlay function, everything is fine. I have a trouble for resizing or closing Windows for the target process.While I resized or closed a injected D3D (10\11) application’s Window I got the error message:

    The Direct3D device has a non-zero reference count, meanin some objects were not released?

    I traced the Injected DLL and Target Process. One of the reasons is from the Cleanup for DXHookD3D11/10 in the following class. The Cleanup function never executed when I closed the target process(D3D application).

    How can I release the resources in the injected DLL when I closed D3D Application?
    How can I make CleanUp function be executed when I closed D3D Application Windows?

    I look forward to your reply.

    public class ScreenshotInjection : EasyHook.IEntryPoint {
    public void Run(
    RemoteHooking.IContext InContext,
    String channelName,
    Direct3DVersion version)
    {
    // Wait for host process termination…
    try
    {
    while (_interface.Ping(RemoteHooking.GetCurrentProcessId()))
    {
    Thread.Sleep(10);

    ScreenshotRequest request = _interface.GetScreenshotRequest(RemoteHooking.GetCurrentProcessId());

    if (request != null)
    {
    _directXHook.Request = request;
    }
    }
    }
    catch
    {
    // .NET Remoting will raise an exception if host is unreachable
    }
    finally
    {
    try
    {
    _directXHook.Cleanup();
    }
    catch
    {
    }
    }
    }

    1. Hi Jianming Li,

      You will want to clean up your resources in two locations, firstly within the resize hook. Being able to hook during the target process termination will need to be done by listening for the WM_CLOSE event I think.

      The current .Cleanup() call is executed when the host process is terminated, or the IPC channel is closed.

      I hope this helps.

      Cheers,
      J

  31. Hi Justin,

    I stumbled upon your blog and am very impressed with your work. I was wondering if you ever returned to the idea of full-screen capturing or if you had any ideas which direction would yield a great low-latency option.

    Thanks.

    1. Hi Carl,

      Check through the comments and you will find the fixes to get fullscreen capture working (maybe read from bottom up). The latency is fairly low, however can be improved by doing some buffering within the hooked process. You will see some comments about this also.

      I’m planning on setting up a github repository for this project soon so that the fixes are immediately available, and so that others can contribute.

      Cheers,
      J

      1. Hi Justin,

        Thank you for the reply. I didn’t realize I wasn’t being clear enough. I was actually wondering about full desktop screen capture as opposed to a fullscreen application. I think in earlier versions of this code you attempted to hook the windows DWM to get the full screen but they had changed something about DWM in Windows 7 making it only work for Vista. Any insight you might have would be fantastic. Thanks!

          1. I’m glad to see that you are still actively working on this project. I was looking at this to use as a base for an overlay I want to create, but as other people have mentioned, I’m having issues with GAC since I’m using .NET4.0. Even after installing the dll with gacutil 4.0.x, the application is having issues finding ScreenshotInject. The tool I’m working on is going to be potentially be used by many people, so I wanted to create a process that is as easy as possible. I saw that you mentioned about streamlining all of this, so I was wondering how that is going. Anyway, thanks for all your hard work. This is the only good resource I’ve found for DX injection.

          2. Mike,

            Yes I created a version that works with .NET 4 and doesn’t require the GAC, however I had to make changes to the EasyHook library. I have joined the EasyHook project at Codeplex and will be committing changes there once I get some spare time to clean up my implementation.

            I plan to also get this code into a project on Github so others can contribute to it.

            I’ll post any updates on the blog and here.

            Cheers,
            J

      2. Carl,
        I also have a fullscreen trouble in Windows 7. My testing environment is Direct3D 11, Windows 7/64bit. My target process (game) with injected DLL crashes when I try to switch from Windowed mode to fullscreen mode. Do you test the switching between two mdoes when you try to get fullscreen capture working? Could we share some ideas about full-screen mode?

        Regards,

        Jianming

  32. Justin,

    Could I inject an overlay DLL into a full-screen game application(Direct3D 11), keep the application in the full-screen state, or inject the DLL into a windowed-mode game application, then switch to the full-screen mode?

    I need to hook the full-screen game application. I have added hooked functions– ResizeTarget and ResizeBuffer. When I resize the windows of game application, ResizeTarget never works, only ResizeBuffer works in the injected DLL.

    If I inject DLL into the full-screen game apps, the apps automatically switcches from full-screen mode to windowed mode.

    After I successfully inject a DLL into the game application, then switch from Windowed mode to full-screen mode, the game application crashes. The error info:Unhandled exception at 0x000007fefda3cacd (KernelBase.dll) in DecalTessellation11.exe: 0xE0434F4D: 0xe0434f4d.

    Do you meet the similar case when you switch the game apps with injected DLL from Windowed mode to full-sceen mode? I have spent a long time, try the different ways to fix the bug. Until now, I haven’t a clue.
    Could you provide any solution or clue to fix the full-screen bug. I wiil really appreciate your help.

    Jianming

    1. Yes I’m fairly certain that this would work fine in VB.NET, the code does not use any specific C# language constructs that do not have a VB.NET equivalent.

  33. Justin, I’m not a pro at visual studio (my background is in Linux), but I’m trying to get this to work. I open your example, make sure it’s using my installed slimDX (the January 2012 version), compile, run, and give it the name of the window the DirectX example Text3D has, and I get an Argument NULL exception: “value cannot be NULL” in file dwmcapture.cs, line 71. Any idea what I’m doing wrong?

    I would greatly appreciate your help. Thank you!

      1. What library would you recommend for realtime video file creation I used aforge but the frame rate is all messed up?

  34. We’re trying to use this for a DX9 application. For example, the Text3D sample in the DirectX SDK. We installed the newest SlimDX (Jan. 2012) and updated the example to link to it. When we ran it and tried to inject the Text3D program (it claimed it was successful), the the Text3D program simply disappeared– no error or anything. Then trying to “request capture” resulted in the green progress bar, but no image (same with the “Load Test” button). We tried this three ways– running as admin and checking the auto GAC box, adding the DLLs to the GAC through the terminal and running as a regular user WITHOUT checking the auto GAC box, and finally, after the DLLS were added to the GAC through the terminal, running as an admin without checking the auto GAC box. All of these resulting in the same behavior– namely, the Text3D program disappears with no error, and no captures can be had.

    Yes, the DX9 radio button is selected. We tried both 64-bit and 32-bit Text3D versions. There were no apparent errors in the program output box.

    We’re out of ideas. Can you help? Thank you!

    Please note that we’re using 64-bit Windows 7, with two video cards in SLI.

    1. Hi David,

      I’ve just run this now (first time on this new Windows 7 64-bit install), and it has worked fine using the run as admin + “Auto Register” approach (I have used Jan 2012 SlimDX SKD, with the Direct3DHook source that is now hosted on Github – https://github.com/spazzarama/Direct3DHook).

      For a sanity check, copy the “EmptyProject” DirectX sample into your build output dir and run from there (no auto register to GAC) – if it works then you are having issues with the GAC, if not then it is something more to do with the hooking/injection I would guess.

      Then I would try the following:

      1. Uninstall EasyHook + screenshotinject from GAC (force uninstall if necessary)
      2. Reboot
      3. Install into GAC again manually
      4. Reboot
      5. Retest

      I have found that using the Auto register option can result in issues within the GAC (it has been noted on the EasyHook forums also). So its best to use the manual install. I have found that a reboot has been necessary after installing in the past – for no explainable reason.

      I am working on a version that no longer requires the GAC – you’ll find this once I commit it on Github. But that is waiting for some EasyHook improvements first, you will find the status of the EasyHook 2.7 release at http://easyhook.codeplex.com

      Cheers,
      Justin

      1. Justin, thank you for the prompt and thorough response. Unfortunately, it didn’t seem to change anything. I cloned your git repo so I have the most up-to-date stuff, and I can see that both ScreenshotInject.dll and EasyHook.dll are in the GAC (and I rebooted), and I copied EmptyProject into Direct3DHook/bin. I run both EmptyProject and TestScreenshot as non-admin, and the Test Screenshot app shows the following in its log:

        6052:DXHookD3D9: Hook: End
        6052:DXHookD3D9: Hook: Device created
        6052:DXHookD3D9: Hook: Before device creation
        6052:DXHookD3D9: Hook: Begin
        6052:64-bit Process: False
        6052:DLL Injection succeeded

        After that, the EmptyProject closes, and Request Capture does nothing. For the heck of it, I also tried running both AS admin, with no change. What does that mean?

        1. Hi David,

          Couple things you can do to further debug:

          1. Add additional debug message(s) within the EndScene hook (note: if you can see the FPS appear on the DX9 window then it is working). It is probably a good idea to only do a debug message on every 20th call as it will be hit a lot of times otherwise.
          2. Try without the SLI – and perhaps on another machine if you have one handy.
          3. Try a D3D 10/11 sample, do they work?

          Hope that helps.
          Cheers,
          J

          1. Hey Justin,

            Thanks again for the insight. I now have it running perfectly on another computer with the DirectX samples. (Still not sure what was wrong on the first computer, but I can look into that later).

            My issue now is I am running a simulation I created in the Unity3D game engine (http://unity3d.com/) and when I run the TestScreenshot.exe and press inject nothing happens and TestScreenshot locks up and crashes with no output in the log.

            Do you have any ideas?

            Thanks so much,

            David

  35. I was wondering, have you had heard of XSplit and their game source capture implementation? It seems like they are doing D3D hooking as well in order to capture the rendering from games for live streaming. The performance is quite good. The program now costs money in order to use the game source mode though.

    There is also another program by a Japanese guy called DXTory which hooks into D3D and OGL programs in order to capture and encode video from them.

    Were you planning on adding any kind of capture functionality to your app? I haven’t downloaded it to check it out yet but I hope to see more of this kind of thing in the future.

    1. Hi Paul,

      Yes I am planning on adding video capture support (or at least the beginnings of it) to the project on Github, along with a more complete implementation of the DX overlay. I’ll probably be trying to find some more people to participate in creating a more complete application in the future also.

      I hadn’t heard of those projects yet so thx for the pointer, I’ll take a look at them.

      Cheers,
      J

      1. Awesome! That’s great to hear. With web streaming becoming so popular right now, especially in the gaming segment, I’m surprised there aren’t more solutions out there for direct, 3D screen capturing with a generic output to Direct Show or something similar. I know that DShow is on the way out (XP just won’t die!) but hopefully we will see a good general solution in the future that doesn’t require purchasing a bunch of other software on top. For example not everyone that buys XSplit may want to use their app to do the video compression as well as the capture.

        1. Yes I would certainly like to see something like that happen. Keep an ear out, I’ll post any progress in that direction somewhere on this site. It will definitely require some additional input from others with more experience in that area than me.

          Cheers,
          J

  36. Justin, thank you so much for taking the time to create this project and share your knowledge with the rest of us. I had no problem hooking in with Direct3D 9 examples built from the DirectXSDK. After getting the hang of things, I tried hooking into a published game that uses Direct3D 9 and kept getting the error “STATUS_ACCESS_DENIED: The given process is not accessible. (Code: 5)”. I narrowed down the issue and found that ScreenshotInjection.Run() was throwing an exception at the line:

    bool isX64Process = RemoteHooking.IsX64Process(RemoteHooking.GetCurrentProcessId());

    I noticed the bool value isn’t actually used other than for printing out a debug message. I commented out this line of code, and surprising, everything after that worked flawlessly. I honestly don’t have the know-how to figure out why this error is occurring or how to delve into it further. Hope this helps if anyone else runs into this issue.

    Any ideas what would be causing it? I’m running Win7 64-bit and the process is 32-bit.

    Many thanks,
    Brett

    1. Hi Brett,

      My best guess is that the app you are trying to hook into is running under a different user account (sometimes games install their shortcut to run as administrator or the like). Try running the TestScreenshot as administrator.

      You cannot access processes in another user session without running as administrator (you will see that when in the Task Manager and selecting show all processes can result in UAC request depending on UAC config). But why you get that far and then fail on that particular call? Not really sure…

      Cheers,
      J

  37. Hello Justin, a very useful project, thank you. Not only it is an excellent intro into Direct3D hooking, but it is also a nice
    example of interprocess communication. I have learned a lot from it and was able to make it capture a gameplay video clip of an adequate
    quality using a push source filter in a DirectShow graph. I also use a simple text overlay that tells the user
    about a hot key combination that stops/starts the capture. While I want this overlay be shown in the target app, I don’t want it to
    get recorded into the video. And it almost works as expected, except for that overlay somehow gets captured in an occasional frame
    and appears randomly in the recorded video for a fraction of a second. I just don’t understand how this could happen. Apparenly it
    stays in the back buffer after the previous call to EndScene or perhaps gets cached elsewhere. I tried hooking to Present and to avoid calling multiple
    pairs of BeginScene/EndScene without a Present between them (not shown in my code listing below). Anyway here is my EndScene code,
    if you have some idea of how a frame with the overlay can sometimes end up in my DirectShow push source filter, I’d like to hear it.
    Thanks again for a great example.

    using System;
    using SlimDX;
    using SlimDX.Direct3D9;
    using System.Diagnostics;
    using DirectShowLib;
    using System.Runtime.InteropServices;
    
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [System.Security.SuppressUnmanagedCodeSecurity]
    [Guid("EA2829B9-F644-4341-B3CF-82FF92FD7C20")]
    
    public interface IScene
    {
        unsafe int PassMemoryPtr(void* ptr, bool noheaders);
        int SetBITMAPINFO([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]byte[] ptr, bool noheaders);
    }
    
    public class Class1
    {
        object _lockRenderTarget = new object();
        public string StatusMess { get; set; }
        Surface _renderTarget;
        //points to image bytes
        unsafe void* bytesptr;
        //used to store headers AND image bytes
        byte[] bytes;
        IFilterGraph2 ifg2;
        ICaptureGraphBuilder2 icgb2;
        IBaseFilter push;
        IBaseFilter compressor;
        IScene scene;
        IBaseFilter mux;
        IFileSinkFilter sink;
        IMediaControl media;
        bool NeedRunGraphInit = true;
        bool NeedRunGraphClean = true;
        DataStream s;
        DataRectangle dr;
    
    
        unsafe int   EndSceneHook(IntPtr devicePtr)
        {
            int hr;
            
            using (Device device = Device.FromPointer(devicePtr))
                {
                   try
                    {
                        lock (_lockRenderTarget)
                        {
                            
                            bool TimeToGrabFrame = false;
    
                            //....
                            //logic based on elapsed milliseconds deciding if it is time to grab another frame
                
                            if (TimeToGrabFrame)
                            {
                                         
                                //First ensure we have a Surface to render target data into
                                //called only once
                                if (_renderTarget == null)
                                {
            
                                    //Create offscreen surface to use as copy of render target data
                                    using (SwapChain sc = device.GetSwapChain(0))
                                    {
                                                                            
                                        //Att: created in system memory, not in video memory
                                        _renderTarget = Surface.CreateOffscreenPlain(device, sc.PresentParameters.BackBufferWidth, sc.PresentParameters.BackBufferHeight, sc.PresentParameters.BackBufferFormat, Pool.SystemMemory);
                                              
                                    } //end using
                                } // end if
                                
                                using (Surface backBuffer = device.GetBackBuffer(0, 0))
                                {
                                    //The following line is where main action takes place:
                                    //Direct3D 9 back buffer gets copied to Surface _renderTarget,
                                    //which has been connected by references to DirectShow's
                                    //bitmap capture filter
                                    //Inside the filter ( code not shown in this listing) the bitmap is periodically
                                    //scanned to create a streaming video.
                                    device.GetRenderTargetData(backBuffer, _renderTarget);
                                    
                                    if (NeedRunGraphInit) //ran only once
                                    {
                                        ifg2 = (IFilterGraph2)new FilterGraph();
                                        icgb2 = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
                                        icgb2.SetFiltergraph(ifg2);
                                        push = (IBaseFilter) new PushSourceFilter();
                                        scene = (IScene)push;
                                                                           
                                        //this way we get bitmapfile and bitmapinfo headers
                                        //ToStream is slow, but run it only once to get the headers
                                        s = Surface.ToStream(_renderTarget, ImageFileFormat.Bmp);
                                        bytes = new byte[s.Length];
                                            
                                        s.Read(bytes, 0, (int)s.Length);
                                        hr = scene.SetBITMAPINFO(bytes, false);
    
                                        //we just supplied the header to the PushSource
                                        //filter. Let's pass reference to
                                        //just image bytes from LockRectangle
                                      
                                        dr = _renderTarget.LockRectangle(LockFlags.None);
                                        s = dr.Data;
                                        Result r = _renderTarget.UnlockRectangle();
                                        bytesptr = s.DataPointer.ToPointer();
                                        hr = scene.PassMemoryPtr(bytesptr, true);
                                                                   
                                        //continue building graph
                                        ifg2.AddFilter(push, "MyPushSource");
                                              
                                        icgb2.SetOutputFileName(MediaSubType.Avi, "C:\foo.avi", out mux, out sink);
                                                                    
                                        icgb2.RenderStream(null, null, push, null, mux);
                                                                                                                   
                                         media = (IMediaControl)ifg2;
                                            
                                         media.Run();
                                                                           
                                         NeedRunGraphInit = false;
                                         NeedRunGraphClean = true;
    
                                         StatusMess = "now capturing, press shift-F11 to stop";
                                        
                                    } //end if
                                                                                      
                                } // end using backbuffer
                            } //  end if Time to grab frame
                            
                        } //end lock
                    } // end  try
                    
                    //It is usually thrown when the user makes game window inactive
                    //or it is thrown deliberately when time is up, or the user pressed F11 and
                    //it resulted in stopping a capture.
                    //If it is thrown for another reason, it is still a good
                    //idea to stop recording and free the graph
                    catch (Exception ex) 
                    {
                       //..
                       //stop the DirectShow graph and cleanup
                       
                    } // end catch
    
                    //draw overlay
                    using (SlimDX.Direct3D9.Font font = new SlimDX.Direct3D9.Font(device, new System.Drawing.Font("Times New Roman", 26.0f, FontStyle.Bold)))
                    {
                        font.DrawString(null, StatusMess, 20, 100, System.Drawing.Color.FromArgb(255, 255, 255, 255));
                    }
              
                    return device.EndScene().Code;
          
                } // end using device
       
            } //end EndSceneHook
        }
    
    1. Hi Mikhail,

      Thanks for the feedback. I’m actually looking to implement something like what you have done soon, so a very timely question.

      My best guess at the moment is a threading issue. But I think I will need to try the code out myself to better understand what is happening, do you have the project hosted anywhere or can you send it through?

      Cheers,
      Justin

      1. If anyone is interested, here is a workaround that helps to resolve this issue. It turned out that backbuffer in some Direct3D9 apps is not necessarily refreshed each time the hooked EndScene is called. Hence, occasionally the backbuffer with the text overlay from the previous EndScene hook call was passed to the DirectShow source filter responsible for collecting input frames. I started stamping each frame with a tiny 3 pixel overlay with known RGB values and checking if this dummy overlay was still present before passing the frame to the DirectShow filter. If the overlay was there, the previously cached frame was passed instead of the current one. This approach effectively removed the text overlay from the video recorded in the DirectShow graph.

  38. Hello. How would you determine current yaw and pitch of the camera? I want to build a head tracker and I need this for feedback.

  39. I get a “System.BadImageFormatException” while

    RemoteHooking.Inject(
    process.Id,
    InjectionOptions.Default,
    typeof(ScreenshotInject.ScreenshotInjection).Assembly.Location,//”ScreenshotInject.dll”, // 32-bit version (the same because AnyCPU) could use different assembly that links to 32-bit C++ helper dll
    typeof(ScreenshotInject.ScreenshotInjection).Assembly.Location, //”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
    direct3DVersion.ToString(), // The direct3DVersion used in the target application
    cbDrawOverlay.Checked
    );

    is being selected as the cause, saying ScreenshotInject.dll or a dependency wasn’t found.

    What now?

  40. I solved the issue by using slimdx for .net 2 instead of .net 4.

    But still, how do I have to change your code for it to output the pitch and yaw?

    1. Hi Malwine,

      For Direct3D9 I would suggest you hook the SetTransform method of the IDirect3D9Device and log what comes in. I think you will need to do this for calls where D3DTRANSFORMSTATETYPE is equal to D3DTS_VIEW, see IDirect3DDevice9::SetTransform. This won’t work for shader based transforms.

      In Direct3D 10+ fixed-function transform and lighting pipeline in not available. Instead, you must use shaders.

      I don’t know a lot more than that and you will find more by going through samples and seeing what API’s they use to update camera positions (pitch/yaw/roll) across the different Direct3D versions.

      Hope this helps point you in the correct direction and I would love to hear how you get along with it.

      Cheers,
      Justin

  41. Hi Justin,

    Having trouble with DX9 PresentHook like a few others. I’ve narrow’d it down to my implementation of RECT:

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
    public Int32 left;
    public Int32 top;
    public Int32 right;
    public Int32 bottom;
    }

    As soon as I try to access either sourceRect or destRect (“ref RECT sourceRect, ref RECT destRect”) it will crash the target application (presumably it’s throwing). Any thoughts?

    1. Hi Ant,

      It is possible for both to be NULL if the entire surface is to be presented. I would need to see more of your code to give you any further hints.

      Cheers,
      J

  42. Hi, first I must thank you for creating this brilliant project and it’s very inspirational to me. I run your demo successfully, and want to enhance it a bit. The first thing is I want to make the in game overlay responds to mouse and keyboard input so that I could create some menus, drop down list, etc, or even better an in-game browser. Any idea on how could this be implemented, what library could be used?

    1. Hi Roland,

      Thanks. For the user input you will want to look at installing global mouse/keyboard hooks. I have seen people attempting to “pause” rendering of the game behind the overlay (I can’t remember how sorry).

      For an in game browser take a look at the Chromium project, I believe this is used in games such as EVE Online as the in game browser – not sure if Steam also uses this.

      Good luck! Feel free to post any updates to the GitHub repository, I would love to get a proper overlay framework in place with it.

      Cheers,
      J

  43. Hi Justin,

    I am also looking at Chromium. The problem with current release is its .NET wrapper is outdated. The new candidate release 1.7 RC1 seems promising:

    There’s still a couple things coming to the 1.7 branch (we may put out an RC2 to handle these before the official launch)
    Popup-menu handling (we broke it as we were wrapping up this package)
    Context-menu handling
    IME Input
    Login authentication handling
    Geolocation permission handling
    Cookie manipulation
    Mac OSX and Linux ports
    Advanced Samples (WebUI is coming, baby!)
    Updated wrappers for various 3D engines

    If I could make it work, I will do a pull request on the Github repository.

    Pause the game when rendering the overlay is not a very good idea to me because it will give others advantage when playing online games.

    Cheers,
    Roland

  44. Hi Justin,
    Thanks for your code. I was learning Windows programming and that was very useful. I did a little sceenshot app like Fraps. It works good with DX9, still some problems with 10 and 11. Btw, I found it’s better to move DX9 FPS calculation to Present hook, because some programs make 2 EndScene calls for 1 Present calls. Not sure about Present signature, i’ve just put intptr and I use the shortest version from SlimDx. I plan to put the code on github too.

    Thanks
    Stan

  45. Ive been all around the internet to find a viable method to hook and display graphics ontop of a game. and this helped alot.
    though i’m stuck trying to figure out if its possible to draw a windows form and all its poperties (drag n’ drop, clickable buttons, etc) could you give me any pointers?

    1. Hi Kashmir,

      You have to implement your own drawing and passing through of events. Take a look on gamedeception.net for “.NET Gui” under “Public Releases”. This is a simple class in Managed C++ that implements a windowing system for use in overlays but there is still a lot more you have to implement yourself.

  46. Excellent howto, I’m trying to code a desktop/game streaming app (like onlive but homebrew), I will use your method for an alternate capture method.

    My current implementation gives me a frame at around 32ms. I wonder why does this seem slower? you mention 50ms for DX9 apps, my preliminar testing showed no measurable difference between DX9, a Windows Forms app and DX11.

    My github page is here https://github.com/fr500/desktop_streamer in case it’s of any interest

    1. Yes, capture within DX9 is always faster for me, but DX10/11 allow you to process the image in a separate thread so you can buffer a few captures and so on.

      To improve performance resize the image within DX (I only know who to do this with DX9 at the moment), this significantly increases capture performance. Also you can exclude the IPC overhead by including any logic within the injected assembly as well – not always desirable of course.

  47. Could you maybe guide me as to how to approach passing a few parameters to the function that calls for the overlay to be drawn, so that the application using ScreenshotInject can pass the string to be overlaid to ScreenshotInject?

    e.g. Allow the user to enter a string, and overlay that string in the DX application.

    1. Hi Leftos,

      Take a look at the ScreenshotRequest object if you mean you would like the user to be able to change the text at any time. This is the object passed through via IPC to the injected assembly.

      If you mean to only set the text at the start then you would add a parameter to you Run method

      Hope this helps – J

  48. Hi again, ive been trying to display a 2d image next to the FPS counter or dxd9, though i always get an exception “D3DERR_INVALIDCALL: Invalid call (-2005530516)” do you have any knowledge of that?

    As I don’t want to load the image’s i’d like to display everytime its rendered, I need to have the code for loading images somewhere else than in EndSceneHook, where do you suggest i put that code? i moved it to Hook(), not sure if thats the best place though

    1. Hi Kashmir,

      That error usually indicates some sort of problem with the combination of parameters u are passing e.g an incompatible texture or surface.

      U will want to create a method to do the initialisation of the images and call it in endscene if it isn’t already init’d. You can also do it in reset device.

      Btw how did u go with the form overlay?

      Cheers,
      J

      1. Hi,
        Thanks, I think it was a combination of wrong path, wrong extension (*.png) and freak out as I didnt understand the exception :)

        I havent had any luck with the forms yet, To be honest im quite new to direct3d and hooking, things are very different compared to the window forms and websites, ive been doing for some years. So im currently just experimenting and really appreciate your feedback, even when it digress from the original screen capture thread.

        1. Hi again
          ive started to look into forms and similar, so i found this thing called “squid”
          however they have this renderer i need to load with the “device” though I shouldn’t do this in endscenehook as its looped. are there any good places at all i could assign this? yes im still looking for a good place to init my vars.

          until now ive called a method in endsceenehook which checks if this is the first time and if yes, do this..

          1. Basically what I ask is,

            This squid ui (ionstar.org/) uses a static reference, and if i define that in endscene, all elements needs to be loaded on each cycle, which ofcourse draws alot of memory and fps..
            initially this reference is supposed to be defined in the initialization method (when building a slimdx app from scratch) but afaik I don’t have any initialization method available?

            I’ve also tried to put it in BeginScene without any luck. In most cases it crashes as the reference is null when program reaches EndScene

  49. Hi,
    Hope is not a too stupid question, but i’d like to if is possible to overay to a Direct3D an external GUI of a software, so an precompiled .exe
    Thanks

      1. It’s really cool, too bad i don’t have the knowledge.
        When you have some time maybe you can post an example or directly a binary for check it out.
        Have a nice day

        1. There is a code repository with the source, this article isn’t really that useful for non-programmers, although I would like to see a full screen capture application implemented.

  50. DXHookD3D11.cs error

    _swapChainPointer that is checked in conditions is never assigned

    May I recommand you ReSharper in Visual Studio, it highlights such inconcistency

  51. (Sorry if double post, had javascript and cookies disabled first time)

    DXHookD3D11.cs error

    _swapChainPointer that is checked in conditions is never assigned

    May I recommand you ReSharper in Visual Studio, it highlights such inconcistencies

  52. Feel free to delete my bug report here Justin, I will post cleaner way in the GitHub

    Hooking in DirectX made that easy, still can’t believe it

  53. Btw I’m using EasyHook + SharpDX it works fine too, SharpDX seems more up to date than SlimDX, it has already some Win8 stuffs ready

    Code changes to apply to your tutorial Justin are really smalls

    =)

    1. Hi Arnaud,

      It would be great if you could post these changes to Git – I did try with SharpDX a while ago but had some issues with com dereferences.

      I agree SharpDX is a better library for this purpose.

      Cheers,
      J

  54. There is some nice feature in as the option : SharpDX.Configuration.EnableObjectTracking = True

    so you can loop then a collection of Com object waiting to be disposed

    For Each obj As SharpDX.Diagnostics.ObjectReference In SharpDX.Diagnostics.ObjectTracker.FindActiveObjects()

    Also in DX11, the swapchain.Device directly points to the D11 Device so its handy to get the device from the swapchain in the Present() hook

  55. Hello,

    Decided to give this another try, with updated code from GitHub. Able to run the test app and successfully hook in to process, however getting stuck upon taking screenshot getting the following exception:

    6876:DXHookD3D9: EndSceneHook: Exeception: SlimDX.Direct3D9.Direct3D9Exception: D3DERR_INVALIDCALL: Invalid call (-2005530516)

    I’ve read all posts above referencing this ex code, but non seemed relevant to my situation. Any idea what would cause this or how to resolve?

    Running on Win 7 64 bit, Jan 2012 rel. SlimDX

    Thanks in advance.

  56. Bingo – that did the trick! Thanks for the quick response.

    Turning off anti-aliasing worked, able to take screenshots. In the description you mentioned that anti-aliasing is supported for DX 10 & 11, does that mean it wont work w/ DX 9?

    The game I was testing on was Counter Strike Source (HL2 engine), just tried DX 11 based Call of Duty Black Ops 2 – and with this just hooking into the process causes the target to crash. Any pointers on how to make it work ?

    BTW really appreciate the quick replies, this is by far the most useful post on the topic I was able to find!

    1. Yes you can capture anti-aliased in D3D9 – take a quick look at the Afterglow project I linked to in another post and see the revised D3D9 capture in there. I will try to get around to updating this project with it also.

    2. I have not tried CoD but have with BF3 without any issues. Hard to say what the problem is without more info – try to determine if it is the injection or the hooking that is causing you issues.

      1. Justin – many thanks for supporting this project. I will take the latest from GitHub and try out DX 9 with anti-aliasing.

        I also preloaded a bunch of DX 10/11 games to test with, will share my findings shortly.

  57. have heard cod games have hooking preventions with all the aim bot hacks it has, also cod and all valve games are VAC secured, just for you to know you cna get troubles no matter what does the code

    1. Arnaud, thanks for pointing this out. That may very well be the case for COD.

      However FRAPS worked w/ COD. From my research they use a similar approach by also hooking into the process but do so more aggressively, not sure what that entirely means as I’m still very new to DLL hooking but a couple of posts mentioned that FRAPS hooks into every process to be fool proof.

      Also just wanted to share that Justin’s method does work with Steam games, the ones I tried were Counter Strike Source (I think its VAC enabled) and Dragon Age II.

  58. Is it possible to merge this project with a project of mine that is written in VB.NET? if so, can you tell me how? My project is in C# as well, so I can merge it in C# as well, but I’d prefer VB.NET (I would prefer using this from my app’s form1, not the form1 of testscreenshot, but the thing is this project is so complicated). Also, when I tried this in Dota2, there were 2 frame-rates that would be in the hundreds or so, is it possible to just hide them, I don’t need to see the frame-rates, I just need to capture the SS.

  59. Pretty nice project you have there, would love to see a screen recorder though as only screenshots seems a bit limited to me.

  60. Hi I’m getting this error
    1712:Exception during device creation and hooking: D3DERR_INVALIDCALL: Invalid call (-2005530516)
    at SlimDX.Result.Throw[T](Object dataKey, Object dataValue)
    at SlimDX.Result.Record[T](Int32 hr, Boolean failed, Object dataKey, Object dataValue)
    at SlimDX.Direct3D9.Device..ctor(Direct3D direct3D, Int32 adapter, DeviceType deviceType, IntPtr controlHandle, CreateFlags createFlags, PresentParameters[] presentParameters)
    at ScreenshotInject.DXHookD3D9.Hook() in C:\Documents and Settings\CYGNUX01\Escritorio\Direct3DHook-master\ScreenshotInject\DXHookD3D9.cs:line 45
    at ScreenshotInject.ScreenshotInjection.Run(IContext InContext, String channelName, String strVersion, Boolean showOverlay) in C:\Documents and Settings\CYGNUX01\Escritorio\Direct3DHook-master\ScreenshotInject\ScreenshotInjection.cs:line 138
    1712:DXHookD3D9: Hook: Device created
    1712:DXHookD3D9: Hook: Before device creation
    1712:DXHookD3D9: Hook: Begin
    1712:Autodetect found Direct3D 9
    1712:64-bit Process: False
    1712:DLL Injection succeeded
    I have an Intel 945 express card and Direct x 9.0c

    1. You can dispose of the LocalHook and this will uninstall the hook.

      You cannot un-inject the assembly unless a custom AppDomain is created and then closed when ready to un-inject.

  61. Justin, there is 3 methods of loading .net in a target process, you seem to use only one which fails in a few app, ie Borderlands 2

    I developed my own Library to Hook and Inject directly from C#, the only C++ part uses this to load .NET in the target process

    http://code.msdn.microsoft.com/CppHostCLR-e6581ee0 (2x methods, borderlands2 fails)
    http://code.msdn.microsoft.com/CppCallNETAssemblyWrapper-23cc3840 (1x method, borderlands2 works)

    I have all 3 methods inside one dll

    Let me know if interested can share codes

    1. Ah yeah I have used inverse P/Invoke before and was planning on creating a version for this project (or EasyHook) that uses C# and creative round tripping to do a similar thing.

      Thanks for the reminder about this approach and great sample!

  62. warning CS0168: The variable ‘re’ is declared but never used
    warning CS0168: The variable ‘re’ is declared but never used
    warning CS0649: Field ‘ScreenshotInject.DXHookD3D10._swapChainPointer’ is never assigned to, and will always have its default value

    i think you forgot to specify something, Work great with MW2 (DirectX 9), but crash with MW3(DirectX 10/10.1). Can you check your code. I think these swapChainPointer have something wrong.

    Thanks anyway

      1. I think that can be a easyhook problem, (MW3), The test program fail in injection. The game in task manager appear like iw5m.dat *32. I tried, iw5m.dat, iw5m.dat *32, iw5m.dll, iw5mp.exe and crash.

  63. hope you like it, basically if you would like to use as it is you will have to comment a few ShowError() under method 1 and 2 to let it run all tests silently but honestly for the moment I test this on a large panel of games I have 500+ on Steam, and havent caught one yet failing at method 3, but a lot failures with 1 and 2

  64. Hi, this is a bit off topic but would appreciate your help.
    As I’ve hooked into this application, Is it possible to find text written by the application from the Device or Hook? or do i need to do screengrabs and advanced OCR?

    1. If a standard API is being used e.g. The drawtext method in directx 9 or GDI+ then yes you can (or if u can determine the address of an internal method that handles it)

      I have done the OCR approach in the past with success but I would try looking for an API call first.

    1. EndScene won’t really give you what you need (except to do OCR), you are better off starting with looking at ID3DXFont::DrawText. If hooking that doesn’t help then it really depends on what the app is using, I suggest you use something like API Monitor to see if that helps you find what API is being used to draw text.

      1. after scanning steam rep it seems widely used in game industry, often the same version in the same subfolders \Binaries\Win32\

        Aliens Colonial Marines => 1.0.0.1 (md5:d3118c3594b44e9330c939ff76f9fe4d)
        Borderlands2 => no assembly infos, seems recompiled, (md5:8b58ded2f7267d1f81142ffdd732040e)
        Dungeon Defenders => 1.0.0.1 digitally signed to Epic Games Inc (md5:d3118c3594b44e9330c939ff76f9fe4d)
        Special Forces Team X => 1.0.0.1 digitally signed to Epic Games Inc(md5:d3118c3594b44e9330c939ff76f9fe4d)
        XCom-Enemy-Unknown => no assembly infos, digitally signed to Epic Games Inc, (md5:6de260691f7179572b54a1989347efd3)

      1. after scanning steam rep it seems widely used in game industry, often the same version in the same subfolders \Binaries\Win32\

        Aliens Colonial Marines => 1.0.0.1 (md5:d3118c3594b44e9330c939ff76f9fe4d)
        Borderlands2 => no assembly infos, seems recompiled, (md5:8b58ded2f7267d1f81142ffdd732040e)
        Dungeon Defenders => 1.0.0.1 digitally signed to Epic Games Inc (md5:d3118c3594b44e9330c939ff76f9fe4d)
        Special Forces Team X => 1.0.0.1 digitally signed to Epic Games Inc(md5:d3118c3594b44e9330c939ff76f9fe4d)
        XCom-Enemy-Unknown => no assembly infos, digitally signed to Epic Games Inc, (md5:6de260691f7179572b54a1989347efd3)

  65. Dear Justin,

    Thank you for creating this blog, it helped me a lot in my current project where I had to add a text overlay in dx9/11 games.

    For dx11 I used a technique described here http://www.aaronblog.us/?p=36.

    I’ve split the code from above-mentioned program into initialization and present functions inside DXHookD3D11 class as follows:

    Initialization:

    private static SlimDX.Direct3D10_1.Device1 device10;
    private static SlimDX.Direct2D.Factory d2Factory;
    private static SlimDX.DirectWrite.TextFormat textFormat;
    private static SlimDX.Direct2D.RenderTargetProperties rtp;
    private static BlendStateDescription bsd;
    private static ShaderBytecode shaderByteCode;
    private static DataStream verticesText;

    public DXHookD3D11(ScreenshotInterface.ScreenshotInterface ssInterface)
    : base(ssInterface)
    {
    Factory1 factory1 = new Factory1();

    Adapter1 adapter1 = factory1.GetAdapter1(0);

    device10 = new SlimDX.Direct3D10_1.Device1(adapter1,
    SlimDX.Direct3D10.DriverType.Hardware,
    SlimDX.Direct3D10.DeviceCreationFlags.BgraSupport,
    SlimDX.Direct3D10_1.FeatureLevel.Level_10_0);

    // Direct2D Factory
    d2Factory = new SlimDX.Direct2D.Factory(
    SlimDX.Direct2D.FactoryType.SingleThreaded,
    SlimDX.Direct2D.DebugLevel.Information
    );

    // Direct Write factory
    SlimDX.DirectWrite.Factory dwFactory = new SlimDX.DirectWrite.Factory(
    SlimDX.DirectWrite.FactoryType.Isolated
    );

    // The textFormat we will use to draw text with
    textFormat = new SlimDX.DirectWrite.TextFormat(
    dwFactory,
    “Arial”,
    SlimDX.DirectWrite.FontWeight.Normal,
    SlimDX.DirectWrite.FontStyle.Normal,
    SlimDX.DirectWrite.FontStretch.Normal,
    16,
    “en-US”
    );

    rtp = new SlimDX.Direct2D.RenderTargetProperties();
    rtp.MinimumFeatureLevel = SlimDX.Direct2D.FeatureLevel.Direct3D10;
    rtp.Type = SlimDX.Direct2D.RenderTargetType.Hardware;
    rtp.Usage = SlimDX.Direct2D.RenderTargetUsage.None;
    rtp.PixelFormat = new SlimDX.Direct2D.PixelFormat(Format.Unknown, SlimDX.Direct2D.AlphaMode.Premultiplied);

    // Think of the shared textureD3D10 as an overlay.
    // The overlay needs to show the text but let the underlying triangle (or whatever)
    // show thru, which is accomplished by blending.
    bsd = new BlendStateDescription();
    bsd.RenderTargets[0].BlendEnable = true;
    bsd.RenderTargets[0].SourceBlend = BlendOption.SourceAlpha;
    bsd.RenderTargets[0].DestinationBlend = BlendOption.InverseSourceAlpha;
    bsd.RenderTargets[0].BlendOperation = BlendOperation.Add;
    bsd.RenderTargets[0].SourceBlendAlpha = BlendOption.Zero;
    bsd.RenderTargets[0].DestinationBlendAlpha = BlendOption.Zero;
    bsd.RenderTargets[0].BlendOperationAlpha = BlendOperation.Add;
    bsd.RenderTargets[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;

    // Load Effect. This includes both the vertex and pixel shaders.
    // Also can include more than one technique.
    shaderByteCode = ShaderBytecode.CompileFromFile(
    “effectDx11.fx”,
    “fx_5_0″,
    ShaderFlags.EnableStrictness,
    EffectFlags.None);

    // create text vertex data, making sure to rewind the stream afterward
    // Top Left of screen is -1, +1
    // Bottom Right of screen is +1, -1
    verticesText = new DataStream(VertexPositionTexture.SizeInBytes * 4, true, true);
    verticesText.Write(new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0f)));
    verticesText.Write(new VertexPositionTexture(new Vector3(1, 1, 0), new Vector2(1, 0)));
    verticesText.Write(new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(0, 1)));
    verticesText.Write(new VertexPositionTexture(new Vector3(1, -1, 0), new Vector2(1, 1)));
    verticesText.Position = 0;

    factory1.Dispose();
    adapter1.Dispose();
    dwFactory.Dispose();
    }

    Present:

    int PresentHook(IntPtr swapChainPtr, int syncInterval, SlimDX.DXGI.PresentFlags flags)
    {
    ……
    #region TODO: Draw overlay (after screenshot so we don’t capture overlay as well)
    if (this.ShowOverlay)
    {
    Texture2D backBuffer = Texture2D.FromSwapChain(swapChain, 0);
    SlimDX.Direct3D11.Device device11 = backBuffer.Device;

    // setting a viewport is required if you want to actually see anything
    var context = device11.ImmediateContext;

    if (device11 == null)
    this.DebugMessage(“NULL”);

    SlimDX.Direct3D11.Texture2D texture11 = new Texture2D(device11, new Texture2DDescription()
    {
    Width = backBuffer.Description.Width,
    Height = backBuffer.Description.Height,
    MipLevels = 1,
    ArraySize = 1,
    Format = Format.B8G8R8A8_UNorm,
    SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0),
    Usage = ResourceUsage.Default,
    BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
    CpuAccessFlags = CpuAccessFlags.None,
    OptionFlags = ResourceOptionFlags.KeyedMutex
    });

    SlimDX.DXGI.Resource sharedResource = new SlimDX.DXGI.Resource(texture11);
    SlimDX.Direct3D10.Texture2D texture10 = device10.OpenSharedResource(sharedResource.SharedHandle);

    KeyedMutex mutex10 = new KeyedMutex(texture10);
    KeyedMutex mutex11 = new KeyedMutex(texture11);

    // Query for a IDXGISurface.
    // DirectWrite and DirectX10 can interoperate thru DXGI.
    Surface surface = texture10.AsSurface();
    SlimDX.Direct2D.RenderTarget dwRenderTarget = SlimDX.Direct2D.RenderTarget.FromDXGI(d2Factory, surface, rtp);

    // Brush used to DrawText
    SlimDX.Direct2D.SolidColorBrush brushSolidWhite = new SlimDX.Direct2D.SolidColorBrush(
    dwRenderTarget,
    System.Drawing.Color.Red
    );

    BlendState BlendState_Transparent = BlendState.FromDescription(device11, bsd);

    Effect effect = new Effect(backBuffer.Device, shaderByteCode);

    // create the text vertex layout and buffer
    InputLayout layoutText = new InputLayout(device11, effect.GetTechniqueByName(“Text”).GetPassByIndex(0).Description.Signature, VertexPositionTexture.inputElements);
    verticesText.Seek(0, SeekOrigin.Begin);
    SlimDX.Direct3D11.Buffer vertexBufferText = new SlimDX.Direct3D11.Buffer(device11, verticesText, (int)verticesText.Length, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);

    mutex10.Acquire(0, 100);
    dwRenderTarget.BeginDraw();
    dwRenderTarget.Clear(new Color4(0, 0, 0, 0));
    string text = “TEST”;
    dwRenderTarget.DrawText(text, textFormat, new System.Drawing.Rectangle(100, 100, 300, 300), brushSolidWhite);
    dwRenderTarget.EndDraw();
    mutex10.Release(0);

    // Draw the shared texture2D onto the screen
    // Need to Aquire the shared texture for use with DirectX11
    mutex11.Acquire(0, 100);
    ShaderResourceView srv = new ShaderResourceView(device11, texture11);
    effect.GetVariableByName(“g_textOverlay”).AsResource().SetResource(srv);
    context.InputAssembler.InputLayout = layoutText;
    context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
    context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBufferText, VertexPositionTexture.SizeInBytes, 0));
    context.OutputMerger.BlendState = BlendState_Transparent;
    EffectTechnique currentTechnique = effect.GetTechniqueByName(“Text”);
    for (int pass = 0; pass < currentTechnique.Description.PassCount; ++pass)
    {
    EffectPass Pass = currentTechnique.GetPassByIndex(pass);
    System.Diagnostics.Debug.Assert(Pass.IsValid, "Invalid EffectPass");
    Pass.Apply(context);
    context.Draw(4, 0);
    }
    srv.Dispose();
    mutex11.Release(0);

    vertexBufferText.Dispose();
    layoutText.Dispose();
    effect.Dispose();

    mutex10.Dispose();
    mutex11.Dispose();
    texture10.Dispose();
    texture11.Dispose();
    sharedResource.Dispose();
    surface.Dispose();
    dwRenderTarget.Dispose();
    brushSolidWhite.Dispose();
    BlendState_Transparent.Dispose();
    backBuffer.Dispose();
    device11.Dispose();
    }
    #endregion
    …..
    }

    For this to run 'effectDx11.fx' file must be present at the location of hooked executable.

    I would assume that this can still be optimized further and would love to hear your opinion on that matter.

    Still from many techniques that I tried which included:
    * using a library from http://fw1.codeplex.com/ with an additional c# wrapper class
    * drawing characters from texture file

    this method seemed to be the fastest one.

    I hope it helps you and hope that you will incorporate it into the project.

    1. Thanks for that, I will definitely take a closer look and incorporate something into the project – I have actually started doing something for it in DX11 using GDI+ to create a texture for the specified font and then rendering those directly to the surface, but I think it would be better to go the shared surface/resource route like you have here.

      Thanks again for posting your examples.
      Cheers,
      Justin

  66. Hi Justin,

    I’ve noticed you have uploaded new version with DX11 overlay.
    However it doesn’t work for me, even tho I clearly see that overlayEngine is being initialized and drawn – no text or fps is being shown on screen.
    Can you confirm that it is still in development or it’s something wrong on my side.

    Thanks,
    Platon

    1. Hi Platon,

      Yes it is still in development, however I have had it working fine here…

      It sounds like perhaps the font engine is failing – I’ll take a look at it and see if there is anything obvious going wrong for you.

      Cheers,
      Justin

  67. As my UI platform is not supporting SharpDX, would it be difficult to replace ShartDX with slimdx on your new version, or do I need to stick to an older version?

    1. Very easy – just have to a couple of renames and you will be fine. Compare against previous will sort it for you. May I ask what platform you are referring to? I was pretty sure sharp dx was good to go on them all. Oh the actual UI framework u mean? Nevermind.

  68. Hey, I’ve tried this code. I downloaded the latest from your repo. But it doesn’t seem to work properly. I tried it with Black Ops II worked no problem. All my other games it gives me an error “Additional information: Injection to the target process failed. See InnerException for more detail.” Well the other games I’ve tried was War Of The Roses, Battlefield 3 and Borderlands all give me that error stopped trying other games because it’s discouraging lol

    1. Hey stevo, I will be looking at this but not for some time – I have a few pots bubbling away that is taking all my free time right at the moment. Let me know if you make any progress. I’ll be looking at it later in the year.
      Cheers, J

  69. Hi Justin
    Thanks for this awesome code:)
    Could use some pointers dough….hoping you could help me.
    I am trying to display dynamic text overlay in i DX11 game.
    I have added / uncommented the follwing line in “DXHookD3D11.cs”

    new Capture.Hook.Common.TextElement(new System.Drawing.Font(“Times New Roman”, 22)) { Text = “Test”, Location = new System.Drawing.Point(200, 200), Color = System.Drawing.Color.Yellow, AntiAliased = false},

    And
    new Capture.Hook.Common.FramesPerSecond(new System.Drawing.Font(“Arial”, 16)) { Location = new System.Drawing.Point(10,10), Color = System.Drawing.Color.Red, AntiAliased = true }

    And now it displays the FPS and static “Test” how can i make that text dynamic from my class?

    I am also trying to use:

    captureInterface.DisplayInGameText(“Test text from Main”,new TimeSpan(0,0,30));
    And
    _captureProcess.CaptureInterface.DisplayInGameText(“Test text from Main 2″);

    But i get no respond on those..

    Reg. Morten

  70. I’m working on an Ambilight project and I’m trying to use captureProcess.CaptureInterface.BeginGetScreenshot() to capture screenshots repeatedly. This works, but whenever I hook into an application and begin the capture, the memory usage rapidly increases until I get an OutOfMemoryException and the host process crashes. I’ve tried to add in delay, only capture a new screenshot when the previous one is done processing, and slow it down to only 1 capture per second, but in all cases, the application eventually crashes.

    I believe this is due to a memory leak somewhere in the application, but I’m not skilled enough to make heads or tails out of the source. In the meantime, I’m looking at your Afterglow project, but I was wondering if I’m doing something wrong which is leading to the OutOfMemoryException? I’d include my source but it’s basically just captureProcess.CaptureInterface.BeginGetScreenshot(), then in the event handler I call EndGetScreenshot(), then do a Console.WriteLine() without processing the screenshot at all.

    1. An update: I tried using the Direct3DCapture library in Afterglow 0.2. I added a GetCapture() method that returns the FastBitmap, and then I call ReleaseCapture() once I’m done processing the FastBitmap. The code is something like this:

      while (true)
      {
      var bitmap = capture.GetCapture();

      if (bitmap != null)
      {
      var color = bitmap.GetPixel(200, 200);
      Console.WriteLine(color);
      }

      capture.ReleaseCapture();
      }

      Unfortunately, I still run into the same problem, after a few seconds the memory usage shoots through the roof and I get the OutOfMemoryException.

  71. Can you tell me how I can use this from some kind of command line? Or is there a simple class to get the screenshot without using the GUI. C# is not my language of choise, so I am a bit lost here.

    I just want to get a 2d Array of the bitmap data like: Screenshot screenshot = takeScreenshot(“asdf.exe”);

Leave a Reply