Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi USB Stick sicher entfernen unter Vista (https://www.delphipraxis.net/120716-usb-stick-sicher-entfernen-unter-vista.html)

Pichel 15. Sep 2008 19:54


USB Stick sicher entfernen unter Vista
 
Hallöchen.

Was unter XP tadellos funktionierte das streikt nun unter Vista.
Ich möchte aus meinem Programm heraus auch unter Vista einen Usb-Stick sicher entfernen.

Hierzu habe ich auch alle Laufwerke in einer Listbox inkl. Instanz als Object basieren auf irgendnem Code den ich mal irgendwo gefunden habe.
Bem.nicht schön aber funktionier ;-)

Delphi-Quellcode:
function TaForm.SaveRemoveDrive(Drive: Char): Boolean;
var S: string;
    idx, it,
    I: Integer;
    Inst: DEVINST;
    SymbolicName: string;
    VetoType: PNP_VETO_TYPE;
    //VetoBuffer: array [0..MAX_PATH-1] of TCHAR;
begin
  Result := False;
  idx   := -1;
  // update the list of drive mount points
  FillInRemovableDriveMountPoints(DriveMountPoints);
  // show the list of drive letters from the drive mount point list
  DriveList.Items.BeginUpdate;
  DriveList.Items.Clear;
  S := 'A:';
  for I := 0 to DriveMountPoints.Count - 1 do begin
    S[1] := Char(DriveMountPoints.Objects[I]);
    GetDriveInstanceID(DriveMountPoints[I], Inst);
    SymbolicName := GetSymbolicName(Inst);
    it := DriveList.Items.AddObject(S + ' ' + ExtractSerialNumber(SymbolicName), TObject(Inst));
    if UpperCase(S[1]) = UpperCase(Drive) then begin
      idx := it;
    end;
  end;
  DriveList.Items.EndUpdate;
  if idx > -1 then
    // find the mount point name for the drive letter clicked
    for I := 0 to DriveMountPoints.Count - 1 do begin
      if Char(DriveMountPoints.Objects[I]) = DriveList.Items[idx][1] then begin
        VetoType := 0;
        // try to do a silent safe removal
        // for drives not able to do a safe removal the function simply fails
        // FillChar(VetoBuffer[0], SizeOf(VetoBuffer), 0);
        // CM_Request_Device_Eject(DEVINST(DriveList.Items.Objects[I]),
        //  @VetoType, @VetoBuffer[0], Length(VetoBuffer), 0);
        // do a safe removal with dialog
        CM_Request_Device_Eject(DEVINST(DriveList.Items.Objects[idx]), @VetoType, nil, 0, 0);
        Result := True;
      end;
    end;
end;
Den restlichen Programmcode hab ich mal hier gepostet

Delphi-Quellcode:
uses
  JwaWinBase, JwaWinType, Cfg, CfgMgr32, SetupApi, mySysUtils;

{$R *.dfm}

// encapsulate GetVolumeNameForVolumeMountPoint in a Delphi-style function

function GetVolumeNameForVolumeMountPointString(Name: string): string;
var
  Volume: array [0..MAX_PATH] of Char;
begin
  FillChar(Volume[0], SizeOf(Volume), 0);
  GetVolumeNameForVolumeMountPoint(PChar(Name), @Volume[0], SizeOf(Volume));
  Result := Volume;
end;

// fills the TStringList with the mount points of all removable drives

procedure FillInRemovableDriveMountPoints(MountPoints: TStrings);
const
  MAX_DRIVES = 26;
var
  I: Integer;
  dwDriveMask: DWORD;
  DriveName: string;
begin
  MountPoints.Clear;
  // get all mounted drive letters as bitmask
  dwDriveMask := GetLogicalDrives;
  DriveName := 'A:\';
  // check all drive letters
  for I := 0 to MAX_DRIVES - 1 do
    // if drive letter exists
    if (dwDriveMask and (1 shl I)) <> 0 then
    begin
      DriveName[1] := 'A';
      Inc(DriveName[1], I);
      // see if it is a removable drive
      if GetDriveType(PChar(DriveName)) = DRIVE_REMOVABLE then
        // store mount point string and corresponding drive letter in list
        MountPoints.AddObject(GetVolumeNameForVolumeMountPointString(DriveName), TObject(DriveName[1]));
    end;
end;

// Delphi style encapsulation for CM_Get_Device_ID

function GetDeviceID(Inst: DEVINST): string;
var
  Buffer: PTSTR;
  Size: ULONG;
begin
  CM_Get_Device_ID_Size(Size, Inst, 0);
  // Required! See DDK help for CM_Get_Device_ID
  Inc(Size);
  Buffer := AllocMem(Size * SizeOf(TCHAR));
  CM_Get_Device_ID(Inst, Buffer, Size, 0);
  Result := Buffer;
  FreeMem(Buffer);
end;

// simple extraction of the bus name from DeviceID string

function ExtractBus(DeviceID: string): string;
begin
  Result := Copy(DeviceID, 1, Pos('\', DeviceID) - 1);
end;

// get the "SymbolicName" registry entry of a device
// for an USB device this string contains VID, PID and SerialNumber string

function GetSymbolicName(Inst: DEVINST): string;
var
  Len: DWORD;
  Key: HKEY;
  // a hopefully sufficiently large buffer
  Buffer: array [0..4095] of Char;
begin
  CM_Open_DevNode_Key(Inst, KEY_READ, 0,
    REGDISPOSITION(RegDisposition_OpenExisting), Key, 0);
  Buffer[0] := #0;
  if Key <> INVALID_HANDLE_VALUE then
  begin
    Len := SizeOf(Buffer);
    RegQueryValueEx(Key, 'SymbolicName', nil, nil, @Buffer[0], @Len);
    RegCloseKey(Key);
  end;
  Result := Buffer;
end;

// extract a 4 digit hex number from SymbolicName
// example "\??\USB#Vid_08ec&Pid_0010#0918121014000B59#{a5dcbf10-6530-11d2-901f-00c04fb951ed}"

function ExtractNum(const SymbolicName, Prefix: string): Integer;
var
  S: string;
  N: Integer;
begin
  S := LowerCase(SymbolicName);
  N := Pos(Prefix, S);
  if N > 0 then
  begin
    S := '$' + Copy(SymbolicName, N + Length(Prefix), 4);
    Result := StrToInt(S);
  end
  else
    Result := 0;
end;

function ExtractVID(const SymbolicName: string): Integer;
begin
  Result := ExtractNum(SymbolicName, 'vid_');
end;

function ExtractPID(const SymbolicName: string): Integer;
begin
  Result := ExtractNum(SymbolicName, 'pid_');
end;

function ExtractSerialNumber(SymbolicName: string): string;
var
  N: Integer;
begin
  N := Pos('#', SymbolicName);
  if N >= 0 then
  begin
    SymbolicName := Copy(SymbolicName, N + 1, Length(SymbolicName));
    N := Pos('#', SymbolicName);
    if N >= 0 then
    begin
      SymbolicName := Copy(SymbolicName, N + 1, Length(SymbolicName));
      N := Pos('#', SymbolicName);
      if N >= 0 then
        Result := Copy(SymbolicName, 1, N - 1)
      else
        Result := '';
    end;
  end
  else
    Result := '';
end;

// find the "bus" DeviceID for a given mount point

function GetDriveInstanceID(MountPointName: string; var DeviceInst: DEVINST): Boolean;
const
  GUID_DEVINTERFACE_VOLUME: TGUID = '{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}';
var
  StorageGUID: TGUID;
  PnPHandle: HDEVINFO;
  DevData: TSPDevInfoData;
  DeviceInterfaceData: TSPDeviceInterfaceData;
  FunctionClassDeviceData: PSPDeviceInterfaceDetailData;
  Success: LongBool;
  Devn: Integer;
  BytesReturned: DWORD;
  Inst: DEVINST;
  S, FileName, MountName, DevID: string;
begin
  Result := False;
  DeviceInst := 0;
  // enumerate all volumes
  StorageGUID := GUID_DEVINTERFACE_VOLUME;
  PnPHandle := SetupDiGetClassDevs(@StorageGUID, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
  if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then
    Exit;
  Devn := 0;
  repeat
    DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
    Success := SetupDiEnumDeviceInterfaces(PnPHandle, nil, StorageGUID, Devn, DeviceInterfaceData);
    if Success then
    begin
      DevData.cbSize := SizeOf(DevData);
      BytesReturned := 0;
      SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, nil, 0, BytesReturned, @DevData);
      if (BytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
      begin
        FunctionClassDeviceData := AllocMem(BytesReturned);
        try
          FunctionClassDeviceData.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
          if SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData,
            FunctionClassDeviceData, BytesReturned, BytesReturned, @DevData) then
          begin
            FileName := PTSTR(@FunctionClassDeviceData.DevicePath[0]);
            // get the grandparent DevNode which is the "bus" device
            // like "USB". This is the DevNode for CM_Request_Device_Eject and
            // several other useful operations
            Inst := DevData.DevInst;
            CM_Get_Parent(Inst, Inst, 0);
            CM_Get_Parent(Inst, Inst, 0);
            DevID := GetDeviceID(Inst);

            // no need in this example to check for USB only
            // if ExtractBus(DevID) = 'USB' then
            begin
              S := '\';
              S := PTSTR(@FunctionClassDeviceData.DevicePath) + S;
              MountName := GetVolumeNameForVolumeMountPointString(S);
              if MountName = MountPointName then
              begin
                DeviceInst := Inst;
                Result := True;
                Exit;
              end;
            end;
          end;
        finally
          FreeMem(FunctionClassDeviceData);
        end;
      end;
    end;
    Inc(Devn);
  until not Success;
  SetupDiDestroyDeviceInfoList(PnPHandle);
end;

//============================================================================

procedure TaForm.FormCreate(Sender: TObject);
var i : Integer;
begin
  // never forget to load the dynamically linked APIs
  LoadSetupApi;
  LoadConfigManagerApi;
  DriveMountPoints := TStringList.Create;
  DriveMountPoints.Sorted := True;
  // initialize drive list
  if ParamCount > 0 then begin
    for i := 1 to ParamCount do begin
      if (Pos('DRIVEREMOVE:', UpperCase(ParamStr(i))) > 0)
      and (ParamStr(i)[length(ParamStr(i))] in ['A'..'Z','a'..'z']) then begin
        if not SaveRemoveDrive(ParamStr(i)[length(ParamStr(i))])
          then begin end;
      end;
    end;
  end;
  PostMessage(Handle, WM_CLOSE, 0, 0);
end;

procedure TaForm.FormDestroy(Sender: TObject);
begin
  DriveMountPoints.Free;
  UnloadConfigManagerApi;
  UnloadSetupApi;
end;
Da dies nicht unter Vista funktioniert, bzw. einfach gar nichts tut hab ich recherchiert und nur bei MS gefunden das unter Vista man die Privilegien haben muss, also hab ichs noch damit versucht den CM_Request_Device_Eject wie folgt abzuändern:

Delphi-Quellcode:
        SetPrivilege('SeUndockPrivilege', true);
        CM_Request_Device_Eject(DEVINST(DriveList.Items.Objects[idx]), @VetoType, nil, 0, CM_REMOVE_NO_RESTART);
        SetPrivilege('SeUndockPrivilege', False);
Die SetPrivilege Funktion hier:
Delphi-Quellcode:
function SetPrivilege(privilegeName: string; enable: boolean): boolean;
var
  tpPrev,
  tp        : TTokenPrivileges;
  token     : THandle;
  dwRetLen  : DWord;
begin
  result := False;
  OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, token);
  tp.PrivilegeCount := 1;
  if LookupPrivilegeValue(nil, pchar(privilegeName), tp.Privileges[0].LUID) then begin
    if enable then
      tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
    else
      tp.Privileges[0].Attributes := 0;
    dwRetLen := 0;
    result := AdjustTokenPrivileges(token, False, tp, SizeOf(tpPrev), tpPrev, dwRetLen);
  end;
  CloseHandle(token);
end;
Funktioniert nicht, wer kann hier auf die Sprünge helfen nachdem ich alle gefundenen Quellen schon nach Lösungen durchforstet habe.

Pichel 18. Sep 2008 13:47

Re: USB Stick sicher entfernen unter Vista
 
Wie denn, keiner hier irgendeine Idee :(

Pichel 3. Okt 2008 09:43

Re: USB Stick sicher entfernen unter Vista
 
Schade, ich denke kann dann diese Frage als unbeantwortet schließen :-(

Dust Signs 3. Okt 2008 10:02

Re: USB Stick sicher entfernen unter Vista
 
Hallo!

Kann es evtl. damit zusammenhängen, dass du unter Vista für einige Operationen höhere Rechte benötigst? Funktioniert denn dein Code, wenn du das Programm als Administrator startest?

Dust Signs

Zacherl 3. Okt 2008 11:46

Re: USB Stick sicher entfernen unter Vista
 
Soweit ich weiß, ist es unter Vista nicht mehr notwendig dieses "sichere Entfernen" zu verwenden.

mkinzler 3. Okt 2008 11:50

Re: USB Stick sicher entfernen unter Vista
 
Zitat:

Zitat von Zacherl
Soweit ich weiß, ist es unter Vista nicht mehr notwendig dieses "sichere Entfernen" zu verwenden.

Warum sollte das nicht mehr nötig sein?

MagicAndre1981 3. Okt 2008 11:57

Re: USB Stick sicher entfernen unter Vista
 
das ist nur dann nicht nötig wenn man den Cache deaktiviert. Als Standard ist der Cache deaktiviert und dann kann man den Stick einfach abziehen sonst nicht. Unter *nix muss man den Stick auch erst unmounten, sonst sind die Daten noch im Cache und nicht auf dem Stick

Pichel 5. Okt 2008 17:23

Re: USB Stick sicher entfernen unter Vista
 
Zitat:

Zitat von Dust Signs
Kann es evtl. damit zusammenhängen, dass du unter Vista für einige Operationen höhere Rechte benötigst? Funktioniert denn dein Code, wenn du das Programm als Administrator startest?

Leider liegt es nicht daran da ich sowas immer als Admin teste ;-) Laut MS sind ja weiter Privilegien nötig (wie oben geschildert) welche ich in allem möglichen Kombinationen getestet habe - ohne Erfolg, der Dialog aus dem Systemtray poppt einfach nicht auf.

Bin schon fast soweit mich direkt an dem TrayIcon und dem dahinter liegendem Popupmenü zum sicheren entfernen zu vergreifen :-D

Zitat:

Zitat von Zacherl
Soweit ich weiß, ist es unter Vista nicht mehr notwendig dieses "sichere Entfernen" zu verwenden.

Aber das TrayIcon erscheint immer noch unter Vista, also wird das wohl irgend enen Sinn haben, und möchte mir ja nur den Umweg über das TrayIcon sparen.

Uwe Sieber 21. Jan 2009 15:57

Re: USB Stick sicher entfernen unter Vista
 
Sieht so aus, dass Du ein Parent/Child-Beziehung zwischen Volume und Disk voraussetzt.
Das war schon unter XP nur für Wechseldatenträger und CDROMs ok, unter Vista nur noch
für CDROMs.

Hier habe ich in C++ gezeigt, wie es geht:
http://www.codeproject.com/KB/system...eByLetter.aspx


Gruß Uwe

worker.db 5. Nov 2009 14:26

Re: USB Stick sicher entfernen unter Vista
 
Hallöchen.

Was unter XP tadellos funktionierte das streikt nun unter Vista.
Ich möchte aus meinem Programm heraus auch unter Vista einen Usb-Stick sicher entfernen.

Hierzu habe ich auch alle Laufwerke in einer Listbox inkl. Instanz als Object basieren auf irgendnem Code den ich mal irgendwo gefunden habe.
Bem.nicht schön aber funktionier Wink

TurboMartin 5. Nov 2009 14:49

Re: USB Stick sicher entfernen unter Vista
 
Probier mal Folgendes:

Delphi-Quellcode:
function OpenVolume(ADrive: char): THandle;
var
  RootName, VolumeName: string;
  AccessFlags: DWORD;
  DriveType:Cardinal;
begin
  RootName := ADrive + ':\';
  DriveType := GetDriveType(PChar(RootName));
  case DriveType of
    DRIVE_REMOVABLE:
      AccessFlags := GENERIC_READ or GENERIC_WRITE;
    DRIVE_CDROM:
      AccessFlags := GENERIC_READ;
//    DRIVE_FIXED:
//      AccessFlags := GENERIC_READ or GENERIC_WRITE;
  else
    Result := INVALID_HANDLE_VALUE;
    exit;
  end;
  VolumeName := Format('\\.\%s:', [ADrive]);
  Result := CreateFile(PChar(VolumeName), AccessFlags,
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if Result = INVALID_HANDLE_VALUE then
    RaiseLastWin32Error;
end;
 
function LockVolume(AVolumeHandle: THandle): boolean;
const
  LOCK_TIMEOUT = 10 * 1000; // 10 Seconds
  LOCK_RETRIES = 20;
  LOCK_SLEEP = LOCK_TIMEOUT div LOCK_RETRIES;
  FSCTL_LOCK_VOLUME = (9 shl 16) or (0 shl 14) or (6 shl 2) or 0;
var
  Retries: integer;
  BytesReturned: Cardinal;
begin
  for Retries := 1 to LOCK_RETRIES do begin
    Result := DeviceIoControl(AVolumeHandle, FSCTL_LOCK_VOLUME, nil, 0,
      nil, 0, BytesReturned, nil);
    if Result then
      break;
    Sleep(LOCK_SLEEP);
  end;
end;
 
function DismountVolume(AVolumeHandle: THandle): boolean;
const
  FSCTL_DISMOUNT_VOLUME = (9 shl 16) or (0 shl 14) or (8 shl 2) or 0;
var
  BytesReturned: Cardinal;
begin
  Result := DeviceIoControl(AVolumeHandle, FSCTL_DISMOUNT_VOLUME, nil, 0,
    nil, 0, BytesReturned, nil);
  if not Result then
    RaiseLastWin32Error;
end;
 
function PreventRemovalOfVolume(AVolumeHandle: THandle;
  APreventRemoval: boolean): boolean;
const
  IOCTL_STORAGE_MEDIA_REMOVAL = ($2d shl 16) or (1 shl 14) or ($201 shl 2) or 0;
type
  TPreventMediaRemoval = record
    PreventMediaRemoval: BOOL;
  end;
var
  BytesReturned: Cardinal;
  PMRBuffer: TPreventMediaRemoval;
begin
  PMRBuffer.PreventMediaRemoval := APreventRemoval;
  Result := DeviceIoControl(AVolumeHandle, IOCTL_STORAGE_MEDIA_REMOVAL,
    @PMRBuffer, SizeOf(TPreventMediaRemoval), nil, 0, BytesReturned, nil);
  if not Result then
    RaiseLastWin32Error;
end;
 
function AutoEjectVolume(AVolumeHandle: THandle): boolean;
const
  IOCTL_STORAGE_EJECT_MEDIA = ($2d shl 16) or (1 shl 14) or ($202 shl 2) or 0;
var
  BytesReturned: Cardinal;
begin
  Result := DeviceIoControl(AVolumeHandle, IOCTL_STORAGE_EJECT_MEDIA, nil, 0,
    nil, 0, BytesReturned, nil);
  if not Result then
    RaiseLastWin32Error;
end;
 
function EjectVolume(ADrive: char): boolean;
var
  VolumeHandle: THandle;
begin
  Result := FALSE;
  // Open the volume
  VolumeHandle := OpenVolume(ADrive);
  if VolumeHandle = INVALID_HANDLE_VALUE then
    exit;
  try
    // Lock and dismount the volume
    if LockVolume(VolumeHandle) and DismountVolume(VolumeHandle) then begin
      // Set prevent removal to false and eject the volume
      if PreventRemovalOfVolume(VolumeHandle, FALSE) then
        AutoEjectVolume(VolumeHandle);
    end;
  finally
    // Close the volume so other processes can use the dr
     CloseHandle(VolumeHandle);
  end;
end;
Aufruf mit z.B. EjectVolume('J');

worker.db 6. Nov 2009 11:59

Re: USB Stick sicher entfernen unter Vista
 
Vielen Dank für Ihre Antworten.

worker.db 6. Nov 2009 13:53

Re: USB Stick sicher entfernen unter Vista
 
Wie bekomme ich 'angeschlossenen USB-Info "(Geräte-Instanz-ID, Fahrerairbag, Schlüsselnamen ..) aus der Registry in Vista oder Windows 7 mit Delphi?
Wo ist diese Information in Windows Registry?
Ich habe einen Code es funktioniert unter XP, aber nicht in Vista.
Warum wird der Code funktioniert nicht auf Vista?
Ich bin Stack wirklich darüber. Bitte helfen Sie.

Alter Mann 6. Nov 2009 14:33

Re: USB Stick sicher entfernen unter Vista
 
Hallo TurboMartin,

unter Win 7 und als Admin geht es, unter Vista nicht.

Keine Meldung, nix.

Gruß

Pichel 6. Nov 2009 20:33

Re: USB Stick sicher entfernen unter Vista
 
Zitat:

Zitat von TurboMartin
Probier mal Folgendes:
Delphi-Quellcode:
function DismountVolume(AVolumeHandle: THandle): boolean;
const
  FSCTL_DISMOUNT_VOLUME = (9 shl 16) or (0 shl 14) or (8 shl 2) or 0;
var
  BytesReturned: Cardinal;
begin
  Result := DeviceIoControl(AVolumeHandle, FSCTL_DISMOUNT_VOLUME, nil, 0,
    nil, 0, BytesReturned, nil);

Habs jetzt mal getestet und es kracht genau bei obigem Aufruf in der "kernel32".

worker.db 8. Nov 2009 16:51

Re: USB Stick sicher entfernen unter Vista
 
Für Vista und Win7:
GetDriveInstanceID () except alles korrekt funktioniert.
So GetDriveInstanceID(DriveMountPoints[I], Inst) returned from 'Inst' ist falsch.

Ergebnisse für den gleichen USB:

----------------XP-----------Win7
-----------------------------------
Inst--------2788----------3908
Key---------1924-----------0

Was hat sich in Vista Registry geändert?

Uwe Sieber 8. Nov 2009 23:00

Re: USB Stick sicher entfernen unter Vista
 
Zitat:

Zitat von worker.db
Für Vista und Win7:
GetDriveInstanceID () except alles korrekt funktioniert.
So GetDriveInstanceID(DriveMountPoints[I], Inst) returned from 'Inst' ist falsch.

Ergebnisse für den gleichen USB:

----------------XP-----------Win7
-----------------------------------
Inst--------2788----------3908
Key---------1924-----------0

Was hat sich in Vista Registry geändert?


Nochmal: Die Funktion setzt eine Parent/Child-Beziehung zwischen Volume und Disk voraus.
Das war schon unter XP nur für Wechseldatenträger und CDROMs ok, aber nicht für Festplatten.
Ab Vista ist das nur noch für CDROMs und USB-Floppys der Fall, sonst ist der Volume-Manager im Spiel.

Hier habe ich in C++ gezeigt, wie es geht:
http://www.codeproject.com/KB/system...eByLetter.aspx


Gruß Uwe

Bomberbb 13. Nov 2009 12:30

Re: USB Stick sicher entfernen unter Vista
 
Ich hänge mich hier mal an mit meinem Problem:

Ich nutze folgenden Quelltext:
Delphi-Quellcode:
Function EjectVolume(ADrive: char): Boolean;

    Function OpenVolume(ADrive: char): THandle;
    Var
      RootName, VolumeName        : String;
      AccessFlags                 : DWORD;
      DriveType                   : Cardinal;
    Begin
      RootName := ADrive + ':\';
      DriveType := GetDriveType(pChar(RootName));
      Case DriveType Of
        DRIVE_REMOVABLE:
          AccessFlags := GENERIC_READ Or GENERIC_WRITE;
        DRIVE_CDROM:
          AccessFlags := GENERIC_READ;
        //    DRIVE_FIXED:
        //      AccessFlags := GENERIC_READ or GENERIC_WRITE;
      Else
        Result := INVALID_HANDLE_VALUE;
        Exit;
      End;
      VolumeName := Format('\\.\%s:', [ADrive]);
      Result := CreateFile(pChar(VolumeName), AccessFlags,
        FILE_SHARE_READ Or FILE_SHARE_WRITE, Nil, OPEN_EXISTING, 0, 0);
      If Result = INVALID_HANDLE_VALUE Then
        RaiseLastWin32Error;
    End;

    Function LockVolume(AVolumeHandle: THandle): Boolean;
    Const
      LOCK_TIMEOUT                = 10 * 1000; // 10 Seconds
      LOCK_RETRIES                = 20;
      LOCK_SLEEP                  = LOCK_TIMEOUT Div LOCK_RETRIES;
      FSCTL_LOCK_VOLUME           = (9 Shl 16) Or (0 Shl 14) Or (6 Shl 2) Or 0;
    Var
      Retries                     : Integer;
      BytesReturned               : Cardinal;
    Begin
      For Retries := 1 To LOCK_RETRIES Do
      Begin
        Result := DeviceIoControl(AVolumeHandle, FSCTL_LOCK_VOLUME, Nil, 0,
          Nil, 0, BytesReturned, Nil);
        If Result Then
          Break;
        Sleep(LOCK_SLEEP);
      End;
    End;

    Function DismountVolume(AVolumeHandle: THandle): Boolean;
    Const
      FSCTL_DISMOUNT_VOLUME       = (9 Shl 16) Or (0 Shl 14) Or (8 Shl 2) Or 0;
    Var
      BytesReturned               : Cardinal;
    Begin
      Result := DeviceIoControl(AVolumeHandle, FSCTL_DISMOUNT_VOLUME, Nil, 0,
        Nil, 0, BytesReturned, Nil);
      If Not Result Then
        RaiseLastWin32Error;
    End;

    Function PreventRemovalOfVolume(AVolumeHandle: THandle;
      APreventRemoval: Boolean): Boolean;
    Const
      IOCTL_STORAGE_MEDIA_REMOVAL = ($2D Shl 16) Or (1 Shl 14) Or ($201 Shl 2) Or 0;
    Type
      TPreventMediaRemoval = Record
        PreventMediaRemoval: BOOL;
      End;
    Var
      BytesReturned               : Cardinal;
      PMRBuffer                   : TPreventMediaRemoval;
    Begin
      PMRBuffer.PreventMediaRemoval := APreventRemoval;
      Result := DeviceIoControl(AVolumeHandle, IOCTL_STORAGE_MEDIA_REMOVAL,
        @PMRBuffer, SizeOf(TPreventMediaRemoval), Nil, 0, BytesReturned, Nil);
      If Not Result Then
        RaiseLastWin32Error;
    End;

    Function AutoEjectVolume(AVolumeHandle: THandle): Boolean;
    Const
      IOCTL_STORAGE_EJECT_MEDIA   = ($2D Shl 16) Or (1 Shl 14) Or ($202 Shl 2) Or 0;
    Var
      BytesReturned               : Cardinal;
    Begin
      Result := DeviceIoControl(AVolumeHandle, IOCTL_STORAGE_EJECT_MEDIA, Nil, 0,
        Nil, 0, BytesReturned, Nil);
      If Not Result Then
        RaiseLastWin32Error;
    End;

  Var
    VolumeHandle                  : THandle;
    str                           : String;
  Begin
    Result := False;

    // Open the volume
    VolumeHandle := OpenVolume(ADrive);
    If VolumeHandle = INVALID_HANDLE_VALUE Then
      Exit;
    Try
      // Lock and dismount the volume
      If LockVolume(VolumeHandle) And DismountVolume(VolumeHandle) Then
      Begin
        // Set prevent removal to false and eject the volume
        If PreventRemovalOfVolume(VolumeHandle, False) Then
          Result := AutoEjectVolume(VolumeHandle);
      End;
    Finally
      // Close the volume so other processes can use the dr
      CloseHandle(VolumeHandle);
    End;
    If Result Then
    Begin
      str := ADrive + '\';
      ShChangeNotify(
        SHCNE_MEDIAREMOVED,
        SHCNF_PATH,
        pChar(str),
        Nil);
    End;
  End;
Der USB-Stick wird ausgehängt, aber irgendwie scheint ShChangeNotify zu scheitern. Das Symbol in der Taskleiste bleibt erhalten, ebendso wird im Explorer noch das Laufwerk angezeigt. Wie sag ich Windows nun, dass ich den Stick ausgehängt habe?

Gruß

Sascha

Uwe Sieber 13. Nov 2009 12:47

Re: USB Stick sicher entfernen unter Vista
 
Zitat:

Zitat von Bomberbb
Der USB-Stick wird ausgehängt, aber irgendwie scheint ShChangeNotify zu scheitern. Das Symbol in der Taskleiste bleibt erhalten, ebendso wird im Explorer noch das Laufwerk angezeigt. Wie sag ich Windows nun, dass ich den Stick ausgehängt habe?

IOCTL_STORAGE_EJECT_MEDIA meldet nicht das Laufwerk ab, es wirft das Medium aus - daher der Name.
Bei einem CD-Laufwerk ginge Schublade auf, mehr nicht. Bei einem Kartenleser könnte man die
Karte entnehmen und wieder einlegen -> geht wieder. Einem USB-Stick kann man nach dem Auswerfen
beruhigt abziehen, Wiederbeleben geht nur durch erneutes Anschließen.


Gruß Uwe

p80286 13. Nov 2009 15:31

Re: USB Stick sicher entfernen unter Vista
 
Zitat:

Zitat von Uwe Sieber

IOCTL_STORAGE_EJECT_MEDIA meldet nicht das Laufwerk ab, es wirft das Medium aus - daher der Name.
Bei einem CD-Laufwerk ginge Schublade auf, mehr nicht. Bei einem Kartenleser könnte man die
Karte entnehmen und wieder einlegen -> geht wieder. Einem USB-Stick kann man nach dem Auswerfen
beruhigt abziehen, Wiederbeleben geht nur durch erneutes Anschließen.

Was ist mit "Anschließen" gemeint?

Ein IOCTL_STORAGE_LOAD_MEDIA nach IOCTL_STORAGE_EJECT_MEDIA scheint Windows (Unter Vista auf jeden Fall! unter XP geht's nicht!) ein erneutes Einstecken des Sticks vorzugaukeln. Ob das Entfernen "sicher" ist kann ich nicht beurteilen.

Gruß
K-H

Uwe Sieber 13. Nov 2009 19:02

Re: USB Stick sicher entfernen unter Vista
 
Zitat:

Zitat von p80286

Was ist mit "Anschließen" gemeint?

Das Gegenteil von Abziehen.

Zitat:

Zitat von p80286
Ein IOCTL_STORAGE_LOAD_MEDIA nach IOCTL_STORAGE_EJECT_MEDIA scheint Windows (Unter Vista auf jeden Fall! unter XP geht's nicht!) ein erneutes Einstecken des Sticks vorzugaukeln. Ob das Entfernen "sicher" ist kann ich nicht beurteilen.

Dass IOCTL_STORAGE_LOAD_MEDIA das Medium eines Wechseldatenträgers
unter Vista zurück bringt wußte ich nicht - interessant.

Sicher ist das Entfernen wenn - wie im Quelltext gezeigt -
vor dem EJECT ein erfolgreiches LOCK und DISMOUNT erfolgten.
Sonst ist es nicht besser als den Datenträger einfach zu
entnehmen oder den Stick abzuziehen.


Gruß Uwe

Bomberbb 16. Nov 2009 07:48

Re: USB Stick sicher entfernen unter Vista
 
Danke für die Antworten!

Bomberbb 26. Nov 2009 09:00

Re: USB Stick sicher entfernen unter Vista
 
Für alle, die mal das gleiche Problem haben. Ich hab noch ne Möglichkeit gefunden, das Laufwerk aus dem Explorer zu entfernen.
Nachdem der Stick korrekt ausgeworfen wurde kann man folgendes aufrufen:
Delphi-Quellcode:
DefineDosDevice(DDD_REMOVE_DEFINITION Or DDD_RAW_TARGET_PATH, pChar(String(ADrive + ':')), Nil);
oder
Delphi-Quellcode:
DefineDosDevice(DDD_EXACT_MATCH_ON_REMOVE Or DDD_REMOVE_DEFINITION Or DDD_RAW_TARGET_PATH, pChar(String(ADrive + ':')), pchar(DriveName);
Allerdings braucht man Adminrechte!

Gruß

BBB

MuTzE.Y85 24. Apr 2014 01:31

AW: USB Stick sicher entfernen unter Vista
 
Genau das gleiche Problem hatte ich auch :)
Ich frage mich nur gerade, ob das auch irgendwie ohne Adminrechte geht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz