EjectUSB
Posted: Tue Apr 01, 2008 4:41 pm
TPFC Forums
https://www.portablefreeware.com/forums/
https://www.portablefreeware.com/forums/viewtopic.php?t=3075
Code: Select all
; ----------------------------------------------------------------------------
;
; EjectUSB
; AutoIt Version: 3.0
; Language: English
; Platform: Win9x/NT
; Author: Queue
;
; Script Function:
; Closes all programs running from Flash Drive then ejects.
;
; ----------------------------------------------------------------------------
; Prepare environment
#NoTrayIcon
$name = "EjectUSB"
$version = "1.0"
$title = "Remove Flash Drive"
$sysdrive = StringLeft(@WindowsDir, 3)
; Check parameters
If $CmdLine[0] = 0 Then
$file = @ScriptDir
Else
If $CmdLine[1] = "/help" OR $CmdLine[1] = "/h" OR $CmdLine[1] = "/?" _
OR $CmdLine[1] = "-h" OR $CmdLine[1] = "-?" Then
MsgBox(64, $title, "The syntax for this utility is:" & @CRLF & $name & ".exe DriveLetterToEject")
Exit 0
Else
If FileExists($CmdLine[1]) Then
$file = $CmdLine[1]
ElseIf StringLen($CmdLine[1]) = 1 Then
If FileExists($CmdLine[1] & ":") Then
$file = $CmdLine[1] & ":\"
Else
$file = @ScriptDir
EndIf
Else
$file = @ScriptDir
EndIf
EndIf
EndIf
; Find current drive letter if not specified
If $file = @ScriptDir Then
If StringMid(@ScriptDir,2,1)=":" Then
$file = StringLeft(@ScriptDir, 2) & "\"
Else
$file = StringLeft(@ScriptDir, StringInStr(@ScriptDir, "\", 2, 3))
EndIf
EndIf
; Append backslash if missing
If StringRight($file, 1) <> "\" Then
$file &= "\"
EndIf
; Prevent system drive eject
If $file = $sysdrive OR StringInStr($file, @WindowsDir, 2) <> 0 Then
MsgBox(16, $title, "Cannot Eject the System Drive.")
Exit 0
EndIf
; Read INI settings
$timetowait = IniRead(@ScriptDir & "\" & $name & ".ini", "TimeToWait", "Seconds", "5")
$exceptions = IniReadSection(@ScriptDir & "\" & $name & ".ini", "Exceptions")
; Process exceptions
If @error = 0 Then
If $exceptions[0][0] > 0 Then
$list = ProcessList()
For $i = 1 To $list[0][0]
$loc = _ProcessGetLocation($list[$i][1])
If StringLeft($loc, StringLen($file)) = $file Then
If StringInStr($loc, @ScriptName, 2) = 0 Then
$k = 0
For $j = 1 To $exceptions[0][0]
If StringInStr($loc, $exceptions[$j][1], 2) <> 0 Then $k = 1
Next
If $k = 0 Then
If ProcessExists($list[$i][1]) Then ProcessClose($list[$i][1])
EndIf
EndIf
EndIf
Next
$list = ProcessList()
For $i = 1 To $list[0][0]
$loc = _ProcessGetLocation($list[$i][1])
If StringLeft($loc, StringLen($file)) = $file Then
If StringInStr($loc, @ScriptName, 2) = 0 Then
If ProcessExists($list[$i][1]) Then ProcessWaitClose($list[$i][1], Number($timetowait))
EndIf
EndIf
Next
EndIf
EndIf
; Terminate programs
$list = ProcessList()
For $i = 1 To $list[0][0]
$loc = _ProcessGetLocation($list[$i][1])
If StringLeft($loc, StringLen($file)) = $file Then
If StringInStr($loc, @ScriptName, 2) = 0 Then
If ProcessExists($list[$i][1]) Then ProcessClose($list[$i][1])
EndIf
EndIf
Next
_RefreshSystemTray()
; Run drive ejector
If StringLen($file) = 3 Then
If StringRight($file, 2) = ":\" Then
If FileExists(@ScriptDir & "\RemoveDrive.exe") Then
Run(@ScriptDir & "\RemoveDrive.exe " & StringLeft($file, 2))
ElseIf FileExists(@ScriptDir & "\USB_Disk_Eject.exe") Then
Run(@ScriptDir & "\USB_Disk_Eject.exe /REMOVELETTER " & StringLeft($file, 1))
EndIf
EndIf
EndIf
Exit 1
; -------------------------- Begin Custom Functions ---------------------------
Func _ProcessGetLocation($iPID)
Local $aProc = DllCall('kernel32.dll', 'hwnd', 'OpenProcess', 'int', BitOR(0x0400, 0x0010), 'int', 0, 'int', $iPID)
If $aProc[0] = 0 Then Return SetError(1, 0, '')
Local $vStruct = DllStructCreate('int[1024]')
DllCall('psapi.dll', 'int', 'EnumProcessModules', 'hwnd', $aProc[0], 'ptr', DllStructGetPtr($vStruct), 'int', DllStructGetSize($vStruct), 'int*', 0)
Local $aReturn = DllCall('psapi.dll', 'int', 'GetModuleFileNameEx', 'hwnd', $aProc[0], 'int', DllStructGetData($vStruct, 1), 'str', '', 'int', 2048)
If StringLen($aReturn[3]) = 0 Then Return SetError(2, 0, '')
Return $aReturn[3]
EndFunc
; ===================================================================
; _RefreshSystemTray($nDealy = 1000)
;
; Removes any dead icons from the notification area.
; Parameters:
; $nDelay - IN/OPTIONAL - The delay to wait for the notification area to expand with Windows XP's
; "Hide Inactive Icons" feature (In milliseconds).
; Returns:
; Sets @error on failure:
; 1 - Tray couldn't be found.
; 2 - DllCall error.
; ===================================================================
Func _RefreshSystemTray($nDelay = 1000)
; Save Opt settings
Local $oldMatchMode = Opt("WinTitleMatchMode", 4)
Local $oldChildMode = Opt("WinSearchChildren", 1)
Local $error = 0
Do; Pseudo loop
Local $hWnd = WinGetHandle("classname=TrayNotifyWnd")
If @error Then
$error = 1
ExitLoop
EndIf
Local $hControl = ControlGetHandle($hWnd, "", "Button1")
; We're on XP and the Hide Inactive Icons button is there, so expand it
If $hControl <> "" And ControlCommand($hWnd, "", $hControl, "IsVisible") Then
ControlClick($hWnd, "", $hControl)
Sleep($nDelay)
EndIf
Local $posStart = MouseGetPos()
Local $posWin = WinGetPos($hWnd)
Local $y = $posWin[1]
While $y < $posWin[3] + $posWin[1]
Local $x = $posWin[0]
While $x < $posWin[2] + $posWin[0]
DllCall("user32.dll", "int", "SetCursorPos", "int", $x, "int", $y)
If @error Then
$error = 2
ExitLoop 3; Jump out of While/While/Do
EndIf
$x = $x + 8
WEnd
$y = $y + 8
WEnd
DllCall("user32.dll", "int", "SetCursorPos", "int", $posStart[0], "int", $posStart[1])
; We're on XP so we need to hide the inactive icons again.
If $hControl <> "" And ControlCommand($hWnd, "", $hControl, "IsVisible") Then
ControlClick($hWnd, "", $hControl)
EndIf
Until 1
; Restore Opt settings
Opt("WinTitleMatchMode", $oldMatchMode)
Opt("WinSearchChildren", $oldChildMode)
SetError($error)
EndFunc; _RefreshSystemTray()
Code: Select all
; Utility AutoIt3 Script
;
; Prepared by Queue
;
; Last compiled with AutoIt3 version 3.2.10.0
; http://www.autoitscript.com/autoit3/
;
; Uses RefreshSystemTray version 1st Feb 2005
; http://www.autoitscript.com/forum/index.php?showtopic=7404
;
; [QM]
;
; No Copyright. No Company.
; ========================================================================================
;symbols
#NoTrayIcon
$name = "EjectUSB"
$title = "Remove Portable Drive"
;end
; ========================================================================================
;code
; Parse parameters
If $CmdLine[0] = 0 Then
; Find current drive letter if not specified
If StringMid(@ScriptDir,2,1) = ":" Then
$file = StringLeft(@ScriptDir, 2) & "\"
Else
$file = StringLeft(@ScriptDir, StringInStr(@ScriptDir, "\", 2, 3))
EndIf
Else
If $CmdLine[1] = "/help" OR $CmdLine[1] = "/h" OR $CmdLine[1] = "/?" _
OR $CmdLine[1] = "-h" OR $CmdLine[1] = "-?" Then
MsgBox(64, $title, "The syntax for this utility is:" & @CRLF & $name & ".exe DriveLetterToEject")
Exit 0
Else
If FileExists($CmdLine[1]) Then
$file = $CmdLine[1]
ElseIf StringLen($CmdLine[1]) = 1 AND FileExists($CmdLine[1] & ":\") Then
$file = $CmdLine[1] & ":\"
Else
; Find current drive letter if invalid value specified
If StringMid(@ScriptDir,2,1) = ":" Then
$file = StringLeft(@ScriptDir, 2) & "\"
Else
$file = StringLeft(@ScriptDir, StringInStr(@ScriptDir, "\", 2, 3))
EndIf
EndIf
EndIf
EndIf
; Append backslash if missing
If StringRight($file, 1) <> "\" Then
$file &= "\"
EndIf
; Prevent system drive eject
If $file = StringLeft(@WindowsDir, 3) OR StringInStr($file, @WindowsDir, 2) <> 0 Then
MsgBox(16, $title, "Cannot Eject the System Drive.")
Exit 0
EndIf
; Read INI settings
$timetowait = IniRead(@ScriptDir & "\" & $name & ".ini", "TimeToWait", "Seconds", "5")
SetError(0)
$exclusions = IniReadSection(@ScriptDir & "\" & $name & ".ini", "Exclusions")
$excerror = @error
$inclusions = IniReadSection(@ScriptDir & "\" & $name & ".ini", "Inclusions")
$incerror = @error
If $excerror = 0 Then
If $exclusions[0][0] = 0 Then $excerror = 1
EndIf
If $incerror = 0 Then
If $inclusions[0][0] = 0 Then $incerror = 1
EndIf
; Process exclusions and inclusions
$list = ProcessList()
For $i = 1 To $list[0][0]
$loc = _ProcessGetLocation($list[$i][1])
$k = 0
If StringLeft($loc, StringLen($file)) = $file Then
If StringInStr($loc, @ScriptName, 2) = 0 Then
$k = 1
If $excerror = 0 Then
For $j = 1 To $exclusions[0][0]
If StringInStr($loc, $exclusions[$j][1], 2) <> 0 Then $k = 0
Next
EndIf
EndIf
EndIf
; Process inclusions
If $incerror = 0 Then
For $j = 1 To $inclusions[0][0]
If StringInStr($loc, $inclusions[$j][1], 2) <> 0 Then $k = 1
Next
EndIf
; Terminate programs
If $k = 1 Then
If ProcessExists($list[$i][1]) Then ProcessClose($list[$i][1])
EndIf
Next
If $excerror = 0 Then
$list = ProcessList()
For $i = 1 To $list[0][0]
$loc = _ProcessGetLocation($list[$i][1])
$k = 0
If StringLeft($loc, StringLen($file)) = $file Then
If StringInStr($loc, @ScriptName, 2) = 0 Then
$k = 1
For $j = 1 To $exclusions[0][0]
If StringInStr($loc, $exclusions[$j][1], 2) <> 0 Then $k = 0
Next
EndIf
EndIf
; Process inclusions
If $incerror = 0 Then
For $j = 1 To $inclusions[0][0]
If StringInStr($loc, $inclusions[$j][1], 2) <> 0 Then $k = 1
Next
EndIf
; Wait for programs to close
If $k = 1 Then
If ProcessExists($list[$i][1]) Then ProcessWaitClose($list[$i][1], Number($timetowait))
EndIf
Next
; Terminate programs
$list = ProcessList()
For $i = 1 To $list[0][0]
$loc = _ProcessGetLocation($list[$i][1])
$k = 0
If StringLeft($loc, StringLen($file)) = $file Then
If StringInStr($loc, @ScriptName, 2) = 0 Then
$k = 1
EndIf
EndIf
; Process inclusions
If $incerror = 0 Then
For $j = 1 To $inclusions[0][0]
If StringInStr($loc, $inclusions[$j][1], 2) <> 0 Then $k = 1
Next
EndIf
; Terminate programs
If $k = 1 Then
If ProcessExists($list[$i][1]) Then ProcessClose($list[$i][1])
EndIf
Next
EndIf
; Wait for programs to close
$list = ProcessList()
For $i = 1 To $list[0][0]
$loc = _ProcessGetLocation($list[$i][1])
$k = 0
If StringLeft($loc, StringLen($file)) = $file Then
If StringInStr($loc, @ScriptName, 2) = 0 Then
$k = 1
EndIf
EndIf
; Process inclusions
If $incerror = 0 Then
For $j = 1 To $inclusions[0][0]
If StringInStr($loc, $inclusions[$j][1], 2) <> 0 Then $k = 1
Next
EndIf
; Wait for programs to close
If $k = 1 Then
If ProcessExists($list[$i][1]) Then
If ProcessWaitClose($list[$i][1], Number($timetowait)) = 0 Then
If ProcessExists($list[$i][1]) Then ProcessClose($list[$i][1])
EndIf
EndIf
EndIf
Next
; Close PStart portable Win98SE error window
WinClose("PStart portable")
; Remove dead system tray icons
_RefreshSystemTray()
; Close explorer windows that are viewing the portable drive
Opt("WinTitleMatchMode", -1)
$list = WinList($file)
For $i = 1 To $list[0][0]
WinClose($list[$i][0])
Next
; Run drive ejector
If StringLen($file) = 3 Then
If StringRight($file, 2) = ":\" Then
If FileExists(@ScriptDir & "\RemoveDrive.exe") Then
Run(@ScriptDir & "\RemoveDrive.exe " & StringLeft($file, 2), "", @SW_HIDE)
ElseIf FileExists(@ScriptDir & "\USB_Disk_Eject.exe") Then
Run(@ScriptDir & "\USB_Disk_Eject.exe /REMOVELETTER " & StringLeft($file, 1))
EndIf
EndIf
EndIf
Exit 1
; ........................................................................................
Func _ProcessGetLocation($iPID)
Local $hProc = DllCall("kernel32.dll", "int", "OpenProcess", "int", 0x0410, "int", 0, "int", $iPID)
If @error OR $hProc[0] = 0 Then
If $hProc[0] Then DllCall("kernel32.dll", "int", "CloseHandle", "int", $hProc[0])
Return SetError(1, 0, "")
EndIf
Local $stHMod = DllStructCreate("int hMod")
Local $stCB = DllStructCreate("dword cbNeeded")
Local $resEnum = DllCall("psapi.dll", "int", "EnumProcessModules", "int", $hProc[0], "ptr", DllStructGetPtr($stHMod), "dword", DllStructGetSize($stHMod), "ptr", DllStructGetPtr($stCB, 1))
If @error OR $resEnum[0] = 0 Then
DllCall("kernel32.dll", "int", "CloseHandle", "int", $hProc[0])
Return SetError(2, 0, "")
EndIf
Local $resPath = DllCall("psapi.dll", "int", "GetModuleFileNameEx", "int", $hProc[0], "int", DllStructGetData($stHMod, 1), "str", "", "dword", 32768)
DllCall("kernel32.dll", "int", "CloseHandle", "int", $hProc[0])
If @error Then
Return SetError(3, 0, "")
EndIf
SetError(0)
Return $resPath[3]
EndFunc
; ........................................................................................
; _RefreshSystemTray($nDelay = 1000)
;
; Removes any dead icons from the system tray.
; Parameters:
; $nDelay - IN/OPTIONAL - The delay to wait for the notification area to expand with
; Windows XP's "Hide Inactive Icons" feature (In milliseconds).
; Returns:
; Sets @error on failure:
; 1 - Tray couldn't be found.
; 2 - DllCall error.
; ........................................................................................
Func _RefreshSystemTray($nDelay = 1000)
; Save Opt settings
Local $oldMatchMode = Opt("WinTitleMatchMode", 4)
Local $oldChildMode = Opt("WinSearchChildren", 1)
Local $error = 0
Do; Pseudo loop
Local $hWnd = WinGetHandle("classname=TrayNotifyWnd")
If @error Then
$error = 1
ExitLoop
EndIf
Local $hControl = ControlGetHandle($hWnd, "", "Button1")
; We're on XP and the Hide Inactive Icons button is there, so expand it
If $hControl <> "" And ControlCommand($hWnd, "", $hControl, "IsVisible") Then
ControlClick($hWnd, "", $hControl)
Sleep($nDelay)
EndIf
Local $posStart = MouseGetPos()
Local $posWin = WinGetPos($hWnd)
Local $y = $posWin[1]
While $y < $posWin[3] + $posWin[1]
Local $x = $posWin[0]
While $x < $posWin[2] + $posWin[0]
DllCall("user32.dll", "int", "SetCursorPos", "int", $x, "int", $y)
If @error Then
$error = 2
ExitLoop 3; Jump out of While/While/Do
EndIf
$x = $x + 8
WEnd
$y = $y + 8
WEnd
DllCall("user32.dll", "int", "SetCursorPos", "int", $posStart[0], "int", $posStart[1])
; We're on XP so we need to hide the inactive icons again.
If $hControl <> "" And ControlCommand($hWnd, "", $hControl, "IsVisible") Then
ControlClick($hWnd, "", $hControl)
EndIf
Until 1
; Restore Opt settings
Opt("WinTitleMatchMode", $oldMatchMode)
Opt("WinSearchChildren", $oldChildMode)
SetError($error)
EndFunc; _RefreshSystemTray()
;end
Code: Select all
$Temp = ProcessList()
$Snapshot = ""
For $i = 1 to $Temp[0][0]
$Snapshot=$Snapshot&" "&$Temp[$i][1]
Next
;CONTINUE SCRIPT
$Temp = ProcessList()
Dim $Closelist[20]
For $i = 1 to $Temp[0][0]
If StringInStr( $Snapshot,$Temp[$i][1] )=0 Then
ProcessClose( $Temp[$i][1])
ProcessWaitClose( $Temp[$i][1] )
EndIf
Next
Great, then I have all major testing environments available (98SE, XPSP3, VistaSP1).MiDoJo wrote:Queue, Vista SP1 is me (hurrah)
So far no major problems and I'm fairly meticulous so I seriously doubt there will be any. Just some work to get everything functional between OSes.MiDoJo wrote:Everything seems to go as planned
It shouldn't, it should do the entire ejection process automatically and that's what I need to get figured out. The Vista SP1 dialog seems to have a different button ID or something because the ejection routine worked when I tried it on a Vista (non-SP1) machine.MiDoJo wrote:Should Eject Require Me to click OK on second eject screen? Currently it does.
That's not too surprising; I plan to have a mechanism in place to account for that. Does the ''in use'' message happen every time you try and eject with EjectUSB? It actually depends upon the speed that Windows copes with trying to eject the drive; on 98SE Windows is so slow that EjectUSB has time to close before Windows finishes ejecting, but on XP (and I presume Vista) Windows is fast enough that the drive can't eject because EjectUSB hasn't closed yet.MiDoJo wrote:I ran into some problems (drive cannot be eject it is in use type errors, wink (from John Haller) was recording from a different portable drive when I got this message) but they are not repeatable and do not seem to be related to USB Port type/Hub.
Here's hoping!MiDoJo wrote:I think that the program will be great use for Portie Users
Alrighty.MiDoJo wrote:Downloading regshot after lunch so I can get you the reg data on the latest release.
Good. The next time you test can you have an Explorer window open to your portable drive and see if it closes when you run EjectUSB as well? Not sure what happens on Vista in that regard.MiDoJo wrote:My test programs that are running off of my test drive are
Peazip
MMCompView
Unknown Devices
All seem to close fine.
And with you. Thanks for all your help!MiDoJo wrote:More later.
Fun working with you.
Code: Select all
>>>> Window <<<<
Title: Stop a Hardware device
Class: #32770
Position: 87, 93
Size: 400, 277
Style: 0x94C802C4
ExStyle: 0x00010101
Handle: 0x00180B1C
>>>> Control <<<<
Class: Button
Instance: 1
ClassnameNN: Button1
Advanced (Class): [CLASS:Button; INSTANCE:1]
ID: 1
Text: OK
Position: 216, 206
Size: 75, 23
ControlClick Coords: 39, 14
Style: 0x50030000
ExStyle: 0x00000004
Handle: 0x00160C68
>>>> Mouse <<<<
Position: 350, 341
Cursor ID: 0
Color: 0x31317A
>>>> StatusBar <<<<
>>>> Visible Text <<<<
Confirm devices to be stopped, Choose OK to continue.
Windows will attempt to stop the following devices. After the devices are stopped they may be removed safely.
OK
Cancel
>>>> Hidden Text <<<<
Code: Select all
>>>> Window <<<<
Title: Safe To Remove Hardware
Class: #32770
Position: 92, 386
Size: 378, 152
Style: 0x94C801C5
ExStyle: 0x00010101
Handle: 0x002B0AF6
>>>> Control <<<<
Class: Button
Instance: 1
ClassnameNN: Button1
Advanced (Class): [CLASS:Button; INSTANCE:1]
ID: 2
Text: OK
Position: 268, 79
Size: 88, 26
ControlClick Coords: 29, 10
Style: 0x50030001
ExStyle: 0x00000004
Handle: 0x000F08D6
>>>> Mouse <<<<
Position: 397, 503
Cursor ID: 2
Color: 0xDCF1FC
>>>> StatusBar <<<<
>>>> Visible Text <<<<
OK
This device can now be safely removed from the computer.
>>>> Hidden Text <<<<
This makes sense; I'm using an older 512 stick and it was reading on a 1.0 USB. Please note: I'm no longer getting this error and was not getting it everytime. Explorer window closes fine.It actually depends upon the speed that Windows copes with trying to eject the drive