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:
- 100% C# implementation
- Added Direct3D 10 and 11 support
- Capturing multi-sampled/anti-aliased images (for 10 & 11) is supported
- Re-organised code making it easier to support multiple D3D versions
- Implemented a new and improved test bed application
- Provided example overlays for D3D 9 and 10
- 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
- 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)
- No example overlay for Direct3D 11 yet
- If you try to inject / capture using the wrong Direct3D version you could crash your host or target application
- 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
Direct3D 10: SDK Sample CubeMapGS
Direct3D 11: SDK Sample SubD11
Download
You can access the source on Github here
It’s also recommended you read the previous post
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?
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.
Not sure when I will get to it due to current busyness tho.
Cheers,
J
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…
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.
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.
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?
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
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.
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
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!
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
Hi Michael,
Try the following:
Cheers,
J
Hey Justin, that code doesn’t work for me. How is your RECT struc?
I did make it work. Dunno what was the problem. I’m using Int32 in the RECT struct.
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!
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.
Hi Threk,
What Direct3D version did you use when you hooked? I’m pretty sure you need to use Direct3D 9 with WoW.
Cheers,
J
Yes, I’ve used Direct3D 9.
My D3D Hook in c++ works fine with Wow.
Mybe we can write in icq? :)
376916619
I’ve compiled the project with Visual studio 2010.
Maybe it dont work with vs 2010 ?
I use VS 2010 as well.
After chatting with Justin, we have found the problem.
You must type the process name without extensions.
So not Wow.exe but Wow.
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. 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
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
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
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
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
Hi Chen,
Windows XP only supports up to Direct3D 9. To use D3D 10 or above you must have Vista or later.
Cheers,
J
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
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
hi justin,now the progarm can work now,i install the related softwares again.so it can work.thanks for guiding
Glad to hear it and good luck!
Cheers,
J
Hi,
Do you think with a hook system is it possible to get vehicule movement from a game like rfactor or sbk that use directx ?
Thanks
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
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.
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
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
Try specifying the coordinates for the capture with this in mind, does it then work correctly?
Cheers,
J
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.
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!
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
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
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.
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 :)
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
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.
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
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!
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
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 :)
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
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
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.
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
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
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!
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
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 :)
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
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
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.
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
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):
DXHookD3D11.cs (change start of PresentHook):
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,
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
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
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
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
If I remove the Present() hook, then other hooks work pretty well. I’ll try to debug it today. Thank you very much!
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
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.
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
I add “3704:DXHookD3D9: Insert Present Hook” in Hook() method.
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.
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.
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
I have a problem when i try to build it : “ResGen.exe” exited with code 2
can anybody help me?
Not sure.. sounds like the kind of error you get when converting the solution file to a new VS version.
Yes, but how can i fix it?
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();
}
I can’t make it work with Day of Defeat Source at full screen. No snapshot is captured.
Any idea how can I debug it?
I’ve already found the answer here at the comments.
Justin,
I’m trying to hook swapchain present method in dx9.
Here is the relevant part of the code to get the vtable. But I do receive D3DERR_INVALIDCALL: Invalid call (-2005530516) at swapchain construction. Do you know what is wrong?
http://pastebin.com/jjHSDGvX
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?
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
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!
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
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.
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
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
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
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 ;]
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
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
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
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?
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).
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.
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.
Hi mouser,
I will be working on this code over the next few weeks for an Ambilight clone project, I’ll keep you informed of any fixes I find.
Cheers,
J
Thanks Justin! Looking forward to any updates.
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.
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
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?
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
Thanks for the quick reply. One question though, when you mention permanently registering the assembly into the GAC, do you mean using the Gacutil.exe utility directly? I’m curious because I’ve also noticed serveral folks taking this approach:
http://msdn.microsoft.com/en-us/library/system.enterpriseservices.internal.publish.gacinstall.aspx
Yeah I use the gacutil.exe. I haven’t used the method you linked, but that would probably suffice as well.
Cheers,
J
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.
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.
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
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.
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
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
{
}
}
}
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
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.
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
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!
Hi Carl, I did look at it again but haven’t had the time to implement a solution as yet.
Cheers,
J
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.
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
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
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
Would it be possible to do this in VB?
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.
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!
Hi Kyle,
Which project have you downloaded to try? It looks like you are trying to run the Vista DWM example – this will not work for you except on Vista. Instead I would grab the Direct3DHook project located here: https://github.com/spazzarama/Direct3DHook and try again.
Cheers,
J
What library would you recommend for realtime video file creation I used aforge but the frame rate is all messed up?
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.
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:
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
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?
Hi David,
Couple things you can do to further debug:
Hope that helps.
Cheers,
J
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
Hi, where are you guys seeing this log? I’m just seeing a bunch of
Debug: DXHookD3D9: System.NullReferenceException: Object reference not set to an instance of an object.
at Capture.Hook.DXHookD3D9.DoCaptureRenderTarget(Device device, String hook) in c:\Users\ts\Documents\hooktest2011\Direct3DHook-master\Capture\Hook\DXHookD3D9.cs:line 381
printed over and over again in the bottom box of the Test Screenshot app, like it’s stuck in a loop. Is that your log? Anyone know what this NullRef error is about? As far as I can tell, I haven’t been able to successfully inject to anything.
I see it’s a little more stable using EmptyProject10 – I have it inside the project bin folder and am attempting to do a capture that will show the capture location overlay as in the image example on this page.
What can this mean though?
Debug: DXHookD3D10: Hook: Device created
Debug: DXHookD3D10: Hook: Before device creation
Debug: DXHookD3D10: Hook: Begin
Debug: DXHookD3D10: Create
Information: Remote process is a 32-bit process.
Information: Injected into process Id:6076.
Debug: Total Time: 00:00:03.6412082
Information: Disconnecting from process 6076
Error: Error in InitialiseHook: SharpDX.SharpDXException: HRESULT: [0x80004002], Module: [General], ApiCode: [E_NOINTERFACE/No such interface supported], Message: No such interface supported
at SharpDX.Result.CheckError()
at SharpDX.Direct3D10.D3D10.CreateDevice1(Adapter adapterRef, DriverType driverType, IntPtr software, DeviceCreationFlags flags, FeatureLevel hardwareLevel, Int32 sDKVersion, Device1 deviceOut)
at SharpDX.Direct3D10.Device1..ctor(Adapter adapter, DeviceCreationFlags flags, FeatureLevel featureLevel)
at Capture.Hook.DXHookD3D10_1.Hook() in c:\Users\ts\Documents\hooktest2011\Direct3DHook-master\Capture\Hook\DXHookD3D10_1.cs:line 167
at Capture.EntryPoint.InitialiseDirectXHook(CaptureConfig config) in c:\Users\ts\Documents\hooktest2011\Direct3DHook-master\Capture\EntryPoint.cs:line 245
Debug: DXHookD3D10_1: Hook: Before device creation
Debug: DXHookD3D10_1: Hook: Begin
Debug: DXHookD3D10_1: Create
Debug: Autodetect found Direct3D 9
Debug: Autodetect found Direct3D 10
Debug: Autodetect found Direct3D 10.1
Information: Remote process is a 32-bit process.
Information: Injected into process Id:6076.
I haven’t tried the D3D10 hook for a while, it could be that it is detecting the wrong D3D10 version.
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.
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
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.
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
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
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
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.
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
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.
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.
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?
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?
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
Hello Justin, a good project, thank you.
I compiled it and made a couple of tests, everything works fine.
I wanted to ask you. Is it possible with your “hook” such as manipulating/get textures?
I found interesting project http://www.deep-shadows.com/hax/3DRipperDX.htm But it is closed and not OpenSource.
And another interesting project http://graphics.stanford.edu/~mdfisher/GameAIs.html (I could not run it)
Hi Eugene, yes you should be able to do that sort of thing using hooks.
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?
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
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?
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
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
Thanks for your input Roland, let me know if you sort something out!
Cheers,
J
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
I’ll keep an eye out for it on github, or if you could let me know when its there.. Thanks!
Done. I don’t know git and github very well, I hope I did it right: https://github.com/stani/GScreensTool
I will write about problems later
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?
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.
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
My biggest issue is that my method is skipping frames, it can’t keep up with the display. Do you think I can at 60fp with this method?
Yes – you will want to resize the textures within DX before completing the capture.
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.
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.
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
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
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
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.
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..
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
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
You probably could by capturing the visual output of that application and overlaying that in the D3D device.
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
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.
DXHookD3D11.cs error
_swapChainPointer that is checked in conditions is never assigned
May I recommand you ReSharper in Visual Studio, it highlights such inconcistency
(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
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
Thanks Arnaud
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
=)
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
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
I’ll try with the latest SharpDX it could be that there were changes that now fix the issues I was experiencing previously.
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.
Hi Alex can you check if you have anti-aliasing turned on and if so try with it off.
Cheers,
J
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!
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.
Just realized this was in another project that isn’t available online – I will update the Direct3D project soon.
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.
Hi Alex – I have updated the project to support anti-aliasing in DX 9, let me know how it goes.
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.
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
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.
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.
Yes u can use assemblies written in C# in you VB.NET project
Pretty nice project you have there, would love to see a screen recorder though as only screenshots seems a bit limited to me.
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
Are there a way to unhook, remove the hook from the application?
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.
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
Hi Arnaud thanks I would love to see it.
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!
http://code.google.com/p/csharp-hook-and-inject/downloads/list
basically if you manage to inject netloader32.dll or netloader64.dll in your target process, this last will load .NET 4.0 Full, and will load netloaderdetoured.dll (anycpu) and show you a message box, with 3 differents methods of loading .NET I haven’t seen it to fail yet and if it fails you will know which line failed exatcly.
Looks good Arnaud, thanks for sharing.
By the way, I ‘m under Visual Studio 2012 so if you are looking to try the compiled dlls you will need Framework 4.0 Full + VC 2012++ Redist
http://www.microsoft.com/en-us/download/details.aspx?id=30679
Have you seen OBSProject? Best d3d and OGL capture performance I’ve seen in any application. Open source too. http://www.obsproject.com
I’ll take a look, thanks Paul
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
I’ve been using it with DirectX 10/10.1 a bit lately for a video capture project without any issues, I’ll check if I have made any changes.
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.
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
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?
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.
Yeah im using directx 9, would this be possible from the EndScene or do i need to hook even deeper?
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.
Isn’t you developing EasyHook Justin ? Just to let you know in case you wonder, Gearbox software redistributes EasyHook in Aliens Colonial Marines as EasyHook32.dll
Nice to know thanks – do you know what version was on the DLL?
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)
btw sorry for not posting in the trees I figured out I had not enabled javascript and cookies for your site hehe
Company assembly name : Christoph Husse
Christoph is the original author of EasyHook. I took over development of the project mid-2012 (with his blessing) after he had stopped maintaining it for some time.
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)
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.
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
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
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
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?
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.
Hi, yeah, I found the Squid UI to be very neat, the developer are daily on the forums to help out, http://www.ionstar.org/. He also said he would make it work with ShartDX, so maybe I don’t have to change it back to SlimDX :)
That’s great news I’ll go check it out when I find some free time. Thanks for the link.
Cheers,
Justin
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
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
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
Hi Morten, I’m away on business at the moment but will try to get around to answering your questions next week.
Cheers,
Justin
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.
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.
Let’s take the discussion to the afterglow site – perhaps u could paste some of your code of the getcapture and releasecapture so we can help u out further.
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”);
You would need to wrap the library within a console application.
The folks at http://www.donationcoder.com did a similar thing for a Direct3D capture test for their screenshot application.
I’ve tried testing your code on bf4. I’m trying to make a program that randomnly captures screenshots to test for cheaters for my clan, however, in all my tests, I keep getting an error: { DirectX funtion “screen->swapChain->ResizeBuffers(renderer->m_settings->getTripleBufferingEnable() ? 3 : 2, screen->info.windowWidth, screen->info.windowHeight, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCHED)” failed with DXGI_ERROR_INVALID_CALL (“The application provided invalided parameter data; this must be debugged and fixed before the application is released.”). GPU: “NVIDIA GeForce GTX 760”, Driver: 34411 }
Is this using the test screenshot app or some of your custom logic? I have an example app that is for that exact purpose as well, ill try to get that posted for you.
That was with the test screenshot app compiled from your code. The only thing I changed was adding a button with a file picker to make entering the window to capture easier.
I’ve done a quick test and have also experienced some issues, although not the same errors you listed. I’ll try to look at it soon.
Justin,
I found some issues in battlelog forums saying that this might be related to language options. I’ve changed my options to English US, but I get the same errors still. The Debug window shows that it was injected into the process, however, whenever I hit “Request Capture”, I get the aforementioned errors.
Not sure if that helps, but I have no idea how to fix this, really hoping you can help out.
Thanks,
Jake
Justin,
Ok, I tried manually selecting Direct3D 11, and it doesn’t fail, however, nothing gets rendered or anything. It did crash under a load test of 100 captures, and eventually crashed bf4 too. Hope that helps.
Thanks again,
Jake
Hi Justin,
When I try to save a screenshot from a directx 11 game to the hard drive in a png format, the screenshot’s colors are “washed out”. But the picture appears fine in picturebox1 of the form.
I tried it with MetroLL, Bioshock Infinite and Watchdogs. This doesn’t happen for other formats regardless of directx version.
I’m referencing DXHookD3D11.cs
Added 2 lines:
FileStream fs = new FileStream(@”c:\temp.png”, FileMode.Create);
Texture2D.ToStream(textureDest.Device.ImmediateContext, textureDest,ImageFileFormat.Png, fs);
You’ll probably find that the image captured is slightly transparent. Could you please check what the alpha looks like in the resulting image?
Thanks Justin for taking a look at this.
I’ve uploaded 4 .png formats from 3 different directx 11 titles.
Bioshock Infinite: persistent rectangle. If you capture early, during menu screens, you’ll notice that they stack up…what you capture “now” has remnants from previous screen on the captured png.
http://i60.tinypic.com/2nuufwx.png
http://i58.tinypic.com/2wp2eqr.png
Metro Last Light:
http://i60.tinypic.com/2ll2bra.png
Watch Dogs:
http://i59.tinypic.com/wqxyeb.png
Alpha is as set in R8G8B8A8_UNorm format. No other format worked.
I tried capturing without copying to memory stream.
I tried capturing before destination texture was created.
Same results.
These all seem like artefacts of the way these games are performing their rendering, e.g. they might have more than one swapchain and so on. I would need to take a closer look at these games to work it out, but don’t have any of them at the moment sorry. Did you say that the preview shows correctly, what happens if you save as BMP instead?
Saving as BMP in any directx version yields no issues.
Saving as JPG in any directx version yields no issues.
Saving as PNG in directx 9 and 10 yields no issues.
The only anomaly is when capturing/saving as PNG in directx 11.
Preview shows fine.
It’s not a big deal since it works fine in bmp and jpg.
It must be, like you said, the way the games are rendering…I’ll go down this road and post back if successful.
Many thanks
works fine! and many comments in source.. it’s very helpful.
i’ll follow your github.
thanks
I was able to solve the issue above…saving pngs to hard drive was not working.
I’ll post the code here…maybe it’ll help someone. I looked at several projects on github using Justin’s code but with some enhancements.
DXHookD3D11.cs
using System.Drawing;
using System.Drawing.Imaging;
private byte[] dd;
Texture2D textureDest;
int PresentHook(IntPtr swapChainPtr, int syncInterval, SharpDX.DXGI.PresentFlags flags)
{
var theTexture = texture;
// 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 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 SharpDX.DXGI.SampleDescription(1, 0), // Ensure single sample
BindFlags = BindFlags.None,
MipLevels = 1,
OptionFlags = texture.Description.OptionFlags
});
// Resolve into textureResolved
texture.Device.ImmediateContext.ResolveSubresource(texture, 0, textureResolved, 0, texture.Description.Format);
// Make “theTexture” be the resolved texture
theTexture = textureResolved;
}
// Create destination texture
if (textureDest == null)
{
textureDest = new Texture2D(texture.Device, new Texture2DDescription()
{
CpuAccessFlags = CpuAccessFlags.Read,//CpuAccessFlags.Read,
Format = SharpDX.DXGI.Format.R8G8B8A8_UNorm, // Supports BMP/PNG
Height = regionToCaptureDest.Height,
Usage = ResourceUsage.Staging,// ResourceUsage.Default,
Width = regionToCaptureDest.Width,
ArraySize = 1,//texture.Description.ArraySize,
SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0),// texture.Description.SampleDescription,
BindFlags = BindFlags.None,
MipLevels = 1,//texture.Description.MipLevels,
OptionFlags = texture.Description.OptionFlags //| ResourceOptionFlags.GdiCompatible
});
}
Texture2D theTexture2 = new Texture2D(texture.Device, new Texture2DDescription()
{
CpuAccessFlags = CpuAccessFlags.None,
Format = SharpDX.DXGI.Format.R8G8B8A8_UNorm,//texture.Description.Format,
Height = regionToCaptureDest.Height,
Usage = ResourceUsage.Default,
Width = regionToCaptureDest.Width,
ArraySize = 1,
SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0), // Ensure single sample
BindFlags = BindFlags.None,
MipLevels = 1,
OptionFlags = texture.Description.OptionFlags //| ResourceOptionFlags.GdiCompatible
});
SharpDX.Direct3D11.Resource.LoadTextureFromTexture(texture.Device.ImmediateContext, theTexture, theTexture2,
new TextureLoadInformation()
{
SourceRegion = new ResourceRegion()
{
Top = regionToCapture.Top,
Bottom = regionToCapture.Bottom,
Left = regionToCapture.Left,
Right = regionToCapture.Right,
Front = 0,
Back = 1 // Must be 1 or only black will be copied
},
DestinationRegion = new ResourceRegion()
{
Top = regionToCaptureDest.Top,
Bottom = regionToCaptureDest.Bottom,
Left = regionToCaptureDest.Left,
Right = regionToCaptureDest.Right,
Front = 0,
Back = 1 // Must be 1 or only black will be copied
},
ElementCount = 1,
Filter = FilterFlags.Linear,
FirstDestinationElement = 0,
FirstDestinationMip = 0,
FirstSourceElement = 0,
FirstSourceMip = 0,
MipCount = 1,
MipFilter = FilterFlags.Linear
});
// Copy the subresource region, we are dealing with a flat 2D texture with no MipMapping, so 0 is the subresource index
theTexture.Device.ImmediateContext.CopySubresourceRegion(theTexture2, 0, new ResourceRegion()
{
Top = regionToCaptureDest.Top,
Bottom = regionToCaptureDest.Bottom,
Left = regionToCaptureDest.Left,
Right = regionToCaptureDest.Right,
Front = 0,
Back = 1 // Must be 1 or only black will be copied
}, textureDest, 0, 0, 0, 0);
theTexture.Device.ImmediateContext.CopyResource(theTexture, textureDest);
DataStream dstream;
DataBox box = textureDest.Device.ImmediateContext.MapSubresource(textureDest, 0, 0, SharpDX.Direct3D11.MapMode.Read, SharpDX.Direct3D11.MapFlags.None, out dstream);
IntPtr data = dstream.DataPointer;
int width = textureDest.Description.Width;
int height = textureDest.Description.Height;
if (bitmap == null)
{
bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb);
dd = new byte[width * height * 4];
}
Marshal.Copy(data, dd, 0, dd.Length);
textureDest.Device.ImmediateContext.UnmapSubresource(textureDest, 0);
for (int i = 0; i < dd.Length; i += 4)
{
byte tmp = dd[i];
dd[i] = dd[i + 2];
dd[i + 2] = tmp;
}
BitmapData bd = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
Marshal.Copy(dd, 0, bd.Scan0, dd.Length);
bitmap.UnlockBits(bd);
}
//save it…
FileStream fs = new FileStream(@Path + "\\Screenshots\\" + thisgame + DateTime.Now.ToString("-yyyy-MM-dd@hh.mm.ss") + ".png", FileMode.Create);
bitmap.Save(fs, System.Drawing.Imaging.ImageFormat.Png);
I also applied this to d3d10 but with some minor changes.
Thanks
Hello,
I have problem, with EasyHook: “System.IO.FileLoadException”
All show the error can not, cause it is in Russian.
Hi Vlad, please make sure you have ALL the EasyHook binaries in your target build directory.
Yes, I encountered the same problem.
2013 VS, Windows 8.1
Lines
_captureProcess = new CaptureProcess(process, cc, captureInterface);
Yes, I encountered the same problem
Line:
_captureProcess = new CaptureProcess(process, cc, captureInterface);
VS 2013 Windows 8.1
this happens because in
public CaptureProcess(Process process, CaptureConfig config, CaptureInterface captureInterface) {
…
…
// Initialise the IPC server (with our instance of _serverInterface)
_screenshotServer = RemoteHooking.IpcCreateServer(
ref _channelName,
WellKnownObjectMode.Singleton,
_serverInterface);
…
…
}
and on RemoteHooking
#region Assembly EasyHook.dll, v2.5.0.0
// C:\work\development\Direct3DHook_2011-03-14\Direct3DHook-master\bin\EasyHook.dll
#endregion
we have another instance
public static IpcServerChannel IpcCreateServer(ref string RefChannelName, WellKnownObjectMode InObjectMode, params WellKnownSidType[] InAllowedClientSIDs) where TRemoteObject : MarshalByRefObject;
how this fix ? or project reference use wrong version EasyHook.dll?
anyone know how to draw an image overlay?
Load an image into a D3D texture, and then render that to a quad either fullscreen or whatever. Look for D3D render to screen-aligned quad or similar (my book covers it), I am working on an example to implement GPU resizing of captured images but will take me a while to find the time to complete.
If it returns error “not supported,” then nothing can be done, correct? The program was written in a way to block this sort of interaction? Thanks.
I would probably need to see more about why you received “not supported” to rule out any incorrect behaviour on behalf of the library.
when drawing text in directx 11, does you method natively support formatting like BottomLeft, TopRight, NoClip…etc. I’m trying to draw text in corners.
I’ve included ContentAlignment and StringFormatFlags in TextElements.cs and updated DrawString() function in DXSprite.cs and updated the line where elements are drawn in Present() but those changes are ignored.
My implementation is fairly basic and doesn’t support any anchors, shouldn’t be too difficult to add however.
Hi Justin,
Thank you. I was able to solve it by measuring textElement width and height and then taking the values and subtracting them from _renderTarget width and height. It works on multilines and even when textElement width changes each frame.
I am now facing another challenge. On a bunch of DX11 games, changing resolution leaves the games either frozen or with a black screen (with overlay still showing) *only if I draw the overlay*. If I uncheck the option, changing resolution succeeds. This has nothing to do with measuring textElement because it also happens using original code.
I read that we must dispose of all resources associated with the swap chain prior to changing resolution, and your code is doing that. Games are running full-screen also so I don’t think hooking ResizeBuffers is needed although I have tried it.
DXFont.cs and DXSprite.cs dispose of all assets…I made no changes here
DXOverlayEngine.cs…only change I made is dispose both _renderTarget and _renderTargetView
Grateful for any tip.
Hi Abo, can you try turning on the overlays, then turning them off in code before resolution change, then turning on again after resolution change. This might help us work out what is happening.
Hi Justin,
Thank you, yes I can. My version implements a keyboard hook.
One hotkey hooks the DX11 game.
Another hotkey toggles the overlay on / off.
I am *almost* certain that this is due to text drawing because if I don’t draw (comment out all draw calls), just hook and take screenshots then resolution change succeeds.
Trying out what you requested:
Launch the game (draw calls uncommented), hotkey hooks game, overlay is on, hotkey turns overlay off, attempt resolution change: different outcomes depending on which game:
a. user interface elements are now offset. To click an element with the pointer, you have to place it away from the element…as if resolution is way larger than the screen…in fact some elements become cut off
b. completely frozen screen…no way to accept/reject new resolution (pointer moves but elements are unresponsive)
Turning on the overlay after a or b will show a garbled overlay…fps counter painting over itself
After Ctrl+Alt+Del out of the game, starting it again shows the requested resolution which had failed earlier.
If needed, I can upload pictures.
Many other DX11 games work fine.
Hi Justin,
I’ve been working with your library for about a day now and it seems pretty nifty, but I’m not quite sure how you intended people to use it.
What I want to do is display a simple image overlay in a DirectX9 game. I’ve played around with the TestScreenshot application that you provided so I know that this is possible, but I’m not sure where my code should go. Did you intend people to put custom overlay code in the EndSceneHook() method ? (Or perhaps DoCaptureRenderTarget().)
Since this is an internal class in the library it sort of feels wrong to plop my own code in there, but given the nature of the application I understand if this is the simplest solution. Any help is much appreciated.
Hi Luke,
It is really only meant to be an example of how it can be done and act as a starting point, from there pull apart the code and do what you need to do with it :)
If you want to leave most of the library unchanged, I would probably do your changes within DoCaptureRenderTarget, then you can ignore most of the hooking / other logic necessary and focus on your task. Perhaps write your code in another class and call it from DoCaptureRenderTarget (or from where ever DoCaptureRenderTarget is called if you want it to make more sense :).
Good luck and cheers!
Justin
Hi again,
So I’ve been messing around with TestScreenshot a bit more and I appear to have run into some synchronization issues. I want to send a string from my host application to the target one so I’ve been using CaptureInterface.DisplayInGameText() to see when it can be called, since this does essentially the same thing. I’ve found that calling it after initializing the CaptureProcess leads to some issues. For example, if I try to call this method at the end of AttachProcess() in Form1.cs then nothing happens – the text isn’t displayed in the overlay. More specifically, the TextDisplay in the corresponding BaseDXHook is never set, probably because the event handler never gets called. However, if I call DisplayInGameText() from the click handler for the Request Capture button after the injection is complete, then the text does get displayed in the overlay.
It seems to me like the source of the issue is that something asynchronous is going on. If I had to guess I’d say that the IPC channel isn’t set up immediately when the CaptureProcess is created, which would explain why the event handler for DisplayInGameText() isn’t getting called. I imagine that there’s a similar problem if you try to call other methods in CaptureInterface like GetScreenshot() immediately after creating the CaptureProcess, although I haven’t verified this myself. Is there an easy way to synchronize the two processes?
What is happening is that you are calling the method before the hook has completed installing (which is happening in your remote process – so yes definitely asynchronous). You could create an event on the interface that is triggered after hooking has completed (within the Run method of the entry point in the Capture project).
Hey, thanks for the awesome work. Screenshotting works perfect, however for capturing video the whole process is kind of slow. I mean, if I just keep querying for screenshots and feeding them to ffmpeg or whatever, my resulting FPS won’t exceed 10-15. Is there any chance of getting proper video recording support?
Once again, thank you for your work, much appreciated. Cheers.
Hi, you need to do this differently, but changing the capture library to hook multiple frames, rather than requesting each individually.
I semi-started this piece of work, and have implemented a circular-buffer (ring-buffer) using shared memory to help with this (see sharedmemory.codeplex.com).
Still plenty of work needed however.
Hi !
I’m trying to learn how to display a steam-like overlay using C#, that’s why I ended up on your post.
First, thank you for this post, it’s very helpfull !
But I still have some trouble. With your code, we can display something on top of games, but what I mean by “steam-like overlay” is an overlay I can interact with.
My ultimate goal is to display an ingame web browser.
Any advice how to make the overlay clickable ?
Or if you know how to have a web browser on top of fullscreen apps / games, it would be very appreciated !
Thank you, and sorry for my bad english.
To do this you could render your browser control to an image (or a DX surface perhaps), and then intercept the mouse and keyboard events using Global Hooks and then push those events to your browser control.
Hey Justin!
Thanks for your wonderful post. I’m stuck in a similar situation where I want to implement a steam like overlay on a game. Overwolf does it too. You say, that I can render my browser control on a DX surface and that might do it. Did you happen to try anything in similar context? I don’t have much experience with DirectX so it would be helpful if you can point me in the right direction.
Thanks again!
Yes, using CefSharp you can render to offscreen browser and then render with the resulting bitmap as a texture.
You can take a look at my SharedMemory library for quick xfer of image data between processes if you don’t want to host CEF in your target process.
Thanks a lot for responding. Where can I find your SharedMemory library?
I just found it out. Thanks again. I’ll try this stuff :)
Hi Justin!
First of all, thank you for your wonderful work! I appreciate it and
I implemented it in my new application.
I have a question though.
In DxHookD3D9.cs in the PresentHook (parameter “IntPtr windowOverride”) function I get automatically from the calling Main Window and/or Child Window of the game the Window Handle.
So my question is, how I’m gonna get the (Child) Window Handle from the calling window of the game in In DxHookD3D10.cs and In DxHookD3D11.cs in PresentHook, because there is no Parameter “IntPtr windowOverride”.
I know, I can loop the Current Process Threads through and get those Handles in a List, but I need to identify the calling Window and get the current Window Handle of it.
Hope you can help.
Thanx in advance.
Cheers,
Sebastian
You can use the WinAPI to determine which HWND is for which thread, e.g. GetWindowThreadProcessId. You may need to first iterate each child window handle for the process and call that method, the return value is the thread id, the out param is the process id.
Thanx for the reply Justin.
But how I’m gonna get then the Thread.Id of the Current Calling Child Window in PresentHook to compare it with the Child Window Thread Ids that you suggested?
The game has a lot of Child Windows, that’s why I need to know, which window exactly is calling PresentHook, because I need the Window Handle and the Window Title of it to add it in a list, and if the user clicks one of the Entries in that List in my app, he would see a stream of Jpgs in my app from the game of that Child Window.
Here is the function I call to get the Child Window Handles and their Thread Ids in PresentHook:
[DllImport(“user32.dll”)]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport(“user32.dll”)]
private static extern bool IsWindowVisible(int hWnd);
[DllImport(“user32.dll”, CharSet = CharSet.Auto)]
private static extern int GetWindowText(int hWnd, StringBuilder title, int size);
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
int PresentHook(IntPtr swapChainPtr, int syncInterval, SlimDX.DXGI.PresentFlags flags)
{
…
getWindowhandle();
…
}
public void getWindowhandle()
{
if (gameProcess == null)
{
return;
}
foreach (ProcessThread processThread in gameProcess.Threads)
{
EnumThreadWindows(processThread.Id,
(windowhandle, lParam) =>
{
if (!IsWindowVisible((int)windowhandle))
{
return true;
}
StringBuilder title = new StringBuilder(256);
GetWindowText((int)windowhandle, title, 256);
if (title.Length == 0)
{
return true;
}
// QUESTION:
// ———————————————————————————–
// How Im gonna get the Thread Id of the current calling Window in
// PresentHook?
if (processThread.Id == CurrentCallingWindowThreadId)
{
Yes, thats the Child Window Handle Im looking for!
}
//————————————————————————————
return true;
}, IntPtr.Zero);
}
}
I could solve my problem. Yeah!
For people in need who needs the Handle to/of the current calling Window in PresentHook in DXHookD3D10.cs or DXHookD3D11.cs too, just use in the PresentHook function this code:
This is already written there:
——————————————————————-
SlimDX.DXGI.SwapChain swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr);
——————————————————————-
And add this below:
———————————————————————-
windowOverride = swapChain.Description.OutputHandle;
———————————————————————-
Have a nice coding!
Sebastian
Hello Justin,
i’m also working with your code and startet to develop a HUD overlay for a FlightSimulator (DCS) who is running in Oculus Rift in extended mode. I draw a overlay to the backbuffer and it worked fine (check video on youtube). Since Oculus swaped to Direct driver mode in version 0.7, i can still draw the overlay, but it was just displayed on the mirrored picture in a window not in the oculus rift.
Question: do you have an idea how to get the texture for left and right eye who is displayed in oculus rift and how to draw a overlay there?
Here you can see how it looks in extended mode with the old oculus runtime <0.7.
http://youtu.be/MEEaXmRm7Vg
Thank you for any hint…
oh i forgot, thanks for your wonderful work, it helped me a lot to digin to this project i just startet. By the way, the project (JHMCS for DCS) is just a workaround to have the possibility to read some flight datas, because the gauges are so small an bad to read inside the rift.
Thanks again for your great work!
Hi Daniel, glad you have found this helpful.
I haven’t played with Oculus – I would love to but unfortunately it is a little lower down the priority list, anyway keep in mind I have zero experience with their API. I took a very briefly look at the Oculus API and think you would be able to hook into the ovr_CreateSwapTextureSetD3D11 (or something similar) to gain access to the textures needed (Rendering to the Oculus ). With access to each texture you should be able to then do your rendering to each eye.
Your project looks interesting – good luck! Let me know when it is up and running and you have a location I can point to.
Btw if anybody has a spare Oculus they want to unload give me a PM :P
Hi Justin!
I’m kinda lost when it comes to D10/D11 image streaming via Thread, BackgroundWorker, DoWork or ThreadPool.QueueUserWorkItem in DXHookD3D10.cs/DXHookD3D11.cs in PresentHook. No matter which one of those (thread, bw or threadpool) I use, I get in Visual Studio either
– Unhandled Exception or
– Access Violation Exception or
– SlimDX Device was removed (in Debug.WriteLine)
after some time or right away and the game crashes! Without Threading it works fine, but the game lags (as your comment in code says “delay in the rendering pipeline”).
Also the images in my app from the game change colors and look distorted after some time.
I think it has something to do with object relocation or disposal through the Garbage Collector, because the Access Violation is here in PresentHook:
ThreadPool.QueueUserWorkItem(delegate
{
using (MemoryStream ms = new MemoryStream())
{
// ACCESS VIOLATION HERE WITH textureDest:
Texture2D.ToStream(textureDest, ImageFileFormat.Bmp, ms);
//—
ms.Position = 0;
SendResponse(ms, requestId);
}
textureDest.Dispose();
textureDest = null;
});
Any ideas how to solve this Access Violation Exception?
Thanx in advance!
Cheers,
Sebastian
Hi Sebastian, I would make sure you have tried with the latest version on GitHub that uses SharpDX, the SlimDX version is quite old now.
Hi Justin!
Thanx. I’ve tried it with both SlimDX and SharpDX. Further tests showed me that it is NOT the variable Texture2D textureDest but just the function Texture2D.toStream(…) itself (only when called from a Thread/Task/ThreadPool/BackgroundWorker) which causes the Access Violation. Any further ideas how to solve this?
The device lost error occurs if the window is resized (or fullscreen is brought to foreground etc).
I’ve changed the approach a little for D3D11 in the latest code on GitHub, where it now uses a shared texture. Can you confirm you are using latest example it has changed A LOT since it used SlimDX.
Yup, I’ve tried DX10 (DX11 only with SlimDX) with SlimDX, SharpDX 242 and SharpDX 263. I’ve seen the new D11 Class. It has a new function EnsureResources and KeyMutex etc. But I did not try that yet with D11, because I think, when I solve the problem with D10, it will work also with D11 automatically. So I focus on D10 for now.
I have to say, that your original project with with Load Test and the 100 Images or more works (but if I click a 2nd (or more) time after it finished, the game lags – but no Exceptions at all) but if I change the Request call to my custom one (cuz the Request mechanism is a bit different) then the above described errors occur (just with threads). Maybe you could give me some hints here what to consider. (I investigated it already, but I don’t see anything wrong there.)
I do not resize the game window while I stream those bitmaps (I resized it just once at the very beginning and it stays with that Width and Height all the time (and your Load Test works without any errors too)).
I also don’t use fullscreen, but I do bring the game window to front just once for hooking.
Ok Justin. I know now why your app “TestScreenshot” (with SharpDX 242 & 263) did not crash the game and mine did. It is because of the GUI-Logs. In my app I have no Logs at all.
So I commented out/removed in your app in class DXHookD3D10.cs (SharpDX 242 & 263) all the Logs (“this.DebugMessage…”) and it crashed the game right away (just few images were streamed of course) at “Texture2D.ToStream(textureDest, ImageFileFormat.Bmp, ms);” with an Access Violation after clicking on “Load Test”. Also those few streamed images got distorted and changed colors too like in my app. I checked it with my Laptop (integrated graphic card) and desktop (not integrated graphic card) and on both it crashed. Would be interesting if that happens also on your computer. Can you test it and let me know?
To prove that more I added also GUI-Logs for showing in my app window and it took some long time till the game finally crashed (it was not right away like before). So it is not about how I make the Frame Request, it is about the timing and the Threadpool, because the Logs slow down the Frame-Requests process.
And here is what I figured out with the help of the Logs: when the line “Texture2D.ToStream(textureDest, ImageFileFormat.Bmp, ms);” is called at the same time when “SwapChain swapChain = (SharpDX.DXGI.SwapChain)swapChainPtr;” is called, the game crashes with Access Violation.
I have some ideas and if they solve that I’ll let you know what I did.
If you have a work around too, please let me know. Thank you.
Cheers,
Sebastian
Hi Justin!
I’ve tested your App (with SharpDX 263 and uncommented Debugs) with D11 and it works fine! No game crash. Thats why I added the function “EnsureResources” and the Locks from DXHookD3D11.cs to DXHookD3D10.cs but I get an exception in EnsureResources in
“_resolvedRT = ToDispose(new Texture2D(device, new Texture2DDescription()…”:
“SharpDX.SharpDXException: HRESULT: [0x80070057] : Invalid Arguments …
SharpDX.Direct3D10.Device.CreateTexture2D(…)…”
I guess it has something to do with “OptionFlags = ResourceOptionFlags.SharedKeyedmutex” but I would like to know where and why exactly.
So I enabled in VS “Native Code Debugging” and in DirectX SDK “Force On” and added my app to the list but still cannot see any output in VS.
Is there something I’m missing? How do you debug DirectX? Can’t find anything helpful on the net…
Cheers,
Sebastian
Hi I got an error after that texture creation when trying to get the shared handler:
Debug: DXHookD3D11: PresentHook: Exeception: SharpDX.SharpDXException: SharpDX.SharpDXException: HRESULT: [0x80070057], Module: [General], ApiCode: [E_INVALIDARG/Invalid Arguments], Message: The parameter is incorrect.
at SharpDX.Result.CheckError()
at SharpDX.Direct3D11.Device.OpenSharedResource(IntPtr hResource, Guid returnedInterface, IntPtr& resourceOut)
at SharpDX.Direct3D11.Device.OpenSharedResource[T](IntPtr resourceHandle)
at Capture.Hook.DXHookD3D11.EnsureResources(Device device, Texture2DDescription description, Rectangle captureRegion, ScreenshotRequest request) in C:\Projects\Direct3DHook\Capture\Hook\DXHookD3D11.cs
Hook injection works correctly, as I can see the FPS overlay.
(I’m on Windows 10 and working with a DX11 32 bits game)
Reading the docs of SharpDX, it suggets to use Resource1.CreateSharedHandle to create a sharedhandle instead of recovering it using GetSharedHandle but I cannot find a way to get the resource as a Resource1 class instead of a Resource class.
Any tips on where to follow?
Hi Luis, you could try using QueryInterface on the Resource instance, otherwise it will be related to how it is being created to begin with.
Justin,
Hoping you can help… I get this error when sporadically when the client closes. It’s for a 64-bit DX11 game:
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.InvalidOperationException
Stack:
at SharpDX.ComObject.SharpDX.IUnknown.Release()
at SharpDX.Direct3D11.DeviceChild.Dispose(Boolean)
at SharpDX.DisposeBase.CheckAndDispose(Boolean)
at SharpDX.DisposeCollector.DisposeAndClear()
at SharpDX.DisposeCollector.Dispose(Boolean)
at SharpDX.DisposeBase.Finalize()
Any suggestions for this error? I appreciate any insight you can provide.
Looks like an error while cleaning up some resources, can you confirm if this is happening while disposing of the DX11 hook?
Perhaps try using a try..catch in your dispose?
Justin, yes, this error occurs when I call this line:
“_captureProcess.CaptureInterface.Disconnect();”
on a DX11 hook.
I’m attempting to modify the program to inject an off-screen rendered CEFSharp window. But to include CEF sharp I need to configure the project to build for x86/x64 as it doesn’t support AnyCPU according to https://github.com/cefsharp/CefSharp/wiki/Trouble-Shooting
When I do this however I get an error: “STATUS_INVALID_PARAMETER_4: The given 32-Bit library does not exist! (Code: 2)”
When calling RemoteHooking.Inject
The path to the Capture.dll seems fine though and the dll is there. Thoughts?
Is the EasyLoad32.dll also there? RemoteHooking.Inject starts by injecting EasyLoad32/64.dll which in turn loads your target assembly.
I must have been missing it, I made sure to copy all of the dlls again and it worked this time. However the code I am trying to run depends on CefSharp and now the DirectX host app I am hooking is complaining it can’t find one of the CefSharp dll files. I tried placing them in the same directory as the DirectX application, as well as registering them with the gacutil. It’s still unable to find the dll files though. I read the tutorial pdf for easyhook but my impression was I just needed to register the dlls in the GAC or place them in the same directory as the app I was hooking…
Best to not use the GAC as it just complicates the whole process. Ensure EasyHook is completely unregistered from the GAC, and see how you go then.
Hi Justin,
I would very much appreciate your help with some hardships I face trying to run TestScreenshot.
I’ve been trying to run the most recent version on Windows 10 out of VS2012 and had no luck so far. The build goes smoothly, and It looks like all Dlls are in the bin folder, including the newly generated capture Dll. I try to hook to two very basic demo applications, both in the same bin folder. One is based on DirectX 10 and the other on DirectX 11.
Once I run the application and call for “Inject”, in either Win32 or X64 mode, the application waits for 5 seconds and then breaks on the InjectionFailedException exception, with the inner exception stating “Unable to wait for service application due to timeout”. (As I see, this comes from the WOW64Bypass class in EasyHook).
The thing that gets more strange is that when I try to reproduce this now, a day after, the inner exception shows now “STATUS_INVALID_PARAMETER_4: The given 32-Bit library does not exist! (Code: 2)”.
I am pretty stuck at the moment and not sure how to get this to run.
Thank you very much,
Avi S.
Make sure you have all the 32- and 64-bit versions of the EasyHook binaries (e.g. easyhook32/64svc.exe and easyhook32/64.dll and easyload32/64.dll)
Hi Justin, thanks a lot! One of the Dlls was indeed missing, and some paths were incorrect. After fixing this – I got the example to work on Windows 10! Thank you very much!
While I was trying to run the project on Windows 7 as well, I managed to easily hook to a simple DirectX10 window, but when I tried to hook to a simple DirectX11 window, the target window became unresponsive, followed by the message “Display Driver AMD stopped responding, and was successfully restored”. The project itself kept running, and threw debug messages. The problem seems to be related to SharpDX and calling “draw” to the overlayEngine, but I am still not very familiar with the code.
Please let me know if you can help. I really appreciate it.
Have a great year to come!
Avi
This is the debug dump. The first long debug message was thrown 8 times in the activation:
Debug: DXHookD3D11: PresentHook: Exeception: SharpDX.SharpDXException: SharpDX.SharpDXException: HRESULT: [0x887A0005], Module: [SharpDX.DXGI], ApiCode: [DXGI_ERROR_DEVICE_REMOVED/DeviceRemoved], Message: Unknown
at SharpDX.Result.CheckError()
at SharpDX.Direct3D11.DeviceContext.FinishCommandListInternal(Bool restoreDeferredContextState, CommandList& commandListOut)
at SharpDX.Direct3D11.DeviceContext.FinishCommandList(Boolean restoreState)
at Capture.Hook.DX11.DXOverlayEngine.End() in c:\Cyndr\Direct3DHook-master\Capture\Hook\DX11\DXOverlayEngine.cs:line 168
at Capture.Hook.DX11.DXOverlayEngine.Draw() in c:\Cyndr\Direct3DHook-master\Capture\Hook\DX11\DXOverlayEngine.cs:line 161
at Capture.Hook.DXHookD3D11.PresentHook(IntPtr swapChainPtr, Int32 syncInterval, PresentFlags flags) in c:\Cyndr\Direct3DHook-master\Capture\Hook\DXHookD3D11.cs:line 538
Debug: DXHookD3D11: Hook: Device created
Debug: DXHookD3D11: Hook: Before device creation
Debug: DXHookD3D11: Hook: Begin
Debug: Autodetect found Direct3D 11
Information: Remote process is a 32-bit process.
Information: Injected into process Id:73912.
“DeviceRemoved” sounds like the device was disposed of. Are you using the latest version of the source located in the GitHub repository? Does it work fine elsewhere or with other DirectX11 apps on your Win7 machine? Also it might be easier to log it on the GitHub repository as an issue.
Hi Justin, Thank you for always responding right away, and sorry it took me a while to write you back.
I am using the latest version of the GitHub source. It works fine when I use it with DirectX9, 10, and 11 on Windows 10. It also works fine when I use it with DirectX 9 and 10 on Windows 7.
It is only DirectX 11 on Windows 7 that causes the crash.
I will post the issue up.
Thank you very much!
Avi
Hi Justin,
Hope you’ve been doing well.
I’ll really appreciate if you can please help me with this problem I’ve been struggling with:
I am using Direct3DHook DLL (Capture.DLL) and building a wrapper DLL around it in order to be able to use the Direct3DHook from C++ code. I am using a public ref class for this, and the whole thing works fine on Visual Studios 2015.
The calls sequence goes like: (My Main C++ function)–>(My Capture_DLL_Wrapper functions)–>(Capture.DLL HookIntoProcess function)… Then EasyHook DLLs are called. This works very nicely on Visual Studios 2015 and I can see the FPS for hooked games.
Now, I am trying to get this same sequence to work in QT Creator. I added message boxes along the calls path and I can see that the call to “RemoteHooking.Inject” crashes from within EasyHook with the error:
“STATUS_INTERNAL_ERROR Unknown error in injected c++ completion 15”.
I added the following definitions to the .pro file:
CONFIG(debug, debug|release) {
LIBS += “C:/Users/Andrei/Desktop/Projects/QT/build-sAvi/debug/CaptureDllWrapper.lib”
LIBS += “C:/Users/Andrei/Desktop/Projects/QT/build-sAvi/debug/CaptureDllWrapper.dll”
LIBS += “C:/Users/Andrei/Desktop/Projects/QT/build-sAvi/debug/capture.dll”
LIBS += “C:/Users/Andrei/Desktop/Projects/QT/build-sAvi/debug/EasyHook.dll”
LIBS += “C:/Users/Andrei/Desktop/Projects/QT/build-sAvi/debug/EasyHook32.dll”
LIBS += “C:/Users/Andrei/Desktop/Projects/QT/build-sAvi/debug/EasyLoad32.lib”
}
I made sure that all files are in the folder, but still.. I get this exception.
I’ll really appreciate if you can give me some advise, as I’ve been struggling with the “dll hell” for quite a while. Thank you very much!
Avi
Hi, Sometimes I get this error.
What’s the problem? can you help me?
DXHookD3D9: SharpDX.SharpDXException: HRESULT: [0x8876086C], Module: [SharpDX.Direct3D9], ApiCode: [D3DERR_INVALIDCALL/InvalidCall], Message: Unknown
at SharpDX.Result.CheckError()
at SharpDX.Direct3D9.Device.StretchRectangle(Surface sourceSurfaceRef, Nullable`1 sourceRectRef, Surface destSurfaceRef, Nullable`1 destRectRef, TextureFilter filter)
at SharpDX.Direct3D9.Device.StretchRectangle(Surface sourceSurfaceRef, Surface destSurfaceRef, TextureFilter filter)
at Capture.Hook.DXHookD3D9.DoCaptureRenderTarget(Device device, String hook) in C:\Users\DelphiCoder\Desktop\Direct3DHook-master\Capture\Hook\DXHookD3D9.cs:line 341
Hi Ahmet, this could be some incompatibility with the two surface descriptions. The error is happening internally to Direct3D, if this is your 3D app then you could use a debug DirectX device and get some additional details, otherwise it may require you to investigate the details of the two surfaces, and the capabilities of the Direct3D device that was created by the target application.
Hey Justin, Thanks for the awesome work, this is awesome!
I was having trouble getting it to work with UWP Apps. Notably Microsoft’s Solitaire Collection. Have you run into this at all?
(I forget the exact error right now but while I was here I figured I would mention it!)
Hi Riley, it is very difficult to inject into UWP Apps. Not impossible but you may need to locate your DLLs within a system directory in order to do so (LoadLibrary does not support paths within UWP).
I haven’t looked into this much further than some precursory reading.
Good luck and feel free to comment back here as to how you go.
So, I just quickly downloaded the Git repo and compiled and ran… no overlays are happening on D3D9 application. I’m trying to follow your code, but am having a tough time. I set breakpoints where i think it should be setting the overlay, but they never get hit.
Does the current version just not call the methods that apply the overlay?
never mind.. it’s getting hit, it’s just not showing the overlay. I am assuming it has to do with that I am running Win7x64 from Parallels on a mac. I’ll have to try on a different computer.
Have you got any clue what was going wrong? I am having a similar problem for a D3D9 game. Screen capture works fine, no error messages, but no overlays at all. I am running Win10x64 from BootCamp on a macbook.
Is it possible to use this project to display winform/wpf as overlay to the fullscreen game?
If yes, how? Can someone explain me process of displaying winform elements as overlay? I understand the concept of drawing text, but how can I display real form?
You could render the form to an image and then display that upon the overlay.
Is it possible to bring a polygon into the game using this project?
Please show an example.
Yes. Take a look at the Direct3DHook project on GitHub and the overlay engine. It is only rendering quads but it should give you a starting point.
How to do it? Can you give an example?
The game crashes, after frequent use of _captureProcess.CaptureInterface.DrawOverlayInGame (new Capture.Hook.Common.Overlay {}).
How can I call DrawOverlayInGame once and change the text?
Hi,
Thanks for the great product.
I run demo success on windows 10 pro 64.
But on windows 10 enterprise 64, it had error “STATUS_NO_MEMORY: Unable to allocate memory in target process. (Code: 5)”. i use process game fifa online 4. Can you check help me cause the error ?
Thanks .
Hi Tuyen, this error is typical when the target employs memory protection techniques like you would find in anticheat libraries. In this instance you may need to try different injection techniques, or you can use the Desktop Duplication API to record (but even this can also be blocked).
Thanks Justin Stenning,
I will try some solutions.
Justin, my overlay disappears when I ALT+TAB with a DX11 game.
I start in full screen mode, and both the hook and on-screen display are working beautifully.
However, when I ALT+TAB to the Windows 10 desktop, and then return back to the full screen game, ResizeTargetHook gets called, but then PresentHook appears to not be getting called again after that, and my overlay disappears.
Any Idea/suggestion?
Please let me know, and best regards!
I recently tried this tool on Battlefield V and a few days later I received a message from EA that my game got permanently blocked for breaking the rules in online play.
After emailing EA they refused to lift the permanent block because I have violated their user agreement which was most likely caused by this tool because I had no other fps tools running at the time.
I just wanted to share this warning that you need to be very careful when you use this tool in online games that have an anti-cheat system in place, especially in EA / Origin games because they are unforgiving and their support is not listening to reason.
Thanks for sharing your experiences Arnold. This is actually the first instance of a ban due to simple overlays using this example that has been reported to me.
Hi, is it neccessary for my IOvelayElement implementations to be Serializable? Because without this attribute they dont work. It limits me hardly (because I cant use any non-serializable objects as fields) and dont get the reason behind that…
I think due to the way we send IOverlayElement over IPC this is required (MarshalByRef).
And also im for some reason unable to call methods from my main thread, f.e. i have a main class with a field List elements = new List(); (those elements are later passed into captureProcess.CaptureInterface.DrawOverlayInGame(new Overlay {Elements = elements, Hidden = false}); ). And if I write MainClass.elements.remove(this) in the code of my element implementation, it simply not happens. And I cant call any methods from MainClass, if I do this, overlay is just not working (neither my elements, not FramesPerSecond).
So the element is sent to the overlay, and then within there it tries to call back to your host app? Sounds like IPC setup issues. .NET remoting will be removed from EasyHook in a future version in favour of whatever user supplied approach you wish to use (e.g. signalR, pipes, https, whatever).