Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

SafelyRemoveUMS() : Safely Remove USB Mass Storage Drives


  • Please log in to reply
61 replies to this topic
SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Latest version ( 06-Sep-2014 ) has been posted here: Eject()

 gAHK32  
Generic AutoHotkey 32bit


staiL.gif
09-Sep-2014


Usage:

SafelyRemoveUMS( "H:" )

or

SafelyRemoveUMS( "H:", True ) ; If you need Retry prompt

TwF2J.png
 
 
 
 
 
SafelyRemoveUMS( DrivePath, Retry=0 ) { ; v1.01 by SKAN,   CD:01-Sep-2012 / LM:09-Sep-2014
;      AutoHotkey Forum Topic :  http://www.autohotkey.com/community/viewtopic.php?t=44873

 hVol  := DllCall( "CreateFile"
                 , Str,  "\\.\" . ( Drive  := SubStr( DrivePath, 1, 1 ) . ":" )
                 , UInt, 0 
                 , UInt, 0 
                 , UInt, 0
                 , UInt, 0x3
                 , UInt, 0x0
                 , UInt, 0  )


 If ( hvol < 1 )
    Return 0, ErrorLevel := 1             ; Unable to access volume!


 VarSetCapacity( GenBuf, 2080, 0 )        ; General, all-purpose buffer


 pSTORAGE_PROPERTY_QUERY    := &GenBuf                       ;  MSDN  http://bit.ly/SvILmx
 pSTORAGE_DESCRIPTOR_HEADER := &GenBuf + 12                  ;  MSDN  http://bit.ly/O8UNiH
 NumPut( StorageDeviceProperty := 0, pSTORAGE_PROPERTY_QUERY + 0 )


 DllCall( "DeviceIoControl"
        , UInt, hVol
        , UInt, 0x2D1400   ; IOCTL_STORAGE_QUERY_PROPERTY    ;  MSDN: http://bit.ly/OdLos0
        , UInt, pSTORAGE_PROPERTY_QUERY
        , UInt, 12
        , UInt, pSTORAGE_DESCRIPTOR_HEADER ;STORAGE_DEVICE_DESCRIPTOR http://bit.ly/O8UNiH
        , UInt, 1024
        , UIntP, BR
        , UInt, 0 )


 BT := NumGet( pSTORAGE_DESCRIPTOR_HEADER + 28 )   ; STORAGE_BUS_TYPE http://bit.ly/T3qt9C
 If ( BT <> 0x7 )                                  ; BusTypeUsb = 0x7
    Return 0,   DllCall( "CloseHandle", UInt,hVol )
  , ErrorLevel := 2                                ; Drive not USB Mass Storage Device!




 IOCTL_STORAGE_GET_DEVICE_NUMBER := 0x2D1080
 pSTORAGE_DEVICE_NUMBER          := &GenBuf


 DllCall( "DeviceIoControl"
        , UInt,  hVol
        , UInt,  IOCTL_STORAGE_GET_DEVICE_NUMBER   ; MSDN http://bit.ly/Ssuzfm
        , UInt,  0
        , UInt,  0
        , UInt,  pSTORAGE_DEVICE_NUMBER            ; MSDN http://bit.ly/PF17hX
        , UInt,  12
        , UIntP, BR
        , UInt,  0   )


 DllCall( "CloseHandle", UInt,hVol )


 If ( BR = 0 )
    Return 0,  ErrorLevel := 3                     ; Unable to ascertain the Device number


 sDevNum := NumGet( pSTORAGE_DEVICE_NUMBER + 4 )


 GUID_DEVINTERFACE_DISK := "{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}" ; http://bit.ly/TXvGlC
 VarSetCapacity( DiskGUID, 16, 0 )
 NumPut( 0x53F56307,   DiskGUID, 0, "UInt"  )  ,  NumPut( 0xB6BF, DiskGUID, 4, "UShort" )
 NumPut( 0x11D0,       DiskGUID, 6, "UShort")  ,  NumPut( 0xF294, DiskGUID, 8, "UShort" )
 NumPut( 0x1EC9A000,   DiskGUID,10, "UInt"  )  ,  NumPut( 0x8BFB, DiskGUID,14, "UShort" )


 hMod := DllCall( "LoadLibrary", Str,"SetupAPI.dll", UInt )


 hDevInfo := DllCall( "SetupAPI\SetupDiGetClassDevs"                ; http://bit.ly/Pf6vHX
                    . ( A_IsUnicode ? "W" : "A" )
                    , UInt,  &DiskGUID
                    , UInt,  0
                    , UInt,  0
                    , UInt,  0x12  ;  DIGCF_PRESENT := 0x2 | DIGCF_DEVICEINTERFACE := 0x10
                          ,  Int )


 If ( hDevInfo < 1 )
    Return 0,  ErrorLevel := 4                    ; No storage class devices were found!




 pSP_DEVICE_INTERFACE_DATA        :=  &GenBuf + 12             ; MSDN http://bit.ly/PJFcbj
 pSP_DEVICE_INTERFACE_DETAIL_DATA :=  &GenBuf + 40             ; MSDN http://bit.ly/SXr3We
 pSP_DEVINFO_DATA                 :=  &GenBuf + 1040           ; MSDN http://bit.ly/Rgp02c




 NumPut( 28, pSP_DEVICE_INTERFACE_DATA + 0 )
 NumPut( 28, pSP_DEVINFO_DATA + 0 )
 NumPut( 4 + ( A_IsUnicode ? 2 : 1 ), pSP_DEVICE_INTERFACE_DETAIL_DATA + 0 )


 DeviceFound := Instance := DeviceNumber := 0


 While DllCall( "SetupAPI\SetupDiEnumDeviceInterfaces"
              , UInt,  hDevInfo
              , UInt,  0
              , UInt,  &DiskGUID
              , UInt,  Instance
              , UInt,  pSP_DEVICE_INTERFACE_DATA ) {


       Instance ++


       DllCall( "SetupAPI\SetupDiGetDeviceInterfaceDetail"     ; MSDN http://bit.ly/NINIci
            . ( A_IsUnicode ? "W" : "A" )
              , UInt,  hDevInfo
              , UInt,  pSP_DEVICE_INTERFACE_DATA               ; MSDN http://bit.ly/PJFcbj
              , UInt,  0
              , UInt,  0
              , UIntP, nBytes
              , UInt,  pSP_DEVINFO_DATA )                      ; MSDN http://bit.ly/Rgp02c




       DllCall( "SetupAPI\SetupDiGetDeviceInterfaceDetail"     ; MSDN http://bit.ly/NINIci
            . ( A_IsUnicode ? "W" : "A" )
              , UInt,  hDevInfo
              , UInt,  pSP_DEVICE_INTERFACE_DATA               ; MSDN http://bit.ly/PJFcbj
              , UInt,  pSP_DEVICE_INTERFACE_DETAIL_DATA        ; MSDN http://bit.ly/SXr3We
              , UInt,  nBytes
              , UInt,  0
              , UInt,  pSP_DEVINFO_DATA )                      ; MSDN http://bit.ly/Rgp02c


      hVol  := DllCall( "CreateFile"
                      , UInt,  pSP_DEVICE_INTERFACE_DETAIL_DATA + 4
                      , UInt,  0 
                      , UInt,  0 
                      , UInt,  0
                      , UInt,  0x3
                      , UInt,  0x0
                      , UInt,  0  )


      If ( hVol < 0 )
           Continue


      DllCall( "DeviceIoControl"
             , UInt,  hVol
             , UInt,  IOCTL_STORAGE_GET_DEVICE_NUMBER          ; MSDN http://bit.ly/Ssuzfm
             , UInt,  0
             , UInt,  0
             , UInt,  pSTORAGE_DEVICE_NUMBER                   ; MSDN http://bit.ly/PF17hX
             , UInt,  12
             , UIntP, BR
             , UInt,  0   )


      DllCall( "CloseHandle", UInt,hVol )


      If ( BR = 0   )
           Continue


      tDevNum := NumGet( pSTORAGE_DEVICE_NUMBER + 4 )
      If DeviceFound := ( tDevNum == sDevnum )
         Break
 }


 If ( DeviceFound = 0 )
    Return 0,  ErrorLevel := 5                  ; No matching storage class devices found!


 DllCall( "SetupAPI\SetupDiGetDeviceRegistryProperty"
      . ( A_IsUnicode ? "W" : "A" )
        , UInt, hDevInfo
        , UInt, pSP_DEVINFO_DATA
        , UInt, 12 ; SPDRP_FRIENDLYNAME
        , UInt, 0
        , Str,  GenBuf
        , UInt, 1024
        , UInt, 0 )


 FRIENDLY := GenBuf
 DllCall( "SetupAPI\SetupDiDestroyDeviceInfoList", UInt,hDevInfo )  ; http://bit.ly/TWTmsN


 DllCall( "SetupAPI\CM_Get_Parent"
        , UIntP, hDeviceID
        , UInt,  NumGet( pSP_DEVINFO_DATA + 20 )
        , UInt,  0 )


 If ( hDeviceID = 0 )
    Return 0,  ErrorLevel := 6                  ; Problem IDentifying USB Device!




 DllCall( "SetupAPI\CM_Get_Device_ID"
      . ( A_IsUnicode ? "W" : "A" )
        , UInt,  hDeviceID
        , Str,   GenBuf
        , UInt,  1024
        , UInt,  0 )


 DeviceID := GenBuf
 MAX_PATH := ( A_IsUnicode ? 520 : 260 )


Label_SafelyRemoveUMS:


 Loop 5 {


          DllCall( "SetupAPI\CM_Request_Device_Eject"
               . ( A_IsUnicode ? "W" : "A" )
                 , UInt,  hDeviceID
                 , UIntP, VetoType
                 , Str,   GenBuf
                 , UInt,  MAX_PATH
                 , Int,   0 )


          If ( VetoType = 0 )
             Break


        }


 If ( Retry && VetoType == 6 ) {                ; PNP_VetoOutstandingOpen = 6


   MsgBox, 0x1035 ; MB_SYSTEMMODAL=0x1000 | MB_ICONEXCLAMATION=0x30 | MB_RETRYCANCEL=0x5
         , Safely Remove USB Mass Storage Drive %Drive%
         , Unable to Eject Drive due to Open File Handles!`n`n%FRIENDLY%`n%DeviceID%


   IfMsgBox Retry, GoTo Label_SafelyRemoveUMS


 }


Return ( VetoType ? 0 : FRIENDLY "`n" DeviceID  )
      , DllCall( "SetLastError", UInt,VetoType )
      , ErrorLevel := VetoType ? 7 : 0
}

 



Previous Version: Crazy Scripting : Safely Remove USB Flash Drive - 45L
kWo4Lk1.png

MasterFocus
  • Moderators
  • 4323 posts
  • Last active: Jan 28 2016 01:38 AM
  • Joined: 08 Apr 2009
Amazing! :D
Always wanted to safely remove quicker than usual. Thanks!

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Antonio França -- git.io -- github.com -- ahk4.net -- sites.google.com -- ahkscript.org

Member of the AHK community since 08/Apr/2009. Moderator since mid-2012.


HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Many thanks ;)

el
  • Guests
  • Last active:
  • Joined: --
found it useful for quick hotkey to eject usb(s)
thanks

Obi-Wahn
  • Members
  • 77 posts
  • Last active: Oct 29 2012 05:55 PM
  • Joined: 20 Apr 2006
Hmm, great script, but when I disconnect my thumb drive with you script, it'll eject it, so I don't see it in the 'safely removal' dialog-box from windows, but I hear the sound when I disconnect the drive from the computer.

Also, I can't eject USB-HDDs from my Computer with your script.
Any suggestions?

Pip
  • Guests
  • Last active:
  • Joined: --
Hi

This is great...but...is it possible for it to eject the USB - if it is running from there itself?

Pip

Raccoon
  • Members
  • 178 posts
  • Last active: Oct 06 2014 05:58 PM
  • Joined: 02 Jan 2008
IfNotEqual,DriveType,Removable, Return
FYI, not all USB flash drives have the 'removable' flag. I believe the same applies with eSATA drives as well certain USB harddrive enclosures.
Posted Image

Need help right away? Get live support on IRC.
Already have an IRC client installed? /join #ahk

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

FYI, not all USB flash drives have the 'removable' flag.


Thanks for the info.. I did guess that when I wrote the wrapper.
1) It just did not make sense to me to 'remove' something that is not marked removable
2) I did not have the hardware to test it.
3) I had mentioned the option of using DeviceEject() directly.

TomXIII
  • Members
  • 182 posts
  • Last active: Feb 28 2011 11:42 PM
  • Joined: 14 Apr 2009
@SKAN : I like this script but it seems that doesn't work with AHK_L

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

it seems that doesn't work with AHK_L


I will check it.. BTW, what OS?.. I can test only in XP!

TomXIII
  • Members
  • 182 posts
  • Last active: Feb 28 2011 11:42 PM
  • Joined: 14 Apr 2009
I'm on Win7. I did a test with both AutoHotkey & AutoHotkey_L and it works fine only with AutoHotkey

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
I will be glad if this can be tested in XP/Vista/Win7 in AHK_LW

; USBD_SafelyRemoveW() for AutoHotkey_LW
; www.autohotkey.com/forum/viewtopic.php?p=384003#384003

USBD_SafelyRemoveW( Drv ) {
 If A_OSVersion not in  WIN_VISTA,WIN_XP,WIN_2000
   Return
 If ! ( Serial := USBD_GetDeviceSerialW( Drv ) )
   Return
 DeviceID := USBD_GetDeviceIDW( Serial )
 DeviceEjectW( DeviceID )
 IfExist, %Drv%\, TrayTip, %DeviceID%, Drive %Drv% was not Ejected!, 10, 3
 Else, TrayTip, %DeviceID%, Drive %Drv% was safely Removed, 10, 1
}

USBD_GetDeviceSerialW( Drv="" ) {
 DriveGet, DriveType, Type, %Drv%
 IfNotEqual,DriveType,Removable, Return
 RegRead, Hex, HKLM, SYSTEM\MountedDevices, \DosDevices\%Drv%
 VarSetCapacity(U,(Sz:=StrLen(Hex)//2)+2, 0 )
 Loop % Sz
  NumPut( "0x" . SubStr(hex,2*A_Index-1,2), U, A_Index-1, "Char" )
 VarSetCapacity( U, -1 )
 StringSplit, Part, U, #
 ParentIdPrefixCheck := SubStr( Part3,1,InStr(Part3,"&",0,0)-1 )
 [color=red] IfNotEqual,A_OSVersion,WIN_XP, Return,ParentIdPrefixCheck[/color] ; this line needs testing in Win7
 Loop, HKLM, SYSTEM\CurrentControlSet\Enum\USBSTOR,1,0
  { Device := A_LoopRegName
    Loop, HKLM, SYSTEM\CurrentControlSet\Enum\USBSTOR\%Device%,1,0
     { Serial := A_LoopRegName
       RegRead, PIPrefix, HKLM, SYSTEM\CurrentControlSet\Enum\USBSTOR\%Device%\%Serial%
              , ParentIdPrefix
       If ( PIPrefix = ParentIdPrefixCheck )
         Return, SubStr( Serial,1,InStr(Serial,"&",0,0)-1 )
     }
}}

USBD_GetDeviceIDW( Serial ) {
 Loop, HKLM, SYSTEM\CurrentControlSet\Enum\USB\,1,0
  { Device := A_LoopRegName
    Loop, HKLM, SYSTEM\CurrentControlSet\Enum\USB\%Device%,1,0
    If ( A_LoopRegName=Serial )
      Return DllCall( "CharUpperW", Str, "USB\" Device "\" Serial, Str )
}}

DeviceEjectW( DeviceID ) {
 hMod := DllCall( "LoadLibrary", Str,"SetupAPI.dll" ), VarSetCapacity(VE,255,0)
 If ! DllCall( "SetupAPI\CM_Locate_DevNodeW", UIntP,DI, Str,DeviceID, Int,0 )
 If ! DllCall( "SetupAPI\CM_Get_DevNode_Status", UIntP,STS, UIntP,PR, UInt,DI, Int,0)
 DllCall( "SetupAPI\CM_Request_Device_EjectW", UInt,DI, UIntP,VT, Str,VE, UInt,255, Int,0)
 DllCall( "FreeLibrary", UInt,hMod )
}


TomXIII
  • Members
  • 182 posts
  • Last active: Feb 28 2011 11:42 PM
  • Joined: 14 Apr 2009
Previous code tested with AHK_L on Win7:

It works with a little modification:
USBD_SafelyRemoveW( Drv ) {
 If A_OSVersion not in  WIN_VISTA,WIN_XP,WIN_2000[color=red],WIN_7[/color]
   Return


SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Alter the following line

IfNotEqual,A_OSVersion,WIN_XP, Return,ParentIdPrefixCheck

to

IfEqual,A_OSVersion,WIN_VISTA, Return,ParentIdPrefixCheck

and try again, please.

USBD_SafelyRemoveW( "H:" )


TomXIII
  • Members
  • 182 posts
  • Last active: Feb 28 2011 11:42 PM
  • Joined: 14 Apr 2009
Sorry for the previous post.
I edited it after you last post! OUPS

But... It's works for me!