当 WMI 使用完提供程序后,它会将提供程序从内存中卸载。 WMI 卸载提供程序的主要原因是节省系统资源。 因此,您必须添加代码,使 WMI 能够以高效的方式卸载您的提供程序。 WMI 卸载提供程序的时间从缓存控制中指定的间隔到该间隔的两倍之间。
WMI 可以通过以下方式之一来卸载提供程序:
- 在提供程序完成给定的任务后卸载提供程序。
- 当用户关闭系统时,快速卸载所有提供程序。 请注意,当 WMI 服务从命令行关闭时,WMI 会卸载进程内提供程序。
虽然第一种方案更为常见,但必须编写提供程序,以便实现这两种可能性。
本主题将讨论以下部分:
卸载未活动提供程序
WMI 卸载空闲提供程序时执行以下动作:
确定提供程序是否处于空闲状态。
WMI 使用 ClearAfter 属性来确定提供程序在卸载该提供程序之前可能保持空闲的时间。 有关详细信息,请参阅 访问供应商的空闲状态。
调用提供程序的 Release 方法。
如果提供程序是纯提供程序,则 Release 会完全从活动内存中删除提供程序。 但是,非纯提供者可能会在WMI调用Release后继续运行。
访问服务提供商的空闲时间
提供程序保持活动的最短时间由 ClearAfter 属性确定。 可以在 \root 命名空间中派生自 WMI 系统类 __CacheControl 的类实例中找到 ClearAfter。
以下列表描述了派生自 __CacheControl的类,这些类控制提供程序卸载:
- __EventConsumerProviderCacheControl
- __EventProviderCacheControl
- __EventSinkCacheControl
- __ObjectProviderCacheControl
- __PropertyProviderCacheControl
可以通过编辑特定类型的提供程序的缓存控件实例中的 ClearAfter 属性来更改 WMI 允许提供程序保持非活动状态的最短时间。 例如,若要限制属性提供程序可以保持空闲的时间量,可以在 \root 命名空间中编辑 __PropertyProviderCacheControl 实例的 ClearAfter 属性。
提供程序与 WMI 客户端同为一体时的卸载
提供程序在完成其被调用的功能后,可能仍需继续保留为 WMI 的客户端。 例如,推送提供程序可能需要向 WMI 发出查询。 有关详细信息,请参阅 “确定推送或拉取状态”。 在这种情况下,表示提供程序的 __Win32Provider 实例的 Pure 属性应设置为 TRUE。 如果 Pure 属性设置为 FALSE,则当 WMI 调用其主接口的 Release 方法时,提供程序会在所有未完成的接口点上调用 IUnknown::Release 来准备卸载。 有关详细信息,请参阅 __Win32Provider中的“备注”部分。
以下过程介绍如何为提供程序的主接口实现发布方法。
卸载提供程序
当 WMI 调用您提供程序主接口的 Release 方法时,释放持有的所有针对 WMI 的接口指针。
通常,提供程序会持有指向 IWbemServices 和 IWbemContext 接口的指针,这些接口是在 IWbemProviderInit::Initialize 中提供的。
如果关联的__Win32Provider实例中的 Pure 属性设置为 FALSE,提供程序可以在 WMI 调用 Release 后转换为客户端应用程序的角色。 但是,WMI 无法卸载作为客户端系统运行的提供程序,这会增加系统开销。
纯设置为 TRUE 的提供程序仅存在于服务请求中。 因此,这种类型的提供程序无法承担客户端应用程序的角色,WMI 可以卸载它。
在系统关闭时卸载提供程序
在正常情况下,使用同时作为WMI客户端的提供程序卸载中的指导原则可以让WMI正确地卸载提供程序。 但是,可能会遇到 WMI 无法引入正常卸载过程的情况,例如当用户选择关闭系统时。 通过使用事务型数据存储模型,并实施良好的清理策略,可以确保提供程序被正确卸载。
用户可以随时停止 WMI。 在这种情况下,WMI 不会卸载任何提供程序,也不会在任何进程内提供程序上调用 DllCanUnloadNow 入口点。 此外,如果在关闭时进程内提供程序正处于方法调用过程中,WMI 可能会在执行过程中终止该调用的线程。 在这种情况下,WMI 不调用通常处理清理的例程,例如对象析构函数。 在大多数情况下,WMI 将仅调用 DllMain 。
当操作系统关闭 WMI 时,系统会自动释放分配给进程内的提供程序的所有内存。 操作系统还会关闭提供程序持有的大多数资源,例如文件句柄、窗口句柄等。 提供者无需采取任何特定行动即可实现该目标。
由于 WMI 可能会在调用期间关闭,因此提供程序不应使数据源处于不一致状态。 使数据处于不一致状态对于只读提供程序来说不是问题。 但是,具有写入功能的提供程序可能想要实现某种事务模型,以便在突然终止后允许安全回滚。
虽然作系统可能会释放某些常规系统资源,但系统不会自动释放所有资源。 例如,操作系统可能无法释放一个套接字或数据库连接。 相反,提供者可能需要手动清理这些资源。 为了避免这些问题,您可以将提供程序实现为独立进程,或者添加清理代码。
最简单的解决方案是实现提供商在进程之外。 当 WMI 关闭时,进程外提供程序不会终止,尽管 WMI 将在 COM 超时后释放提供程序。 清理和终止可靠性问题对某些提供商而言比性能更重要,因此他们可能会采用进程外解决方案。
如果必须在提供程序中放置清理代码,可以采取两个选项。 执行此类清理的一个位置是 DllMain,即卸载 DLL 时作系统调用的 DLL 入口点函数。 可以直接将清理代码添加到 DllMain 中,以响应 DLL_PROCESS_DETACH。 在 DllMain 中实现清理代码可能有些困难,尤其是在 MFC 或 ATL 等编程环境中。 有关详细信息,请参阅Microsoft知识库文章Q148791“如何在 MFC 常规 DLL 中提供自己的 DllMain”。(此资源可能在某些语言和国家或地区不可用。
或者,还可以将清理代码放置在全局类的析构函数中。 有关详细信息,请参阅卸载提供程序。 Windows操作系统不会在堆上分配全局对象。 相反,操作系统会在 DLL 卸载期间调用析构函数。
下面是一个简单的清理过程,可能适合放在 WMI 的全局对象中。
class CMyCleanup
{
~CMyCleanup()
{
CloseHandle(m_hOpenFile);
CloseDatabaseConnection(g_hDatabase);
}
} g_Cleanup;
关于使用任一方法的清理代码所能执行的操作,有很多限制。 例如,不能以任何方式访问未隐式链接的线程和任何 DLL。 此外,在任何情况下都无法进行 COM 调用。
相关主题