Sign in to follow this  
Followers 0

Detecting USB Drive Insertion

11 posts in this topic

Posted

Hello all, I've recently gotten into AutoIt, and so far, I love it. All those old hacks and tricks I used to do with BAT files are now trivial, and I'm on to bigger and better things. That being said, I have a slight problem.

My latest project involves USB Drives. I am trying to write a script that, while running on the computer, can detect when a USB drive is inserted, and automatically backup my files off of it (given the Label matches). I've gotten a good bit of the code figured out, but I'm stuck. I was wondering if anyone could help me.

Here's the code I have so far:

$DBT_DEVICEARRIVAL = "0x00008000"
$WM_DEVICECHANGE = 0x0219
GUICreate("")
GUIRegisterMsg($WM_DEVICECHANGE , "MyFunc")
Func MyFunc($hWndGUI, $MsgID, $WParam, $LParam)
    If $WParam == $DBT_DEVICEARRIVAL Then
    ; Find newly inserted Drive here, test for correct label, and then backup files from it
    EndIf
EndFunc
While 1
    $GuiMsg = GUIGetMsg()
WEnd

It can successfully detect whenever a new Device is inserted into the computer just fine, there's just 2 problems with it so far:

1. It can't tell the difference between a newly inserted CD and a newly inserted USB drive, which is not good, as I would like for it to only trigger on USBs

2. Even though it can detect that something was inserted, it can't find the drive in question (by Letter, I mean). I could make a function that, whenever something was inserted, compare it to a list of drives before the insertion, and the difference has to be my new drive, but that's ugly, and also means I'd have to investigate as to detecting when drives were removed to keep my list clean.

From all of my readings and Googling, all I can come up with is that the $LParam has something to do with what kind of drive is being inserted, and where it might be located (as per this article). But, I can't seem to detect the differences with what I have so far.

If anyone can help me, I would greatly appreciate it. Thanks for any help you provide me.

Share this post


Link to post
Share on other sites



Posted (edited)

Sorry I don't have answer to you question , but I'd just like to say thank you for your function :whistle:

This was just what I was looking for.

I use your function to detect when my psp is plugged in and sharing it's flash memory so it auto updates my modified files to the flash.

For me to check that your function has the correct drive I'm using a sloppy approach , but it works all the same.

I use

DriveGetDrive() to list the drives available

DriveGetType() to get the type of drive ("REMOVABLE" is what I'm after)

$DBT_DEVICEARRIVAL = "0x00008000"
$WM_DEVICECHANGE = 0x0219
GUICreate("")
GUIRegisterMsg($WM_DEVICECHANGE , "MyFunc")
Func MyFunc($hWndGUI, $MsgID, $WParam, $LParam)
    If $WParam == $DBT_DEVICEARRIVAL Then
    $dg = DriveGetDrive( "REMOVABLE" )
        For $i = 1 to $dg[0]
            $dt = DriveGetType( $dg[$i] )
            If $dt = "REMOVABLE" And $dg[$i] <> "a:" And $dg[$i] <> "b:" Then
                ;;look for my marker file at $dg[$i] , if found do check...
                MsgBox(4096,"Drive " & $i, $dg[$i] & " = " & $dt)
            EndIf
        Next
    EndIf
EndFunc
While 1
    $GuiMsg = GUIGetMsg()
WEnd

If removable drive is found then I look for a unique marker file on the removable drive that I have an MD5 for and compare.

If the removable drive has this correct marker file then I know it's the drive I'm looking for.

If there is no marker file on the removable drive then check the next removable drive and ... so on

Once I have the correct drive I then do a file compare from my update archive on the pc with the plugged in psp flash memory...

If there are differences in the files on the pc and psp (md5 check all files compare), time stamp is newer and psp flash has enough space then they are transfered to the psp flash memory.

Must admit I'm still trying to quash some minor bugs in my script, method is crude but works for my needs.

Thank You

&

Cheers.

Edited by smashly

Share this post


Link to post
Share on other sites

Posted

Well, I did some more work on it, and did manage to get it to tell the difference between a CD insertion and USB, though it's a crude hack at best. It ends up using the Drive Removal Event, too, so I may end up just hacking it all together without using the LParam, but I have a very strong feeling there's an easier way to do all this than I'm seeing. I can't post it this weekend. It's on my computer at work, so I'll try to post it Monday.

@Smashly: Glad I could be of some assistance, and I think I can help you even more. The DriveGetDrive your using supports "REMOVABLE" as an argument, so that next step you were doing to test for removable can be skipped by just using that arguement.

Share this post


Link to post
Share on other sites

Posted (edited)

Yep the "REMOVABLE" argument is what I use , but most times I have multiple removable drives on my PC..

Even a: or b: drives are classed as removable. this is why I exclude a: and b: and check for more then 1 removable drive against a marker file. edited my post above with what I mean.

Cheers

Edit I see what you mean , use DriveGetDrive for "removable" drives only.

But the funny thing is if I have a removable drive already plugged in and I put a cdrom in the drive then it invokes the script again, not really a problem as the script still sees the correct removable drive. just a quirk you could say... lol

Edited by smashly

Share this post


Link to post
Share on other sites

Posted

I know, any media insertion will trigger the script I gave here (even floppy, if I'm not mistaken. Haven't tested).

I've done a good bit more digging, and come up with something that's both promising and disconcerting:

Latest Find

This version of basically what I want to do uses VB and the WMI, both of which AutoIt supports wonderfully. I've used WMI ScriptOMatic and seen what things I'd need to do some of what that page shows, but some parts still elude me.

While all this is very promising, I can't figure it out, and I have a sinking feeling that the original code I wrote, while interesting, may be thrown out in favor of this new found code, given I, or someone else on here, can figure out what this new script does, and how to get AutoIt to do it too.

As a side note, I also realized another use for this script (if you purposefully searched for CD-ROMS): Auto-Ripping of Audio Tracks from each Audio CD you inserted without having to run the program more than once, just have this type of event trigger another script to start the ripping. Hopefully, something like this could make digitizing someone's collection a lot easier.

Share this post


Link to post
Share on other sites

Posted

Well, I got bored last night/today. Read a whole lot of info, discovered how to read the data from LParam (a struct), then discovered that it too held the same data no matter if I inserted a CD or a USB drive. So, I fought with it, kept trying, couldn't make heads or tails of it that way. I still don't understand near enough about the WMI, especially how to register the event watcher for it, so, I went the ugly route. I give you the USB Backup Script, that, while active, will watch for any inserted drives, and once it detects that the drive is the one you configured, has space to backup specific folders as you wish (I left that part up to anyone else to fill in). Here it is:

Dim $DBT_DEVICEARRIVAL = "0x00008000"
Dim $DBT_DEVICECOMPLETEREMOVAL = "0x00008004"
Dim $USB_ATTENTION = "0x00000007"
Dim $WM_DEVICECHANGE = 0x0219
Dim $Drives
Dim $Drive_Type = "ALL"  ; Set to ALL because some USB Drives are detected as Fixed Disks, and we don't want to miss those
Dim $WATCH = False
Dim $MyDrive = "STUFF"

;Get Initial List of Drives to Check Against
UpdateDrives()
;Setup The GUI to watch for the DeviceChange Event
GUICreate("")
GUIRegisterMsg($WM_DEVICECHANGE, "DeviceChange")
Func DeviceChange($hWndGUI, $MsgID, $WParam, $LParam)
    Switch $WParam
        Case $USB_ATTENTION
        ; This only happens when USB drives are inserted, so I use it to tell the difference between these and CDROMs
            $WATCH = True
        Case $DBT_DEVICECOMPLETEREMOVAL
        ; Whenever a Drive is Removed, Update the Drive List
            UpdateDrives()
        Case $DBT_DEVICEARRIVAL
        ; A drive was inserted
        ; Use $WATCH to tell if this was a CDROM or USB
        ; $WATCH = True, USB
        ; $WATCH = False, CDROM
            If $WATCH = True Then
            ; New USB Drive Was Detected, Time to Find it's Letter
                $New = FindNewDrive(); $New now has the Drive Letter of our New Drive, so USE IT!!!
                $Label = DriveGetLabel($New)
                If $Label == $MyDrive Then
                    MsgBox(4096, "Info", "My Drive has been Inserted, Backup My Files!")
                EndIf
            ; Now Reset Drive List so more insertions can also be detected accurately
                UpdateDrives()
            EndIf
    EndSwitch
EndFunc  ;==>DeviceChange

; This just jumps through the new Drive List, comparing them until it finds the entry that is in the new one that isn't in the old one
Func FindNewDrive()
    $Temp = DriveGetDrive( "REMOVABLE" )
    For $i = 1 to $Temp[0]
        $Old = False
        For $j = 1 to $DRIVES[0]
            If $DRIVES[$j] == $Temp[$i] Then $Old = True
        Next
        If $Old == False Then Return $Temp[$i]  
    Next
EndFunc  ;==>FindNewDrive

; Just to keep things neat, and so if Variables Ever Change, this makes updating easier
Func UpdateDrives()
    $Drives = DriveGetDrive( $Drive_Type )
EndFunc  ;==>UpdateDrives

; Main Loop to Keep the Program Open
; No Real Way of ending this program, except for just killing the process
; Which is what I want, an always on backup for my drive every time I insert it
While 1
    $GuiMsg = GUIGetMsg()
; This is needed because the watch event above not only triggers before a USB Drive is inserted/removed,
; but also AFTER insertion too, and if not reset, a subsequent CD insertion will trigger it again.
; So, every second, we reset $WATCH, to keep things clean
    Sleep (1000)
    $WATCH = False
WEnd

As good as the above code works, I'd still love to see the WMI version. I'll write it myself, I just need to know how to register a WMI event watcher to watch for events for me. I know how to do that with VB, but I can't seem to find how to do it in Autoit, and I have this feeling it is possible, I just don't know how to do it. Any help from people here who know more about that than I do would be greatly appreciated.

Share this post


Link to post
Share on other sites

Posted

Ok, I got a little more time to read around, and found an answer to my own question. I discovered the ExecNotificationQuery, and found that it does the trick just as nice as before, maybe even better. The code at least looks simpler, not as jumbled, just a little more above my head now...

I give you the WMI version of the USB Backup Script

$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)
        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

I hope someone else can find some use for what I did here. I know I have.

Share this post


Link to post
Share on other sites

Posted

@SkInNyWhItEGuY

Perfect !! :whistle:

This belongs in the SCRIPT EXAMPLES section !!

regards

ptrex

Share this post


Link to post
Share on other sites

Posted

I sometimes get a Line 10 Error: "Variable must be of type "Object" with the USB Backup Script. Anyone else get this?

Share this post


Link to post
Share on other sites

Posted

i'll answer you in another 7 years from now..

You do realize, autoit has changed from then to now, it's possible there are going to be incompatibilities with those old scripts.

Share this post


Link to post
Share on other sites

Posted

I have been search the net for hours trying to find a way to get this to work. Hopefully some one here can let me know how.

I have a USB game pad (HID) and am trying to write the script to launch a program once inserted and then close that program once removed. When i plug in the HID device it doesn't seem to register the same way a USB drive does. 

Is there anything way that i can do this? I am on a Windows 8.1 box.

Thank ins advance for any help

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

  • Recently Browsing   0 members

    No registered users viewing this page.