2009-01-20 12 views
5

Wie kann ich sicherstellen, dass eine DLL nicht entladen wird, während irgendwelche Objekte vorhanden sind?C++: Dll Entladen Problem

Das Problem ist, wenn ich explizite Speicherverwaltung verwendete, konnte ich die Dll-Objekte vor dem Löschen der DLL löschen, aber mit intelligenten Zeigern habe ich keine Kontrolle über die Reihenfolge zerstört, was bedeutet, dass die DLL zuerst freigegeben werden kann, verursacht einen Absturz wenn sie versuchen, eine der anderen Objekte zu befreien:

FlPtr ist eine einfache, das ist refrence Zählklasse ruft AddRef und Release als

benötigt
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 

ich habe versucht, die dLL Griff machen itsself Entladen, also erst nach allen Objekten entladen wurden gelöscht. Dies funktioniert, indem Sie ein neues Objekt, IExampleDll, erstellen, das von der DLL implementiert wird. Dies ist wie das ExampleDll-Objekt von vor, aber lebt in der DLL und nicht in der exe und wird auch reference gezählt. Jedes Objekt in der DLL erhöht diese Referenz auf die Konstruktion und dekrementiert sie bei der Zerstörung. Dies bedeutet, dass die Referenzenzählung nur Null erreicht, wenn die exe ihre refrences freigegeben hat und alle dlls-Objekte zerstört wurden. Es löscht dann selbst FreeLibrary (GetModuleHandle()) in seinem Destruktor.

Dies ist jedoch bei der Free abstürzt, im asuming weil der Faden noch im dlls Code ist, entladen wird ...

ich ratlos bin jetzt, wie man sicherstellen, dass die dll nur entladen werden, wenn Es gibt keine verbleibenden Objekte, abgesehen davon, dass die dll explizit freigegeben wird, nachdem alles andere gelöscht werden sollte.

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

Dieser Ansatz wird schwierig, wenn Dlls müssen saftly geladen/entladen Mitte Programm werden, das heißt, wenn der Benutzer von d3d geändert in Optionen OPENGL.

Antwort

6

Angenommen, Sie möchten den Thread beim Entladen der Bibliothek nicht beenden (andernfalls siehe MSalters), müssen Sie die Bibliothek von dem Aufrufer freigeben, der sie geladen hat.

COM löst das durch einen in-DLL-Instanz-Zähler (ähnlich wie Ihres, wenn ich Sie richtig verstehe) und regelmäßig überprüfen, indem Sie eine globale exportierte CanUnloadNow-Funktion aufrufen.

Eine andere Möglichkeit ist, dass Ihre Objekt/Interface Smartpointer auch auf die DLL verweisen, von der sie kamen. Dies würde die Client-Datengröße erhöhen, aber Sie müssen die DLL nicht berühren. Sie könnten sogar den Referenzzähler LoadLibrary/FreeLibrary wiederverwenden, was jedoch die Leistung beeinträchtigen könnte.

Außerdem hilft keines dieser Schemata viel, wenn Sie zirkuläre DLL-Abhängigkeiten erhalten (Komponente DllA.X verweist auf DllB.Y, die auf DllA.Z verweist). Ich habe noch keine gute Lösung gefunden, die globales Wissen nicht erfordert.

+0

Ok Ich hatte einen Gedanken, was ist, wenn die Dlls Release() -Methode einen bool zurückgegeben hat, false, wenn es keine verbleibenden refrences gab, konnte die spezielle DllPtr freie Bibliothek aufrufen, wenn Release falsch zurückgegeben –

+0

Aber das hat immer noch das Problem der letzten refrence wird in der dll gelöscht, wenn die DllPtrs zuerst gelöscht werden ... Wie würde ich den COM-Ansatz implementieren, so dass ich weiterhin die dll abfragen kann, nachdem der Stack und statische (dh globals) zerstört wurden, denn nur dann wird es dort garunteed keine ref? –

+0

Sie benötigen eine Art "sauberer" Objekt, das nicht in der DLL enthalten ist, die Sie entladen, um eine solche DLL erfolgreich zu entladen. Dies ist ungefähr das, was ole32.dll tut, wenn CoUnitialize aufgerufen wird. – Ismael

0

MSDN ist zu diesem Thema ausdrücklich:.. „Ein Thread, der die DLL entladen müssen, in dem sie ausgeführt wird und selbst dann sollte FreeLibraryAndExitThread statt separat FreeLibrary und ExitThread Aufruf Andernfalls rufen beenden, eine Race-Bedingung auftreten können Einzelheiten siehe den Abschnitt Hinweise FreeLibraryAndExitThread

+0

Ich möchte nicht den Thread zu beenden, nur die DLL frei und zurück zur exe/vorherige dll –

+0

Das macht keinen Sinn - Sie haben nur alle Return-Anweisungen entladen, auch! – MSalters

1

für den Fall, dass die DLL zur Laufzeit umgeschaltet wird, würde ich das Smart-Pointer-System für Objekte, die von den DLL und verwendet ein System wie diese erstellt vermeiden.

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

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

Die Anwendung enthält eine Liste aller Zuordnungen Ted Abstraktionsschicht Objekte. Die Abstraktionsschichtobjekte sind die einzigen Objekte, die Zeiger auf Objekte besitzen dürfen, die von der Implementierungsebene erstellt wurden. Wenn Sie DLLs austauschen, iterieren Sie zuerst alle Objekte der Abstraktionsschicht und weisen Sie sie an, die implementierungsspezifischen Daten freizugeben. Entladen Sie dann die DLL und laden Sie die neue DLL. Wiederholen Sie dann die Objekte der Abstraktionsschicht erneut und weisen Sie sie an, neue implementierungsspezifische Daten zu erstellen.

0

Ok Ich denke, die beste Wahl ist, den COM-Ansatz der Abfrage der DLL verwenden, um zu sehen, ob es entladen werden kann. Wie kann ich das aber trotzdem tun, damit ich die Dll fortsetzen kann, nachdem alles andere beendet wurde (dh der Haupt-Thread wurde beendet)? Muss ich einen komplett separaten Prozess erstellen, in diesem Fall, wie mache ich es so, dass dieser separate Prozess über alle geladenen DLLs informiert, und auf eine Weise, die sehr wenig Einfluss auf die Proformanz hat?

Mayby Ich könnte nur das Abfragesystem erstellen, wenn alle DllPtrs außerhalb des Geltungsbereichs sind und es beenden, sobald es die DLL frei hat? Auf diese Weise existiert es nur so lange, bis alle verbleibenden Smart Pointer zerstört sind.