如何强制销毁不同dll中的静态对象顺序?

5

我有两个静态对象在两个不同的dlls中:

一个名为资源(Resources)的单例对象,和一个名为用户(User)的对象。在其析构函数中,对象User需要访问对象Resources。

我该如何确保对象Resources在对象User之前不被销毁?


你有没有改变实现的方法?或者第三方提供了2个dll文件吗? - yves Baumes
我可以将两个静态对象的定义移动到同一个dll中,但不能在同一个cpp中。这样有帮助吗? - Igor
@IgorOks 这是一个老问题,你可能早已忘记了,但我认为yves Baumes得到了应该标记的答案,以供将来可能访问此主题的其他人参考。 - Marcin K.
4个回答

4
如果你能将这两个全局变量放在同一个DLL中,情况就不同了。正如Jem在自己的回答中所说,DLL分离顺序不能由系统保证。因此,当有两个分离的DLL时,可能会出现大问题。我不是Windows系统专家,但是通过谷歌搜索,我发现msdn博客作者告诉他们遇到了相同的问题,没有好的解决方案来解决它。
如果你能将它们放在同一个DLL中,那么解决方案就更容易了,在这种情况下,你不需要解决“无法保证DLL分离顺序”的问题(据我所知是无法解决的)。但是,你仍然需要解决一个新问题:C++语言不能保证全局变量销毁顺序。但是这个问题可以解决:
你需要使用某种引用计数方式,boost::shared_ptr也许可以解决问题。
将其声明为全局变量,并以这种方式定义:
boost::shared_ptr my_resource_ptr ( new Resource() ); // new operator is important here!

然后您需要修改用户实现来存储其自己的shared_ptr:

class User
{
    ...
    boost::share_ptr a_resource_ptr;
    ...
};

只要您的User实例中有一个没有被销毁,它们就会“保留”Resource实例,从而防止它被过早删除,即使全局shared_ptr已经被销毁。最后一个被销毁的User实例将(间接)删除Resource实例。 无论您使用的是哪种引用计数方式,ComPtr还是自己的引用计数方式,都应该可以解决这个问题。

3

全局对象在其对应的DLL被卸载时被销毁。因此,由于您的“User”DLL可能依赖于您的“Resource”DLL,所以您会遇到麻烦:'resource'将始终在'user'之前被销毁。

如果存在一个好的答案,我也很感兴趣。直到现在,我使用了一个清理函数,在应用程序退出之前必须调用它,并且我只在析构函数中保留无害的代码。


1
那么,实际上你的意思是一个 DLL 可能在没有销毁所有全局变量的情况下被卸载? :/ - yves Baumes
经过多次尝试和测试,我最终得出了相同的解决方案。DLL卸载顺序很难理解,如果跨平台则更加困难。因此,在DLL析构函数中不应编写危险代码。但是为了仍然允许用户清理资源,使用特殊的DLL清理函数。 - Alex Che

1

我不认为你可以更改不同模块中全局变量的销毁顺序。 有没有可能添加一些引用计数?


1

如果您真的想要获取2个分离的Dlls,我可能有一些提示:您可以考虑使用Windows API中的FreeLibrary()。根据msdn所述,FreeLibrary()会为要卸载的Dll减少一个引用计数器,当计数器达到0时,该Dll将被卸载。

缺点:使用FreeLibrary()意味着您正在使用LoadLibrary()msdn link)加载它,并且调用此库中的函数意味着您正在使用GetProcAddress()函数,这可能导致非常丑陋的代码。而且,这也可能意味着您需要更改代码-获取指向Dll函数的全局变量以存储每个函数的地址...

如果您想要实现它:

你必须在进程的 main() 函数中加载和释放库,也要在实现用户类的 DLL 中从 DllMain() 函数加载和释放库,当原因是 DLL_PROCESS_DETACH 时(参见 mdsn's DllMain link)。
因此,只有在 "User" 库完成后,才会卸载 "Resource" 库。如果你愿意可以试试,让我知道是否有效,因为我从未实现过...
附注:我已经在你的问题中发布了第二个答案,以便在两个答案之间得到有意义的区分,因为我(尝试)详细说明了它们两个。我不希望你将它们混淆并感到困惑...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接