winpnp 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- winpnp/__init__.py +1 -0
- winpnp/_setupapi.py +208 -0
- winpnp/info/__init__.py +1 -0
- winpnp/info/_pnp_property_mapping.py +126 -0
- winpnp/info/device.py +171 -0
- winpnp/info/setup_class.py +110 -0
- winpnp/properties/__init__.py +1 -0
- winpnp/properties/decoding.py +137 -0
- winpnp/properties/keys/__init__.py +18 -0
- winpnp/properties/keys/bluetooth.py +88 -0
- winpnp/properties/keys/dev_query.py +11 -0
- winpnp/properties/keys/device.py +742 -0
- winpnp/properties/keys/device_class.py +144 -0
- winpnp/properties/keys/device_container.py +358 -0
- winpnp/properties/keys/device_interface.py +53 -0
- winpnp/properties/keys/device_interface_class.py +18 -0
- winpnp/properties/keys/driver_package.py +43 -0
- winpnp/properties/kinds.py +284 -0
- winpnp/properties/pnp_property.py +141 -0
- winpnp-0.0.1.dist-info/METADATA +45 -0
- winpnp-0.0.1.dist-info/RECORD +23 -0
- winpnp-0.0.1.dist-info/WHEEL +4 -0
- winpnp-0.0.1.dist-info/licenses/LICENSE +21 -0
winpnp/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import info, properties
|
winpnp/_setupapi.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from ctypes import (
|
|
2
|
+
POINTER,
|
|
3
|
+
Structure,
|
|
4
|
+
c_bool,
|
|
5
|
+
c_ubyte,
|
|
6
|
+
c_uint16,
|
|
7
|
+
c_uint32,
|
|
8
|
+
c_void_p,
|
|
9
|
+
c_wchar_p,
|
|
10
|
+
windll,
|
|
11
|
+
)
|
|
12
|
+
from enum import IntFlag
|
|
13
|
+
from uuid import UUID
|
|
14
|
+
|
|
15
|
+
INVALID_HANDLE_VALUE = -1
|
|
16
|
+
|
|
17
|
+
HDEVINFO = c_void_p
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DIGCF(IntFlag):
|
|
21
|
+
DEFAULT = 0x00000001
|
|
22
|
+
PRESENT = 0x00000002
|
|
23
|
+
ALLCLASSES = 0x00000004
|
|
24
|
+
PROFILE = 0x00000008
|
|
25
|
+
DEVICEINTERFACE = 0x00000010
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DICLASSPROP(IntFlag):
|
|
29
|
+
INSTALLER = 0x00000001
|
|
30
|
+
INTERFACE = 0x00000002
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class GUID(Structure):
|
|
34
|
+
_fields_ = (
|
|
35
|
+
("Data1", c_uint32),
|
|
36
|
+
("Data2", c_uint16),
|
|
37
|
+
("Data3", c_uint16),
|
|
38
|
+
("Data4", c_ubyte * 8),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def __str__(self) -> str:
|
|
42
|
+
return str(self.to_uuid())
|
|
43
|
+
|
|
44
|
+
def __repr__(self) -> str:
|
|
45
|
+
return f"{type(self).__name__}({{{str(self.to_uuid())}}})"
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_uuid(cls, guid: UUID) -> "GUID":
|
|
49
|
+
return cls.from_buffer_copy(guid.bytes_le)
|
|
50
|
+
|
|
51
|
+
def to_uuid(self) -> UUID:
|
|
52
|
+
return UUID(bytes_le=bytes(self))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SP_DEVINFO_DATA(Structure):
|
|
56
|
+
_fields_ = (
|
|
57
|
+
("cbSize", c_uint32),
|
|
58
|
+
("ClassGuid", GUID),
|
|
59
|
+
("DevInst", c_uint32),
|
|
60
|
+
("Reserved", c_void_p),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class DEVPROPKEY(Structure):
|
|
65
|
+
_fields_ = (
|
|
66
|
+
("fmtid", GUID),
|
|
67
|
+
("pid", c_uint32),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class DEVPROP_TYPEMOD(IntFlag):
|
|
72
|
+
NONE = 0x00000000
|
|
73
|
+
ARRAY = 0x00001000 # array of fixed-sized data elements
|
|
74
|
+
LIST = 0x00002000 # list of variable-sized data elements
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class DEVPROP_TYPE(IntFlag):
|
|
78
|
+
EMPTY = 0x00000000 # nothing, no property data
|
|
79
|
+
NULL = 0x00000001 # null property data
|
|
80
|
+
SBYTE = 0x00000002 # 8-bit signed int (SBYTE)
|
|
81
|
+
BYTE = 0x00000003 # 8-bit unsigned int (BYTE)
|
|
82
|
+
INT16 = 0x00000004 # 16-bit signed int (SHORT)
|
|
83
|
+
UINT16 = 0x00000005 # 16-bit unsigned int (USHORT)
|
|
84
|
+
INT32 = 0x00000006 # 32-bit signed int (LONG)
|
|
85
|
+
UINT32 = 0x00000007 # 32-bit unsigned int (ULONG)
|
|
86
|
+
INT64 = 0x00000008 # 64-bit signed int (LONG64)
|
|
87
|
+
UINT64 = 0x00000009 # 64-bit unsigned int (ULONG64)
|
|
88
|
+
FLOAT = 0x0000000A # 32-bit floating-point (FLOAT)
|
|
89
|
+
DOUBLE = 0x0000000B # 64-bit floating-point (DOUBLE)
|
|
90
|
+
DECIMAL = 0x0000000C # 128-bit data (DECIMAL)
|
|
91
|
+
GUID = 0x0000000D # 128-bit unique identifier (GUID)
|
|
92
|
+
CURRENCY = 0x0000000E # 64 bit signed int currency value (CURRENCY)
|
|
93
|
+
DATE = 0x0000000F # date (DATE)
|
|
94
|
+
FILETIME = 0x00000010 # file time (FILETIME)
|
|
95
|
+
BOOLEAN = 0x00000011 # 8-bit boolean (DEVPROP_BOOLEAN)
|
|
96
|
+
STRING = 0x00000012 # null-terminated string
|
|
97
|
+
STRING_LIST = STRING | DEVPROP_TYPEMOD.LIST # multi-sz string list
|
|
98
|
+
SECURITY_DESCRIPTOR = 0x00000013 # self-relative binary SECURITY_DESCRIPTOR
|
|
99
|
+
SECURITY_DESCRIPTOR_STRING = 0x00000014 # security descriptor string (SDDL format)
|
|
100
|
+
DEVPROPKEY = 0x00000015 # device property key (DEVPROPKEY)
|
|
101
|
+
DEVPROPTYPE = 0x00000016 # device property type (DEVPROPTYPE)
|
|
102
|
+
BINARY = BYTE | DEVPROP_TYPEMOD.ARRAY # custom binary data
|
|
103
|
+
ERROR = 0x00000017 # 32-bit Win32 system error code
|
|
104
|
+
NTSTATUS = 0x00000018 # 32-bit NTSTATUS code
|
|
105
|
+
STRING_INDIRECT = 0x00000019 # string resource (@[path\]<dllname>,-<strId>)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
SetupDiCreateDeviceInfoList = windll.setupapi.SetupDiCreateDeviceInfoList
|
|
109
|
+
SetupDiCreateDeviceInfoList.restype = HDEVINFO
|
|
110
|
+
SetupDiCreateDeviceInfoList.argtypes = (
|
|
111
|
+
POINTER(GUID), # [in, optional] ClassGuid
|
|
112
|
+
c_void_p, # [in, optional] hwndParent
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
SetupDiDestroyDeviceInfoList = windll.setupapi.SetupDiDestroyDeviceInfoList
|
|
116
|
+
SetupDiDestroyDeviceInfoList.restype = c_bool
|
|
117
|
+
SetupDiDestroyDeviceInfoList.argtypes = (HDEVINFO,) # [in] DeviceInfoSet
|
|
118
|
+
|
|
119
|
+
SetupDiGetClassDevsW = windll.setupapi.SetupDiGetClassDevsW
|
|
120
|
+
SetupDiGetClassDevsW.restype = HDEVINFO
|
|
121
|
+
SetupDiGetClassDevsW.argtypes = (
|
|
122
|
+
POINTER(GUID), # [in, optional] ClassGuid
|
|
123
|
+
c_wchar_p, # [in, optional] Enumerator
|
|
124
|
+
c_void_p, # [in, optional] hwndParent
|
|
125
|
+
c_uint32, # [in] Flags
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
SetupDiEnumDeviceInfo = windll.setupapi.SetupDiEnumDeviceInfo
|
|
129
|
+
SetupDiEnumDeviceInfo.restype = c_bool
|
|
130
|
+
SetupDiEnumDeviceInfo.argtypes = (
|
|
131
|
+
HDEVINFO, # [in] DeviceInfoSet
|
|
132
|
+
c_uint32, # [in] MemberIndex
|
|
133
|
+
POINTER(SP_DEVINFO_DATA), # [out] DeviceInfoData
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
SetupDiOpenDeviceInfoW = windll.setupapi.SetupDiOpenDeviceInfoW
|
|
137
|
+
SetupDiOpenDeviceInfoW.restype = c_bool
|
|
138
|
+
SetupDiOpenDeviceInfoW.argtypes = (
|
|
139
|
+
HDEVINFO, # [in] DeviceInfoSet
|
|
140
|
+
c_wchar_p, # [in] DeviceInstanceId
|
|
141
|
+
c_void_p, # [in, optional] hwndParent
|
|
142
|
+
c_uint32, # [in] OpenFlags
|
|
143
|
+
POINTER(SP_DEVINFO_DATA), # [out, optional] DeviceInfoData
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
SetupDiGetDevicePropertyW = windll.setupapi.SetupDiGetDevicePropertyW
|
|
147
|
+
SetupDiGetDevicePropertyW.restype = c_bool
|
|
148
|
+
SetupDiGetDevicePropertyW.argtypes = (
|
|
149
|
+
HDEVINFO, # [in] DeviceInfoSet
|
|
150
|
+
POINTER(SP_DEVINFO_DATA), # [in] DeviceInfoData
|
|
151
|
+
POINTER(DEVPROPKEY), # [in] PropertyKey
|
|
152
|
+
POINTER(c_uint32), # [out] PropertyType
|
|
153
|
+
POINTER(c_ubyte), # [out, optional] PropertyBuffer
|
|
154
|
+
c_uint32, # [in] PropertyBufferSize
|
|
155
|
+
POINTER(c_uint32), # [out, optional] RequiredSize
|
|
156
|
+
c_uint32, # [in] Flags
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
SetupDiGetDevicePropertyKeys = windll.setupapi.SetupDiGetDevicePropertyKeys
|
|
160
|
+
SetupDiGetDevicePropertyKeys.restype = c_bool
|
|
161
|
+
SetupDiGetDevicePropertyKeys.argtypes = (
|
|
162
|
+
HDEVINFO, # [in] DeviceInfoSet
|
|
163
|
+
POINTER(SP_DEVINFO_DATA), # [in] DeviceInfoData
|
|
164
|
+
POINTER(DEVPROPKEY), # [out, optional] PropertyKeyArray
|
|
165
|
+
c_uint32, # [in] PropertyKeyCount
|
|
166
|
+
POINTER(c_uint32), # [out, optional] RequiredPropertyKeyCount
|
|
167
|
+
c_uint32, # [in] Flags
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
SetupDiBuildClassInfoList = windll.setupapi.SetupDiBuildClassInfoList
|
|
171
|
+
SetupDiBuildClassInfoList.restype = c_bool
|
|
172
|
+
SetupDiBuildClassInfoList.argtypes = (
|
|
173
|
+
c_uint32, # [in] Flags
|
|
174
|
+
POINTER(GUID), # [out, optional] ClassGuidList
|
|
175
|
+
c_uint32, # [in] ClassGuidListSize
|
|
176
|
+
POINTER(c_uint32), # [out] RequiredSize
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
SetupDiClassGuidsFromNameW = windll.setupapi.SetupDiClassGuidsFromNameW
|
|
180
|
+
SetupDiClassGuidsFromNameW.restype = c_bool
|
|
181
|
+
SetupDiClassGuidsFromNameW.argtypes = (
|
|
182
|
+
c_wchar_p, # [in] ClassName,
|
|
183
|
+
POINTER(GUID), # [out] ClassGuidList,
|
|
184
|
+
c_uint32, # [in] ClassGuidListSize,
|
|
185
|
+
POINTER(c_uint32), # [out] RequiredSize
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
SetupDiGetClassPropertyW = windll.setupapi.SetupDiGetClassPropertyW
|
|
189
|
+
SetupDiGetClassPropertyW.restype = c_bool
|
|
190
|
+
SetupDiGetClassPropertyW.argtypes = (
|
|
191
|
+
POINTER(GUID), # [in] ClassGuid,
|
|
192
|
+
POINTER(DEVPROPKEY), # [in] PropertyKey,
|
|
193
|
+
POINTER(c_uint32), # [out] PropertyType,
|
|
194
|
+
POINTER(c_ubyte), # [out] PropertyBuffer,
|
|
195
|
+
c_uint32, # [in] PropertyBufferSize,
|
|
196
|
+
POINTER(c_uint32), # [out, optional] RequiredSize,
|
|
197
|
+
c_uint32, # [in] Flags
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
SetupDiGetClassPropertyKeys = windll.setupapi.SetupDiGetClassPropertyKeys
|
|
201
|
+
SetupDiGetClassPropertyKeys.restype = c_bool
|
|
202
|
+
SetupDiGetClassPropertyKeys.argtypes = (
|
|
203
|
+
POINTER(GUID), # [in] ClassGuid,
|
|
204
|
+
POINTER(DEVPROPKEY), # [out, optional] PropertyKeyArray,
|
|
205
|
+
c_uint32, # [in] PropertyKeyCount,
|
|
206
|
+
POINTER(c_uint32), # [out, optional] RequiredPropertyKeyCount,
|
|
207
|
+
c_uint32, # [in] Flags
|
|
208
|
+
)
|
winpnp/info/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import device, setup_class
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from ctypes import (
|
|
3
|
+
POINTER,
|
|
4
|
+
GetLastError,
|
|
5
|
+
WinError,
|
|
6
|
+
byref,
|
|
7
|
+
c_ubyte,
|
|
8
|
+
c_uint32,
|
|
9
|
+
cast,
|
|
10
|
+
create_string_buffer,
|
|
11
|
+
)
|
|
12
|
+
from typing import Any, Iterator, Protocol, TypeVar
|
|
13
|
+
|
|
14
|
+
from winerror import ERROR_INSUFFICIENT_BUFFER, ERROR_NOT_FOUND
|
|
15
|
+
|
|
16
|
+
from winpnp._setupapi import DEVPROPKEY, GUID
|
|
17
|
+
from winpnp.properties.pnp_property import PnpProperty, PnpPropertyKey, PnpPropertyType
|
|
18
|
+
|
|
19
|
+
T = TypeVar("T")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class QueryValueFunc(Protocol):
|
|
23
|
+
def __call__(
|
|
24
|
+
self,
|
|
25
|
+
PropertyKey,
|
|
26
|
+
PropertyType,
|
|
27
|
+
PropertyBuffer,
|
|
28
|
+
PropertyBufferSize,
|
|
29
|
+
RequiredSize,
|
|
30
|
+
) -> bool: ...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class QueryKeysFunc(Protocol):
|
|
34
|
+
def __call__(
|
|
35
|
+
self, PropertyKeyArray, PropertyKeyCount, RequiredPropertyKeyCount
|
|
36
|
+
) -> bool: ...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class PnpPropertyMapping(Mapping[PnpPropertyKey[T], PnpProperty[T]]):
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
description: str,
|
|
43
|
+
query_value_func: QueryValueFunc,
|
|
44
|
+
query_keys_func: QueryKeysFunc,
|
|
45
|
+
) -> None:
|
|
46
|
+
super().__init__()
|
|
47
|
+
|
|
48
|
+
self.description = description
|
|
49
|
+
self.query_value = query_value_func
|
|
50
|
+
self.query_keys = query_keys_func
|
|
51
|
+
|
|
52
|
+
def __getitem__(self, key: PnpPropertyKey[T]) -> PnpProperty[T]:
|
|
53
|
+
if not isinstance(key, PnpPropertyKey):
|
|
54
|
+
raise KeyError(key)
|
|
55
|
+
|
|
56
|
+
win_prop_key = DEVPROPKEY(GUID.from_uuid(key.category), key.property_id)
|
|
57
|
+
type_id = c_uint32()
|
|
58
|
+
required_size = c_uint32()
|
|
59
|
+
|
|
60
|
+
if self.query_value(
|
|
61
|
+
byref(win_prop_key),
|
|
62
|
+
byref(type_id),
|
|
63
|
+
None,
|
|
64
|
+
0,
|
|
65
|
+
byref(required_size),
|
|
66
|
+
):
|
|
67
|
+
return self.__build_property(key, PnpPropertyType(type_id.value), b"")
|
|
68
|
+
|
|
69
|
+
error = GetLastError()
|
|
70
|
+
if error == ERROR_NOT_FOUND:
|
|
71
|
+
raise KeyError(key)
|
|
72
|
+
if error != ERROR_INSUFFICIENT_BUFFER:
|
|
73
|
+
raise WinError(error)
|
|
74
|
+
|
|
75
|
+
value_buffer = create_string_buffer(required_size.value)
|
|
76
|
+
if not self.query_value(
|
|
77
|
+
byref(win_prop_key),
|
|
78
|
+
byref(type_id),
|
|
79
|
+
cast(value_buffer, POINTER(c_ubyte)),
|
|
80
|
+
required_size,
|
|
81
|
+
None,
|
|
82
|
+
):
|
|
83
|
+
raise WinError()
|
|
84
|
+
|
|
85
|
+
return self.__build_property(
|
|
86
|
+
key, PnpPropertyType(type_id.value), value_buffer.raw
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def __len__(self) -> int:
|
|
90
|
+
required_count = c_uint32()
|
|
91
|
+
if self.query_keys(None, 0, byref(required_count)):
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
error = GetLastError()
|
|
95
|
+
if error != ERROR_INSUFFICIENT_BUFFER:
|
|
96
|
+
raise WinError(error)
|
|
97
|
+
|
|
98
|
+
return required_count.value
|
|
99
|
+
|
|
100
|
+
def __iter__(self) -> Iterator[PnpPropertyKey[T]]:
|
|
101
|
+
keys = (DEVPROPKEY * len(self))()
|
|
102
|
+
if not self.query_keys(keys, len(keys), None):
|
|
103
|
+
raise WinError()
|
|
104
|
+
|
|
105
|
+
for key in keys:
|
|
106
|
+
yield PnpPropertyKey(key.fmtid.to_uuid(), key.pid)
|
|
107
|
+
|
|
108
|
+
def __build_property(
|
|
109
|
+
self,
|
|
110
|
+
key: PnpPropertyKey[T],
|
|
111
|
+
actual_type: PnpPropertyType[Any],
|
|
112
|
+
data: bytes,
|
|
113
|
+
) -> PnpProperty[T]:
|
|
114
|
+
if (
|
|
115
|
+
key.allowed_types is not None
|
|
116
|
+
and actual_type.type_id not in key.allowed_types
|
|
117
|
+
):
|
|
118
|
+
raise ValueError(
|
|
119
|
+
f"Property {key} of {self.description} has type {actual_type}, which is not expected."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if key.allowed_types is not None:
|
|
123
|
+
# Prefer key from allowed_types over original actual_type in case the one from allowed_types has a custom decoder
|
|
124
|
+
actual_type = key.allowed_types[actual_type.type_id]
|
|
125
|
+
|
|
126
|
+
return PnpProperty(actual_type.decode(data), actual_type)
|
winpnp/info/device.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from ctypes import GetLastError, WinError, byref, create_unicode_buffer, sizeof
|
|
4
|
+
from typing import Any, Iterable, Iterator, TypeVar
|
|
5
|
+
|
|
6
|
+
from winerror import ERROR_NO_MORE_ITEMS
|
|
7
|
+
|
|
8
|
+
from winpnp._setupapi import (
|
|
9
|
+
DIGCF,
|
|
10
|
+
HDEVINFO,
|
|
11
|
+
INVALID_HANDLE_VALUE,
|
|
12
|
+
SP_DEVINFO_DATA,
|
|
13
|
+
SetupDiCreateDeviceInfoList,
|
|
14
|
+
SetupDiDestroyDeviceInfoList,
|
|
15
|
+
SetupDiEnumDeviceInfo,
|
|
16
|
+
SetupDiGetClassDevsW,
|
|
17
|
+
SetupDiGetDevicePropertyKeys,
|
|
18
|
+
SetupDiGetDevicePropertyW,
|
|
19
|
+
SetupDiOpenDeviceInfoW,
|
|
20
|
+
)
|
|
21
|
+
from winpnp.properties import keys
|
|
22
|
+
from winpnp.properties.pnp_property import PnpProperty, PnpPropertyKey
|
|
23
|
+
|
|
24
|
+
from ._pnp_property_mapping import PnpPropertyMapping
|
|
25
|
+
|
|
26
|
+
T = TypeVar("T")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DeviceInfo(Mapping[PnpPropertyKey[Any], PnpProperty[Any]]):
|
|
30
|
+
"""
|
|
31
|
+
Represents information about a single PnP device node.
|
|
32
|
+
|
|
33
|
+
This class should not be instantiated directly. Instead, use one of the factory methods: `of_instance_id`, `of_all_devices`, `of_present_devices`:
|
|
34
|
+
|
|
35
|
+
Can be used as a `Mapping`, with `PnpPropertyKey`s as keys and `PnpProperty` as values.
|
|
36
|
+
|
|
37
|
+
For example:
|
|
38
|
+
>>> from winpnp.properties.keys.device import INSTANCE_ID
|
|
39
|
+
...
|
|
40
|
+
>>> with DeviceInfo.of_instance_id("HTREE\\ROOT\\0") as device:
|
|
41
|
+
... instance_id = device[INSTANCE_ID]
|
|
42
|
+
...
|
|
43
|
+
>>> instance_id
|
|
44
|
+
PnpProperty(value='HTREE\\ROOT\\0', kind=PnpPropertyType(type_id=18, name='STRING'))
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
__init_token = object()
|
|
48
|
+
|
|
49
|
+
def __init__(self, handle: HDEVINFO, data: SP_DEVINFO_DATA, *, _token=None) -> None:
|
|
50
|
+
"""
|
|
51
|
+
This function should not be called directly, instead use one of the factory methods: `of_instance_id`, `of_all_devices`, `of_present_devices`.
|
|
52
|
+
"""
|
|
53
|
+
if _token is not self.__init_token:
|
|
54
|
+
raise TypeError(
|
|
55
|
+
f"{type(self).__name__} should not be instantiated directly. You should use one of the factory methods: `of_instance_id`, `of_all_devices`, `of_present_devices`"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
super().__init__()
|
|
59
|
+
|
|
60
|
+
self._handle = handle
|
|
61
|
+
self._data = data
|
|
62
|
+
|
|
63
|
+
self._properties: PnpPropertyMapping[Any] = PnpPropertyMapping(
|
|
64
|
+
description="<uninitialized DeviceInfo>",
|
|
65
|
+
query_value_func=lambda PropertyKey, PropertyType, PropertyBuffer, PropertyBufferSize, RequiredSize: SetupDiGetDevicePropertyW(
|
|
66
|
+
self._handle,
|
|
67
|
+
byref(self._data),
|
|
68
|
+
PropertyKey,
|
|
69
|
+
PropertyType,
|
|
70
|
+
PropertyBuffer,
|
|
71
|
+
PropertyBufferSize,
|
|
72
|
+
RequiredSize,
|
|
73
|
+
0,
|
|
74
|
+
),
|
|
75
|
+
query_keys_func=lambda PropertyKeyArray, PropertyKeyCount, RequiredPropertyKeyCount: SetupDiGetDevicePropertyKeys(
|
|
76
|
+
self._handle,
|
|
77
|
+
byref(self._data),
|
|
78
|
+
PropertyKeyArray,
|
|
79
|
+
PropertyKeyCount,
|
|
80
|
+
RequiredPropertyKeyCount,
|
|
81
|
+
0,
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
self._instance_id: str = self._properties[keys.device.INSTANCE_ID].value
|
|
86
|
+
# Update description now that instance_id is known
|
|
87
|
+
self._properties.description = repr(self)
|
|
88
|
+
|
|
89
|
+
def __repr__(self) -> str:
|
|
90
|
+
return f"{type(self).__name__}(instance_id={repr(self._instance_id)})"
|
|
91
|
+
|
|
92
|
+
def __getitem__(self, key: PnpPropertyKey[T]) -> PnpProperty[T]:
|
|
93
|
+
return self._properties[key]
|
|
94
|
+
|
|
95
|
+
def __len__(self) -> int:
|
|
96
|
+
return len(self._properties)
|
|
97
|
+
|
|
98
|
+
def __iter__(self) -> Iterator[PnpPropertyKey[Any]]:
|
|
99
|
+
return iter(self._properties)
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def __enumerate_device_info_set(cls, handle: HDEVINFO) -> Iterator["DeviceInfo"]:
|
|
103
|
+
index = 0
|
|
104
|
+
data = SP_DEVINFO_DATA()
|
|
105
|
+
data.cbSize = sizeof(data)
|
|
106
|
+
|
|
107
|
+
while SetupDiEnumDeviceInfo(handle, index, byref(data)):
|
|
108
|
+
yield cls(
|
|
109
|
+
handle, SP_DEVINFO_DATA.from_buffer_copy(data), _token=cls.__init_token
|
|
110
|
+
)
|
|
111
|
+
index += 1
|
|
112
|
+
|
|
113
|
+
error = GetLastError()
|
|
114
|
+
if error != ERROR_NO_MORE_ITEMS:
|
|
115
|
+
raise WinError(error)
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
@contextmanager
|
|
119
|
+
def __create_using_SetupDiGetClassDevsW(
|
|
120
|
+
cls, flags: DIGCF
|
|
121
|
+
) -> Iterator[Iterable["DeviceInfo"]]:
|
|
122
|
+
handle: HDEVINFO = SetupDiGetClassDevsW(None, None, None, flags)
|
|
123
|
+
if handle == INVALID_HANDLE_VALUE:
|
|
124
|
+
raise WinError()
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
yield cls.__enumerate_device_info_set(handle)
|
|
128
|
+
finally:
|
|
129
|
+
SetupDiDestroyDeviceInfoList(handle)
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
@contextmanager
|
|
133
|
+
def of_instance_id(cls, instance_id: str) -> Iterator["DeviceInfo"]:
|
|
134
|
+
"""
|
|
135
|
+
Opens information of the device with the specified instance id.
|
|
136
|
+
"""
|
|
137
|
+
handle: HDEVINFO = SetupDiCreateDeviceInfoList(None, None)
|
|
138
|
+
if handle == INVALID_HANDLE_VALUE:
|
|
139
|
+
raise WinError()
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
data = SP_DEVINFO_DATA()
|
|
143
|
+
data.cbSize = sizeof(data)
|
|
144
|
+
if not SetupDiOpenDeviceInfoW(
|
|
145
|
+
handle, create_unicode_buffer(instance_id), None, 0, byref(data)
|
|
146
|
+
):
|
|
147
|
+
raise WinError()
|
|
148
|
+
|
|
149
|
+
yield cls(handle, data, _token=cls.__init_token)
|
|
150
|
+
finally:
|
|
151
|
+
SetupDiDestroyDeviceInfoList(handle)
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
@contextmanager
|
|
155
|
+
def of_all_devices(cls) -> Iterator[Iterable["DeviceInfo"]]:
|
|
156
|
+
"""
|
|
157
|
+
Opens information of all devices known to Windows, including ones that are no longer present.
|
|
158
|
+
"""
|
|
159
|
+
with cls.__create_using_SetupDiGetClassDevsW(DIGCF.ALLCLASSES) as devices:
|
|
160
|
+
yield devices
|
|
161
|
+
|
|
162
|
+
@classmethod
|
|
163
|
+
@contextmanager
|
|
164
|
+
def of_present_devices(cls) -> Iterator[Iterable["DeviceInfo"]]:
|
|
165
|
+
"""
|
|
166
|
+
Opens information of all devices that are currently present.
|
|
167
|
+
"""
|
|
168
|
+
with cls.__create_using_SetupDiGetClassDevsW(
|
|
169
|
+
DIGCF.ALLCLASSES | DIGCF.PRESENT
|
|
170
|
+
) as devices:
|
|
171
|
+
yield devices
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from ctypes import WinError, byref, c_uint32, create_unicode_buffer
|
|
3
|
+
from typing import Any, Iterator, TypeVar
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from winpnp._setupapi import (
|
|
7
|
+
DICLASSPROP,
|
|
8
|
+
GUID,
|
|
9
|
+
SetupDiBuildClassInfoList,
|
|
10
|
+
SetupDiClassGuidsFromNameW,
|
|
11
|
+
SetupDiGetClassPropertyKeys,
|
|
12
|
+
SetupDiGetClassPropertyW,
|
|
13
|
+
)
|
|
14
|
+
from winpnp.properties import keys
|
|
15
|
+
from winpnp.properties.pnp_property import PnpProperty, PnpPropertyKey
|
|
16
|
+
|
|
17
|
+
from ._pnp_property_mapping import PnpPropertyMapping
|
|
18
|
+
|
|
19
|
+
T = TypeVar("T")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SetupClassInfo(Mapping[PnpPropertyKey[Any], PnpProperty[Any]]):
|
|
23
|
+
"""
|
|
24
|
+
Represents information about a single PnP setup class.
|
|
25
|
+
|
|
26
|
+
Can be used as a `Mapping`, with `PnpPropertyKey`s as keys and `PnpProperty` as values.
|
|
27
|
+
|
|
28
|
+
For example:
|
|
29
|
+
>>> from uuid import UUID
|
|
30
|
+
>>> from winpnp.properties.keys.device_class import CLASS_NAME
|
|
31
|
+
...
|
|
32
|
+
>>> SetupClassInfo(UUID("36fc9e60-c465-11cf-8056-444553540000"))[CLASS_NAME]
|
|
33
|
+
PnpProperty(value='USB', kind=PnpPropertyType(type_id=18, name='STRING'))
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, guid: UUID) -> None:
|
|
37
|
+
super().__init__()
|
|
38
|
+
|
|
39
|
+
self._guid = guid
|
|
40
|
+
|
|
41
|
+
self._properties: PnpPropertyMapping[Any] = PnpPropertyMapping(
|
|
42
|
+
description="<uninitialized SetupClassInfo>",
|
|
43
|
+
query_value_func=lambda PropertyKey, PropertyType, PropertyBuffer, PropertyBufferSize, RequiredSize: SetupDiGetClassPropertyW(
|
|
44
|
+
byref(GUID.from_uuid(self._guid)),
|
|
45
|
+
PropertyKey,
|
|
46
|
+
PropertyType,
|
|
47
|
+
PropertyBuffer,
|
|
48
|
+
PropertyBufferSize,
|
|
49
|
+
RequiredSize,
|
|
50
|
+
DICLASSPROP.INSTALLER,
|
|
51
|
+
),
|
|
52
|
+
query_keys_func=lambda PropertyKeyArray, PropertyKeyCount, RequiredPropertyKeyCount: SetupDiGetClassPropertyKeys(
|
|
53
|
+
byref(GUID.from_uuid(self._guid)),
|
|
54
|
+
PropertyKeyArray,
|
|
55
|
+
PropertyKeyCount,
|
|
56
|
+
RequiredPropertyKeyCount,
|
|
57
|
+
DICLASSPROP.INSTALLER,
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
self._name: str = self._properties[keys.device_class.CLASS_NAME].value
|
|
62
|
+
# Update description now that instance_id is known
|
|
63
|
+
self._properties.description = repr(self)
|
|
64
|
+
|
|
65
|
+
def __repr__(self) -> str:
|
|
66
|
+
return (
|
|
67
|
+
f"{type(self).__name__}(guid={repr(self._guid)}, name={repr(self._name)})"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def __getitem__(self, key: PnpPropertyKey[T]) -> PnpProperty[T]:
|
|
71
|
+
return self._properties[key]
|
|
72
|
+
|
|
73
|
+
def __len__(self) -> int:
|
|
74
|
+
return len(self._properties)
|
|
75
|
+
|
|
76
|
+
def __iter__(self) -> Iterator[PnpPropertyKey]:
|
|
77
|
+
return iter(self._properties)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def guid(self) -> UUID:
|
|
81
|
+
return self._guid
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def of_class_name(cls, name: str) -> Iterator["SetupClassInfo"]:
|
|
85
|
+
name_buffer = create_unicode_buffer(name)
|
|
86
|
+
required_size = c_uint32()
|
|
87
|
+
if SetupDiClassGuidsFromNameW(name_buffer, None, 0, byref(required_size)):
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
guids = (GUID * required_size.value)()
|
|
91
|
+
if not SetupDiClassGuidsFromNameW(
|
|
92
|
+
name_buffer, guids, required_size, byref(required_size)
|
|
93
|
+
):
|
|
94
|
+
raise WinError()
|
|
95
|
+
|
|
96
|
+
for guid in guids:
|
|
97
|
+
yield cls(guid.to_uuid())
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def of_all_classes(cls) -> Iterator["SetupClassInfo"]:
|
|
101
|
+
required_size = c_uint32()
|
|
102
|
+
if SetupDiBuildClassInfoList(0, None, 0, byref(required_size)):
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
guids = (GUID * required_size.value)()
|
|
106
|
+
if not SetupDiBuildClassInfoList(0, guids, required_size, byref(required_size)):
|
|
107
|
+
raise WinError()
|
|
108
|
+
|
|
109
|
+
for guid in guids:
|
|
110
|
+
yield cls(guid.to_uuid())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import keys, kinds, pnp_property
|