Jump to content

Safely Eject a USB Drive


Recommended Posts

  • 4 months later...
  • Replies 66
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

I have had times when NTFS formatted USB HDDs (not flash drives) have been stuck by explorer because of filesystem journals etc. In that case my handle script has always found those handles and can cl

For those interested, if you download the Devcon package as mentioned by VeeDub above, it only contains the small (55Kb) executable 'devcon.exe', which can be run with the command-line of 'devcon.exe restart @USB\ROOT_*\*' to reload soft ejected USB drives. You don't need to specify an exact ID. It worked for me anyway, on WinXp.

Handy for those who wish to bypass physically unplugging and then re-plugging a drive to get it detected again.

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Link to post
Share on other sites
  • 6 months later...

Pull it out of the socket?

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

Link to post
Share on other sites
  • 8 months later...

@ wraithdu have enough time I'm looking for something like this, thanks for sharing worked for me.

Link to post
Share on other sites
  • 2 weeks later...

I'm pretty sure it's by design that you have to remove it and re-insert it. A work around that I have used is to disable and reenable the USB Root Hub the device is attached to. Takes a little time but it's the only way I'm aware of to reinitialize a device once it has been "ejected" OR "safely removed" without physical intervention.

edit: You could prob use disk mgmt to remount an "ejected" drive.....I think?

or use the devcon.exe tool if you don't mind another dependency.

Edited by spudw2k
Link to post
Share on other sites
  • 3 weeks later...

I got this :(

>"C:Program FilesAutoIt3SciTE..autoit3.exe" /ErrorStdOut "C:Documents and SettingsGAMESDesktopNew AutoIt v3 Script.au3"
C:Documents and SettingsGAMESDesktopNew AutoIt v3 Script.au3 (110) : ==> Variable used without being declared.:
Local $hVolume = DllCall("kernel32.dll", "ptr", "CreateFileW", "wstr", $szVolumeAccessPath, "dword", 0, "dword", BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), "ptr", 0, "dword", $OPEN_EXISTING, "dword", 0, "ptr", 0)
Local $hVolume = DllCall("kernel32.dll", "ptr", "CreateFileW", "wstr", $szVolumeAccessPath, "dword", 0, "dword", BitOR(^ ERROR
>Exit code: 1    Time: 2.319

I don't know how to solve the problem:(

Link to post
Share on other sites
  • 1 month later...

Hi there, It doesn't work, I'm using Win 7 Home and Kingmax USB 2.0:)

>"C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.exe" /run /prod /ErrorStdOut /in "D:AutoITUSB SecurityRemover.au3" /UserParams    
+>11:47:57 Starting AutoIt3Wrapper v.2.1.0.33    Environment(Language:0409  Keyboard:00000409  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64)
>Running AU3Check (1.54.22.0)  params:-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6  from:C:Program Files (x86)AutoIt3
+>11:47:57 AU3Check ended.rc:0
>Running:(3.3.8.1):C:Program Files (x86)AutoIt3autoit3.exe "D:AutoITUSB SecurityRemover.au3"    
--> Press Ctrl+Alt+F5 to Restart or Ctrl+Break to Stop
-Device Number:  2
-Drive Type:  2
-DOS Device Name:  DeviceHarddiskVolume13
-Device Path:  ?usbstor#disk&ven_kingmax&prod_usb2.0_flashdisk&rev_1100#a120000000001424&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
-Device ID:  2368
-Device Instance:  USBSTORDISK&VEN_KINGMAX&PROD_USB2.0_FLASHDISK&REV_1100A120000000001424&0
-Device Instance Parent:  2524
-Parent Device ID:  USBVID_1687&PID_3254A120000000001424
-IsRemovable:  True
-Is USBHDD:  False
D:AutoITUSB SecurityRemoveUSB.au3 (268) : ==> Subscript used with non-Array variable.:
If $res[0] = $CR_ACCESS_DENIED Then
If $res^ ERROR
->11:48:17 AutoIT3.exe ended.rc:1
>Exit code: 1    Time: 21.815
Link to post
Share on other sites

No no! I am just a newbie, I don't understand much about AutoIT. Teach me but please don't criticize me:((

Here is my code (taken from your first post):

Note : "RemoveUSB.au3" is your UDF in the first post.

I'm using Win 7.

#include "RemoveUSB.au3"
#NoTrayIcon
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
;;;EXAMPLE
Global $drive = InputBox("Eject Drive", "Enter drive (letter only):", "", " M1", 200, 130)
If @error Then Exit
$drive = StringUpper(StringLeft($drive, 1))
If Not FileExists($drive & ":\") Then
MsgBox(0 + 16, "Error", "Drive not found.")
Exit
EndIf

Global $driveInfo[9] = ["Device Number", "Drive Type", "DOS Device Name", "Device Path", "Device ID", "Device Instance", "Device Instance Parent", _
                     "Parent Device ID", "IsRemovable"]
Global $driveArray = _QueryDrive($drive)
For $i = 0 To UBound($driveArray) - 1
ConsoleWrite("-" & $driveInfo[$i] & ": " & $driveArray[$i] & @CRLF)
Next
ConsoleWrite("-Is USBHDD: " & _IsUSBHDD($driveArray) & @CRLF)

If (6 = MsgBox(4 + 32, "Eject?", "Eject this drive?")) Then ConsoleWrite("Ejecting drive <" & $drive & ":> - " & _QueryDrive($drive, True) & @CRLF)
Link to post
Share on other sites

Here's my latest version (v1.5) of the program I made using the detail from this topic.

Many thanks to wraithdu, etc.

Please note, that some elements use the devcon.exe, which can be downloaded from Microsoft (see the link in my post #42 above). Also note, that I have not yet tried this program outside of Win XP.

You will also need to obtain the 3rd party Unlocker program for that element to work. I'm aware, that someone has written some code here in AutoIt to do this now, but my first experiment with it failed, and I've not yet had time to look into it .... hopefully it will be in a later version, though I welcome anyone here willing to do so sooner.

For obvious reasons, I removed devcon.exe from the zip, so if you use my Universal Installer program, it may complain, etc. I have also added my program source, which was built with AutoIt v3.0.0.0.

USB Safe Ejector v1.5.zip

As always, see my Disclaimer here.

Win 7 users please report your success or otherwise here.

Edited by TheSaint

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Link to post
Share on other sites
Link to post
Share on other sites

@TheSaint, Thanks for the improvements made.

No worries, happy to share.

Please note, that I remembered a bug today, that's not major, but should be fixed.

This is where you start the program, with only one USB drive inserted, then later insert another one, then do a refresh, and then click Eject or Eject All, with the Close On Exit option enabled. The program behaves as if only one drive is loaded and so exits, which it is not currently supposed to do with more than one drive loaded. Obviously I forgot to program a reset for the number of drives (stored variable value), when refresh is used.

When I get a chance, I'll fix that, plus maybe the Unlock alternative. Then I may make it's own topic, linked to this one, and with some screen shots, etc. However, I do welcome an attempt by any other person here, that includes Win 7 checking & at the very least, icon changes for buttons.

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Link to post
Share on other sites

No no! I am just a newbie, I don't understand much about AutoIT. Teach me but please don't criticize me:((

Here is my code (taken from your first post):

Note : "RemoveUSB.au3" is your UDF in the first post.

I'm using Win 7.

Ok then... the error is appearing from line 268 which checks the return of the previous DllCall. For some reason the acutall DllCall to setupapi.dll is failing on your system (not the function failing, but the actual call). See if you can figure out why. Try adding this line before the 'If $res[0] = ' test:

ConsoleWrite("error: " & @error & @CRLF)

On another note, I looked at your other devcon post. If devcon is also failing, this points to a more general problem in your system.

Link to post
Share on other sites

I updated the OP with a function to restart a stopped drive... one function call. Who knew it was that easy? Test it out on other platforms like XP, but it worked on my Win7 x64 machine.

Thanks, sounds good, will check it out. I take it this means we can drop the dependency of devcon.exe then.

It appears the little bug I mentioned in my program above, should be fixed by adding a single short line -> $quit = ""

Which I insert about Line 411 or 412. This is inside the AddBasicControls function, after -> If $found > 1 Then

You could if you wish instead, put it in the function before that point too.

I will upload an updated version of the program, after deciding on a few other changes and looking into the restart element that wraithdu has just added. Some of the changes, may be another window to run an assortment of drive related programs, much like the standard autorun gui that Windows offers, plus an Options window for things like labeling & formatting, etc.

I've also had another play with AutoIt Unlocker, which you can find It still fails for me, but I've determined that at the very least, I need to run a newer version of AutoIt, because some functions called, are not currently available to my latest install ... I always run a bit behind the times.

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Link to post
Share on other sites

@TheSaint

I gave up on unlocking handles with AutoIt a long time ago. To do it properly really requires a kernel mode driver. To that end, I wrote a wrapper around the Sysinternals tool handle.exe which I use in a USB HDD removal script. Just place handle.exe in the script directory and call _SearchAndCloseHandles with the drive letter and a colon, ie E: .

The function returns the number of remaining open handles when you click the 'Done' button, or 0 if you click the 'Ignore' button.

#include-once
#include 
#include 
#include 
#include 
#include 

; function returns number of remaining open handles
Func _SearchAndCloseHandles($szSearch)
    ; handle registry key
    Local $fDelete
    If __RegValueExists("HKEY_CURRENT_USERSoftwareSysinternalsHandle", "EulaAccepted") Then
        $fDelete = False
    Else
        $fDelete = True
        RegWrite("HKEY_CURRENT_USERSoftwareSysinternalsHandle", "EulaAccepted", "REG_DWORD", 1)
    EndIf
    Local $HandlePath = @ScriptDir & "handle.exe"
    If Not FileExists($HandlePath) Then Return SetError(1, 0, 0)
    Local $retval = 0
    Local $szData = _GetHandles($HandlePath, $szSearch)
    Local $iPid = @extended ; handle.exe PID
    If Not StringInStr($szData, "No matching handles found.") Then
        Local $gui = GUICreate("Check Handles to Close...", 700, 600, -1, -1, BitOR($WS_MINIMIZEBOX, $WS_CAPTION, $WS_POPUP, $WS_SYSMENU, $WS_SIZEBOX))
        Local $lv = GUICtrlCreateListView("Process|PID|Handle|Path", 0, 0, 700, 550, BitOR($LVS_SHOWSELALWAYS, $LVS_SINGLESEL, $LVS_NOSORTHEADER))
        GUICtrlSetResizing(-1, $GUI_DOCKBORDERS)
        Local $hLV = GUICtrlGetHandle($lv)
        _GUICtrlListView_SetExtendedListViewStyle($hLV, BitOR($LVS_EX_GRIDLINES, $LVS_EX_DOUBLEBUFFER, $LVS_EX_CHECKBOXES))
        Local $close = GUICtrlCreateButton("Close Handles", 10, 560)
        Local $ignore = GUICtrlCreateButton("Ignore Open Handles", 100, 560)
        Local $done = GUICtrlCreateButton("Done", 223, 560)
        _UpdateListView($lv, $szData, $iPid)
        ; show it
        Local $msg, $oldOpt = Opt("GuiOnEventMode", 0)
        GUISetState()
        Do
            $msg = GUIGetMsg()
            Switch $msg
                Case $close
                    GUISetCursor(15, 1, $gui)
                    _CloseCheckedHandles($hLV, $HandlePath)
                    $szData = _GetHandles($HandlePath, $szSearch)
                    $iPid = @extended
                    _UpdateListView($lv, $szData, $iPid)
                    GUISetCursor()
                    $retval = _GUICtrlListView_GetItemCount($hLV)
                    If Not $retval Then ExitLoop ; exit loop if no more handles
                Case $ignore
                    ; ignore open handles, return 0 anyway
                    $retval = 0
                    ExitLoop
                Case $done
                    $retval = _GUICtrlListView_GetItemCount($hLV)
                    ExitLoop
            EndSwitch
        Until $msg = $GUI_EVENT_CLOSE
        Opt("GuiOnEventMode", $oldOpt)
        GUIDelete($gui)
    EndIf
    ; handle registry key cleanup
    If $fDelete Then
        RegDelete("HKEY_CURRENT_USERSoftwareSysinternalsHandle", "EulaAccepted")
        If __RegKeyEmpty("HKEY_CURRENT_USERSoftwareSysinternalsHandle") Then RegDelete("HKEY_CURRENT_USERSoftwareSysinternalsHandle")
        If __RegKeyEmpty("HKEY_CURRENT_USERSoftwareSysinternals") Then RegDelete("HKEY_CURRENT_USERSoftwareSysinternals")
    EndIf
    Return $retval ; number of remaining open handles
EndFunc

Func _GetHandles($HandlePath, $szSearch)
    Local $iPid = Run('"' & $HandlePath & '" "' & $szSearch & '"', "", @SW_HIDE, 0x8) ; STDERR_MERGED
    ProcessWaitClose($iPid)
    Local $szData = ""
    While 1
        $szData &= StdoutRead($iPid)
        If @error Then ExitLoop
    WEnd
    Return SetError(0, $iPid, $szData)
EndFunc

Func _UpdateListView($lv, $szData, $iPid)
    Local $hLV = GUICtrlGetHandle($lv)
    _GUICtrlListView_BeginUpdate($hLV)
    _GUICtrlListView_DeleteAllItems($hLV)
    If Not StringInStr($szData, "No matching handles found.") Then
        Local $iItem, $newData
        Local $aData = StringSplit($szData, @CRLF, 3)
        ; remove extraneous data: 1st five rows, last row
        For $i = 1 To 5
            _ArrayDelete($aData, 0)
        Next
        _ArrayDelete($aData, UBound($aData) - 1)
        _ArraySort($aData) ; sort by process name
        ; remove handle.exe handles from array by PID
        For $i = UBound($aData) - 1 To 0 Step -1; iterate in reverse
            $newData = StringRegExp($aData[$i], "pid: (d+?)s", 1)
            If Number($newData[0]) = $iPid Then _ArrayDelete($aData, $i)
        Next
        ; display data
        For $i = 0 To UBound($aData) - 1
            $newData = StringRegExp($aData[$i], "^(.+?)s+?pid: (d+?)s+?type: (.+?)s+([w]+?): (.+?)$", 1) ; info
            $iItem = _GUICtrlListView_AddItem($hLV, $newData[0]) ; process name
            _GUICtrlListView_AddSubItem($hLV, $iItem, $newData[1], 1) ; PID
            _GUICtrlListView_AddSubItem($hLV, $iItem, $newData[3], 2) ; handle HEX value
            _GUICtrlListView_AddSubItem($hLV, $iItem, $newData[4], 3) ; handle path
        Next
        _GUICtrlListView_SetColumnWidth($hLV, 0, $LVSCW_AUTOSIZE_USEHEADER)
        _GUICtrlListView_SetColumnWidth($hLV, 1, 60)
        _GUICtrlListView_SetColumnWidth($hLV, 2, $LVSCW_AUTOSIZE_USEHEADER)
        _GUICtrlListView_SetColumnWidth($hLV, 3, $LVSCW_AUTOSIZE_USEHEADER)
    EndIf
    _GUICtrlListView_EndUpdate($hLV)
EndFunc

Func _CloseCheckedHandles($hLV, $HandlePath)
    ; to close handle
    ; handle.exe -c HEX -y -p PID
    Local $iPID, $sHandle, $iElem = _GUICtrlListView_GetItemCount($hLV)
    For $i = 0 To $iElem - 1
        If _GUICtrlListView_GetItemChecked($hLV, $i) Then
            $iPID = _GUICtrlListView_GetItemText($hLV, $i, 1)
            $sHandle = _GUICtrlListView_GetItemText($hLV, $i, 2)
            RunWait('"' & $HandlePath & '" -c ' & $sHandle & ' -y -p ' & $iPID, "", @SW_HIDE)
        EndIf
    Next
EndFunc

Func __RegValueExists($s_key, $s_val)
    RegRead($s_key, $s_val)
    ; @error = -2 is 'type not supported', implying value exists
    If @error = -1 Or @error >= 1 Then ; value does not exist
        Return 0
    Else
        Return 1
    EndIf
EndFunc ;==>__RegValueExists

Func __RegKeyEmpty($s_key)
    Local $err1 = 0, $err2 = 0

    ; check for keys
    RegEnumKey($s_key, 1)
    If @error Then $err1 = 1
    ; check for values
    RegEnumVal($s_key, 1)
    If @error Then $err2 = 1
    ; set return
    If $err1 And $err2 Then ; empty
        Return 1
    Else
        Return 0
    EndIf
EndFunc ;==>__RegKeyEmpty
Edited by wraithdu
Link to post
Share on other sites

@wraithdu - No worries, thanks for that, I'll look into adding it.

Have you ever had a scenario where it doesn't work?

As on occasion, Unlocker will fail to find anything open. My understanding, is that is caused by a Windows (Explorer) bug?

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Link to post
Share on other sites

I have had times when NTFS formatted USB HDDs (not flash drives) have been stuck by explorer because of filesystem journals etc. In that case my handle script has always found those handles and can close them. The other part of my script from above uses sdparm to physically sync and spin down HDDs. I tend to do that and then just disconnect the USB rather than forcing closed filesystem handles. I suppose it's really about the same.

But to answer your question, no, in recent memory I have not had a drive with no open handles refuse to be ejected.

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
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...