USB Architecture in Windows Virtual PC

Some old legacy devices may not have driver support for newer operating systems (Windows® Vista and beyond). Windows® Virtual PC (WVPC) makes it possible to use these devices in Windows® 7, using Windows® XP Mode. Users can, at the same time, utilize the benefits of newer and secure features of Windows® 7. In this article, we will describe the USB architecture in WVPC. Typical USB devices used in a Virtual Machine (VM) are:

  • Printers
  • Flash memory sticks
  • External hard disks
  • Webcams and Digital cameras
  • Smartcards

Fig. 1

Figure 1a: USB Menu on a Virtual Machine’s Tool Bar

Fig 2

Figure 1b: Manage USB Devices for Virtual Applications

Details on using these devices with the VM can be found in an earlier post.

WVPC uses the Redirection Policy Manager (RPM) of the Windows to provide the USB redirection in a Virtual Machine. It loads an alternate driver in the lieu of the original driver to redirect the device to a Virtual machine. WVPC creates a virtualized host controller in the Virtual Machine that is being offered using a VPCBus Channel. For more details, please refer to earlier post.

USB Architecture

The overall USB architecture is shown in Figure 2.

Fig 3

Figure 2: USB Architecture showing a USB Device in VM

USB architecture consists of a server side component running in Host OS and a client side component running in Virtual Machine. The server side involves a Connector driver to manage USB devices and a Stub instance for every USB Device. The client side implements a VPCBus enumerated virtual host controller that supports the subset of the USB driver interfaces that are necessary for compatibility with the supported devices. The redirection process also triggers the connector driver to send commands to the guest to create the PDO for the redirected device. After this the Stub driver, Connector driver and the Virtual bus/hub driver work in unison to enable communication of commands, responses and data between physical USB device and the redirected USB device. Component details are described below:

Server Side (Host OS)

A channel is created by Virtual PC host process (VPC.exe) through the Connector Driver using VPCBus whenever a VM boots up. Also, a server side hub instance is created internally by the Connector with this channel for every VM. There are two drivers on the server side as below:

  • The Connector driver provides services regardless of the devices that are currently connected.
  • The USB Stub driver is the alternate driver loaded by the USB stack instead of the vendor supplied or system supplied driver for the device using RPM. Please refer Figure 2. The Stub driver just forwards requests from the Connector driver to the PDO created by the hub driver.

A total of up to 8 USB devices can be assigned to a VM at a time. HID and Hub Class Devices cannot be assigned to the VM. The USB devices in VM can be safely removed just like in Host OS whenever the USB device driver supports.

Client Side (Virtual Machine)

The virtual host controller is a VPCBus enumerated bus driver (USB Virtualization Bus Driver) that takes the place of normal host controller driver. It is exposed to the system as two devices – a host controller and a root-hub device.

The virtual hub (USB Virtualization Hub Driver) creates the PDOs that mirror virtualized devices in the parent partition. The PNP manager loads the Client USB device drivers (or stacks of drivers) for the device. The virtual hub driver handles the IOCTLs either locally or sends the same to the USB device using Connector Driver. Please refer Figure 3 showing the Device Manager of Guest and Host.

Fig 3B

Figure 3: A USB Device assigned to a Virtual Machine

Power State Changes of Host and Virtual Machine

USB Device assigned to a Virtual Machine reacts to the power state changes of the host OS as well as Virtual Machine. The changes are described below:

Virtual Machine Power State Changes

A USB device gets automatically assigned to host OS whenever a Virtual machine is shutdown, restarted, turned off or saved. The device remains within the VM whenever a VM is put to ‘Sleep’ state.

Host Power State Changes

USB Device gets assigned to host OS on host restart/shutdown/logoff. It remains within the VM on host sleep/hibernate.

Device Sharing vs. USB Device Redirection

Devices like printers, smart cards and USB external/flash drives are shared with Virtual Machine in Integration Features Enabled mode by the (Remote Desktop Protocol) RDP. These devices can be used with host OS as well as the Virtual Machine(s) at the same time. More details about this can be found in an earlier post.

Assigning a USB Device to a VM removes the device from host OS. It is only visible to the Virtual machine it is attached to and an application running on the host OS cannot access the device.

Prevent Device Redirection using Group Policy

Group policy can be used to prevent the redirection of selected USB devices to a Virtual Machine, for security or compliance reasons for example. This can be done at per device/device-class level. Also, all USB devices can be prevented to be used inside a VM. These settings are helpful in an organization where users are not allowed to use these devices in the physical machine as well.

These Group policy settings can be found under Computer Configuration -> Administrative Templates -> System->Device Redirection -> Device Redirection Restrictions as shown below (Figure 4).

Fig 4

Figure 4: Group Policy Settings for USB Device restrictions

There are two group policy settings for the Device Redirection Restrictions as described below:

Preventing redirection of devices that match any of these Device IDs

Enabling this policy setting will prevent the redirection of specific USB devices on a machine that matches the criteria specified in the Value field. For example, to block all USB Audio class devices (USB Class Code 01) for the redirection, the policy needs to be configured with Class_01 as shown below (Figure 5).

Fig 5

Figure 5: Group Policy setting for redirection of specific USB devices restrictions

Preventing redirection of USB devices

Enabling this policy setting will prevent the redirection of all USB devices on a machine.

More details about these policy settings can be found on MSDN here.

Implementation and Code examples

WVPC provides COM support to manage USB devices. The APIs are documented on MSDN at https://msdn.microsoft.com/en-us/library/dd796758(VS.85).aspx. Let’s take a look few scenarios for using these APIs to manage USB devices. A user wants to assign a USB Device whenever a VM is started. Another scenario is that an IT admin wants to restrict the USB Device Redirection feature due to some piracy/security concerns. In these scenarios, a developer can write scripts in VB/powershell or a code in any COM capable language to achieve this. Some samples are provided below:

Assign a USB Device

A developer can use AttachUSBDevice method of IVMVirtualMachine interface to assign a USB device to the VM. A sample C++ code is shown below:

    1: // Steps to Assign a Device
    2: // Step 1: Get the Virtual PC object
    3: // Step 2: Get the Virtual Machine Object
    4: // Step 3: Start the Virtual Machine if it's not running
    5: // Step 3: Get the USB Device Collection
    6: // Step 4: Get the USB Device Object to be assigned based on Device String
    7: // Step 5: Assign the Device
    8:  
    9:  
   10: #define CHECK_NULL_RELEASE(obj) if(obj) \
   11:                     {\
   12:                     obj->Release();\
   13:                     obj = NULL;\
   14:                     }
   15:  
   16: int __cdecl main(int , char* )
   17: {
   18:     IVMVirtualMachine*      pVM                     = NULL;
   19:     IVMVirtualPC*           pIVPC                   = NULL;
   20:     HRESULT                 hr                      = S_OK;
   21:     BSTR                    bName                   = NULL;
   22:     VMVMState               vmState                 = vmVMState_Invalid;
   23:     IVMTask*                pIVMTask                = NULL;
   24:     IVMUSBDeviceCollection* pUSBDeviceCollection    = NULL;
   25:     long                    usbDeviceCount          = 0;
   26:     IVMUSBDevice*           pUsbDevice              = NULL;
   27:     long                    index                   = 0;
   28:     BSTR                    bDeviceString           = NULL;
   29:     bool                    bDeviceFound            = FALSE;
   30:  
   31:     // Change the VM name as needed 
   32:     LPWSTR                  lVMNameToBeAssigned     = L"Windows XP Mode";
   33:  
   34:     // Change the Device String as needed 
   35:     LPWSTR                  lUsbDeviceString        = L"Cruzer Micro"; //L"Mass Storage Device";
   36:  
   37:     // Initialize VPC COM interfaces.
   38:     hr = InitVPCComInterfaces(&pIVPC);
   39:     if (hr != S_OK)
   40:     {
   41:         goto Cleanup;
   42:     }
   43:  
   44:     // Allocate the Memory for the Virtual Machine Name
   45:     // on which Device needs to be assigned.
   46:     bName = SysAllocString(lVMNameToBeAssigned);
   47:     if (bName == NULL) // Unable to allocate the Memory for VM Name String
   48:     {
   49:         hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
   50:         goto Cleanup;
   51:     }
   52:  
   53:     // Get the Virtual Machine object of the provided VMNAME
   54:     hr = pIVPC->FindVirtualMachine(bName, &pVM);
   55:     if (hr != S_OK)
   56:     {
   57:         goto Cleanup;
   58:     }
   59:  
   60:     // Start the Virtual Machine if it's not running
   61:     hr = pVM->get_State(&vmState);
   62:     if (hr != S_OK)
   63:     {
   64:         goto Cleanup;
   65:     }
   66:  
   67:     if(vmState == vmVMState_Running)
   68:     {
   69:         // VM is already Running
   70:     }
   71:     else
   72:     {
   73:         hr = pVM->Startup(&pIVMTask);
   74:         if (hr !=S_OK)
   75:         {
   76:             goto Cleanup;
   77:         }
   78:  
   79:         // Wait till the VM boots up
   80:         hr = pIVMTask->WaitForCompletion(-1);
   81:         if (hr != S_OK)
   82:         {
   83:             goto Cleanup;
   84:         }        
   85:     }
   86:  
   87:     // Get the USB Device Collection
   88:     hr = pIVPC->get_USBDeviceCollection(&pUSBDeviceCollection);
   89:     if (hr != S_OK)
   90:     {
   91:         goto Cleanup;
   92:     }
   93:  
   94:     // Get the USB Device Collection Count
   95:     hr = pUSBDeviceCollection->get_Count(&usbDeviceCount);
   96:     if (hr != S_OK)
   97:     {
   98:         goto Cleanup;
   99:     }
  100:  
  101:     if (usbDeviceCount == 0)
  102:         goto Cleanup;
  103:  
  104:     // Iterate through the USB Device Collection for the Device to be Assigned
  105:     for ( index = 1; index <= usbDeviceCount; index++)
  106:     {
  107:         hr = pUSBDeviceCollection->get_Item(index, &pUsbDevice);
  108:         if (hr != S_OK)
  109:             goto Cleanup;
  110:                 
  111:         // Get the Device Name or Manufacturer String 
  112:         hr = pUsbDevice->get_DeviceString(&bDeviceString); 
  113:         if (hr != S_OK)
  114:             goto Cleanup;
  115:  
  116:         // Check the Device String for the Match
  117:         if (_wcsicmp(bDeviceString, lUsbDeviceString) == 0 )
  118:         {
  119:             // Device is Found
  120:             bDeviceFound = TRUE;
  121:             break;
  122:         }
  123:     }
  124:  
  125:     if (bDeviceFound == TRUE)
  126:     {
  127:         // Device was found above.
  128:         // Assign the Device to the VM
  129:         hr = pVM->AttachUSBDevice(pUsbDevice);
  130:         if (hr != S_OK)
  131:             goto Cleanup;
  132:     }
  133:  
  134: Cleanup:
  135:     CHECK_NULL_RELEASE(pIVPC);
  136:     CHECK_NULL_RELEASE(pVM);
  137:     CHECK_NULL_RELEASE(pUSBDeviceCollection); 
  138:     CHECK_NULL_RELEASE(pUsbDevice); 
  139:     CHECK_NULL_RELEASE(pIVMTask);
  140:     SysFreeString(bName);
  141:     SysFreeString(bDeviceString);
  142:  
  143:     return 0;
  144: }
  145:  
  146: HRESULT 
  147: InitVPCComInterfaces(
  148:             __inout IVMVirtualPC** ppIVPC
  149:     )
  150: {
  151:     HRESULT     hr      = S_OK;
  152:     BSTR        bVer    = NULL;
  153:     
  154:     // Initialize COM
  155:  
  156:     hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  157:     if (hr !=  S_OK) 
  158:         return hr;
  159:  
  160:     REFCLSID classID = _uuidof(VMVirtualPC);
  161:  
  162:     // Get the VPC object
  163:     hr = CoCreateInstance(classID, 
  164:                           NULL, 
  165:                           CLSCTX_ALL, 
  166:                           IID_IVMVirtualPC, 
  167:                           (LPVOID*)ppIVPC);
  168:  
  169:     if (SUCCEEDED(hr) && (*ppIVPC != NULL))
  170:     {       
  171:         // Make a call to the VPC interface and see if it works.
  172:         hr = (*ppIVPC)->get_Version(&bVer);
  173:     }
  174:  
  175:     SysFreeString(bVer);
  176:     return hr;
  177: }

Release a USB Device

Similarly, a developer can use DetachUSBDevice method of IVMVirtualMachine interface to release a USB device from the VM. A sample C++ code is shown below:

    1: // Steps to Release a Device
    2: // Step 1: Get the Virtual PC object
    3: // Step 2: Get the Virtual Machine Object
    4: // Step 3: Get the USB Device Collection
    5: // Step 4: Get the USB Device Object to be assigned based on Device String
    6: // Step 5: Verify the Device is attached to the VM
    7: // Step 6: Release the USB Device
    8:  
    9: #define SAFE_RELEASE(obj) if(obj) \
   10:                     {\
   11:                     obj->Release();\
   12:                     obj = NULL;\
   13:                     }
   14:  
   15: int __cdecl main(int , char* )
   16: {
   17:     IVMVirtualMachine*      pVM                     = NULL;
   18:     IVMVirtualPC*           pIVPC                   = NULL;
   19:     HRESULT                 hr                      = S_OK;
   20:     BSTR                    bName                   = NULL;
   21:     BSTR                    bVmNameAttachedTo       = NULL;
   22:     VMVMState               vmState                 = vmVMState_Invalid;
   23:     IVMUSBDeviceCollection* pUSBDeviceCollection    = NULL;
   24:     long                    usbDeviceCount          = 0;
   25:     IVMUSBDevice*           pUsbDevice              = NULL;
   26:     long                    index                   = 0;
   27:     BSTR                    bDeviceString           = NULL;
   28:     bool                    bDeviceFound            = FALSE;
   29:  
   30:     // Change the VM name as needed 
   31:     LPWSTR                  lVMNameToBeAssigned     = L"Windows XP Mode";
   32:  
   33:     // Change the Device String as needed 
   34:     LPWSTR                  lUsbDeviceString        = L"Mass Storage Device";
   35:  
   36:     // Initialize VPC COM interfaces.
   37:     hr = InitVPCComInterfaces(&pIVPC);
   38:     if (hr != S_OK)
   39:     {
   40:         goto Cleanup;
   41:     }
   42:  
   43:     // Allocate the Memory for the Virtual Machine Name
   44:     // on which Device needs to be assigned.
   45:     bName = SysAllocString(lVMNameToBeAssigned);
   46:     if (bName == NULL) // Unable to allocate the Memory for VM Name String
   47:     {
   48:         hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
   49:         goto Cleanup;
   50:     }
   51:  
   52:     // Get the Virtual Machine object of the provided VMNAME
   53:     hr = pIVPC->FindVirtualMachine(bName, &pVM);
   54:     if (hr != S_OK)
   55:     {
   56:         goto Cleanup;
   57:     }
   58:  
   59:     // Check the Virtual Machine State for Running
   60:     hr = pVM->get_State(&vmState);
   61:     if (hr != S_OK)
   62:     {
   63:         goto Cleanup;
   64:     }
   65:  
   66:     if(vmState == vmVMState_Running)
   67:     {
   68:         // VM is Running
   69:     }
   70:     else 
   71:     {
   72:         // VM is not Running
   73:         goto Cleanup;
   74:     }
   75:  
   76:     // Get the USB Device Collection
   77:     hr = pIVPC->get_USBDeviceCollection(&pUSBDeviceCollection);
   78:     if (hr != S_OK)
   79:     {
   80:         goto Cleanup;
   81:     }
   82:  
   83:     // Get the USB Device Collection Count
   84:     hr = pUSBDeviceCollection->get_Count(&usbDeviceCount);
   85:     if (hr != S_OK)
   86:     {
   87:         goto Cleanup;
   88:     }
   89:  
   90:     if (usbDeviceCount == 0) // No USB Devices Connected to the Machine
   91:         goto Cleanup;
   92:         
   93:     // Iterate through the USB Device Collection for the Device to be Assigned
   94:     for ( index = 1; index <= usbDeviceCount; index++)
   95:     {
   96:         hr = pUSBDeviceCollection->get_Item(index, &pUsbDevice);
   97:         if (hr != S_OK)
   98:             goto Cleanup;
   99:                 
  100:         // Get the Device Name or Manufacturer String 
  101:         hr = pUsbDevice->get_DeviceString(&bDeviceString); 
  102:         if (hr != S_OK)
  103:             goto Cleanup;
  104:  
  105:         // Check the Device String for the Match
  106:         if (_wcsicmp(bDeviceString, lUsbDeviceString) == 0 )
  107:         {
  108:             // Verify that the Device is attached to this VM
  109:             hr = pUsbDevice->get_AttachedToVM(&bVmNameAttachedTo); 
  110:             if (hr != S_OK)
  111:                 goto Cleanup;
  112:  
  113:             if (_wcsicmp(bVmNameAttachedTo, bName) == 0 )
  114:             {
  115:                 // Device is Found attached to this VM
  116:                 bDeviceFound = TRUE;
  117:                 break;
  118:             }
  119:         }
  120:     }
  121:  
  122:     if (bDeviceFound == TRUE)
  123:     {
  124:         // Device was found above.
  125:         // Assign the Device to the VM
  126:         hr = pVM->DetachUSBDevice(pUsbDevice);
  127:         if (hr != S_OK)
  128:             goto Cleanup;
  129:     }
  130:  
  131: Cleanup:
  132:     SAFE_RELEASE(pIVPC);
  133:     SAFE_RELEASE(pVM);
  134:     SAFE_RELEASE(pUSBDeviceCollection); 
  135:     SAFE_RELEASE(pUsbDevice); 
  136:     SysFreeString(bName);
  137:     SysFreeString(bDeviceString);
  138:     SysFreeString(bVmNameAttachedTo);
  139:     
  140:     return 0;
  141: }
  142:  
  143: HRESULT 
  144: InitVPCComInterfaces(
  145:             __inout IVMVirtualPC** ppIVPC
  146:     )
  147: {
  148:     HRESULT     hr      = S_OK;
  149:     BSTR        bVer    = NULL;
  150:     
  151:     // Initialize COM
  152:  
  153:     hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  154:     if (hr !=  S_OK) 
  155:         return hr;
  156:  
  157:     REFCLSID classID = _uuidof(VMVirtualPC);    
  158:  
  159:     // Get the VPC object
  160:     hr = CoCreateInstance(classID, 
  161:                           NULL, 
  162:                           CLSCTX_ALL, 
  163:                           IID_IVMVirtualPC, 
  164:                           (LPVOID*)ppIVPC);
  165:  
  166:     if (SUCCEEDED(hr) && (*ppIVPC != NULL))
  167:     {       
  168:         // Make a call to the VPC interface and see if it works.
  169:         hr = (*ppIVPC)->get_Version(&bVer);
  170:     }
  171:  
  172:     SysFreeString(bVer);
  173:     return hr;
  174: }

Using Group Policy Objects to restrict USB Devices

In Windows 7, GP objects can be managed using Powershell or using COM scripts. These can be extended to restrict USB Devices in Virtual Machines.

Create own applications on top of these APIs

A device gets assigned to host OS whenever the VM is hibernated, shutdown/restarted. WVPC doesn’t provide a direct option to assign a USB device automatically in these scenarios. However, a developer can create their own applications using the above APIs.

Conclusion

Managing USB devices of Windows XP Mode and WVPC is very simple and straightforward. USB devices can be shared or even assigned to a VM. COM APIs can be used to assign-release devices or even creating applications for their own customized scenarios. We hope this information is useful to you.

Technorati Tags: Windows 7,Windows Virtual PC,Windows XP Mode,Networking,Application Compatibility,Windows Upgrade,VPC,VM,Virtual Machine,Virtualization,USB,Device Redirection

Rahul Rajwanshi

SDET

Microsoft Virtualization Team