mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Reworking windows.com.Variant + working on new wmi API + wmi test
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
import pytest
|
||||
|
||||
import windows
|
||||
import windows.generated_def as gdef
|
||||
|
||||
from pfwtest import *
|
||||
|
||||
## This comment was in test_system.py: still revelant ?
|
||||
# Well, pytest initialize COM with its own parameters
|
||||
# It might make our own com.init() in WMI fail and therefore not call
|
||||
# CoInitializeSecurity. But looks like pytest/default COM-security parameters
|
||||
# does not allow to perform the request we want..
|
||||
# So we try & do it ourself here.
|
||||
|
||||
wmimanager = windows.system.wmi
|
||||
|
||||
@pytest.mark.parametrize("name, expected_cls", [
|
||||
("root\\cimv2", "Win32_Process"),
|
||||
("root\\subscription", "__EventFilter"),
|
||||
])
|
||||
def test_wmimanager_getnamespace(name, expected_cls):
|
||||
namespace = wmimanager[name]
|
||||
assert namespace.name == name
|
||||
assert namespace.get_object(expected_cls)
|
||||
|
||||
|
||||
def test_wmimanager_subnamespaces():
|
||||
subnamespaces = wmimanager.get_subnamespaces("root")
|
||||
subnamespaces = [x.lower() for x in subnamespaces]
|
||||
|
||||
assert "cimv2" in subnamespaces
|
||||
assert "security" in subnamespaces
|
||||
assert "subscription" in subnamespaces
|
||||
|
||||
# Test WmiNamespace
|
||||
|
||||
@pytest.mark.parametrize("name, query",[
|
||||
("root\\cimv2", "select * from Win32_Process"),
|
||||
("root\\subscription", "select * from __EventFilter"),
|
||||
])
|
||||
def test_query_select(name, query):
|
||||
namespace = wmimanager[name]
|
||||
x = namespace.query(query)
|
||||
assert x
|
||||
assert isinstance(x, list)
|
||||
|
||||
|
||||
def test_bad_query_raise():
|
||||
namespace = wmimanager["root\\cimv2"]
|
||||
with pytest.raises(WindowsError) as e:
|
||||
x = namespace.query("BADSELECT QUERY BAD")
|
||||
assert (e.value.winerror & 0xffffffff) == gdef.WBEM_E_INVALID_QUERY
|
||||
|
||||
def test_create_class_enum():
|
||||
namespace = wmimanager["root\\cimv2"]
|
||||
enum = namespace.create_class_enum(None)
|
||||
assert enum
|
||||
classes = list(enum)
|
||||
cls_names = [cls["__CLASS"].lower() for cls in classes]
|
||||
assert "win32_process" in cls_names
|
||||
assert "win32_shortcutfile" in cls_names
|
||||
assert "__win32provider" in cls_names
|
||||
|
||||
@pytest.mark.parametrize("name, cls", [
|
||||
("root\\cimv2", "Win32_Process"),
|
||||
("root\\subscription", "__EventFilter"),
|
||||
])
|
||||
def test_get_object(name, cls):
|
||||
namespace = wmimanager[name]
|
||||
assert namespace.name == name
|
||||
obj = namespace.get_object(cls)
|
||||
assert obj["__CLASS"] == cls
|
||||
assert obj["__PATH"]
|
||||
|
||||
# Todo: test
|
||||
# - put_instance
|
||||
# - exec_method
|
||||
|
||||
@pytest.mark.parametrize("cmdline", [r"c:\windows\notepad.exe trolol.exe"])
|
||||
def test_exec_method_Win32_Process_create(cmdline):
|
||||
namespace = wmimanager["root\\cimv2"]
|
||||
win32_process_cls = namespace.get_object("Win32_Process")
|
||||
inparam = win32_process_cls.get_method("Create").inparam.spawn_instance()
|
||||
inparam["CommandLine"] = cmdline
|
||||
result = namespace.exec_method(win32_process_cls, "Create", inparam)
|
||||
assert result
|
||||
assert not result["ReturnValue"]
|
||||
assert result["ProcessId"]
|
||||
proc = windows.WinProcess(pid=result["ProcessId"])
|
||||
assert proc.peb.commandline.str == cmdline
|
||||
proc.exit(0)
|
||||
|
||||
|
||||
## Test enum
|
||||
def test_enumeration_iteration_no_timeout():
|
||||
namespace = wmimanager["root\\cimv2"]
|
||||
processes = namespace.exec_query("select * from Win32_Process").all()
|
||||
assert isinstance(processes, list)
|
||||
assert processes
|
||||
processes = list(namespace.exec_query("select * from Win32_Process"))
|
||||
assert processes
|
||||
assert isinstance(processes, list)
|
||||
proc = namespace.exec_query("select * from Win32_Process").next()
|
||||
assert proc
|
||||
assert proc["__CLASS"].lower() == "win32_process"
|
||||
|
||||
def test_enumeration_iteration_timeout():
|
||||
namespace = wmimanager["root\\cimv2"]
|
||||
timegen = namespace.exec_query("select * from Win32_Process").iter_timeout(0)
|
||||
# Iter on Win32_Process should not be immediat
|
||||
# so itering on timegen should trigger a timeout
|
||||
with pytest.raises(WindowsError) as e:
|
||||
x = list(timegen)
|
||||
assert (e.value.winerror & 0xffffffff) == gdef.WBEM_S_TIMEDOUT
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wmi_cls():
|
||||
# Test expect the cls to have a "Name" attribute & "Create" method
|
||||
# Maybe doing something more generic
|
||||
namespace = wmimanager["root\\cimv2"]
|
||||
yield namespace.get_object("Win32_Process")
|
||||
|
||||
def test_wmiobject_spawn(wmi_cls):
|
||||
assert wmi_cls["__Genus"] == wmi_cls.genus == gdef.WBEM_GENUS_CLASS
|
||||
wmi_obj = wmi_cls()
|
||||
assert wmi_obj["__Genus"] == wmi_obj.genus == gdef.WBEM_GENUS_INSTANCE
|
||||
assert wmi_obj["__CLASS"] == wmi_cls["__CLASS"]
|
||||
|
||||
def test_wmiobject_getitem(wmi_cls):
|
||||
assert wmi_cls["Name"] is None
|
||||
wmi_obj = wmi_cls()
|
||||
assert wmi_obj["Name"] is None
|
||||
with pytest.raises(WindowsError) as e:
|
||||
wmi_obj["BAD_NAME"]
|
||||
assert (e.value.winerror & 0xffffffff) == gdef.WBEM_E_NOT_FOUND
|
||||
# Complexe type
|
||||
assert isinstance(wmi_obj["__CLASS"], basestring)
|
||||
assert isinstance(wmi_obj["__PROPERTY_COUNT"], int)
|
||||
assert isinstance(wmi_obj["__DERIVATION"], list)
|
||||
|
||||
props = wmi_obj.get_properties()
|
||||
assert isinstance(props, list)
|
||||
assert len(props) > wmi_obj["__PROPERTY_COUNT"]
|
||||
# Check that other dict-like methods exists
|
||||
assert wmi_obj.keys()
|
||||
assert wmi_obj.values()
|
||||
assert wmi_obj.items()
|
||||
|
||||
|
||||
def test_wmiobject_getmethod(wmi_cls):
|
||||
wmi_method = wmi_cls.get_method("Create")
|
||||
# Wmi method is a custom PFW object (namedtuple)
|
||||
assert wmi_method
|
||||
assert wmi_method.inparam
|
||||
inparam_attrs = wmi_method.inparam.keys()
|
||||
assert "CommandLine" in inparam_attrs
|
||||
assert wmi_method.outparam
|
||||
outparam_attrs = wmi_method.outparam.keys()
|
||||
assert "ProcessId" in outparam_attrs
|
||||
|
||||
def test_wmiobject_setitem(wmi_cls):
|
||||
wmi_obj = wmi_cls()
|
||||
assert wmi_obj["Name"] is None
|
||||
wmi_obj["Name"] = "Test"
|
||||
assert wmi_obj["Name"] == "Test"
|
||||
# Strange but that how WMI api works with variant :D
|
||||
wmi_obj["Name"] = 2
|
||||
assert wmi_obj["Name"] == "2"
|
||||
with pytest.raises(WindowsError) as e:
|
||||
wmi_obj["PageFaults"] = "ERROR_BAD_INT"
|
||||
assert (e.value.winerror & 0xffffffff) == gdef.WBEM_E_TYPE_MISMATCH
|
||||
with pytest.raises(WindowsError) as e:
|
||||
wmi_obj["__PROPERTY_COUNT"] = 42
|
||||
assert (e.value.winerror & 0xffffffff) == gdef.WBEM_E_READ_ONLY
|
||||
|
||||
|
||||
|
||||
|
||||
+147
-94
@@ -7,6 +7,7 @@ import windows
|
||||
from windows import winproxy
|
||||
from windows.generated_def.winstructs import *
|
||||
|
||||
import windows.generated_def as gdef
|
||||
from windows.generated_def import RPC_C_IMP_LEVEL_IMPERSONATE, CLSCTX_INPROC_SERVER
|
||||
from windows.generated_def import interfaces
|
||||
from windows.generated_def.interfaces import generate_IID, IID
|
||||
@@ -35,9 +36,17 @@ def create_instance(clsiid, targetinterface, custom_iid=None, context=CLSCTX_INP
|
||||
custom_iid = targetinterface.IID
|
||||
return winproxy.CoCreateInstance(byref(clsiid), None, context, byref(custom_iid), byref(targetinterface))
|
||||
|
||||
|
||||
def resolve_progid(progid):
|
||||
clsid = CLSID()
|
||||
winproxy.CLSIDFromProgID(progid, clsid)
|
||||
# We just filed the CLSID: refresh the __repr__
|
||||
clsid.update_strid()
|
||||
return clsid
|
||||
|
||||
# Improved COM object
|
||||
# Todo: ctypes_genertation extended struct ?
|
||||
class ImprovedSAFEARRAY(SAFEARRAY):
|
||||
# Todo: ctypes_generation extended struct ?
|
||||
class SafeArray(SAFEARRAY):
|
||||
@classmethod
|
||||
def of_type(cls, addr, t):
|
||||
self = cls.from_address(addr)
|
||||
@@ -46,7 +55,7 @@ class ImprovedSAFEARRAY(SAFEARRAY):
|
||||
|
||||
@classmethod
|
||||
def from_PSAFEARRAY(self, psafearray):
|
||||
res = cast(psafearray, POINTER(ImprovedSAFEARRAY))[0]
|
||||
res = cast(psafearray, POINTER(SafeArray))[0]
|
||||
return res
|
||||
|
||||
def to_list(self, t=None):
|
||||
@@ -83,111 +92,155 @@ class ImprovedSAFEARRAY(SAFEARRAY):
|
||||
#VT_LPWSTR : LPWSTR,
|
||||
#}
|
||||
|
||||
class ImprovedVariant(VARIANT):
|
||||
@property
|
||||
def asbstr(self):
|
||||
if self.vt != VT_BSTR:
|
||||
raise ValueError("asbstr on non-bstr variant")
|
||||
#import pdb;pdb.set_trace()
|
||||
return self._VARIANT_NAME_3.bstrVal
|
||||
# VARIANT type checker
|
||||
# Allow to guess a VARIANT_TYPE og a python value
|
||||
|
||||
@property
|
||||
def aslong(self):
|
||||
if not self.vt in [VT_I4]:
|
||||
raise ValueError("aslong on non-long variant")
|
||||
return self._VARIANT_NAME_3.lVal
|
||||
def never_match(value):
|
||||
return False
|
||||
|
||||
@property
|
||||
def asbool(self):
|
||||
if not self.vt in [VT_BOOL]:
|
||||
raise ValueError("get_bstr on non-bool variant")
|
||||
return bool(self._VARIANT_NAME_3.boolVal)
|
||||
def check_type_null(value):
|
||||
return value is None
|
||||
|
||||
@property
|
||||
def asdispatch(self):
|
||||
if not self.vt in [VT_DISPATCH]:
|
||||
raise ValueError("asdispatch on non-VT_DISPATCH variant")
|
||||
return interfaces.IDispatch(self._VARIANT_NAME_3.pdispVal)
|
||||
def check_type_i4(value):
|
||||
# 31 ? as we may want to keep sign :)
|
||||
return isinstance(value, (int, long)) and (value).bit_length() <= 32
|
||||
|
||||
@property
|
||||
def asshort(self):
|
||||
if not self.vt in [VT_I2]:
|
||||
raise ValueError("asshort on non-VT_I2 variant")
|
||||
return self._VARIANT_NAME_3.iVal
|
||||
def check_type_i8(value):
|
||||
# 63 ? as we may want to keep sign :)
|
||||
return isinstance(value, (int, long)) and (value).bit_length() <= 64
|
||||
|
||||
@property
|
||||
def asbyte(self):
|
||||
if not self.vt in [VT_UI1]:
|
||||
raise ValueError("asbyte on non-VT_UI1 variant")
|
||||
return self._VARIANT_NAME_3.bVal
|
||||
def check_type_bstr(value):
|
||||
return isinstance(value, basestring)
|
||||
|
||||
@property
|
||||
def asunknown(self):
|
||||
if not self.vt in [VT_UNKNOWN]:
|
||||
raise ValueError("asunknown on non-VT_UNKNOWN variant")
|
||||
return self._VARIANT_NAME_3.punkVal
|
||||
def check_type_bool(value):
|
||||
return isinstance(value, bool)
|
||||
|
||||
@property
|
||||
def asarray(self):
|
||||
if not self.vt & VT_ARRAY:
|
||||
raise ValueError("asarray on non-VT_ARRAY variant")
|
||||
# TODO: auto extract VT_TYPE for the array ?
|
||||
#type = VT_VALUE_TO_TYPE[self.vt & VT_TYPEMASK]
|
||||
return ImprovedSAFEARRAY.from_PSAFEARRAY(self._VARIANT_NAME_3.parray)
|
||||
def check_type_array(value):
|
||||
return True
|
||||
|
||||
|
||||
@property
|
||||
def aslong_array(self):
|
||||
if not self.vt & VT_I4:
|
||||
raise ValueError("as_bstr_array on non-VT_BSTR variant")
|
||||
return self.asarray.to_list(LONG)
|
||||
VARIAN_NAME_3_TYPE = [f[1] for f in VARIANT._fields_ if f[0] == "_VARIANT_NAME_3"][0]
|
||||
|
||||
def generate_asarray_property(vttype):
|
||||
empty = object()
|
||||
class Variant(VARIANT):
|
||||
def __init__(self, value=empty, type=None):
|
||||
if type is not None:
|
||||
self.set_value_and_type(value, type)
|
||||
return
|
||||
elif value is empty:
|
||||
self.vt = VT_EMPTY
|
||||
return
|
||||
self.guess_type_and_set_value(value)
|
||||
|
||||
# Copy raw-ctypes fields which is a descriptor :)
|
||||
rawvt = VARIANT.vt
|
||||
|
||||
# Most of the value in the colunm[1]
|
||||
# are attribute of the sub-union _VARIANT_NAME_3
|
||||
# This union must be ctypes-anonymous for this code to works
|
||||
# We want to access these directly from the VARIANT
|
||||
# to allow custom descriptor for complexe type to be referenced here
|
||||
CHECK_TYPE = [
|
||||
# Order is important
|
||||
# as VT_I4 check may match VT_BOOL values
|
||||
# VT_BOOL check must be before VT_I4 one
|
||||
(VT_BOOL, "boolVal", check_type_bool),
|
||||
(VT_I4, "lVal", check_type_i4),
|
||||
(VT_I8, "llVal", check_type_i8),
|
||||
(VT_BSTR, "bstrVal", check_type_bstr),
|
||||
(VT_NULL, None, check_type_null),
|
||||
(VT_EMPTY, None, never_match),
|
||||
(VT_DISPATCH, "pdispVal", never_match), # I cannot recognize DISPATCH ptr for now
|
||||
(VT_UNKNOWN, "punkVal", never_match), # recognise PFW ComInterface ?
|
||||
# Test: do not allow auto-creation of small int values
|
||||
# I don't know but a feel it may confuse some API expecting VT_I4
|
||||
(VT_I2, "iVal", never_match),
|
||||
(VT_UI1, "bVal", never_match),
|
||||
]
|
||||
|
||||
VARIANT_TYPE_BY_NAME = {f[0]: f[1] for f in VARIAN_NAME_3_TYPE._fields_}
|
||||
QUICK_CHECK_TYPE = {x: y for x,y, _ in CHECK_TYPE}
|
||||
|
||||
def get_vt(self):
|
||||
rawvt = super(Variant, self).vt
|
||||
return gdef.VARENUM.mapper[self.rawvt]
|
||||
|
||||
def set_vt(self, value):
|
||||
self.rawvt = value
|
||||
|
||||
vt = property(get_vt, set_vt)
|
||||
|
||||
def set_value_and_type(self, value, type):
|
||||
attr = self.QUICK_CHECK_TYPE[type]
|
||||
# No check: user must be careful about non-match value&type
|
||||
setattr(self, attr, value)
|
||||
self.vt = type
|
||||
|
||||
def get_value_based_on_type(self):
|
||||
rawvt = self.rawvt
|
||||
if rawvt & VT_ARRAY:
|
||||
realtype = rawvt & ~VT_ARRAY
|
||||
attr = self.QUICK_CHECK_TYPE[realtype]
|
||||
attrtype = self.VARIANT_TYPE_BY_NAME[attr]
|
||||
array = SafeArray.from_PSAFEARRAY(self._VARIANT_NAME_3.parray)
|
||||
return array.to_list(attrtype)
|
||||
attr = self.QUICK_CHECK_TYPE[rawvt]
|
||||
if attr is None:
|
||||
return None
|
||||
return getattr(self, attr)
|
||||
|
||||
def guess_type_and_set_value(self, value):
|
||||
for t, attr, check in self.CHECK_TYPE:
|
||||
try:
|
||||
checkres = check(value)
|
||||
except TypeError as e:
|
||||
continue
|
||||
if checkres:
|
||||
self.vt = t
|
||||
if attr is not None:
|
||||
setattr(self, attr, value)
|
||||
return True
|
||||
raise ValueError("Could not guess VT_TYPE for <{0}> of type <{1}>".format(value, type(value)))
|
||||
|
||||
value = property(get_value_based_on_type, guess_type_and_set_value)
|
||||
|
||||
# quick_check: bypass python lookup-limitation
|
||||
def generate_getter(vt_type, transfo=(lambda x:x), quick_check=QUICK_CHECK_TYPE):
|
||||
attr = quick_check[vt_type]
|
||||
@property
|
||||
def as_array_generated(self):
|
||||
# TODO: vt check like the others ?
|
||||
return self.asarray.to_list(vttype)
|
||||
return as_array_generated
|
||||
def getter(self):
|
||||
if not self.rawvt == vt_type:
|
||||
raise ValueError("Invalid vt-type for attribute expected <{0}> got <{1}>".format(vt_type, self.vt))
|
||||
return transfo(getattr(self, attr))
|
||||
return getter
|
||||
|
||||
asbstr_array = generate_asarray_property(BSTR)
|
||||
aslong_array = generate_asarray_property(LONG)
|
||||
asbyte_array = generate_asarray_property(BYTE)
|
||||
asbool_array = generate_asarray_property(VARIANT_BOOL)
|
||||
asbstr = generate_getter(VT_BSTR)
|
||||
aslong = generate_getter(VT_I4)
|
||||
asbool = generate_getter(VT_BOOL)
|
||||
asdispatch = generate_getter(VT_DISPATCH, transfo=interfaces.IDispatch)
|
||||
asshort = generate_getter(VT_I2)
|
||||
asbyte = generate_getter(VT_UI1)
|
||||
asunknown = generate_getter(VT_UNKNOWN)
|
||||
|
||||
def to_pyobject(self):
|
||||
# if self.vt & VT_ARRAY:
|
||||
# # Something better TODO i guess
|
||||
# if self.vt & VT_TYPEMASK == VT_BSTR:
|
||||
# import pdb;pdb.set_trace()
|
||||
# print("VT_TYPEMASK ARRAY")
|
||||
# return self.asarray.to_list(BSTR)
|
||||
# if self.vt & VT_TYPEMASK == VT_I4:
|
||||
# import pdb;pdb.set_trace()
|
||||
# print("VT_TYPEMASK ARRAY")
|
||||
# return self.asarray.to_list(LONG)
|
||||
# raise NotImplementedError("Variant of type {0:#x}".format(self.vt))
|
||||
# use the ImprovedVariant.MAPPER that dispatch by self.vt
|
||||
try:
|
||||
return self.MAPPER[self.vt](self)
|
||||
except KeyError:
|
||||
raise NotImplementedError("Variant of type {0:#x}".format(self.vt))
|
||||
def __repr__(self):
|
||||
return """<{0} of type {1}>""".format(type(self).__name__, self.vt)
|
||||
|
||||
|
||||
ImprovedVariant.MAPPER = {
|
||||
VT_UI1: ImprovedVariant.asbyte.fget,
|
||||
VT_I2: ImprovedVariant.asshort.fget,
|
||||
VT_DISPATCH: ImprovedVariant.asdispatch.fget,
|
||||
VT_BOOL: ImprovedVariant.asbool.fget,
|
||||
VT_I4: ImprovedVariant.aslong.fget,
|
||||
VT_BSTR: ImprovedVariant.asbstr.fget,
|
||||
VT_EMPTY: (lambda x: None),
|
||||
VT_NULL: (lambda x: None),
|
||||
VT_UNKNOWN: ImprovedVariant.asunknown.fget,
|
||||
(VT_ARRAY | VT_BSTR): ImprovedVariant.asbstr_array.fget,
|
||||
(VT_ARRAY | VT_I4): ImprovedVariant.aslong_array.fget,
|
||||
(VT_ARRAY | VT_UI1): ImprovedVariant.asbyte_array.fget,
|
||||
(VT_ARRAY | VT_BOOL): ImprovedVariant.asbool_array.fget
|
||||
}
|
||||
# Deprecated: remove me when test pass :)
|
||||
# ImprovedVariant.MAPPER = {
|
||||
# VT_UI1: ImprovedVariant.asbyte.fget,
|
||||
# VT_I2: ImprovedVariant.asshort.fget,
|
||||
# VT_DISPATCH: ImprovedVariant.asdispatch.fget,
|
||||
# VT_BOOL: ImprovedVariant.asbool.fget,
|
||||
# VT_I4: ImprovedVariant.aslong.fget,
|
||||
# VT_BSTR: ImprovedVariant.asbstr.fget,
|
||||
# VT_EMPTY: (lambda x: None),
|
||||
# VT_NULL: (lambda x: None),
|
||||
# VT_UNKNOWN: ImprovedVariant.asunknown.fget,
|
||||
# (VT_ARRAY | VT_BSTR): ImprovedVariant.asbstr_array.fget,
|
||||
# (VT_ARRAY | VT_I4): ImprovedVariant.aslong_array.fget,
|
||||
# (VT_ARRAY | VT_UI1): ImprovedVariant.asbyte_array.fget,
|
||||
# (VT_ARRAY | VT_BOOL): ImprovedVariant.asbool_array.fget
|
||||
# }
|
||||
|
||||
|
||||
|
||||
|
||||
+114
-43
@@ -2,13 +2,13 @@ import windows
|
||||
import ctypes
|
||||
import struct
|
||||
import functools
|
||||
from collections import namedtuple
|
||||
|
||||
from ctypes.wintypes import *
|
||||
|
||||
import windows.com
|
||||
import windows.generated_def as gdef
|
||||
from windows.generated_def.winstructs import *
|
||||
from windows.generated_def.interfaces import IWbemLocator, IWbemServices, IEnumWbemClassObject, IWbemClassObject, IWbemCallResult
|
||||
|
||||
# Common error check for all WMI COM interfaces
|
||||
# This 'just' add the corresponding 'WBEMSTATUS' to the hresult error code
|
||||
@@ -16,36 +16,47 @@ class WmiComInterface(object):
|
||||
def errcheck(self, result, func, args):
|
||||
if result < 0:
|
||||
wmitag = gdef.WBEMSTATUS.mapper[result & 0xffffffff]
|
||||
raise WindowsError(result , wmitag)
|
||||
raise WindowsError(result, wmitag)
|
||||
return args
|
||||
|
||||
# https://docs.microsoft.com/en-us/windows/desktop/api/wbemcli/nn-wbemcli-iwbemclassobject
|
||||
|
||||
WmiMethod = namedtuple("WmiMethod", ["inparam", "outparam"])
|
||||
|
||||
# https://docs.microsoft.com/en-us/windows/desktop/WmiSdk/calling-a-method
|
||||
class WmiObject(IWbemClassObject, WmiComInterface):
|
||||
class WmiObject(gdef.IWbemClassObject, WmiComInterface):
|
||||
## low level API
|
||||
|
||||
def get_variant(self, name):
|
||||
variant_res = windows.com.ImprovedVariant()
|
||||
if not isinstance(name, basestring):
|
||||
nametype = type(name).__name__
|
||||
raise TypeError("WmiObject attributes name must be str, not <{0}>".format(nametype))
|
||||
variant_res = windows.com.Variant()
|
||||
self.Get(name, 0, variant_res, None, None)
|
||||
return variant_res
|
||||
|
||||
def get(self, name):
|
||||
return self.get_variant(name).to_pyobject()
|
||||
return self.get_variant(name).value
|
||||
|
||||
def get_method(self, name):
|
||||
inpararm = type(self)()
|
||||
outpararm = type(self)()
|
||||
variant_res = windows.com.ImprovedVariant()
|
||||
variant_res = windows.com.Variant()
|
||||
self.GetMethod(name, 0, inpararm, outpararm)
|
||||
return inpararm, outpararm
|
||||
return WmiMethod(inpararm, outpararm)
|
||||
|
||||
|
||||
def put_variant(self, name, variant):
|
||||
if not isinstance(name, basestring):
|
||||
nametype = type(name).__name__
|
||||
raise TypeError("WmiObject attributes name must be str, not <{0}>".format(nametype))
|
||||
return self.Put(name, 0, variant, 0)
|
||||
|
||||
def put(self, name, value):
|
||||
variant_value = windows.com.ImprovedVariant(value)
|
||||
variant_value = windows.com.Variant(value)
|
||||
return self.put_variant(name, variant_value)
|
||||
|
||||
def spawn(self):
|
||||
def spawn_instance(self):
|
||||
instance = type(self)()
|
||||
self.SpawnInstance(0, instance)
|
||||
return instance
|
||||
@@ -54,19 +65,35 @@ class WmiObject(IWbemClassObject, WmiComInterface):
|
||||
def genus(self):
|
||||
return gdef.tag_WBEM_GENUS_TYPE.mapper[self.get("__GENUS")]
|
||||
|
||||
@property
|
||||
def properties(self):
|
||||
res = POINTER(SAFEARRAY)()
|
||||
self.GetNames(None, 0, None, byref(res))
|
||||
safe_array = ctypes.cast(res, POINTER(windows.com.ImprovedSAFEARRAY))[0]
|
||||
safe_array.elt_type = BSTR
|
||||
return safe_array.to_list()
|
||||
## Higher level API
|
||||
|
||||
# TODO: put this in WmiObject
|
||||
def as_dict(self, attrs="**"):
|
||||
return {k: self.get(k) for k in self.properties}
|
||||
def get_properties(self):
|
||||
# res = POINTER(SAFEARRAY)()
|
||||
res = POINTER(windows.com.SafeArray)()
|
||||
x = ctypes.pointer(res)
|
||||
self.GetNames(None, 0, None, cast(x, POINTER(POINTER(gdef.SAFEARRAY))))
|
||||
# need to free the safearray / unlock ?
|
||||
return res[0].to_list(BSTR)
|
||||
|
||||
properties = property(get_properties)
|
||||
|
||||
# Make WmiObject a mapping object
|
||||
keys = get_properties
|
||||
__getitem__ = get
|
||||
__setitem__ = put
|
||||
|
||||
def items(self):
|
||||
return [(k, self.get(k)) for k in self.properties]
|
||||
|
||||
def values(self): # Not sur anyone will use this but keep the dict interface
|
||||
return [x[1] for x in self.items()]
|
||||
|
||||
## Make it callable like any class :D
|
||||
__call__ = spawn_instance
|
||||
|
||||
def __repr__(self):
|
||||
if not self:
|
||||
return """<{0} (NULL)>""".format(type(self).__name__,)
|
||||
if self.genus == gdef.WBEM_GENUS_CLASS:
|
||||
return """<{0} class "{1}">""".format(type(self).__name__, self.get("__Class"))
|
||||
return """<{0} instance of "{1}">""".format(type(self).__name__, self.get("__Class"))
|
||||
@@ -80,14 +107,20 @@ class WmiEnumeration(gdef.IEnumWbemClassObject, WmiComInterface):
|
||||
# For now the count is hardcoded to 1
|
||||
obj = WmiObject()
|
||||
return_count = gdef.ULONG(0)
|
||||
self.Next(timeout, 1, obj, return_count)
|
||||
if not return_count or not obj:
|
||||
error = self.Next(timeout, 1, obj, return_count)
|
||||
if error == gdef.WBEM_S_TIMEDOUT:
|
||||
raise WindowsError(gdef.WBEM_S_TIMEDOUT, "Wmi timeout")
|
||||
elif error == WBEM_S_FALSE:
|
||||
return None
|
||||
return obj
|
||||
else:
|
||||
return obj
|
||||
|
||||
def __iter__(self):
|
||||
return self.iter_timeout(self.DEFAULT_TIMEOUT)
|
||||
|
||||
def iter_timeout(self, timeout=None):
|
||||
while True:
|
||||
obj = self.next()
|
||||
obj = self.next(timeout)
|
||||
if obj is None:
|
||||
return
|
||||
yield obj
|
||||
@@ -95,12 +128,45 @@ class WmiEnumeration(gdef.IEnumWbemClassObject, WmiComInterface):
|
||||
def all(self):
|
||||
return list(self) # SqlAlchemy like :)
|
||||
|
||||
class WmiLocator(IWbemLocator, WmiComInterface):
|
||||
|
||||
class WmiCallResult(gdef.IWbemCallResult, WmiComInterface):
|
||||
def __init__(self, result_type=None, namespace_name=None):
|
||||
self.result_type = result_type
|
||||
self.namespace_name = namespace_name
|
||||
|
||||
def get_call_status(self, timeout=gdef.WBEM_INFINITE):
|
||||
status = gdef.LONG()
|
||||
self.GetCallStatus(timeout, status)
|
||||
return WBEMSTATUS.mapper[status.value & 0xffffffff]
|
||||
|
||||
def get_result_object(self, timeout=gdef.WBEM_INFINITE):
|
||||
result = WmiObject()
|
||||
self.GetResultObject(timeout, result)
|
||||
return result
|
||||
|
||||
def get_result_string(self, timeout=gdef.WBEM_INFINITE):
|
||||
result = gdef.BSTR()
|
||||
self.GetResultString(timeout, result)
|
||||
return result
|
||||
|
||||
def get_result_service(self, timeout=gdef.WBEM_INFINITE):
|
||||
result = WmiNamespace()
|
||||
self.GetResultServices(timeout, result)
|
||||
return result
|
||||
|
||||
@property
|
||||
def result(self):
|
||||
if self.result_type is None:
|
||||
raise ValueError("Cannot call <result> with no result_type")
|
||||
return getattr(self, "get_result_" + self.result_type)()
|
||||
|
||||
|
||||
class WmiLocator(gdef.IWbemLocator, WmiComInterface):
|
||||
pass # Just for the WMI errcheck callback
|
||||
|
||||
|
||||
# !TEST CODE
|
||||
class WmiNamespace(IWbemServices, WmiComInterface):
|
||||
class WmiNamespace(gdef.IWbemServices, WmiComInterface):
|
||||
r"""An object to perform wmi request to ``a given namespace``"""
|
||||
|
||||
#CLSID_WbemAdministrativeLocator_IID = windows.com.IID.from_string('CB8555CC-9128-11D1-AD9B-00C04FD8FDFF')
|
||||
@@ -110,7 +176,7 @@ class WmiNamespace(IWbemServices, WmiComInterface):
|
||||
WBEM_FLAG_FORWARD_ONLY)
|
||||
|
||||
def __init__(self, namespace, *args, **kwargs):
|
||||
self.namespace = namespace
|
||||
self.name = namespace
|
||||
|
||||
@classmethod
|
||||
def connect(cls, namespace, user=None, password=None):
|
||||
@@ -122,9 +188,6 @@ class WmiNamespace(IWbemServices, WmiComInterface):
|
||||
locator.Release()
|
||||
return self
|
||||
|
||||
|
||||
### OLD IMPLEM
|
||||
|
||||
def query(self, query):
|
||||
"""TODO: doc"""
|
||||
return list(self.exec_query(query))
|
||||
@@ -141,6 +204,8 @@ class WmiNamespace(IWbemServices, WmiComInterface):
|
||||
execq("WQL", query, flags, ctx, enumerator)
|
||||
return enumerator
|
||||
|
||||
# Create friendly name for create_class_enum & create_instance_enum ?
|
||||
|
||||
def create_class_enum(self, superclass, flags=DEFAULT_ENUM_FLAGS, deep=True):
|
||||
flags |= gdef.WBEM_FLAG_DEEP if deep else gdef.WBEM_FLAG_SHALLOW
|
||||
|
||||
@@ -148,7 +213,7 @@ class WmiNamespace(IWbemServices, WmiComInterface):
|
||||
self.CreateClassEnum(superclass, flags, None, enumerator)
|
||||
return enumerator
|
||||
|
||||
# subclasses
|
||||
# subclasses ?
|
||||
|
||||
def create_instance_enum(self, filter, flags=DEFAULT_ENUM_FLAGS, deep=True):
|
||||
# ??? marche pas :(
|
||||
@@ -160,7 +225,6 @@ class WmiNamespace(IWbemServices, WmiComInterface):
|
||||
|
||||
select = create_instance_enum
|
||||
|
||||
# TEST 2
|
||||
def get_object(self, path):
|
||||
result = WmiObject()
|
||||
self.GetObject(path, gdef.WBEM_FLAG_RETURN_WBEM_COMPLETE, None, result, None)
|
||||
@@ -168,30 +232,37 @@ class WmiNamespace(IWbemServices, WmiComInterface):
|
||||
|
||||
def put_instance(self, instance):
|
||||
# TODO: change flag
|
||||
res = IWbemCallResult()
|
||||
self.service.PutInstance(instance, gdef.WBEM_FLAG_CREATE_ONLY, None, res)
|
||||
res = WmiCallResult(result_type="string")
|
||||
self.PutInstance(instance, gdef.WBEM_FLAG_CREATE_ONLY, None, res)
|
||||
return res
|
||||
|
||||
def exec_method(self, obj, method, inparam):
|
||||
result = IWbemCallResult()
|
||||
outparam = IWbemClassObject()
|
||||
if isinstance(obj, IWbemClassObject):
|
||||
def exec_method(self, obj, method, inparam, flags=0):
|
||||
if flags & gdef.WBEM_FLAG_RETURN_IMMEDIATELY:
|
||||
# semisynchronous call -> WmiCallResult
|
||||
result = WmiCallResult(result_type="object")
|
||||
outparam = None
|
||||
else:
|
||||
# Synchronous call -> WmiObject (outparam)
|
||||
result = None
|
||||
outparam = WmiObject()
|
||||
if isinstance(obj, gdef.IWbemClassObject):
|
||||
obj = obj.get("__Path")
|
||||
# Flags 0 -> synchronous call
|
||||
# No WmiCallResult result is directly in outparam
|
||||
self.ExecMethod(obj, method, 0, None, inparam, outparam, result)
|
||||
return outparam, result
|
||||
|
||||
return outparam or result
|
||||
|
||||
def __repr__(self):
|
||||
null = "" if self else " (NULL)"
|
||||
return """<{0} "{1}"{2}>""".format(type(self).__name__, self.namespace, null)
|
||||
return """<{0} "{1}"{2}>""".format(type(self).__name__, self.name, null)
|
||||
|
||||
class WmiManager(dict):
|
||||
"""The main WMI class exposed, used to list and access differents WMI namespace, can be used as a dict to access
|
||||
:class:`WmiRequester` by namespace
|
||||
:class:`WmiNamespace` by name
|
||||
|
||||
Example:
|
||||
>>> windows.system.wmi["root\\SecurityCenter2"]
|
||||
<WmiRequester namespace="root\\SecurityCenter2">
|
||||
<WmiNamespace "root\SecurityCenter2">
|
||||
"""
|
||||
DEFAULT_NAMESPACE = "root\\cimv2" #: The default namespace for :func:`select` & :func:`query`
|
||||
def __init__(self):
|
||||
@@ -214,7 +285,7 @@ class WmiManager(dict):
|
||||
return self.default_namespace.query
|
||||
|
||||
def get_subnamespaces(self, root="root"):
|
||||
return [x["Name"] for x in self[root].select("__NameSpace", ["Name"])]
|
||||
return [x["Name"] for x in self[root].select("__NameSpace")]
|
||||
|
||||
namespaces = property(get_subnamespaces)
|
||||
"""The list of available WMI namespaces"""
|
||||
|
||||
Reference in New Issue
Block a user