2010-05-03 14 views
6

J'ai un programme auquel j'ajoute des effets de post-traitement en plein écran. Je n'ai pas la source pour le programme (c'est propriétaire, bien qu'un développeur m'envoie une copie des symboles de débogage, format .map). J'ai le code pour les effets écrits et fonctionnels, pas de problèmes.Éviter les débordements de pile dans les DLL d'encapsulation

Mon problème est maintenant de relier les deux.

J'ai essayé deux méthodes à ce jour:

Utilisez pour modifier Detours la table d'importation de programme original. Cela fonctionne très bien et est stable, mais l'utilisateur à qui j'ai parlé n'est pas à l'aise, il nécessite une installation (au-delà de l'extraction d'une archive), et il y a des doutes sur le fait que le programme Detours est valide du CLUF. Donc, cette option est sortie.

L'autre option est le remplacement de DLL traditionnel. J'ai enveloppé OpenGL (opengl32.dll), et j'ai besoin du programme pour charger ma DLL au lieu de la copie du système (il suffit de le déposer dans le dossier du programme avec le bon nom, c'est facile). J'ai alors besoin de ma DLL pour charger le framework Cg et le runtime (qui repose sur OpenGL) et quelques autres choses. Quand Cg se charge, il appelle certaines de mes fonctions, qui appellent des fonctions Cg, et j'ai tendance à avoir des débordements de pile et des boucles infinies. Je dois être capable d'inclure les DLL Cg dans un sous-répertoire et d'utiliser leurs fonctions (je ne sais pas s'il est possible que mes DLL importent la table pointent vers une DLL dans un sous-répertoire) ou je dois les lier dynamiquement d plutôt ne pas faire, juste pour simplifier le processus de construction), quelque chose pour les forcer à se référer au fichier du système (pas mon remplacement personnalisé).

La chaîne entière est: Le programme charge la DLL A (appelée opengl32.dll). DLL A charge Cg.dll et lie dynamiquement (GetProcAddress) à sysdir/opengl32.dll. J'ai maintenant besoin de Cg.dll pour se référer également à sysdir/opengl32.dll, pas DLL A.

Comment cela serait-il fait? Modifier: Comment cela serait-il fait facilement sans utiliser GetProcAddress? Si rien d'autre ne fonctionne, je suis prêt à y revenir, mais je préfère ne pas le faire du tout.

Édition2: Je viens de trébucher sur la fonction SetDllDirectory dans les documents MSDN (sur une recherche totalement indépendante). À première vue, cela ressemble à ce dont j'ai besoin. Est-ce exact, ou est-ce que je mésestime? (off pour le tester maintenant)

Édition3: J'ai résolu ce problème en faisant quelque chose d'un peu différemment. Au lieu de supprimer un OpenGL32.dll, j'ai renommé ma DLL en DInput.dll. Non seulement cela a l'avantage de devoir exporter une fonction au lieu de bien au-delà de 120 (pour le programme, Cg et GLEW), mais je n'ai pas à m'inquiéter des fonctions qui retournent (je peux lier OpenGL comme d'habitude) . Pour entrer dans les appels que j'ai besoin d'intercepter, j'utilise Detours. Dans l'ensemble, cela fonctionne beaucoup mieux. Cette question, cependant, est toujours un problème intéressant (et j'espère sera utile pour quelqu'un d'autre qui essaie de faire des choses folles à l'avenir). Les deux réponses sont bonnes, donc je ne suis pas encore sûr lequel choisir ...

+0

Pour tous ceux qui parcourent cette question à l'avenir, les meilleures options (que j'ai fini par utiliser plus tard) sont ** n'utilisez pas Detours **. Il est buggé, en particulier dans WoW64, et il manque des fonctionnalités de base comme la prévention de la récursivité. [EasyHook] (http://easyhook.codeplex.com/) est de loin supérieur dans les caractéristiques, la stabilité/performance, et la manipulation générale. – ssube

Répondre

1

SetDllDirectory probablement ne fonctionnera pas travail. Cg.dll probablement juste des liens vers OpenGL.dll. Lorsque le système d'exploitation charge Cg.dll, il voit qu'il y a déjà un module chargé avec ce nom (le vôtre), donc il lie Cg avec cela au lieu d'aller chercher une autre copie. C'est-à-dire que l'ordre de recherche que SetDllDirectory modifie ne joue même jamais parce que le système d'exploitation ne fait aucune recherche.

Je soupçonne que votre meilleur pari sera en effet de détecter les appels entrants à votre bibliothèque. Lorsque vous en détectez un, au lieu d'effectuer votre traitement personnalisé, transférez l'appel directement à la bibliothèque OpenGL réelle, à laquelle vous faites référence en raison de l'appel de LoadLibrary, puis de GetProcAddress pour chacune des fonctions de la bibliothèque.

+0

Avez-vous des suggestions sur la façon la plus simple de les détecter? Je n'ai jamais rencontré ce problème auparavant, donc je ne connais pas la meilleure façon de le gérer. Ajouter un extrait au début de chaque fonction pour appeler le vrai sur une condition particulière n'est pas un problème, mais quelle est la meilleure chose à vérifier? – ssube

+1

En fait, j'ai eu une pensée. Serait-il efficace et efficient d'utiliser une simple valeur booléenne globale? Au début de ma fonction, cochez 'if (comeBack) {call realFunc; } else {comeBack = true; } ', puis définissez-le à false lorsque l'exécution quitte. Cela ressemble à du travail. Mon code ne doit pas, pour autant que je sache, être thread-safe (le programme a un seul thread de rendu, et un seul thread qui se réfère à OpenGL du tout, à partir de mon débogage). – ssube

+1

Une variable globale est exactement ce que j'aurais utilisé pour une première tentative. Si vous avez besoin de plusieurs threads, vous pouvez essayer le stockage local au lieu de simples globals. Si l'une des fonctions que vous interceptez finit par appeler une fonction OpenGL * différente * que vous souhaitez également intercepter, vous pouvez utiliser des variables globales spécifiques à la fonction au lieu d'une seule. –

1

Vous pouvez utiliser la magie des contextes d'activation pour tenter de résoudre votre problème. Beaucoup de choses dépendent des conditions météorologiques Les composants tiers de votre système ont déjà des manifestes - et combien de manipulations avec ces manifestes peuvent constituer une violation de licence. Pour résoudre les problèmes de versionnement de DLL, Windows XP a obtenu une technologie appelée contextes d'activation. Parfois, connu sous le nom des assemblages côte à côte, ou même quelque chose d'horrible comme Application Isolation

Pour résumer beaucoup de lecture dans un petit espace: manifeste sont des morceaux de données XML qui peuvent décrire un ensemble, ou décrire une dépendance à l'égard des ensembles. Un assembly est un manifeste, plus sa DLL.

La raison pour laquelle cela existe est qu'un assembly peut prendre une DLL simple. "comctl32.dll" et son numéro de version (v6), et créer une chose avec un plus grand nom plus unique, de sorte que plusieurs versions de la DLL simple peuvent être installés au même endroit en toute sécurité. Les assemblages sont destinés à être installés au C:\Windows\WinSxS.

Lorsqu'un fichier manifeste décrit les DLL dans un assembly, il est appelé un manifeste d'assembly. Et a généralement un nom différent de la DLL. Lorsqu'un fichier manifeste décrit les assemblys qu'utilise dll ou exe, il est appelé un manifeste d'application et est généralement incorporé en tant que ressource RT_MANIFEST - dans les EXE avec un ID de 1, dans les DLL avec un ID de 2 - ou sur le disque en tant que fichier avec un nom "appname.exe.manifest"/"dllname.dll.2.manifest". Les manifestes d'application définissent une chose appelée un contexte d'activation - qui est essentiellement un espace de noms dans lequel les fenêtres rechercheront des choses. Chaque manifeste crée un contexte d'activation. Chaque contexte d'activation a un mappage des noms de DLL simples aux assemblys.

Donc, si vous créez un assemblage avec votre fichier opengl32.dll et créer un contexte d'activation pour le référencement app.exe fichier (le opengl32.dll local), puis, éventuellement, tous les dll restants peuvent (et va) continuer à utiliser le système opengl32.dll fichier malgré le fait que les noms sont très similaires coff. Le problème est que le res-id du manifeste d'application - 1 - signifie qu'il est utilisé pour créer le contexte d'activation par défaut du processus - donc TOUS les DLLs qui n'ont pas leurs propres manifestes explicites (Cg?) Vont chercher l'espace par défaut de processus et de trouver que opengl32.dll

vous devez créer des manifestes pour tous les dll qui n'a pas déjà un noyer, en vous assurant de référence tout simplement pas votre ensemble opengl32.dll, ce qui devrait permettre ensuite revenir à l'ordre de recherche par défaut et le trouver dans l'emplacement system32 normal.

Cela signifie que votre opengl32.dll ne peut pas être dans le dossier de l'exe car ce dossier est recherché pour dll avant system32 (le fait que vous comptez sur l'accrochage).

Nous sommes sauvegardés par l'ordre de recherche plutôt simple que le système prend lors de la recherche d'assemblages. D'abord, il recherche dans WinSxS. Votre Opengl32.dll ne sera pas là, l'installation il y a un problème difficile. Ensuite, il recherche dans le dossier de l'exe, un sous-dossier avec le nom de l'assemblage, puis il recherche directement dans le dossier de l'exe le manifeste des assemblages.

Cela signifie que vous pouvez créer un assemblage - appelé quelque chose comme: "OpenGLHook" Et la structure de votre dossier ressemblerait à ceci:

\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, Bonne chance?