[Device] Implement apis to retrieve the list of devices under each class

This commit is contained in:
lucasg
2020-04-13 14:31:28 +02:00
committed by hakril
parent 0ee191c981
commit 445c68537c
5 changed files with 199 additions and 43 deletions
+4 -3
View File
@@ -4,10 +4,11 @@ from windows.winobject.device import DeviceManager
def main():
for device_class in DeviceManager.enumerate_active_class():
print("-[%s] Class %s" % (device_class.guid, device_class.name))
print("-Class %s %s [%s]" % (device_class.name, " "*(60 - min(60,len(device_class.name))), device_class.guid))
# for device in device_class.devices:
# print(" -Device : %s" % (device.name))
for device in device_class.devices:
if device.name != "N/A":
print(" -Device : %s" % (device.name))
# for resource in device.resources:
# print(' -%s : [0x%08x - 0x%08x] (0x%04x)' % (resource.type, resource.start, resource.end, resource.flags))
+18
View File
@@ -3166,3 +3166,21 @@ SetupDiClassNameFromGuidAParams = ((1, 'ClassGuid'),(1, 'ClassName'),(1, 'ClassN
SetupDiClassNameFromGuidWPrototype = WINFUNCTYPE(BOOL, POINTER(GUID), LPCWSTR, DWORD , POINTER(DWORD))
SetupDiClassNameFromGuidWParams = ((1, 'ClassGuid'),(1, 'ClassName'),(1, 'ClassNameSize'),(1, 'RequiredSize'))
SetupDiGetClassDevsAPrototype = WINFUNCTYPE(HANDLE, POINTER(GUID), LPCSTR, HANDLE , DWORD)
SetupDiGetClassDevsAParams = ((1, 'ClassGuid'), (1, 'Enumerator'), (1, 'hwndParent'), (1, 'Flags'),)
SetupDiGetClassDevsWPrototype = WINFUNCTYPE(HANDLE, POINTER(GUID), LPCWSTR, HANDLE , DWORD)
SetupDiGetClassDevsWParams = ((1, 'ClassGuid'), (1, 'Enumerator'), (1, 'hwndParent'), (1, 'Flags'),)
SetupDiDestroyDeviceInfoListPrototype = WINFUNCTYPE(BOOL, HANDLE)
SetupDiDestroyDeviceInfoListParams = ((1, 'DeviceInfoSet'),)
SetupDiEnumDeviceInfoPrototype = WINFUNCTYPE(BOOL, HANDLE, DWORD, PSP_DEVINFO_DATA)
SetupDiEnumDeviceInfoParams = ((1, 'DeviceInfoSet'),(1, 'MemberIndex'),(1, 'DeviceInfoData'),)
SetupDiGetDeviceRegistryPropertyAPrototype = WINFUNCTYPE(BOOL, HANDLE, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)
SetupDiGetDeviceRegistryPropertyAParams = ((1, 'DeviceInfoSet'),(1, 'DeviceInfoData'),(1, 'Property'),(1, 'PropertyRegDataType'),(1, 'PropertyBuffer'),(1, 'PropertyBufferSize'),(1, 'RequiredSize'),)
SetupDiGetDeviceRegistryPropertyWPrototype = WINFUNCTYPE(BOOL, HANDLE, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)
SetupDiGetDeviceRegistryPropertyWParams = ((1, 'DeviceInfoSet'),(1, 'DeviceInfoData'),(1, 'Property'),(1, 'PropertyRegDataType'),(1, 'PropertyBuffer'),(1, 'PropertyBufferSize'),(1, 'RequiredSize'),)
+9
View File
@@ -9828,3 +9828,12 @@ PWIN32_FIND_DATAW = POINTER(_WIN32_FIND_DATAW)
WIN32_FIND_DATAW = _WIN32_FIND_DATAW
LPWIN32_FIND_DATAW = POINTER(_WIN32_FIND_DATAW)
class _SP_DEVINFO_DATA(Structure):
_fields_ = [
("cbSize", DWORD),
("ClassGuid", GUID),
("DevInst", DWORD),
("Reserved1", ULONG_PTR),
]
PSP_DEVINFO_DATA = POINTER(_SP_DEVINFO_DATA)
SP_DEVINFO_DATA = _SP_DEVINFO_DATA
+69 -22
View File
@@ -1,19 +1,61 @@
import windows
from windows import winproxy
from windows.generated_def import windef
import windows.generated_def as gdef
SPDRP_DEVICEDESC = 0x0000000
SPDRP_FRIENDLYNAME = 0x000000C
class DeviceObject(object):
def __init__(self, dev_data, device_name):
self.dev_data = dev_data
self._name = device_name
@property
def name(self):
return self._name
@classmethod
def from_device_info(cls, h_devs, device_data):
# DO NOT STORE h_devs since it may be invalidated in the future
try:
device_name = winproxy.SetupDiGetDeviceRegistryPropertyW(h_devs, device_data, SPDRP_FRIENDLYNAME, None)
if device_name:
device_name = device_name.decode('utf-16-le').rstrip('\x00')
else:
device_name = winproxy.SetupDiGetDeviceRegistryPropertyW(h_devs, device_data, SPDRP_DEVICEDESC, None)
if device_name:
device_name = device_name.decode('utf-16-le').rstrip('\x00')
else:
device_name = "N/A"
except WindowsError as e:
if e.winerror == gdef.ERROR_INVALID_DATA:
return cls(device_data, "N/A")
raise
return cls(device_data, device_name)
class DeviceClass(object):
def __init__(self, guid):
# class GUID
self._guid = guid
# associated class name
self._name = None
# self._devices = None
# self._h_devs = None
# List of devices registered under the class
self._devices = None
@property
def name(self):
@@ -24,33 +66,38 @@ class DeviceClass(object):
@property
def guid(self):
return self._guid
return self._guid
# @property
# def devices(self):
# if not self._devices:
@property
def devices(self):
if not self._devices:
# with OpenDeviceClass(self):
# self._devices = list(do for do in self._get_devices())
try:
h_devs = winproxy.SetupDiGetClassDevsA(self.guid)
self._devices = list(do for do in self._get_devices(h_devs))
finally:
winproxy.SetupDiDestroyDeviceInfoList(h_devs)
# return self._devices
return self._devices
# def _get_devices(self):
def _get_devices(self, h_devs):
# if not self._h_devs:
# return
if not h_devs:
return
# device_index = 0
device_index = 0
# while True:
while True:
# device_data = winproxy.SetupDiEnumDeviceInfo(self._h_devs, device_index)
# if not device_data:
# return # stop iterating
try:
device_data = winproxy.SetupDiEnumDeviceInfo(h_devs, device_index)
except WindowsError as e:
if e.winerror == gdef.ERROR_NO_MORE_ITEMS:
return # stop iterating
raise
# device_index+=1
# yield DeviceObject.from_device_info(self._h_devs, device_data)
device_index+=1
yield DeviceObject.from_device_info(h_devs, device_data)
def _get_class_name(self):
@@ -61,7 +108,7 @@ class DeviceManager(object):
@staticmethod
def enumerate_active_class():
return list(DeviceManager.enumerate_class())
return list(DeviceManager.enumerate_class())
# return list(filter(lambda dc: len(dc.devices) > 0, DeviceManager.enumerate_class()))
@staticmethod
+99 -18
View File
@@ -3,14 +3,16 @@ from ctypes import wintypes
import windows.generated_def as gdef
from ..apiproxy import ApiProxy, NeededParameter, is_implemented
from ..error import succeed_on_zero, fail_on_zero
from ..error import succeed_on_zero, fail_on_zero, result_is_handle, no_error_check
MAX_CLASS_NAME_LEN = 32
MAX_DEV_LEN = 1000
class SetupApiProxy(ApiProxy):
APIDLL = "SetupApi"
default_error_check = staticmethod(succeed_on_zero)
default_error_check = staticmethod(fail_on_zero)
@SetupApiProxy()
@@ -31,7 +33,7 @@ def SetupDiClassNameFromGuidA(Guid):
raw_class_name = bytes(class_name)
return raw_class_name.decode("utf-8").rstrip("\x00")
@SetupApiProxy(error_check=fail_on_zero)
@SetupApiProxy()
def SetupDiClassNameFromGuidW(Guid):
"""
@@ -50,24 +52,103 @@ def SetupDiClassNameFromGuidW(Guid):
raw_class_name = bytes(class_name)
return raw_class_name.decode("utf-16-le").rstrip("\x00")
# @SetupApiProxy()
# def SetupDiGetClassDevsA(Guid):
# return SetupDiGetClassDevsA.ctypes_function()
@SetupApiProxy(error_check=result_is_handle)
def SetupDiGetClassDevsA(Guid, Enumerator = None, hwndParent = None, Flags=0):
"""
Given a class GUID, return a HANDLE to the device's information set or raise an Exception
"""
# @SetupApiProxy()
# def SetupDiEnumDeviceInfo(hDevInfo, NumDevice):
# return SetupDiEnumDeviceInfo.ctypes_function()
return SetupDiGetClassDevsA.ctypes_function(
ctypes.byref(Guid),
Enumerator,
hwndParent,
Flags
)
# @SetupApiProxy()
# def SetupDiGetDeviceRegistryPropertyA(hDevInfo, DevData, Property, PropertyType):
# return SetupDiGetDeviceRegistryPropertyA.ctypes_function()
@SetupApiProxy(error_check=result_is_handle)
def SetupDiGetClassDevsW(Guid, Enumerator = None, hwndParent = None, Flags=0):
"""
Given a class GUID, return a HANDLE to the device's information set or raise an Exception
"""
return SetupDiGetClassDevsW.ctypes_function(
ctypes.byref(Guid),
Enumerator,
hwndParent,
Flags
)
@SetupApiProxy()
def SetupDiEnumDeviceInfo(hDevInfo, MemberIndex):
"""
Given a device information set, return the info associated with the index
or raise ERROR_NO_MORE_ITEMS if there is none anymore.
"""
data = gdef.winstructs.SP_DEVINFO_DATA()
data.cbSize = ctypes.sizeof(gdef.winstructs.SP_DEVINFO_DATA)
success = SetupDiEnumDeviceInfo.ctypes_function(
hDevInfo,
MemberIndex,
ctypes.byref(data)
)
return data
@SetupApiProxy()
def SetupDiGetDeviceRegistryPropertyA(hDevInfo, DevData, Property, PropertyType, PropertySize = MAX_DEV_LEN*ctypes.sizeof(wintypes.CHAR)):
property_buffer = ctypes.create_string_buffer(PropertySize)
bytes_written = wintypes.DWORD(0)
success = SetupDiGetDeviceRegistryPropertyA.ctypes_function(
hDevInfo,
ctypes.byref(DevData),
Property,
PropertyType,
ctypes.cast(ctypes.byref(property_buffer), gdef.winstructs.PBYTE),
wintypes.DWORD(PropertySize),
ctypes.byref(bytes_written),
)
if not success:
return None
# Truncate read data
registry_data = bytes(property_buffer)
registry_data = registry_data[0:bytes_written.value]
return registry_data
# @SetupApiProxy()
# def SetupDiGetDeviceRegistryPropertyW(hDevInfo, DevData, Property, PropertyType):
# return SetupDiGetDeviceRegistryPropertyW.ctypes_function()
@SetupApiProxy()
def SetupDiGetDeviceRegistryPropertyW(hDevInfo, DevData, Property, PropertyType, PropertySize = MAX_DEV_LEN*ctypes.sizeof(wintypes.WCHAR)):
property_buffer = ctypes.create_unicode_buffer(PropertySize)
bytes_written = wintypes.DWORD(0)
success = SetupDiGetDeviceRegistryPropertyW.ctypes_function(
hDevInfo,
ctypes.byref(DevData),
Property,
PropertyType,
ctypes.cast(ctypes.byref(property_buffer), gdef.winstructs.PBYTE),
wintypes.DWORD(PropertySize),
ctypes.byref(bytes_written),
)
if not success:
return None
# Truncate read data
registry_data = bytes(property_buffer)
registry_data = registry_data[0:bytes_written.value]
return registry_data
# @SetupApiProxy()
# def SetupDiDestroyDeviceInfoList(hDevInfo):
# return SetupDiDestroyDeviceInfoList.ctypes_function(hDevInfo)
@SetupApiProxy()
def SetupDiDestroyDeviceInfoList(hDevInfo):
return SetupDiDestroyDeviceInfoList.ctypes_function(hDevInfo)