Sign in to follow this  
Followers 0
bourny

USB detection using WMI

13 posts in this topic

I am looking to enumerate the connected USB devices with their corresponding logical drive letters into an array. I am using WMI to achieve this. At first I struggled to get both to show but then stumbled across the attached code. This code does an excellent job of what I need but with the slight difference in that I need to remove and readd the usb drive to detect it as an event.

My issue is I would like to scan and pick up the connected USB drives logical drive letters without having to replug the usb device. I am guessing I need to use the "OAssociators OF" code inside WMI but struggling to work out how to do this without using the __InstanceCreationEvent used in the example below.

Any ideas welcome.

$wbemFlagReturnImmediately = 0x10
$wbemFlagForwardOnly = 0x20
$colItems = ""
$strComputer = "localhost"
$MyDrive = "SCSD TOOLS"
$objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2")
$m_MediaConnectWatcher = $objWMIService.ExecNotificationQuery("SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_DiskDrive'")
;While 1
    $mbo = $m_MediaConnectWatcher.NextEvent
    $obj = $mbo.TargetInstance
    If $obj.InterfaceType == "USB" Then
        $New = GetDriveLetterFromDisk($obj.Name)
        $Label = DriveGetLabel($New)
        MsgBox(0, "", $new)

  ;If $Label == $MyDrive Then
         ;   MsgBox(4096, "Info", "My drive has been plugged in, backup my files!")
        ;EndIf
    EndIf
;WEnd
Func GetDriveLetterFromDisk($name)
    $ans = ""
    $name = StringReplace($name,"\","\\")
    $oq_part = $objWMIService.ExecQuery("ASSOCIATORS OF {Win32_DiskDrive.DeviceID=""" & $name & """} WHERE AssocClass = Win32_DiskDriveToDiskPartition", "WQL", $wbemFlagReturnImmediately + $wbemFlagForwardOnly)
    If IsObj($oq_part) Then
        For $obj_part In $oq_part
            $oq_disk = $objWMIService.ExecQuery("ASSOCIATORS OF {Win32_DiskPartition.DeviceID=""" & $obj_part.DeviceID & """} WHERE AssocClass = Win32_LogicalDiskToPartition", "WQL", $wbemFlagReturnImmediately + $wbemFlagForwardOnly)
            If IsObj($oq_disk) Then
                For $obj_disk in $oq_disk
                    $ans = $ans & $obj_disk.Name
                Next
            EndIf
        Next
    EndIf
Return $ans
EndFunc

Share this post


Link to post
Share on other sites



You could simplify it by using DriveGetDrive with type removable and get an array of all removable disks.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

BrewManNH :This does not work as My external USB hard drive is not classified as removable but as a disk drive. A solid state is but not a portable usb hard drive.

Any more ideas welcome.

The script I attached is exactly what I need apart from the fact I need to scan attached USB drives (Solid state / portable hard drive) without disconnecting them and reconnecting them. It is the bit at the top of the script I need to change but not sure to what.

Share this post


Link to post
Share on other sites

Hi, bourny. What about something like this, if you will be using the same drive(s) every time? The example uses a message box to determine the drive, but you could use a similar If statement to do what you want with that drive (it also gives you the drive letter, in case that changes each time):

I used my thumb drive and the total space it contains (not used space).

Local $aArray = DriveGetDrive("ALL")
For $i = 1 To $aArray[0]
If DriveSpaceTotal(StringUpper($aArray[$i])) = "3813.4453125" Then
  MsgBox(4096, "", "Drive " & StringUpper($aArray[$i]) & " is my thumb drive.")
EndIf
Next

√-1 2^3 ∑ π, and it was delicious!

Share this post


Link to post
Share on other sites

bourny,

Does this give you what you expect?

#include <Array.au3>
$arrResults=_GetLogicalDriveToPartition()
_ArrayDisplay($arrResults)
Func _GetLogicalDriveToPartition()
    $objWMIService = ObjGet("winmgmts:{impersonationLevel=Impersonate}!.rootcimv2")
    If Not IsObj($objWMIService) Then Return -1 ;Failed to Connect to WMI on Local Machine
    Local $arr[1][2]
    $colDevice = $objWMIService.ExecQuery("SELECT * from Win32_LogicalDiskToPartition")
    For $objItem in $colDevice
        ReDim $arr[UBound($arr)+1][2]
        $arr[UBound($arr)-1][0]=StringTrimLeft($objItem.Antecedent,StringInStr($objItem.Antecedent,"=",0,-1))
        $arr[UBound($arr)-1][1]=StringTrimLeft($objItem.Dependent,StringInstr($objItem.Dependent,"=",0,-1))
    Next
    $colDevice=0
    $objWMIService=0
    $arr[0][0]="Disk #, Partition #"
    $arr[0][1]="Drive Letter"
    Return $arr
EndFunc

kylomas


Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Share this post


Link to post
Share on other sites

JLogan3o13: Nice Try but unfortunatly I have various different types of USB drives which users could use. These range from solid state which are easy to detect as they are classified as removable drive by the OS. The portable hard drives are the issue as these are recognised as a disk drive same as the fixed system disk. The only difference is the connector is by USB instead of IDE / SATa etc. If I can see which drives are connected vie USB and then return each logical drive letter that would be spot on. My above example shows this but only if you reconnect the device to USB.

kylomas: Nearly what I need. If you can add another column to the array with information if the logical drive is hanging off a usb / IDE / Sata connection then that is my question answered.

This thread is nearly has an answer any further suggestions welcome to crack this difficult issue. It seems impossible to do this so far.

Share this post


Link to post
Share on other sites

Even though the WMI code to do this is probably in many posts on the forum, It was easy enough to look through the existing WMI scripts I have.

added more object checking and the necessary COM error handler

#include <Array.au3>
Opt("MustDeclareVars", 1)
Local $arrResults = _GetLogicalDriveToPartition()
_ArrayDisplay($arrResults, "USB Drives")
Exit
Func _GetLogicalDriveToPartition()
; Error monitoring. This will trap all COM errors while alive.
    ; This particular object is declared as local, meaning after the function returns it will not exist.
Local $oErrorHandler, $oWMIService, $arr[1][4], $iPos, $iPhysDrive, $iUB, $oColDevice, $oColItems
    $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc")
If Not IsObj($oErrorHandler) Then ConsoleWrite("!No COM error handler" & @CRLF)
$oWMIService = ObjGet("winmgmts:{impersonationLevel=Impersonate}!\\.\root\cimv2")
If Not IsObj($oWMIService) Then Return -1 ;Failed to Connect to WMI on Local Machine
$oColDevice = $oWMIService.ExecQuery("SELECT * from Win32_LogicalDiskToPartition", "WQL", 48)
If Not IsObj($oColDevice) Then Return -1
For $objItem In $oColDevice
  $iPos = StringInStr($objItem.Antecedent, "Disk #")
  If Not @error And $iPos <> 0 Then
   $iPhysDrive = StringMid($objItem.Antecedent, $iPos +6, 1)
   $oColItems = $oWMIService.ExecQuery("SELECT InterfaceType,Model FROM Win32_DiskDrive WHERE DeviceID = '\\\\.\\PHYSICALDRIVE"&$iPhysDrive&"'", "WQL", 48)
   If Not IsObj($oColItems) Then Return -1
   For $obj In $oColItems
    If $obj.InterfaceType == "USB" Then
     $iUB = UBound($arr)
     ReDim $arr[$iUB + 1][4]
     $arr[$iUB][0] = StringTrimLeft($objItem.Antecedent, StringInStr($objItem.Antecedent, "=", 0, -1))
     $arr[$iUB][1] = StringReplace(StringTrimLeft($objItem.Dependent, StringInStr($objItem.Dependent, "=", 0, -1)), """", "")
     $arr[$iUB][2] = DriveGetLabel($arr[$iUB][1])
     $arr[$iUB][3] = $obj.Model
     ;ConsoleWrite("+ Drive " & $arr[$iUB][1]& " is USB" & @CRLF)
    EndIf
   Next
  EndIf
Next
$arr[0][0] = "Disk #, Partition #"
$arr[0][1] = "Drive Letter"
$arr[0][2] = "Drive Label"
$arr[0][3] = "Drive Model"
Return $arr
EndFunc   ;==>_GetLogicalDriveToPartition

; User's COM error function. Will be called if COM error occurs
Func _ErrFunc($oError)
    ; Do anything here.
    ConsoleWrite("err.number is: " & @TAB & $oError.number & @CRLF & _
            "err.windescription:" & @TAB & $oError.windescription & @CRLF & _
            "err.description is: " & @TAB & $oError.description & @CRLF & _
            "err.source is: " & @TAB & $oError.source & @CRLF & _
            "err.helpfile is: " & @TAB & $oError.helpfile & @CRLF & _
            "err.helpcontext is: " & @TAB & $oError.helpcontext & @CRLF & _
            "err.lastdllerror is: " & @TAB & $oError.lastdllerror & @CRLF & _
            "err.scriptline is: " & @TAB & $oError.scriptline & @CRLF & _
            "err.retcode is: " & @TAB & $oError.retcode & @CRLF & @CRLF)
EndFunc   ;==>_ErrFunc

I see fascists...

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

How about

Local $aArray = DriveGetDrive("ALL")
For $i = 1 To $aArray[0]
If DriveSpaceTotal(StringUpper($aArray[$i])) < "65000" And DriveSpaceTotal(StringUpper($aArray[$i])) > "0" Then
  MsgBox(4096, "", "Drive " & StringUpper($aArray[$i]) & " is my thumb drive.")
EndIf
Next

Most Usb drives are less than 64gb and they are rare so just set the first figure to that or just aboveand i used the "0" value to exclude empty DVDs etc

Edit

Just realised this will pick up a DVD drive with a disc in it i think... Yep it does bugger....

Local $aArray = DriveGetDrive("FIXED")
For $i = 1 To $aArray[0]
If DriveSpaceTotal(StringUpper($aArray[$i])) < "65000" Then
  MsgBox(4096, "", "Drive " & StringUpper($aArray[$i]) & " is my thumb drive.")
EndIf
Next
Local $aArray = DriveGetDrive("REMOVABLE")
For $i = 1 To $aArray[0]
If DriveSpaceTotal(StringUpper($aArray[$i])) < "65000" Then
  MsgBox(4096, "", "Drive " & StringUpper($aArray[$i]) & " is my thumb drive.")
EndIf
Next

this works for me

Edited by Chimaera

Share this post


Link to post
Share on other sites

Tested the following against fixed and removable drives, outputs the drive letter along with the model. All USB connected devices have "USB Device" at the end of the string.

$strComputer = @ComputerName
Dim $objWMIService = objget("winmgmts:\\" & $strComputer & "\root\cimv2")
Dim $colDevices = $objWMIService.ExecQuery ("Select * From Win32_logicaldisktopartition")
For $objDevice in $colDevices
    $strDeviceName = $objDevice.antecedent
$strdrive = $objDevice.dependent
$strQuote = Chr(34)
$strDriveName = StringReplace($strDrive, $strQuote, "")
$arrDriveNames = stringSplit($strDriveName, "=")
    $strDeviceName = StringReplace($strDeviceName, $strQuote, "")
$arrDeviceNames = stringSplit($strDeviceName, "=")
$split = stringsplit($arrDeviceNames[2],",")
$replace = Stringtrimleft($split[1], 6)
    Dim $colUSBDevices = $objWMIService.ExecQuery("Select * From Win32_diskdrive Where index = '" & $replace & "'")
  For $objUSBDevice in $colUSBDevices
  msgbox(0,"",$arrDriveNames[2] & "drive " & " is" & @lf & $objUSBDevice.caption)
   Next
Next

Share this post


Link to post
Share on other sites

There's a script by Yashied floating around the forum somewhere, though I can't seem to find it right now, it doesn't use WMI but registering the Windows Message WM_DEVICECHANGE.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Rover: Spot on. This gives me exactly what I am looking for.

Chimaera: This does not tell me which device is USB. I plugged a portable drive in and it did not detect it. Appreciate your attempt and time spent.

airwilf : Spot on and very impressed in the ammount of code used compared to Rovers.

Summary. 2 very good responses to this issue and I now have the ability to detect all usb drives regardless if they are solid state or a classed as a fixed disk.. Note none of these examples have required the use of "associators Of" to derive the result which is what I was expecting to see in an answer. Makes it easier to understand for WMI newbies.

Share this post


Link to post
Share on other sites
guinness: Will be interested to see this if you can find it. I have a solution now but always keen to see different ways and I am not familiar with WM instead of WMI so might extend my knowledge.

Share this post


Link to post
Share on other sites

#Include <APIConstants.au3>
#Include <GUIConstantsEx.au3>
#Include <WinAPIEx.au3>

Opt('TrayAutoPause', 0)

$hForm = GUICreate('')
GUIRegisterMsg($WM_DEVICECHANGE, 'WM_DEVICECHANGE')

While 1
    Sleep(1000)
WEnd

Func _GetDrive($iMask)
    For $i = 0 To 25
        If BitAND(BitShift($iMask, $i), 1) Then
            Return Chr(65 + $i) & ':'
        EndIf
    Next
    Return ''
EndFunc   ;==>_GetDrive

Func _USBCheck($sVolume)

    Local $hDrive, $tData, $iBus, $iRes, $Fs

    $iBus = _WinAPI_GetDriveBusType($sVolume)
    Switch $iBus
        Case $DRIVE_BUS_TYPE_USB
            $hDrive = _WinAPI_CreateFileEx('.' & $sVolume, 3, 0, 0)
            If Not $hDrive Then
                Return
            EndIf
            $tData = DllStructCreate('dword DeviceType;ulong DeviceNumber;ulong PartitionNumber')
            $iRes = _WinAPI_DeviceIoControl($hDrive, $IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, DllStructGetPtr($tData), DllStructGetSize($tData))
            _WinAPI_CloseHandle($hDrive)
            If (Not $iRes) Or (Not (DllStructGetData($tData, 'DeviceType') = 7)) Then
                Return
            EndIf
;~          $Fs = DriveGetFileSystem($sVolume)
;~          Switch $Fs
;~              Case 'FAT32', 'NTFS'
;~
;~              Case Else
;~                  Return
;~          EndSwitch
        Case Else
            Return
    EndSwitch
    ConsoleWrite('USB device found: ' & $sVolume & @CR)
EndFunc   ;==>_USBCheck

Func WM_DEVICECHANGE($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hForm
            Switch $wParam
                Case 0x8000 ; DBT_DEVICEARRIVAL

                    Local $tDBV = DllStructCreate('dword Size;dword DeviceType;dword Reserved;dword Mask;ushort Flags', $lParam)
                    Local $Type = DllStructGetData($tDBV, 'DeviceType')
                    Local $Flag = DllStructGetData($tDBV, 'Flags')
                    Local $Mask = DllStructGetData($tDBV, 'Mask')

                    Switch $Type
                        Case 2 ; DBT_DEVTYP_VOLUME
                            Switch $Flag
                                Case 0
                                    _USBCheck(_GetDrive($Mask))
                            EndSwitch
                    EndSwitch
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DEVICECHANGE

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0