Eject() : For Removable storage devices

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

Eject() : For Removable storage devices

25 Aug 2021, 08:53

Eject( Drive )
Usage example: Try Eject( "D:" ). Drive letter passed can be like "D" or "D:" or File fullpath. Only the first letter matters.
This function's Result is Thrown as an Error object, so cannot be called as a normal function.

The function could be simply called as
 

Code: Select all

Drive := "H:"
Try Eject(Drive)
Catch Error as Result
 
and then Result properties could be shown in a dialog.
 
  • Result.Message
    • Warning: Eject failed
    • Error: Eject failed
    • Success: Eject succeeded
  • Result.What
    • An Error string returned either by System or by Eject()
  • Result.Extra ;
    • Model of Drive / Volume label, when drive letter is valid.
Here follows an example script that normalizes Eject() with a wrapper function:
 

Code: Select all

#Requires AutoHotkey v2.0-
#Warn
#SingleInstance

MyEject("H:")

MyEject(Drive)
{
    Local Result, Msg, Icon

    Try   Eject(Drive)
    Catch Error as Result

    Msg := StrSplit( Result.Message, ":",, 2 )
    Icon := Msg[1] = "Success" ? "Iconi" : Msg[1]="Error" ? "Iconx" : "Icon!"

    SetTimer( (*) => MsgBox(Result.What , Drive . "`n" . Msg[2], Icon . " T4"), -1)

    Return( Msg[1] = "Success" )
}
 
 
 

Example screen shots for above script:
 
Image        Image        Image
 
 

Code: Select all

Eject(Drive)  ; By SKAN for ah2 on CT91/D48E @ autohotkey.com/r?t=94113
{
    If ( ! DirExist( Drive := SubStr(Drive, 1, 1) . ":" ) )
        Throw( Error("Warning: Eject failed", "Invalid drive letter.", Drive . " ?!") )

    Local hVolume, DriveLabel := "<Label> " . DriveGetLabel(Drive)

    If ( hVolume := OpenVolume(Drive) ) = -1
        Throw( Error("Warning: Eject failed", "Mapped/substitute drive.", DriveLabel) )

    Local DEVICE_NUMBER := IOCTL_STORAGE_GET_DEVICE_NUMBER(hVolume)
    DllCall("Kernel32.dll\CloseHandle", "ptr",hVolume)

    Local queryStr, queryEnum, DiskDrive, UnsupportedMedia
    queryStr  := Format("{1:}'{2:}{3:}'", "Select * from Win32_DiskDrive where DeviceID=", "\\\\.\\PHYSICALDRIVE", DEVICE_NUMBER)
    queryEnum := ComObjGet("winmgmts:").ExecQuery(queryStr)._NewEnum()
    queryEnum(&DiskDrive)

    If ( UnsupportedMedia := CheckMediaType(DiskDrive.MediaType) )
        Throw( Error("Warning: Eject failed", UnsupportedMedia, DiskDrive.Model) )

    Local hSetupApi := DllCall("Kernel32.dll\LoadLibrary", "str","SetupApi.dll", "ptr"),  nDeviceID := 0,  nVetoType := 0
    DllCall("SetupApi.dll\CM_Locate_DevNode", "ptrp",&nDeviceID, "str",DiskDrive.PNPDeviceID, "Int",0)
    DllCall("SetupApi.dll\CM_Get_Parent", "ptrp",&nDeviceID, "uint",nDeviceID, "int",0)
    If DllCall("SetupApi.dll\CM_Request_Device_Eject", "uint",nDeviceID, "ptrp",&nVetoType, "str",nVetoType, "int",1, "int",0)
    {
        DllCall("Kernel32.dll\FreeLibrary", "ptr",hSetupApi)
        Throw( Error("Error: Eject failed", PNP_VETO_TYPE(nVetoType), DiskDrive.Model) )
    }

    While( DirExist(Drive)  )                                ; wait for drive letter to disappear ..
        Sleep(100)
    DllCall("Kernel32.dll\FreeLibrary", "ptr",hSetupApi)     ; .. and then release Lib

    Throw( Error("Success: Eject succeeded", "Safely removed hardware", DiskDrive.Model) )


    OpenVolume(Drive)    ; -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    {
        Return DllCall("Kernel32.dll\CreateFile", "str", "\\.\" . Drive
                     , "int",0, "int", 0, "ptr",0, "int",0x3  ; OPEN_EXISTING := 0x3
                     , "int",0, "ptr",0, "ptr")
    }


    IOCTL_STORAGE_GET_DEVICE_NUMBER(hDevice)  ; -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    {
        Local STORAGE_DEVICE_NUMBER := Buffer(12, 0)

        DllCall("Kernel32.dll\DeviceIoControl", "ptr",hDevice
              , "int",0x2D1080 ; IOCTL_STORAGE_GET_DEVICE_NUMBER
              , "int",0, "int",0, "ptr",STORAGE_DEVICE_NUMBER
              , "int",12, "ptrp",0, "ptr",0)

        Return( NumGet(STORAGE_DEVICE_NUMBER, 4, "uint") )
    }


    CheckMediaType(MT)   ; -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    {
        Switch( MT )
        {
            Case "Removable Media", "External hard disk media":  Return
            Case "Fixed hard disk media":                        Return(MT)
            Default:                                             Return("Media type Unknown")
        }
    }


    PNP_VETO_TYPE(nVetoType)   ; -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    { ; https://docs.microsoft.com/en-us/windows/win32/api/cfg/ne-cfg-pnp_veto_type
        Return(
           ["The specified operation was rejected for an unknown reason." ;                           PNP_VetoTypeUnknown
           , "The device does not support the specified PnP operation."    ;                          PNP_VetoLegacyDevice
           , "The specified operation cannot be completed because of a pending close operation." ;    PNP_VetoPendingClose
           , "A Microsoft Win32 application vetoed the specified operation." ;                        PNP_VetoWindowsApp
           , "A Win32 service vetoed the specified operation." ;                                      PNP_VetoWindowsService
           , "The requested operation was rejected because of outstanding open handles." ;            PNP_VetoOutstandingOpen
           , "The device supports the specified operation, but the device rejected the operation." ;  PNP_VetoDevice
           , "The driver supports the specified operation, but the driver rejected the operation." ;  PNP_VetoDriver
           , "The device does not support the specified operation." ;                                 PNP_VetoIllegalDeviceRequest
           , "There is insufficient power to perform the requested operation." ;                      PNP_VetoInsufficientPower
           , "The device cannot be disabled." ;                                                       PNP_VetoNonDisableable
           , "The driver does not support the specified PnP operation." ;                             PNP_VetoLegacyDriver
           , "The caller has insufficient privileges to complete the operation." ;                    PNP_VetoInsufficientRights
           ][nVetoType] )
    }
}
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
My Scripts and Functions: V1  V2
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Eject_Ex() : Call Eject() from File Explorer

25 Aug 2021, 12:02

Eject_Ex( Drive )
Wrapper function for Ejecting a drive with Ctrl+q hotkey, from file explorer.

Dependency: WiseGui() : Themed splash text UI
The script Will pop progress/result in UI.. samples below:
 
Image        Image
 
Image        Image
 
 

Usage:
Run script, select a drive from This PC and press Ctrl+q and the drive letter will get removed when Eject() is successful.
If Ctrl+q is pressed when in a deep folder, then Eject() would cause filepath to become invalid and will close the File explorer window.
 
Image
 
 
Why I chose Ctrl+q as hotkey.
 
 
The script: Requires WiseGui()
 

Code: Select all

#Requires AutoHotkey v2.0-
#Warn
#SingleInstance
#Include <WiseGui> ; "WiseGui.ahk"

HotIfWinExist("WiseGui\EjectEx ahk_class AutoHotkeyGUI")
Hotkey( "Esc", (*) => WinClose() )
HotIfWinExist()

HotIfWinActive("ahk_class CabinetWClass")
Hotkey( "^q", (*) => Eject_Ex(WinExist()) )
HotIfWinActive()



;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Eject(Drive)  ; By SKAN for ah2 on CT91/D48E @ autohotkey.com/r?t=94113
{
    If ( ! DirExist( Drive := SubStr(Drive, 1, 1) . ":" ) )
        Throw( Error("Warning: Eject failed", "Invalid drive letter.", Drive . " ?!") )

    Local hVolume, DriveLabel := "<Label> " . DriveGetLabel(Drive)

    If ( hVolume := OpenVolume(Drive) ) = -1
        Throw( Error("Warning: Eject failed", "Mapped/substitute drive.", DriveLabel) )

    Local DEVICE_NUMBER := IOCTL_STORAGE_GET_DEVICE_NUMBER(hVolume)
    DllCall("Kernel32.dll\CloseHandle", "ptr",hVolume)

    Local queryStr, queryEnum, DiskDrive, UnsupportedMedia
    queryStr  := Format("{1:}'{2:}{3:}'", "Select * from Win32_DiskDrive where DeviceID=", "\\\\.\\PHYSICALDRIVE", DEVICE_NUMBER)
    queryEnum := ComObjGet("winmgmts:").ExecQuery(queryStr)._NewEnum()
    queryEnum(&DiskDrive)

    If ( UnsupportedMedia := CheckMediaType(DiskDrive.MediaType) )
        Throw( Error("Warning: Eject failed", UnsupportedMedia, DiskDrive.Model) )

    Local hSetupApi := DllCall("Kernel32.dll\LoadLibrary", "str","SetupApi.dll", "ptr"),  nDeviceID := 0,  nVetoType := 0
    DllCall("SetupApi.dll\CM_Locate_DevNode", "ptrp",&nDeviceID, "str",DiskDrive.PNPDeviceID, "Int",0)
    DllCall("SetupApi.dll\CM_Get_Parent", "ptrp",&nDeviceID, "uint",nDeviceID, "int",0)
    If DllCall("SetupApi.dll\CM_Request_Device_Eject", "uint",nDeviceID, "ptrp",&nVetoType, "str",nVetoType, "int",1, "int",0)
    {
        DllCall("Kernel32.dll\FreeLibrary", "ptr",hSetupApi)
        Throw( Error("Error: Eject failed", PNP_VETO_TYPE(nVetoType), DiskDrive.Model) )
    }

    While( DirExist(Drive)  )                                ; wait for drive letter to disappear ..
        Sleep(100)
    DllCall("Kernel32.dll\FreeLibrary", "ptr",hSetupApi)     ; .. and then release Lib

    Throw( Error("Success: Eject succeeded", "Safely removed hardware", DiskDrive.Model) )


    OpenVolume(Drive)    ; -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    {
        Return DllCall("Kernel32.dll\CreateFile", "str", "\\.\" . Drive
                     , "int",0, "int", 0, "ptr",0, "int",0x3  ; OPEN_EXISTING := 0x3
                     , "int",0, "ptr",0, "ptr")
    }


    IOCTL_STORAGE_GET_DEVICE_NUMBER(hDevice)  ; -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    {
        Local STORAGE_DEVICE_NUMBER := Buffer(12, 0)

        DllCall("Kernel32.dll\DeviceIoControl", "ptr",hDevice
              , "int",0x2D1080 ; IOCTL_STORAGE_GET_DEVICE_NUMBER
              , "int",0, "int",0, "ptr",STORAGE_DEVICE_NUMBER
              , "int",12, "ptrp",0, "ptr",0)

        Return( NumGet(STORAGE_DEVICE_NUMBER, 4, "uint") )
    }


    CheckMediaType(MT)   ; -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    {
        Switch( MT )
        {
            Case "Removable Media", "External hard disk media":  Return
            Case "Fixed hard disk media":                        Return(MT)
            Default:                                             Return("Media type Unknown")
        }
    }


    PNP_VETO_TYPE(nVetoType)   ; -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    { ; https://docs.microsoft.com/en-us/windows/win32/api/cfg/ne-cfg-pnp_veto_type
        Return(
           ["The specified operation was rejected for an unknown reason." ;                           PNP_VetoTypeUnknown
           , "The device does not support the specified PnP operation."    ;                          PNP_VetoLegacyDevice
           , "The specified operation cannot be completed because of a pending close operation." ;    PNP_VetoPendingClose
           , "A Microsoft Win32 application vetoed the specified operation." ;                        PNP_VetoWindowsApp
           , "A Win32 service vetoed the specified operation." ;                                      PNP_VetoWindowsService
           , "The requested operation was rejected because of outstanding open handles." ;            PNP_VetoOutstandingOpen
           , "The device supports the specified operation, but the device rejected the operation." ;  PNP_VetoDevice
           , "The driver supports the specified operation, but the driver rejected the operation." ;  PNP_VetoDriver
           , "The device does not support the specified operation." ;                                 PNP_VetoIllegalDeviceRequest
           , "There is insufficient power to perform the requested operation." ;                      PNP_VetoInsufficientPower
           , "The device cannot be disabled." ;                                                       PNP_VetoNonDisableable
           , "The driver does not support the specified PnP operation." ;                             PNP_VetoLegacyDriver
           , "The caller has insufficient privileges to complete the operation." ;                    PNP_VetoInsufficientRights
           ][nVetoType] )
    }
}
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


Eject_Ex(Hwnd)  ; By SKAN for ah2 on CT91/D48E @ autohotkey.com/r?p=417304
{
    Local Window, Win, Result, epath, Msg, ShellFolderView := ""

    For Window in ComObject("Shell.Application").Windows
        If ( Hwnd = Window.HWND )
        {
            ShellFolderView := Window.Document
            Win := Window
            Break
        }

    If ( ! ShellFolderView )
        Return

    epath := ShellFolderView.selecteditems.count ?  ShellFolderView.selecteditems.item(0).path :  ShellFolderView.folder.self.path

    If ( ! FileExist(ePath) )
         Return

    epath := SubStr(epath, 1, 2)

    WiseGui("EjectEx"
          , "Theme: Info"
          , "Subtext: Ejecting drive " . epath . "`n`n`n" ; extra linefeeds to reserve vertical space
          , "Close: 0"
          , "TextWidth: 288"
          , "Show: SlideNorth@400ms"
          , "Hide: SlideSouth@400ms"
          , "Move: -10, -40")

    Sleep( 500 )
    Try Eject(epath)
    Catch Error as Result

    Msg := StrSplit( Result.Message, ":", A_Space, 2)

    WiseGui("EjectEx"
          , "Theme:"    . Msg[1]
          , "Close: 1"
          , "Wmp:" . A_WinDir . ( Msg[1] = "Success" ? "\Media\Windows Notify.wav"  : "\Media\Windows Hardware Fail.wav" )
          , "MainText:" . Result.Extra
          , "SubText:"  . Format("{}... Drive {}`n`n{}", Msg[2], epath, Result.What)
          , "Timer:"    . ( Msg[1] = "Success" ? 3000 : 6000) )
}
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
My Scripts and Functions: V1  V2
Joeyy
Posts: 52
Joined: 08 Mar 2019, 01:57

Re: Eject() : For Removable storage devices

14 Sep 2023, 05:30

Thanks for your code. But it doesn't work. Where am I wrong?

Code: Select all

MyEject("E:")

MyEject(Drive)
{
    Local Result, Msg, Icon

    Try   Eject(Drive)
    Catch Error as Result

    Msg := StrSplit( Result.Message, ":",, 2 )
    Icon := Msg[1] = "Success" ? "Iconi" : Msg[1]="Error" ? "Iconx" : "Icon!"

    SetTimer( (*) => MsgBox(Result.What , Drive . "`n" . Msg[2], Icon . " T4"), -1)

    Return( Msg[1] = "Success" )
}
When run the code, it prompts:
SWarning: This variable appears to never be assigned a value.

pecifically: local Eject

007: {
010: Try
▶ 010: Eject(Drive)
011: Catch Error as Result
013: Msg := StrSplit( Result.Message, ":",, 2 )

For more details, read the documentation for #Warn.
and
Error: Invalid index.

Specifically: 2

014: Icon := Msg[1] = "Success" ? "Iconi" : Msg[1]="Error" ? "Iconx" : "Icon!"
016: {
▶ 016: Return MsgBox(Result.What , Drive . "
" . Msg[2], Icon . " T4")
016: }
016: SetTimer( (*) => MsgBox(Result.What , Drive . "
" . Msg[2], Icon . " T4"), -1)
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: Eject() : For Removable storage devices

14 Sep 2023, 05:35

Joeyy wrote:
14 Sep 2023, 05:30
Where am I wrong?
You are trying only the example without the function Eject().
Paste Eject() function into the example and run it again.

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: No registered users and 14 guests