123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- /****************************************************************************
- ** Copyright (c) 2000-2003 Wayne Roth
- ** Copyright (c) 2004-2007 Stefan Sander
- ** Copyright (c) 2007 Michal Policht
- ** Copyright (c) 2008 Brandon Fosdick
- ** Copyright (c) 2009-2010 Liam Staskawicz
- ** Copyright (c) 2011 Debao Zhang
- ** All right reserved.
- ** Web: http://code.google.com/p/qextserialport/
- **
- ** Permission is hereby granted, free of charge, to any person obtaining
- ** a copy of this software and associated documentation files (the
- ** "Software"), to deal in the Software without restriction, including
- ** without limitation the rights to use, copy, modify, merge, publish,
- ** distribute, sublicense, and/or sell copies of the Software, and to
- ** permit persons to whom the Software is furnished to do so, subject to
- ** the following conditions:
- **
- ** The above copyright notice and this permission notice shall be
- ** included in all copies or substantial portions of the Software.
- **
- ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- **
- ****************************************************************************/
-
- #include "qextserialenumerator.h"
- #include "qextserialenumerator_p.h"
- #include <QtCore/QDebug>
- #include <QtCore/QMetaType>
- #include <QtCore/QRegExp>
- #include <objbase.h>
- #include <initguid.h>
- #include <setupapi.h>
- #include <dbt.h>
- #include "qextserialport.h"
-
- #ifdef QT_GUI_LIB
- #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- #include <QtGui/QWidget>
- class QextSerialRegistrationWidget : public QWidget
- #else
- #include <QtGui/QWindow>
- class QextSerialRegistrationWidget : public QWindow
- #endif
- {
- public:
- QextSerialRegistrationWidget(QextSerialEnumeratorPrivate* qese) {
- this->qese = qese;
- }
- ~QextSerialRegistrationWidget() {}
-
- protected:
-
- #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- bool winEvent( MSG* message, long* result ) {
- #else
- bool nativeEvent(const QByteArray & /*eventType*/, void *msg, long *result) {
- MSG *message = static_cast<MSG*>(msg);
- #endif
- if ( message->message == WM_DEVICECHANGE ) {
- qese->onDeviceChanged(message->wParam, message->lParam );
- *result = 1;
- return true;
- }
- return false;
- }
- private:
- QextSerialEnumeratorPrivate* qese;
- };
-
- #endif // QT_GUI_LIB
-
- void QextSerialEnumeratorPrivate::platformSpecificInit()
- {
- #ifdef QT_GUI_LIB
- notificationWidget = 0;
- #endif // QT_GUI_LIB
- }
-
- /*!
- default
- */
- void QextSerialEnumeratorPrivate::platformSpecificDestruct()
- {
- #ifdef QT_GUI_LIB
- if( notificationWidget )
- delete notificationWidget;
- #endif
- }
-
- // see http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426(v=vs.85).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((char*)(x))
- #define TCHARToQStringN(x,y) QString::fromLocal8Bit((char*)(x),(y))
- #endif /*UNICODE*/
-
- /*!
- \internal
- Get value of specified property from the registry.
- \a key handle to an open key.
- \a property property name.
-
- return property value.
- */
- static QString 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;
- }
-
- /*!
- \internal
- Get specific property from registry.
- \a devInfo pointer to the device information set that contains the interface
- and its underlying device. Returned by SetupDiGetClassDevs() function.
- \a devData pointer to an SP_DEVINFO_DATA structure that defines the device instance.
- this is returned by SetupDiGetDeviceInterfaceDetail() function.
- \a property registry property. One of defined SPDRP_* constants.
-
- return property string.
- */
- static QString 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;
- }
-
- /*!
- \internal
- */
- static bool getDeviceDetailsWin( QextPortInfo* portInfo, HDEVINFO devInfo, PSP_DEVINFO_DATA devData
- , WPARAM wParam = DBT_DEVICEARRIVAL)
- {
- 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_QUERY_VALUE);
- portInfo->portName = getRegKeyValue(devKey, TEXT("PortName"));
- QRegExp idRx(QLatin1String("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;
- }
-
- /*!
- \internal
- */
- static void 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);
- }
- }
-
-
- static bool lessThan(const QextPortInfo &s1, const QextPortInfo &s2)
- {
- if (s1.portName.startsWith(QLatin1String("COM"))
- && s2.portName.startsWith(QLatin1String("COM"))) {
- return s1.portName.mid(3).toInt()<s2.portName.mid(3).toInt();
- }
- return s1.portName < s2.portName;
- }
-
-
- /*!
- Get list of ports.
-
- return list of ports currently available in the system.
- */
- QList<QextPortInfo> QextSerialEnumeratorPrivate::getPorts_sys()
- {
- QList<QextPortInfo> ports;
- enumerateDevicesWin(GUID_DEVCLASS_PORTS, &ports);
- qSort(ports.begin(), ports.end(), lessThan);
- return ports;
- }
-
-
- /*
- Enable event-driven notifications of board discovery/removal.
- */
- bool QextSerialEnumeratorPrivate::setUpNotifications_sys(bool setup)
- {
- #ifndef QT_GUI_LIB
- Q_UNUSED(setup)
- QESP_WARNING("QextSerialEnumerator: GUI not enabled - can't register for device notifications.");
- return false;
- #else
- Q_Q(QextSerialEnumerator);
- if(setup && notificationWidget) //already setup
- return true;
- 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((HWND)notificationWidget->winId(), &dbh, DEVICE_NOTIFY_WINDOW_HANDLE ) == NULL) {
- QESP_WARNING() << "RegisterDeviceNotification failed:" << GetLastError();
- return false;
- }
- // setting up notifications doesn't tell us about devices already connected
- // so get those manually
- foreach(QextPortInfo port, getPorts_sys())
- Q_EMIT q->deviceDiscovered(port);
- return true;
- #endif // QT_GUI_LIB
- }
-
- LRESULT QextSerialEnumeratorPrivate::onDeviceChanged( 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(QLatin1String("#"), QLatin1String("\\"));
-
- matchAndDispatchChangedDevice(deviceID, GUID_DEVCLASS_PORTS, wParam);
- }
- }
- return 0;
- }
-
- bool QextSerialEnumeratorPrivate::matchAndDispatchChangedDevice(const QString & deviceID, const GUID & guid, WPARAM wParam)
- {
- Q_Q(QextSerialEnumerator);
- 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 )
- Q_EMIT q->deviceDiscovered(info);
- else if( wParam == DBT_DEVICEREMOVECOMPLETE )
- Q_EMIT q->deviceRemoved(info);
- break;
- }
- }
- SetupDiDestroyDeviceInfoList(devInfo);
- }
- return rv;
- }
|