卸载提供程序

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

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

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

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

本主题讨论了以下部分:

卸载空闲提供程序

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

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

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

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

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

访问提供程序的空闲时间

提供程序保持活动的最短时间由 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 保留的所有接口指针。

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

  2. 如果关联的__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 调用。

设置 Namepace 安全描述符

保护提供商

开发 WMI 提供程序