10
votes

DLL Fichier chargé deux fois avec la redirection de la DLL via Manifest

Je comprends python.h code> dans mon Visual C ++ projet de fichier DLL qui provoque une liaison implicite avec python25.dll code>. Cependant, je souhaite charger un python25.dll spécifique> (plusieurs peut être présent sur l'ordinateur), donc j'ai créé un fichier manifeste très simple nommé test.manifest em>:

Configuration Properties -> Manifest Tool -> Input and Output -> Additional Manifest Files
-->$(ProjectDir)\src\test.manifest


7 commentaires

Comment le chargement implicite est-il fait? Y a-t-il un commentaire #pragma (lib xxx) dans les en-têtes Python?


Oui, le lien est fait grâce à #pragma Commentaire.


Juste hors de curiosité, que se passe-t-il si vous supprimez le python25.dll à SYSWOW64?


Merci pour l'idée de test, le résultat est assez intéressant! Maintenant, ma DLL "bonne", celle pointée par le manifeste, est elle-même chargée deux fois !!!


Eh bien, c'est assez étrange. Ont-ils des adresses de charge différentes?


Si vous voulez dire la colonne "Adresse de base" dans Process Explorer, YEP: premier -> 0xf10000 Deuxième -> 0x1e000000


Votre application et les images Python25.dll sont 32 bits, non? Vous pouvez voir avec la colonne "Type d'image". Peut-être que c'est une sorte de bizarrerie sur wow64.


4 Réponses :


1
votes

Walker de dépendance est généralement le meilleur outil de résolution de ce type de problème. Je ne suis pas trop sûr à quel point il se manifeste bien que ...

où dans ce désordre enchevêtré est le fichier exécutable de processus réel?

Deux possibilités viennent à l'esprit:

  1. Vous écrivez un fichier DLL d'extension Python. Donc, le processus Python charge votre fichier DLL et il serait déjà avoir sa propre dépendance python25.dll.

  2. Le fichier EXE Chargement de votre fichier DLL est en cours de construction avec des fichiers d'en-tête et des bibliothèques fournies par le projet de fichier DLL. Donc, il hérit d'un commentaire #pragma (lib, "python25.lib") à partir de votre fichier d'en-tête et loge ainsi le fichier DLL lui-même.

    Mon problème avec le deuxième scénario est, je m'attendrais à ce que le fichier EXE et à votre fichier DLL, d'être dans le même dossier dans le cas où le fichier EXE charge implicitement votre fichier DLL. Auquel cas le fichier EXE, votre fichier DLL et le python25.dll sont tous déjà dans le même dossier. Pourquoi alors la version System32 serait-elle jamais chargée? La commande de recherche pour les fichiers DLL implicitement chargés est toujours dans le dossier du fichier EXE de l'application.

    Donc, la question intéressante réelle implicite dans votre requête est la suivante: comment est le système32 python26.dll étant chargé du tout?


2 commentaires

Merci Chris, j'ai clarifié le scénario dans une réponse que j'ai posté. Maintenant, j'ai compris que la question est causée par la dll Boost.python ...


D'une manière ou d'une autre, le contexte d'activation de votre DLL n'est pas utilisé par le Boost - faites-vous une charge statique ou dynamique, de Boost?, Et de la DLL Python? La DLL de Boost aura-t-elle son propre manifeste qui pourrait réinitialiser le courant alternatif et / ou en quelque sorte référencer explicitement la DLL Python dans System32?



3
votes

J'ai fait des progrès pour la compréhension de la question.

Permettez-moi de clarifier le scénario:

  • Je construis un fichier DLL qui incorpore et étend Python, à l'aide de l'API Python C et Boost.python.
  • Ainsi, je fournis un python25.dll dans le même dossier que mon fichier DLL, ainsi qu'un boost_python-vc90-mt-1_39.dll .
  • Puis j'ai un fichier exe qui est une démonstration pour montrer comment créer un lien vers mon fichier DLL: ce fichier EXE ne doit pas nécessairement être dans le même dossier que mon fichier DLL, tant que le fichier DLL peut être trouvé. Sur le chemin (je suppose que l'utilisateur final peut ne pas le mettre dans le même dossier).

    Ensuite, lors de l'exécution du fichier EXE, le répertoire actuel n'est pas celui contenant python25.dll , et c'est pourquoi la commande de recherche est utilisée et un autre python25.dll peut être trouvé avant le mien.

    Maintenant, j'ai compris que la technique Manifeste était la bonne approche: j'ai réussi à rediriger le chargement à "mon" python25.dll . .

    Le problème est que c'est le boost fichier dll < Code> boost_python-vc90-mt-1_39.dll c'est responsable du chargement "double"!

    Si je ne charge pas celui-ci, alors python25.dll est correctement redirigé. Maintenant, je dois trouver en quelque sorte comment dire au fichier DLL de Boost de ne pas charger un autre python25.dll ...


2 commentaires

J'ai réussi à recompiler Boost.python Libs avec l'option "Embed-Manifest = Off". Miracle! "python25.dll" pas plus chargé deux fois. Mais maintenant, chaque fois que j'importe une extension PYD de mon code C ++, elle est chargée deux fois à nouveau ... problème sans fin ...


Essayez de faire le même hanjakcing Manifest pour BOOST_PYTHON-VC90-MT-1_39.DLL - tel que en mettant un fichier manifeste à côté, ou changeez-le à l'aide de MT . C'est mon impression. Je n'ai pas essayé cela, je me débats aussi avec Winsxs .



9
votes

Après une bataille exhaustive avec winsxs et redirection DLL , voici mon conseil pour vous:

de fond

Diverses choses peuvent faire charger un fichier DLL sous Windows:

  • Lien explicite ( LoadLibrary ) - Le chargeur utilise le contexte d'activation actuel du fichier EXE exécutant. C'est intuitif.
  • liaison implicite ("linge de charge", les "auto") - Le chargeur utilise le contexte d'activation par défaut du fichier en fonction de dll . Si a.exe dépend de b.dll dépend de c.dll (toutes les liaisons implicites), le chargeur utilisera b. DLL Contexte d'activation de la DLL lors du chargement c.dll . IIRC, cela signifie que si B 'code> dllmain charges c.dll , il peut utiliser b.dll Context d'activation - la plupart du temps Cela signifie le contexte d'activation par défaut à l'échelle du système. Donc, vous obtenez votre dll python de % systemroot% .
  • com ( coCreateInstance ) - C'est le méchant. Extrêmement subtile. Il s'avère que le chargeur peut rechercher le chemin complet d'un fichier DLL à partir du registre à l'aide de COM (sous HKCR \ CLSID ). LoadLibrary ne fera aucune recherche si l'utilisateur lui confère un chemin complet, le contexte d'activation ne peut donc affecter la résolution du fichier DLL. Ceux-ci peuvent être redirigés avec l'élément COMCLASS et amis, voir [Référence] [MSDN_AXEMBLY_RF].
  • Même si vous avez le manifeste correct, quelqu'un peut parfois changer le contexte d'activation au moment de l'exécution à l'aide du Contexte d'activation API . Si tel est le cas, il n'ya généralement pas grand chose que vous pouvez faire à ce sujet (voir la solution ultime ci-dessous); Ceci est juste ici pour la complétude. Si vous voulez savoir qui est en train de gâcher avec le contexte d'activation, WINDBG BP Kernel32! ActivateAccTX .

    maintenant sur la recherche du coupable
    1. Le moyen le plus simple de savoir ce qui provoque un fichier DLL de charger consiste à utiliser moniteur de processus . Vous pouvez surveiller " chemin contenant python25.dll " ou "em> détail contenant python25.dll " (pour com Recherches). Double-cliquant sur une entrée vous montrera une trace de pile (vous devez d'abord définir les chemins de recherche de symboles, puis définir le serveur PDB de Microsoft). Cela devrait suffire à la plupart de vos besoins.
    2. Parfois, la trace de pile obtenue d'en haut pourrait être engendrée d'un nouveau fil. Pour cette fin, vous avez besoin Windbg . Cela peut être un autre sujet, mais il suffit de dire que vous pouvez SXE LD Python25 et regardez quels autres threads font (! Findstack MyExemodulename ou ~ * k ) qui provoque la charge d'un fichier DLL.

      Solution du monde réel

      Au lieu de violer cette chose Winsxs, essayez d'accrocher Loadlibraryw à l'aide de Mhook ou easyHook . Vous pouvez simplement remplacer totalement cet appel avec votre logique personnalisée. Vous pouvez terminer cela avant le déjeuner et trouver le sens de la vie à nouveau.

      [msdn_assembly_ref]: Manifestes d'assemblage


0 commentaires

0
votes

Récemment, j'ai frappé un Problème similaire :

  1. Mon application Embourding Python charge la python32.dll à partir d'un emplacement connu, c'est un Assemblage côte à côte (Winsxs) avec Python.Manifest
  2. tentative de importer tkinter à l'intérieur de l'interprète python incorporé a provoqué une seconde charge de la même charge python32.dll , mais sous une autre adresse non par défaut .
  3. La fonction d'initialisation du module Tkinter (spécifiquement, _tkinter.pyd ) a échoué car à l'état de thread de python invalide python ( _pythreadstate_current == null ). Évidemment, py_initialize () n'a jamais été appelé pour le deuxième interprète Python chargé de la duplicate python32.dll.

    Pourquoi le python32.dll a-t-il été chargé deux fois? Comme je l'ai expliqué dans Mon message sur Python-Capi , cela a été causé par le fait que l'application chargait Python32.dll de Winsxs, mais _tkinter.pyd n'a pas reconnu l'assemblage, donc python32.dll a été chargé à l'aide du chemin de recherche de la DLL régulier.

    L'assemblage Python.Manifest + Python32.dll a été reconnu par la machine de chargement DLL comme module différent (sous Différents contextes d'activation), que le python32.dll demandé par _TKinter.pyd.

    Retrait de la référence à Python.Manifest de l'application Incorporer Python et permettant au chemin de recherche DLL de rechercher les DLL résolvés le problème.


0 commentaires