mise en page incorrecte vtable pour la classe exportée par DLL: demande de clarification concernant les en-têtes et la construction vtable

StackOverflow https://stackoverflow.com/questions/3453402

Question

Bien que le problème est à portée de main résolu , il m'a un peu confus quant à ce que les données sont utilisées pour construire les vtables pour une classe et où la mise en page pour la vtable est stockée. Si quelqu'un peut fournir des éclaircissements ou me diriger vers des informations qui pourraient assouvir ma curiosité, je serais très heureux.

Historique

  1. Deux projets distincts: VC6.0. Un pour un exe, un pour une dll
  2. Application contient .lib, fichiers .dll et .h de dll versions du projet.
  3. Quand une nouvelle version est prêt, le .lib, .dll et .h sont Copié dans le projet exe du projet dll.
  4. J'ai hérité de ce système.

Problème:

J'ai récemment fait un changement à la DLL et copié sur le .lib et .dll, mais a oublié de copier les fichiers d'en-tête . Il y avait eu quelques changements de la hiérarchie et, par conséquent, la vtable pour une classe particulière (appeler InternalClass) avait changé.

Le fait exe pas directement des méthodes Invoke sur InternalClass . Au lieu de cela, il crée une instance d'une autre classe (appeler InterfaceClass) qui encapsule un pointeur vers un objet InternalClass et invoque diverses méthodes contre ce pointeur.

Lors de l'exécution, les appels effectués à partir de méthodes de InterfaceClass aux méthodes de InternalClass invoquaient les symboles mauvaises méthodes (à savoir InterfaceClass invoquerait InternalClass::A et InternalClass::B aurait fait courir). En regardant le asm , il se trouve que le fixup ou thunk (je suis désolé si cela est le mauvais jargon!) Utilisait le bon élément décalage dans le vtable. Cependant , le vtable contenait lui-même la liste des pointeurs que vous attendez de la vieux les fichiers d'en-tête.

Question actuelle:

Je réalise mon erreur, copié les fichiers d'en-tête sur, recompilé et tout allait bien. Cependant , je reste avec une question lancinante:

Quand la mise en page pour les vtables déterminé et quelles informations sont utilisées pour construire le vtable pour ces classes à l'exécution? Autrement dit, il me semble que le bon vtable doit avoir été assemblé lorsque la dll a été compilé pour que les compensations, etc. peuvent être utilisés pour les appels de InterfaceClass à InternalClass. Pourquoi, alors, était un vtable obsolète utilisé lors de l'exécution? La mise en page aussi déterminé séparément lorsque l'exe est compilé en utilisant les en-têtes, il a?

Je ne sais pas si cela est clair du tout. Laissez-moi savoir si ce que je demande est trop alambiquée. Merci!

Était-ce utile?

La solution

Hmm, les détails de mise en œuvre lourds d'un compilateur de 12 ans. Lorsque vous utilisez __declspec (dllexport) sur une classe, l'éditeur de liens exporte les membres de la classe. Pas le vtable. Cela devient reconstruit dans le client de la classe lorsque le compilateur parse la déclaration de classe dans le fichier d'en-tête. L'éditeur de liens remplissages dans ce vtable local avec les adresses des membres exportés.

Ne serait-il mieux fonctionner si le compilateur simplement exporté la vtable de la DLL? Peut-être, mais l'exportation de ces détails de mise en œuvre est terriblement fragile. La peur de vous peindre dans un coin que vous ne pouvez pas sortir à cause de la compatibilité ascendante est un puissant facteur de motivation, j'imagine.

Autres conseils

La façon dont votre décrire l'interaction de ces deux classes ne sont pas tout à fait clair sur un détail très important: Votre soi-disant InternalClass exporté de la dll ou non? Ou de mettre la question d'une manière différente: Est-ce que le code dans les fichiers d'en-tête copiés utilisent des pointeurs vers InternalClass ou est tous connaissance de cette classe cachée uniquement dans le dll

Si cette classe en question est en réalité complètement caché dans le dll, alors je ne sais pas comment ce problème pourrait se produire. Mais de ce que vous décrivez, ça sonne vraiment comme si InternalClass est exposé par le dll.

Si les en-têtes qui seront copiés contiennent un code similaire dans son concept à ceci:

class InterfaceClass
{
    InternalClass* Internal;

    void DoSomething()
    {
        Internal->DoSomething();
    }
};

Alors le InternalClass doit être exposé, sinon le projet de l'exe ne manquera pas de compiler l'en-tête. (Il est donc pas vraiment interne après tout.)

Et si tel est le cas, il explique facilement votre problème. Le exe est construit contre une autre déclaration de InternalClass que la dll a été construit avec. Cela signifie que chaque compile leur propre version de la table virtuelle, et ces versions sont différentes.

Ensuite, lors de l'exécution, le code exe est remis un objet créé à partir du dll, et donc avec son pointage de pointeur vtable à la version de la dll du vtable. Le code exe tente alors de travailler à travers ce pointeur vtable comme si elle a à la version du vtable de l'exe. Et, évidemment, que les problèmes de causes.

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