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:
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.
The overall USB architecture is shown in Figure 2.
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:
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:
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.
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.
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:
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.
USB Device gets assigned to host OS on host restart/shutdown/logoff. It remains within the VM on host sleep/hibernate.
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.
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).
There are two group policy settings for the Device Redirection Restrictions as described below:
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).
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.
WVPC provides COM support to manage USB devices. The APIs are documented on MSDN at http://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:
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: }
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
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
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;
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
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)
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: }
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: }
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.
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.
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.
Rahul Rajwanshi
SDET
Microsoft Virtualization Team