当C++/CLI DLL卸载时,我该如何清理.NET相关内容?

5

我刚接触C++/CLI,请多包涵...

我正在开发一个混合的C++/CLI DLL,它应该作为Win32进程和.NET程序集之间的桥梁。在DLL中,我需要在整个DLL生命周期内存在一些.NET内容。初始化不是太大的问题,但我无法弄清楚何时可以安全地清理.NET内容。通常的C++工具(DLL_PROCESS_DETACH、全局变量和静态本地析构函数)似乎都在CLR消失后被调用。

那么,有什么方法可以通知DLL即将从CLR分离,这样我就可以释放我所持有的.NET引用了吗?

4个回答

2
如果您需要处理AppDomain卸载,请使用AppDomain.DomainUnload事件进行清理。 正如STW所说,具有托管代码的库永远无法从AppDomain中分离出来,唯一的方法是卸载AppDomain。 如果您没有管理AppDomains,只需泄漏托管资源(并在进程分离期间正确刷新非托管资源)。更好的做法是将其设计为仅崩溃软件,因此不需要清理。

DomainUnload似乎是一条有效的路径,我刚开始探索它,但还没有成功。至于泄漏托管资源,这可能没关系,但解决这个问题和原始问题一样困难...为了防止它们被垃圾回收,我必须有对这些对象的引用。我很想在卸载时释放这些引用,但这必须在.NET仍然存在时完成。找到正确的时间正是我的问题... - Eran
您可以通过调用 GCHandle.Alloc 来防止对象被回收。资源将持续到匹配的 GCHandle.Free 调用或应用程序域卸载。 - Ben Voigt

2

回答自己的问题似乎有些尴尬,但是没有其他人提出这个建议,而且这正是我想要的……所以:

原来微软提供了一个名为_onexit_m的奇特变体,应该在混合模式DLL中使用。当使用_onexit_m来注册(托管)回调函数时,该函数将在DLL即将被卸载但CLR尚未关闭时调用。它类似于AppDomain.DomainUnload,由Ben Voigt建议,但由于某种原因我无法使DomainUnload工作,而IMHO使用_onexit_m更简单。


0

这可能不适用于 .NET 的 C++ 部分,但据我所知,让 CLR 释放已加载的程序集的唯一方法是处理 AppDomain;而且,除非您在手动管理 AppDomain 方面进行了工程化,否则您可能只有最初的 AppDomain——使处理相当于杀死应用程序。


我并不打算发布一个程序集 - 我只是想释放我创建的.NET对象的引用,并在DLL的生命周期中一直持有着这些引用... - Eran
.NET中的对象是否是可处理的?如果是,一旦使用完成,您可以尝试对这些对象执行dispose()。 - Kangkan
@Kangkan - 告诉何时使用结束正是我的问题... 知道本地世界即将结束(进程分离,全局析构函数等)很容易,但此时托管世界已经结束了。 - Eran

0

.NET Dll 应被视为一组独立的 ref 类。每个 ref 类实例都有自己的生命周期,资源管理相对于类实例生命周期进行。正确实现 Dispose 模式可以解决所有资源管理问题。

每个具有本地资源或可释放类成员的 C++/CLI 类都应该有析构函数和可选终结器。具体来说,.NET 引用在类析构函数中释放,该函数映射到 C# 的 Dispose 方法。


我的问题可能是使用了错误的模式,但我无法找到正确的模式。把我的情况看作是一个本地单例,它在auto_gcroot中存储对.NET对象的引用。当单例被销毁时,auto_gcroot尝试释放引用 - 但为时已晚,导致崩溃。 - Eran
1
如果您将对象保存与 DLL 加载的时间一样长,只需“泄漏”句柄即可。垃圾收集器会在您 DLL 卸载后 CLR 仍在运行的极少情况下进行处理。 - Ben Voigt
我猜您保留了auto_gcroot引用以进行从本机单例到.NET对象的某种回调通信。也许考虑另一种回调算法会更好,例如基于本机事件? - Alex F

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