Python wmi 参数反转
我在使用Python的wmi模块创建VSS快照时发现,参数的顺序不对就不管用,必须把它们反过来才能正常工作:
import wmi
def vss_create():
shadow_copy_service = wmi.WMI(moniker='winmgmts:\\\\.\\root\\cimv2:Win32_ShadowCopy')
res = shadow_copy_service.Create('ClientAccessible', 'C:\\')
在msdn文档中,这个函数应该这样使用:
Win32_ShadowCopy.Create("C:\\", "ClientAccessible");
为什么会这样呢?有没有办法按照预期的顺序来使用呢?
1 个回答
总结
看起来,PyWin32层对wmi对象的方法参数顺序进行了反转,这种情况至少已经存在了五年。相关的wmi规范说明,wmi客户端可以以任何顺序传递参数,因此PyWin32这样做并不是“错误”的,虽然我无法判断这是故意的还是意外的。我猜这种情况不太可能改变,因为需要向后兼容,但你可以通过将参数指定为关键字参数来解决这个问题,像这样:Create(Volume=, Context=)
。
详细信息
注意:在下面的详细信息中,我试图从Python WMI模块代码逐层深入,了解通过PyWin32代码访问的WMI对象,再到其他语言中使用的WMI对象,最后到通过MOF文件定义的WMI对象,甚至是相关的规范文档。这里有好几个层次,我会多次提到“WMI”,在不同层次上它的意思也不同。
当你提到“Python的wmi模块”时,是指Tim Golden的Python WMI模块(链接到源代码),它是基于PyWin32构建的吗?
当你从wmi模块获取一个Python WMI对象时,它的初始化步骤是在_wmi_object
for m in ole_object.Methods_:
self.methods[m.Name] = None
我将跳过Python的wmi模块,直接使用PyWin32来查看查询WMI COM对象时可用的方法:
>>> from win32com.client import GetObject
>>> vss = GetObject('winmgmts:\\\\.\\root\\cimv2:Win32_ShadowCopy')
>>> [method.Name for method in list(vss.Methods_)]
[u'Create', u'Revert']
我们看到Win32_ShadowCopy对象有
接下来,Python WMI包装类会进行一些设置工作,我没有完全追踪这些步骤,但似乎它会为COM对象的每个可用方法初始化_wmi_method
self.method = ole_object.Methods_ (method_name)
self.in_parameter_names = [(i.Name, i.IsArray) for i in self.in_parameters.Properties_]
使用列表推导式获取每个方法的可用参数。回到我的测试,探索没有Python WMI层的情况,它的输出如下:
>>> CreateMethod = vss.Methods_('Create')
>>> [n.Name for n in list(CreateMethod.InParameters.Properties_)]
[u'Context', u'Volume']
这个示例测试展示了PyWin32的后续,Win32_ShadowCopy的COM对象,以及
当你通过Python WMI的包装器调用Win32_ShadowCopy对象的_wmi_method
会这样做:
def __call__ (self, *args, **kwargs):
for n_arg in range (len (args)):
arg = args[n_arg]
parameter = parameters.Properties_[n_arg]
parameter.Value = arg
换句话说;它将你传入的参数(*args
)与存储的参数列表一一对应,按照你传入的顺序,将参数与WMI返回的参数顺序配对 - 也就是说,它并不聪明,只是将你输入的第一个参数与'Context'配对,第二个与'Volume'配对,结果就反了过来,导致你的代码崩溃。
调用方法还包括Python的**kwargs
参数,它可以接收所有给定的关键字,这意味着你可以这样做:
Create(Volume='C:\\', Context="ClientAccessible")
并通过将它们作为关键字参数来按你想要的顺序放置。(我还没有尝试过)。
我尝试追踪.Properties_
在PyWin32com中的查找,以确定在更低层次上顺序来自哪里,这经过了一长串动态和缓存的查找。我看不到发生了什么,也不够了解COM或PyWin32,不知道该寻找什么,所以这对我来说是个死胡同。
换个方法,试图从WMI对象的设置文件中找出顺序来源:运行随Windows一起提供的mofcomp.exe
,它处理管理对象格式(MOF)文件... 点击连接,创建类“Win32_ShadowCopy”;在方法列表中点击“Create”方法,然后点击“编辑方法”按钮;接着点击“编辑输入参数”,然后点击“显示MOF”,得到这个结果:
[abstract]
class __PARAMETERS
{
[in, ID(0): DisableOverride ToInstance] string Volume;
[in, ID(1): DisableOverride ToInstance] string Context = "ClientAccessible";
};
这是从Windows MOF文件中输出的“正确”参数顺序,带有参数的数字ID - 这意味着它们有一个正确的顺序0,1等。
c:\windows\system32\wbem\vss.mof
,这个看起来涵盖卷影复制对象的MOF文件包含:
[static,implemented,constructor] uint32 Create([in] string Volume,[in] string Context = "ClientAccessible",[out] string ShadowID);
而评论中的PowerShell示例在这个MSDN链接中包括$class.create("C:\", "ClientAccessible")
。
所以这三者都与相同的顺序相符,暗示有一个正确或标准的顺序。
这让我想到以下几种可能性:
- 有顺序信息来自PythonCOM,而wmi模块应该查看,但没有找到。我快速查找了一下,没找到参数列表中的ID/顺序数据,所以这似乎不太可能。
- 有某个我不知道的顺序信息,PyWin32 COM层应该查看但没有找到。 - 这里不太确定。
没有官方顺序。为了确认这一点,我们得到了一条有趣的链:
- 什么是WMI?微软实现的标准管理框架WBEM和CIM,由DTMF指定。(DTMF = 分布式管理任务组,WBEM是基于Web的企业管理,CIM是通用信息模型)。
- MOF是管理对象格式,是CIM的文本表示。
这份文档:http://www.dmtf.org/sites/default/files/standards/documents/DSP0221_3.0.0.pdf似乎是MOF规范。查看7.3.3 类声明部分,从第18页开始:
第570行:
“一个方法可以有零个或多个参数。”
第626到628行:
方法参数是通过名称而不是位置来识别的,调用方法的客户端可以以任何顺序传递相应的参数。因此,可以在方法签名的任何位置添加具有默认值的参数。
我不确定这是否是权威和最新的规范,也没有仔细阅读以寻找例外,但听起来你应该使用命名参数。
WMI对象和方法有MOF定义,而MOF规范说明你不应该依赖参数顺序;然而,通过PyWin32访问WMI对象时显示的顺序与(MSDN文档、MOF文件和PowerShell示例)不同。我仍然不知道为什么。
而搜索这个 引导我到这个邮件列表帖子,作者是Python wmi模块的Tim Golden,他基本上说了和我刚刚发现的相同的事情,只不过是五年前:
方法定义按照WMI返回的顺序获取参数[..] 我不知道参数顺序是否有任何保证[..] 快速浏览几个其他方法定义,似乎WMI始终以与MOF中定义的顺序相反的顺序返回参数。
此时,看起来PyWin32返回的参数列表与典型的Windows参数顺序相反,但如果CIM管理对象方法参数列表规范文档明确说明不要依赖参数顺序,这算不算一个bug呢?