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
  • Guests
  • Last active:
  • Joined: --
Hi thanks for answering..

Ive just replaced my ID info in your example from 1st post

DeviceEject( "USB\VID_058F&PID_6387\GDLL4HW4" )


capitalH
  • Members
  • 64 posts
  • Last active: Apr 05 2012 05:18 AM
  • Joined: 23 Apr 2010

Open notepad / type something / save it to USB drive / Do not close notepad / try to eject the drive.


notepad does not lock up the USB drive (well not on my box in any case) MS Word/Excel/Powerpoint does or opening an explorer window on the USB drive

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

notepad does not lock up the USB drive


I came to know that after I posted :)

opening an explorer window on the USB drive


Explorer does not lock up the drive for me in XP. The explorer window mostly closes or redirects to 'Control Panel' depending on its ahk_class ( I think ).

  • Guests
  • Last active:
  • Joined: --
how do I get a usb device VID and PID? It means product ID and vendor ID, right?

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

how do I get a usb device VID and PID?


You can view it from Windows 'Device Manager' or use command-line tool like DevCon

If you need to find Device Instance ID for your USB Flash Drive, then you may insert
'Clipboard :=' in the following line
[color=red]Clipboard := [/color]DeviceID := USBD_GetDeviceID( Serial )
in USBD_SafelyRemove()

  • Guests
  • Last active:
  • Joined: --

USBD_SafelyRemove() would work with a drive letter, but not ID.

You can get the ID and assign the driveletter and then use USBD_SafelyRemove() to assure its the right device

  • Guests
  • Last active:
  • Joined: --
For some reason I dont get the traytip notification.. If I replace with msgbox It works. It disconnect as expected, I am just wondering why I dont get the notification?



Kingston := FindDriveBySerial( "0018F30C9" )
USBD_SafelyRemove( Kingston )
return

FindDriveBySerial( DeviceSerial ) { 
 DriveGet,RD, List, REMOVABLE 
 StringReplace, RD, RD, A     ; Remove Drive Letter 'A' from List 
 Loop, Parse, RD 
  If ( USBD_GetDeviceSerial( A_LoopField ":" ) = DeviceSerial ) 
    Return A_LoopField ":"
}

USBD_SafelyRemove( Drv ) { 
 If A_OSVersion not in  WIN_VISTA,WIN_XP,WIN_2000 
   Return 
 If ! ( Serial := USBD_GetDeviceSerial( Drv ) ) 
   Return  
 DeviceID := USBD_GetDeviceID( Serial ) 
 DeviceEject( DeviceID ) 
 IfExist, %Drv%\, TrayTip, %DeviceID%, Drive %Drv% was not Ejected!, 10, 3 
 ;Else, TrayTip, %DeviceID%, Drive %Drv% was safely Removed, 10, 1 
 Else, MsgBox, Test
} 

USBD_GetDeviceSerial( Drv="" ) { 
 DriveGet, DriveType, Type, %Drv% 
 IfNotEqual,DriveType,Removable, Return 
 RegRead, Hex, HKLM, SYSTEM\MountedDevices, \DosDevices\%Drv% 
 VarSetCapacity(U,(Sz:=StrLen(Hex)//2)),  VarSetCapacity(A,Sz+1) 
 Loop % Sz 
  NumPut( "0x" . SubStr(hex,2*A_Index-1,2), U, A_Index-1, "Char" ) 
 DllCall( "WideCharToMultiByte", Int,0,Int,0, UInt,&U,UInt,Sz, Str,A,UInt,Sz, Int,0,Int,0) 
 StringSplit, Part, A, # 
 ParentIdPrefixCheck := SubStr( Part3,1,InStr(Part3,"&",0,0)-1 ) 
 IfEqual,A_OSVersion,WIN_VISTA, Return,ParentIdPrefixCheck 
 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_GetDeviceID( 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( "CharUpperA", Str, "USB\" Device "\" Serial, Str ) 
}} 

DeviceEject( DeviceID ) { 
 hMod := DllCall( "LoadLibrary", Str,"SetupAPI.dll" ), VarSetCapacity(VE,255,0) 
 If ! DllCall( "SetupAPI\CM_Locate_DevNodeA", 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_EjectA", UInt,DI, UIntP,VT, Str,VE, UInt,255, Int,0) 
 DllCall( "FreeLibrary", UInt,hMod ) 
}

[ SKAN: Jeez! This post was originally dated: Sun Jun 05, 2011. I do not understand how Bump topic works! ]

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

Previous version was XP specific and did not work properly in Unicode version.
kWo4Lk1.png

robertcollier4
  • Members
  • 141 posts
  • Last active: Jun 28 2019 01:05 AM
  • Joined: 11 Nov 2012

Does anyone know how I can concatenate a colon in a string or in a function call? So as to make the following possible?

InputBox, DriveLetter, Drive Letter, Drive Letter to "Safely Remove"
if ErrorLevel
    Exit
else
	SafelyRemoveUMS(%DriveLetter% . ":", True)    
Exit

Doesn't work! I can't figure out how to get the colon ":" to be there!



Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Your error is not with the colon, but with the percent signs.

 

FAQ: When exactly are variable names enclosed in percent signs?



robertcollier4
  • Members
  • 141 posts
  • Last active: Jun 28 2019 01:05 AM
  • Joined: 11 Nov 2012

Thank you Lexikos. Here is what I added to the top of this great script: Then I have it in my Start Menu and I load it via my keyboard HUD launcher (Executor) - type in the drive letter - and hit enter. Great! So much faster than having to find the icon in system tray and having to go through multiple mouse clicks.

Menu Tray, Icon, C:\WINDOWS\system32\hotplug.dll, 0

InputBox, DriveLetter, Safe Removal, Drive Letter to "Safely Remove"
if ErrorLevel
    Exit
else
	SafelyRemoveUMS(DriveLetter . ":", True)
Exit


ynuance
  • Members
  • 12 posts
  • Last active: Mar 12 2017 08:32 AM
  • Joined: 02 Jan 2013

does anyone know how to retrieve the friendly name with a given driveletter ?

just like

 

 

name := getfriendname(H:)

 

@ SKAN

the function is too complex for me to understand



SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
@ SKAN
the function is too complex for me to understand
 
 

You just have to remove everything after
FRIENDLY := GenBuf  
DllCall( "SetupAPI\SetupDiDestroyDeviceInfoList", UInt,hDevInfo )  ; http://bit.ly/TWTmsN 
.. and put "return friendly" at the end of function
kWo4Lk1.png

ynuance
  • Members
  • 12 posts
  • Last active: Mar 12 2017 08:32 AM
  • Joined: 02 Jan 2013

You just have to remove everything after

FRIENDLY := GenBuf  
DllCall( "SetupAPI\SetupDiDestroyDeviceInfoList", UInt,hDevInfo )  ; http://bit.ly/TWTmsN 
.. and put "return friendly" at the end of function


great ,thanks so much!!! wink.png



robertcollier4
  • Members
  • 141 posts
  • Last active: Jun 28 2019 01:05 AM
  • Joined: 11 Nov 2012

Here is a GUI interface made for this function.

 

guiaddonsafelyremoveums.gif

 

;---------------------------------------------------------------
; Eject Removable Drives (GUI Addon to SKAN SafelyRemoveUMS)
; This script adds a GUI control ListView to display removable drives and then remove them with SKAN SafelyRemoveUMS
;---------------------------------------------------------------

#NoTrayIcon
Menu Tray, Icon, C:\WINDOWS\system32\hotplug.dll, 0

Gui, Add, ListView, -Multi x6 y6 r6 w488 vDriveListView gProcessDriveList, Drive|Size(MB)|Volume Label|Device
Gui, Add, Button, x6 w241 h60 gProcessDriveList Default, Eject
Gui, Add, Button, xp+247 yp wp h60 gCancelButton, Close

DriveGet, DrvList, List, REMOVABLE
Loop, Parse, DrvList
{
	DriveGet, ThisDrvLabel, Label, %A_Loopfield%:
	DriveGet, ThisDrvSize, Capacity, %A_Loopfield%:
	ThisDrvInfo := SafelyRemoveUMS(A_Loopfield . ":", True, True)
	LV_Add("", A_Loopfield, ThisDrvSize, ThisDrvLabel, ThisDrvInfo)
}

LV_ModifyCol(1, "40")
LV_ModifyCol(2, "60")
LV_ModifyCol(3, "160")
LV_ModifyCol(4, "AutoHdr")
 
Gui, -MinimizeBox -MaximizeBox
Gui, Show, w500, Eject Removable Drives
Return

ProcessDriveList:
RowSelected := LV_GetNext()
If(RowSelected = 0)
	{
		MsgBox, , No selection, No selection
	} 
	Else
	{
		LV_GetText(DriveSelected, RowSelected, 1)
		SafelyRemoveUMS(DriveSelected . ":", True)
		Reload
	}
Return

GuiEscape:
CancelButton:
GuiClose:
ExitApp
Return

;ADD SKAN SafelyRemoveUMS() Function HERE

 

 

After adding the SKAN SafelyRemoveUMS() Function, perform the following 2 modifications to support the "Device" field showing in the GUI.


1. Remove this line:

SafelyRemoveUMS( DrivePath, Retry=0) { 

and replace it with:

SafelyRemoveUMS( DrivePath, Retry=0, InfoOnly=0 ) {

 
2. Find the following lines:

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

and after the above lines, add:

If(InfoOnly)
    Return FRIENDLY