#include "qextserialenumerator.h" #include #include #include #include //#include "qextserialport.h" #include #ifdef Q_OS_WIN #ifndef _MSC_VER #include #include #include #endif #endif QextSerialEnumerator::QextSerialEnumerator( ) { if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) ) qRegisterMetaType("QextPortInfo"); #if (defined QT_GUI_LIB) notificationWidget = 0; #endif // Q_OS_WIN } QextSerialEnumerator::~QextSerialEnumerator( ) { #if (defined QT_GUI_LIB) if( notificationWidget ) delete notificationWidget; #endif } // see http://msdn.microsoft.com/en-us/library/ms791134.aspx for list of GUID classes #ifndef GUID_DEVCLASS_PORTS DEFINE_GUID(GUID_DEVCLASS_PORTS, 0x4D36E978, 0xE325, 0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 ); #endif /* Gordon Schumacher's macros for TCHAR -> QString conversions and vice versa */ #ifdef UNICODE #define QStringToTCHAR(x) (wchar_t*) x.utf16() #define PQStringToTCHAR(x) (wchar_t*) x->utf16() #define TCHARToQString(x) QString::fromUtf16((ushort*)(x)) #define TCHARToQStringN(x,y) QString::fromUtf16((ushort*)(x),(y)) #else #define QStringToTCHAR(x) x.local8Bit().constData() #define PQStringToTCHAR(x) x->local8Bit().constData() #define TCHARToQString(x) QString::fromLocal8Bit((x)) #define TCHARToQStringN(x,y) QString::fromLocal8Bit((x),(y)) #endif /*UNICODE*/ //static QString QextSerialEnumerator::getRegKeyValue(HKEY key, LPCTSTR property) { DWORD size = 0; DWORD type; RegQueryValueEx(key, property, NULL, NULL, NULL, & size); BYTE* buff = new BYTE[size]; QString result; if( RegQueryValueEx(key, property, NULL, &type, buff, & size) == ERROR_SUCCESS ) result = TCHARToQString(buff); RegCloseKey(key); delete [] buff; return result; } //static QString QextSerialEnumerator::getDeviceProperty(HDEVINFO devInfo, PSP_DEVINFO_DATA devData, DWORD property) { DWORD buffSize = 0; SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, NULL, 0, & buffSize); BYTE* buff = new BYTE[buffSize]; SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, buff, buffSize, NULL); QString result = TCHARToQString(buff); delete [] buff; return result; } QList QextSerialEnumerator::getPorts() { QList ports; enumerateDevicesWin(GUID_DEVCLASS_PORTS, &ports); return ports; } void QextSerialEnumerator::enumerateDevicesWin( const GUID & guid, QList* infoList ) { HDEVINFO devInfo; if( (devInfo = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT)) != INVALID_HANDLE_VALUE) { SP_DEVINFO_DATA devInfoData; devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for(int i = 0; SetupDiEnumDeviceInfo(devInfo, i, &devInfoData); i++) { QextPortInfo info; info.productID = info.vendorID = 0; getDeviceDetailsWin( &info, devInfo, &devInfoData ); infoList->append(info); } SetupDiDestroyDeviceInfoList(devInfo); } } #ifdef QT_GUI_LIB bool QextSerialRegistrationWidget::winEvent( MSG* message, long* result ) { if ( message->message == WM_DEVICECHANGE ) { qese->onDeviceChangeWin( message->wParam, message->lParam ); *result = 1; return true; } return false; } #endif void QextSerialEnumerator::setUpNotifications( ) { #ifdef QT_GUI_LIB if(notificationWidget) return; notificationWidget = new QextSerialRegistrationWidget(this); DEV_BROADCAST_DEVICEINTERFACE dbh; ZeroMemory(&dbh, sizeof(dbh)); dbh.dbcc_size = sizeof(dbh); dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; CopyMemory(&dbh.dbcc_classguid, &GUID_DEVCLASS_PORTS, sizeof(GUID)); if( RegisterDeviceNotification( notificationWidget->winId( ), &dbh, DEVICE_NOTIFY_WINDOW_HANDLE ) == NULL) qWarning() << "RegisterDeviceNotification failed:" << GetLastError(); // setting up notifications doesn't tell us about devices already connected // so get those manually foreach( QextPortInfo port, getPorts() ) emit deviceDiscovered( port ); #else qWarning("QextSerialEnumerator: GUI not enabled - can't register for device notifications."); #endif // QT_GUI_LIB } LRESULT QextSerialEnumerator::onDeviceChangeWin( WPARAM wParam, LPARAM lParam ) { if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) { PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam; if( pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE ) { PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; // delimiters are different across APIs...change to backslash. ugh. QString deviceID = TCHARToQString(pDevInf->dbcc_name).toUpper().replace("#", "\\"); matchAndDispatchChangedDevice(deviceID, GUID_DEVCLASS_PORTS, wParam); } } return 0; } bool QextSerialEnumerator::matchAndDispatchChangedDevice(const QString & deviceID, const GUID & guid, WPARAM wParam) { bool rv = false; DWORD dwFlag = (DBT_DEVICEARRIVAL == wParam) ? DIGCF_PRESENT : DIGCF_ALLCLASSES; HDEVINFO devInfo; if( (devInfo = SetupDiGetClassDevs(&guid,NULL,NULL,dwFlag)) != INVALID_HANDLE_VALUE ) { SP_DEVINFO_DATA spDevInfoData; spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for(int i=0; SetupDiEnumDeviceInfo(devInfo, i, &spDevInfoData); i++) { DWORD nSize=0 ; TCHAR buf[MAX_PATH]; if ( SetupDiGetDeviceInstanceId(devInfo, &spDevInfoData, buf, MAX_PATH, &nSize) && deviceID.contains(TCHARToQString(buf))) // we found a match { rv = true; QextPortInfo info; info.productID = info.vendorID = 0; getDeviceDetailsWin( &info, devInfo, &spDevInfoData, wParam ); if( wParam == DBT_DEVICEARRIVAL ) emit deviceDiscovered(info); else if( wParam == DBT_DEVICEREMOVECOMPLETE ) emit deviceRemoved(info); break; } } SetupDiDestroyDeviceInfoList(devInfo); } return rv; } bool QextSerialEnumerator::getDeviceDetailsWin( QextPortInfo* portInfo, HDEVINFO devInfo, PSP_DEVINFO_DATA devData, WPARAM wParam ) { portInfo->friendName = getDeviceProperty(devInfo, devData, SPDRP_FRIENDLYNAME); if( wParam == DBT_DEVICEARRIVAL) portInfo->physName = getDeviceProperty(devInfo, devData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME); portInfo->enumName = getDeviceProperty(devInfo, devData, SPDRP_ENUMERATOR_NAME); QString hardwareIDs = getDeviceProperty(devInfo, devData, SPDRP_HARDWAREID); HKEY devKey = SetupDiOpenDevRegKey(devInfo, devData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); QRegExp rx("^COM(\\d+)"); QString fullName(getRegKeyValue(devKey, TEXT("PortName"))); if(fullName.contains(rx)) { int portnum = rx.cap(1).toInt(); if(portnum > 9) // COM ports greater than 9 need \\.\ prepended fullName.prepend("\\\\.\\"); } portInfo->portName = fullName; QRegExp idRx("VID_(\\w+)&PID_(\\w+)"); if( hardwareIDs.toUpper().contains(idRx) ) { bool dummy; portInfo->vendorID = idRx.cap(1).toInt(&dummy, 16); portInfo->productID = idRx.cap(2).toInt(&dummy, 16); //qDebug() << "got vid:" << vid << "pid:" << pid; } return true; }