Jump to content
Sign in to follow this  
ModemJunki

I built a 2d array of devices from devcon (works but doesn't seem right).

Recommended Posts

ModemJunki

The below code works (requires DevCon / Windows 7) - an array of device ID strings and their names comes - but it doesn't seem right to have to manipulate the drive letter strings in the array to "fix them up" as I have done.

I need a list of all devices from Devcon (including non-present devices). At the end I will check for both present and non-present devices filtered against a list of known devices so I can see which are unplugged from the computer, but first I am building the list of all devices (plugged in or not).

I am doing this workaround because DevCon outputs a list with ":" delimiter and if a filter driver (for some softwares) populates a drive letter they also have ":" in the description. See my comments in the "_RunDevcon" functions for where I do this manipulation.

Is there a better/smarter way to do this?

#include <String.au3>
#include <Constants.au3>
#include <Array.au3>

Global $path_Devcon = @ScriptDir & '\devcon.exe'
Global $aRemovable, $strRun, $foundall

$foundall = _RunDevcon('"' & $path_Devcon & '" findall *'); the findall means we will see "non present devices"
_ArrayDisplay($foundall, "List of devices and their PnP IDs", -1, 0, "", "", "Index|Device String|Device Name")

; Builds a device list using devcon
; by BugFix in 2009 and modified for this script
Func _RunDevcon($strRun)
    Local $data, $dvcn = Run("cmd.exe", @SystemDir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD)
    StdinWrite($dvcn, $strRun & @CRLF)
    StdinWrite($dvcn)
    While True
        $data &= StdoutRead($dvcn)
        If @error Then ExitLoop
        Sleep(25)
    WEnd
    Local $split = StringSplit($data, @LF)
    $data = ''

    For $i = 5 To $split[0]
        If StringStripWS($split[$i], 8) == '' Then ExitLoop
        $data &= $split[$i]
        Dim $devices[1][1]
        $devices = StringSplit($data, @CRLF)
    Next

    _ArrayDelete($devices, $devices[0] - 1); because the last line is "<number> matching device(s) found."
    $devices[0] = UBound($devices, 1)

;~ Some hoops to jump because Devcon returns a list of devices with
;~ a ":" in their descriptions and the delimiter between the devices is a ":"!
    $dvstr = _ArrayToString($devices, "'"); split array to string
    $dvstr_fixed = StringReplace($dvstr, ":", "!", 0, 2); replace the colons with something else
    $devarray = _String_Split($dvstr, "'", ":"); put all back into an array

    For $j = 1 To $devarray[0][0] - 1; we remove leading whitespace from second column
        If $devarray[$j][2] = "\" Then; now we fix the drive letters to have a colon again
            $Noblank = StringReplace($devarray[$j][1] & ":" & $devarray[$j][2], " ", "", 1)
            $devarray[$j][1] = $Noblank
        Else
            $Noblank = StringReplace($devarray[$j][1], " ", "", 1)
            $devarray[$j][1] = $Noblank
        EndIf
    Next

    _ArrayDeleteCol($devarray, 2); now we can delete the not-needed column
    _ArrayDelete($devarray, $devarray[0][0] - 1); we ended up with a blank at the end
    $devarray[0][0] = UBound($devarray, 1)

    Return $devarray
EndFunc   ;==>_RunDevcon

;~ Splits a string into an multidimensional array
;~ By wolf9228 in 2009
Func _String_Split($string, $delimiters1, $delimiters2)
    $Array1 = StringSplit($string, $delimiters1)
    Dim $MAXUBound = 0, $OutArray2[1][1]

    For $i = 1 To $Array1[0] Step 1
        $OutArray = StringSplit($Array1[$i], $delimiters2)
        If $MAXUBound < $OutArray[0] Then
            ReDim $OutArray2[$i][$OutArray[0]]
            $MAXUBound = $OutArray[0]
        Else
            ReDim $OutArray2[$i][UBound($OutArray2, 2)]
        EndIf

        For $j = 1 To $OutArray[0] Step 1
            $OutArray2[$i - 1][$j - 1] = $OutArray[$j]
        Next
    Next
    Return $OutArray2
EndFunc   ;==>_String_Split

;~ Deletes column from array
;~ By PsaltyDS in 2008
Func _ArrayDeleteCol(ByRef $avWork, $iCol)
    If Not IsArray($avWork) Then Return SetError(1, 0, 0); Not an array
    If UBound($avWork, 0) <> 2 Then Return SetError(1, 1, 0); Not a 2D array
    If ($iCol < 0) Or ($iCol > (UBound($avWork, 2) - 1)) Then Return SetError(1, 2, 0); $iCol out of range
    If $iCol < UBound($avWork, 2) - 1 Then
        For $c = $iCol To UBound($avWork, 2) - 2
            For $r = 0 To UBound($avWork) - 1
                $avWork[$r][$c] = $avWork[$r][$c + 1]
            Next
        Next
    EndIf
    ReDim $avWork[UBound($avWork)][UBound($avWork, 2) - 1]
    Return 1
EndFunc   ;==>_ArrayDeleteCol

Always carry a towel.

Share this post


Link to post
Share on other sites
kylomas

ModemJunki,

On my machine one device called "HTREEROOT0" does not have a description. 

The below code works (requires DevCon / Windows 7)

 

Are you sure about that?  Your code returns 2 devices, in fact there are 251 devices.

The output returned uses a ': ' (colon+space) delimiter between the device name and description.  Incidentally, some of the device names also have a colon in them.

Based on that I would do this...

#include <String.au3>
#include <Constants.au3>
#include <Array.au3>
#include <userudfs.au3>

Global $path_Devcon = @ScriptDir & '\devcon.exe'
Global $aRemovable, $strRun, $foundall

$foundall = _RunDevcon('"' & $path_Devcon & '" findall *'); the findall means we will see "non present devices"
_ArrayDisplay($foundall, "List of devices and their PnP IDs", -1, 0, "", "", "Index|Device String|Device Name")

; Builds a device list using devcon
; by BugFix in 2009 and modified for this script

Func _RunDevcon($strRun)
    Local $data, $dvcn = Run("cmd.exe", @SystemDir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD)
    StdinWrite($dvcn, $strRun & @CRLF)
    StdinWrite($dvcn)
    While True
        $data &= StdoutRead($dvcn)
        If @error Then ExitLoop
        Sleep(25)
    WEnd

    local $aData = stringregexp($data,'([\V]+\: [\V]+)',3)

    local $aRet[ubound($aData)][2]
    for $1 = 0 to ubound($aData) - 1
        $aRet[$1][0] = stringregexpreplace($aData[$1],'([\V]+)\: [\V]+','$1')
        $aRet[$1][1] = stringregexpreplace($aData[$1],'[\V]+\: ([\V]+)','$1')
    next

    return $aRet

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
ModemJunki

Thank you kylomas, that is a much better implementation. I will have to find time to study regex now. http://xkcd.com/1171/

On my system the original returned the same number of devices as devcon (in a cmd box) did.

Edited by ModemJunki

Always carry a towel.

Share this post


Link to post
Share on other sites
kylomas

ModemJunki,

On my system the original returned the same number of devices as devcon (in a cmd box) did

 

The problem is in the  _arraydisplay.  When I comment out allof the parameters I get all of the data.  I never use the parms so have no idea why I am getting abbreviated output (2 items) with them.

SRE Explanation:

local $aData = stringregexp($data,'([V]+: [V]+)',3)
 

(             - start capture group

[V]          - capture all non vertical whitespace chars (everything except line feed, form feed vertical tab and carrige return)

+             - repeat the previous group/class 1 or more times up to...

:            - the first colon followed by a space ("" = escape special chars)

                This effectively eliminates any lines not having a ": " sequence.

[V]+         - as before, capture all the rest of the string

)             - end capture group

,3            - store each captured group in an array

$aRet[$1][0] = stringregexpreplace($aData[$1],'([V]+): [V]+','$1')

([V]+)       - capture all non-vertical whitespace characters up to..

:            - a colon followed by a space

[V]+         - continue matching till end of string

,'$1'         - replace the matched string with the capture group

$aRet[$1][0]  - write the replacement to column 0 of row $1

$aRet[$1][1] = stringregexpreplace($aData[$1],'[V]+: ([V]+)','$1')

[V]+         - match all non-vertical whitespace characters up to..

:            - a colon followed by a space

([V]+)       - cature everything after the colon+space sequence

,'$1'         - replace the matched string with the capture group

$aRet[$1][1]  - write the replacement to column 1 of row $1

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
ModemJunki

Interesting - on my system there are two items with no description. These get omitted from the final array with the regexed script. :-(

HTREEROOT0
HIDVID_046D&PID_C52B&MI_02&COL038&F5A49A3&0&0002


Always carry a towel.

Share this post


Link to post
Share on other sites
kylomas

Interesting - on my system there are two items with no description. These get omitted from the final array with the regexed script. :-(

HTREEROOT0

HIDVID_046D&PID_C52B&MI_02&COL038&F5A49A3&0&0002

 

Is that a problem?

edit: if it is then this will get the entries with no description.

#include <String.au3>
#include <Constants.au3>
#include <Array.au3>

Global $path_Devcon = @ScriptDir & '\devcon.exe'
Global $aRemovable, $strRun, $foundall

$foundall = _RunDevcon('"' & $path_Devcon & '" findall *'); the findall means we will see "non present devices"
_ArrayDisplay($foundall, "List of devices and their PnP IDs", -1, 0, "", "", "Index|Device String|Device Name")

; Builds a device list using devcon
; by BugFix in 2009 and modified for this script

Func _RunDevcon($strRun)
    Local $data, $dvcn = Run("cmd.exe", @SystemDir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD)
    StdinWrite($dvcn, $strRun & @CRLF)
    StdinWrite($dvcn)
    While True
        $data &= StdoutRead($dvcn)
        If @error Then ExitLoop
        Sleep(25)
    WEnd

    local $aData = stringsplit($data,@crlf,3)

    local $aRet[ubound($aData)-7][2]
    for $1 = 4 to ubound($aData) - 4
        if stringregexp($adata[$1],'\: ') then
            $aRet[$1-4][0] = stringregexpreplace($aData[$1],'([\V]+)\: [\V]+','$1')
            $aRet[$1-4][1] = stringregexpreplace($aData[$1],'[\V]+\: ([\V]+)','$1')
        Else
            $aret[$1-4][0] = $adata[$1]
        endif
    next

    return $aRet

EndFunc
Edited by 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
kylomas

and in a listview control...

; *** Start added by AutoIt3Wrapper ***
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
; *** End added by AutoIt3Wrapper ***
#include <String.au3>
#include <Constants.au3>
#include <Array.au3>
#include <GuiListView.au3>

Global $path_Devcon = @ScriptDir & '\devcon.exe'

$foundall = _RunDevcon('"' & $path_Devcon & '" findall *'); the findall means we will see "non present devices"
;_ArrayDisplay($foundall, "List of devices and their PnP IDs", -1, 0, "", "", "Index|Device String|Device Name")

#AutoIt3Wrapper_Add_Constants=n

local $gui010   =   guicreate('DevCon List',1000,700)
local $aSize    =   wingetclientsize($gui010)
local $lst010   =   guictrlcreatelistview('Device|Description',0,20,$aSize[0],550)
                    guictrlsetbkcolor(-1,$GUI_BKCOLOR_LV_ALTERNATE)

for $1 = 0 to ubound($foundall) - 1
    GUICtrlCreateListViewItem($foundall[$1][0] & '|' & $foundall[$1][1],$lst010)
    guictrlsetbkcolor(-1,0xc0f0c0)
next

for $1= 0 to _GUICtrlListView_GetColumnCount($lst010)
    _GUICtrlListView_SetColumnWidth( $lst010, $1, ($aSize[0]/ _GUICtrlListView_GetColumnCount($lst010)-8) )
next

guisetstate()

while 1
    switch guigetmsg()
        case $gui_event_close
            Exit
    EndSwitch
WEnd

; Builds a device list using devcon
; by BugFix in 2009 and modified for this script

Func _RunDevcon($strRun)
    Local $data, $dvcn = Run("cmd.exe", @SystemDir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD)
    StdinWrite($dvcn, $strRun & @CRLF)
    StdinWrite($dvcn)
    While True
        $data &= StdoutRead($dvcn)
        If @error Then ExitLoop
        Sleep(25)
    WEnd

    local $aData = stringsplit($data,@crlf,3)

    local $aRet[ubound($aData)-7][2]
    for $1 = 4 to ubound($aData) - 4
        if stringregexp($adata[$1],'\: ') then
            $aRet[$1-4][0] = stringregexpreplace($aData[$1],'([\V]+)\: [\V]+','$1')
            $aRet[$1-4][1] = stringregexpreplace($aData[$1],'[\V]+\: ([\V]+)','$1')
        Else
            $aret[$1-4][0] = $adata[$1]
        endif
    next

    return $aRet

EndFunc

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

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  

  • Similar Content

    • logmein
      By logmein
      Hi,
      I'm trying to develope a program to safely eject an USB device. I have tried a lot of way but there was no satisfactory result. I used to consult wraithdu's script here : but that script wasn't work on my Win7 machine, then I heard someone talk about Devcon, then I started to explore it, I have scripted, tested for days and get nothing but errors. I used this command : devcon remove @usb\* to remove all removable devices but here the result goes :



      All failed? I have just plugged on two USB Kingmax and Transcend!

      I need some professional guys to solve the problem:D Thanks!
×