2010-05-03 10 views
6

Tengo un programa al que agrego efectos de posprocesamiento a pantalla completa. No tengo la fuente del programa (es propiedad, aunque un desarrollador me envió una copia de los símbolos de depuración, formato .map). Tengo el código para los efectos escritos y en funcionamiento, sin problemas.Evitar desbordamientos de pila en DLL contenedoras

Mi problema ahora es vincular los dos.

He intentado dos métodos hasta ahora:

utilizar desvíos para modificar la tabla de importación del programa original. Esto funciona muy bien y se garantiza que sea estable, pero los usuarios con los que he hablado no se sienten cómodos, requiere instalación (más allá de extraer un archivo), y hay algunas dudas si el parcheo del programa con Detours es válido bajo los términos del EULA. Entonces, esa opción está fuera.

La otra opción es la tradicional sustitución de DLL. Envolví OpenGL (opengl32.dll) y necesito que el programa cargue mi DLL en lugar de la copia del sistema (simplemente colóquelo en la carpeta del programa con el nombre correcto, eso es fácil).

Necesito mi DLL para cargar el marco de trabajo Cg y el tiempo de ejecución (que se basa en OpenGL) y algunas otras cosas más. Cuando carga Cg, llama a algunas de mis funciones, que llaman funciones Cg, y tiendo a obtener desbordamientos de pila e infinitos bucles. Necesito poder incluir las DLL de Cg en un subdirectorio y seguir usando sus funciones (no estoy seguro de si es posible hacer que mi DLL importe un punto de tabla a una DLL en un subdirectorio) o necesito vincularlos dinámicamente (lo cual más bien, no hacer, solo para simplificar el proceso de compilación), algo para obligarlos a consultar el archivo del sistema (no mi reemplazo personalizado).

Toda la cadena es: El programa carga DLL A (denominado opengl32.dll). DLL A carga Cg.dll y vincula dinámicamente (GetProcAddress) a sysdir/opengl32.dll. Ahora necesito Cg.dll para referirse también a sysdir/opengl32.dll, no DLL A.

¿Cómo se haría esto? Editar: ¿Cómo se haría esto fácilmente sin usar GetProcAddress? Si nada funciona, estoy dispuesto a recurrir a eso, pero prefiero no hacerlo si es posible.

Edit2: Acabo de toparme con la función SetDllDirectory en los documentos de MSDN (en una búsqueda totalmente no relacionada). A primera vista, parece lo que necesito. ¿Es correcto o estoy juzgando mal? (apagado para probarlo ahora)

Edit3: He resuelto este problema haciendo las cosas un poco diferente. En lugar de descartar un OpenGL32.dll, he cambiado el nombre de mi DLL a DInput.dll. No solo tiene la ventaja de tener que exportar una función en lugar de más de 120 (para el programa, Cg y GLEW), no tengo que preocuparme por las funciones que se ejecutan de nuevo (puedo vincularme a OpenGL como siempre) . Para entrar en las llamadas que necesito interceptar, estoy usando Detours. En general, funciona mucho mejor. Esta pregunta, sin embargo, sigue siendo un problema interesante (y con suerte será útil para cualquier otra persona que intente hacer cosas locas en el futuro). Ambas respuestas son buenas, por lo que no estoy seguro todavía de elegir ...

+0

Para cualquier persona que se encuentre con esta pregunta en el futuro, las mejores opciones (que terminé usando más adelante) es ** no use Desvíos **. Tiene errores, especialmente en WoW64, y le faltan funciones básicas como la prevención de recursión. [EasyHook] (http://easyhook.codeplex.com/) es muy superior en funciones, estabilidad/rendimiento y manejo general. – ssube

Respuesta

1

SetDllDirectory probablemente no funcionará. Cg.dll probablemente solo enlaces a OpenGL.dll. Cuando el sistema operativo carga Cg.dll, ve que ya hay un módulo cargado con ese nombre (el suyo), por lo que vincula Cg con eso en lugar de ir a buscar otra copia. Es decir, el orden de búsqueda que SetDllDirectory modifica nunca entra en juego porque el sistema operativo no realiza ninguna búsqueda.

Sospecho que su mejor opción será, de hecho, detectar llamadas de reingreso a su biblioteca. Cuando detecte uno, en lugar de realizar su gestión personalizada, reenvíe la llamada directamente a la biblioteca OpenGL real, a la que tiene una referencia debido a llamar a LoadLibrary y luego a GetProcAddress para cada una de las funciones de la biblioteca.

+0

¿Tiene alguna sugerencia sobre la forma más fácil de detectar estos? No me he encontrado con este problema antes, así que no estoy familiarizado con la mejor manera de manejarlo. Agregar un fragmento al comienzo de cada función para llamar al real en una condición particular no es un problema, pero ¿qué es lo mejor que se debe verificar? – ssube

+1

En realidad, tuve un pensamiento. ¿Sería efectivo y eficiente usar un valor booleano global simple? Al comienzo de mi función, marque 'if (cameBack) {call realFunc; } else {cameBack = true; } ', luego configúralo como falso cuando la ejecución se va. Eso suena como si fuera trabajo. Por lo que yo sé, mi código no necesita ser seguro para subprocesos (el programa tiene una sola cadena de representación y un solo subproceso que hace referencia a OpenGL, desde mi depuración). – ssube

+1

Una variable global es exactamente lo que habría usado en un primer intento. Si necesita varios subprocesos, puede probar el almacenamiento local de subprocesos en lugar de simples globales. Si una de las funciones que intercepta termina llamando a una función * diferente * OpenGL que * también * desea interceptar, puede usar variables globales específicas de la función en lugar de solo una. –

1

Puede utilizar la magia de los contextos de activación para intentar resolver su problema.

Mucho depende del clima, los componentes de terceros en su sistema ya tienen manifiestos y la cantidad de manipulación de esos manifiestos podría constituir una violación de la licencia.

Para resolver problemas de control de versiones dll, Windows XP obtuvo una tecnología llamada contextos de activación. A veces conocidos como conjuntos de lado a lado, o incluso algo horrible como Application Isolation

Para resumir un montón de lectura en un espacio pequeño: manifiestos son fragmentos de datos XML que pueden describir un conjunto, o describen una dependencia en asambleas. Un ensamblado es un manifiesto, más su dll.

La razón de esto es que un ensamblado puede tomar un dll simple. "comctl32.dll" y su número de versión (v6), y crea una cosa con un nombre más grande y único, de forma que se puedan instalar múltiples versiones del dll simple en el mismo lugar de manera segura. Los ensambles están destinados a instalarse en C:\Windows\WinSxS.

Cuando un archivo de manifiesto describe las dlls en un ensamblado, se denomina manifiesto de ensamblaje. Y generalmente tiene un nombre diferente al dll.

Cuando un archivo de manifiesto describe los ensamblajes que usa un dll o exe, se llama un manifiesto de aplicación y generalmente se incorpora como un recurso RT_MANIFEST - en EXEs con un ID de Res de 1, en Dlls con un id de Res de 2 - o en el disco como un archivo con un nombre "appname.exe.manifest"/"dllname.dll.2.manifest". Los manifiestos de aplicación definen una cosa llamada contexto de activación, que básicamente es un espacio de nombres en el que las ventanas buscarán cosas. Cada manifiesto crea un contexto de activación. Cada contexto de activación tiene una asignación de nombres dll simples a ensamblados.

Por lo tanto, si se crea un montaje con su archivo Opengl32.dll, y crear un contexto de activación para la app.exe referencia (el Opengl32.dll local) de archivos y, a continuación, potencialmente, todos los archivos DLL restantes pueden (y continuará usando el archivo de sistemas opengl32.dll a pesar del hecho de que los nombres son muy similares coff.

El problema es que la res-id del manifiesto de la aplicación - 1 - significa que se usa para crear el contexto de activación predeterminado del proceso, por lo que TODOS los que no tienen sus propios manifiestos explícitos (Cg?) Van a buscar el espacio predeterminado proceso y encontrar que Opengl32.dll

Así que hay que crear manifiestos para cada DLL que no tenga ya incrustar uno, asegurándose de que simplemente no hacer referencia a su ensamblaje Opengl32.dll, que debería permitir a continuación, vuelva al orden de búsqueda predeterminado y encuéntrelo en la ubicación normal del sistema32.

esto significa que su opengl32.dll no puede estar en la carpeta del exe ya que esa carpeta se busca dll antes de system32 (el hecho en el que confía para enganchar).

Nos guardamos por el orden de búsqueda bastante simple que el sistema toma cuando se buscan ensamblajes. primero busca en WinSxS. Tu Opengl32.dll no estará allí, la instalación es un problema difícil. Luego busca en la carpeta del exe, para una subcarpeta con el nombre del ensamblado, ENTONCES busca directamente en la carpeta del ejecutor el manifiesto de ensamblajes.

Esto significa que puede crear un ensamblado - llamado algo así como: "OpenGLHook" Y la estructura de carpetas se vería así:

\appfolder\ 
    app.exe 
    app.exe.manifest     - contains a dependentAssembly node to OpenGLHook 
    OpenGLHook\OpenGLHook.manifest - contains a file name=opengl32.dll 
    OpenGLHook\opengl32.dll   - your hook dll 
    yourimpl.dll      - your implementation dll that linkgs to cg.dll 
    cg.dll       - cg libraries 
    cg.dll.2.manifest     - a stub manifest you put together to ensure cg 
             doesnt use the app default activation ctx. 

Um, buena suerte?

Cuestiones relacionadas