卸载提供程序

WMI 使用完提供程序后,它会从内存中卸载该提供程序。 WMI 卸载提供程序的主要原因是节省系统资源。 因此,必须添加允许 WMI 以高效方式卸载提供程序的代码。 这介于缓存控制中指定的间隔到 WMI 卸载提供程序的间隔的两倍之间。

WMI 通过以下方式之一卸载提供程序:

  • 在提供程序完成提供给它的任务后卸载提供程序。
  • 当用户关闭系统时,快速卸载所有提供程序。 请注意,从命令行关闭 WMI 服务时,WMI 会卸载进程内提供程序。

虽然第一种方案更为常见,但必须针对这两种可能性编写提供程序。

本主题讨论以下部分内容:

卸载空闲提供程序

WMI 在卸载空闲提供程序时执行以下操作:

  • 确定提供程序是否处于空闲状态。

    WMI 使用 ClearAfter 属性来确定在卸载提供程序之前该提供程序可以保持空闲状态的时长。 有关详细信息,请参阅访问提供程序的空闲时间

  • 调用提供程序的 Release 方法。

    如果提供程序是纯提供程序,则 Release 会从活动内存中完全移除该提供程序。 但是,在 WMI 调用 Release 之后,非纯提供程序可能会继续运行。

访问提供程序的空闲时间

提供程序保持活动状态的最短持续时间由 ClearAfter 属性确定。 可以在 \root 命名空间中派生自 WMI 系统类 __CacheControl 的类实例中找到 ClearAfter。

以下列表描述了派生自 __CacheControl 的类,这些类控制提供程序卸载:

可以通过编辑特定类型的提供程序的缓存控制实例中的 ClearAfter 属性来更改 WMI 允许提供程序保持非活动状态的最短持续时间。 例如,若要限制属性提供程序可以保持空闲状态的持续时间,请在 \root 命名空间中编辑 __PropertyProviderCacheControl 实例的 ClearAfter 属性。

卸载同时是 WMI 客户端的提供程序

提供程序在完成被调用来执行的提供程序功能后,可能需要保留 WMI 的客户端。 例如,推送提供程序可能需要向 WMI 发出查询。 有关详细信息,请参阅确定推送或拉取状态。 在这种情况下,表示提供程序的 __Win32Provider 实例的 Pure 属性应设置为 TRUE。 如果 Pure 属性设置为 FALSE,则当 WMI 调用提供程序主接口的 Release 方法时,提供程序将通过在所有未完成的接口点上调用 IUnknown::Release 来准备卸载。 有关详细信息,请参阅 __Win32Provider 中的“备注”部分。

以下过程介绍如何为提供程序的主接口实现释放方法。

卸载提供程序

  1. 当 WMI 调用提供程序主接口的 Release 方法时,释放针对 WMI 保持的所有接口指针。

    通常,提供程序保持指向 IWbemProviderInit::Initialize 中提供的 IWbemServicesIWbemContext 接口的指针。

  2. 如果关联 __Win32Provider 实例中的 Pure 属性设置为 FALSE,则在 WMI 调用 Release 后,提供程序可以转换为客户端应用程序的角色。 但是,WMI 无法卸载作为客户端系统运行的提供程序,这会增加系统开销。

    Pure 设置为 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 调用。

设置命名空间安全描述符

保护提供程序

开发 WMI 提供程序