top of page

DirectX 9 Wrapper

  • Writer: Admin
    Admin
  • Dec 29, 2017
  • 2 min read

In the last post we discussed a few of the methods we can use to intercept DLL function calls. I ended up using a wrapper for drawing my custom HUD for dota2. Most of the proxy code is borrowed from here except for a few additional export functions I had to add because Dota2 was expecting them.

Typical directx usage involves calling Direct3DCreate9 to get a pointer to the directx interface ("IDirect3D9"). This pointer is then used to call "CreateDevice" to fetch a pointer to the device interface ("IDirect3DDevice9"). Our wrapper implements two classes which inherit from "IDirect3D9" and "IDirect3DDevice9" respectively. We return objects of these derived classes to the application, which lets us intercept all calls being made through them. Let's go through the important bits of the proxy.

The first part is the "DLLMain" function, called when the DLL is loaded.

We load the original directx library when our proxy is loaded, and store the addresses of all the functions our proxy exports, so that we may call them after executing our custom behavior. The next important function is "Direct3DCreate9", which we change to return our derived object.

We construct the derived object with a call to the original "Direct3DCreate9" function, and our derived object holds on to this pointer to the original directx interface.

Next, we override the "CreateDevice" member function in IDirect3D9 to construct an object of our derived device class instead.

This is a little hard to read but what we're doing is

  • Creating an object of our device wrapper class

  • Creating an IDirect3DDevice

  • Storing a pointer to this device in our wrapper class, so that we can call the original functionality

  • Storing a pointer to our device wrapper object in the "ppReturnedDeviceInterface" argument, to be returned to the application

For my purposes, there was only one function I cared about, the "EndScene" function, which is called after all elements have been drawn. We can use it to reliably draw on top of everything else in the application, which is exactly what I needed. I did call "onLostDevice" and "onResetDevice" in the "Reset" function as recommended. The rest of the functions simple forwarded all calls to the original device.

Example of custom behavior (I will get to the HUD class in the following posts):

Examples of forwarded functions:

Finally, you need to provide a .def file with your DLL which specifies the functions exported by your DLL. You do not need to list all of the functions exported by the original DLL, just the ones used by the application you are targeting. For a list of all exported functions, check out Dependency Walker. In most cases, simply exporting "Direct3DCreate9" will suffice. I started off with just the single function in my .def file and added the others that were needed (the application will notify you that it could not find a particular function in the dll on launch). My final .def file looked like thus:

Make sure that you are compiling the DLL for the correct platform, as a 32 bit DLL will not work with a 64 bit application.


Comentários


bottom of page