2009-01-20 8 views
5

Comment puis-je m'assurer qu'une DLL n'est pas déchargée alors que des objets existent? Le problème est que, lorsque j'utilisais la gestion de mémoire explicite, je pouvais supprimer les objets dll avant de libérer la DLL, mais avec les pointeurs intelligents, je n'avais aucun contrôle sur l'ordre, détruisant ainsi la DLL. en essayant de libérer l'un des autres objets:C++: Problème de déchargement Dll

FlPtr est un simple des thats de classe de comptage refrence appelle AddRef et Release, au besoin

ExampleDll *dll = LoadDll(L"bin\\example.dll"); 
IObject *obj = dll->CreateObject(); 
... 
obj->Release(); 
delete dll;//fine because all objects already deleted 
return 0; 

auto_ptr<ExampleDll> dll = LoadDll(L"bin\\example.dll"); 
FlPtr<IObject> obj = dll->CreateObject(); 
... 
return 0;//crash if dll is destructed before obj since Object::Release needs to call into the dll 

J'ai essayé de faire la poignée dll itsself déchargement, à savoir que le déchargement après tous les objets ont été supprimés. Ce travail en créant un nouvel objet IExampleDll que la DLL implémente. C'est comme l'objet ExampleDll d'avant mais vit dans le dll plutôt que l'exe et est également compté. Chaque objet de la DLL incrémente cette réfrence sur la construction et la désincrémente lors de la destruction. Cela signifie que le nombre de réfrences n'atteint que zéro lorsque l'exe a libéré ses réfrences ET que tous les objets dll ont été détruits. Il supprime ensuite itsself appelant FreeLibrary (GetModuleHandle()) dans son destructeur.

Cette plante cependant au FreeLibrary, Asuming im parce que le fil est toujours dans le code dll qui est déchargé ...

Je suis à une perte maintenant comment assurer que le dll n'est déchargé lorsque il n'y a pas d'objets restants, à part revenir à libérer la DLL explicitement après que tout le reste aurait dû être supprimé;

int main() 
{ 
    ExampleDll *dll = LoadDll("bin\\example.dll"); 
    restOfProgram(); 
    delete dll; 
} 

Cette approche devient difficile lorsque dll doivent être chargés/déchargés mi programme saftly, à savoir si l'utilisateur a modifié de D3D à opengl dans les options.

Répondre

6

En supposant que vous ne voulez pas mettre fin au thread lors du déchargement de la bibliothèque (sinon, voir MSalters), vous devez libérer la bibliothèque de l'appelant qui l'a chargé. COM résout cela par un compteur d'instances DLL (comme le vôtre, si je vous comprends bien), et le vérifie régulièrement en appelant une fonction CanUnloadNow exportée globale.

Une autre option est d'avoir vos pointeurs intelligents objet/interfaces également référence à la DLL ils venaient. Cela augmenterait la taille des données client, mais vous n'auriez pas besoin de toucher la DLL. Vous pouvez même recycler le compteur de référence LoadLibrary/FreeLibrary, mais cela peut affecter les performances.

En outre, aucun de ces schémas n'aide beaucoup si vous obtenez des dépendances circulaires de DLL (DllA.X de référence de composant DllB.Y, qui fait référence à DllA.Z). Je n'ai pas encore trouvé une bonne solution à ce problème qui ne nécessite pas de connaissances globales.

+0

Ok, j'ai eu une pensée, et si la méthode Release() dlls retourné un booléen, faux s'il n'y avait pas de réfrences restantes, le DllPtr spécial pourrait appeler bibliothèque libre si Release retourné false –

+0

Mais cela a toujours le problème de la dernière la réfrence étant supprimée dans la DLL si les DllPtr sont supprimés en premier ... Comment j'irais mettre en œuvre l'approche COM pour continuer à interroger la DLL après que la pile et les statiques (les globales) soient détruites car c'est alors seulement pas de ref? –

+0

Vous aurez besoin d'une sorte d'objet "nettoyeur" qui n'est pas dans la DLL que vous déchargez pour décharger avec succès cette DLL, c'est à peu près ce que fait ole32.dll lorsque CoUnitialize est appelée. – Ismael

0

MSDN est explicite à ce sujet:.. « Un fil qui doit décharger le DLL dans lequel il est en cours d'exécution et se terminer devrait appeler FreeLibraryAndExitThread au lieu d'appeler FreeLibrary et ExitThread séparément Dans le cas contraire, une condition de course peut se produire Pour plus de détails, voir la section Remarques de FreeLibraryAndExitThread

+0

Je ne veux pas terminer le thread, juste libérer la DLL et retourner à la DLL exe/précédente –

+0

Cela n'a pas de sens - vous venez de décharger toutes les déclarations de retour, aussi! – MSalters

1

pour le cas où la DLL est mis à exécution, il faut éviter le système de pointeur intelligent pour les objets créés par la DLL et utiliser un système comme celui-ci.

    |-----------------------| |--------------------------| 
        | Abstraction Interface | | Implementation Interface | 
        |-----------------------| |--------------------------| 
          ^      ^
           |       | 
|-------------|1  *|-------------------|*  *|----------------| 
| Application |-------| Abstraction Layer |--------| Implementation | 
|-------------|  |-------------------|  |----------------| 

\------------- Main Program ------------------/ \-------- DLL --------/ 

L'application contient une liste de tous les alloca objets de couche d'abstraction ted. Les objets de la couche d'abstraction sont les seuls objets autorisés à posséder des pointeurs vers des objets créés par la couche d'implémentation. Lors de l'échange de DLL, commencez par itérer tous les objets de la couche d'abstraction et dites-leur de libérer les données spécifiques à l'implémentation. Puis déchargez la DLL et chargez la nouvelle DLL. Ensuite, réitérez les objets de la couche d'abstraction et dites-leur de créer de nouvelles données spécifiques à l'implémentation.

0

Ok Je pense que le meilleur choix est d'utiliser l'approche COM de l'interrogation de la DLL pour voir si elle peut être déchargée. Comment puis-je faire cela pour que je puisse continuer à interroger la DLL après que tout le reste a fermé (c'est-à-dire que le thread principal s'est terminé)? Ai-je besoin de créer un processus complètement séparé pour ce faire, auquel cas comment le faire pour que ce processus séparé connaisse toutes les DLLs chargées, et d'une manière qui a TRÈS peu d'impact sur la proformance? Mayby Je pourrais juste créer le système d'interrogation quand tous les DllPtr sont hors de portée et le terminer dès qu'il a libéré la DLL? De cette façon, il n'existe que le temps nécessaire pour que les pointeurs intelligents restants soient détruits.