[R] Ejecter un disque dur EXTERNE (USB)

Aide et conseils concernant AutoIt et ses outils.
Règles du forum
.
Répondre
Avatar du membre
franco
Niveau 7
Niveau 7
Messages : 342
Enregistré le : lun. 03 oct. 2011 22:37
Localisation : Liévin
Status : Hors ligne

[R] Ejecter un disque dur EXTERNE (USB)  

#1

Message par franco »

Bonjour.


Je cherchais avant tout un moyen « d'éjecter » tout périphérique USB.

Je suis tombé sur ce topic : https://www.autoitscript.com/forum/topi ... nt=1088380


Ca fonctionne pour les clés USB (var le type est "REMOVABLE")
Mais pas pour les disques dur externes USB...


En fait, ça fonctionne, si je met le type FIXED, comme le suggère le commentaire sur le script.
; USB external hard drives report as "Fixed", not "Removable", so use "All" to cover both kinds

Mais le disque INTERNE a aussi le type "FIXED", et je n'ai pas envie de le "dégommer" :mrgreen:

Je pensais - surement naïvement - faire une condition, en excluant les lecteurs dont le type est "Fixed"
Mais peine perdu :mrgreen:


Voici mon script :
#include <Process.au3>

$DriveLabel = "usbdrive" ; your drive label here

$aDrives = DriveGetDrive("ALL")
 ; USB external hard drives report as "Fixed", not "Removable", so use "All" to cover both kinds

If Not @error Then
    For $i = 1 To $aDrives[0]

;~ if not DriveGetType($aDrives[$i]) = "Fixed" then MsgBox(0,"",$aDrives[$i])
;~ if DriveGetType($aDrives[$i]) <> "Fixed" then MsgBox(0,"",$aDrives[$i])
MsgBox(0,"",DriveGetType($aDrives[$i]))
;~             MsgBox(0,"",DriveGetType($aDrives[$i]))

;~             EjectDrive($aDrives[$i] & "")



    Next
EndIf



Func EjectDrive($dLetter) ; From VB script, Authors XoLoX and ChrisL
    Local Const $SSF_DRIVES = 17
    Local $oShell, $oNameSpace, $oDrive, $strDrive
    $strDrive = $dLetter
    $strName = DriveGetLabel($strDrive) & "(" & $strDrive & ")"
    $oShell = ObjCreate("Shell.Application")
    $oNameSpace = $oShell.NameSpace($SSF_DRIVES)
    $oDrive = $oNameSpace.ParseName($strDrive)
    $oDrive.InvokeVerb("Eject")
    MsgBox(0, "2", "We're here in the function")
    TrayTip("USB Drive " & $strName & " ejected", "You can now remove the device safely.", 5, 1)
    MsgBox(0, "", $strName & " drive ejected. You can now remove the device safely.", 5)
EndFunc   ;==>EjectDrive

De là, j'ai une idée (mais surement pas fameuse)
- Faire un $aDrives = DriveGetDrive("ALL")
- exclure le lecteur si = à @homedrive...


Je vous l'avais bien dit, l'idée est foireuse...


Avez-vous une piste à me conseiller ?

PS : sans passer par un outil tiers, comme DevCon par exemple.


EDIT :
J'aurais pas le temps d'étudier cette piste, qui m'a l'air excellente : viewtopic.php?p=87533#p87533

je verrais surement ça demain, boulot oblige


++
Modifié en dernier par franco le lun. 18 févr. 2019 22:23, modifié 1 fois.
L'entraide, c'est mon dada ;)
Avatar du membre
walkson
Modérateur
Modérateur
Messages : 1020
Enregistré le : ven. 12 août 2011 19:49
Localisation : Hurepoix
Status : Hors ligne

Re: [..] Ejecter un disque dur EXTERNE (USB)

#2

Message par walkson »

Bonjour,
Pas facile votre question. J'ai testé plein de codes sauf résultats sauf celui-ci

Code : Tout sélectionner

#include <WinApi.au3>
;Prototypes
;BOOL EjectVolume(TCHAR cDriveLetter);
;HANDLE OpenVolume(TCHAR cDriveLetter);
;BOOL LockVolume(HANDLE hVolume);
;BOOL DismountVolume(HANDLE hVolume);
;BOOL PreventRemovalOfVolume(HANDLE hVolume, BOOL fPrevent);
;BOOL AutoEjectVolume(HANDLE hVolume);
;BOOL CloseVolume(HANDLE hVolume);

;StringFormat Output
$szVolumeFormat = "\\\\.\\%s"
$szRootFormat = "%s\\"
$szErrorFormat = "Error %d: %s\n"
;------------------------------------------
;Arbitrary variables
;Global Const $INVALID_HANDLE_VALUE = 0
;------------------------------------------
;DeviceIoControl Contants
;Global Const $FSCTL_LOCK_VOLUME = int(0x090018)
;Global Const $FSCTL_DISMOUNT_VOLUME = int(0x00090020)
;Global Const $IOCTL_STORAGE_EJECT_MEDIA  = int(0x002D4808)
;Global Const $IOCTL_STORAGE_MEDIA_REMOVAL = int(0x002D4804)

;------------------------------------------
;Retry Constants
Global Const $LOCK_TIMEOUT = 10000       ; 10 Seconds
Global Const $LOCK_RETRIES = 20
$OpenVolume = InputBox("Ejecting...", "Enter the drive to eject", "G:")
ConsoleWrite("Trying to Eject the drive " & EjectVolume($OpenVolume) & @crlf)

Func ReportError($szMsg)
    ConsoleWrite(StringFormat($szErrorFormat, _WinAPI_GetLastErrorMessage (), $szMsg) & @CRLF)
    Exit
EndFunc   ;==>ReportError
Func OpenVolume($cDriveLetter)
    ;HANDLE hVolume
    ;UINT uDriveType
    ;TCHAR szVolumeName[8]
    ;TCHAR szRootName[5]
    ;DWORD dwAccessFlags
    $szRootName = StringFormat($szRootFormat, $cDriveLetter)
    $uDriveType = DriveGetType($szRootName);
    ConsoleWrite($szRootName & @tab & $uDriveType & @crlf)
    Switch $uDriveType
        Case "Fixed"
            $dwAccessFlags = 6
		Case "Removable"
            $dwAccessFlags = 6
        Case "CDROM"
            $dwAccessFlags = 2
        Case Else
            ConsoleWrite("Cannot eject.  Drive type is incorrect." & @CRLF)
            Return $INVALID_HANDLE_VALUE
    EndSwitch
    $szVolumeName = StringFormat($szVolumeFormat, $cDriveLetter)
    ;$szVolumeName = $szVolumeFormat & $cDriveLetter
    ConsoleWrite($szVolumeName & @crlf )
    $hVolume = _WinAPI_CreateFile ($szVolumeName, 2,$dwAccessFlags, 6)

    #cs
        hVolume = CreateFile(   szVolumeName,
        dwAccessFlags,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL );
    #ce
    If ($hVolume == $INVALID_HANDLE_VALUE) Then ReportError("CreateFile");
    Return $hVolume;
EndFunc   ;==>OpenVolume
Func CloseVolume($hVolume)
    Return _WinAPI_CloseHandle ($hVolume);
EndFunc   ;==>CloseVolume
Func LockVolume($hVolume)
    Local $dwBytesReturned
    Local $dwSleepAmount
    Local $nTryCount
    local $iRead
    $dwSleepAmount = $LOCK_TIMEOUT / $LOCK_RETRIES;
    ; Do this in a loop until a timeout period has expired
    For $nTryCount = 0 To $nTryCount < $LOCK_RETRIES
        If _Device_Control($hVolume, $FSCTL_LOCK_VOLUME, $iRead) Then
            Return True
        Else
            Sleep($dwSleepAmount);
        EndIf
    Next
    Return False;
EndFunc   ;==>LockVolume
Func DismountVolume($hVolume)
    ConsoleWrite("Dismount " & $hVolume & @crlf)
    Local $dwBytesReturned, $iRead
    local $aResult = _Device_Control($hVolume, $FSCTL_DISMOUNT_VOLUME, $iRead)
    msgbox(0,"",$aResult)
    Return $aResult
    ;Return $dwBytesReturned
EndFunc   ;==>DismountVolume
Func PreventRemovalOfVolume($hVolume, $fPreventRemoval)
    Local $dwBytesReturned
    Local $aResult
    Local $lpInbuffer,$nInBufferSize,$lpOutBuffer,$nOutBufferSize,$lpOverlapped

    $PMRBUFFER = DllStructCreate("bool PreventMediaRemoval")

    DllStructSetData($PMRBUFFER,"PreventMediaRemoval",$fPreventRemoval)

    $lpBytesReturned    = DllStructCreate("int Read")
    $pRead   = DllStructGetPtr($lpBytesReturned, "Read")
    $aResult = Dllcall("kernel32.dll","int","DeviceIoControl","hwnd",$hVolume,"uint",$IOCTL_STORAGE_MEDIA_REMOVAL,"ptr",DllStructGetPtr($PMRBUFFER),"uint",DllStructGetSize($PMRBUFFER), _
    "ptr",$lpOutBuffer,"uint",$nOutBufferSize,"ptr",$pRead,"ptr",$lpOverlapped)
    if $aResult = 0 then msgbox(0,"",_WinAPI_GetLastErrorMessage())
    Return $aResult <> 0

     ;& PMRBuffer, sizeof (PREVENT_MEDIA_REMOVAL),
    ;NULL, 0,
    ; & dwBytesReturned,
    ;NULL);
EndFunc   ;==>PreventRemovalOfVolume
Func AutoEjectVolume($hVolume)
    Local $aResult, $iRead;
    $aResult = _Device_Control($hVolume, $IOCTL_STORAGE_EJECT_MEDIA, $iRead)
    Return $aResult
EndFunc   ;==>AutoEjectVolume
Func EjectVolume($cDriveLetter)
    Local $hVolume;
    Local $fRemoveSafely = False;
    Local $fAutoEject = False;
    ; Open the volume.
    $hVolume = OpenVolume($cDriveLetter);
    If $hVolume == $INVALID_HANDLE_VALUE Then Return False
    ; Lock and dismount the volume.
    If LockVolume($hVolume) And DismountVolume($hVolume) Then
        $fRemoveSafely = True;
        ConsoleWrite("Volume Locked and Dismounted, trying to eject " & @crlf)
        ; Set prevent removal to false and eject the volume.
        If PreventRemovalOfVolume($hVolume, False) And AutoEjectVolume($hVolume) Then
            $fAutoEject = True;
        EndIf
    Else
        ConsoleWrite("Volume can't be locked or dismounted, please close possible opened files" & @crlf)
    EndIf

    ; Close the volume so other processes can use the drive.
    If CloseVolume($hVolume) = False Then
        Return False;
    EndIf

    If $fAutoEject Then
        ConsoleWrite(StringFormat("Media in Drive %s has been ejected safely.\n", $cDriveLetter))
    Else
        If $fRemoveSafely Then
            ConsoleWrite(StringFormat("Media in Drive %s can be safely removed.\n", $cDriveLetter))
        EndIf
    EndIf

    Return True;
EndFunc   ;==>EjectVolume
Func _Device_Control($hDevice, $dwIoControlCode, ByRef $iRead)
    Local $aResult
    Local $lpInbuffer,$nInBufferSize,$lpOutBuffer,$nOutBufferSize,$lpOverlapped
    $tRead   = DllStructCreate("int Data")

    $aResult = Dllcall("kernel32.dll","int","DeviceIoControl","hwnd",$hDevice,"uint",$dwIoControlCode,"ptr",$lpInBuffer,"uint",0, _
    "ptr",$lpOutBuffer,"uint",0,"ptr",DllStructGetPtr($tRead),"ptr",$lpOverlapped)

    $iRead   = DllStructGetData($tRead, "Data")

    ConsoleWrite("Device Control " & $iRead & @CRLF)
    Return $aResult<>0
EndFunc   ;==>_Device_Control
et encore j'ai du le modifier en rajoutant
Case "Fixed"
$dwAccessFlags = 6
Le résultat n'est pas terrible car le disque est désactivé mais pas éjecté (reste visible mais inaccessible)
Dans mes recherches, plusieurs fois a été cité devcon.exe
https://docs.microsoft.com/fr-fr/window ... est/devcon
https://forum.pcastuces.com/commande_de ... s11219.htm
Je n'ai pas poussé plus loin mes recherches avec ce programme. Par contre, USB Disk Ejector semble plus simple d'emploi, programme portable et commandline https://quickandeasysoftware.net/softwa ... sk-ejector
The following command line options are available:

  * /?
    Displays a dialog that shows all command line options.

  * /NOSAVE
    Stops the program from saving any setting to a file and ignores any
    existing settings file.

  * /SILENT
    Stops balloon messages from appearing when the program is run in GUI
    mode.

  * /SHOWEJECT
    Shows the standard ''It is now safe to now safe to remove'' message
    when a drive is ejected. This message is disabled by default.

  * /REMOVETHIS
    Ejects the drive that the program is running from. Eg if the program
    is run from a usb stick on drive G then drive G would be ejected.

  * /REMOVELETTER
    Ejects the specified drive letter. Eg /REMOVELETTER G

  * /REMOVENAME
    Ejects the drive with the specified name. Eg /REMOVEDRIVE "Sandisk
    U3 Titanium"

    Partial name matching is possible if a wildcard (*) is used. Eg
    /REMOVENAME "*SANDISK" would eject a drive that had Sandisk in its name.

  * /CLOSEAPPS
    If there are any applications running from the drive, asks them to
    close. If any applications refuse to close then the eject will fail.
    This switch is considered relatively safe and should mean that no
    unsaved data is lost.

  * /CLOSEAPPSFORCE
    If there are any applications running from the drive, force them to
    close. This will mean that any unsaved data in those programs will
    be lost. This has the same effect as ending a task/process.
Fonctionne sur clef ou disque USB.
Je devine que vous préféreriez ne pas passer par un programme intermédiaire, désolé :P
Je vous joints un bout de code, la première partie liste toutes les connections USB (clef, disque, souris...) et la deuxième partie surveille les connections et déconnections de clefs ou de disque USB

Code : Tout sélectionner

$strComputer = "."

$objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\cimv2")
$colDevices = $objWMIService.ExecQuery ("Select * From Win32_USBControllerDevice")

For $objDevice in $colDevices
    $strDeviceName = $objDevice.Dependent
    $strQuotes = Chr(34)
    $strDeviceName = StringReplace($strDeviceName, $strQuotes, "")
    $arrDeviceNames = StringSplit($strDeviceName, "=")
    $strDeviceName = $arrDeviceNames[2]
    $colUSBDevices = $objWMIService.ExecQuery ("Select * From Win32_PnPEntity Where DeviceID = '" & $strDeviceName & "'")
    For $objUSBDevice in $colUSBDevices
        MsgBox(0,"USB Description",$objUSBDevice.Description)
    Next
    MsgBox(0,"USB Devices",$strDeviceName)
Next
;===========================================================================================================================================================
$colEvents = $objWMIService.ExecNotificationQuery("Select * From __InstanceOperationEvent Within 5 Where " & "TargetInstance isa 'Win32_LogicalDisk'")

While 1
$objEvent = $colEvents.NextEvent
If $objEvent.TargetInstance.DriveType = 3 Or $objEvent.TargetInstance.DriveType = 2  Then ;2 / Removable Disk, 3 / Local Disk, 4 / Network Drive
Select
Case $objEvent.Path_.Class()="__InstanceCreationEvent"
Consolewrite("Drive " & $objEvent.TargetInstance.DeviceId & "has been added." & @CR)
Case $objEvent.Path_.Class()="__InstanceDeletionEvent"
Consolewrite("Drive " & $objEvent.TargetInstance.DeviceId & "has been removed."& @CR)
EndSelect
EndIf
WEnd
A noter que j'ai essayé Diskpart sans résultat et "RunDll32.exe shell32.dll,Control_RunDLL hotplug.dll" voie sans issue
Cordialement,
Walkson
"Horas non numero nisi serenas " Le canon de midi
(Je ne compte que les heures heureuses)
Avatar du membre
Tlem
Site Admin
Site Admin
Messages : 11773
Enregistré le : ven. 20 juil. 2007 21:00
Localisation : Bordeaux
Status : Hors ligne

Re: [..] Ejecter un disque dur EXTERNE (USB)

#3

Message par Tlem »

Bonsoir.
Même si cela ne permet pas d'automatiser à 100% l'éjection, il y a cette solution : RunDll32.exe shell32.dll,Control_RunDLL hotplug.dll
Au moins, là il n'y a que les périphériques "éjectables".
Thierry

Rechercher sur le forum ----- Les règles du forum
Le "ça ne marche pas" est une conséquence commune découlant de beaucoup trop de raisons potentielles ...

Une idée ne peut pas appartenir à quelqu'un. (Albert Jacquard) tiré du documentaire "Copié n'est pas volé".
Avatar du membre
franco
Niveau 7
Niveau 7
Messages : 342
Enregistré le : lun. 03 oct. 2011 22:37
Localisation : Liévin
Status : Hors ligne

Re: [..] Ejecter un disque dur EXTERNE (USB)

#4

Message par franco »

Salut.

Désolé de répondre que maintenant, je suis en plein dans un état grippal lol...

Du coup, je réfléchis à 2 à l'heure.



@walkson : j'avoue que je voulais, au départ, me passer d'outil tiers. Mais plus j'ai testé usb disk ejector, moins j'ai envie de m'en passer. :mrgreen:

Le seul hic, c'est quand j'ai branché mon disque dur interne via boitier externe : plusieurs lettres...
Et selon la lettre, erreur avec usb disk ejector.

Du coup, j'ai cherché un autre outil en ligne de commandes, car ce concept m'a plu :mrgreen:
et j'ai trouvé RemoveDrive : https://www.uwe-sieber.de/drivetools_e.html

Suffit de faire RemoveDrive f: (par exemple) pour que cela fonctionne.


Du coup, j'ai pu finaliser un script à la vitesse d'une tortue (^^)


Voici le code :
#RequireAdmin
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

$RmDr = @TempDir & "\RemoveDrive.exe"
FileInstall("RemoveDrive.exe",$RmDr)


$USBStor = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR"

Global $Form1 = GUICreate("Form1", 615, 437, 192, 124)

$act_usb = GUICtrlCreateButton("Acctiver port USB", 5, 5, 120, 20)
$desact_usb = GUICtrlCreateButton("Désactiver port USB", 130, 5, 120, 20)
$eject_usb = GUICtrlCreateButton("Ejecter tout périphérique USB", 255, 5, 150, 20)

GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

While 1
   $nMsg = GUIGetMsg()
   Switch $nMsg
      Case $GUI_EVENT_CLOSE
         FileDelete($RmDr)
         Exit

      Case $desact_usb
         _ejectUSB()
         RegWrite($USBStor, "Start", "REG_DWORD", 4)

      Case $act_usb
         RegWrite($USBStor, "Start", "REG_DWORD", 3)

      Case $eject_usb
         _ejectUSB()


   EndSwitch
WEnd

Func _FindExtHdd()
   Local $iPid, $sStdout, $iTotalSize, $sGetSpace, $addspace, $externaldrives
   $iPid = Run(@ComSpec & ' /c wmic diskdrive get PNPDeviceID, size /format:csv | find /v "USBSTOR"', '', @SW_HIDE, 2)
   While ProcessExists($iPid)
      $sStdout &= StdoutRead($iPid)
   WEnd
   $sStdout = StringSplit($sStdout, ",")
   $iTotalSize = $sStdout[$sStdout[0]] / 1024 / 1024 / 1024 ; GB
   Do
      $iTotalSize = StringTrimRight($iTotalSize, 1)
   Until Not StringInStr($iTotalSize, ".")
   Local $aDriveList = DriveGetDrive("FIXED")
   For $w = 1 To UBound($aDriveList) - 1
      $sGetSpace = DriveSpaceTotal($aDriveList[$w]) / 1024
      Do
         $sGetSpace = StringTrimRight($sGetSpace, 1)
      Until Not StringInStr($sGetSpace, ".")
      $addspace += $sGetSpace
      If $addspace > $iTotalSize Then $externaldrives &= $aDriveList[$w] & @CRLF
   Next
   If $externaldrives Then Return $externaldrives
EndFunc   ;==>_FindExtHdd

Func _ejectUSB()
   Local $letters
   If Not _FindExtHdd() = 0 Then $letters &= _FindExtHdd() & @CRLF
   Local $aArray = DriveGetDrive("REMOVABLE")
   If Not @error Then
      For $i = 1 To $aArray[0]
         $letters &= $aArray[$i] & @CRLF
      Next
   EndIf
   If $letters <> '' Then
      For $a = 1 To StringSplit($letters, @LF)[0]
         $letter = StringSplit($letters, @LF)[$a]
         If $letter <> '' Then
            $command = '"' & $RmDr & '" ' & $letter
            RunWait(@ComSpec & " /c " & $command, "", @SW_HIDE)
         EndIf
      Next
   EndIf
EndFunc   ;==>_ejectUSB
A-t-il besoin d'être modifié ?
Il est fonctionnel pour ma part.


++ et merci de m'avoir aidé.
L'entraide, c'est mon dada ;)
Avatar du membre
franco
Niveau 7
Niveau 7
Messages : 342
Enregistré le : lun. 03 oct. 2011 22:37
Localisation : Liévin
Status : Hors ligne

Re: [R] Ejecter un disque dur EXTERNE (USB)

#5

Message par franco »

Bonsoir.

J'ai passé le topic en Résolu ( car je dois en poster un autre, radicalement différent ^^ )


A toute ! ^^
L'entraide, c'est mon dada ;)
Répondre