#include-once #include #include #include Global Const $LOCK_RETRIES = 20 Global Const $LOCK_TIMEOUT = 10000 Global $_g_aDRIVE_EVENT_INFOS[1][4] Global $_g_bEVENT_OCCURED = False Global $_g_DRIVE_NAME = "" Global Const $_g_sDRIVE_TYPES = "ALL CDROM REMOVABLE FIXED NETWORK RAMDISK UNKNOWN Unknown Removable Fixed Network CDROM RAMDisk" Global $_g_sMOUNTED_LABELS = "" Global Const $_g_sNO_LABEL = "" Global Const $_g_sSTATE_MOUNTED = "MOUNTED" Global Const $_g_sSTATE_UNMOUNTED = "UNMOUNTED" Global $_g_sUNMOUNTED_LABELS = "" Global Const $_g_aNOT_INDEX_OUT_BOUND = ["", 0, 0, 0xF, "", "", "", ""] Global $_g_bAUTO_EJECT_DEVICE = False Global Const $_g_sVOLUME_NAME = 0 Global Const $_g_iSERIAL_NUMBER = 1 Global Const $_g_iFILE_NAME_LENGTH = 2 Global Const $_g_iFLAGS = 3 Global Const $_g_sFILE_SYSTEM = 4 Global Const $_g_sLABEL = 5 Global Const $_g_sSTATE = 6 Global Const $_g_DRIVE_TYPE = 7 Global Const $ERROR_INVALID_PATH = 1 Global Const $ERROR_GET_VOLUME_INFO = 2 #cs You can read these discussion topics on drives: https://autoitscript.fr/forum/viewtopic.php?p=87533#p87533 https://www.autoitscript.com/forum/topic/151878-ejecting-usb-drive/#comment-1088380 https://www.autoitscript.fr/forum/viewtopic.php?f=3&t=15066&p=103251&hilit=drive&sid=d01483bed974cea4190beab38f8894d5#p103251 The UDF you find here was developed in part thanks to the inspiration drawn from the following discussions on AutoIt forums: AutoItScript.fr - Ejecting a USB Key AutoItScript.com - Ejecting USB Drive We would like to thank the members of the AutoIt community for their contributions and discussions, which have contributed to the improvement of this UDF. #ce ; #FUNCTION# ==================================================================================================================== ; Name ..........: _CheckMountedLabels ; Description ...: Checks for changes in the mounted labels of a specified drive and updates the drive information accordingly. ; Syntax ........: _CheckMountedLabels() ; Parameters ....: None ; Return values .: None ; Author ........: Numeric ; =============================================================================================================================== Func _CheckMountedLabels() Local $sCurrent = _GetDriveLabels($_g_DRIVE_NAME) If $sCurrent <> $_g_sMOUNTED_LABELS Then Local $sMounted = "" Local $sUnmounted = "" Local $sReadLabel = "" For $i = 1 To StringLen($sCurrent) $sReadLabel = StringMid($sCurrent, $i, 1) If Not StringInStr($_g_sMOUNTED_LABELS, $sReadLabel) Then $sMounted &= $sReadLabel EndIf Next For $i = 1 To StringLen($_g_sMOUNTED_LABELS) $sReadLabel = StringMid($_g_sMOUNTED_LABELS, $i, 1) If Not StringInStr($sCurrent, $sReadLabel) Then $sUnmounted &= $sReadLabel EndIf Next $_g_sMOUNTED_LABELS = $sCurrent $_g_sUNMOUNTED_LABELS = $sUnmounted Local $sLabels = "" Local $sState = "" If $sMounted <> "" Then $sLabels = $sMounted $sState = $_g_sSTATE_MOUNTED Else $sLabels = $sUnmounted $sState = $_g_sSTATE_UNMOUNTED EndIf __setDriveInfos($_g_DRIVE_NAME, $sLabels, $sState) If $_g_bAUTO_EJECT_DEVICE Then __WinAPI_EjectVolume($sLabels) EndIf $_g_bEVENT_OCCURED = True Else $_g_aDRIVE_EVENT_INFOS = $_g_aNOT_INDEX_OUT_BOUND $_g_bEVENT_OCCURED = False EndIf EndFunc ;==>_CheckMountedLabels ; #FUNCTION# ==================================================================================================================== ; Name ..........: __setter_Device_Control ; Description ...: Performs device control operations on the specified device. ; Syntax ........: __setter_Device_Control($hDevice, $dwIoControlCode, ByRef $iRead) ; Parameters ....: $hDevice - A handle to the device on which the operation is to be performed. ; $dwIoControlCode - The control code for the operation. ; $iRead - [in/out] An integer variable to store the result of the operation. ; Return values .: True if the operation succeeds, False otherwise. ; Author ........: Numeric ; Modified ......: ; Remarks .......: This function allows you to perform low-level device control operations. Ensure that the provided ; handle ($hDevice) is valid and that the control code ($dwIoControlCode) corresponds to a supported ; operation. The result of the operation is stored in the provided variable ($iRead). ; Related .......: _WinAPI_DeviceIoControl ; Link ..........: https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol ; Example .......: ; ; Local $hDevice = __WinAPI_OpenVolume("D:") ; Local $iResult ; If __setter_Device_Control($hDevice, $IOCTL_SERIAL_GET_BAUD_RATE, $iResult) Then ; ConsoleWrite("Baud Rate: " & $iResult & @CRLF) ; Else ; ConsoleWrite("Error performing device control operation." & @CRLF) ; EndIf ; =============================================================================================================================== ; ........: See https://www.autoitscript.fr/forum/viewtopic.php?f=3&t=15066&p=103251&hilit=drive&sid=d01483bed974cea4190beab38f8894d5#p103251 ; =============================================================================================================================== Func __setter_Device_Control($hDevice, $dwIoControlCode, ByRef $iRead) Local $lpBytesReturned = 0 Local $tRead = DllStructCreate("int64 Data") Local $aResult = DllCall("kernel32.dll", "bool", "DeviceIoControl", _ "handle", $hDevice, _ "dword", $dwIoControlCode, _ "ptr", 0, _ "dword", 0, _ "ptr", DllStructGetPtr($tRead), _ "dword", DllStructGetSize($tRead), _ "dword*", $lpBytesReturned, _ "ptr", 0) $iRead = DllStructGetData($tRead, "Data") Return $aResult <> 0 EndFunc ;==>__setter_Device_Control ; #FUNCTION# ==================================================================================================================== ; Name ..........: _driveEventOccurred ; Description ...: Checks if a drive event has occurred. ; Syntax ........: _driveEventOccurred() ; Parameters ....: None ; Return values .: True if a drive event has occurred, False otherwise. ; =============================================================================================================================== Func _driveEventOcurred() If $_g_bEVENT_OCCURED == True Then $_g_bEVENT_OCCURED = False Return True Else Return False EndIf EndFunc ;==>_driveEventOcurred ; #FUNCTION# ==================================================================================================================== ; Name ..........: _GetDriveLabels ; Description ...: Gets the labels of drives of a specific type. ; Syntax ........: _GetDriveLabels([$sDriveType = "REMOVABLE"]) ; Parameters ....: $sDriveType - [optional] a string value. Default is "REMOVABLE". ; Return values .: Returns a string containing the labels of the specified drive type. ; Author ........: Numeric ; =============================================================================================================================== Func _GetDriveLabels($sDriveName = "REMOVABLE") Local $aDrives = DriveGetDrive($sDriveName) If Not IsArray($aDrives) Then Return $_g_sNO_LABEL Local $sLabels = "" For $i = 1 To UBound($aDrives) - 1 $sLabels &= $aDrives[$i] Next Return $sLabels EndFunc ;==>_GetDriveLabels ;Retrieves the drive name from the drive event information. Func _getEventDriveName() Return $_g_aDRIVE_EVENT_INFOS[$_g_DRIVE_TYPE] EndFunc ;==>_getEventDriveName ;Retrieves the volume name from the drive event information. Func _getEventVolumeName() Return $_g_aDRIVE_EVENT_INFOS[$_g_sVOLUME_NAME] EndFunc ;==>_getEventVolumeName ;Retrieves the volume file name length from the drive event information. Func _getEventVolumeFileNameLength() Return $_g_aDRIVE_EVENT_INFOS[$_g_iFILE_NAME_LENGTH] EndFunc ;==>_getEventVolumeFileNameLength ;Retrieves the volume flag from the drive event information. Func _getEventVolumeFlag() Return $_g_aDRIVE_EVENT_INFOS[$_g_iFLAGS] EndFunc ;==>_getEventVolumeFlag ;Retrieves the drive label from the drive event information. Func _getEventDriveLabel() Return $_g_aDRIVE_EVENT_INFOS[$_g_sLABEL] EndFunc ;==>_getEventDriveLabel ;Retrieves the drive serial number from the drive event information. Func _getEventDriveSerial() Return $_g_aDRIVE_EVENT_INFOS[$_g_iSERIAL_NUMBER] EndFunc ;==>_getEventDriveSerial ;Retrieves the event state from the drive event information. Func _getEventDriveState() Return $_g_aDRIVE_EVENT_INFOS[$_g_sSTATE] EndFunc ;==>_getEventDriveState ; #FUNCTION# ==================================================================================================================== ; Name ..........: __WinAPI_GetVolumeInformation ; Description ...: Retrieves information about the file system and volume associated with the specified root directory. ; Syntax ........: __WinAPI_GetVolumeInformation($sRoot) ; Parameters ....: $sRoot - A string value representing the root directory for the volume. ; Return values .: An array containing information about the file system and volume on success, or sets an error and returns an empty string on failure. ; The array includes: [0] Volume Name, [1] Serial Number, [2] File Name Length, [3] Flags, [4] File System, [5] Label, [6] State, [7] Drive Type. ; Author ........: Numeric ; Modified ......: ; Remarks .......: This function internally uses _WinAPI_GetVolumeInformation from WinAPIEx UDF. ; Related .......: ; Link ..........: ; Example .......: No ; =============================================================================================================================== Func __WinAPI_GetVolumeInformation($sRoot) ; Validation du chemin If Not _IsValidDrivePath($sRoot) Then Return SetError($ERROR_INVALID_PATH, 0, "") EndIf $sRoot = StringUpper($sRoot) ; Ajoute "\" à la fin si nécessaire If StringRight($sRoot, 1) <> "\" Then $sRoot &= "\" EndIf Local $iCurrentErrorMode = _WinAPI_GetErrorMode() _WinAPI_SetErrorMode($SEM_FAILCRITICALERRORS) Local $aData = _WinAPI_GetVolumeInformation($sRoot) Local $iError = @error _WinAPI_SetErrorMode($iCurrentErrorMode) If $iError Then Return SetError($ERROR_GET_VOLUME_INFO, 0, "") _ArrayAdd($aData, StringTrimRight($sRoot, 1)) Return $aData EndFunc ;==>__WinAPI_GetVolumeInformation ;Checks if the provided drive type is valid. Func _isValidDriveType($sDriveType) Return StringInStr($_g_sDRIVE_TYPES, $sDriveType, 1) EndFunc ;==>_isValidDriveType ;Validates the provided drive path. Func _IsValidDrivePath($sPath) Return StringRegExp($sPath, "[a-zA-Z]:\\?", $STR_REGEXPMATCH) EndFunc ;==>_IsValidDrivePath ;Initializes the mounted labels based on the current drive name. Func _InitLabels() $_g_sMOUNTED_LABELS = _GetDriveLabels($_g_DRIVE_NAME) EndFunc ;==>_InitLabels ; #FUNCTION# ==================================================================================================================== ; Name ..........: _RegisterDriveEvents ; Description ...: Registers drive events for monitoring. ; Syntax ........: _RegisterDriveEvents([$sDriveName = "REMOVABLE"[, $bReinitLabels = False[, $bAutoEject = False]]]) ; Parameters ....: $sDriveName - [optional] A string value representing the drive type. Default is "REMOVABLE". ; $bReinitLabels - [optional] A boolean value indicating whether to reinitialize labels. Default is False. ; $bAutoEject - [optional] A boolean value indicating whether to auto-eject the device on events. Default is False. ; $bReinitLabels : If set to True, the program will consider the events already occurred. If set to False, it will wait for the next events. ; Return values .: True on success, False on failure. ; Author ........: Numeric ; Modified ......: ; Remarks .......: Ensure that the script has appropriate access rights to monitor the specified drive. ; Related .......: ; Link ..........: ; Example .......: Yes ; _RegisterDriveEvents("REMOVABLE", True, True) ; ; Explanation of parameters: ; ; - "REMOVABLE": Monitors removable drives. ; ; - True: Reinitializes labels, considering the events already occurred. ; ; - True: Auto-ejects the device on events. ; While 1 ; If _driveEventOccurred() Then ; MsgBox(0, "Drive Event", "Drive " & _getDriveName() & " state: " & _getEventDriveState()) ; EndIf ; Sleep(100) ; WEnd ; =============================================================================================================================== Func _RegisterDriveEvents($sDriveName = "REMOVABLE", $bReinitLabels = False, $bAutoEject = False) If $sDriveName == Default Then $sDriveName = "REMOVABLE" If $bReinitLabels = Default Then $bReinitLabels = False If $bAutoEject == Default Then $bAutoEject = False If _isValidDriveType($sDriveName) Then $_g_DRIVE_NAME = $sDriveName $_g_bAUTO_EJECT_DEVICE = $bAutoEject If $bReinitLabels Then _InitLabels() EndIf AdlibRegister("_CheckMountedLabels") OnAutoItExitRegister("__freeRessources") Return True Else Return SetError(1, 0, False) EndIf EndFunc ;==>_RegisterDriveEvents Func __freeRessources() AdlibUnRegister("_CheckMountedLabels") EndFunc ;==>__freeRessources ;Sets drive information based on the provided parameters. Func __setDriveInfos($sDriveType, $sLabels, $sState) $_g_aDRIVE_EVENT_INFOS = __WinAPI_GetVolumeInformation($sLabels) If Not @error Then _ArrayAdd($_g_aDRIVE_EVENT_INFOS, $sState) _ArrayAdd($_g_aDRIVE_EVENT_INFOS, $sDriveType) Else $_g_aDRIVE_EVENT_INFOS = $_g_aNOT_INDEX_OUT_BOUND $_g_aDRIVE_EVENT_INFOS[$_g_sLABEL] = $sLabels $_g_aDRIVE_EVENT_INFOS[$_g_sSTATE] = $sState $_g_aDRIVE_EVENT_INFOS[$_g_DRIVE_TYPE] = $sDriveType EndIf EndFunc ;==>__setDriveInfos ; #FUNCTION# ==================================================================================================================== ; Name ..........: __WinAPI_AutoEjectVolume ; Description ...: Ejects the media from the volume. ; Syntax ........: __WinAPI_AutoEjectVolume($hVolume) ; Parameters ....: $hVolume - A handle value representing the volume. ; Return values .: True on success, False on failure. ; Author ........: Numeric ; =============================================================================================================================== Func __WinAPI_AutoEjectVolume($hVolume) Local $iRead Return __setter_Device_Control($hVolume, $IOCTL_STORAGE_EJECT_MEDIA, $iRead) EndFunc ;==>__WinAPI_AutoEjectVolume ; #FUNCTION# ==================================================================================================================== ; Name ..........: __WinAPI_CloseVolumeHwnd ; Description ...: Closes the volume handle. ; Syntax ........: __WinAPI_CloseVolumeHwnd($hVolume) ; Parameters ....: $hVolume - A handle value representing the volume. ; Return values .: True on success, False on failure. ;==> _WinAPI_CloseHandle ; =============================================================================================================================== Func __WinAPI_CloseVolumeHwnd($hVolume) Local $aCall = DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hVolume) If @error Then Return SetError(@error, @extended, False) Return $aCall[0] EndFunc ;==>__WinAPI_CloseVolumeHwnd ; #FUNCTION# ==================================================================================================================== ; Name ..........: __WinAPI_DismountVolume ; Description ...: Dismounts the volume associated with the specified handle. ; Syntax ........: __WinAPI_DismountVolume($hVolume) ; Parameters ....: $hVolume - A handle value. ; Return values .: True if the dismount operation succeeds, False otherwise. ; Author ........: Numeric ; =============================================================================================================================== Func __WinAPI_DismountVolume($hVolume) Local $iRead Return __setter_Device_Control($hVolume, $FSCTL_DISMOUNT_VOLUME, $iRead) ;Return _WinAPI_DeviceIoControl($hVolume,$FSCTL_DISMOUNT_VOLUME) EndFunc ;==>__WinAPI_DismountVolume ; #FUNCTION# ==================================================================================================================== ; Name ..........: __WinAPI_EjectVolume ; Description ...: Ejects the specified drive's media safely or forcefully, providing feedback in the console. ; Syntax ........: __WinAPI_EjectVolume($sDriveLetter) ; Parameters ....: $sDriveLetter - A string value representing the drive letter. ; Return values .: True if the media is ejected successfully, False otherwise. ; Author ........: Numeric ; =============================================================================================================================== Func __WinAPI_EjectVolume($sDriveLetter) Local $hVolume Local $bRemoveSafely = False Local $bAutoEject = False $hVolume = __WinAPI_OpenVolume($sDriveLetter) If $hVolume == $INVALID_HANDLE_VALUE Then Return False EndIf If __WinAPI_hardyLockVolume($hVolume) And __WinAPI_DismountVolume($hVolume) Then $bRemoveSafely = True ConsoleWrite("Volume Locked and Dismounted, trying to eject ..." & @CRLF) ;TrayTip("Volume " & $sDriveLetter & " Informations:", $sDriveLetter & " Successfuly Locked and dismounted! trying to Eject...", 2, BitOR($TIP_ICONASTERISK, $TIP_NOSOUND)) If __WinAPI_PreventVolumeRemoval($hVolume, False) And __WinAPI_AutoEjectVolume($hVolume) Then $bAutoEject = True EndIf Else ConsoleWrite("Volume can't be locked or dismounted. Please close any open files." & @CRLF) ;TrayTip("Volume " & $sDriveLetter & " Informations:", $sDriveLetter & " Volume can't be locked or dismounted. Please close any open files.", 2, BitOR($TIP_ICONASTERISK, $TIP_NOSOUND)) EndIf If Not __WinAPI_CloseVolumeHwnd($hVolume) Then Return False EndIf $sDriveLetter = StringUpper($sDriveLetter) If $bAutoEject Then ConsoleWrite(StringFormat("Media in Drive %s has been ejected safely.\n", $sDriveLetter)) ;TrayTip("Volume " & $sDriveLetter & " Informations:", StringFormat("Media in Drive %s has been ejected safely.\n", $sDriveLetter), 2, BitOR($TIP_ICONASTERISK, $TIP_NOSOUND)) Else If $bRemoveSafely Then ConsoleWrite(StringFormat("Media in Drive %s can be safely removed.\n", $sDriveLetter)) ;TrayTip("Volume " & $sDriveLetter & " Informations:", StringFormat("Media in Drive %s can be safely removed.\n", $sDriveLetter), 0, BitOR($TIP_ICONASTERISK, $TIP_NOSOUND)) EndIf EndIf Return True EndFunc ;==>__WinAPI_EjectVolume ; #FUNCTION# ==================================================================================================================== ; Name ..........: __WinAPI_hardyLockVolume ; Description ...: Attempts to lock the specified volume, making multiple attempts in case of failure. ; Syntax ........: __WinAPI_hardyLockVolume($hVolume) ; Parameters ....: $hVolume - A handle value representing the volume. ; Return values .: True if the volume is successfully locked, False otherwise. ; Author ........: Numeric ; =============================================================================================================================== Func __WinAPI_hardyLockVolume($hVolume) Local $iRead Local $nTryCount Local $dwSleepAmount = $LOCK_TIMEOUT / $LOCK_RETRIES For $nTryCount = 0 To $LOCK_RETRIES - 1 If __setter_Device_Control($hVolume, $FSCTL_LOCK_VOLUME, $iRead) Then Return True Else Sleep($dwSleepAmount) EndIf Next Return False EndFunc ;==>__WinAPI_hardyLockVolume ; #FUNCTION# ==================================================================================================================== ; Name ..........: __WinAPI_OpenVolume ; Description ...: Opens a handle to the specified volume. ; Syntax ........: __WinAPI_OpenVolume($sDriveLetter) ; Parameters ....: $sDriveLetter - A string value representing the drive letter (e.g., "C:"). ; Return values .: Returns a handle to the volume on success, $INVALID_HANDLE_VALUE on failure. ; Author ........: Numeric ; =============================================================================================================================== Func __WinAPI_OpenVolume($sDriveLetter) If StringRight($sDriveLetter, 1) <> ":" Or StringLen($sDriveLetter) <> 2 Or Not StringIsAlpha(StringLeft($sDriveLetter, 1)) Then Return SetError(1, 0, $INVALID_HANDLE_VALUE) Local Const $sDriveType = DriveGetType($sDriveLetter & "\") If Not _isValidDriveType($sDriveType) Then Return SetError(2, 0, $INVALID_HANDLE_VALUE) Local $dwAccessFlags Switch $sDriveType Case "Removable", "REMOVABLE" $dwAccessFlags = $GENERIC_READWRITE Case Else $dwAccessFlags = $GENERIC_READ ; secure mode EndSwitch Local Const $hVolume = _WinAPI_CreateFileEx("\\.\" & $sDriveLetter, $OPEN_EXISTING, $dwAccessFlags, $FILE_SHARE_READWRITE) If $hVolume == $INVALID_HANDLE_VALUE Then Return SetError(3, 0, $INVALID_HANDLE_VALUE) Return $hVolume EndFunc ;==>__WinAPI_OpenVolume ; #FUNCTION# ==================================================================================================================== ; Name ..........: __WinAPI_PreventVolumeRemoval ; Description ...: Prevents or allows the removal of the volume from the drive. ; Syntax ........: __WinAPI_PreventVolumeRemoval($hVolume, $bPreventRemoval = True) ; Parameters ....: $hVolume - A handle value representing the volume. ; $bPreventRemoval - [optional] A boolean value indicating whether to prevent removal. Default is True. ; Return values .: True on success, False on failure. ; Author ........: Numeric ; =============================================================================================================================== Func __WinAPI_PreventVolumeRemoval($hVolume, $bPreventRemoval = True) Local $tagPMR = DllStructCreate("boolean PreventMedialRemoval") DllStructSetData($tagPMR, "PreventMedialRemoval", $bPreventRemoval) Local $lpBytesReturned = DllStructCreate("byte Read") Local $pRead = DllStructGetPtr($lpBytesReturned) Local $aResult = DllCall("kernel32.dll", "bool", "DeviceIoControl", _ "handle", $hVolume, _ "dword", $IOCTL_STORAGE_MEDIA_REMOVAL, _ "ptr", DllStructGetPtr($tagPMR), _ "dword", DllStructGetSize($tagPMR), _ "ptr", 0, _ "dword", 0, _ "dword*", $pRead, _ "ptr", 0) Return $aResult <> 0 EndFunc ;==>__WinAPI_PreventVolumeRemoval