Update: Since new DLLs were pushed out a while back, this no longer works. The function offsets are wrong, and the hook injection method is a little too flaky to be relied upon. Feel free to use the code as a basis, but I’d recommend the use of Microsoft Detours for the hook injection. One day I’ll write a new version along these lines but until then you’re on your own.
A sample for hooking a Direct3D 9 program and drawing on its viewport under Windows Vista 32 and XP. Translating this to Direct3D 8 should be trivial.
Previous versions: Original source file (XP Only) - CPP file only
The new version is agnostic between Vista and XP, but has undergone only minimal testing. Please report any crashes.
While the zip contains a VC++2008 project, all of the real work is in the cpp file so don’t worry if you’re not up to date with the IDE.
For easier debugging, the Initialise function now returns 0 for success, (as opposed to -1 previously). Watch out for this if you’re updating a project.
This is not safe for 64-bit consumption, though that should be obvious.
While there’s no reason it can’t be made to work with Unicode, I’ve written everything in ASCII, for simplicity.
By default, the DLL will increase its own reference count to prevent it being unloaded prior to termination of the host process. This is because there is a small risk of the DLL being unloaded by one thread, while a hooked function in another returns to the now dead memory. I figured that it’s best to waste a little bit of everybody’s memory than to crash unnecessarily.
The d3d9.dll function addresses (and prologues) are hard-coded, or at least their offsets are. While this may look very unprofessional and rather risky, I can assure you that it’s quite safe. The alternative would be to hack up some virtual-function tables and that’s a whole other story for a whole other post.
You may notice that the compiled DLL is dependent upon D3DX. This isn’t necessary for the hook itself, but I used ID3DXFont in my example for demonstrative purposes. The only reason I mention this is that there is no way to guarantee the existence of any D3DX DLLs on a DirectX 9 machine, and distributing them yourself is in violation of the DirectX Runtime EULA. So if you happen to need to distribute this code, you’ll either need to carry the huge runtime installer around, or avoid using D3DX altogether.
The soft-hooks used here will cause problems with PunkBuster if applied to any of its monitored functions. If you need to do this then you’ll have to be a bit cleverer.
The source assumes that the graphics device will never become invalid. If you suspect that this isn’t the case (which will be true for any full-screen game at a minimum) then you’ll need to add the appropriate sanity checks (see IDirect3DDevice9::TestCooperativeLevel) before attempting to render anything, lest you want to crash and burn.