| import ctypes |
| import re |
| |
| def ValidHandle(value): |
| if value == 0: |
| raise ctypes.WinError() |
| return value |
| |
| NULL = 0 |
| HDEVINFO = ctypes.c_int |
| BOOL = ctypes.c_int |
| CHAR = ctypes.c_char |
| PCTSTR = ctypes.c_char_p |
| HWND = ctypes.c_uint |
| DWORD = ctypes.c_ulong |
| PDWORD = ctypes.POINTER(DWORD) |
| ULONG = ctypes.c_ulong |
| ULONG_PTR = ctypes.POINTER(ULONG) |
| #~ PBYTE = ctypes.c_char_p |
| PBYTE = ctypes.c_void_p |
| |
| class GUID(ctypes.Structure): |
| _fields_ = [ |
| ('Data1', ctypes.c_ulong), |
| ('Data2', ctypes.c_ushort), |
| ('Data3', ctypes.c_ushort), |
| ('Data4', ctypes.c_ubyte*8), |
| ] |
| def __str__(self): |
| return "{%08x-%04x-%04x-%s-%s}" % ( |
| self.Data1, |
| self.Data2, |
| self.Data3, |
| ''.join(["%02x" % d for d in self.Data4[:2]]), |
| ''.join(["%02x" % d for d in self.Data4[2:]]), |
| ) |
| |
| class SP_DEVINFO_DATA(ctypes.Structure): |
| _fields_ = [ |
| ('cbSize', DWORD), |
| ('ClassGuid', GUID), |
| ('DevInst', DWORD), |
| ('Reserved', ULONG_PTR), |
| ] |
| def __str__(self): |
| return "ClassGuid:%s DevInst:%s" % (self.ClassGuid, self.DevInst) |
| PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA) |
| |
| class SP_DEVICE_INTERFACE_DATA(ctypes.Structure): |
| _fields_ = [ |
| ('cbSize', DWORD), |
| ('InterfaceClassGuid', GUID), |
| ('Flags', DWORD), |
| ('Reserved', ULONG_PTR), |
| ] |
| def __str__(self): |
| return "InterfaceClassGuid:%s Flags:%s" % (self.InterfaceClassGuid, self.Flags) |
| |
| PSP_DEVICE_INTERFACE_DATA = ctypes.POINTER(SP_DEVICE_INTERFACE_DATA) |
| |
| PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p |
| |
| class dummy(ctypes.Structure): |
| _fields_=[("d1", DWORD), ("d2", CHAR)] |
| _pack_ = 1 |
| SIZEOF_SP_DEVICE_INTERFACE_DETAIL_DATA_A = ctypes.sizeof(dummy) |
| |
| SetupDiDestroyDeviceInfoList = ctypes.windll.setupapi.SetupDiDestroyDeviceInfoList |
| SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO] |
| SetupDiDestroyDeviceInfoList.restype = BOOL |
| |
| SetupDiGetClassDevs = ctypes.windll.setupapi.SetupDiGetClassDevsA |
| SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD] |
| SetupDiGetClassDevs.restype = ValidHandle # HDEVINFO |
| |
| SetupDiEnumDeviceInterfaces = ctypes.windll.setupapi.SetupDiEnumDeviceInterfaces |
| SetupDiEnumDeviceInterfaces.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, ctypes.POINTER(GUID), DWORD, PSP_DEVICE_INTERFACE_DATA] |
| SetupDiEnumDeviceInterfaces.restype = BOOL |
| |
| SetupDiGetDeviceInterfaceDetail = ctypes.windll.setupapi.SetupDiGetDeviceInterfaceDetailA |
| SetupDiGetDeviceInterfaceDetail.argtypes = [HDEVINFO, PSP_DEVICE_INTERFACE_DATA, PSP_DEVICE_INTERFACE_DETAIL_DATA, DWORD, PDWORD, PSP_DEVINFO_DATA] |
| SetupDiGetDeviceInterfaceDetail.restype = BOOL |
| |
| SetupDiGetDeviceRegistryProperty = ctypes.windll.setupapi.SetupDiGetDeviceRegistryPropertyA |
| SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD] |
| SetupDiGetDeviceRegistryProperty.restype = BOOL |
| |
| |
| GUID_CLASS_COMPORT = GUID(0x86e0d1e0L, 0x8089, 0x11d0, |
| (ctypes.c_ubyte*8)(0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73)) |
| |
| DIGCF_PRESENT = 2 |
| DIGCF_DEVICEINTERFACE = 16 |
| INVALID_HANDLE_VALUE = 0 |
| ERROR_INSUFFICIENT_BUFFER = 122 |
| SPDRP_HARDWAREID = 1 |
| SPDRP_FRIENDLYNAME = 12 |
| SPDRP_LOCATION_INFORMATION = 13 |
| ERROR_NO_MORE_ITEMS = 259 |
| |
| def comports(available_only=True): |
| """This generator scans the device registry for com ports and yields |
| (order, port, desc, hwid). If available_only is true only return currently |
| existing ports. Order is a helper to get sorted lists. it can be ignored |
| otherwise.""" |
| flags = DIGCF_DEVICEINTERFACE |
| if available_only: |
| flags |= DIGCF_PRESENT |
| g_hdi = SetupDiGetClassDevs(ctypes.byref(GUID_CLASS_COMPORT), None, NULL, flags); |
| #~ for i in range(256): |
| for dwIndex in range(256): |
| did = SP_DEVICE_INTERFACE_DATA() |
| did.cbSize = ctypes.sizeof(did) |
| |
| if not SetupDiEnumDeviceInterfaces( |
| g_hdi, |
| None, |
| ctypes.byref(GUID_CLASS_COMPORT), |
| dwIndex, |
| ctypes.byref(did) |
| ): |
| if ctypes.GetLastError() != ERROR_NO_MORE_ITEMS: |
| raise ctypes.WinError() |
| break |
| |
| dwNeeded = DWORD() |
| # get the size |
| if not SetupDiGetDeviceInterfaceDetail( |
| g_hdi, |
| ctypes.byref(did), |
| None, 0, ctypes.byref(dwNeeded), |
| None |
| ): |
| # Ignore ERROR_INSUFFICIENT_BUFFER |
| if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: |
| raise ctypes.WinError() |
| # allocate buffer |
| class SP_DEVICE_INTERFACE_DETAIL_DATA_A(ctypes.Structure): |
| _fields_ = [ |
| ('cbSize', DWORD), |
| ('DevicePath', CHAR*(dwNeeded.value - ctypes.sizeof(DWORD))), |
| ] |
| def __str__(self): |
| return "DevicePath:%s" % (self.DevicePath,) |
| idd = SP_DEVICE_INTERFACE_DETAIL_DATA_A() |
| idd.cbSize = SIZEOF_SP_DEVICE_INTERFACE_DETAIL_DATA_A |
| devinfo = SP_DEVINFO_DATA() |
| devinfo.cbSize = ctypes.sizeof(devinfo) |
| if not SetupDiGetDeviceInterfaceDetail( |
| g_hdi, |
| ctypes.byref(did), |
| ctypes.byref(idd), dwNeeded, None, |
| ctypes.byref(devinfo) |
| ): |
| raise ctypes.WinError() |
| |
| # hardware ID |
| szHardwareID = ctypes.create_string_buffer(250) |
| if not SetupDiGetDeviceRegistryProperty( |
| g_hdi, |
| ctypes.byref(devinfo), |
| SPDRP_HARDWAREID, |
| None, |
| ctypes.byref(szHardwareID), ctypes.sizeof(szHardwareID) - 1, |
| None |
| ): |
| # Ignore ERROR_INSUFFICIENT_BUFFER |
| if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: |
| raise ctypes.WinError() |
| |
| # friendly name |
| szFriendlyName = ctypes.create_string_buffer(1024) |
| if not SetupDiGetDeviceRegistryProperty( |
| g_hdi, |
| ctypes.byref(devinfo), |
| SPDRP_FRIENDLYNAME, |
| None, |
| ctypes.byref(szFriendlyName), ctypes.sizeof(szFriendlyName) - 1, |
| None |
| ): |
| # Ignore ERROR_INSUFFICIENT_BUFFER |
| if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: |
| #~ raise ctypes.WinError() |
| # not getting friendly name for com0com devices, try something else |
| szFriendlyName = ctypes.create_string_buffer(1024) |
| if SetupDiGetDeviceRegistryProperty( |
| g_hdi, |
| ctypes.byref(devinfo), |
| SPDRP_LOCATION_INFORMATION, |
| None, |
| ctypes.byref(szFriendlyName), ctypes.sizeof(szFriendlyName) - 1, |
| None |
| ): |
| port_name = "\\\\.\\" + szFriendlyName.value |
| order = None |
| else: |
| port_name = szFriendlyName.value |
| order = None |
| else: |
| try: |
| m = re.search(r"\((.*?(\d+))\)", szFriendlyName.value) |
| #~ print szFriendlyName.value, m.groups() |
| port_name = m.group(1) |
| order = int(m.group(2)) |
| except AttributeError, msg: |
| port_name = szFriendlyName.value |
| order = None |
| yield order, port_name, szFriendlyName.value, szHardwareID.value |
| |
| SetupDiDestroyDeviceInfoList(g_hdi) |
| |
| |
| if __name__ == '__main__': |
| import serial |
| print "-"*78 |
| print "Serial ports" |
| print "-"*78 |
| for order, port, desc, hwid in sorted(comports()): |
| print "%-10s: %s (%s) ->" % (port, desc, hwid), |
| try: |
| serial.Serial(port) # test open |
| except serial.serialutil.SerialException: |
| print "can't be openend" |
| else: |
| print "Ready" |
| print |
| # list of all ports the system knows |
| print "-"*78 |
| print "All serial ports (registry)" |
| print "-"*78 |
| for order, port, desc, hwid in sorted(comports(False)): |
| print "%-10s: %s (%s)" % (port, desc, hwid) |