PDA

View Full Version : USB Win Events messages Detecting in QT



AlphaWolfXV
14th August 2008, 13:36
Hello all,
I have been searching through the QT Assistant and the forums here on events and processing them. I am working on an application where I need to detect when the VID/PID of the device is attached/detached to/from the system. From what I have discovered so far, it would appear as though I can create a class using a QAbstractEventDispatcher and an event filter function referencing a pointer to the received message. Which in the implementation would set the instance thread to 0, i.e. m_EvtDispatch = QAbstractEventDispatcher::instance(0);" and then set event filter, "m_EvtDispatch->setEventFilter(myClass::myEventFilter);"

For the function implementation, it would process the message, maybe as a MSG type (unsure). I have a sample I am working with now as described, and the myEvent loop activates, but the parameters don't seem to ever match DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE/or even DBT_DEVNODES_CHANGED, from the WM_DEVICECHANGE message.

I have searched through the QEvent and QActionEvent and multiple others looking for something close on searching for new devices, but I am kind of at a loss. Any help would be greatly appreciated.

Thanks,
Alphawolfxv

[EDIT 2008-Aug-14]: Should have noted system parameters, QT4.3.3, Win Vista.
[EDIT 2008-Aug-14]: Added code sample.

All these files are trying to do is to break when the WM_DEVICECHANGE is received, I can look for the Attach/Detach once this step is complete, I hope... :o

Header:

#ifndef TESTMSG1_H
#define TESTMSG1_H

#include <QtGui/QDialog>
#include <QAbstractEventDispatcher>
#include <QtDebug>
#include <windows.h>
#include <dbt.h>

class TestMsg : public QDialog
{
Q_OBJECT

public:
TestMsg(QWidget *parent = 0);
~TestMsg();

static bool myEvtFilter(void *message);

private:
QAbstractEventDispatcher * m_EvtDispatch;
};

#endif // TESTMSG1_H


Implementation:


#include "testmsg1.h"

bool TestMsg::myEvtFilter(void *message)
{
MSG *msg;
static int i = 0;

msg = (MSG*)message;
qDebug() << "Test that we are getting events...";
qDebug() << "message: " << msg->message << " wParam: " << msg->wParam
<< " lParam: " << msg->lParam << " handle: " << msg->hwnd;
if (msg->message == WM_DEVICECHANGE)
{
i++;
qDebug() << "Count:" << i << "wParam:" << msg->wParam << "lParam:" << msg-> lParam;
}

return false;
}


TestMsg::TestMsg(QWidget *parent)
: QDialog(parent)
{

m_EvtDispatch = QAbstractEventDispatcher::instance(0);
m_EvtDispatch->setEventFilter(TestMsg::myEvtFilter);
}

TestMsg::~TestMsg()
{

}

LaOnze2000
14th August 2008, 16:46
I have also tried to install an event filter for CD arrival notification and could not
get it to work. Here is what i did :

_________ MFrame.h_________________

#include <qt_windows.h>

class MFrame : public QWidget
{
Q_OBJECT
public:
MFrame();

protected :
bool winEvent(MSG *msg,long * result);

signals:
void CD_Removed(QChar tchar);
void CD_Arrived(QChar tchar);
};

________ MFrame.cpp ____________________

#include <QtCore>
#include <QtGui>
#include <QtDebug>
#include "mframe.h"
#include <dbt.h>

static char FirstDriveFromMask (ULONG unitmask)
{
char i;

for (i = 0; i < 26; ++i)
{
if (unitmask & 0x1)
break;
unitmask = unitmask >> 1;
}

return (i + 'A');
}

bool MFrame::winEvent(MSG * msg, long * result)
{
int msgType = msg->message;
if(msgType == WM_DEVICECHANGE)
{
qDebug() << "winEvent in MgFrame : WM_DEVICECHANGE" ;
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
switch(msg->wParam)
{
case DBT_DEVICEARRIVAL:
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
char ALET = FirstDriveFromMask(lpdbv->dbcv_unitmask);
emit CD_Arrived(QChar(ALET));
qDebug() << "CD_Arrived ";

}
}
break;

case DBT_DEVICEREMOVECOMPLETE:
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
char ALET = FirstDriveFromMask(lpdbv->dbcv_unitmask);
emit CD_Removed(QChar(ALET));
qDebug() << "CD_Removed";
}
}
break;
} // skip the rest

} // end of if msgType
return false; // let qt handle the rest
}

I found that It only works on the main window. This is why I emit CD_Arrived and
CD_Removed signals.
Hope this helps
Murielle

AlphaWolfXV
14th August 2008, 17:45
Murielle,
Thank you so very much. I am able to now at least detect when the change happens! I can filter through the rest of the necessary parameters for VID/PID now that I have a starting point! Thanks again for the great example!! :)

Cyrus

Mechaboy
16th August 2013, 14:33
It's been a while since the original post, but I encountered the same problem and here is how i solved it:

1. How to recognize if an USB drive is plugged in or out:

inclde the following headers: #include <windows.h> #include <Dbt.h> #include <SetupAPI.h>
register this function in your GUI class header filer under public: bool winEvent ( MSG * msg, long * result );
also define the char variable DriveLetter there: char DriveLetter;
in your GUI class cpp-file this function shoud be definde like this:
bool myGUI::winEvent(MSG *msg, long *result)
{
int msgType = msg->message;
if(msgType == WM_DEVICECHANGE)
{
switch(msg->wParam)
{
case DBT_DEVICEARRIVAL:
getDriveLetterFromMsg(msg);
getMoreDriveInfosFromLetter(DriveLetter);
break;

case DBT_DEVICEREMOVECOMPLETE:
getDriveLetterFromMsg(msg);
break;
}
}
return false;
}

2. What you now need are the functions getDriveLetterFromMsg(msg) and getMoreDriveInfosFromLetter(DriveLetter). Here they are:

char getDriveLetterFromMsg(MSG *msg)
{
char DriveLetter = 0;
PDEV_BROADCAST_HDR drive_hdr = (PDEV_BROADCAST_HDR)msg->lParam;

if (drive_hdr -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME volume_hdr = (PDEV_BROADCAST_VOLUME)drive_hdr;
PDEV_BROADCAST_DEVICEINTERFACE device_info = (PDEV_BROADCAST_DEVICEINTERFACE)drive_hdr;
char name = (char)device_info->dbcc_name;
DriveLetter = FirstDriveFromMask(volume_hdr->dbcv_unitmask);
}

return DriveLetter;
}
bool getMoreDriveInfosFromLetter(char driveletter, mydrive *drive){

//catch errors
DWORD ercode;

//create Path
std::wstring drivePath_str( L"X:\\" );
std::wstring drivePath_str_UNC( L"\\\\.\\X:" );
drivePath_str[0] = driveletter;
drivePath_str_UNC[4] = driveletter;
const wchar_t* drivePath_chr = drivePath_str.c_str();
const wchar_t* drivePath_chr_UNC = drivePath_str_UNC.c_str();

//get Drive Type
UINT DriveType = GetDriveType((LPCWSTR)drivePath_chr);
ercode = GetLastError();
if(ercode > 0){ tools::writeError(ercode); return false; }

//get Device Number
HANDLE DriveHandle = CreateFile((LPCTSTR)drivePath_chr_UNC,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE ,NULL, OPEN_EXISTING, 0, NULL);
STORAGE_DEVICE_NUMBER storageDeviceNumber;
DWORD BytesReturned = 0;
DeviceIoControl(DriveHandle,IOCTL_STORAGE_GET_DEVI CE_NUMBER,NULL, 0, &storageDeviceNumber, sizeof(storageDeviceNumber),&BytesReturned, NULL);
ercode = GetLastError();
if(ercode > 0){ tools::writeError(ercode); return false; }
unsigned long DeviceNumber = storageDeviceNumber.DeviceNumber;

//get more Infos
QString driveInfos = GetDrivesDevInstByDiskNumber(DeviceNumber,DriveTyp e);
if(driveInfos.isEmpty())
{
tools::writeError(QString("Can't get Infos of Drive %1").arg(driveletter));
return false;
}

//save Infos: All the Information gathered is stored in 'driveInfos'
//do something with driveInfos


//everything went ok
return true;

}

3. In addition you need the functions FirstDriveFromMask(volume_hdr->dbcv_unitmask) and GetDrivesDevInstByDiskNumber(long DiskNumber, UINT DriveType):

char FirstDriveFromMask( ULONG unitmask ){
char i;

for (i = 0; i < 26; ++i){
if (unitmask & 0x1)
break;
unitmask = unitmask >> 1;
}
return( i + 'A' );
}
QString GetDrivesDevInstByDiskNumber(long DiskNumber, UINT DriveType)
{
GUID* guid;
QString DriveInfos;

switch (DriveType)
{
case DRIVE_REMOVABLE:
case DRIVE_FIXED:
guid = (GUID*)(void*)&GUID_DEVINTERFACE_DISK;
break;
case DRIVE_CDROM:
guid = (GUID*)(void*)&GUID_DEVINTERFACE_CDROM;
break;
default:
return 0;
}

// Get device interface info set handle for all devices attached to system

HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
return 0;
}

SP_DEVICE_INTERFACE_DATA devInterfaceData = {0};
BOOL bRet = FALSE;
BYTE Buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD dwSize;

devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
spdid.cbSize = sizeof(spdid);
for(DWORD dwIndex = 0; TRUE; dwIndex++)
{
bRet = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, dwIndex, &devInterfaceData);
if (!bRet)
break;

SetupDiEnumInterfaceDevice(hDevInfo, NULL, guid, dwIndex, &spdid);
dwSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL);
if ( dwSize!=0 && dwSize<=sizeof(Buf) )
{
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!

ZeroMemory((PVOID)&spdd, sizeof(spdd));
spdd.cbSize = sizeof(spdd);

long res = SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, pspdidd, dwSize, &dwSize, &spdd);
if ( res )
{
HANDLE hDrive = CreateFile(pspdidd->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
if ( hDrive != INVALID_HANDLE_VALUE )
{
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
CloseHandle(hDrive);
if (res && (DiskNumber == (long)sdn.DeviceNumber))
{
DriveInfos = QString::fromWCharArray(pspdidd->DevicePath);
break;
}
}
}
}
}

SetupDiDestroyDeviceInfoList(hDevInfo);
return DriveInfos;

}