1cef7893435aa41160dd1255c43cb8498279738ccChris Craikimport ctypes
2cef7893435aa41160dd1255c43cb8498279738ccChris Craikimport re
3cef7893435aa41160dd1255c43cb8498279738ccChris Craik
4cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef ValidHandle(value, func, arguments):
5cef7893435aa41160dd1255c43cb8498279738ccChris Craik    if value == 0:
6cef7893435aa41160dd1255c43cb8498279738ccChris Craik        raise ctypes.WinError()
7cef7893435aa41160dd1255c43cb8498279738ccChris Craik    return value
8cef7893435aa41160dd1255c43cb8498279738ccChris Craik
9cef7893435aa41160dd1255c43cb8498279738ccChris Craikimport serial
10cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom serial.win32 import ULONG_PTR, is_64bit
11cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import HANDLE
12cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import BOOL
13cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import HWND
14cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import DWORD
15cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import WORD
16cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import LONG
17cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import ULONG
18cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import LPCSTR
19cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import HKEY
20cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom ctypes.wintypes import BYTE
21cef7893435aa41160dd1255c43cb8498279738ccChris Craik
22cef7893435aa41160dd1255c43cb8498279738ccChris CraikNULL = 0
23cef7893435aa41160dd1255c43cb8498279738ccChris CraikHDEVINFO = ctypes.c_void_p
24cef7893435aa41160dd1255c43cb8498279738ccChris CraikPCTSTR = ctypes.c_char_p
25cef7893435aa41160dd1255c43cb8498279738ccChris CraikPTSTR = ctypes.c_void_p
26cef7893435aa41160dd1255c43cb8498279738ccChris CraikCHAR = ctypes.c_char
27cef7893435aa41160dd1255c43cb8498279738ccChris CraikLPDWORD = PDWORD = ctypes.POINTER(DWORD)
28cef7893435aa41160dd1255c43cb8498279738ccChris Craik#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE)
29cef7893435aa41160dd1255c43cb8498279738ccChris CraikLPBYTE = PBYTE = ctypes.c_void_p        # XXX avoids error about types
30cef7893435aa41160dd1255c43cb8498279738ccChris Craik
31cef7893435aa41160dd1255c43cb8498279738ccChris CraikACCESS_MASK = DWORD
32cef7893435aa41160dd1255c43cb8498279738ccChris CraikREGSAM = ACCESS_MASK
33cef7893435aa41160dd1255c43cb8498279738ccChris Craik
34cef7893435aa41160dd1255c43cb8498279738ccChris Craik
35cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef byte_buffer(length):
36cef7893435aa41160dd1255c43cb8498279738ccChris Craik    """Get a buffer for a string"""
37cef7893435aa41160dd1255c43cb8498279738ccChris Craik    return (BYTE*length)()
38cef7893435aa41160dd1255c43cb8498279738ccChris Craik
39cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef string(buffer):
40cef7893435aa41160dd1255c43cb8498279738ccChris Craik    s = []
41cef7893435aa41160dd1255c43cb8498279738ccChris Craik    for c in buffer:
42cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if c == 0: break
43cef7893435aa41160dd1255c43cb8498279738ccChris Craik        s.append(chr(c & 0xff)) # "& 0xff": hack to convert signed to unsigned
44cef7893435aa41160dd1255c43cb8498279738ccChris Craik    return ''.join(s)
45cef7893435aa41160dd1255c43cb8498279738ccChris Craik
46cef7893435aa41160dd1255c43cb8498279738ccChris Craik
47cef7893435aa41160dd1255c43cb8498279738ccChris Craikclass GUID(ctypes.Structure):
48cef7893435aa41160dd1255c43cb8498279738ccChris Craik    _fields_ = [
49cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ('Data1', DWORD),
50cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ('Data2', WORD),
51cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ('Data3', WORD),
52cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ('Data4', BYTE*8),
53cef7893435aa41160dd1255c43cb8498279738ccChris Craik    ]
54cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def __str__(self):
55cef7893435aa41160dd1255c43cb8498279738ccChris Craik        return "{%08x-%04x-%04x-%s-%s}" % (
56cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.Data1,
57cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.Data2,
58cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.Data3,
59cef7893435aa41160dd1255c43cb8498279738ccChris Craik            ''.join(["%02x" % d for d in self.Data4[:2]]),
60cef7893435aa41160dd1255c43cb8498279738ccChris Craik            ''.join(["%02x" % d for d in self.Data4[2:]]),
61cef7893435aa41160dd1255c43cb8498279738ccChris Craik        )
62cef7893435aa41160dd1255c43cb8498279738ccChris Craik
63cef7893435aa41160dd1255c43cb8498279738ccChris Craikclass SP_DEVINFO_DATA(ctypes.Structure):
64cef7893435aa41160dd1255c43cb8498279738ccChris Craik    _fields_ = [
65cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ('cbSize', DWORD),
66cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ('ClassGuid', GUID),
67cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ('DevInst', DWORD),
68cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ('Reserved', ULONG_PTR),
69cef7893435aa41160dd1255c43cb8498279738ccChris Craik    ]
70cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def __str__(self):
71cef7893435aa41160dd1255c43cb8498279738ccChris Craik        return "ClassGuid:%s DevInst:%s" % (self.ClassGuid, self.DevInst)
72cef7893435aa41160dd1255c43cb8498279738ccChris CraikPSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
73cef7893435aa41160dd1255c43cb8498279738ccChris Craik
74cef7893435aa41160dd1255c43cb8498279738ccChris CraikPSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
75cef7893435aa41160dd1255c43cb8498279738ccChris Craik
76cef7893435aa41160dd1255c43cb8498279738ccChris Craiksetupapi = ctypes.windll.LoadLibrary("setupapi")
77cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
78cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
79cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiDestroyDeviceInfoList.restype = BOOL
80cef7893435aa41160dd1255c43cb8498279738ccChris Craik
81cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameA
82cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD]
83cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiClassGuidsFromName.restype = BOOL
84cef7893435aa41160dd1255c43cb8498279738ccChris Craik
85cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
86cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
87cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiEnumDeviceInfo.restype = BOOL
88cef7893435aa41160dd1255c43cb8498279738ccChris Craik
89cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetClassDevs = setupapi.SetupDiGetClassDevsA
90cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
91cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetClassDevs.restype = HDEVINFO
92cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetClassDevs.errcheck = ValidHandle
93cef7893435aa41160dd1255c43cb8498279738ccChris Craik
94cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyA
95cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
96cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetDeviceRegistryProperty.restype = BOOL
97cef7893435aa41160dd1255c43cb8498279738ccChris Craik
98cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdA
99cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD]
100cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiGetDeviceInstanceId.restype = BOOL
101cef7893435aa41160dd1255c43cb8498279738ccChris Craik
102cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey
103cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM]
104cef7893435aa41160dd1255c43cb8498279738ccChris CraikSetupDiOpenDevRegKey.restype = HKEY
105cef7893435aa41160dd1255c43cb8498279738ccChris Craik
106cef7893435aa41160dd1255c43cb8498279738ccChris Craikadvapi32 = ctypes.windll.LoadLibrary("Advapi32")
107cef7893435aa41160dd1255c43cb8498279738ccChris CraikRegCloseKey = advapi32.RegCloseKey
108cef7893435aa41160dd1255c43cb8498279738ccChris CraikRegCloseKey.argtypes = [HKEY]
109cef7893435aa41160dd1255c43cb8498279738ccChris CraikRegCloseKey.restype = LONG
110cef7893435aa41160dd1255c43cb8498279738ccChris Craik
111cef7893435aa41160dd1255c43cb8498279738ccChris CraikRegQueryValueEx = advapi32.RegQueryValueExA
112cef7893435aa41160dd1255c43cb8498279738ccChris CraikRegQueryValueEx.argtypes = [HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD]
113cef7893435aa41160dd1255c43cb8498279738ccChris CraikRegQueryValueEx.restype = LONG
114cef7893435aa41160dd1255c43cb8498279738ccChris Craik
115cef7893435aa41160dd1255c43cb8498279738ccChris Craik
116cef7893435aa41160dd1255c43cb8498279738ccChris CraikDIGCF_PRESENT = 2
117cef7893435aa41160dd1255c43cb8498279738ccChris CraikDIGCF_DEVICEINTERFACE = 16
118cef7893435aa41160dd1255c43cb8498279738ccChris CraikINVALID_HANDLE_VALUE = 0
119cef7893435aa41160dd1255c43cb8498279738ccChris CraikERROR_INSUFFICIENT_BUFFER = 122
120cef7893435aa41160dd1255c43cb8498279738ccChris CraikSPDRP_HARDWAREID = 1
121cef7893435aa41160dd1255c43cb8498279738ccChris CraikSPDRP_FRIENDLYNAME = 12
122cef7893435aa41160dd1255c43cb8498279738ccChris CraikDICS_FLAG_GLOBAL = 1
123cef7893435aa41160dd1255c43cb8498279738ccChris CraikDIREG_DEV = 0x00000001
124cef7893435aa41160dd1255c43cb8498279738ccChris CraikKEY_READ = 0x20019
125cef7893435aa41160dd1255c43cb8498279738ccChris Craik
126cef7893435aa41160dd1255c43cb8498279738ccChris Craik# workaround for compatibility between Python 2.x and 3.x
127cef7893435aa41160dd1255c43cb8498279738ccChris CraikPorts = serial.to_bytes([80, 111, 114, 116, 115]) # "Ports"
128cef7893435aa41160dd1255c43cb8498279738ccChris CraikPortName = serial.to_bytes([80, 111, 114, 116, 78, 97, 109, 101]) # "PortName"
129cef7893435aa41160dd1255c43cb8498279738ccChris Craik
130cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef comports():
131cef7893435aa41160dd1255c43cb8498279738ccChris Craik    GUIDs = (GUID*8)() # so far only seen one used, so hope 8 are enough...
132cef7893435aa41160dd1255c43cb8498279738ccChris Craik    guids_size = DWORD()
133cef7893435aa41160dd1255c43cb8498279738ccChris Craik    if not SetupDiClassGuidsFromName(
134cef7893435aa41160dd1255c43cb8498279738ccChris Craik            Ports,
135cef7893435aa41160dd1255c43cb8498279738ccChris Craik            GUIDs,
136cef7893435aa41160dd1255c43cb8498279738ccChris Craik            ctypes.sizeof(GUIDs),
137cef7893435aa41160dd1255c43cb8498279738ccChris Craik            ctypes.byref(guids_size)):
138cef7893435aa41160dd1255c43cb8498279738ccChris Craik        raise ctypes.WinError()
139cef7893435aa41160dd1255c43cb8498279738ccChris Craik
140cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # repeat for all possible GUIDs
141cef7893435aa41160dd1255c43cb8498279738ccChris Craik    for index in range(guids_size.value):
142cef7893435aa41160dd1255c43cb8498279738ccChris Craik        g_hdi = SetupDiGetClassDevs(
143cef7893435aa41160dd1255c43cb8498279738ccChris Craik                ctypes.byref(GUIDs[index]),
144cef7893435aa41160dd1255c43cb8498279738ccChris Craik                None,
145cef7893435aa41160dd1255c43cb8498279738ccChris Craik                NULL,
146cef7893435aa41160dd1255c43cb8498279738ccChris Craik                DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
147cef7893435aa41160dd1255c43cb8498279738ccChris Craik
148cef7893435aa41160dd1255c43cb8498279738ccChris Craik        devinfo = SP_DEVINFO_DATA()
149cef7893435aa41160dd1255c43cb8498279738ccChris Craik        devinfo.cbSize = ctypes.sizeof(devinfo)
150cef7893435aa41160dd1255c43cb8498279738ccChris Craik        index = 0
151cef7893435aa41160dd1255c43cb8498279738ccChris Craik        while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
152cef7893435aa41160dd1255c43cb8498279738ccChris Craik            index += 1
153cef7893435aa41160dd1255c43cb8498279738ccChris Craik
154cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # get the real com port name
155cef7893435aa41160dd1255c43cb8498279738ccChris Craik            hkey = SetupDiOpenDevRegKey(
156cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    g_hdi,
157cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.byref(devinfo),
158cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    DICS_FLAG_GLOBAL,
159cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    0,
160cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    DIREG_DEV,  # DIREG_DRV for SW info
161cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    KEY_READ)
162cef7893435aa41160dd1255c43cb8498279738ccChris Craik            port_name_buffer = byte_buffer(250)
163cef7893435aa41160dd1255c43cb8498279738ccChris Craik            port_name_length = ULONG(ctypes.sizeof(port_name_buffer))
164cef7893435aa41160dd1255c43cb8498279738ccChris Craik            RegQueryValueEx(
165cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    hkey,
166cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    PortName,
167cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    None,
168cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    None,
169cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.byref(port_name_buffer),
170cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.byref(port_name_length))
171cef7893435aa41160dd1255c43cb8498279738ccChris Craik            RegCloseKey(hkey)
172cef7893435aa41160dd1255c43cb8498279738ccChris Craik
173cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # unfortunately does this method also include parallel ports.
174cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # we could check for names starting with COM or just exclude LPT
175cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # and hope that other "unknown" names are serial ports...
176cef7893435aa41160dd1255c43cb8498279738ccChris Craik            if string(port_name_buffer).startswith('LPT'):
177cef7893435aa41160dd1255c43cb8498279738ccChris Craik                continue
178cef7893435aa41160dd1255c43cb8498279738ccChris Craik
179cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # hardware ID
180cef7893435aa41160dd1255c43cb8498279738ccChris Craik            szHardwareID = byte_buffer(250)
181cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # try to get ID that includes serial number
182cef7893435aa41160dd1255c43cb8498279738ccChris Craik            if not SetupDiGetDeviceInstanceId(
183cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    g_hdi,
184cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.byref(devinfo),
185cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.byref(szHardwareID),
186cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.sizeof(szHardwareID) - 1,
187cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    None):
188cef7893435aa41160dd1255c43cb8498279738ccChris Craik                # fall back to more generic hardware ID if that would fail
189cef7893435aa41160dd1255c43cb8498279738ccChris Craik                if not SetupDiGetDeviceRegistryProperty(
190cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        g_hdi,
191cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        ctypes.byref(devinfo),
192cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        SPDRP_HARDWAREID,
193cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        None,
194cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        ctypes.byref(szHardwareID),
195cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        ctypes.sizeof(szHardwareID) - 1,
196cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        None):
197cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    # Ignore ERROR_INSUFFICIENT_BUFFER
198cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
199cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        raise ctypes.WinError()
200cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # stringify
201cef7893435aa41160dd1255c43cb8498279738ccChris Craik            szHardwareID_str = string(szHardwareID)
202cef7893435aa41160dd1255c43cb8498279738ccChris Craik
203cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # in case of USB, make a more readable string, similar to that form
204cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # that we also generate on other platforms
205cef7893435aa41160dd1255c43cb8498279738ccChris Craik            if szHardwareID_str.startswith('USB'):
206cef7893435aa41160dd1255c43cb8498279738ccChris Craik                m = re.search(r'VID_([0-9a-f]{4})&PID_([0-9a-f]{4})(\\(\w+))?', szHardwareID_str, re.I)
207cef7893435aa41160dd1255c43cb8498279738ccChris Craik                if m:
208cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    if m.group(4):
209cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        szHardwareID_str = 'USB VID:PID=%s:%s SNR=%s' % (m.group(1), m.group(2), m.group(4))
210cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    else:
211cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        szHardwareID_str = 'USB VID:PID=%s:%s' % (m.group(1), m.group(2))
212cef7893435aa41160dd1255c43cb8498279738ccChris Craik
213cef7893435aa41160dd1255c43cb8498279738ccChris Craik            # friendly name
214cef7893435aa41160dd1255c43cb8498279738ccChris Craik            szFriendlyName = byte_buffer(250)
215cef7893435aa41160dd1255c43cb8498279738ccChris Craik            if not SetupDiGetDeviceRegistryProperty(
216cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    g_hdi,
217cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.byref(devinfo),
218cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    SPDRP_FRIENDLYNAME,
219cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    #~ SPDRP_DEVICEDESC,
220cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    None,
221cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.byref(szFriendlyName),
222cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    ctypes.sizeof(szFriendlyName) - 1,
223cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    None):
224cef7893435aa41160dd1255c43cb8498279738ccChris Craik                # Ignore ERROR_INSUFFICIENT_BUFFER
225cef7893435aa41160dd1255c43cb8498279738ccChris Craik                #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
226cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value))
227cef7893435aa41160dd1255c43cb8498279738ccChris Craik                # ignore errors and still include the port in the list, friendly name will be same as port name
228cef7893435aa41160dd1255c43cb8498279738ccChris Craik                yield string(port_name_buffer), 'n/a', szHardwareID_str
229cef7893435aa41160dd1255c43cb8498279738ccChris Craik            else:
230cef7893435aa41160dd1255c43cb8498279738ccChris Craik                yield string(port_name_buffer), string(szFriendlyName), szHardwareID_str
231cef7893435aa41160dd1255c43cb8498279738ccChris Craik
232cef7893435aa41160dd1255c43cb8498279738ccChris Craik        SetupDiDestroyDeviceInfoList(g_hdi)
233cef7893435aa41160dd1255c43cb8498279738ccChris Craik
234cef7893435aa41160dd1255c43cb8498279738ccChris Craik# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
235cef7893435aa41160dd1255c43cb8498279738ccChris Craik# test
236cef7893435aa41160dd1255c43cb8498279738ccChris Craikif __name__ == '__main__':
237cef7893435aa41160dd1255c43cb8498279738ccChris Craik    import serial
238cef7893435aa41160dd1255c43cb8498279738ccChris Craik
239cef7893435aa41160dd1255c43cb8498279738ccChris Craik    for port, desc, hwid in sorted(comports()):
240cef7893435aa41160dd1255c43cb8498279738ccChris Craik        print "%s: %s [%s]" % (port, desc, hwid)
241