15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import ctypes
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import re
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def ValidHandle(value, func, arguments):
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if value == 0:
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        raise ctypes.WinError()
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return value
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import serial
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from serial.win32 import ULONG_PTR, is_64bit
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import HANDLE
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import BOOL
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import HWND
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import DWORD
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import WORD
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import LONG
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import ULONG
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import LPCSTR
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import HKEY
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ctypes.wintypes import BYTE
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)NULL = 0
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)HDEVINFO = ctypes.c_void_p
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PCTSTR = ctypes.c_char_p
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PTSTR = ctypes.c_void_p
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)CHAR = ctypes.c_char
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)LPDWORD = PDWORD = ctypes.POINTER(DWORD)
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE)
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)LPBYTE = PBYTE = ctypes.c_void_p        # XXX avoids error about types
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)ACCESS_MASK = DWORD
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)REGSAM = ACCESS_MASK
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def byte_buffer(length):
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Get a buffer for a string"""
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return (BYTE*length)()
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def string(buffer):
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    s = []
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for c in buffer:
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if c == 0: break
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        s.append(chr(c & 0xff)) # "& 0xff": hack to convert signed to unsigned
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return ''.join(s)
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class GUID(ctypes.Structure):
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    _fields_ = [
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ('Data1', DWORD),
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ('Data2', WORD),
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ('Data3', WORD),
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ('Data4', BYTE*8),
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ]
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def __str__(self):
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return "{%08x-%04x-%04x-%s-%s}" % (
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.Data1,
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.Data2,
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.Data3,
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            ''.join(["%02x" % d for d in self.Data4[:2]]),
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            ''.join(["%02x" % d for d in self.Data4[2:]]),
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        )
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class SP_DEVINFO_DATA(ctypes.Structure):
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    _fields_ = [
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ('cbSize', DWORD),
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ('ClassGuid', GUID),
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ('DevInst', DWORD),
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ('Reserved', ULONG_PTR),
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ]
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def __str__(self):
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return "ClassGuid:%s DevInst:%s" % (self.ClassGuid, self.DevInst)
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)setupapi = ctypes.windll.LoadLibrary("setupapi")
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiDestroyDeviceInfoList.restype = BOOL
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameA
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD]
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiClassGuidsFromName.restype = BOOL
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiEnumDeviceInfo.restype = BOOL
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsA
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetClassDevs.restype = HDEVINFO
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetClassDevs.errcheck = ValidHandle
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyA
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetDeviceRegistryProperty.restype = BOOL
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdA
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD]
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiGetDeviceInstanceId.restype = BOOL
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM]
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SetupDiOpenDevRegKey.restype = HKEY
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)advapi32 = ctypes.windll.LoadLibrary("Advapi32")
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)RegCloseKey = advapi32.RegCloseKey
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)RegCloseKey.argtypes = [HKEY]
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)RegCloseKey.restype = LONG
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)RegQueryValueEx = advapi32.RegQueryValueExA
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)RegQueryValueEx.argtypes = [HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD]
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)RegQueryValueEx.restype = LONG
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DIGCF_PRESENT = 2
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DIGCF_DEVICEINTERFACE = 16
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)INVALID_HANDLE_VALUE = 0
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)ERROR_INSUFFICIENT_BUFFER = 122
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SPDRP_HARDWAREID = 1
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SPDRP_FRIENDLYNAME = 12
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DICS_FLAG_GLOBAL = 1
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DIREG_DEV = 0x00000001
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)KEY_READ = 0x20019
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# workaround for compatibility between Python 2.x and 3.x
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Ports = serial.to_bytes([80, 111, 114, 116, 115]) # "Ports"
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PortName = serial.to_bytes([80, 111, 114, 116, 78, 97, 109, 101]) # "PortName"
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def comports():
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    GUIDs = (GUID*8)() # so far only seen one used, so hope 8 are enough...
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    guids_size = DWORD()
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not SetupDiClassGuidsFromName(
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            Ports,
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            GUIDs,
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            ctypes.sizeof(GUIDs),
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            ctypes.byref(guids_size)):
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        raise ctypes.WinError()
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # repeat for all possible GUIDs
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for index in range(guids_size.value):
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        g_hdi = SetupDiGetClassDevs(
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                ctypes.byref(GUIDs[index]),
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                None,
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                NULL,
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        devinfo = SP_DEVINFO_DATA()
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        devinfo.cbSize = ctypes.sizeof(devinfo)
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        index = 0
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            index += 1
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # get the real com port name
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            hkey = SetupDiOpenDevRegKey(
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    g_hdi,
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.byref(devinfo),
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    DICS_FLAG_GLOBAL,
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    0,
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    DIREG_DEV,  # DIREG_DRV for SW info
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    KEY_READ)
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port_name_buffer = byte_buffer(250)
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port_name_length = ULONG(ctypes.sizeof(port_name_buffer))
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            RegQueryValueEx(
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    hkey,
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    PortName,
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    None,
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    None,
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.byref(port_name_buffer),
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.byref(port_name_length))
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            RegCloseKey(hkey)
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # unfortunately does this method also include parallel ports.
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # we could check for names starting with COM or just exclude LPT
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # and hope that other "unknown" names are serial ports...
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if string(port_name_buffer).startswith('LPT'):
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                continue
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # hardware ID
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            szHardwareID = byte_buffer(250)
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # try to get ID that includes serial number
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if not SetupDiGetDeviceInstanceId(
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    g_hdi,
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.byref(devinfo),
1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.byref(szHardwareID),
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.sizeof(szHardwareID) - 1,
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    None):
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                # fall back to more generic hardware ID if that would fail
1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if not SetupDiGetDeviceRegistryProperty(
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        g_hdi,
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        ctypes.byref(devinfo),
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        SPDRP_HARDWAREID,
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        None,
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        ctypes.byref(szHardwareID),
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        ctypes.sizeof(szHardwareID) - 1,
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        None):
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    # Ignore ERROR_INSUFFICIENT_BUFFER
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        raise ctypes.WinError()
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # stringify
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            szHardwareID_str = string(szHardwareID)
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # in case of USB, make a more readable string, similar to that form
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # that we also generate on other platforms
2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if szHardwareID_str.startswith('USB'):
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                m = re.search(r'VID_([0-9a-f]{4})&PID_([0-9a-f]{4})(\\(\w+))?', szHardwareID_str, re.I)
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if m:
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if m.group(4):
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        szHardwareID_str = 'USB VID:PID=%s:%s SNR=%s' % (m.group(1), m.group(2), m.group(4))
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    else:
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        szHardwareID_str = 'USB VID:PID=%s:%s' % (m.group(1), m.group(2))
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # friendly name
2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            szFriendlyName = byte_buffer(250)
2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if not SetupDiGetDeviceRegistryProperty(
2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    g_hdi,
2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.byref(devinfo),
2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    SPDRP_FRIENDLYNAME,
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    #~ SPDRP_DEVICEDESC,
2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    None,
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.byref(szFriendlyName),
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    ctypes.sizeof(szFriendlyName) - 1,
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    None):
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                # Ignore ERROR_INSUFFICIENT_BUFFER
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value))
2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                # ignore errors and still include the port in the list, friendly name will be same as port name
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                yield string(port_name_buffer), 'n/a', szHardwareID_str
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            else:
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                yield string(port_name_buffer), string(szFriendlyName), szHardwareID_str
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        SetupDiDestroyDeviceInfoList(g_hdi)
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# test
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)if __name__ == '__main__':
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    import serial
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for port, desc, hwid in sorted(comports()):
2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        print "%s: %s [%s]" % (port, desc, hwid)
241