[vbox-dev] code patch to auto detect USB insertion

Huihong Luo huisinro at yahoo.com
Thu Sep 15 16:01:03 GMT 2011


since there are other changes, so I post here manual changes, instead of a pacth file.
 
The patch is attached as a text file, also shown as below:
 
UIMachineWindow.h:
public:
 /* usb support, monitor usb device insertion */
#ifdef Q_WS_WIN32
 QStringList m_hostUSBDevices; 
 void updateHostUSBDeviceList(); 
 QString getDriverKey(QString devId);
 QString getDeviceIdFromDriverKey(QString driverKey);
#endif

protected:
 /* vmlite, usb support, monitor usb device insertion */
#ifdef Q_WS_WIN32 
 void registerUSBDeviceNotification();
 void onDeviceChange(MSG *aMsg); 
#endif
UIMachineWindow.cpp:
#ifdef Q_WS_WIN32
#include <Dbt.h>
#include <setupapi.h>
#endif
/* vmlite, usb support, monitor usb device insertion */
#ifdef Q_WS_WIN32
void UIMachineWindow::registerUSBDeviceNotification()
{
 //return; // this disables USB insertion detection
 const GUID GUID_DEVINTERFACE_USB_DEVICE =
 { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
 const GUID GUID_DEVINTERFACE_HID =
 { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
 updateHostUSBDeviceList();
    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
    ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 
 NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
 HDEVNOTIFY hDevNotify = RegisterDeviceNotification(m_pMachineWindow->winId(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
 NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_HID;
 hDevNotify = RegisterDeviceNotification(m_pMachineWindow->winId(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
}
// sync the following with our usb drivers
//#define VMLITE_USB_DEVICE_ID "USB\\VID_80EE&PID_CAFE"
#define VMLITE_USB_DEVICE_ID   "USB\\VMLITEUSBSTUB"
typedef struct _DEVICE_CHANGE_PARAMETER
{
 UIMachineWindow* pThis;
 DWORD dbch_devicetype;
 WPARAM kind;
 WCHAR dbcc_name[MAX_PATH];
} DEVICE_CHANGE_PARAMETER, *PDEVICE_CHANGE_PARAMETER;
static DWORD WINAPI deviceChangeThreadProc(LPVOID Parameter)
{
 PDEVICE_CHANGE_PARAMETER pDeviceChange = (PDEVICE_CHANGE_PARAMETER)Parameter;
 UIMachineWindow* pThis = pDeviceChange->pThis;
 WPARAM kind = pDeviceChange->kind; 
  
 /* pDevInf->dbcc_name, e.g. \\?\USB#VID_04e8&PID_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
   szDevId: USB#VID_04e8&PID_503b#0002F9A9828E0F06
   szClass: USB
    */
 // extract the devId portion, covert '#' to '\'  
 QString devIdStr((QChar *)pDeviceChange->dbcc_name + 4, wcslen(pDeviceChange->dbcc_name + 4)); 
 devIdStr = devIdStr.left(devIdStr.lastIndexOf('#'));
 devIdStr = devIdStr.replace('#', '\\');
 free(pDeviceChange); 
 // address is the driver key, e.g., {36FC9E60-C465-11CF-8056-444553540000}\0025    
 QString address = pThis->getDriverKey(devIdStr);
 // only hanlde devices that are really inserted, 
 // when an attached device is released, arrival event will come here too, ignore such insertion
 if (pThis->m_hostUSBDevices.contains(address))
 {  
  return -1;
 }
    
 /* Get HOST: */
 CHost host = vboxGlobal().virtualBox().GetHost();
 
 /* find the host usb device, and attach it, try 180 secs */
 for (int i=0; i<2 * 180; i++)
 {
  // this is extremely import, freezes otherwise
  QCoreApplication::processEvents();
  /* Get USB devices list: */
  CHostUSBDeviceVector devices = host.GetUSBDevices();
  foreach (const CHostUSBDevice hostDevice, devices)
  {   
   if (hostDevice.GetAddress() == address)
   {
    /* Get current console: */
    CConsole console = pThis->session().GetConsole();
    CUSBDevice attachedDevice = console.FindUSBDeviceById(hostDevice.GetId());
    if (attachedDevice.isNull())
    {
     console.AttachUSBDevice(hostDevice.GetId());     
     return console.isOk() ? 0 : -1;
    }
    else
    {     
     return -1;
    }
   }
  }
  Sleep(500);
 }
 
 return -1;
}
/* get driver key for the specified usb device id, from registry, so works even if device is off or deteched
 use registry to read the driver key, we don't use Setup api because the device is not active
e.g., devId: USB\VID_046D&PID_C016\5&9e58382&0&1
*/
static QString getDriverKeyThroughRegistry(const QString& devId)
{ 
 WCHAR keyName[MAX_PATH];
 //e.g., HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_046D&PID_C016\5&9e58382&0&1
 swprintf(keyName, L"SYSTEM\\CurrentControlSet\\Enum\\%ls", (WCHAR *)devId.unicode());
 HKEY hkey;
 long lret = RegOpenKeyW(HKEY_LOCAL_MACHINE, keyName, &hkey);
 if (lret != ERROR_SUCCESS) 
  return "";
 WCHAR driver[MAX_PATH];
 DWORD type;
 DWORD size = sizeof(driver);
 lret = RegQueryValueExW(hkey, L"Driver", 0, &type, (PBYTE)driver, &size);
 RegCloseKey(hkey);
 if (lret == ERROR_SUCCESS) 
 {
  return QString((QChar *)driver, wcslen(driver));
 }
 else
 {
  return "";
 } 
}
void UIMachineWindow::onDeviceChange(MSG *aMsg)
{
 if (aMsg->wParam != DBT_DEVICEREMOVECOMPLETE && aMsg->wParam != DBT_DEVICEARRIVAL)
  return;
 PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)aMsg->lParam;
 if ( pHdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
 {  
  return;
 }
 if ( DBT_DEVICEREMOVECOMPLETE == aMsg->wParam)
 {
  updateHostUSBDeviceList();
  // need to remove the device that is just removed, in case our updated list still has it in the list
  PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
  /* pDevInf->dbcc_name, e.g. \\?\USB#VID_04e8&PID_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}    
  */   
  // extract the devId portion, covert '#' to '\'  
  QString devIdStr((QChar *)pDevInf->dbcc_name + 4, wcslen(pDevInf->dbcc_name + 4)); 
  devIdStr = devIdStr.left(devIdStr.lastIndexOf('#'));
  devIdStr = devIdStr.replace('#', '\\');
  // address is the driver key, e.g., {36FC9E60-C465-11CF-8056-444553540000}\0025    
  QString address = getDriverKeyThroughRegistry(devIdStr);  
  if (!address.isEmpty() && m_hostUSBDevices.contains(address))
  {  
   m_hostUSBDevices.removeAll(address);
  }  
 }
 else if ( DBT_DEVICEARRIVAL == aMsg->wParam)
 {
  PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
  if (!m_pMachineWindow->isActiveWindow())
  {
   updateHostUSBDeviceList();
   return;
  }
  
  /* pDevInf->dbcc_name, e.g. \\?\USB#VID_04e8&PID_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
    szDevId: USB#VID_04e8&PID_503b#0002F9A9828E0F06
    szClass: USB
  */   
  // extract the devId portion, covert '#' to '\'  
  QString devIdStr((QChar *)pDevInf->dbcc_name + 4, wcslen(pDevInf->dbcc_name + 4)); 
  devIdStr = devIdStr.left(devIdStr.lastIndexOf('#'));
  devIdStr = devIdStr.replace('#', '\\');
  // when an usb device is attached, our vmlite virtualizazed usb will come here, ignore them
  if (devIdStr.startsWith(VMLITE_USB_DEVICE_ID, Qt::CaseInsensitive))
  {
   updateHostUSBDeviceList();
   return;
  }
  DWORD ThreadId;
  PDEVICE_CHANGE_PARAMETER param = (PDEVICE_CHANGE_PARAMETER)malloc(sizeof(DEVICE_CHANGE_PARAMETER));
  if (param)
  {   
   param->pThis = this;
   param->dbch_devicetype = pHdr->dbch_devicetype;
   param->kind = aMsg->wParam;
   wcscpy(param->dbcc_name, pDevInf->dbcc_name);
   HANDLE ThreadHandle = CreateThread(
         NULL,
         0,
         deviceChangeThreadProc,
         param,
         0,
         &ThreadId 
        );
  }
 }
}
/* get driver key for the original host usb device being attached from the attached device
*/
static QString getDriverKeyForOriginalDevice(const CHostUSBDevice& attachedDevice)
{
 // the attached device and the host device have same vendor id, product id, etc.
 unsigned short vid = attachedDevice.GetVendorId();
 unsigned short pid = attachedDevice.GetProductId();
 // for non attached usb device, address is the driver key, e.g., {36FC9E60-C465-11CF-8056-444553540000}\0025  
 // attached host device has address of device name
 QString address = attachedDevice.GetAddress();
 if (!address.startsWith("\\\\?\\"))
 {
  return address;
 }
 /* pDevInf->dbcc_name, e.g. \\?\USB#VID_04e8&PID_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
   szDevId: USB#VID_04e8&PID_503b#0002F9A9828E0F06    
 */    
 address = address.right(address.length() - 4);
 int pos = address.lastIndexOf('#');
 address = address.left(pos);
 // the string after 2nd # is the serial #, same as the original device being attached
 pos = address.indexOf("#");
 address = address.right(address.length() - pos - 1);
 pos = address.indexOf("#");
 address = address.right(address.length() - pos - 1);
 WCHAR *serial = (WCHAR *)address.unicode(); 
 // use registry to read the driver key, we don't use Setup api because the device is not active
 // e.g., HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_046D&PID_C016\5&9e58382&0&1
 WCHAR devId[MAX_PATH];
 swprintf(devId, L"SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_%04x&PID_%04x\\%ls", vid, pid, serial);
 HKEY hkey;
 long lret = RegOpenKeyW(HKEY_LOCAL_MACHINE, devId, &hkey);
 if (lret != ERROR_SUCCESS) 
  return "";
 WCHAR driver[MAX_PATH];
 DWORD type;
 DWORD size = sizeof(driver);
 lret = RegQueryValueExW(hkey, L"Driver", 0, &type, (PBYTE)driver, &size);
 RegCloseKey(hkey);
 if (lret == ERROR_SUCCESS) 
 {
  return QString((QChar *)driver, wcslen(driver));
 }
 else
 {
  return "";
 } 
}
void UIMachineWindow::updateHostUSBDeviceList()
{
 m_hostUSBDevices.clear();
 /* Get HOST: */
 CHost host = vboxGlobal().virtualBox().GetHost();
 
 /* Get USB devices list: */
 CHostUSBDeviceVector devices = host.GetUSBDevices(); 
 foreach (const CHostUSBDevice hostDevice, devices)
 {
  // address is the driver key, e.g., {36FC9E60-C465-11CF-8056-444553540000}\0025      
  QString address = hostDevice.GetAddress();
  if (address.startsWith("\\\\?\\")) /* attached host device has address of device name */
  {   
   address = getDriverKeyForOriginalDevice(hostDevice); 
  }
    
  m_hostUSBDevices << address;
 }
}
/* 
Get driver key from a usb id, devId: USB\Vid_04e8&Pid_503b\0002F9A9828E0F06
we use driver key to identify a host usb device, as this is unique, 
vid/pid/serial is not unique because serial might be emppty
*/
QString UIMachineWindow::getDriverKey(QString devId)
{
 HDEVINFO hDevInfo = SetupDiGetClassDevsW(NULL, L"USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
 if( INVALID_HANDLE_VALUE == hDevInfo ) 
 {
  return "";
 }
 WCHAR driverKey[MAX_PATH] = L"";
 SP_DEVINFO_DATA spDevInfoData;
 spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
 for(int i=0; SetupDiEnumDeviceInfo(hDevInfo, i, &spDevInfoData); i++) 
 {
  DWORD nSize = 0;
  WCHAR buf[MAX_PATH];
  if ( !SetupDiGetDeviceInstanceIdW(hDevInfo, &spDevInfoData, buf, sizeof(buf), &nSize) )    
   return "";
  if (0 == QString((QChar *)buf, wcslen(buf)).compare(devId, Qt::CaseInsensitive))
  {
   nSize = 0;
   if ( SetupDiGetDeviceRegistryPropertyW(hDevInfo, &spDevInfoData, 
     SPDRP_DRIVER, NULL, (PBYTE)buf, sizeof(buf), &nSize) ) 
   {
    wcscpy(driverKey, buf);
   }
   break;
  }
 }
 SetupDiDestroyDeviceInfoList(hDevInfo);
 if (driverKey[0])
  return QString((QChar *)driverKey, wcslen(driverKey));
 else
  return "";
}
/* 
Get usb id from driver key, devId: USB\Vid_04e8&Pid_503b\0002F9A9828E0F06, driver key: {36FC9E60-C465-11CF-8056-444553540000}\0025
*/
QString UIMachineWindow::getDeviceIdFromDriverKey(QString driverKey)
{
 HDEVINFO hDevInfo = SetupDiGetClassDevsW(NULL, L"USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
 if( INVALID_HANDLE_VALUE == hDevInfo ) 
 {
  return "";
 }
 WCHAR deviceId[MAX_PATH] = L"";
 SP_DEVINFO_DATA spDevInfoData;
 spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
 for(int i=0; SetupDiEnumDeviceInfo(hDevInfo, i, &spDevInfoData); i++) 
 {
  DWORD nSize = 0;
  WCHAR buf[MAX_PATH];
  if ( !SetupDiGetDeviceInstanceIdW(hDevInfo, &spDevInfoData, buf, sizeof(buf), &nSize) )    
   return "";
  wcscpy(deviceId, buf);
  nSize = 0;
  if ( SetupDiGetDeviceRegistryPropertyW(hDevInfo, &spDevInfoData, 
    SPDRP_DRIVER, NULL, (PBYTE)buf, sizeof(buf), &nSize) ) 
  {
   if (0 == QString((QChar *)buf, wcslen(buf)).compare(driverKey, Qt::CaseInsensitive))
   {   
    break;
   }   
  }
  deviceId[0] = 0;
 }
 SetupDiDestroyDeviceInfoList(hDevInfo);
 if (deviceId[0])
  return QString((QChar *)deviceId, wcslen(deviceId));
 else
  return "";
}
#endif /* Q_WS_WIN32 */

UIMachineWindowNormal.cpp:
UIMachineWindowNormal::UIMachineWindowNormal(UIMachineLogic *pMachineLogic, ulong uScreenId)
    : QIWithRetranslateUI2<QMainWindow>(0, Qt::Window)
    , UIMachineWindow(pMachineLogic, uScreenId)
    , m_pIndicatorsPool(new UIIndicatorsPool(pMachineLogic->uisession()->session(), this))
    , m_pIdleTimer(0)
{
........
// add as last line
/* vmlite, usb and dnd support, monitor usb device insertion */
#ifdef Q_WS_WIN32 
 registerUSBDeviceNotification();
#endif
}
UIMachineWindowFullscreen.cpp:
UIMachineWindowFullscreen::UIMachineWindowFullscreen(UIMachineLogic *pMachineLogic, ulong uScreenId)
    : QIWithRetranslateUI2<QMainWindow>(0, Qt::FramelessWindowHint)
    , UIMachineWindow(pMachineLogic, uScreenId)
    , m_pMainMenu(0)
    , m_pMiniToolBar(0)
{
........
// add as last line
/* vmlite, usb and dnd support, monitor usb device insertion */
#ifdef Q_WS_WIN32 
 registerUSBDeviceNotification();
#endif
}


--- On Thu, 9/15/11, Michael Thayer <michael.thayer at oracle.com> wrote:


From: Michael Thayer <michael.thayer at oracle.com>
Subject: Re: [vbox-dev] code patch to auto detect USB insertion
To: "Huihong Luo" <huisinro at yahoo.com>
Cc: vbox-dev at virtualbox.org
Date: Thursday, September 15, 2011, 2:31 AM


Hello Huihong,

On 09/13/2011 11:49 PM, Huihong Luo wrote:
> I have some code (around 600 lines) that auto attaches usb device when
> inserted to the windows host while vm window is on focus, similar to
> vmware's handling.
> not sure if vbox team wants to integrate this patch.
We might do if it is something which can be enabled or disabled by the user, but we would have to see the patch first (the usual requirements apply [1]).  Would you like to include it?

Regards and thanks,

Michael

[1] http://www.virtualbox.org/wiki/Contributor_information
-- ORACLE Deutschland B.V. & Co. KG   Michael Thayer
Werkstrasse 24                     VirtualBox engineering
71384 Weinstadt, Germany           mailto:michael.thayer at oracle.com

Hauptverwaltung: Riesstr. 25, D-80992 München
Registergericht: Amtsgericht München, HRA 95603

Komplementärin: ORACLE Deutschland Verwaltung B.V.
Hertogswetering 163/167, 3543 AS Utrecht, Niederlande
Handelsregister der Handelskammer Midden-Niederlande, Nr. 30143697
Geschäftsführer: Jürgen Kunz, Marcel van de Molen, Alexander van der Ven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.virtualbox.org/pipermail/vbox-dev/attachments/20110915/6e10b659/attachment.html>
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: usb-insertion-detection-patch.txt
URL: <http://www.virtualbox.org/pipermail/vbox-dev/attachments/20110915/6e10b659/attachment.txt>


More information about the vbox-dev mailing list