list/enable/disable USB devices

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

list/enable/disable USB devices

30 Oct 2019, 12:45

- Here is some code to list USB devices, and to enable/disable them.

Code: Select all

q:: ;list USB devices
vBarrier := "=================================================="
vOutputFull := vBarrier "`r`n`r`n"

oArray := JEE_DeviceList("`r`n")
for _, oObj in oArray
{
	vOutput := ""
	for vKey, vValue in oObj
		vOutput .= vKey ":`r`n" vValue (vValue=""?"":"`r`n") "`r`n"
	vOutputFull .= vOutput vBarrier "`r`n`r`n"
	MsgBox, % vOutput
}
Clipboard := SubStr(vOutputFull, 1, -2)
MsgBox, % "done"
return

;==================================================

;w:: ;disable/enable specific USB device
if !A_IsAdmin
{
	Run, % "*RunAs " (A_IsCompiled ? "" : A_AhkPath " ") Chr(34) A_ScriptFullPath Chr(34)
	ExitApp
}

;information from http://www.linux-usb.org/usb.ids:
;13ba: PCPlay
;0017: PS/2 Keyboard+Mouse Adapter

vDeviceID := "USB\VID_13BA&PID_0017&MI_01\7&195d6bc&0&0001"
JEE_DeviceDisable(vDeviceID)
MsgBox, % "disabled"
JEE_DeviceEnable(vDeviceID)
MsgBox, % "enabled"
JEE_DeviceDisableEnable(vDeviceID)
MsgBox, % "disabled + enabled"
return

;==================================================

;loosely based on:
;DllCall to SetupDiEnumDeviceInfo throws "ERROR_INVALID_USER_BUFFER" - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=5&t=40942

;note: can specify a device ID and a state value/array, e.g.:
;vDeviceID := "USB\VID_13BA&PID_0017&MI_01\7&195d6bc&0&0001"
;JEE_DeviceList("", vDeviceID, "DE")

;note: vSep affects vHardwareIDs (delimited string, or set to blank to return an array)

JEE_DeviceList(vSep:=", ", vDeviceIDSetState:="", oState:="")
{
	local
	;DIGCF_DEFAULT := 0x1 ;only valid with DIGCF_DEVICEINTERFACE
	;DIGCF_PRESENT := 0x2
	;DIGCF_ALLCLASSES := 0x4
	;DIGCF_PROFILE := 0x8
	;DIGCF_DEVICEINTERFACE := 0x10

	if !(hModule := DllCall("kernel32\LoadLibrary", "Str","setupapi.dll", "Ptr"))
	|| !(hDevInfo := DllCall("setupapi\SetupDiGetClassDevs", "Ptr",0, "Str","USB", "Ptr",0, "UInt",0x2|0x4, "Ptr"))
		return

	oArray := []
	vSize := A_PtrSize=8?32:28
	VarSetCapacity(SP_DEVINFO_DATA, vSize, 0)
	NumPut(vSize, SP_DEVINFO_DATA, 0, "UInt") ;cbSize

	JEE_PropertyKeyCreate(DEVPKEY_Device_DeviceDesc, "{A45C254E-DF1C-4EFD-8020-67D146A850E0}", 2) ;source: propkey.h
	JEE_PropertyKeyCreate(DEVPKEY_Device_FriendlyName, "{A45C254E-DF1C-4EFD-8020-67D146A850E0}", 14) ;source: propkey.h
	JEE_PropertyKeyCreate(DEVPKEY_Device_HardwareIds, "{A45C254E-DF1C-4EFD-8020-67D146A850E0}", 3) ;source: propkey.h
	JEE_PropertyKeyCreate(DEVPKEY_Device_BusReportedDeviceDesc, "{540B947E-8B40-45BC-A8A2-6A0B894CBDA2}", 4) ;source: propkey.h

	vPropType := vRequiredSize := vLen := 0
	Loop
	{
		if (!DllCall("setupapi\SetupDiEnumDeviceInfo", "Ptr",hDevInfo, "UInt",A_Index-1, "Ptr",&SP_DEVINFO_DATA))
		{
			if (A_LastError != 0x103) ;ERROR_NO_MORE_ITEMS := 0x103
				MsgBox, % "SetupDiEnumDeviceInfo error"
			break
		}

		vDeviceDesc := vFriendlyName := vHardwareIDs := vBusReportedDeviceDesc := vDeviceID := ""

		;get property: device description:
		if (!DllCall("setupapi\SetupDiGetDevicePropertyW", "Ptr",hDevInfo, "Ptr",&SP_DEVINFO_DATA, "Ptr",&DEVPKEY_Device_DeviceDesc, "UInt*",vPropType, "Ptr",0, "UInt",0, "UInt*",vRequiredSize, "UInt",0) && A_LastError == 0x7A) ;ERROR_INSUFFICIENT_BUFFER := 0x7A
		{
			VarSetCapacity(vDeviceDesc, vRequiredSize)
			if (!DllCall("setupapi\SetupDiGetDevicePropertyW", "Ptr",hDevInfo, "Ptr",&SP_DEVINFO_DATA, "Ptr",&DEVPKEY_Device_DeviceDesc, "UInt*",vPropType, "WStr",vDeviceDesc, "UInt",vRequiredSize, "Ptr",0, "UInt",0))
				MsgBox, % "SetupDiGetDevicePropertyW (DEVPKEY_Device_DeviceDesc) error"
		}

		;get property: friendly name:
		if (!DllCall("setupapi\SetupDiGetDevicePropertyW", "Ptr",hDevInfo, "Ptr",&SP_DEVINFO_DATA, "Ptr",&DEVPKEY_Device_FriendlyName, "UInt*",vPropType, "Ptr",0, "UInt",0, "UInt*",vRequiredSize, "UInt",0) && A_LastError == 0x7A) ;ERROR_INSUFFICIENT_BUFFER := 0x7A
		{
			VarSetCapacity(vFriendlyName, vRequiredSize)
			if (!DllCall("setupapi\SetupDiGetDevicePropertyW", "Ptr",hDevInfo, "Ptr",&SP_DEVINFO_DATA, "Ptr",&DEVPKEY_Device_FriendlyName, "UInt*",vPropType, "WStr",vFriendlyName, "UInt",vRequiredSize, "Ptr",0, "UInt",0))
				MsgBox, % "SetupDiGetDevicePropertyW (DEVPKEY_Device_FriendlyName) error"
		}

		;get property: hardware IDs:
		if (!DllCall("setupapi\SetupDiGetDevicePropertyW", "Ptr",hDevInfo, "Ptr",&SP_DEVINFO_DATA, "Ptr",&DEVPKEY_Device_HardwareIds, "UInt*",vPropType, "Ptr",0, "UInt",0, "UInt*",vRequiredSize, "UInt",0) && A_LastError == 0x7A)
		{
			VarSetCapacity(vHardwareIDs, vRequiredSize)
			if (!DllCall("setupapi\SetupDiGetDevicePropertyW", "Ptr",hDevInfo, "Ptr",&SP_DEVINFO_DATA, "Ptr",&DEVPKEY_Device_HardwareIds, "UInt*",vPropType, "Ptr",&vHardwareIDs, "UInt",vRequiredSize, "Ptr",0, "UInt",0))
				MsgBox, % "SetupDiGetDevicePropertyW (DEVPKEY_Device_HardwareIds) error"
			else
				vHardwareIDs := JEE_NSLGet(&vHardwareIDs, "UTF-16", vSep)
		}

		;get property: bus reported device description:
		if (!DllCall("setupapi\SetupDiGetDevicePropertyW", "Ptr",hDevInfo, "Ptr",&SP_DEVINFO_DATA, "Ptr",&DEVPKEY_Device_BusReportedDeviceDesc, "UInt*",vPropType, "Ptr",0, "UInt",0, "UInt*",vRequiredSize, "UInt",0) && A_LastError == 0x7A) ;ERROR_INSUFFICIENT_BUFFER := 0x7A
		{
			VarSetCapacity(vBusReportedDeviceDesc, vRequiredSize)
			if (!DllCall("setupapi\SetupDiGetDevicePropertyW", "Ptr",hDevInfo, "Ptr",&SP_DEVINFO_DATA, "Ptr",&DEVPKEY_Device_BusReportedDeviceDesc, "UInt*",vPropType, "WStr",vBusReportedDeviceDesc, "UInt",vRequiredSize, "Ptr",0, "UInt",0))
				MsgBox, % "SetupDiGetDevicePropertyW (DEVPKEY_Device_FriendlyName) error"
		}

		;get device ID:
		vDevInst := NumGet(&SP_DEVINFO_DATA, 20, "UInt") ;DevInst
		if (!DllCall("cfgmgr32\CM_Get_Device_ID_Size", "UInt*",vLen, "UInt",vDevInst, "UInt",0, "UInt"))
		{
			if vLen ;'If the specified device instance does not exist, the function supplies a size value of zero.'
			{
				vLen++ ;add extra character to hold null character
				VarSetCapacity(vDeviceID, vLen*(A_IsUnicode?2:1))
				if DllCall("cfgmgr32\CM_Get_Device_ID", "UInt",vDevInst, "Str",vDeviceID, "UInt",vLen, "UInt",0, "UInt")
					MsgBox, % "CM_Get_Device_IDW error"
			}
		}

		oObj := {}
		oObj.DeviceDesc := vDeviceDesc
		oObj.FriendlyName := vFriendlyName
		oObj.HardwareIDs := vHardwareIDs
		oObj.BusReportedDeviceDesc := vBusReportedDeviceDesc
		oObj.DeviceID := vDeviceID
		oArray.Push(oObj)

		if !(vDeviceIDSetState = "")
		&& (vDeviceIDSetState = vDeviceID)
			JEE_DeviceListSetState(hDevInfo, &SP_DEVINFO_DATA, oState)
	}

	DllCall("setupapi\SetupDiDestroyDeviceInfoList", "Ptr",hDevInfo)
	DllCall("kernel32\FreeLibrary", "Ptr",hModule)
	return oArray
}

;==================================================

;loosely based on:
;Temporarily disable CD/DVD drive to prevent spinning-up...? - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/93988-temporarily-disable-cddvd-drive-to-prevent-spinning-up/

JEE_DeviceListSetState(hDevInfo, pSP_DEVINFO_DATA, oState:="")
{
	local
	;source: SetupAPI.h
	;DICS_ENABLE := 0x1
	;DICS_DISABLE := 0x2
	;DICS_PROPCHANGE := 0x3
	;DICS_START := 0x4
	;DICS_STOP := 0x5

	if !IsObject(oState)
	{
		if (oState = "") || (oState = "DE")
			oState := [0x2, 0x1] ;disable, enable
		else if (oState = "E")
			oState := [0x1] ;enable
		else if (oState = "D")
			oState := [0x2] ;disable
		else
			oState := [oState]
	}
	for _, vState in oState
	{
		VarSetCapacity(SP_PROPCHANGE_PARAMS, 20, 0)
		NumPut(8, &SP_PROPCHANGE_PARAMS, 0, "UInt") ;SP_CLASSINSTALL_HEADER.cbSize
		NumPut(0x12, &SP_PROPCHANGE_PARAMS, 4, "UInt") ;SP_CLASSINSTALL_HEADER.InstallFunction ;DIF_PROPERTYCHANGE := 0x12
		NumPut(vState, &SP_PROPCHANGE_PARAMS, 8, "UInt") ;StateChange
		NumPut(0x1, &SP_PROPCHANGE_PARAMS, 12, "UInt") ;Scope ;DICS_FLAG_GLOBAL := 0x1
		NumPut(0, &SP_PROPCHANGE_PARAMS, 16, "UInt") ;HwProfile

		if !DllCall("setupapi\SetupDiSetClassInstallParams", "Ptr",hDevInfo, "Ptr",pSP_DEVINFO_DATA, "Ptr",&SP_PROPCHANGE_PARAMS, "UInt",20)
		|| !DllCall("setupapi\SetupDiChangeState", "Ptr",hDevInfo, "Ptr",pSP_DEVINFO_DATA)
			return 0
	}
	return 1
}

;==================================================

;e.g.:
;vDeviceID := "USB\VID_13BA&PID_0017&MI_01\7&195d6bc&0&0001"
;JEE_DeviceDisableEnable(vDeviceID)

JEE_DeviceDisableEnable(vDeviceID)
{
	return JEE_DeviceList("", vDeviceID, "DE")
}

;==================================================

JEE_DeviceDisable(vDeviceID)
{
	return JEE_DeviceList("", vDeviceID, "D")
}

;==================================================

JEE_DeviceEnable(vDeviceID)
{
	return JEE_DeviceList("", vDeviceID, "E")
}

;==================================================

;note: null-separated list to string/array (null-separated items, double null at end)
JEE_NSLGet(vAddr, vEnc:="", vSep:="")
{
	local
	static vEncDefault := A_IsUnicode ? "UTF-16" : "CP0"
	static vChrSizeDefault := A_IsUnicode ? 2 : 1
	if (vEnc = "")
		vEnc := vEncDefault
	vChrSize := InStr(vEnc, "UTF-16") ? 2 : 1
	vFunc := (vChrSize=2) ? "msvcrt\wcslen" : "msvcrt\strlen"
	oArray := []
	vLenSep := StrLen(vSep)
	vLenTotal := 0
	Loop
	{
		vLen := DllCall(vFunc, "Ptr",vAddr, "Cdecl UPtr")
		vLenTotal += vLen + vLenSep
		if !vLen
			break
		oArray.Push(StrGet(vAddr, vEnc))
		vAddr += (vLen+1) * vChrSize
	}
	if (vSep = "")
		return oArray
	vOutput := ""
	VarSetCapacity(vOutput, vLenTotal*vChrSizeDefault)
	for _, vValue in oArray
		vOutput .= vValue vSep
	return SubStr(vOutput, 1, -vLenSep)
}

;==================================================

JEE_PropertyKeyCreate(ByRef vPropertyKey, vGUID, vPropertyID)
{
	local
	VarSetCapacity(vPropertyKey, 20, 0)
	vAddr := &vPropertyKey
	DllCall("ole32\CLSIDFromString", "WStr",vGUID, "Ptr",vAddr) ;fmtid
	NumPut(vPropertyID, vAddr+0, 16, "UInt") ;pid
}

;==================================================
- This is what prompted me to investigate the problem:
USB mouse stops working after hibernating PC - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=64809

- By doing a search for 'winapi list usb devices', I found some potentially relevant dll functions:
GetRawInputDeviceList, GetRawInputDeviceInfo, SetupDiGetClassDevs.
- And then these 2 useful links, one for listing devices, and one for enabling/disabling them:
DllCall to SetupDiEnumDeviceInfo throws "ERROR_INVALID_USER_BUFFER" - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=40942
Temporarily disable CD/DVD drive to prevent spinning-up...? - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/93988-temporarily-disable-cddvd-drive-to-prevent-spinning-up/

- Some curios:
- My experience had been that hibernating disabled the mouse. But at one point, three times in a row, hibernating did not disable the mouse. At that point, I logged out and logged back in again, and hibernating did disable the mouse again.
- To enable the mouse after hibernation I required my 'DisableEnable' function, the 'Enable' function alone didn't work. This appears to be a well-known issue, since USBDeview specifically gives 3 options: Disable/Enable/Disable+Enable.
- Mystery CLSIDs. The script uses various property keys, which are made up of a 16-byte CLSID/GUID/format ID and a 4-byte property ID. The CLSIDs do appear in the header files that came with Visual Studio Express for Windows Desktop, however, I could not find a name for the CLSIDs in isolation, only names for the property keys as a whole. The CLSIDs in question: {A45C254E-DF1C-4EFD-8020-67D146A850E0} and {540B947E-8B40-45BC-A8A2-6A0B894CBDA2}.
Last edited by jeeswg on 30 Oct 2019, 17:57, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
rommmcek
Posts: 1474
Joined: 15 Aug 2014, 15:18

Re: list/enable/disable USB devices

30 Oct 2019, 17:17

Pretty complete assemblage, however it scarce me (it displays more info about my PC I'm aware of)!

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 273 guests