Jump to content



Photo

Safely Eject a USB Drive


  • Please log in to reply
60 replies to this topic

#1 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 05:35 AM

UPDATE 3:
Included a _RestartDrive function (needs testing on other platforms). Call _RestartDrive and pass it the query array from _QueryDrive (called with the eject parameter set to FALSE, which is the default).

UPDATE 2:
General fixes and x64 compatibility.

UPDATE 1:
I updated this script a while ago and never posted it, so here it is. It can be used two ways now. First and foremost it returns a lot of low level info about your attached device. Second, it can be used to eject that device the same way as the old script.

I've seen several solutions on this board and on others, in AutoIt, VB, and C++. Even the one from MSDN. None of them worked correctly. Either nothing happened, or the device was unmounted but the drive letter was still present in explorer and the drive was not "Safely Removed". Well I came across an article on CodeProject, and after finally getting it all translated, it works perfectly. The script is missing some general comments...I'll get around to it eventually, but I'm tired :P

Please test this on WinXP. It works on Vista and should work on XP, but I wanna be sure.

AutoIt         
#include-once #include <WinAPI.au3> ; Constants ;~ Global Const $FILE_SHARE_READ = 0x1 ;~ Global Const $FILE_SHARE_WRITE = 0x2 ;~ Global Const $OPEN_EXISTING = 3 ;~ Global Const $INVALID_HANDLE_VALUE = Ptr(0xFFFFFFFF) ; invalid ptr value Global Const $IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080 Global Const $DRIVE_REMOVABLE = 2 Global Const $DRIVE_FIXED = 3 Global Const $DRIVE_CDROM = 5 Global Const $DIGCF_PRESENT = 0x2 Global Const $DIGCF_DEVICEINTERFACE = 0x10 Global Const $CR_SUCCESS = 0x0 Global Const $DN_REMOVABLE = 0x4000 Global Const $CM_REMOVE_UI_OK = 0x0 Global Const $CM_REMOVE_UI_NOT_OK = 0x1 Global Const $CM_REMOVE_NO_RESTART = 0x2 Global Const $CM_SETUP_DEVNODE_READY = 0x0 Global Const $CM_SETUP_DEVNODE_RESET = 0x4 Global Const $CR_ACCESS_DENIED = 0x33 Global Const $PNP_VetoTypeUnknown = 0 ; Name is unspecified Global Const $PNP_VetoLegacyDevice = 1 ; Name is an Instance Path Global Const $PNP_VetoPendingClose = 2 ; Name is an Instance Path Global Const $PNP_VetoWindowsApp = 3 ; Name is a Module Global Const $PNP_VetoWindowsService = 4 ; Name is a Service Global Const $PNP_VetoOutstandingOpen = 5 ; Name is an Instance Path Global Const $PNP_VetoDevice = 6 ; Name is an Instance Path Global Const $PNP_VetoDriver = 7 ; Name is a Driver Service Name Global Const $PNP_VetoIllegalDeviceRequest = 8 ; Name is an Instance Path Global Const $PNP_VetoInsufficientPower = 9 ; Name is unspecified Global Const $PNP_VetoNonDisableable = 10 ; Name is an Instance Path Global Const $PNP_VetoLegacyDriver = 11 ; Name is a Service Global Const $PNP_VetoInsufficientRights = 12 ; Name is unspecified ; Structures Global Const $STORAGE_DEVICE_NUMBER = "ulong DeviceType;ulong DeviceNumber;ulong PartitionNumber" Global Const $SP_DEV_BUF = "byte[2052]" Global Const $SP_DEVICE_INTERFACE_DETAIL_DATA = "dword cbSize;wchar DevicePath[1024]" ; created at SP_DEV_BUF ptr Global Const $SP_DEVICE_INTERFACE_DATA = "dword cbSize;byte InterfaceClassGuid[16];dword Flags;ulong_ptr Reserved" ; GUID struct = 16 bytes Global Const $SP_DEVINFO_DATA = "dword cbSize;byte ClassGuid[16];dword DevInst;ulong_ptr Reserved" ; GUIDs ; GUID_DEVINTERFACE_DISK =      0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b ; GUID_DEVINTERFACE_CDROM =     0x53f56308L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b ; GUID_DEVINTERFACE_FLOPPY =    0x53f56311L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b Global Const $guidDisk = DllStructCreate($tagGUID) DllStructSetData($guidDisk, "Data1", 0x53f56307) DllStructSetData($guidDisk, "Data2", 0xb6bf) DllStructSetData($guidDisk, "Data3", 0x11d0) DllStructSetData($guidDisk, "Data4", Binary("0x94f200a0c91efb8b")) Global Const $guidCDROM = DllStructCreate($tagGUID) DllStructSetData($guidCDROM, "Data1", 0x53f56308) DllStructSetData($guidCDROM, "Data2", 0xb6bf) DllStructSetData($guidCDROM, "Data3", 0x11d0) DllStructSetData($guidCDROM, "Data4", Binary("0x94f200a0c91efb8b")) Global Const $guidFloppy = DllStructCreate($tagGUID) DllStructSetData($guidFloppy, "Data1", 0x53f56311) DllStructSetData($guidFloppy, "Data2", 0xb6bf) DllStructSetData($guidFloppy, "Data3", 0x11d0) DllStructSetData($guidFloppy, "Data4", Binary("0x94f200a0c91efb8b")) Func _IsUSBHDD(ByRef $query)     ; if drive type is fixed (3), and device path contains 'usbstor', and parent device ID contains 'USB', and IsRemovable     Return (($query[1] = 3) And StringInStr($query[3], "usbstor") And StringInStr($query[7], "usb") And $query[8]) EndFunc ;==>_IsUSBHDD Func _QueryDrive($drive, $fEject = False)     ; drive letter only!     $drive = StringLeft($drive, 1)     Local $queryarray[9]     ; [0] = device number (int)     ; [1] = drive type (int)     ; [2] = dos device name (string)     ; [3] = device path (string)     ; [4] = device ID (string)     ; [5] = device instance (int)     ; [6] = device instance parent (int)     ; [7] = parent device ID (string)     ; [8] = IsRemovable (boolean)     ; "X:" -> for GetDriveType     Local $szRootPath = $drive & ":"     ; "X:" -> for QueryDosDevice     Local $szDevicePath = $drive & ":"     ; ".X:" -> to open the volume     Local $szVolumeAccessPath = "." & $drive & ":"     Local $DeviceNumber = -1     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)     $hVolume = $hVolume[0]     If $hVolume <> $INVALID_HANDLE_VALUE Then ;~ ConsoleWrite("hVolume: " & $hVolume & @CRLF)         Local $sdn = DllStructCreate($STORAGE_DEVICE_NUMBER)         Local $res = DllCall("kernel32.dll", "int", "DeviceIoControl", "ptr", $hVolume, "dword", $IOCTL_STORAGE_GET_DEVICE_NUMBER, _                 "ptr", 0, "dword", 0, "ptr", DllStructGetPtr($sdn), "dword", DllStructGetSize($sdn), _                 "dword*", 0, "ptr", 0)         If $res[0] Then             $DeviceNumber = DllStructGetData($sdn, "DeviceNumber") ;~ ConsoleWrite("DeviceNumber: " & $DeviceNumber & @CRLF)             $queryarray[0] = $DeviceNumber             $res = DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hVolume)             If $res[0] And $DeviceNumber <> -1 Then                 Local $DriveType = DllCall("kernel32.dll", "uint", "GetDriveTypeW", "wstr", $szRootPath)                 $DriveType = $DriveType[0] ;~ ConsoleWrite("Drive type: " & $DriveType & @CRLF)                 $queryarray[1] = $DriveType                 ; get the dos device name (like devicefloppy0)                 ; to decide if it's a floppy or not                 $res = DllCall("kernel32.dll", "dword", "QueryDosDeviceW", "wstr", $szDevicePath, "wstr", "", "dword", 260)                 If $res[0] Then                     Local $szDosDeviceName = $res[2] ;~ ConsoleWrite("DosDeviceName: " & $szDosDeviceName & @CRLF)                     $queryarray[2] = $szDosDeviceName                     Local $DevInst = _GetDrivesDevInstByDeviceNumber($DeviceNumber, $DriveType, $szDosDeviceName, $queryarray) ;~ ConsoleWrite("DevInst: " & $DevInst & @CRLF)                     If $DevInst <> 0 Then                         $queryarray[4] = $DevInst                         $res = DllCall("setupapi.dll", "dword", "CM_Get_Device_IDW", "ptr", $DevInst, "wstr", "", "ulong", 1024, "ulong", 0) ;~ ConsoleWrite("DeviceID: " & $res[2] & @CRLF)                         $queryarray[5] = $res[2]                         Local $EjectSuccess = _DevInstQuery($DevInst, $queryarray, $fEject)                     EndIf                 EndIf             EndIf         EndIf     EndIf     If $fEject Then         Return $EjectSuccess     Else         Return $queryarray     EndIf EndFunc ;==>_QueryDrive Func _RestartDrive($queryarray) Local $res = DllCall("setupapi.dll", "dword", "CM_Setup_DevNode", "dword", $queryarray[6], "ulong", 0) Return SetError(Number(Not ($res[0] = 0)), 0, ($res[0] = 0)) EndFunc ;==>_RestartDrive Func _GetDrivesDevInstByDeviceNumber($DeviceNumber, $DriveType, $szDosDeviceName, ByRef $queryarray)     Local $IsFloppy = (StringInStr($szDosDeviceName, "floppy") <> 0) ;~ ConsoleWrite("Is floppy: " & $IsFloppy & @CRLF)     Local $GUID     Switch $DriveType         Case $DRIVE_REMOVABLE             If $IsFloppy Then                 $GUID = DllStructGetPtr($guidFloppy)             Else                 $GUID = DllStructGetPtr($guidDisk)             EndIf         Case $DRIVE_FIXED             $GUID = DllStructGetPtr($guidDisk)         Case $DRIVE_CDROM             $GUID = DllStructGetPtr($guidCDROM)         Case Default             Return 0     EndSwitch     ; Get device interface info set handle     ; for all devices attached to system     Local $hDevInfo = DllCall("setupapi.dll", "ptr", "SetupDiGetClassDevsW", "ptr", $GUID, "ptr", 0, "hwnd", 0, _             "dword", BitOR($DIGCF_PRESENT, $DIGCF_DEVICEINTERFACE))     $hDevInfo = $hDevInfo[0]     If $hDevInfo <> $INVALID_HANDLE_VALUE Then ;~ ConsoleWrite("hDevInfo: " & $hDevInfo & @CRLF)         ; Retrieve a context structure for a device interface         ; of a device information set.         Local $dwIndex = 0         Local $bRet         Local $buf = DllStructCreate($SP_DEV_BUF)         Local $pspdidd = DllStructCreate($SP_DEVICE_INTERFACE_DETAIL_DATA, DllStructGetPtr($buf))         Local $cb_spdidd = 6 ; size of fixed part of structure         If @AutoItX64 Then $cb_spdidd = 8 ; fix for x64         Local $spdid = DllStructCreate($SP_DEVICE_INTERFACE_DATA)         Local $spdd = DllStructCreate($SP_DEVINFO_DATA)         DllStructSetData($spdid, "cbSize", DllStructGetSize($spdid))         While True             $bRet = DllCall("setupapi.dll", "int", "SetupDiEnumDeviceInterfaces", "ptr", $hDevInfo, "ptr", 0, _                     "ptr", $GUID, "dword", $dwIndex, "ptr", DllStructGetPtr($spdid))             If Not $bRet[0] Then ExitLoop             Local $res = DllCall("setupapi.dll", "int", "SetupDiGetDeviceInterfaceDetailW", "ptr", $hDevInfo, "ptr", DllStructGetPtr($spdid), _                     "ptr", 0, "dword", 0, "dword*", 0, "ptr", 0)             Local $dwSize = $res[5] ;~ ConsoleWrite("dwSize: " & $dwSize & @CRLF)             If $dwSize <> 0 And $dwSize <= DllStructGetSize($buf) Then                 DllStructSetData($pspdidd, "cbSize", $cb_spdidd)                 _ZeroMemory(DllStructGetPtr($spdd), DllStructGetSize($spdd))                 DllStructSetData($spdd, "cbSize", DllStructGetSize($spdd))                 $res = DllCall("setupapi.dll", "int", "SetupDiGetDeviceInterfaceDetailW", "ptr", $hDevInfo, "ptr", DllStructGetPtr($spdid), _                         "ptr", DllStructGetPtr($pspdidd), "dword", $dwSize, "dword*", 0, "ptr", DllStructGetPtr($spdd))                 If $res[0] Then ;~ ConsoleWrite("DevicePath: " & DllStructGetData($pspdidd, "DevicePath") & @CRLF)                     $queryarray[3] = DllStructGetData($pspdidd, "DevicePath")                     Local $hDrive = DllCall("kernel32.dll", "ptr", "CreateFileW", "wstr", DllStructGetData($pspdidd, "DevicePath"), "dword", 0, _                             "dword", BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), "ptr", 0, "dword", $OPEN_EXISTING, _                             "dword", 0, "ptr", 0)                     $hDrive = $hDrive[0] ;~ ConsoleWrite("hDrive: " & $hDrive & @CRLF)                     If $hDrive <> $INVALID_HANDLE_VALUE Then                         Local $sdn2 = DllStructCreate($STORAGE_DEVICE_NUMBER)                         $res = DllCall("kernel32.dll", "int", "DeviceIoControl", "ptr", $hDrive, "dword", $IOCTL_STORAGE_GET_DEVICE_NUMBER, _                                 "ptr", 0, "dword", 0, "ptr", DllStructGetPtr($sdn2), "dword", DllStructGetSize($sdn2), _                                 "dword*", 0, "ptr", 0)                         If $res[0] Then                             If $DeviceNumber = DllStructGetData($sdn2, "DeviceNumber") Then                                 $res = DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hDrive) ;~ If Not $res[0] Then ConsoleWrite("Error closing volume: " & $hDrive & @CRLF)                                 $res = DllCall("setupapi.dll", "int", "SetupDiDestroyDeviceInfoList", "ptr", $hDevInfo) ;~ If Not $res[0] Then ConsoleWrite("Destroy error." & @CRLF)                                 Return DllStructGetData($spdd, "DevInst")                             EndIf                         EndIf                         $res = DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hDrive) ;~ If Not $res[0] Then ConsoleWrite("Error closing volume: " & $hDrive & @CRLF)                     EndIf                 EndIf             EndIf             $dwIndex += 1         WEnd         $res = DllCall("setupapi.dll", "int", "SetupDiDestroyDeviceInfoList", "ptr", $hDevInfo) ;~ If Not $res[0] Then ConsoleWrite("Destroy error." & @CRLF)     EndIf     Return 0 EndFunc ;==>_GetDrivesDevInstByDeviceNumber Func _DevInstQuery($DevInst, ByRef $queryarray, $fEject)     ; get drives's parent, e.g. the USB bridge,     ; the SATA port, an IDE channel with two drives, etc.     Local $res = DllCall("setupapi.dll", "dword", "CM_Get_Parent", "dword*", 0, "dword", $DevInst, "ulong", 0)     If $res[0] = $CR_SUCCESS Then         Local $DevInstParent = $res[1] ;~ ConsoleWrite("DevInstParent: " & $DevInstParent & @CRLF)         $queryarray[6] = $DevInstParent         $res = DllCall("setupapi.dll", "dword", "CM_Get_Device_IDW", "ptr", $DevInstParent, "wstr", "", "ulong", 1024, "ulong", 0) ;~ ConsoleWrite("Parent DeviceID: " & $res[2] & @CRLF)         $queryarray[7] = $res[2]         $res = DllCall("setupapi.dll", "dword", "CM_Get_DevNode_Status", "ulong*", 0, "ulong*", 0, "ptr", $DevInstParent, "ulong", 0)         Local $IsRemovable = (BitAND($res[1], $DN_REMOVABLE) <> 0) ;~ ConsoleWrite("IsRemovable: " & $IsRemovable & @CRLF)         $queryarray[8] = $IsRemovable     EndIf     If $fEject Then         Local $bSuccess = False         For $tries = 1 To 3             ; sometimes we need some tries... ;~          ConsoleWrite("Try: " & $tries & @CRLF)             $res = DllCall("setupapi.dll", "dword", "CM_Query_And_Remove_SubTreeW", "dword", $DevInstParent, _                     "dword*", 0, "wstr", "", "ulong", 260, "ulong", $CM_REMOVE_UI_OK)             If $res[0] = $CR_ACCESS_DENIED Then ;~              ConsoleWrite("Trying CM_Request_Device_Eject..." & @CRLF)                 $res = DllCall("setupapi.dll", "dword", "CM_Request_Device_EjectW", "dword", $DevInstParent, _                         "dword*", 0, "wstr", "", "ulong", 260, "ulong", 0)             EndIf ;~          ConsoleWrite("VetoType: " & $res[2] & @CRLF) ; success when type = 0 ;~          ConsoleWrite("VetoName: " & $res[3] & @CRLF) ; name will be blank on success             $bSuccess = (($res[0] = $CR_SUCCESS) And ($res[2] = $PNP_VetoTypeUnknown))             If $bSuccess Then ExitLoop             Sleep(500) ; required to give the next tries a chance         Next         Return $bSuccess     Else         Return 0     EndIf EndFunc ;==>_DevInstQuery Func _ZeroMemory($ptr, $size)     DllCall("kernel32.dll", "none", "RtlZeroMemory", "ptr", $ptr, "ulong_ptr", $size) EndFunc ;==>_ZeroMemory



Example:
#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) If (6 = MsgBox(4 + 32, "Restart?", "Restart this drive?")) Then ConsoleWrite("Restarting drive <" & $drive & ":> - " & _RestartDrive($driveArray) & @CRLF)


I'll throw this in here too. It's a small example how to monitor addition and removal of volumes such as USB flash drives / HDDs.
AutoIt         
$wmiSink = ObjCreate("WbemScripting.SWbemSink") ObjEvent($wmiSink , "SINK_") $Obj_WMIService = ObjGet('winmgmts:localhostrootcimv2') If Not @error Then $obj_WMIService.ExecNotificationQueryAsync($wmiSink, "SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_LogicalDisk'") ConsoleWrite("Ready and waiting for changes" & @CRLF) EndIf While 1 Sleep(1000) WEnd Func SINK_OnObjectReady($objLatestEvent, $objAsyncContext) Switch $objLatestEvent.Path_.Class Case "__InstanceCreationEvent" ConsoleWrite("->==========================================" & @CRLF) ConsoleWrite("> Creation Event" & @CRLF) Case "__InstanceDeletionEvent" ConsoleWrite("->==========================================" & @CRLF) ConsoleWrite("> Deletion Event" & @CRLF) EndSwitch ; Access: ; 0 - Unknown ; 1 - Readable ; 2 - Writable ; 3 - Read / Write Supported ; 4 - Write Once Switch $objLatestEvent.Path_.Class Case "__InstanceCreationEvent", "__InstanceDeletionEvent" ConsoleWrite("Access: " & $objLatestEvent.TargetInstance.Access & @CRLF) ConsoleWrite("Caption: " & $objLatestEvent.TargetInstance.Caption & @CRLF) ConsoleWrite("Description: " & $objLatestEvent.TargetInstance.Description & @CRLF) ConsoleWrite("DeviceID: " & $objLatestEvent.TargetInstance.DeviceID & @CRLF) ConsoleWrite("DriveType: " & $objLatestEvent.TargetInstance.DriveType & @CRLF) ConsoleWrite("FileSystem: " & $objLatestEvent.TargetInstance.FileSystem & @CRLF) ConsoleWrite("FreeSpace: " & Int($objLatestEvent.TargetInstance.FreeSpace / 1000000) & " MB" & @CRLF) ConsoleWrite("Name: " & $objLatestEvent.TargetInstance.Name & @CRLF) ConsoleWrite("Size: " & Int($objLatestEvent.TargetInstance.Size / 1000000) & " MB" & @CRLF) ConsoleWrite("VolumeSerialNumber: " & $objLatestEvent.TargetInstance.VolumeSerialNumber & @CRLF) ConsoleWrite("->==========================================" & @CRLF) EndSwitch EndFunc;==>SINK_OnObjectReady

Edited by wraithdu, 23 August 2012 - 05:12 PM.






#2 Xenobiologist

Xenobiologist

    Xx Code~Mega xX

  • MVPs
  • 4,729 posts

Posted 29 October 2008 - 06:27 AM

Hi,

doesn't work :P

Plain Text         
>"C:\Programme\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.exe" /run /prod /ErrorStdOut /in "C:\Downloads\AutoIt-Skripte\Entwicklung\ForumTests\USBEject.au3" /autoit3dir "C:\Programme\AutoIt3" /UserParams    +>07:26:04 Starting AutoIt3Wrapper v.1.10.1.13  Environment(Language:0407  Keyboard:00000407  OS:WIN_XP/Service Pack 2  CPU:X86  ANSI) >Running AU3Check (1.54.13.0)  from:C:\Programme\AutoIt3 +>07:26:05 AU3Check ended.rc:0 >Running:(3.2.12.1):C:\Programme\AutoIt3\autoit3.exe "C:\Downloads\AutoIt-Skripte\Entwicklung\ForumTests\USBEject.au3"  hVolume: 0x00000700 DeviceNumber: 1 Drive type: 2 DosDeviceName: \Device\Harddisk1\DP(1)0-0+5 Is floppy: False hDevInfo: 0x008A6828 dwSize: 123 DevicePath: \\?\ide#diskwdc_wd1600bevs-08rst2___________________08.01g08#5&1635548a&0&0.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b} hDrive: 0x000006E4 dwSize: 104 DevicePath: \\?\usbstor#disk&ven_&prod_cnmemory&rev_1100#0004012700243&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b} hDrive: 0x000006E4 DevInst: 2204 DevInstParent: 2324 Try: 1 Try: 2 Try: 3 Ejecting drive <E:>  -  False +>07:26:10 AutoIT3.exe ended.rc:0 +>07:26:11 AutoIt3Wrapper Finished >Exit code: 0   Time: 6.993


Mega

Edited by Xenobiologist, 29 October 2008 - 06:28 AM.

Scripts & functions Organize Includes Let Scite organize the include files *newYahtzee The game "Yahtzee" (Kniffel, DiceLion) LoginWrapper Secure scripts by adding a query (authentication)_RunOnlyOnThis UDF Make sure that a script can only be executed on ... (Windows / HD / ...)Internet-Café Server/Client Application Open CD, Start Browser, Lock remote client, etc.MultipleFuncsWithOneHotkey Start different funcs by hitting one hotkey different times

#3 farhan879

farhan879

    Wayfarer

  • Active Members
  • Pip
  • 81 posts

Posted 29 October 2008 - 06:33 AM

Error

F:\My documents\My scripts\script tester.au3(156,13) : ERROR: syntax error
$bRet = False
~~~~~~~~~~~~^
F:\My documents\My scripts\script tester.au3(122,34) : ERROR: _DevInstEject(): undefined function.
Return _DevInstEject($DevInst)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
F:\My documents\My scripts\script tester.au3 - 2 error(s), 0 warning(s)


I'm using XP
Posted ImageSystem task ---> My first GUICalculator v1.0 ---> My version of the calculatorNetZilla 1.0 --> Web browserEmail Sender --> You can Send emails with this without opening a web browser

#4 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 12:47 PM

@Xenobiologist
That means your USB stick had an open handle by the system and could not be safely ejected. What happened when you used the normal tray icon to try and eject the stick?
Another thought, it looks like you might be trying this with a USB HDD, not a flash drive...I haven't tested this yet. Let me get back to you on that.

@farhan879
You have a copy and paste error somewhere if it's saying you're missing the function. Try again and be sure you copied everything.

Edited by wraithdu, 29 October 2008 - 01:38 PM.


#5 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 12:50 PM

Yep, works on my USB HDD as well.

hVolume: 0x0000015C DeviceNumber: 1 Drive type: 3 DosDeviceName: \Device\HarddiskVolume2 Is floppy: False hDevInfo: 0x006BE8B8 dwSize: 123 DevicePath: \\?\ide#diskfujitsu_mhv2080bh_______________________892c____#5&35674a2c&0&0.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b} hDrive: 0x00000160 dwSize: 125 DevicePath: \\?\usbstor#disk&ven_wd&prod_1200bev_external&rev_1.04#575845583037383238373539&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b} hDrive: 0x00000160 DevInst: 2204 DevInstParent: 2364 Try: 1 Ejecting drive <E:>  -  True


Here's a quick snippet from the CodeProject article about your situation -
What Makes the Removal Fail The prepartion for safe removal fails as long as there is one open handle to the disk or to the storage volume. And of course you cannot run this EXE from the drive to remove it. To do that you would need a temporary copy on another drive. ProcessExplorer is great for discovering which process holds an open handle to a drive. Press Ctrl+F and enter the drive letter, like U:. I've often seen that it cannot resolve drive letters, so you have to search for the DOS device name of the drive. It should be something like \Device\Harddisk4\DP(1)0-0+11. A significant part, such as 'disk4,' is usually good enough. On occasion, however, even the driver-driven ProcessExplorer isn't able to find the nasty handle.

Edited by wraithdu, 29 October 2008 - 12:54 PM.


#6 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 01:39 PM

Added a quick example for monitoring addition and removal of volumes such as USB flash drives / HDDs.

#7 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 02:14 PM

Made an update to try to work with disks not marked as REMOVABLE (DN_REMOVABLE flag), using a different function. I believe this other func requires admin privileges.

Edited by wraithdu, 29 October 2008 - 02:15 PM.


#8 Zedna

Zedna

    AutoIt rulez!

  • MVPs
  • 8,315 posts

Posted 29 October 2008 - 02:23 PM

Doesn't work for me on WinXP SP2 Czech with my USB card reader (looks like flash disk):

hVolume: 0x00000700
DeviceNumber: 1
Drive type: 2
DosDeviceName: \Device\Harddisk1\DP(1)0-0+27
Is floppy: False
hDevInfo: 0x00896938
dwSize: 145
DevicePath: \\?\ide#diskwdc_wd800bb-22jha0______________________05.01c05#4457572d4143394d393136303939203320202020#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
hDrive: 0x000006F8
dwSize: 111
DevicePath: \\?\usbstor#disk&ven_multi&prod_flash_reader&rev_1.00#058f0o1111b&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
hDrive: 0x000006F8
DevInst: 2248
DevInstParent: 2380
IsRemovable: True
Try: 1
Try: 2
Try: 3
Ejecting drive <D:> - False



No blocked files were opened.

Also your monitor didn't catch event when I manually did "safety remove" of my drive :-(

#9 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 02:27 PM

I'll have to test more on SP2, but it looks like you have an open handle on your drive, same as Xenobiologixt. That's what I think a device name like this means -

\Device\Harddisk1\DP(1)0-0+27

#10 Zedna

Zedna

    AutoIt rulez!

  • MVPs
  • 8,315 posts

Posted 29 October 2008 - 02:39 PM

I'll have to test more on SP2, but it looks like you have an open handle on your drive, same as Xenobiologixt. That's what I think a device name like this means -

\Device\Harddisk1\DP(1)0-0+27


No. I'm sure I have no open handles. I know what I'm talking about.


I have just mounted USB device and after it appeard in tray area I have run your script.

When it failed I remved it by hand with no errors immediatelly.

#11 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 02:43 PM

Hmmm...I'll try a few sticks I have on XP and see what happens.

#12 arcker

arcker

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 556 posts

Posted 29 October 2008 - 03:06 PM

ahhh great script.
Seems to be the source code of deveject.exe, wanted to do this.
BTW it has several limitations since you eject ROOT controllers by their IDs so this method maybe doesn't work for HDD or Flash Card ( i don't think they use the same driver or root driver ).
-- Arck System _ Soon --Ideas make everything"La critique est facile, l'art est difficile"Projects :Au3Service = Run your exe as service / Updated 27/05/2011 Get it Here

#13 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 03:21 PM

Ok, so I got the same problems in XP. In the _DevInstEject() function, switch the versions to use the ones that DO NOT issue the popup message from the OS. I'm not sure why, but those versions worked on my XP test VM when the others failed.

@arcker
I'm not sure. I did test with a USB HDD and it worked correctly.

Edited by wraithdu, 29 October 2008 - 03:23 PM.


#14 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 04:37 PM

Updated function in first post. I removed the version that would show the normal windows popup message, as it wasn't working on XP. This current one is tested on XP and Vista.

#15 Zedna

Zedna

    AutoIt rulez!

  • MVPs
  • 8,315 posts

Posted 29 October 2008 - 05:00 PM

Updated function in first post. I removed the version that would show the normal windows popup message, as it wasn't working on XP. This current one is tested on XP and Vista.


Yes. This works fine for me.

#16 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 29 October 2008 - 05:23 PM

Great!

I'm at a loss why the Sink isn't working on XP though. I coded in an error handler, and there aren't any COM errors happening. MSDN says that all the methods and classes being used are supported in XP. Any ideas from the COM experts here?

#17 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 30 October 2008 - 07:57 PM

Updated first post with a version of the WMI monitor that works for XP and Vista. Thanks to arcker for the nudge in the right direction.

#18 wraithdu

wraithdu

    I am less fun than a twisted ankle.

  • MVPs
  • 2,137 posts

Posted 03 November 2008 - 05:41 PM

After speaking with the CodeProject author, I slightly changed the logic for the device removal. Now, CM_Query_And_Remove_SubTreeW is always tried first, and only if it returns CR_ACCESS_DENIED (from a limited user account usually) is CM_Request_Device_EjectW used. Apparently, this is the Microsoft recommended procedure.

#19 VeeDub

VeeDub

    Polymath

  • Active Members
  • PipPipPipPip
  • 202 posts

Posted 08 November 2008 - 10:51 AM

@wraithdu

Do you have any plans to add code to re-enable the device if it has not been physically removed?

That way if you had a USB drive attached to a workstation for backup purposes, after the backup has been done you could "eject" the drive - so that the user could safely remove the drive anytime that they want.

But when next backup about to start, if drive still present, then you can re-enable - do the backup - and then un-plug again.

I can see that re-enable could possibly be done with Devcon but a pure AutoIt solution would be easier to manage

VW

#20 VeeDub

VeeDub

    Polymath

  • Active Members
  • PipPipPipPip
  • 202 posts

Posted 08 November 2008 - 11:09 AM

Possibly a rescan of devices will "re-enable" the device if it is still attached.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users