Eject() : For Removable storage devices

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Eject() : For Removable storage devices

06 Sep 2014, 15:22

Eject(DriveLetter, DontCheck, DontEject). Returns a Win32_DIskDrive class for the physical drive

Usage example: Eject( "D:" ). Drive letter passed can be like "D" or "D:" or "D:\" or "D:\\". Only the first letter matters.

ErrorLevel will be blank when eject() was successful or contain an error text explaining what went wrong.

The other 2 parameters exist only for debug purposes.
DontCheck : When true will skip MediaType check. I used this to try ejecting my C: drive and Internal DVD drive.
DontEject : When true, returns the class without ejecting the drive. Useful for populating a removable drives list.

What has changed in this new version?

Win32_DiskDrive->MediaType values for a physical drive are as follows:
  • Fixed hard disk media : Any drive directly plugged into mainboard. HDD/CDD/DVD etc
  • Removable Media : Flash/Pen drives, card readers, external HDD etc. plugged via USB port
  • External hard disk media : UAS - USB Attached SCSI
  • Unknown ; Probably a Floppy disk drive. I don't have the hardware to test
Previous version of Eject() would eject a physical drive only if Win32_DiskDrive->InterfaceTpye is USB.
A UAS drive identifies itself as USB in Windows 7 but as SCSI in Windows 8 and upwards so the old approach is now no more useful
The new rewritten version now checks Win32_DiskDrive->MediaType and proceeds with eject if media type is either Removable Media or External hard disk media
If you have a eject supported drive but the new Eject() fails, Please run

Code: Select all

MsgBox % Clipboard := Eject("X:",,True ).MediaType ; Replace X: with your drive
and paste the result in your reply.


The function along with code for testing
Explore/Eject Drives

Run script and use as follows:
Hotkey Ctrl+Win+<DriveLetter> to explore the drive
Hotkey Ctrl+Win+Alt+<DriveLetter> to eject the drive.
Hotkey Win+E to open My Computer/Computer/This PC maximized

Code: Select all

Eject(DRV, DontCheck:=0, DontEject:=0) {  ;       By SKAN on CT91/D29R @ goo.gl/pUUGRt  
Local  STORAGE_DEVICE_NUMBER, IOCTL_STORAGE_GET_DEVICE_NUMBER:=0x2D1080    
Local  OPEN_EXISTING:=3, hVol:=0, sPHDRV:="", qStr:="", qEnum:= "", nDID:=0, nVT:=1
Local  AMT := "[Removable Media][External hard disk media]", dObj:={}, VT, VAR 

  hVol := DllCall("CreateFile", "Str","\\.\" . (DRV:=SubStr(DRV,1,1) . ":"), "UInt",0
                    ,"UInt",0, "Ptr",0,"UInt",OPEN_EXISTING, "UInt",0, "Ptr",0, "Ptr")
  If (hVol = -1 )
   {
     ErrorLevel := FileExist(DRV) ? "Mapped/substitute drive" : "Invalid drive letter"
     Return dObj
   }

  VarSetcapacity(STORAGE_DEVICE_NUMBER,12,0)
  DllCall("DeviceIoControl", "Ptr",hVol, "UInt",IOCTL_STORAGE_GET_DEVICE_NUMBER
         ,"Int",0, "Int",0, "Ptr",&STORAGE_DEVICE_NUMBER, "Int",12, "PtrP",0, "Ptr",0)  
  DllCall( "CloseHandle", "Ptr",hVol )
      
  sPHDRV := "\\\\.\\PHYSICALDRIVE" . NumGet(STORAGE_DEVICE_NUMBER,4,"UInt")
  qStr   := "Select * from Win32_DiskDrive where DeviceID='$$$'"
  qEnum  := ComObjGet("winmgmts:").ExecQuery(StrReplace(qStr,"$$$",sPHDRV))._NewEnum()
  qEnum[dObj]

  If ( DontEject )
    {
      ErrorLevel := ""
      Return dObj 
    }

  If ! ( DontCheck || InStr(AMT, "[" . dObj.MediaType . "]", True) )
    {
      ErrorLevel := (dObj.MediaType=="Fixed hard disk media" 
                 ?  "Media is a Fixed hard disk" : "Media type Unknown")
      Return dObj 
    }

  If ! ( DllCall("GetModuleHandle", "Str","SetupAPI.dll", "Ptr") ) 
    {
         DllCall("LoadLibrary", "Str","SetupAPI.dll", "Ptr")
    }

  DllCall("SetupAPI\CM_Locate_DevNode", "PtrP",nDID, "Str",dObj.PNPDeviceID, "Int",0)
  DllCall("SetupAPI\CM_Get_Parent", "PtrP",nDID, "UInt",nDID, "Int",0)

  VarSetCapacity(VAR,520,0)
  DllCall("SetupAPI\CM_Request_Device_Eject"
          ,"UInt",nDID, "PtrP",nVT,"Str",VAR, "Int",260, "Int",0)

  ErrorLevel := ( nVT=0 ? 0 : ["PNP_VetoTypeUnknown`nThe specified operation was reje"
  . "cted for an unknown reason.","PNP_VetoLegacyDevice`nThe device does not support "
  . "the specified PnP operation.","PNP_VetoPendingClose`nThe specified operation can"
  . "not be completed because of a pending close operation.","PNP_VetoWindowsApp`nA M"
  . "icrosoft Win32 application vetoed the specified operation.","PNP_VetoWindowsServ"
  . "ice`nA Win32 service vetoed the specified operation.","PNP_VetoOutstandingOpen`n"
  . "The requested operation was rejected because of outstanding open handles.","PNP_"
  . "VetoDevice`nThe device supports the specified operation, but the device rejected"
  . " the operation.","PNP_VetoDriver`nThe driver supports the specified operation, b"
  . "ut the driver rejected the operation.","PNP_VetoIllegalDeviceRequest`nThe device"
  . " does not support the specified operation.","PNP_VetoInsufficientPower`nThere is"
  . " insufficient power to perform the requested operation.","PNP_VetoNonDisableable"
  . "`nThe device cannot be disabled.","PNP_VetoLegacyDriver`nThe driver does not sup"
  . "port the specified PnP operation.","PNP_VetoInsufficientRights`nThe caller has i"
  . "nsufficient privileges to complete the operation.","PNP_VetoAlreadyRemoved`nThe "
  . "device has been already removed"][nVT] )

Return dObj                    
}                              
                              
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  
; DEMO SCRIPT FOLLOWS:
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


#NoEnv
#SingleInstance, Force

; If ! FileExist( "T:\" )        
;   Run subst T: "%A_TEMP%",,Hide ; Substitute drive T: for temp folder

Loop 26
 {
   HotKey % "^#"  . Chr(A_Index+64), ExploreDrive
   HotKey % "^!#" . Chr(A_Index+64), EjectDrive
 }   
Return

#E::Run ::{20d04fe0-3aea-1069-a2d8-08002b30309d},,Max
F1::ListVars

ExploreDrive:
  TrayTip
  DRV := SubStr(A_ThisHotkey,0) . ":\"
  If FIleExist( DRV )
    {
        Run %Drv%,, Max
        Return
    }      
  TrayTip, Explore drive, Drive %Drv%: does not exist,, 0x22
  SetTimer, TrayTipClose, -3000                                    
Return

EjectDrive:
  TrayTip
  DRV  := SubStr(A_ThisHotkey,0) . ":\"
  dObj := Eject(DRV)         
  
  If ! ( ErrorLevel )
    {
      TrayTip, Eject drive %DRV%, % dObj.Model "`nsafely removed",,0x21
      SetTimer, TrayTipClose, -4000
      Return 
    } 
  TrayTip, Eject Drive %DRV%, % "`n" . ErrorLevel,, 0x23
  SetTimer, TrayTipClose, -4000
Return

TrayTipClose:
 TrayTip
Return 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
My Scripts and Functions: V1  V2
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: Eject() - For CD/DVD and USB Mass Storage devices

06 Sep 2014, 17:51

You should probably state what the DRV parameter expects.. is it "D", "D:", "D:\" etc

User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() - For CD/DVD and USB Mass Storage devices

06 Sep 2014, 18:13

Old version dated: 13-Sep-2014
Skrell
Posts: 302
Joined: 23 Jan 2014, 12:05

Re: Eject() - For CD/DVD and USB Mass Storage devices

07 Sep 2014, 10:13

isn't the last line missing a ")" ?
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() - For CD/DVD and USB Mass Storage devices

07 Sep 2014, 14:52

guest3456 wrote: You should probably state what the DRV parameter expects.. is it "D", "D:", "D:\" etc
Thanks. Done!.
Skrell wrote:isn't the last line missing a ")" ?
Last few chars go missing when downloaded. Some forum quirk, I guess. I have appended a ; end eject.ahk at the end of the code.
Thanks for reporting this.
:)
Skrell
Posts: 302
Joined: 23 Jan 2014, 12:05

Re: Eject() - For CD/DVD and USB Mass Storage devices

08 Sep 2014, 08:17

SKAN wrote:
Skrell wrote:isn't the last line missing a ")" ?
Last few chars go missing when downloaded. Some forum quirk, I guess. I have appended a ; end eject.ahk at the end of the code.
Thanks for reporting this.
:)
No prob Skan, thank YOU for all the scripts you've contributed!!!!
jantom
Posts: 12
Joined: 21 Feb 2014, 10:49

Re: Eject() - For CD/DVD and USB Mass Storage devices

11 Sep 2014, 11:48

I get A_LastError = 1.
I'm trying to safely remove a portable USB drive (2.5 inch, 1TB).
It works clicking the standard Win 7 tray icon ('Safely Remove Hardware').
Any suggestion?
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() - For CD/DVD and USB Mass Storage devices

11 Sep 2014, 14:08

Hi jantom,
Welcome to the forum. :)
jantom wrote:I get A_LastError = 1
What about ErrorLevel ? Is it -3 ?

Edit:
Can you insert a MsgBox in Eject() as follows and let me know what sDID is? ( sDID = strDeviceID )

Code: Select all

sDID  := Split[1] "\" Split[2] "\" Split[3]
MsgBox % Clipboard := sDID
jantom
Posts: 12
Joined: 21 Feb 2014, 10:49

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 02:15

Here's the content of Msgbox:

---------------------------
Eject.ahk
---------------------------
\\
---------------------------
OK
---------------------------
jantom
Posts: 12
Joined: 21 Feb 2014, 10:49

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 02:17

yes, ErrorLevel is -3
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 09:40

jantom wrote:yes, ErrorLevel is -3
Thanks. ErrorLevel should have been -2. A different problem, fixed now.

Just one more help:
Set the correct drive and post the hex.

Code: Select all

DRV := "H:" 
RegRead, Hex, HKLM, SYSTEM\MountedDevices, \DosDevices\%DRV%
MsgBox % Hex
jantom
Posts: 12
Joined: 21 Feb 2014, 10:49

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 10:14

Hex is:

B14CB7010000100000000000
jantom
Posts: 12
Joined: 21 Feb 2014, 10:49

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 10:38

I get this message:
---------------------------
Eject.ahk
---------------------------
Error at line 19.

Line Text: Local sHex, Split, sDID, nDID := 0, nVT := 1, nTC := A_TickCount, VAR, nLen := VarSetCapacity( VAR,1...
Error: Local variables must not be declared in this function.

The program will exit.
---------------------------
OK
---------------------------
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 11:05

jantom wrote:Hex is:

B14CB7010000100000000000
As expected, your drive is typed as "FIXED". Allow me sometime please, I will update the code to handle "FIXED" typed USB Mass Storage Devices.

:)
jantom
Posts: 12
Joined: 21 Feb 2014, 10:49

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 11:09

Great. Thank you so much.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 14:02

jantom, I do not have external HDD drive to test the new code.
Please try and let me know, so that I can update my code in the first post.
Ejectv2 - WMI based enumeration
jantom
Posts: 12
Joined: 21 Feb 2014, 10:49

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 14:12

Thank you. Unfortunately, I can't make it work.
Can you suggest how to debug?
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() - For CD/DVD and USB Mass Storage devices

12 Sep 2014, 19:13

jantom wrote:Can you suggest how to debug?
Insert MsgBox after hVol := and sPHDRV :=
hVol should not be -1
sPHDRV should not be blank and should be like: \\\\.\\PHYSICALDRIVE1

The following will list all your drives. Please let me know the properties for the drive in question.

Code: Select all

List := ""
for ObjItem in ComObjGet("winmgmts:").ExecQuery( "Select * from Win32_DiskDrive" )
List .= "`nCaption: "         . objItem.Caption
     .  "`nDescription: "     . objItem.Description
     .  "`nDeviceID: "        . objItem.DeviceID
     .  "`nInterfaceType: "   . objItem.InterfaceType
     .  "`nMediaLoaded: "     . objItem.MediaLoaded
     .  "`nMediaType: "       . objItem.MediaType
     .  "`nModel: "           . objItem.Model
     .  "`nName: "            . objItem.Name
     .  "`nPartitions: "      . objItem.Partitions
     .  "`nPNPDeviceID: "     . objItem.PNPDeviceID
     .  "`nSectorsPerTrack: " . objItem.SectorsPerTrack
     .  "`nStatus: "          . objItem.Status 
     .  "`n----------------------------------------------`n"

MsgBox % Clipboard := List
jantom
Posts: 12
Joined: 21 Feb 2014, 10:49

Re: Eject() - For CD/DVD and USB Mass Storage devices

13 Sep 2014, 03:21

hVol = 248
sPHDRV = (blank)
ErrorLevel = -2


The drive in question is:
----------------------------------------------

Caption: Seagate Expansion USB Device
Description: Disk drive
DeviceID: \\.\PHYSICALDRIVE1
InterfaceType: USB
MediaLoaded: -1
MediaType: External hard disk media
Model: Seagate Expansion USB Device
Name: \\.\PHYSICALDRIVE1
Partitions: 1
PNPDeviceID: USBSTOR\DISK&VEN_SEAGATE&PROD_EXPANSION&REV_060E\NA481WBG&0
SectorsPerTrack: 63
Status: OK
----------------------------------------------
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() - For CD/DVD and USB Mass Storage devices

13 Sep 2014, 04:35

Last piece of info required. ( I promise )
Set the correct drive and post the info:

Code: Select all

DRV := "H:"

hVol := DllCall( "CreateFile", "Str","\\.\" DRV, "Int",0, "Int",0, "Int",0, "Int",3, "Int",0, "Int",0 )

VarSetcapacity( STORAGE_DEVICE_NUMBER, 12, 0 )
DllCall( "DeviceIoControl", "Ptr",hVol, "UInt",IOCTL_STORAGE_GET_DEVICE_NUMBER := 0x2D1080
       , "Int",0, "Int",0, "Ptr",&STORAGE_DEVICE_NUMBER, "Int",12, "PtrP",0, "Ptr",0 )  
       
DllCall( "CloseHandle", "Ptr",hVol )

MsgBox % Clipboard := "DeviceType:`t"      NumGet( STORAGE_DEVICE_NUMBER, 0, "Int" ) "`n"
                    . "DeviceNumber:`t"    NumGet( STORAGE_DEVICE_NUMBER, 4, "Int" ) "`n" 
                    . "PartitionNumber:`t" NumGet( STORAGE_DEVICE_NUMBER, 8, "Int" ) 

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: gwarble and 118 guests