12

delphiを使用してプログラムでUSBフラッシュディスクを検出して削除するにはどうすればよいですか?

私はこのウェブサイトでいくつかの例を見ましたが、それらはそれについてどうやって行くかについての明確な説明を欠いています!

例が本当に役立ちます!

4

3 に答える 3

4

USB ドライブを取り外すための鍵は、CM_Request_Device_Eject関数を使用することです。

この記事に基づいてHow to Prepare a USB Drive for Safe Removalおり、JEDI API Library & Security Code Library

{$APPTYPE CONSOLE}

{$R *.res}

uses
  JwaWinIoctl,
  Cfg,
  CfgMgr32,
  SetupApi,
  Windows,
  SysUtils;


function GetDrivesDevInstByDeviceNumber(DeviceNumber : LONG; DriveType : UINT; szDosDeviceName: PChar) : DEVINST;
var
 StorageGUID : TGUID;
 IsFloppy : Boolean;
 hDevInfo : SetupApi.HDEVINFO;
 dwIndex  : DWORD;
 res      : BOOL;
 pspdidd  : PSPDeviceInterfaceDetailData;
 spdid    : SP_DEVICE_INTERFACE_DATA;
 spdd     : SP_DEVINFO_DATA;
 dwSize   : DWORD;
 hDrive   : THandle;
 sdn      : STORAGE_DEVICE_NUMBER;
 dwBytesReturned : DWORD;
begin
  Result:=0;
    IsFloppy := pos('\\Floppy', szDosDeviceName)>0; // who knows a better way?
    case DriveType of
    DRIVE_REMOVABLE:
                if ( IsFloppy ) then
                  StorageGUID := GUID_DEVINTERFACE_FLOPPY
                else
                  StorageGUID := GUID_DEVINTERFACE_DISK;

    DRIVE_FIXED:  StorageGUID := GUID_DEVINTERFACE_DISK;
    DRIVE_CDROM:    StorageGUID := GUID_DEVINTERFACE_CDROM;
    else
        exit
  end;

    // Get device interface info set handle for all devices attached to system
    hDevInfo := SetupDiGetClassDevs(@StorageGUID, nil, 0, DIGCF_PRESENT OR DIGCF_DEVICEINTERFACE);
  if (NativeUInt(hDevInfo) <> INVALID_HANDLE_VALUE) then
  try
    // Retrieve a context structure for a device interface of a device information set
    dwIndex := 0;
    //PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
    spdid.cbSize := SizeOf(spdid);

    while true do
    begin
      res := SetupDiEnumDeviceInterfaces(hDevInfo, nil, StorageGUID, dwIndex, spdid);
      if not res then
        break;

      dwSize := 0;
      SetupDiGetDeviceInterfaceDetail(hDevInfo, @spdid, nil, 0, dwSize, nil); // check the buffer size

      if ( dwSize<>0) then
      begin
       pspdidd := AllocMem(dwSize);
       try
        pspdidd.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
        ZeroMemory(@spdd, sizeof(spdd));
        spdd.cbSize := SizeOf(spdd);
        res := SetupDiGetDeviceInterfaceDetail(hDevInfo, @spdid, pspdidd, dwSize, dwSize, @spdd);
        if res then
        begin
          // open the disk or cdrom or floppy
          hDrive := CreateFile(pspdidd.DevicePath, 0, FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
         if ( hDrive <> INVALID_HANDLE_VALUE ) then
          try
              // get its device number
              dwBytesReturned := 0;
              res := DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, nil, 0, @sdn, sizeof(sdn), dwBytesReturned, nil);
              if res  then
              begin
                if ( DeviceNumber = sdn.DeviceNumber) then
                begin  // match the given device number with the one of the current device
                  Result:= spdd.DevInst;
                  exit;
                end;
              end;
          finally
            CloseHandle(hDrive);
          end;
        end;
       finally
         FreeMem(pspdidd);
       end;
      end;
      Inc(dwIndex);
    end;
  finally
   SetupDiDestroyDeviceInfoList(hDevInfo);
  end;
end;

procedure EjectUSB(const DriveLetter:char);
var
  szRootPath, szDevicePath : PChar;
  szVolumeAccessPath : PChar;
  hVolume : THandle;
  DeviceNumber : LONG;
  sdn  : STORAGE_DEVICE_NUMBER;
  dwBytesReturned : DWORD;
  res : BOOL;
  resCM : Cardinal;
  DriveType : UINT;
  szDosDeviceName : array [0..MAX_PATH-1] of Char;
  DevInst  : CfgMgr32.DEVINST;
  VetoType : PNP_VETO_TYPE;
  VetoName : array [0..MAX_PATH-1] of WCHAR;
  bSuccess : Boolean;
  DevInstParent : CfgMgr32.DEVINST;
  tries :  Integer;
begin
    szRootPath := PChar(DriveLetter+':\');
    szDevicePath := PChar(DriveLetter+':');
  szVolumeAccessPath := PChar(Format('\\.\%s:',[DriveLetter]));

  DeviceNumber:=-1;
    // open the storage volume
  hVolume := CreateFile(szVolumeAccessPath, 0, FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
    if (hVolume <> INVALID_HANDLE_VALUE) then
   try
    //get the volume's device number
    dwBytesReturned := 0;
    res := DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, nil, 0, @sdn, SizeOf(sdn), dwBytesReturned, nil);
    if res then
      DeviceNumber := sdn.DeviceNumber;
   finally
     CloseHandle(hVolume);
   end;
   if DeviceNumber=-1 then exit;

    // get the drive type which is required to match the device numbers correctely
    DriveType := GetDriveType(szRootPath);

    // get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
    QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH);

    // get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
    DevInst := GetDrivesDevInstByDeviceNumber(DeviceNumber, DriveType, szDosDeviceName);

    if ( DevInst = 0 ) then
   exit;

  VetoType := PNP_VetoTypeUnknown;
    bSuccess := false;

    // get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
    DevInstParent := 0;
    resCM := CM_Get_Parent(DevInstParent, DevInst, 0);

    for tries:=0 to 3 do // sometimes we need some tries...
  begin
        FillChar(VetoName[0], SizeOf(VetoName), 0);

        // CM_Query_And_Remove_SubTree doesn't work for restricted users
        //resCM = CM_Query_And_Remove_SubTree(DevInstParent, &VetoType, VetoNameW, MAX_PATH, CM_REMOVE_NO_RESTART); // CM_Query_And_Remove_SubTreeA is not implemented under W2K!
        //resCM = CM_Query_And_Remove_SubTree(DevInstParent, NULL, NULL, 0, CM_REMOVE_NO_RESTART);  // with messagebox (W2K, Vista) or balloon (XP)

        resCM := CM_Request_Device_Eject(DevInstParent, @VetoType, @VetoName[0], Length(VetoName), 0);
        resCM := CM_Request_Device_Eject(DevInstParent,nil, nil, 0, 0); // optional -> shows messagebox (W2K, Vista) or balloon (XP)

        bSuccess := (resCM=CR_SUCCESS) and (VetoType=PNP_VetoTypeUnknown);
        if ( bSuccess )  then
            break;

        Sleep(500); // required to give the next tries a chance!
    end;

    if ( bSuccess ) then
        Writeln('Success')
  else
      Writeln('Failed');
end;

begin
  try
    LoadSetupApi;
    LoadConfigManagerApi;
    EjectUSB('F');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;

end.
于 2012-06-14T16:34:29.383 に答える
2

これはドライブを排出しませんが、ドライブのバッファをフラッシュし、安全に取り外せるようにします。Vista 以降 (権限が制限されたユーザー (IIRC) として実行している場合は XP) での管理者権限が必要です。が呼び出されることを確認するために、おそらく try..finally を実行する必要がありCloseHandleます。ここでは水平スクロールなしでコードのフォーマットがきついので、これは読者の演習として残します。:-)

unit USBDriveFlush;

interface

  uses Windows;

type
  // Taken from JEDI JwaWinIoctl
  PSTORAGE_HOTPLUG_INFO = ^STORAGE_HOTPLUG_INFO;
  {$EXTERNALSYM PSTORAGE_HOTPLUG_INFO}
  _STORAGE_HOTPLUG_INFO = record
    Size: DWORD; // version
    MediaRemovable: BOOLEAN; // ie. zip, jaz, cdrom, mo, etc. vs hdd
    MediaHotplug: BOOLEAN;   // ie. does the device succeed a lock 
                             // even though its not lockable media?
    DeviceHotplug: BOOLEAN;  // ie. 1394, USB, etc.
    WriteCacheEnableOverride: BOOLEAN; // This field should not be 
                             // relied upon because it is no longer used
  end;
  {$EXTERNALSYM _STORAGE_HOTPLUG_INFO}
  STORAGE_HOTPLUG_INFO = _STORAGE_HOTPLUG_INFO;
  {$EXTERNALSYM STORAGE_HOTPLUG_INFO}
  TStorageHotplugInfo = STORAGE_HOTPLUG_INFO;
  PStorageHotplugInfo = PSTORAGE_HOTPLUG_INFO;    

  function FlushUSBDrive(const Drive: string): Boolean;

implementation

function FlushUSBDrive(const Drive: string): Boolean;
var
  shpi : TStorageHotplugInfo;
  retlen : DWORD; //unneeded, but deviceiocontrol expects it
  h : THandle;
begin
  Result := False;
  h := CreateFile(PChar('\\.\' + Drive),
                  0,
                  FILE_SHARE_READ or FILE_SHARE_WRITE,
                  nil,
                  OPEN_EXISTING,
                  0,
                  0);
  if h <> INVALID_HANDLE_VALUE then
  begin
    shpi.Size := SizeOf(shpi);

    if DeviceIoControl(h,
                       IOCTL_STORAGE_GET_HOTPLUG_INFO,
                       nil,
                       0,
                       @shpi,
                       SizeOf(shpi),
                       retlen,
                       nil) then
    begin
      //shpi now has the existing values, so you can check to
      //see if the device is already hot-pluggable
      if not shpi.DeviceHotplug then
      begin
        shpi.DeviceHotplug:= True;

        //Need to use correct administrator security privilages here
        //otherwise it'll just give 'access is denied' error
        Result := DeviceIoControl(h,
                                  IOCTL_STORAGE_SET_HOTPLUG_INFO,
                                   @shpi,
                                   SizeOf(shpi),
                                   nil,
                                   0,
                                   retlen,
                                   nil);
      end;
    end;
    CloseHandle(h);
  end;
end;

サンプル使用:

if FlushUSBDrive('G:') then
  ShowMessage('Safe to remove USB drive G:')
else
  ShowMessage('Flush of drive G: failed!' +
    SysErrorMessage(GetLastError()));
于 2012-06-14T02:00:12.757 に答える