Bike-X  0.8
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
list_ports_windows.py
Go to the documentation of this file.
1 import ctypes
2 import re
3 
4 def ValidHandle(value, func, arguments):
5  if value == 0:
6  raise ctypes.WinError()
7  return value
8 
9 import serial
10 from serial.win32 import ULONG_PTR, is_64bit
11 from ctypes.wintypes import HANDLE
12 from ctypes.wintypes import BOOL
13 from ctypes.wintypes import HWND
14 from ctypes.wintypes import DWORD
15 from ctypes.wintypes import WORD
16 from ctypes.wintypes import LONG
17 from ctypes.wintypes import ULONG
18 from ctypes.wintypes import LPCSTR
19 from ctypes.wintypes import HKEY
20 from ctypes.wintypes import BYTE
21 
22 NULL = 0
23 HDEVINFO = ctypes.c_void_p
24 PCTSTR = ctypes.c_char_p
25 CHAR = ctypes.c_char
26 LPDWORD = PDWORD = ctypes.POINTER(DWORD)
27 #~ LPBYTE = PBYTE = ctypes.POINTER(BYTE)
28 LPBYTE = PBYTE = ctypes.c_void_p # XXX avoids error about types
29 PHKEY = ctypes.POINTER(HKEY)
30 
31 ACCESS_MASK = DWORD
32 REGSAM = ACCESS_MASK
33 
34 
35 def byte_buffer(length):
36  """Get a buffer for a string"""
37  return (BYTE*length)()
38 
39 def string(buffer):
40  s = []
41  for c in buffer:
42  if c == 0: break
43  s.append(chr(c & 0xff)) # "& 0xff": hack to convert signed to unsigned
44  return ''.join(s)
45 
46 
47 class GUID(ctypes.Structure):
48  _fields_ = [
49  ('Data1', DWORD),
50  ('Data2', WORD),
51  ('Data3', WORD),
52  ('Data4', BYTE*8),
53  ]
54  def __str__(self):
55  return "{%08x-%04x-%04x-%s-%s}" % (
56  self.Data1,
57  self.Data2,
58  self.Data3,
59  ''.join(["%02x" % d for d in self.Data4[:2]]),
60  ''.join(["%02x" % d for d in self.Data4[2:]]),
61  )
62 
63 class SP_DEVINFO_DATA(ctypes.Structure):
64  _fields_ = [
65  ('cbSize', DWORD),
66  ('ClassGuid', GUID),
67  ('DevInst', DWORD),
68  ('Reserved', ULONG_PTR),
69  ]
70  def __str__(self):
71  return "ClassGuid:%s DevInst:%s" % (self.ClassGuid, self.DevInst)
72 PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
73 
74 class SP_DEVICE_INTERFACE_DATA(ctypes.Structure):
75  _fields_ = [
76  ('cbSize', DWORD),
77  ('InterfaceClassGuid', GUID),
78  ('Flags', DWORD),
79  ('Reserved', ULONG_PTR),
80  ]
81  def __str__(self):
82  return "InterfaceClassGuid:%s Flags:%s" % (self.InterfaceClassGuid, self.Flags)
83 PSP_DEVICE_INTERFACE_DATA = ctypes.POINTER(SP_DEVICE_INTERFACE_DATA)
84 
85 PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
86 
87 setupapi = ctypes.windll.LoadLibrary("setupapi")
88 SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
89 SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
90 SetupDiDestroyDeviceInfoList.restype = BOOL
91 
92 SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsA
93 SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
94 SetupDiGetClassDevs.restype = HDEVINFO
95 SetupDiGetClassDevs.errcheck = ValidHandle
96 
97 SetupDiEnumDeviceInterfaces = setupapi.SetupDiEnumDeviceInterfaces
98 SetupDiEnumDeviceInterfaces.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, ctypes.POINTER(GUID), DWORD, PSP_DEVICE_INTERFACE_DATA]
99 SetupDiEnumDeviceInterfaces.restype = BOOL
100 
101 SetupDiGetDeviceInterfaceDetail = setupapi.SetupDiGetDeviceInterfaceDetailA
102 SetupDiGetDeviceInterfaceDetail.argtypes = [HDEVINFO, PSP_DEVICE_INTERFACE_DATA, PSP_DEVICE_INTERFACE_DETAIL_DATA, DWORD, PDWORD, PSP_DEVINFO_DATA]
103 SetupDiGetDeviceInterfaceDetail.restype = BOOL
104 
105 SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyA
106 SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
107 SetupDiGetDeviceRegistryProperty.restype = BOOL
108 
109 SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey
110 SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM]
111 SetupDiOpenDevRegKey.restype = HKEY
112 
113 advapi32 = ctypes.windll.LoadLibrary("Advapi32")
114 RegCloseKey = advapi32.RegCloseKey
115 RegCloseKey.argtypes = [HKEY]
116 RegCloseKey.restype = LONG
117 
118 RegQueryValueEx = advapi32.RegQueryValueExA
119 RegQueryValueEx.argtypes = [HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD]
120 RegQueryValueEx.restype = LONG
121 
122 
123 GUID_CLASS_COMPORT = GUID(0x86e0d1e0L, 0x8089, 0x11d0,
124  (BYTE*8)(0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73))
125 
126 DIGCF_PRESENT = 2
127 DIGCF_DEVICEINTERFACE = 16
128 INVALID_HANDLE_VALUE = 0
129 ERROR_INSUFFICIENT_BUFFER = 122
130 SPDRP_HARDWAREID = 1
131 SPDRP_FRIENDLYNAME = 12
132 ERROR_NO_MORE_ITEMS = 259
133 DICS_FLAG_GLOBAL = 1
134 DIREG_DEV = 0x00000001
135 KEY_READ = 0x20019
136 REG_SZ = 1
137 
138 # workaround for compatibility between Python 2.x and 3.x
139 PortName = serial.to_bytes([80, 111, 114, 116, 78, 97, 109, 101]) # "PortName"
140 
141 def comports():
142  """This generator scans the device registry for com ports and yields port, desc, hwid"""
143  g_hdi = SetupDiGetClassDevs(ctypes.byref(GUID_CLASS_COMPORT), None, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
144  #~ for i in range(256):
145  for dwIndex in range(256):
147  did.cbSize = ctypes.sizeof(did)
148 
149  if not SetupDiEnumDeviceInterfaces(g_hdi, None, ctypes.byref(GUID_CLASS_COMPORT), dwIndex, ctypes.byref(did)):
150  if ctypes.GetLastError() != ERROR_NO_MORE_ITEMS:
151  raise ctypes.WinError()
152  break
153 
154  dwNeeded = DWORD()
155  # get the size
156  if not SetupDiGetDeviceInterfaceDetail(g_hdi, ctypes.byref(did), None, 0, ctypes.byref(dwNeeded), None):
157  # Ignore ERROR_INSUFFICIENT_BUFFER
158  if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
159  raise ctypes.WinError()
160  # allocate buffer
161  class SP_DEVICE_INTERFACE_DETAIL_DATA_A(ctypes.Structure):
162  _fields_ = [
163  ('cbSize', DWORD),
164  ('DevicePath', CHAR*(dwNeeded.value - ctypes.sizeof(DWORD))),
165  ]
166  def __str__(self):
167  return "DevicePath:%s" % (self.DevicePath,)
168  idd = SP_DEVICE_INTERFACE_DETAIL_DATA_A()
169  if is_64bit():
170  idd.cbSize = 8
171  else:
172  idd.cbSize = 5
173  devinfo = SP_DEVINFO_DATA()
174  devinfo.cbSize = ctypes.sizeof(devinfo)
175  if not SetupDiGetDeviceInterfaceDetail(g_hdi, ctypes.byref(did), ctypes.byref(idd), dwNeeded, None, ctypes.byref(devinfo)):
176  raise ctypes.WinError()
177 
178  # hardware ID
179  szHardwareID = byte_buffer(250)
180  if not SetupDiGetDeviceRegistryProperty(g_hdi, ctypes.byref(devinfo), SPDRP_HARDWAREID, None, ctypes.byref(szHardwareID), ctypes.sizeof(szHardwareID) - 1, None):
181  # Ignore ERROR_INSUFFICIENT_BUFFER
182  if GetLastError() != ERROR_INSUFFICIENT_BUFFER:
183  raise ctypes.WinError()
184 
185  # friendly name
186  szFriendlyName = byte_buffer(250)
187  if not SetupDiGetDeviceRegistryProperty(g_hdi, ctypes.byref(devinfo), SPDRP_FRIENDLYNAME, None, ctypes.byref(szFriendlyName), ctypes.sizeof(szFriendlyName) - 1, None):
188  # Ignore ERROR_INSUFFICIENT_BUFFER
189  if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
190  #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value))
191  port_name = None
192  else:
193  # the real com port name has to read differently...
194  hkey = SetupDiOpenDevRegKey(g_hdi, ctypes.byref(devinfo), DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ)
195  port_name_buffer = byte_buffer(250)
196  port_name_length = ULONG(ctypes.sizeof(port_name_buffer))
197  RegQueryValueEx(hkey, PortName, None, None, ctypes.byref(port_name_buffer), ctypes.byref(port_name_length))
198  RegCloseKey(hkey)
199  yield string(port_name_buffer), string(szFriendlyName), string(szHardwareID)
200 
202 
203 
204 # test
205 if __name__ == '__main__':
206  import serial
207 
208  for port, desc, hwid in sorted(comports()):
209  print "%s: %s [%s]" % (port, desc, hwid)