Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Interceptor de llamadas a D3D9 - Juan Mellado

Iniciado por ethernet, 06 de Abril de 2003, 05:44:27 PM

« anterior - próximo »

ethernet



    La versión 9 de DirectX trae una utilidad llamada D3DSpy que intercepta las  llamadas a D3D9 que realizan los programas. Similar a la conocida utilidad OGLTrace para OpenGL.

    El código que presento muestra una de las muchas formas de implementar este  tipo de programas interceptores.

    La ídea basicamente consiste en crear una DLL llamada d3d9.dll que exporte una función Direct3DCreate9, y colocarla en el mismo directorio que el ejecutable  que queremos monitorizar. De forma que cuando el ejecutable llame a Direct3DCreate9, no llamará a la DLL de DirectX sino a nuestra DLL. Nuestra Direct3DCreate9 retornará un puntero a una clase especializada (que hereda) de IDirect3D9 para que podamos modificar su comportamiento:





//

// Implementación de Direct3DCreate9

//

IDirect3D9 * WINAPI Direct3DCreate9(UINT SDKVersion)

{

   HINSTANCE l_hModule = LoadLibrary("c:windowssystem32d3d9.dll");

   if (NULL == l_hModule){

        return(NULL);

       }



   DXPROC l_pfnFunction = (DXPROC)GetProcAddress(l_hModule,



"Direct3DCreate9");

   if (NULL == l_pfnFunction){

        return(NULL);

       }



   IDirect3D9 * l_pD3D9 = l_pfnFunction(SDKVersion);

   if (NULL == l_pD3D9){

        return(NULL);

       }



   return( new D3D9Trace::CD3D9Trace(l_pD3D9) );

}




Nota: El path y nombre de la DLL está hard-code, pero sería fácil leerla de un .INI para poder cambiar su ubicación o nombre, por ejemplo para cargar la DLL  de depuración (d3d9d.dll).

La clase CD3D9Trace hereda de IDirect3D9 e implementa todos los métodos de la misma. Veámosla resumida:





class CD3D9Trace : public IDirect3D9

{

public:

   CD3D9Trace::CD3D9Trace(IDirect3D9 * const pv_pInterface)

       : m_pInterface(pv_pInterface)

   {

   }

...

//

// IUnknow

//

private:

   HRESULT STDMETHODCALLTYPE CD3D9Trace::QueryInterface(REFIID riid, void**



ppvObj);

   ULONG   STDMETHODCALLTYPE CD3D9Trace::AddRef(void);

   ULONG   STDMETHODCALLTYPE CD3D9Trace::Release(void);

//

// IDirect3D9

//

private:

   HRESULT  STDMETHODCALLTYPE CD3D9Trace::CheckDepthStencilMatch(UINT



Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT



RenderTargetFormat, D3DFORMAT DepthStencilFormat);

...

   CD3D9Trace::RegisterSoftwareDevice(void *pInitializeFunction);

//

// Miembros

//

private:

   IDirect3D9 * m_pInterface;

};





De esta forma, cada vez que el programa llame a un método de IDirect3D9 en realidad lo hará a través de nuestra clase. Por ejemplo:





HMONITOR STDMETHODCALLTYPE CD3D9Trace::GetAdapterMonitor(UINT Adapter)

{

   _ASSERTE(NULL != GetInterface() );



   return( GetInterface()->GetAdapterMonitor(Adapter) );

}





Esto da la oportunidad de realizar algún tipo de pre-proceso o post-proceso en cada llamada.

Entre los posibles usos, está el típico de añadir trazas de depuración (para    
ver el orden de las llamadas y el tiempo que tarda en ejecutarse cada una de ellas), realizar pruebas simulando fallos en llamadas a funciones (¿como se comportaría nuestro programa si una llamada a CreateVertexBuffer devolviera D3DERR_OUTOFVIDEOMEMORY?), poner pausas activas para simular comportamientos en hardware más lentos al nuestro, monitorizar el código cuando no tenemos los fuentes (¡ejem!), hacer pruebas de stress simulando fallos en la adquisiciónde recursos, ...

La principal ventaja es que el código de depuración y prueba está separado del código de producción. De forma que se pueden hacer pruebas sin modificar el  código. Y sin "empantanarlo" con macros, mensajes de trazas, o condiciones de prueba.

En el .zip se distribuye un proyecto en Visual C++ 6 con el código de la DLL y un ejecutable de ejemplo. En la DLL se ha modificado GetDeviceCaps() para que simule que el tamaño de las dimensiones máximas de una textura soportados por la tarjeta es 256x256.

Como posibles mejoras estaría implementar clases que hereden del resto de interfases expuestas por D3D. Por ejemplo, sobrecargando el método
CreateDevice para que retorne una clase especializada de IDirect3DDevice9.

Puedes bajarte el codigo de:

http://www.stratos-ad.com/codigosemana/d3d9trace.zip


Saludos, Juan Mellado

Éste código fue enviado por Juan Mellado, si quiere enviar tu propio código hazlo a eth_cotd@lycos.es


    mICrO

                                    Magnifico codigo de la semana.

    mICrO                                
    ICrO : log off

    ethernet

    Si, rocks, lo q molaria de verdad te lo juro seria algo asi pero ampliable a cualquier libreria, aunque no se me ocurre la manera elegante de hacerlo :/

    saludos

    Juan Mellado

                                    Gracias mICrO.  :D

    ethernet:
    ¿A cualquier librería?. Si te refieres a coger cualquier DLL (que no sea tuya o no documentada) y hacerle un proxy, lo veo complicado. Puedes averiguar las funciones que exporta una DLL (como hace la herramienta Depends que viene con el Visual C++), y crear otra DLL que exporte las mismas funciones. Pero lo que no tengo tan claro es cómo averiguar el número y tipo de los parámetros de cada función. Así como la forma en que se declaró la función (stdcall, naked, ...).

    Otro tema sería hacerlo con librerías que expongan objetos COM o similares. Ya que algunos ofrecen interfaces con métodos que informan de las interfases, funciones y parámetros expuestos (similar a los WebServices, por ejemplo). Eso es lo que permite insertar objetos OLE en diversas aplicaciones, porque exponen sus interfaces y la definición de las mismas.

    Por otra parte, si lo que te refieres es a crear una "super-template" que te cree una clase adaptadora de una clase tuya, te puede servir de punto de partida el siguiente código sacado de "Modern C++ Design":



    class Interface

    {

    public:

     virtual void Fun() = 0;

    }



    template <class T, class P>

    Interface* MakeAdapter(const T& obj, const P& arg)

    {



     class Local: public Interface

     {

       public:

        Local(const T& obj, const P& arg)

         : obj_(obj), arg_(arg) {}

        virtual void Fun()

        {

         obj_.Call(arg_);

        }

       private:

        T obj_;

        P arg_;

     };



     return new Local(obj, arg);

    }



    No sé, depende de lo que quieras hacer exactamente.

    Saludos                                

    ethernet

    Me referia dada una libreria, hacer exactamente lo q hace el cotw ,obviamente es imposible obtener algo tn orientado pero si obtener un sistema basico de debug.

    saludos






    Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
    Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
    Muchísimas gracias.