C ++:Dll卸载问题

时间:2009-01-20 10:57:07

标签: c++ dll

如何在存在任何对象的情况下确保不卸载dll?

问题是,当我使用explict内存管理时,我可以在释放dll之前删除dll对象,但是使用智能指针我无法控制那里被破坏的顺序,这意味着dll可能首先被释放导致崩溃试图释放其他一个对象:

FlPtr是一个简单的引用计数类,可以根据需要调用AddRef和Release

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

我尝试让dll句柄自己卸载,即只删除所有对象后卸载。这项工作是通过创建一个dll实现的新对象IExampleDll。这就像之前的ExampleDll对象一样,但它位于dll而不是exe中,并且也是refrence计数的。 dll中的每个对象都会增加对构造的依赖,并在破坏时将其减去。这意味着当exe释放它的引用并且所有dll对象都被破坏时,引用计数才会达到零。然后删除它自己在析构函数中调用FreeLibrary(GetModuleHandle())。

然而这在FreeLibrary中崩溃了,因为线程仍然在正在卸载的dlls代码中...

我现在不知道如何确保只在没有剩余对象的情况下卸载dll,除了在其他所有内容都应该被删除之后显式释放dll;

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

当需要在程序中间加载/卸载dll时,这种方法变得很困难,即如果用户在选项中从d3d更改为openGL。

4 个答案:

答案 0 :(得分:6)

假设你不想在卸载库时终止线程(否则,请参阅MSalters),你需要从加载它的调用者那里释放库。

COM通过一个in-DLL实例计数器解决这个问题(非常类似于你,如果我理解正确的话),并通过调用全局导出的CanUnloadNow函数定期检查它。

另一个选择是让你的对象/接口智能指针也引用它们来自的DLL。这会增加客户端数据大小,但您不需要触摸DLL。您甚至可以回收LoadLibrary / FreeLibrary引用计数器,但这可能会影响性能。

此外,如果您获得循环DLL依赖项(Component DllA.X引用DllB.Y,它引用DllA.Z),这些方案都不会有太大帮助。对于那些不需要全球知识的人来说,我还没有很好的解决方案。

答案 1 :(得分:1)

对于在运行时切换DLL的情况,我会避免使用DLL创建的对象的智能指针系统,并使用如下系统:

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

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

应用程序包含所有已分配的抽象层对象的列表。抽象层对象是唯一允许拥有指向实现层创建的对象的指针的对象。在交换DLL时,首先迭代所有抽象层对象并告诉它们释放特定于实现的数据。然后卸载DLL并加载新的DLL。然后再次迭代抽象层对象并告诉他们创建新的实现特定数据。

答案 2 :(得分:0)

MSDN在此主题上是明确的:“必须卸载正在执行的DLL然后自行终止的线程应该调用FreeLibraryAndExitThread,而不是分别调用FreeLibraryExitThread。否则,可能会出现竞争条件。有关详细信息,请参阅FreeLibraryAndExitThread的备注部分。

答案 3 :(得分:0)

好的我认为最好的选择是使用COM方法轮询dll以查看它是否可以卸载。我怎么能这样做,以便我可以在其他所有关闭后继续轮询dll(即主线程终止)?我是否需要创建一个完全独立的进程来执行此操作,在这种情况下我该如何操作,以便这个单独的进程知道所有已加载的dll,并且对proformance的影响非常小?

Mayby我可以在所有DllPtr超出范围时创建轮询系统并在释放dll后立即终止它?这样,它只会在任何重新映射的智能指针被销毁时才存在。