# Copyright © 2008, Microsoft Corporation. All rights reserved. # Shared constants, functions, and C# code for USB troubleshooter. # Read localized strings into $localizationString hashtable Import-LocalizedData -BindingVariable localizationString -FileName CL_LocalizationData $Constants = New-Object Object $Constants | Add-Member NoteProperty -Name ResetOnResumeRootcauseId -Value "RC_ResetOnResume" $Constants | Add-Member NoteProperty -Name LegacyDeviceRootcauseId -Value "RC_LegacyDevice" $Constants | Add-Member NoteProperty -Name UsbflagsPath -Value "HKLM:\System\CurrentControlSet\Control\usbflags" $Constants | Add-Member NoteProperty -Name UxdPath -Value "HKLM:\System\CurrentControlSet\Services\usbhub\uxd_control" $Constants | Add-Member NoteProperty -Name ResetOnResumeName -Value "ResetOnResume" $Constants | Add-Member NoteProperty -Name TsWroteResetOnResumeName -Value "TsWroteResetOnResume" $Constants | Add-Member NoteProperty -Name UxdGlobalEnableName -Value "UxdGlobalEnable" $Constants | Add-Member NoteProperty -Name ForceHCResetOnResumeName -Value "ForceHCResetOnResume" $Constants | Add-Member NoteProperty -Name HubService -Value @("usbhub", "usbhub3") $Constants | Add-Member NoteProperty -Name EtwSessionName -Value "UsbTs" $Constants | Add-Member NoteProperty -Name EtlFileName -Value "UsbTs.etl" $Constants | Add-Member NoteProperty -Name ApplicabilityResetOnResumeAlreadySet -Value "ResetOnResumeAlreadySet" $Constants | Add-Member NoteProperty -Name ApplicabilityUxdSettingsExist -Value "UxdSettingsExist" $Constants | Add-Member NoteProperty -Name ApplicabilityTroubleshootedDriver -Value "TroubleshootedDriver" $Constants | Add-Member NoteProperty -Name ApplicabilityDeviceOrDriverInstalled -Value "DeviceOrDriverInstalled" $Constants | Add-Member NoteProperty -Name ApplicabilityGlobalResetOnResumeSet -Value "GlobalResetOnResumeSet" function GetResetOnResumeInstanceId( [string]$VprString, [string]$Port, [string]$ParentHubPath, [string]$DevinstId ) # # Function Description: # Generate a string uniquely identifying the rootcause instance parameters # (which are also parameters to this function). This string will also # be SQMed. # # Arguments: # $VprString # $Port # $ParentHubPath # $DevinstId # # Return value: # A string. This string is guaranteed to be the same across calls if the # parameters are the same, and different if the parameters to the function # change. # { # return value "$VprString, $Port, $ParentHubPath, $DevinstId" } function GetLegacyDeviceInstanceId( [string]$VprString, [string]$DevinstId ) { "$VprString, $DevinstId" } function IsLegacyDeviceOnUSB3( $DeviceInstanceId, $ParentHubPath, $PortNumber ) { $deviceSet = New-Object Microsoft.Windows.Diagnosis.DeviceSet($DeviceInstanceId) # Cannot ask user to unplug the device if it is internal. if ($deviceSet.IsExternal() -eq $false) { return $false } # Device must be downstream of xhci. if (($deviceSet.GetCeipRegValue("DeviceInformation") -band 0x8) -ne 0x8) { return $false } # If device is not SuperSpeed capable then it is a legacy device. $superSpeedChecker = New-Object Microsoft.Windows.Diagnosis.SuperSpeedChecker($ParentHubPath, $PortNumber) if ($superSpeedChecker.IsSuperSpeed() -eq $true) { return $false } return $true } Add-Type -TypeDefinition @" using System; using System.Text; using System.Collections.Generic; using System.Runtime.InteropServices; namespace Microsoft.Windows.Diagnosis { // DeviceSet provides some SetupApi operations (and similar) on a device // Examples: // GetAddress: gets the "address" device property // GetParentId: gets the device instance ID of the parent // GetSoftwareRegValue: gets a registry value at device's software key public class DeviceSet { // Indicates whether the constructor succeeded. Cannot become true if constructor failed. public bool Initialized { get { return (deviceSetHandle != INVALID_HANDLE_VALUE); } } private IntPtr deviceSetHandle; // internal handle for unmanaged SetupApi calls, Initialized determines validity private SP_DEVINFO_DATA deviceInfoData; // internal cached device data, $null if not valid // Constructor. Parameter: device instance ID string for the desired device public DeviceSet(string DevinstId) { deviceInfoData = null; deviceSetHandle = SetupDiGetClassDevs(IntPtr.Zero, // no class GUID, we want a specific device DevinstId, // "enumerator" can instead be device instance id IntPtr.Zero, // no GUI window (DIGCF_DEVICEINTERFACE | // set this to indicate "enumerator" is a device instance id, counterintuitively DIGCF_PRESENT | // we won't deal with missing devices DIGCF_ALLCLASSES // allows us to not specify a class in 1st param ) ); // On failure, deviceSetHandle is INVALID_HANDLE_VALUE and Initialized is false. } // Get the "Address" property of the devnode. // On failure 0 is returned. (0 may be a valid address but not as a USB port number) // For USB-enumerated devices, "Address" is the port number the device is attached to on its parent hub. public UInt32 GetAddress() { if (!Initialized) { return 0; } if (deviceInfoData == null) { GetDeviceInfoData(); // fill in deviceInfoData if (deviceInfoData == null) { // some failure occurred filling in deviceInfoData. return 0; } } UInt32 deviceAddress=0; bool callSuccess; UInt32 propertyType; // non-optional "out" for the following call (thrown away) callSuccess = SetupDiGetDeviceProperty(deviceSetHandle, deviceInfoData, DEVPKEY_Device_Address, out propertyType, out deviceAddress, (UInt32) Marshal.SizeOf(deviceAddress), IntPtr.Zero, // don't want RequiredSize 0 // MBZ ); if (!callSuccess) { return 0; } return deviceAddress; } // Get the "Install Date" property of the devnode (DEVPKEY_Device_InstallDate). // The returned time is in local time (Kind == Local). // On failure DateTime.MinValue is returned. public DateTime GetInstallDate() { if (!Initialized) { return DateTime.MinValue; } if (deviceInfoData == null) { GetDeviceInfoData(); // fill in deviceInfoData if (deviceInfoData == null) { // some failure occurred filling in deviceInfoData. return DateTime.MinValue; } } MY_FILETIME installDate = new MY_FILETIME(); bool callSuccess; UInt32 propertyType; // non-optional "out" for the following call (thrown away) callSuccess = SetupDiGetDeviceProperty(deviceSetHandle, deviceInfoData, DEVPKEY_Device_InstallDate, out propertyType, installDate, (UInt32) Marshal.SizeOf(installDate), IntPtr.Zero, // don't want RequiredSize 0 // MBZ ); if (!callSuccess) { return DateTime.MinValue; } return installDate.ToLocalDateTime(); } public string GetDescription() { bool callSuccess; string emptyString = ""; UInt32 propertyType; Int32 requiredSize; if (!Initialized) { return emptyString; } if (deviceInfoData == null) { GetDeviceInfoData(); if (deviceInfoData == null) { return emptyString; } } requiredSize = 0; callSuccess = SetupDiGetDeviceProperty(deviceSetHandle, deviceInfoData, DEVPKEY_Device_DeviceDesc, out propertyType, IntPtr.Zero, 0, out requiredSize, 0); if (!((callSuccess == false) && (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)) || (requiredSize == 0)) { return emptyString; } StringBuilder description = new StringBuilder(requiredSize / 2); callSuccess = SetupDiGetDeviceProperty(deviceSetHandle, deviceInfoData, DEVPKEY_Device_DeviceDesc, out propertyType, description, requiredSize, IntPtr.Zero, 0); if (!callSuccess) { return emptyString; } return description.ToString(); } // Wrapper function for GetDeviceRegValue // See GetDeviceRegValue public UInt32 GetHardwareRegValue(string ValueName) { return GetDeviceRegValue(ValueName, EDeviceKeyType.HardwareKey); } // Wrapper function for GetDeviceRegValue // See GetDeviceRegValue public UInt32 GetSoftwareRegValue(string ValueName) { return GetDeviceRegValue(ValueName, EDeviceKeyType.SoftwareKey); } // Parameter for GetDeviceRegValue private enum EDeviceKeyType { HardwareKey, // also known as device key, under HKLM\System\CCS\Enum SoftwareKey, // also known as driver key (but still per-device), under HKLM\System\CCS\Control\Class }; // Get a registry value from the software or hardware registry key of the device in the set. // On failure 0 is returned (0 is also a valid registry value). // Parameter: The name of the desired registry value. // This function is expected to be used for values of type REG_DWORD types, but // it may succeed on other types if the data is no larger than a DWORD. private UInt32 GetDeviceRegValue(string ValueName, EDeviceKeyType KeyType) { if (!Initialized) { return 0; } if (deviceInfoData == null) { GetDeviceInfoData(); // fill in deviceInfoData if (deviceInfoData == null) { // some failure occurred filling in deviceInfoData. return 0; } } UInt32 keyTypeForSetupDi = DIREG_DRV; // software key if (KeyType == EDeviceKeyType.HardwareKey) { keyTypeForSetupDi = DIREG_DEV; // hardware key } UInt32 deviceKey = SetupDiOpenDevRegKey(deviceSetHandle, deviceInfoData, DICS_FLAG_GLOBAL, // use HKLM 0, // (ignored for DICS_FLAG_GLOBAL) keyTypeForSetupDi, KEY_QUERY_VALUE // desired key access ); if (deviceKey == HKEY__INVALID_HANDLE_VALUE) { return 0; } UInt32 valueData = 0; UInt32 dataSize = (UInt32) Marshal.SizeOf(valueData); UInt32 callReturn = RegQueryValueEx(deviceKey, ValueName, IntPtr.Zero, // MBZ IntPtr.Zero, // discard type out valueData, ref dataSize ); UInt32 returnValue; if (callReturn == ERROR_SUCCESS) { returnValue = valueData; } else { returnValue = 0; } RegCloseKey(deviceKey); return returnValue; } public UInt32 GetCeipRegValue(string ValueName) { UInt32 callReturn; UInt32 ceipKey; UInt32 dataSize; UInt32 deviceKey; UInt32 returnValue; UInt32 valueData; if (!Initialized) { return 0; } if (deviceInfoData == null) { GetDeviceInfoData(); if (deviceInfoData == null) { return 0; } } deviceKey = SetupDiOpenDevRegKey(deviceSetHandle, deviceInfoData, DICS_FLAG_GLOBAL, // use HKLM 0, // (ignored for DICS_FLAG_GLOBAL) DIREG_DEV, KEY_READ); // desired key access if (deviceKey == HKEY__INVALID_HANDLE_VALUE) { return 0; } callReturn = RegOpenKeyEx(deviceKey, "Ceip", 0, KEY_QUERY_VALUE, out ceipKey); RegCloseKey(deviceKey); if (callReturn != ERROR_SUCCESS) { return 0; } valueData = 0; dataSize = (UInt32) Marshal.SizeOf(valueData); callReturn = RegQueryValueEx(ceipKey, ValueName, IntPtr.Zero, // MBZ IntPtr.Zero, // discard type out valueData, ref dataSize); if (callReturn == ERROR_SUCCESS) { returnValue = valueData; } else { returnValue = 0; } RegCloseKey(ceipKey); return returnValue; } // Get the device instance ID string of the device's parent. // On failure the empty string is returned. public string GetParentId() { if (!Initialized) { return ""; } if (deviceInfoData == null) { GetDeviceInfoData(); // fill in deviceInfoData if (deviceInfoData == null) { // some failure occurred filling in deviceInfoData. return ""; } } UInt32 callResult; UInt32 parentDevinst; callResult = CM_Get_Parent(out parentDevinst, deviceInfoData.DevInst, // device to get parent of 0 // MBZ ); if (CR_SUCCESS != callResult) { return ""; } return DeviceIdFromCmDevinst(parentDevinst); } // Wrapper for recursive function GetAllDescendantDevices(UInt32) // See overload GetAllDescendantDevices(UInt32) // This function calls the overload with parameter = "this" (the DeviceSet's devnode) public List GetAllDescendantDevices() { List descendantDeviceIds = new List(); if (!Initialized) { return descendantDeviceIds; // return empty list on failure } if (deviceInfoData == null) { GetDeviceInfoData(); // fill in deviceInfoData if (deviceInfoData == null) { // some failure occurred filling in deviceInfoData. return descendantDeviceIds; // return empty list on failure } } return GetAllDescendantDevices(deviceInfoData.DevInst); } // Get a list of all devices in the device tree enumerated under the given device (the "root"). // The root device is included in the list returned. // Each device is returned as a device instance ID (string). // On failures, some devices will be omitted. Therefore an empty list may be returned. private static List GetAllDescendantDevices(UInt32 rootDevinst) { List descendantDeviceIds = new List(); // Get device ID for this "root" device. string rootDeviceId = DeviceIdFromCmDevinst(rootDevinst); if (rootDeviceId == "") { return descendantDeviceIds; // return empty list on failure } descendantDeviceIds.Add(rootDeviceId); // Call self recursively on each child of this device, // adding each child's list of descendants to our list. bool moreChildrenExist = true; UInt32 callResult; UInt32 nextChildDevinst; // Get first child. callResult = CM_Get_Child(out nextChildDevinst, rootDevinst, // device to get child of 0 // MBZ ); if (CR_SUCCESS != callResult) { moreChildrenExist = false; } // Loop over first child and first child's siblings. while(moreChildrenExist) { UInt32 currentChildDevinst = nextChildDevinst; List currentChildDescendants = GetAllDescendantDevices(currentChildDevinst); descendantDeviceIds.AddRange(currentChildDescendants); // Iterate to next sibling. callResult = CM_Get_Sibling(out nextChildDevinst, currentChildDevinst, // device to get sibling of 0 // MBZ ); if (CR_SUCCESS != callResult) { moreChildrenExist = false; } } return descendantDeviceIds; } // Returns the device instance ID corresponding to the given // "DevInst" UInt32 value (comes from configuration manager calls). // On failure the empty string is returned. private static string DeviceIdFromCmDevinst(UInt32 devinst) { UInt32 callResult; UInt32 parentIdLength; callResult = CM_Get_Device_ID_Size(out parentIdLength, devinst, 0 // MBZ ); if (CR_SUCCESS != callResult) { return ""; } StringBuilder parentId = new StringBuilder(); parentId.Capacity = (Int32)parentIdLength + 1; // empirically, space for the null terminator is required for marshalling! callResult = CM_Get_Device_ID(devinst, parentId, // out (UInt32) parentId.Capacity, 0 // MBZ ); if (CR_SUCCESS != callResult) { return ""; } // All calls succeeded. return parentId.ToString(); } // Get the device path of the device in the set. // On failure the empty string is returned. // This function is specific to USB hubs! // The device path can be used to open a handle to the device and send IOCTLs. public string GetHubDevicePath() { bool callSuccess; SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); callSuccess = SetupDiEnumDeviceInterfaces(deviceSetHandle, IntPtr.Zero, // only 1 device in set, no need to specify which device ref GUID_DEVINTERFACE_USB_HUB, 0, // only 1 instance of the interface deviceInterfaceData // in/out ); if (!callSuccess) { return ""; } SP_DEVICE_INTERFACE_DETAIL_DATA detailData = new SP_DEVICE_INTERFACE_DETAIL_DATA(); callSuccess = SetupDiGetDeviceInterfaceDetailW(deviceSetHandle, deviceInterfaceData, detailData, (UInt32) Marshal.SizeOf(detailData), IntPtr.Zero, IntPtr.Zero ); if (!callSuccess) { return ""; } return detailData.DevicePath.ToString(); } public bool IsExternal() { bool callSuccess; UInt32 propertyType; Guid rootContainerId = new Guid(); Guid deviceContainerId = new Guid(); SP_DEVINFO_DATA rootDevice = new SP_DEVINFO_DATA(); if (!Initialized) { return false; } if (deviceInfoData == null) { GetDeviceInfoData(); if (deviceInfoData == null) { return false; } } callSuccess = SetupDiOpenDeviceInfo(deviceSetHandle, REGSTR_VAL_ROOT_DEVNODE, IntPtr.Zero, 0, rootDevice); if (!callSuccess) { return false; } callSuccess = SetupDiGetDeviceProperty(deviceSetHandle, rootDevice, DEVPKEY_Device_ContainerId, out propertyType, ref rootContainerId, (UInt32) Marshal.SizeOf(rootContainerId), IntPtr.Zero, 0); if (!callSuccess) { return false; } callSuccess = SetupDiGetDeviceProperty(deviceSetHandle, deviceInfoData, DEVPKEY_Device_ContainerId, out propertyType, ref deviceContainerId, (UInt32) Marshal.SizeOf(deviceContainerId), IntPtr.Zero, 0); if (!callSuccess) { return false; } return (rootContainerId != deviceContainerId); } // Internal call to fill in private member deviceInfoData. // On failure, deviceInfoData is unchanged (it starts as null). private void GetDeviceInfoData() { if (!Initialized) { return; } SP_DEVINFO_DATA tmpDeviceInfoData = new SP_DEVINFO_DATA(); bool callSuccess; callSuccess = SetupDiEnumDeviceInfo(deviceSetHandle, 0, // first and only device in the set tmpDeviceInfoData // in/out ); if (callSuccess) { deviceInfoData = tmpDeviceInfoData; } } ~DeviceSet() { if (INVALID_HANDLE_VALUE != deviceSetHandle) { SetupDiDestroyDeviceInfoList(deviceSetHandle); deviceSetHandle = INVALID_HANDLE_VALUE; } } // from setupapi.h // // // // Values specifying the scope of a device property change // // // #define DICS_FLAG_GLOBAL 0x00000001 // make change in all hardware profiles // #define DICS_FLAG_CONFIGSPECIFIC 0x00000002 // make change in specified profile only // #define DICS_FLAG_CONFIGGENERAL 0x00000004 // 1 or more hardware profile-specific // // changes to follow. // ... // // // // Flags controlling what is included in the device information set built // // by SetupDiGetClassDevs // // // #define DIGCF_DEFAULT 0x00000001 // only valid with DIGCF_DEVICEINTERFACE // #define DIGCF_PRESENT 0x00000002 // #define DIGCF_ALLCLASSES 0x00000004 // #define DIGCF_PROFILE 0x00000008 // #define DIGCF_DEVICEINTERFACE 0x00000010 // ... // // // // KeyType values for SetupDiCreateDevRegKey, SetupDiOpenDevRegKey, and // // SetupDiDeleteDevRegKey. // // // #define DIREG_DEV 0x00000001 // Open/Create/Delete device key // #define DIREG_DRV 0x00000002 // Open/Create/Delete driver key // #define DIREG_BOTH 0x00000004 // Delete both driver and Device key private const UInt32 DICS_FLAG_GLOBAL = 0x00000001; private const UInt32 DICS_FLAG_CONFIGSPECIFIC = 0x00000002; private const UInt32 DICS_FLAG_CONFIGGENERAL = 0x00000004; private const UInt32 DIGCF_DEFAULT = 0x00000001; private const UInt32 DIGCF_PRESENT = 0x00000002; private const UInt32 DIGCF_ALLCLASSES = 0x00000004; private const UInt32 DIGCF_PROFILE = 0x00000008; private const UInt32 DIGCF_DEVICEINTERFACE = 0x00000010; private const UInt32 DIREG_DEV = 0x00000001; private const UInt32 DIREG_DRV = 0x00000002; private const UInt32 DIREG_BOTH = 0x00000004; // from devpkey.h // DEFINE_DEVPROPKEY(DEVPKEY_Device_Address, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 30); // DEVPROP_TYPE_UINT32 // DEFINE_DEVPROPKEY(DEVPKEY_Device_InstallDate, 0x83da6326, 0x97a6, 0x4088, 0x94, 0x53, 0xa1, 0x92, 0x3f, 0x57, 0x3b, 0x29, 100); // DEVPROP_TYPE_FILETIME // DEFINE_DEVPROPKEY(DEVPKEY_Device_ContainerId, 0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c, 2); // DEVPROP_TYPE_GUID // DEFINE_DEVPROPKEY(DEVPKEY_DeviceContainer_FriendlyName, 0x656A3BB3, 0xECC0, 0x43FD, 0x84, 0x77, 0x4A, 0xE0, 0x40, 0x4A, 0x96, 0xCD, 12288); // DEVPROP_TYPE_STRING // DEFINE_DEVPROPKEY(DEVPKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING // DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING // DEFINE_DEVPROPKEY(DEVPKEY_Device_Manufacturer, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 13); // DEVPROP_TYPE_STRING private static DEVPROPKEY DEVPKEY_Device_Address = new DEVPROPKEY(new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 30); private static DEVPROPKEY DEVPKEY_Device_InstallDate = new DEVPROPKEY(new Guid(0x83da6326, 0x97a6, 0x4088, 0x94, 0x53, 0xa1, 0x92, 0x3f, 0x57, 0x3b, 0x29), 100); private static DEVPROPKEY DEVPKEY_Device_ContainerId = new DEVPROPKEY(new Guid(0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c), 2); private static DEVPROPKEY DEVPKEY_DeviceContainer_FriendlyName = new DEVPROPKEY(new Guid(0x656A3BB3, 0xECC0, 0x43FD, 0x84, 0x77, 0x4A, 0xE0, 0x40, 0x4A, 0x96, 0xCD), 12288); private static DEVPROPKEY DEVPKEY_Device_DeviceDesc = new DEVPROPKEY(new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 2); private static DEVPROPKEY DEVPKEY_Device_FriendlyName = new DEVPROPKEY(new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 14); private static DEVPROPKEY DEVPKEY_Device_Manufacturer = new DEVPROPKEY(new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 13); // from cfgmgr32.h // #define CR_SUCCESS (0x00000000) private const UInt32 CR_SUCCESS = 0x00000000; // from winbase.h // #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) private static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); // Use HKEY__INVALID_HANDLE_VALUE to compare HKEY (DWORD); it's different from HANDLE (PVOID) private const UInt32 HKEY__INVALID_HANDLE_VALUE = unchecked((UInt32)(-1)); // from winerror.h // #define ERROR_SUCCESS 0 // #define ERROR_INSUFFICIENT_BUFFER 122L private const UInt32 ERROR_SUCCESS = 0; private const UInt32 ERROR_INSUFFICIENT_BUFFER = 122; // from winnt.h // #define KEY_QUERY_VALUE (0x0001) // #define KEY_SET_VALUE (0x0002) // #define KEY_CREATE_SUB_KEY (0x0004) // #define KEY_ENUMERATE_SUB_KEYS (0x0008) // #define KEY_NOTIFY (0x0010) // #define KEY_CREATE_LINK (0x0020) // #define KEY_WOW64_32KEY (0x0200) // #define KEY_WOW64_64KEY (0x0100) // #define KEY_WOW64_RES (0x0300) private const UInt32 KEY_QUERY_VALUE = 0x0001; private const UInt32 KEY_SET_VALUE = 0x0002; private const UInt32 KEY_CREATE_SUB_KEY = 0x0004; private const UInt32 KEY_ENUMERATE_SUB_KEYS = 0x0008; private const UInt32 KEY_NOTIFY = 0x0010; private const UInt32 KEY_CREATE_LINK = 0x0020; private const UInt32 KEY_WOW64_32KEY = 0x0200; private const UInt32 KEY_WOW64_64KEY = 0x0100; private const UInt32 KEY_WOW64_RES = 0x0300; private const UInt32 STANDARD_RIGHTS_READ = 0x00020000; private const UInt32 SYNCHRONIZE = 0x00100000; private const UInt32 KEY_READ = ((STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & (~SYNCHRONIZE)); // from usbiodef.h // DEFINE_GUID(GUID_DEVINTERFACE_USB_HUB, 0xf18a0e88, 0xc30c, 0x11d0, 0x88, 0x15, 0x00, \ // 0xa0, 0xc9, 0x06, 0xbe, 0xd8); private static Guid GUID_DEVINTERFACE_USB_HUB = new Guid(0xf18a0e88, 0xc30c, 0x11d0, 0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8); // from regstr.h // #define REGSTR_VAL_ROOT_DEVNODE TEXT("HTREE\\ROOT\\0") private static string REGSTR_VAL_ROOT_DEVNODE = "HTREE\\ROOT\\0"; [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern IntPtr // handle, compare to INVALID_HANDLE_VALUE SetupDiGetClassDevs( IntPtr ClassGuid, // in, optional; this is an IntPtr so we can pass null; to pass a GUID in, write an overloaded declaration same as this but where the type is "ref Guid" string Enumerator, // enumerator or device instance ID IntPtr hwndParent, // in, optional, parent window; IntPtr type is for passing null UInt32 Flags ); [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiDestroyDeviceInfoList( IntPtr DeviceInfoSet ); [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiEnumDeviceInfo( IntPtr DeviceInfoSet, UInt32 MemberIndex, SP_DEVINFO_DATA DeviceInfoData // in/out ); [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiOpenDeviceInfo( IntPtr DeviceInfoSet, string DeviceInstanceId, IntPtr hwndParent, UInt32 OpenFlags, SP_DEVINFO_DATA DeviceInfoData // in/out ); // Overload to retrieve UInt32-type property [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiGetDeviceProperty( IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, // in DEVPROPKEY PropertyKey, // in out UInt32 PropertyType, out UInt32 PropertyBuffer, // this overload retrieves UInt32 properties only UInt32 PropertyBufferSize, // PropertyBuffer's size (bytes) IntPtr RequiredSize, // this is an IntPtr so we can pass null; to pass a PDWORD in, write an overloaded declaration same as this but where the type is "ref UInt32" UInt32 Flags // MBZ ); // Overload to retrieve FILETIME-type property [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiGetDeviceProperty( IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, // in DEVPROPKEY PropertyKey, // in out UInt32 PropertyType, MY_FILETIME PropertyBuffer, // out. this overload retrieves FILETIME properties only UInt32 PropertyBufferSize, // PropertyBuffer's size (bytes) IntPtr RequiredSize, // this is an IntPtr so we can pass null; to pass a PDWORD in, write an overloaded declaration same as this but where the type is "ref UInt32" UInt32 Flags // MBZ ); // Overload to retrieve FILETIME-type property [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiGetDeviceProperty( IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, // in DEVPROPKEY PropertyKey, // in out UInt32 PropertyType, ref Guid PropertyBuffer, // out. this overload retrieves FILETIME properties only UInt32 PropertyBufferSize, // PropertyBuffer's size (bytes) IntPtr RequiredSize, // this is an IntPtr so we can pass null; to pass a PDWORD in, write an overloaded declaration same as this but where the type is "ref UInt32" UInt32 Flags // MBZ ); // Overload to retrieve FILETIME-type property [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiGetDeviceProperty( IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, // in DEVPROPKEY PropertyKey, // in out UInt32 PropertyType, IntPtr PropertyBuffer, // out. this overload retrieves FILETIME properties only UInt32 PropertyBufferSize, // PropertyBuffer's size (bytes) out Int32 RequiredSize, // this is an IntPtr so we can pass null; to pass a PDWORD in, write an overloaded declaration same as this but where the type is "ref UInt32" UInt32 Flags // MBZ ); // Overload to retrieve FILETIME-type property [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiGetDeviceProperty( IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, // in DEVPROPKEY PropertyKey, // in out UInt32 PropertyType, StringBuilder PropertyBuffer, // out. this overload retrieves FILETIME properties only Int32 PropertyBufferSize, // PropertyBuffer's size (bytes) IntPtr RequiredSize, // this is an IntPtr so we can pass null; to pass a PDWORD in, write an overloaded declaration same as this but where the type is "ref UInt32" UInt32 Flags // MBZ ); [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern UInt32 // HKEY SetupDiOpenDevRegKey( IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, // in UInt32 Scope, UInt32 HwProfile, UInt32 KeyType, UInt32 samDesired ); [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success SetupDiEnumDeviceInterfaces( IntPtr DeviceInfoSet, IntPtr DeviceInfoData, // this is an IntPtr so we can pass null; to pass an SP_DEVINFO_DATA in, write an overloaded declaration ref Guid InterfaceClassGuid, // in, Device Interface Class UInt32 MemberIndex, // non-zero only for multiple instances of a single interface SP_DEVICE_INTERFACE_DATA DeviceInterfaceData // out ); [DllImport("cfgmgr32.dll", CharSet=CharSet.Auto)] private static extern UInt32 // compare to CR_SUCCESS CM_Get_Parent( out UInt32 pdnDevInst, // the parent UInt32 dnDevInst, // in, the child UInt32 ulFlags // MBZ ); [DllImport("cfgmgr32.dll", CharSet=CharSet.Auto)] private static extern UInt32 // compare to CR_SUCCESS CM_Get_Child( out UInt32 pdnDevInst, // the 1st child UInt32 dnDevInst, // in, the parent UInt32 ulFlags // MBZ ); [DllImport("cfgmgr32.dll", CharSet=CharSet.Auto)] private static extern UInt32 // compare to CR_SUCCESS CM_Get_Sibling( out UInt32 pdnDevInst, // the next sibling UInt32 dnDevInst, // in, the current sibling UInt32 ulFlags // MBZ ); [DllImport("cfgmgr32.dll", CharSet=CharSet.Auto)] private static extern UInt32 // compare to CR_SUCCESS CM_Get_Device_ID_Size( out UInt32 pulLen, // length in characters without null terminator UInt32 dnDevInst, // in, the device UInt32 ulFlags // MBZ ); [DllImport("cfgmgr32.dll", CharSet=CharSet.Auto)] private static extern UInt32 // compare to CR_SUCCESS CM_Get_Device_ID( UInt32 dnDevInst, // in, the device StringBuilder Buffer, // out, device instance id (string) UInt32 BufferLen, // in, character capacity of Buffer UInt32 ulFlags // MBZ ); [DllImport("advapi32.dll", CharSet=CharSet.Auto)] private static extern UInt32 // compare to ERROR_SUCCESS RegQueryValueEx( UInt32 hKey, string lpValueName, IntPtr lpReserved, // MBZ IntPtr lpType, // this is an IntPtr so we can pass null; to pass an "out UInt32", write an overloaded declaration out UInt32 lpData, // arbitrary size buffer forced to 4 bytes on this overload ref UInt32 lpcbData // size of lpData buffer in bytes ); [DllImport("advapi32.dll", CharSet=CharSet.Auto)] private static extern UInt32 // compare to ERROR_SUCCESS RegOpenKeyEx( UInt32 hKey, string lpSubKey, UInt32 ulOptions, UInt32 samDesired, out UInt32 phkResult ); [DllImport("advapi32.dll", CharSet=CharSet.Auto)] private static extern UInt32 // compare to ERROR_SUCCESS RegCloseKey( UInt32 hKey ); [StructLayout(LayoutKind.Sequential)] private class SP_DEVINFO_DATA { public UInt32 cbSize; // Must be set to sizeof(SP_DEVINFO_DATA) by us, for calling of SetupApi functions public Guid ClassGuid; // Device's setup class public UInt32 DevInst; // DWORD version of device instance id public IntPtr Reserved; public SP_DEVINFO_DATA() { // Constructor sets required cbSize. cbSize = (UInt32) Marshal.SizeOf(this); } }; [StructLayout(LayoutKind.Sequential)] private class DEVPROPKEY { public Guid fmtid; // property category public UInt32 pid; // property public DEVPROPKEY(Guid a_fmtid, UInt32 a_pid) { fmtid = a_fmtid; pid = a_pid; } } [StructLayout(LayoutKind.Sequential)] private class SP_DEVICE_INTERFACE_DATA { public UInt32 cbSize; // Must be set to sizeof(SP_DEVICE_INTERFACE_DATA) by us, for calling SetupApi functions public Guid InterfaceClassGuid; // interface's device interface class public UInt32 Flags; // active, default, or removed (not defined here) IntPtr Reserved; public SP_DEVICE_INTERFACE_DATA() { // Constructor sets required cbSize. cbSize = (UInt32) Marshal.SizeOf(this); } } // System.Runtime.InteropServices.ComTypes.FILETIME uses signed rather than // unsigned 32-bit integer members. MY_FILETIME corresponds better to the // Windows struct. [StructLayout(LayoutKind.Sequential)] private class MY_FILETIME { public UInt32 dwLowDateTime; public UInt32 dwHighDateTime; // Convert the MY_FILETIME struct to a DateTime public DateTime ToLocalDateTime() { UInt64 installFiletimeHighOnly = ((UInt64) dwHighDateTime) << 32; UInt64 installFiletimeLowOnly = (UInt64) dwLowDateTime; UInt64 installFiletime = installFiletimeHighOnly | installFiletimeLowOnly; // Convert FILETIME Int64 to a DateTime storing local time return DateTime.FromFileTime((Int64)installFiletime); } } // Supporting device interface detail (next extern and class) is a little extra complex. // 1) Size of the class/struct must be specified and match C behavior. // Problem: The struct includes a TCHAR and is packed differently on 32 vs 64 bit // Simplification: Use Unicode explicitly so there are fewer possibilties for the size // Hack: Explicitly write cbSize as 6 or 8 bytes // SetupApi uses pack(1) in 32-bit => 6 bytes assuming Unicode // SetupApi uses pack(8) in 64-bit => 8 bytes // 2) The C struct is variable-sized. // Option 1: Dynamically allocate a buffer and use pointer arithmetic // Option 2: Assume string at the end of the struct won't exceed MAX_PATH length (260) // It's not clear if this assumption is always correct but there's no // reason for a USB hub path to come close to that length. // We take option 2 as it's much simpler. // This overload is for getting the interface detail data buffer. [DllImport("setupapi.dll", CharSet=CharSet.Unicode, SetLastError=true)] private static extern bool // true indicates success SetupDiGetDeviceInterfaceDetailW( IntPtr DeviceInfoSet, SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, // in, comes from SetupDiEnumDeviceInterfaces [Out][In] SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, UInt32 DeviceInterfaceDetailDataSize, // use 0 if DeviceInterfaceDetailData is null, otherwise the size of that buffer in bytes IntPtr RequiredSize, // out and optional, when using this overload the required size is already known IntPtr DeviceInfoData // out and optional, this overload is for passing null here ); [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] // explicit use of Unicode private class SP_DEVICE_INTERFACE_DETAIL_DATA { public UInt32 cbSize; // Must be set to sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) by us, for calling SetupApi functions. [MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)] // this max length is assumed public string DevicePath; // out public SP_DEVICE_INTERFACE_DETAIL_DATA() { // Constructor sets required cbSize. // cbSize is tricky due to packing differences on 32 vs 64 bit. if (IntPtr.Size == 4) { cbSize = 6; } else { cbSize = 8; } } } } // Sends a cycle port IOCTL for the USB device. public class CyclePort { private string hubDevicePath; private UInt32 devicePortNumber; // Sets up the class to be able to call Send. public CyclePort(string a_hubDevicePath, UInt32 a_devicePortNumber) { hubDevicePath = a_hubDevicePath; devicePortNumber = a_devicePortNumber; // We won't open the file handle here since it is needed only in Send. } // Sends the cycle port ioctl. // Return value: true on success. public bool Send() { IntPtr deviceHandle = CreateFile(hubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero ); if (deviceHandle == INVALID_HANDLE_VALUE) { return false; } USB_CYCLE_PORT_PARAMS ioctlParams = new USB_CYCLE_PORT_PARAMS(devicePortNumber); UInt32 bytesReturned; // non-optional "out" for the following call (thrown away) bool isIoctlSuccessful = DeviceIoControl(deviceHandle, IOCTL_USB_HUB_CYCLE_PORT, ioctlParams, // in (UInt32) Marshal.SizeOf(ioctlParams), ioctlParams, // out (UInt32) Marshal.SizeOf(ioctlParams), out bytesReturned, IntPtr.Zero ); if (isIoctlSuccessful && ioctlParams.StatusReturned != STATUS_SUCCESS) { // success returned by DeviceIoControl call, but NOT in the ioctl data isIoctlSuccessful = false; } CloseHandle(deviceHandle); return isIoctlSuccessful; } // from winbase.h // #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) // ... // #define OPEN_EXISTING 3 private static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); private const UInt32 OPEN_EXISTING = 3; // from ntstatus.h // #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth private const UInt32 STATUS_SUCCESS = 0x00000000; // from winnt.h // #define GENERIC_WRITE (0x40000000L) // ... // #define FILE_SHARE_READ 0x00000001 // #define FILE_SHARE_WRITE 0x00000002 // #define FILE_SHARE_DELETE 0x00000004 private const UInt32 GENERIC_WRITE = 0x40000000; private const UInt32 FILE_SHARE_READ = 0x00000001; private const UInt32 FILE_SHARE_WRITE = 0x00000002; private const UInt32 FILE_SHARE_DELETE = 0x00000004; // from usbioctl.h // #define IOCTL_USB_HUB_CYCLE_PORT \ // CTL_CODE(FILE_DEVICE_USB, \ // USB_HUB_CYCLE_PORT, \ // METHOD_BUFFERED, \ // FILE_ANY_ACCESS) private static UInt32 IOCTL_USB_HUB_CYCLE_PORT = CTL_CODE(FILE_DEVICE_USB, USB_HUB_CYCLE_PORT, METHOD_BUFFERED, FILE_ANY_ACCESS ); // from usbiodef.h // #define USB_HUB_CYCLE_PORT 273 // ... // #define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN private const UInt32 USB_HUB_CYCLE_PORT = 273; private const UInt32 FILE_DEVICE_USB = FILE_DEVICE_UNKNOWN; // from winioctl.h // #define FILE_DEVICE_UNKNOWN 0x00000022 // ... // #define METHOD_BUFFERED 0 // ... // #define FILE_ANY_ACCESS 0 private const UInt32 FILE_DEVICE_UNKNOWN = 0x00000022; private const UInt32 METHOD_BUFFERED = 0; private const UInt32 FILE_ANY_ACCESS = 0; // from winioctl.h // #define CTL_CODE( DeviceType, Function, Method, Access ) ( \ // ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ // ) private static UInt32 CTL_CODE(UInt32 DeviceType, UInt32 Function, UInt32 Method, UInt32 Access) { return (DeviceType << 16) | (Access << 14) | (Function << 2) | Method; } [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern IntPtr // handle, compare to INVALID_HANDLE_VALUE CreateFile( string lpFileName, // file name or device path UInt32 dwDesiredAccess, // specify GENERIC_READ and/or GENERIC_WRITE (bitwise) UInt32 dwShareMode, IntPtr lpSecurityAttributes, // in, optional, security descriptor; this is an IntPtr so we can pass null, otherwise an overloaded declaration is required UInt32 dwCreationDisposition, // whether to create a new file (e.g. OPEN_EXISTING) UInt32 dwFlagsAndAttributes, // in case of open existing, flags only IntPtr hTemplateFile // in, optional(null) ); [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success CloseHandle( IntPtr hObject // in ); // DeviceIoControl overload for IOCTL_USB_HUB_CYCLE_PORT [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool // true indicates success DeviceIoControl( IntPtr hDevice, // in, device handle UInt32 dwIoControlCode, USB_CYCLE_PORT_PARAMS lpInBuffer, // in, optional UInt32 nInBufferSize, USB_CYCLE_PORT_PARAMS lpOutBuffer, // out, optional UInt32 nOutBufferSize, out UInt32 lpBytesReturned, // not optional if lpOverlapped is null IntPtr lpOverlapped // in/out, optional ); // Based on usbioctl.h [StructLayout(LayoutKind.Sequential)] private class USB_CYCLE_PORT_PARAMS { public UInt32 ConnectionIndex; // in, one-based port number public UInt32 StatusReturned = 0; // out // Constructor requires a port number. public USB_CYCLE_PORT_PARAMS(UInt32 portNumber) { ConnectionIndex = portNumber; } } } // Sends a cycle port IOCTL for the USB device. public class SuperSpeedChecker { private string hubDevicePath; private UInt32 devicePortNumber; // Sets up the class to be able to call Send. public SuperSpeedChecker(string a_hubDevicePath, UInt32 a_devicePortNumber) { hubDevicePath = a_hubDevicePath; devicePortNumber = a_devicePortNumber; } public bool IsSuperSpeed() { UInt32 bytesReturned; IntPtr deviceHandle; bool ioctlSuccess; USB_NODE_CONNECTION_INFORMATION_EX_V2 ioctlParams = new USB_NODE_CONNECTION_INFORMATION_EX_V2(devicePortNumber); deviceHandle = CreateFile(hubDevicePath, 0, FILE_SHARE_WRITE | FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); if (deviceHandle == INVALID_HANDLE_VALUE) { return false; } ioctlSuccess = DeviceIoControl(deviceHandle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, ioctlParams, (UInt32) Marshal.SizeOf(ioctlParams), ioctlParams, (UInt32) Marshal.SizeOf(ioctlParams), out bytesReturned, IntPtr.Zero); CloseHandle(deviceHandle); if (!ioctlSuccess) { return false; } return ((ioctlParams.Flags & 0x2) == 2); } // from winbase.h // #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) // ... // #define OPEN_EXISTING 3 private static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); private const UInt32 OPEN_EXISTING = 3; // from winnt.h // #define GENERIC_WRITE (0x40000000L) // ... // #define FILE_SHARE_READ 0x00000001 // #define FILE_SHARE_WRITE 0x00000002 // #define FILE_SHARE_DELETE 0x00000004 private const UInt32 GENERIC_WRITE = 0x40000000; private const UInt32 FILE_SHARE_READ = 0x00000001; private const UInt32 FILE_SHARE_WRITE = 0x00000002; private const UInt32 FILE_SHARE_DELETE = 0x00000004; // from usbiodef.h // #define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279 // ... // #define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN private const UInt32 USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 = 279; private const UInt32 FILE_DEVICE_USB = FILE_DEVICE_UNKNOWN; // from winioctl.h // #define FILE_DEVICE_UNKNOWN 0x00000022 // ... // #define METHOD_BUFFERED 0 // ... // #define FILE_ANY_ACCESS 0 private const UInt32 FILE_DEVICE_UNKNOWN = 0x00000022; private const UInt32 METHOD_BUFFERED = 0; private const UInt32 FILE_ANY_ACCESS = 0; // from usbioctl.h // #define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \ // CTL_CODE(FILE_DEVICE_USB, \ // USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, \ // METHOD_BUFFERED, \ // FILE_ANY_ACCESS) private static UInt32 IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 = CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS); // from winioctl.h // #define CTL_CODE( DeviceType, Function, Method, Access ) ( \ // ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ // ) private static UInt32 CTL_CODE(UInt32 DeviceType, UInt32 Function, UInt32 Method, UInt32 Access) { return (DeviceType << 16) | (Access << 14) | (Function << 2) | Method; } [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern IntPtr // handle, compare to INVALID_HANDLE_VALUE CreateFile( string lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile ); [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool CloseHandle( IntPtr hObject ); // DeviceIoControl overload for IOCTL_USB_HUB_CYCLE_PORT [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool DeviceIoControl( IntPtr hDevice, UInt32 dwIoControlCode, USB_NODE_CONNECTION_INFORMATION_EX_V2 lpInBuffer, UInt32 nInBufferSize, USB_NODE_CONNECTION_INFORMATION_EX_V2 lpOutBuffer, UInt32 nOutBufferSize, out UInt32 lpBytesReturned, IntPtr lpOverlapped ); // Based on usbioctl.h [StructLayout(LayoutKind.Sequential)] private class USB_NODE_CONNECTION_INFORMATION_EX_V2 { public UInt32 ConnectionIndex; // in, one-based port number public UInt32 Length; // in public UInt32 SupportedUsbProtocols; // in public UInt32 Flags; // out // Constructor requires a port number. public USB_NODE_CONNECTION_INFORMATION_EX_V2(UInt32 portNumber) { ConnectionIndex = portNumber; Length = (UInt32) Marshal.SizeOf(this); SupportedUsbProtocols = 0x7; Flags = 0; } } } } "@