Question

J'écris un code de plugin dans une dll qui est appelé par un hôte sur lequel je ne contrôle pas.

L'hôte suppose que les greffons sont exportés comme __stdcall fonctions. L'hôte est dit le nom de la fonction et les détails des arguments qu'il attend et dynamiquement Crufts une communication avec elle via LoadLibrary, GetProcAddress et en appuyant manuellement les arguments sur la pile.

Le plugin dll Habituellement exposer une interface constante. Mon plug-in présente une interface qui est configurée au moment du chargement dll. Pour ce faire mon plugin expose un ensemble de points d'entrée standard qui sont définis au moment de la dll est compilé et il les attribue au besoin à la fonctionnalité interne qui est exposée.

Chacune des fonctions internes peuvent prendre des arguments différents, mais cela est communiqué à l'hôte ainsi que le nom de point d'entrée physique. Tous mes entrypoints dll physiques sont définis pour prendre un seul pointeur vide * et je maréchal des paramètres suivants de la pile moi-même en travaillant à partir des décalages du premier argument et la liste des arguments connus qui a été communiquée à l'hôte.

L'hôte peut appeler avec succès les fonctions dans mon plug-in avec les bons arguments et tout fonctionne bien ... Cependant, je suis conscient du fait que a) mes fonctions ne sont pas nettoyer la pile comme ils sont censés qu'ils « re définie comme __stdcall fonctions qui prennent un pointeur de 4 octets et ils le font toujours un « ret 4 » à la fin même si l'appelant a poussé plusieurs arguments sur la pile. et b) Je ne peux pas traiter avec des fonctions qui ne prennent pas les arguments que le ret 4 sautera 4 octets trop hors de la pile à mon retour.

Après avoir retracé de mon plug-in dans le code d'appel de l'hôte, je peux voir que réellement a) n'est pas un gros problème; l'hôte perd un peu d'espace de pile jusqu'à ce qu'il revienne de l'appel d'expédition à quel point il nettoie son cadre de pile qui nettoie mes déchets; mais ...

Je peux résoudre b) en passant à __cdecl et non le nettoyage du tout. Je suppose que je peux résoudre) en passant à fonctions nues et écrire mon propre argument générique Code nettoyage.

Depuis que je sais la quantité d'espace d'argument utilisé par la fonction qui vient d'être appelé, je l'avais espéré que ce serait aussi simple que:

extern "C" __declspec(naked) __declspec(dllexport) void  * __stdcall EntryPoint(void *pArg1)
{                                                                                                        
   size_t argumentSpaceUsed;
   {
      void *pX = RealEntryPoint(
         reinterpret_cast<ULONG_PTR>(&pArg1), 
         argumentSpaceUsed);

      __asm
      {
         mov eax, dword ptr pX
      }
   }
   __asm
   {
      ret argumentSpaceUsed
   }
}

Mais cela ne fonctionne pas comme ret a besoin d'un temps de compilation constante ... Toutes les suggestions?

MISE À JOUR:

Merci aux suggestions de Rob Kennedy Je suis arrivé à ce qui semble fonctionner ...

extern "C" __declspec(naked) __declspec(dllexport) void  * __stdcall EntryPoint(void *pArg1)
{      
   __asm {                                                                                                        
      push ebp          // Set up our stack frame            
      mov ebp, esp  
      mov eax, 0x0      // Space for called func to return arg space used, init to 0            
      push eax          // Set up stack for call to real Entry point
      push esp
      lea eax, pArg1                
      push eax                      
      call RealEntryPoint   // result is left in eax, we leave it there for our caller....         
      pop ecx 
      mov esp,ebp       // remove our stack frame
      pop ebp  
      pop edx           // return address off
      add esp, ecx      // remove 'x' bytes of caller args
      push edx          // return address back on                   
      ret                        
   }
}

Est-ce correct?

Était-ce utile?

La solution

Depuis ret nécessite un argument constant, vous devez prendre des dispositions pour votre fonction d'avoir un nombre constant de paramètres, mais cette situation est uniquement nécessaire au point où vous êtes prêt à revenir de la fonction. Ainsi, juste avant la fin de la fonction, procédez comme suit:

  1. Pop l'adresse de retour du haut de la pile et le stocker dans un temporaire; ECX est un bon endroit.
  2. Retirez le nombre variable d'arguments de la pile, soit en faisant éclater chacun désactiver individuellement, ou en ajustant directement ESP.
  3. Appuyez sur l'adresse de retour en arrière sur la pile.
  4. Utilisez ret avec un argument constant.

Par ailleurs, la question que vous appelez (a) est vraiment un problème, dans le cas général. Vous avez eu de la chance que l'appelant semble toujours se référer à ses propres variables locales à l'aide d'un pointeur de trame au lieu du pointeur de pile. Les fonctions ne sont pas tenus de le faire, cependant, et il n'y a aucune garantie qu'une future version du programme hôte continuera de travailler de cette façon. Le compilateur est également responsable d'enregistrer des valeurs de registre sur la pile que pour la durée de l'appel, et alors attendez-vous à être en mesure de les faire éclater à nouveau par la suite. Votre code briserait que.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top