Function of USB Composite Device fails to start if not first function on device

Article translations Article translations
Close Close
Article ID: 2410682 - View products that this article applies to.
Expand all | Collapse all

SYMPTOMS

USB Composite Devices consist of multiple Functions (functional devices) within a single USB device package.

Some USB function drivers (drivers which support USB functional devices) may work correctly for single-function USB devices, but may not correctly support functional devices within a USB Composite Device. The affected USB function will be marked in Device Manager with a Code 10 error(CM_PROB_FAILED_START).

This problem does not occur with similar USB devices using the same USB function drivers, when the device is configured as a single-function (not Composite) device.

CAUSE

Affected USB function drivers may not correctly use the USBD_ParseConfigurationDescriptorEx or USBD_ParseConfigurationDescriptor routines to locate their USB Interface descriptors within the USB Composite Device's Configuration Descriptor.

For example, the following sample code demonstrates a technique for parsing a USB Configuration Descriptor that does not successfully locate the relevant USB Interface descriptors for a USB function of USB Composite Device.

Incorrect Sample A:
/* pConfigurationDescriptor points to the descriptor previously
   requested from the driver. */
PUSB_CONFIGURATION_DESCRIPTOR pConfigurationDescriptor;

/* pInterfaceList points to an array with
    pConfigurationDescriptor->bNumInterfaces entries. */
PUSBD_INTERFACE_LIST_ENTRY pInterfaceList;

PUSB_INTERFACE_DESCRIPTOR pInterfaceDescriptor;
LONG InterfaceNumber = 0;
LONG NumInterfaces = 0;

PURB pUrb;

NumInterfaces = pConfigurationDescriptor->bNumInterfaces;

pInterfaceList = AllocPool (NumInterfaces * sizeof(USBD_INTERFACE_LIST_ENTRY));

for (InterfaceNumber = 0;
     InterfaceNumber < NumInterfaces;
     InterfaceNumber++
     )
{
    /* Get the next matching descriptor. Here we implicitly use
       the fact that the interface descriptors are laid out in
       order in memory.
    */
    pInterfaceDescriptor = USBD_ParseConfigurationDescriptorEx (
                               pConfigurationDescriptor,
                               pConfigurationDescriptor,
                               InterfaceNumber, 
                                0, // alternate setting
                               -1, // interface class
                               -1, // interface subclass
                               -1, // interface protocol
                           );
    pInterfaceList.Interface[InterfaceNumber] = pInterfaceDescriptor;
}

// allocate the URB
pUrb = USBD_CreateConfigurationRequestEx(
           pConfigurationDescriptor,
           pInterfaceList
       );


Incorrect Sample B:
/*++
Routine Description:
This helper routine selects the specified configuration.

Arguments:
ConfigurationDescriptor - Pointer to the configuration
descriptor for the device. The caller receives this pointer
from the URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE request.

Return Value: NT status value
--*/

NTSTATUS
SelectConfigurationOnDevice ( IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{

    PURB urb = NULL;
    NTSTATUS                     ntStatus;
    PUSBD_INTERFACE_LIST_ENTRY   ListOfInterfaces = NULL; 
    PUSB_INTERFACE_DESCRIPTOR    InterfaceDescriptor = NULL;
    PUSBD_INTERFACE_INFORMATION  Interface = NULL;
    LONG                         interfaceIndex;
    LONG                         numOfInterfaces;
    USBD_PIPE_HANDLE             pipeHandle;

    //1. Get the number of interfaces in the configuration
    numOfInterfaces = ConfigurationDescriptor->bNumInterfaces;

    //2. Allocate for the array
    ListOfInterfaces = (PUSBD_INTERFACE_LIST_ENTRY)ExAllocatePool (
        NonPagedPool, 
        sizeof(USBD_INTERFACE_LIST_ENTRY) *
        (numOfInterfaces + 1));

    if(!ListOfInterfaces)
    {
        //Failed to allocate memory for pInterfaceList
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    // 3. Initialize the array by setting all members to NULL.
    RtlZeroMemory(ListOfInterfaces, sizeof (
        USBD_INTERFACE_LIST_ENTRY) *
        (numOfInterfaces + 1));

    // 4. Enumerate interfaces in the configuration.
    for ( interfaceIndex = 0; 
          interfaceIndex < numOfInterfaces; 
          interfaceIndex++) 
    {
        InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
            ConfigurationDescriptor, 
            ConfigurationDescriptor,
            interfaceIndex,
            0, -1, -1, -1);

        if (!InterfaceDescriptor) 
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto done;
        }

        //5. Populate the element in the array.
        ListOfInterfaces[interfaceIndex].InterfaceDescriptor = InterfaceDescriptor;
    }
...


When the USB function driver is loaded for a function of a USB Composite Device, the USB Configuration Descriptor is provided by the Microsoft USB Composite Common Generic Parent driver (Usbccgp.sys). The Configuration Descriptor only contains the Interface Descriptors and other descriptors that pertain to the specific child function for which the USB function driver is loaded. The number of Interfaces reported in this "partial" Configuration Descriptor is less than the total number of Interfaces defined for the USB Composite Device. Thus, the Interface Numbers for the USB Interfaces contained in this "partial" Configuration Descriptor may be higher than the number of Interfaces reported in this "partial" Configuration Descriptor.

As a result, calling the USBD_ParseConfigurationDescriptorEx or USBD_ParseConfigurationDescriptor routines to search for Interface numbers between 0 and the number of Interfaces in the "partial" Configuration Descriptor may fail to return any Interface Descriptors. If the USB function driver fails to find any Interface Descriptors in its device's Configuration Descriptor, the USB function driver will not be able to configure and communicate with the device, and wil fail to start successfully.

RESOLUTION

Correct USB Function Driver implementation


Search for all Interfaces in the Configuration Descriptor, regardless of Interface Number

The following sample code snippets demonstrateshow to search for any and all Interfaces in the Configuration Descriptor:

Correct Sample A:

/* pConfigurationDescriptor points to the descriptor previously
   requested from the driver. */
PUSB_CONFIGURATION_DESCRIPTOR pConfigurationDescriptor;

/* pInterfaceList points to an array with
    pConfigurationDescriptor->bNumInterfaces entries. */
PUSBD_INTERFACE_LIST_ENTRY pInterfaceList;

PUSB_INTERFACE_DESCRIPTOR pInterfaceDescriptor;
LONG InterfaceNumber = 0;
LONG NumInterfaces = 0;

PURB pUrb;

PUCHAR pStartPosition = (PUCHAR)pConfigurationDescriptor;

NumInterfaces = pConfigurationDescriptor->bNumInterfaces;

pInterfaceList = AllocPool (NumInterfaces * sizeof(USBD_INTERFACE_LIST_ENTRY));

for (InterfaceNumber = 0;
     InterfaceNumber < NumInterfaces;
     InterfaceNumber++
     )
{
    /* Get the next matching descriptor. Here we implicitly use
       the fact that the interface descriptors are laid out in
       order in memory.
    */
    pInterfaceDescriptor = USBD_ParseConfigurationDescriptorEx (
                               pConfigurationDescriptor,
                               pStartPosition, // start of search within Config Descriptor
                               -1, // interface number
                                0, // alternate setting
                               -1, // interface class
                               -1, // interface subclass
                               -1, // interface protocol
                           );
    pInterfaceList.Interface[InterfaceNumber] = pInterfaceDescriptor;
    if (!pInterfaceDescriptor)
    {
        break;
    }
    pStartPosition = (PUCHAR)pInterfaceDescriptor + pInterfaceDescriptor->bLength;
}

// allocate the URB
pUrb = USBD_CreateConfigurationRequestEx(
           pConfigurationDescriptor,
           pInterfaceList
       );


Correct Sample B:
/*++

Routine Description:
This helper routine selects the specified configuration.

Arguments:
ConfigurationDescriptor - Pointer to the configuration
descriptor for the device. The caller receives this pointer
from the URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE request.

Return Value: NT status value
--*/

NTSTATUS
SelectConfigurationOnDevice ( IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
    PURB urb = NULL;
    NTSTATUS                     ntStatus;
    PUSBD_INTERFACE_LIST_ENTRY   ListOfInterfaces = NULL; 
    PUSB_INTERFACE_DESCRIPTOR    InterfaceDescriptor = NULL;
    PUSBD_INTERFACE_INFORMATION  Interface = NULL;
    LONG                         interfaceIndex;
    LONG                         numOfInterfaces;
    USBD_PIPE_HANDLE             pipeHandle;

    PUCHAR StartPosition = (PUCHAR)ConfigurationDescriptor;

    //1. Get the number of interfaces in the configuration
    numOfInterfaces = ConfigurationDescriptor->bNumInterfaces;

    //2. Allocate for the array
    ListOfInterfaces = (PUSBD_INTERFACE_LIST_ENTRY)ExAllocatePool (
        NonPagedPool, 
        sizeof(USBD_INTERFACE_LIST_ENTRY) *
        (numOfInterfaces + 1));

    if(!ListOfInterfaces)
    {
        //Failed to allocate memory for pInterfaceList

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    // 3. Initialize the array by setting all members to NULL.
    RtlZeroMemory(ListOfInterfaces, sizeof (
        USBD_INTERFACE_LIST_ENTRY) *
        (numOfInterfaces + 1));


    // 4. Enumerate interfaces in the configuration.
    for ( interfaceIndex = 0; 
          interfaceIndex < numOfInterfaces; 
          interfaceIndex++) 
    {
        InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
            ConfigurationDescriptor, 
            StartPosition, // StartPosition 
            -1,  // InterfaceNumber
             0,  // AlternateSetting
            -1,  // InterfaceClass
            -1,  // InterfaceSubClass
            -1); // InterfaceProtocol

        if (!InterfaceDescriptor) 
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto done;
        }

        StartPosition = (PUCHAR)InterfaceDescriptor + InterfaceDescriptor->bLength;

        //5. Populate the element in the array.
        ListOfInterfaces[interfaceIndex].InterfaceDescriptor = InterfaceDescriptor;
    }
...


Search for Interfaces of the appropriate Class, SubClass, and/or Protocol

Correct Sample A:

/* pConfigurationDescriptor points to the descriptor previously
   requested from the driver. */
PUSB_CONFIGURATION_DESCRIPTOR pConfigurationDescriptor;

/* pInterfaceList points to an array with
    pConfigurationDescriptor->bNumInterfaces entries. */
PUSBD_INTERFACE_LIST_ENTRY pInterfaceList;

PUSB_INTERFACE_DESCRIPTOR pInterfaceDescriptor;
LONG InterfaceNumber = 0;
LONG NumInterfaces = 0;

PURB pUrb;

PUCHAR pStartPosition = (PUCHAR)pConfigurationDescriptor;

NumInterfaces = pConfigurationDescriptor->bNumInterfaces;

pInterfaceList = AllocPool (NumInterfaces * sizeof(USBD_INTERFACE_LIST_ENTRY));

for (InterfaceNumber = 0;
     InterfaceNumber < NumInterfaces;
     InterfaceNumber++
     )
{
    /* Get the next matching descriptor. Here we implicitly use
       the fact that the interface descriptors are laid out in
       order in memory.
    */
    pInterfaceDescriptor = USBD_ParseConfigurationDescriptorEx (
                             pConfigurationDescriptor,
                             pStartPosition,
                             -1, // interface number
                              0, // alternate setting
                             MY_FUNCTION_CLASS,    // interface class
                             MY_FUNCTION_SUBCLASS, // interface subclass
                             MY_FUNCTION_PROTOCOL, // interface protocol
                         );
    pInterfaceList.Interface[InterfaceNumber] = pInterfaceDescriptor;
    if (!pInterfaceDescriptor)
    {
        break;
    }
    pStartPosition = (PUCHAR)pInterfaceDescriptor + pInterfaceDescriptor->bLength;
}

// allocate the URB
pUrb = USBD_CreateConfigurationRequestEx(
           pConfigurationDescriptor,
           pInterfaceList
       );


Workaround in USB Device Design

If you are the vendor of an affected USB device which is still under design, and you are unable to change the USB function driver to resolve this problem, you may be able to work around this problem by changing the design of your device.

Reorder the Interfaces in your device's Configuration Descriptor
Make sure the Interface (or Interfaces) for the affected function appear first in the list of Interface Descriptors contained within your device's Configuration Descriptor. For example, the Interface(s) for the affected function would appear first device's Configuration Descriptor, and be numbered starting with number 0. The Interfaces for any functions would appear afterward in the device's Configuration Descriptor, and would be assigned subsequent interface numbers.

Implement your USB device as a Compound Device

A USB Compound Device consists of a single physical package which implements multiple functions and an embedded hub with a single USB cable. A compound device appears to the host as a hub with one or more non-removable USB devices. Each of these devices would be implemented as single-function devices.

MORE INFORMATION

USB Configuration Descriptors

A Configuration Descriptor for a USB device contains a Configuration Header followed by descriptors for the Interfaces associated with the selected configuration of the USB device, as well as additional descriptors that may be associated with each Interface such as Endpoint descriptors and class-specific descriptors.

A USB Configuration Descriptor for a single-Interface device may appear as follows:

Collapse this tableExpand this table
Configuration Header (USB_CONFIGURATION_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_CONFIGURATION_DESCRIPTOR_TYPE (0x02)
- wTotalLength = total length, in bytes, of all data for the configuration
- bNumInterfaces = 1 (single Interface)
...
Interface Descriptor (USB_INTERFACE_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_DESCRIPTOR_TYPE (0x04)
- bInterfaceNumber = 0
- bAlternateSetting
- bNumEndpoints
- bInterfaceClass
- bInterfaceSubClass
- bInterfaceProtocol
...
Endpoint Descriptor(s) (USB_ENDPOINT_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE (0x05)
- bEndpointAddress
...
Class-Specific Descriptor(s) (optional)
- bLength = size of this descriptor in bytes
- bDescriptorType = class-specific descriptor type
...
...

A complete USB Configuration Descriptor for a multiple-Interface (Composite) device may appear as follows:

Collapse this tableExpand this table
Configuration Header (USB_CONFIGURATION_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_CONFIGURATION_DESCRIPTOR_TYPE (0x02)
- wTotalLength = total length, in bytes, of all data for the configuration
- bNumInterfaces = 3 (multiple Interfaces, Composite device)
...
Interface Association Descriptor (USB_INTERFACE_ASSOCIATION_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE (0x0B)
- bFirstInterface = 0
- bInterfaceCount = 2
- bFunctionClass
- bFunctionSubClass
- bFunctionProtocol
...
Interface Descriptor (USB_INTERFACE_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_DESCRIPTOR_TYPE (0x04)
- bInterfaceNumber = 0
- bAlternateSetting
- bNumEndpoints
- bInterfaceClass
- bInterfaceSubClass
- bInterfaceProtocol
...
Endpoint Descriptor(s) (USB_ENDPOINT_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE (0x05)
- bEndpointAddress
...
Class-Specific Descriptor(s) (optional)
- bLength = size of this descriptor in bytes
- bDescriptorType = class-specific descriptor type
...
...
Interface Descriptor (USB_INTERFACE_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_DESCRIPTOR_TYPE (0x04)
- bInterfaceNumber = 1
- bAlternateSetting
- bNumEndpoints
- bInterfaceClass
- bInterfaceSubClass
- bInterfaceProtocol
...
Endpoint Descriptor(s) (USB_ENDPOINT_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE (0x05)
- bEndpointAddress
...
Class-Specific Descriptor(s) (optional)
- bLength = size of this descriptor in bytes
- bDescriptorType = class-specific descriptor type
...
...
Interface Descriptor (USB_INTERFACE_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_DESCRIPTOR_TYPE (0x04)
- bInterfaceNumber = 2
- bAlternateSetting
- bNumEndpoints
- bInterfaceClass
- bInterfaceSubClass
- bInterfaceProtocol
...
Endpoint Descriptor(s) (USB_ENDPOINT_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE (0x05)
- bEndpointAddress
...
Class-Specific Descriptor(s) (optional)
- bLength = size of this descriptor in bytes
- bDescriptorType = class-specific descriptor type
...
...

The partial USB Configuration Descriptor presented by Usbccgp to the USB function driver for the first function of the above device may appear as follows:

Collapse this tableExpand this table
Configuration Header (USB_CONFIGURATION_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_CONFIGURATION_DESCRIPTOR_TYPE (0x02)
- wTotalLength = total length, in bytes, of all data for the configuration
- bNumInterfaces = 2
...
Interface Association Descriptor (USB_INTERFACE_ASSOCIATION_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE (0x0B)
- bFirstInterface = 0
- bInterfaceCount = 2
- bFunctionClass
- bFunctionSubClass
- bFunctionProtocol
...
Interface Descriptor (USB_INTERFACE_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_DESCRIPTOR_TYPE (0x04)
- bInterfaceNumber = 0
- bAlternateSetting
- bNumEndpoints
- bInterfaceClass
- bInterfaceSubClass
- bInterfaceProtocol
...
Endpoint Descriptor(s) (USB_ENDPOINT_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE (0x05)
- bEndpointAddress
...
Class-Specific Descriptor(s) (optional)
- bLength = size of this descriptor in bytes
- bDescriptorType = class-specific descriptor type
...
...
Interface Descriptor (USB_INTERFACE_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_DESCRIPTOR_TYPE (0x04)
- bInterfaceNumber = 1
- bAlternateSetting
- bNumEndpoints
- bInterfaceClass
- bInterfaceSubClass
- bInterfaceProtocol
...
Endpoint Descriptor(s) (USB_ENDPOINT_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE (0x05)
- bEndpointAddress
...
Class-Specific Descriptor(s) (optional)
- bLength = size of this descriptor in bytes
- bDescriptorType = class-specific descriptor type
...
...

The partial USB Configuration Descriptor presented by Usbccgp to the USB function driver for the second function of the above device may appear as follows:

Collapse this tableExpand this table
Configuration Header (USB_CONFIGURATION_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_CONFIGURATION_DESCRIPTOR_TYPE (0x02)
- wTotalLength = total length, in bytes, of all data for the configuration
- bNumInterfaces = 1
...
Interface Descriptor (USB_INTERFACE_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_INTERFACE_DESCRIPTOR_TYPE (0x04)
- bInterfaceNumber = 2
- bAlternateSetting
- bNumEndpoints
- bInterfaceClass
- bInterfaceSubClass
- bInterfaceProtocol
...
Endpoint Descriptor(s) (USB_ENDPOINT_DESCRIPTOR)
- bLength = size of this descriptor in bytes
- bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE (0x05)
- bEndpointAddress
...
Class-Specific Descriptor(s) (optional)
- bLength = size of this descriptor in bytes
- bDescriptorType = class-specific descriptor type
...
...

Using one of the incorrect sample code implementations, a USB function driver loaded for this example second function would call USBD_ParseConfigurationDescriptorEx to search for Interface number 0. When this call failed to return a matching Interface Descriptor, the loop would increment the Interface number counter to 1, then terminate the loop since this counter now equals the number of interfaces reported for this partial Configuration Descriptor.


References

For more information on USB descriptor formats used by USB client drivers, see the following topics in the Windows Driver Kit (WDK) documentation:

USB_COMMON_DESCRIPTOR Structure
USB_CONFIGURATION_DESCRIPTOR Structure
USB_INTERFACE_DESCRIPTOR Structure
USB_ENDPOINT_DESCRIPTOR Structure

For more information on routines used to parse USB descriptors, see the following topic sin the Windows Driver Kit (WDK) documentation:

USBD_ParseConfigurationDescriptorEx
USBD_ParseDescriptors

For more information on USB Composite and Compound devices, see the following sections of the USB 2.0 specification:
  • 4.8.2.2 Functions
  • 5.2.3 Physical Bus Topology

The USB 2.0 specification is available for download from:

http://www.usb.org/developers/docs

Affected Microsoft Windows drivers

The following drivers provided in Microsoft Windows are known to exhibit this problem:

Windows 7 and Windows Server 2008 R2:
  • Usb8023.sys, Usb8023x.sys, Usb80236.sys (Remote NDIS [RNDIS] USB drivers)
  • BthUsb.sys (Bluetooth USB Miniport Driver)
  • UsbCamd.sys (Universal Serial Bus Camera Driver)
Windows Vista and Windows Server 2008:
  • Usb8023.sys, Usb8023x.sys, Usb80236.sys (Remote NDIS [RNDIS] USB drivers)
  • BthUsb.sys (Bluetooth USB Miniport Driver)
  • UsbCamd.sys (Universal Serial Bus Camera Driver)
For USB composite devices with functions which use these in-box drivers, the device vendor may be able to work around the problem by reconfiguring their device so that the Interface(s) for the affected function appear(s) first in the USB Composite Device's Configuration Descriptor, or by implementing the device as a USB Compound device, as described above.
Note This is a "FAST PUBLISH" article created directly from within the Microsoft support organization. The information contained herein is provided as-is in response to emerging issues. As a result of the speed in making it available, the materials may include typographical errors and may be revised at any time without notice. See Terms of Use for other considerations.

Properties

Article ID: 2410682 - Last Review: September 30, 2010 - Revision: 2.0
APPLIES TO
  • Microsoft Windows XP Professional
  • Microsoft Windows XP Home Edition
  • Microsoft Windows Server 2003, Standard Edition (32-bit x86)
  • Microsoft Windows Server 2003, Enterprise Edition (32-bit x86)
  • Microsoft Windows Server 2003, Datacenter Edition (32-bit x86)
  • Microsoft Windows Server 2003, Standard x64 Edition
  • Microsoft Windows Server 2003, Enterprise x64 Edition
  • Microsoft Windows Server 2003, Datacenter x64 Edition
  • Microsoft Windows Server 2003, Enterprise Edition for Itanium-based Systems
  • Microsoft Windows Server 2003, Datacenter Edition for Itanium-Based Systems
  • Windows Vista Ultimate
  • Windows Vista Enterprise
  • Windows Vista Business
  • Windows Vista Home Premium
  • Windows Vista Home Basic
  • Windows Vista Ultimate 64-bit Edition
  • Windows Vista Enterprise 64-bit Edition
  • Windows Vista Home Premium 64-bit Edition
  • Windows Vista Home Basic 64-bit Edition
  • Windows Vista Business 64-bit Edition
  • Windows Server 2008 Standard
  • Windows Server 2008 Enterprise
  • Windows Server 2008 Datacenter
  • Windows 7 Ultimate
  • Windows 7 Enterprise
  • Windows 7 Professional
  • Windows 7 Home Premium
  • Windows 7 Home Basic
  • Windows 7 Starter
  • Windows Server 2008 R2 Standard
  • Windows Server 2008 R2 Enterprise
  • Windows Server 2008 R2 Datacenter
Keywords: 
KB2410682

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com