• wmi

    Windows Management Instrumentation (WMI) 是 Microsoft 对基于 Web 的企业管理 (WBEM) 的实现,这是一项行业计划,旨在为几乎所有有关计算机系统的信息提供通用信息模型 (CIM)。

    Python WMI 模块是 pywin32 扩展之上的轻量级包装器,隐藏了让 Python 与 WMI API 对话所需的一些混乱的管道。 它是纯 Python,应该适用于 2.1 及以上的任何版本的 Python(列表推导式)以及任何最新版本的 pywin32。

    How do I install it?

    How do I use it?

    这里有一个教程:http://timgolden.me.uk/python/wmi/tutorial.html,还有一些示例:http://timgolden.me.uk/python/wmi/cookbook.html,但作为快速品尝者 ,试试这个,显示所有停止的服务:

    远程执行bat文件

    Prerequisites

    如果您在最新的 Windows(2k、2k3、XP)上运行最新的 Python(2.1+)并且安装了 Mark Hammond 的 win32 扩展,那么您可能已经开始运行了。 否则…

    Windows

    如果您运行的是 Win9x / NT4,则需要从 Microsoft 获得 WMI 支持。 Microsoft URL 经常更改,因此我建议您这样做:http://www.google.com/search?q=wmi+downloads

    Python

    http://www.python.org/ (just in case you didn’t know)

    pywin32 (was win32all)

    http://starship.python.net/crew/mhammond/win32/Downloads.html 具体来说,版本 154/155 修复了影响 WMI 名称构造的问题。 如果没有此修复,您仍然可以工作,但一些更复杂的名字将会失败。

    makepy

    (注意,我自己在多个系统上的经验是这一步不是必需的。但是,如果您遇到问题......)您可能必须编译某些类型库的 makepy 支持。 据报道,以下内容具有重要意义:

    如果您以前没有这样做过,请启动PythonWin环境,从菜单中选择“工具”>“Com Makepy实用程序”,按名称选择库,然后单击[确定]。

    wmi Tutorial

    Introduction

    From the Wikipedia entry for WMI:

    Windows Management Instrumentation (WMI) 是 Windows 驱动程序模型的一组扩展,它提供了一个操作系统接口,通过该接口,检测组件可以提供信息和通知。 WMI 是 Microsoft 对分布式管理任务组 (DMTF) 基于 Web 的企业管理 (WBEM) 标准的实现。 WMI 允许 VBScript 等脚本语言在本地和远程管理 Microsoft Windows 个人计算机和服务器。 WMI 预装在 Windows Vista、Windows Server 2003、Windows XP、Windows Me 和 Windows 2000 中。

    本教程介绍专门使用 Python 编程语言访问 WMI,并假设您已下载并安装了 Python 本身、pywin32 扩展和 WMI Python 模块。 Python 能够通过支持 COM 的包(例如 Mark Hammond 的 pywin32 扩展或 Thomas Heller 的 ctypes 的 comtypes 衍生产品)来使用 WMI。 WMI 模块假定存在 pywin32 扩展,并向有时混乱的 WMI 脚本 API 公开稍微更 Python 友好的接口。 注意,它只是包装 pywin32 功能:如果 pywin32 下的 WMI 太慢,这个模块不会更快。

    The Basics

    除了演示模块内的一些功能之外,我们实际上并不打算研究一般可以使用 WMI 做什么。 网络上有很多关于您可以使用该技术做什么的示例(如果您足够努力,您可以用它做大多数事情)。 一些链接位于文档底部。

    Connecting

    大多数时候,您只需使用默认设置连接到本地计算机即可:

    如果需要连接到不同的机器,请将其名称指定为第一个参数:

    Querying

    使用 WMI 最常见的事情是了解系统的各个部分。 这涉及确定要询问哪个 WMI 类,然后将其视为 Python WMI 对象的属性:

    前面那句话中的“询问哪个 WMI 类”部分并不总是像听起来那么容易。 然而,有了一个好的搜索引擎,您就可以非常确信某个地方的某个人已经做了与您相同的事情,尽管使用的是不同的语言。 本文档底部有一些有用的链接,但我通常只是将“WMI 待办事项”粘贴到我选择的搜索引擎中,然后扫描结果以获取令人信服的答案。

    请注意,虽然在本例中只有一个操作系统,但 WMI 查询始终返回一个列表,可能只有一项。 列表中的项目由 Python 模块包装以进行属性访问。 在本例中,Win32_OperatingSystem 类具有多个属性,其中之一是 .Caption,它指的是已安装操作系统的名称。

    Monitoring

    WMI 有事件的概念。 有两种类型,内在的和外在的,下面将讨论。 Python 模块使差异尽可能透明。 假设您想跟踪启动的新进程:

    请注意,您必须将“创建”、“删除”、“修改”或“操作”之一传递给 .watch_for 方法。 如果没有,将会产生一些奇怪的错误。

    版本 1.3.1 中的新增功能: 如果您没有为内部事件指定通知类型,则将假定为“操作”,该操作会在对该类的对象进行任何更改时触发。

    Updating

    某些(但并非全部)WMI 类允许写回信息。 您可以像往常一样在 Python 中设置属性来完成此操作。 请注意,即使没有发生任何有用的事情,课程也可能会让您毫无怨言地这样做。 例如,要更改服务的显示名称,您需要调用服务的 .Change 方法。 可以直接更新displayName属性,不会报错,但是不会有任何效果。

    直接设置属性的最典型的地方是当您从类的 .new 方法创建一个全新的对象时。 在这种情况下,您可以将所有参数设置为 .new 调用的关键字参数,也可以随后一一指定它们。

    Methods

    某些 WMI 类具有对其进行操作的方法。 您可以像调用 Python 中的普通类或实例方法一样调用它们。 从1.3.1版本开始,参数可以定位; 在此之前,必须命名他们。 如果您想停止(正在运行的)RunAs 服务,其简称为“seclogon”:

    Advanced Stuff内容

    上面介绍了 WMI 模块的基本功能,这可能是许多人需要了解的内容。 然而,WMI 有许多细微的差别,您可能会发现自己在网络上的某个地方研究面向 VBS 的示例,并思考“我如何在 Python 中做到这一点?”。

    Advanced Connecting

    .connect 函数(别名为 .WMI)有相当多的参数,其中大部分是可选的,可以安全地忽略。 对于其中的大多数,我建议您参阅有关 WMI 名称的 MS 文档,它们相当简单地插入其中。 我们将在这里介绍一些比较常见的要求。

    Connecting to a remote machine

    这是最常见、最直接的额外参数。 它是第一个位置参数或名为“computer”的参数。 您可以通过这种方式连接到您自己的计算机,只需指定任何内容、空白字符串、点或任何计算机的 DNS 名称(包括 localhost)。 但通常你根本不需要传递参数。 要连接到名为“MachineB”的计算机上的 WMI 子系统:

    Connecting to a remote machine as a named user

    这是第二个最常见的需求,相当简单,但有一些注意事项。 首先,无论你如何努力混淆,你都无法以这种方式连接到本地计算机。 其次,这种技术并不总是能很好地适应多层 WMI 安全性。 下面的故障排除中将详细介绍这一点。 要使用用户名“fred”和密码“secret”连接到名为“MachineB”的计算机:

    Connecting to a particular namespace

    WMI 类被组织成命名空间层次结构。 大多数有用的都位于 cimv2 命名空间下,这是默认的。 但附加提供程序可能会提供额外的命名空间,例如 MicrosoftIISv2 或 DEFAULT/StdRegProv。 要使用与默认名称空间不同的名称空间(顺便说一句,不是名为默认名称的名称空间!),请通过名称空间参数指定它。 假定所有名称空间都从 root 开始,因此不需要指定它,但如果您想指定 root 名称空间本身,您可以执行以下操作:

    Specifying the full moniker

    在某些情况下,您希望能够传递完整的名字,要么是因为名字本身非常复杂,要么是因为您希望能够从其他地方剪切和粘贴。 在这种情况下,通过“moniker”参数将名字作为字符串传递:

    Connecting to a specific class or object

    完整名称的一个特殊情况是,它可用于直接连接到 WMI 类甚至特定对象。 Python 模块会注意到名字对象引用了一个类或对象,并将直接返回包装的对象而不是命名空间。 任何 WMI 对象的路径都可以用作名称来重新创建它,因此可以直接附加到 Win32_LogicalDisk 类,例如:

    这相当于通过正常机制获取类,尽管它主要在模块内部以及在翻译使用该技术的示例时使用。 访问特定对象是类似的,并且稍微有用一些:

    该对象与您通过使用 DeviceID=”C:” 参数查询 cimv2 命名空间中的 Win32_LogicalDisk 所收到的对象相同,因此从 Python 模块的角度来看,它并不是很有用。 然而,它在网络上的 VBS 示例中是相当常见的用法,并且稍微简化了翻译。

    Advanced Querying查询

    Filtering the returned list

    我们已经在上面看到了这一点; 我当时只是没有对此发表评论。 当您“调用”WMI 类时,您可以传递简单的等于参数来缩小列表范围。 此过滤发生在 WMI 级别; 一旦你得到了值,你仍然可以在 Python 中进行任何你想要的事后过滤。 请注意,即使结果列表只有一个元素长,它仍然是一个列表。 要查找所有固定磁盘:

    Selecting only certain fields

    默认情况下,将返回类中的所有字段。 出于性能或简单可管理性的原因,您可能希望指定查询仅返回某些字段。 这是通过将第一个位置参数设置为字段名称列表来完成的。 请注意,将始终返回关键字段(通常是 id 或唯一名称甚至组合):

    Performing arbitrary WQL queries

    如果要进行任意WMI查询,使用其伪SQL语言WQL,可以使用命名空间的.query方法。 列出所有非固定磁盘,例如:

    Advanced Monitoring

    Intrinsic events

    当您连接到 WMI 系统提供的通用事件机制以代表您轮询其他类时,就会发生内在事件。 您可以跟踪任何 WMI 类的创建、修改或删除。 您必须指定事件的类型(创建、删除、修改或只是捕获任何类型的操作)并给出以整秒为单位的轮询频率。 在这些参数之后,您可以以正常方式传递关键字参数来缩小返回事件的范围。 请注意,由于这是在幕后轮询,因此您不希望使用它来监视整个目录结构。

    要查看事件日志中的错误,请说:

    这里需要注意的是:这是轮询,并且按照您指定的频率进行。 这样就有可能错过活动。

    The return from a watcher is in fact a special _wmi_event object, subclass of a conventional _wmi_object, and which includes, for intrinsic events, the event type, timestamp and previous value for a modification as attributes: _wmi_event.event_type, _wmi_event.timestamp and _wmi_event.previous respectively.

    Extrinsic events

    请注意,虽然“Win32_NTLogEvent”以“Event”结尾,但它实际上并不是外部事件。 您可以通过检查它们的派生并查找 __ExtrinsicEvent 来判断哪些类是外部事件:

    或者,您可以自上而下查找 __ExtrinsicEvent 的子类:

    使用外部事件的方式与使用内部事件的方式大致相同。 不同之处在于,任何事件类型和延迟都会被忽略,因为 WMI 不会代表您进行轮询,而是等待底层子系统。 观察者的返回仍然是一个 _wmi_event 对象 (1.3.1),但没有 WMI 不提供的额外信息。 假设您希望在计算机退出待机状态时执行某些操作,例如通知 IM 组您的存在:

    对于内在修改事件,您可以比较触发器实例的之前和之后的值:

    Watchers with timeouts

    但还有更多! 尽管您可以在线程内使用这些观察程序(下面会详细介绍),但在某些情况下使用超时轮询它们可能会更容易。 例如,如果您想要监视两个盒子上的事件日志条目而不进入线程和队列:

    More About Methods

    Determing available methods

    如果您检查每个包装的 WMI 类用于缓存其包装方法的 .methods 字典的键,您将看到公开了哪些方法:

    Showing method signatures

    每个包装方法都会生成其函数签名作为其 repr 或 str。 如果诸如 .Shutdown 之类的功能需要额外的权限,也会指出:

    请注意,如果参数预计是一个列表,它将带有“[]”后缀。 另请注意,返回值始终是一个元组,尽管长度为一。

    Finding a method’s Win32 API equivalent

    我自己遇到这个问题有点惊讶,但是当您调用 WMI 方法(不幸的是,不是针对属性)时,WMI 会告诉您在幕后进行哪个 Win32 API 调用。 这作为函数包装器的 .provenance 属性公开:

    More Advanced Topics: Bits & Pieces

    Creating WMI Objects

    WMI 公开了一个 SpawnInstance_ 方法,该方法被包装为 Python WMI 类的 _wmi_object.new() 方法。 但你使用这种方法的频率远比你想象的要少。 例如,如果您想创建一个新的磁盘共享,而不是使用 Win32_Share.new,您实际上将调用 Win32_Share 类的 Create 方法。 事实上,大多数允许通过 WMI 创建实例的类都提供了 Create 方法(Win32_Process、Win32_Share 等):

    当您需要为一个 WMI 对象提供另一个动态创建的实例时,您需要生成一个新实例。 典型的示例是将安全描述符传递给新对象或将进程启动信息传递给新进程。 这个来自 MSDN 的例子可以被翻译成 Python,如下:

    WMI Classes/Objects

    Class/Object details

    当呈现为字符串时,每个类和对象将返回其结构的可读版本:

    The object hierarchy

    WMI 对象出现在类的层次结构中。 每个对象都知道它自己的祖先:

    您还可以通过查找命名类的所有子类来查看树,也可以选择通过正则表达式进行过滤。 要查找除内置事件类之外的所有外部事件类(由前导下划线表示):

    Comparing two WMI objects for equality

    _wmi_object.__eq__() 运算符在包装的 WMI 类中被重写,并调用底层 .CompareTo 方法,因此比较两个 WMI 对象是否相等应该做正确的事情。

    Associators

    Associators 是将其他类链接在一起的类。 例如,如果您想知道系统上有哪些组,以及每个组中有哪些用户:

    也可以用 associator 类来编写:

    版本 1.3.1 中的新增功能: _wmi_object.associators() 方法会将其结果转换为 _wmi_object。

    Caveats, Troubleshooting and Performance

    Speeding things up

    由于去年夏天与 Paul Tiemann 的有益合作,该模块能够在需要时结合缓存和轻量级调用来显着加快速度。 这里并未涵盖所有内容,但最直接的改进结合了删除运行时自省和缓存,以便仅根据需要生成包装器并可以预先缓存。

    Turning off introspection

    该模块最初的重点(现在仍然有很大一部分使用)是在解释器中。 因此,当您实例化 WMI 命名空间时,它会查找该命名空间中可用的所有类。 但这在较大的命名空间上需要相当长的时间,而且一旦你知道自己想要什么,即使在较小的命名空间上也是不必要的。 因此,在生产代码中,您可以将其关闭:

    如果您需要确定哪些类可用,您仍然可以使用上述 subclasses_of 功能来搜索,例如,运行时给定计算机上可用的性能类:

    Note

    从 v1.4 开始,find_classes 参数默认为 False:必须专门打开它。 但是......“classes”属性现在执行惰性查找,因此如果您直接或间接调用它,例如通过使用调用其属性查找魔术方法 _wmi_object._getAttributes() 的IPython,它将返回完整的列表 naeepsace 中的课程。

    Pre-cache class and method wrappers

    为了避免在首次查询类或首次调用其方法时出现初始查找命中,可以通过引用该类来预先将其推入缓存中。 因此,扩展上面的代码:

    Specifying fields in the query

    默认情况下,WMI 查询将返回每个实例中类的所有字段。 通过预先指定您感兴趣的字段作为查询的第一个参数,您将避免任何昂贵的查找。 尽管许多字段代表静态或廉价数据,但也有一些字段是动态计算的。 对于 Win32_Process 等类中的性能或其他实时数据尤其如此:

    Security

    目前这只是一小部分,更多的是一个提示,直到我掌握了一些更确凿的事实。 简而言之,迄今为止访问 WMI 功能的最简单方法是以 NT/AD 域上的域管理员用户身份运行。 其他技术当然也是可能的,但如果它们在任何时候停滞不前,你就必须努力通过至少三层安全保护,满怀希望地刺激每一层,直到你得到结果或厌恶地放弃。

    NT Security

    相关用户必须对正在调用其 WMI 功能的计算机具有某种访问权限。 这可能是由于被包含在本地管理组中,或者是通过授予指定用户特定的访问权限。

    DCOM Security

    WMI 是一种基于 DCOM 的技术,因此适用于 DCOM 连接的任何规则也适用于 WMI。 如果在 DCOM 级别进行身份验证时出现问题,那么从理论上讲,您在 Word.Application 上执行 DispatchEx 时应该也会遇到同样的问题。 您要查看的程序是 dcomcnfg.exe,这就是我现在要说的全部内容。

    WMI Security

    WMI 命名空间是具有自己的 ACL 的系统对象。 如果您转到 WMI MMC 管理单元(通过管理计算机界面访问)并访问命名空间的属性,将会出现一个安全选项卡。 在计算机上使用 WMI 功能的帐户需要通过此安全性拥有足够的访问权限。

    WMI and Threads

    WMI 是一种基于 COM/DCOM 的机制,因此适用于 COM 线程的规则也适用于 WMI。 无论您的程序是否显式调用 Python 线程,情况都是如此:例如,如果您在服务中运行,则无论您喜欢与否,您都可能正在使用线程,因为服务控制管理器似乎在以下位置运行服务控制代码: 与主服务不同的线程。

    CoInitialize & CoUninitialize

    任何想要使用线程的 COM 代码都必须指定线程模型。 关于这个主题有很多说法,但除非您有特定要求,否则通常可以在线程内实例化 WMI 对象之前初始化 COM 线程,然后再取消初始化:

    版本 1.4.1 中的新增功能: 从 v1.4 开始,底层代码将捕获通常因初始化线程 WMI 访问失败而导致的名字对象语法错误,并引发 x_wmi_uninitialised_thread 异常。

    See also

    Translations

    Authoritative Links

    Useful Examples

    Lists, Groups, etc.

     

    wmi Cookbook

    Introduction

    这些示例假设您正在使用此站点的 WMI 模块。 以下是可以在 win32 计算机上使用此模块完成的有用操作的示例。 它几乎没有触及 WMI 的皮毛,但也仅此而已。

    以下示例,除非另有说明,均假设您正在连接到当前计算机。 要连接到远程计算机,只需在 WMI 构造函数中指定远程计算机名称,并且通过 DCOM 的奇迹,一切都应该很好:

    Note

    这些示例设计得很完整,可以直接剪切并粘贴到 .py 文件中,甚至可以粘贴到打开的 Python 解释器窗口中(至少在 Win2000 上的 CMD 下运行;这就是我测试它们的方式)。 只需选择代码,包括最后一个空行,右键单击[复制],选择Python解释器窗口,然后右键单击。

    Examples

    List all running processes

    List all running notepad processes

    Create and then destroy a new notepad process

    Show the interface for the .Create method of a Win32_Process class

    wmi 模块尝试通过查询方法的输入和输出参数、接受输入参数作为 Python 关键字参数并将输出参数作为元组返回值返回,从而减轻 WMI 方法的繁重工作。 伪装成 WMI 方法的函数有一个 __doc__ 值,该值显示输入和返回值。

    Show all automatic services which are not running

    Show the percentage free space for each fixed disk

    Run notepad, wait until it’s closed and then show its text

    Note

    这是运行一个进程并知道它何时完成的示例,而不是操作在记事本中输入的文本。 所以我只是依赖这样一个事实:我指定记事本应该打开什么文件,然后检查其中的内容。

    该进程在远程计算机上无法正常工作,因为出于安全原因,在远程计算机上启动的进程没有界面(即您无法在桌面上看到它们)。 这种技术最有可能的用途是在远程服务器上运行 setup.exe,然后在完成后重新启动。

    Watch for new print jobs

    Reboot a remote machine

    Note

    要对远程系统执行如此剧烈的操作,WMI 脚本必须具有 RemoteShutdown 权限,这意味着您必须在连接名字对象中指定它们。 WMI 构造函数允许您传入精确的名称,或指定您需要的名称部分。 使用 wmi.WMI.__init__ 的帮助来了解更多信息。

    Show the IP and MAC addresses for IP-enabled network interfaces

    What’s running on startup and from where?

    Watch for errors in the event log

    List registry keys

    Note

    此示例和下面的示例使用了早期添加到 wmi 包中的便利函数 Registry() 。 它完全等同于:

    Add a new registry key

    Add a new registry value

    Create a new IIS site

    Show shared drives

    Show print jobs

    Note

    Microsoft 的此页面是使用 WMI 处理打印机问题的一个很好的起点。

    Show disk partitions

    Install a product

    Note

    示例是 Roger Upole 向 python-win32 邮件列表发帖之后

    Connect to another machine as a named user

    Note

    无论您如何努力混淆服务器名称,您都无法以这种方式连接到本地计算机。

    Show a method’s signature

    Schedule a job

    Note

    WMI ScheduledJob 类对应于 AT Windows 服务(通过“at”命令控制)。 据我所知,它与计划任务机制无关,由控制面板小程序控制。

    Run a process minimised

    Note

    感谢 Keith Veleba 提供了引发此示例的问题和代码

    Find Drive Types

    List Namespaces

    Use WMI in a thread

    Note

    注意 pythoncom.Co(Un)initialize 的使用。 WMI是基于COM的技术,因此要在线程中使用它,必须初始化COM线程模型。 例如,如果您在隐式线程化的服务中运行,这也适用。

    Monitor multiple machines for power events

    这是外部事件、线程和远程监控的演示......全部都在一个小包中! 这个想法是,每当机器进入或离开挂起模式时,电源子系统都会通过其 WMI 提供程序生成外部事件。 外部事件很有用,因为 WMI 不必轮询它们,因此您不应错过任何事件。 多台机器只是使用线程的一个实际示例。

    Note

    注意线程控制代码中CoInitialize和CoUninitialize的使用。 另请注意_wmi_class.watch_for() 的简化使用,它将透明地用于内部和外部事件。

    Find the current wallpaper