You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
7.5 KiB
222 lines
7.5 KiB
|
|
|
|
|
|
#include "qextserialenumerator.h" |
|
#include <QDebug> |
|
#include <QMetaType> |
|
|
|
#include <objbase.h> |
|
#include <initguid.h> |
|
//#include "qextserialport.h" |
|
#include <QRegExp> |
|
#ifdef Q_OS_WIN |
|
#ifndef _MSC_VER |
|
#include <windows.h> |
|
#include <dbt.h> |
|
#include <QtCore/qglobal.h> |
|
#endif |
|
#endif |
|
|
|
QextSerialEnumerator::QextSerialEnumerator( ) |
|
{ |
|
if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) ) |
|
qRegisterMetaType<QextPortInfo>("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<QextPortInfo> QextSerialEnumerator::getPorts() |
|
{ |
|
QList<QextPortInfo> ports; |
|
enumerateDevicesWin(GUID_DEVCLASS_PORTS, &ports); |
|
return ports; |
|
} |
|
|
|
void QextSerialEnumerator::enumerateDevicesWin( const GUID & guid, QList<QextPortInfo>* 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; |
|
} |
|
|
|
|