Jump to content

  • Log In with Google      Sign In   
  • Create Account


Member Since 11 Nov 2012
Offline Last Active Nov 14 2012 10:37 PM

Topics I've Started

Managed Starcraft 2 AI Bot, SlimDX and C++, COM Interoptability Issues?

11 November 2012 - 06:38 AM

I'm new to DirectX. I've made the beginnings of a game in XNA, so I am familiar with the concept of a game loop, with beginning and ending a scene (called SpriteBatch.Begin() and SpriteBatch.End() in XNA), with the basic idea of a texture as 'something drawable'. My unfinished game had a working map, collision detection, and a map editor. So I'd say I'm pretty familiar with C#. But I'm completely new to C++ and DirectX. So I discovered this DirectX book. I've skimmed over the first 5 chapters, where I've learned a bit more. For example, I now know what a pixel shader and a vertex shader is used for. And I also have a very simplified high-level overview of 'the DirectX process': transforming, lighting, projection, clipping, rasterizing.

My ultimate goal is to recreate this project, only in managed C# instead of native C++. In short, it's a Starcraft 2 AI bot. Matthew Fischer's strategy was to create a proxy d3d9.dll (automatically loaded by the DirectX application because it searches in its current directory for d3d9.dll first), which would intercept d3d9.dll's API stream, and then interpret the data of certain API calls. For example, he deconstructed a scene from multiple SetTexture() calls, saving each referenced texture and personally classifying it (that's how he came up with the "Name" column in the table I think). The next time SetTexture() gets called, he "understands" the specific object being rendered, and so, he can build a state machine, and, eventually, an AI. (This is pretty simplified, because he did tons of other things, like intercepting the vertex shader to get the final 2D screen coordinate squashed from 3D model space (he had to write his own disassembler for that)).

I think I understand Fischer's general idea. My goal is to somehow achieve the same effect in managed C#. I'm using C# because it's my most familiar (and favorite) language. C# code looks a lot cleaner to me, and the .NET framework has an extensive library. Admittedly, this current situation is probably one example of where C# shouldn't be used. But until I'm convinced it's unrealistically difficult, I'm going to try doing this with C#. Instead of a proxy DLL (since that can't be written in managed code), I'm using EasyHook to intercept Direct3D functions. I found this excellent example by Spazzarama, which I've organized and adapted into my own custom library. From his example, I was able to hook Reset() and EndScene(). I've uploaded my personal proof of concept to this post. I'm now on my way to define the function signatures and necessary interfaces for the other 117 functions in d3d9.dll.

Which is where I run into problems with SlimDX and COM. Again, my goal is to create 119 hook methods, one for each function. Here are what they look like so far. In the code comments, I detail my process and problem.

* These are function hooks.
* They run inside the injected process, and they're called many times per second by DirectX (for some of them).
* The "int" return value of these hook methods are equivalent to the "HRESULT" on most Direct3D API functions.
private int EndScene(IntPtr devicePointer)
	   // Parameter:
	   // Original C++: LPDIRECT3DDEVICE9 devicePointer
	   // C#: IntPtr devicePointer
	   // ^ That was easy to port from C++ to C#. It's a pointer, so just use IntPtr.
	   // ...
	   // Gracefully return from the hook to the injected process
	   return Device.FromPointer(devicePointer).EndScene().Code;
	   // SlimDX's "Code" is equivalent to the HRESULT return value in most Direct3D API functions.
private int Reset(IntPtr devicePointer, ref Direct3DPresentParameters presentParameters)
	   // Parameter:
	   // C#: IntPtr, ref Direct3DPresentParams
	   // ^ The beginning of our SlimDX COM issues. SlimDX's PresentParams class can't be used, even though it already exists. It "can't be converted into a COM object". So I have to make my own C# struct equivalent.
	   // ...
	   // Gracefully return from the hook to the injected process
	  return device.Reset(newPresentParams).Code;
private int Present(IntPtr devicePointer, RECT source, RECT dest, IntPtr destWindowOverride, RGNDATA dirtyRegion)
	   // Same thing as above; luckily, these are all very basic structs and easy to recreate in C#.
	   // But notice how there are so many of these custom structs...
	   // ...
	   return device.Present().Code;
private int SetTexture(IntPtr devicePointer, int stage, IDirect3DBaseTexture9 texture)
	   // I'm stuck here
	   // For the previous methods, it was easy to create basic structs (even though there were a lot). But this time, I had to create an actual *interface*. The problem lies in casting this interface to SlimDX's concrete classes. It can't be done in COM. My custom interface IDirect3DBaseTexture9, has to somehow be re-created to SlimDX's equivalent "BaseTexture" class.
	   // Because I have to eventually call
	   return device.SetTexture(stage, new Texture ...
// - or -
//	 return device.SetTexture(stage, new CubeTexture ...
// - or -
//	return device.SetTexture(stage, new VolumeTexture ...
	   // Because BaseTexture cannot be instantiated. Choose from the derived classes Texture, CubeTexture, or VolumeTexture.
	   // Texture, CubeTexture, and VolumeTexture are all *managed* SlimDX classes. IDirect3DBaseTexture9 was my own custom interface.
	   // SlimDX's Texture class wants width, height, usage, format, and pools. But I don't have any of these properties from my custom interface. These properties aren't specified in the documentation. So how am I supposed to return a width and height?

My question is: How can I make SlimDX play nicely with my custom interfaces?

I need to use these custom interfaces. For example, on the SetTexture hook, it will not work if i replace the signature with:

private int SetTexture(IntPtr devicePointer, int stage, IDirect3DBaseTexture9 texture)  // Original working method signature
private int SetTexture(IntPtr devicePointer, int stage, SlimDX.BaseTexture texture)  // New method signature, won't work, cannot cast to COM object

My guess to this problem is, to simply not use SlimDX. Would that be one solution? My guess is, if I didn't use SlimDX, then I would have to [DllImport] 119 functions from "d3d9.dll", because SlimDX does all that work for me. It's just that SlimDX goes too far and they're not the COM objects EasyHook needs.

Since I'm new to all these concepts, I may be asking the wrong question. Please let me know if there's a better way to get what I'm after (still using graphics interpretation though) or if I've got my concepts mixed up. Thanks in advance.