wraithdu Posted October 29, 2008 Posted October 29, 2008 (edited) 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 Please test this on WinXP. It works on Vista and should work on XP, but I wanna be sure. expandcollapse popup#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(Const $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 _IsUSBFlash(Const $query) ; if drive type is removable (2), and device path contains 'usbstor', and parent device ID contains 'USB', and IsRemovable Return (($query[1] = 2) 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 instance (int) ; [5] = device ID (string) ; [6] = device parent instance (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 \device\floppy0) ; 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 ;==>_ZeroMemoryExample:#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.expandcollapse popup$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 June 7, 2014 by wraithdu
Xenobiologist Posted October 29, 2008 Posted October 29, 2008 (edited) Hi, doesn't work >"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 October 29, 2008 by Xenobiologist Scripts & functions Organize Includes Let Scite organize the include files Yahtzee 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
farhan879 Posted October 29, 2008 Posted October 29, 2008 ErrorF:\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 System 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
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 (edited) @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 October 29, 2008 by wraithdu
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 (edited) 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 October 29, 2008 by wraithdu
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 Added a quick example for monitoring addition and removal of volumes such as USB flash drives / HDDs.
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 (edited) 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 October 29, 2008 by wraithdu
Zedna Posted October 29, 2008 Posted October 29, 2008 Doesn't work for me on WinXP SP2 Czech with my USB card reader (looks like flash disk):hVolume: 0x00000700DeviceNumber: 1Drive type: 2DosDeviceName: \Device\Harddisk1\DP(1)0-0+27Is floppy: FalsehDevInfo: 0x00896938dwSize: 145DevicePath: \\?\ide#diskwdc_wd800bb-22jha0______________________05.01c05#4457572d4143394d393136303939203320202020#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}hDrive: 0x000006F8dwSize: 111DevicePath: \\?\usbstor#disk&ven_multi&prod_flash_reader&rev_1.00#058f0o1111b&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}hDrive: 0x000006F8DevInst: 2248DevInstParent: 2380IsRemovable: TrueTry: 1Try: 2Try: 3Ejecting drive <D:> - FalseNo blocked files were opened.Also your monitor didn't catch event when I manually did "safety remove" of my drive :-( Resources UDF ResourcesEx UDF AutoIt Forum Search
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 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
Zedna Posted October 29, 2008 Posted October 29, 2008 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+27No. 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. Resources UDF ResourcesEx UDF AutoIt Forum Search
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 Hmmm...I'll try a few sticks I have on XP and see what happens.
arcker Posted October 29, 2008 Posted October 29, 2008 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 :[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 (edited) 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 October 29, 2008 by wraithdu
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 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.
Zedna Posted October 29, 2008 Posted October 29, 2008 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. Resources UDF ResourcesEx UDF AutoIt Forum Search
wraithdu Posted October 29, 2008 Author Posted October 29, 2008 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?
wraithdu Posted October 30, 2008 Author Posted October 30, 2008 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.
wraithdu Posted November 3, 2008 Author Posted November 3, 2008 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.
VeeDub Posted November 8, 2008 Posted November 8, 2008 @wraithduDo 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 manageVW
VeeDub Posted November 8, 2008 Posted November 8, 2008 Possibly a rescan of devices will "re-enable" the device if it is still attached.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now