Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Извлечь раздробленный HDD


Автор: Artem148 19.5.2011, 20:09
Привет!

Проблема: Код для извлечения флешки(представлен ниже) не корректно работает с внешним HDD(если HDD раздроблен)
Вопрос:     В чём косячок?

Заранее спасибо!

Код

const
  GUID_DEVINTERFACE_DISK: TGUID=(
    D1: $53F56307; D2: $B6BF; D3: $11D0; D4: ($94, $F2, $00, $A0, $C9, $1E, $FB, $8B));

type
  TStorageDeviceNumber=packed record
    DeviceType: DWORD;
    DeviceNumber: DWORD;
    PartitionNumber: DWORD;
  end;

type
  PSPDevInfoData=^SP_DEVINFO_DATA; //TSPDevInfoData;
  SP_DEVINFO_DATA=packed record
    cbSize: DWORD;
    ClassGuid: TGUID;
    DevInst: DWORD; // DEVINST handle
    Reserved: ULONG_PTR;
  end;

  PSPDeviceInterfaceData=^TSPDeviceInterfaceData;
  SP_DEVICE_INTERFACE_DATA=packed record
    cbSize: DWORD;
    InterfaceClassGuid: TGUID;
    Flags: DWORD;
    Reserved: ULONG_PTR;
  end;
  TSPDeviceInterfaceData=SP_DEVICE_INTERFACE_DATA;

  PSPDeviceInterfaceDetailDataA=^TSPDeviceInterfaceDetailDataA;
  PSPDeviceInterfaceDetailData=PSPDeviceInterfaceDetailDataA;
  SP_DEVICE_INTERFACE_DETAIL_DATA_A=packed record
    cbSize: DWORD;
    DevicePath: array[0..1024-1] of AnsiChar;
  end;
  TSPDeviceInterfaceDetailDataA=SP_DEVICE_INTERFACE_DETAIL_DATA_A;
  TSPDeviceInterfaceDetailData=TSPDeviceInterfaceDetailDataA;

function SetupDiGetClassDevsA(ClassGuid: PGUID; const Enumerator: PAnsiChar;
  hwndParent: HWND; Flags: DWORD): THandle; stdcall; external'SetupApi.dll';

function SetupDiDestroyDeviceInfoList(
  DeviceInfoSet: THandle): LongBool; stdcall; external'SetupApi.dll';

function SetupDiEnumDeviceInterfaces(DeviceInfoSet: THandle;
  DeviceInfoData: PSPDevInfoData; const InterfaceClassGuid: TGUID;
  MemberIndex: DWORD; var DeviceInterfaceData: TSPDeviceInterfaceData):
  LongBool; stdcall; external'SetupApi.dll';

function SetupDiGetDeviceInterfaceDetailA(DeviceInfoSet: THandle;
  DeviceInterfaceData: PSPDeviceInterfaceData;
  DeviceInterfaceDetailData: PSPDeviceInterfaceDetailDataA;
  DeviceInterfaceDetailDataSize: DWORD; var RequiredSize: DWORD;
  Device: PSPDevInfoData): LongBool; stdcall; external'SetupApi.dll';

function CM_Get_Parent(var dnDevInstParent: DWORD;
  dnDevInst: DWORD; ulFlags: ULONG): DWORD; stdcall;
  external'cfgmgr32.dll';

function CM_Request_Device_EjectA(dnDevInst: DWORD;
  pVetoType: DWORD; pszVetoName: PWideChar;
  ulNameLength: ULONG; ulFlags: ULONG): DWORD; stdcall;
  external'SetupApi.dll';

function GetTempDir: string;
var
  Buffer: array[0..1023] of Char;
begin
  SetString(Result, Buffer, GetTempPath(SizeOf(Buffer), Buffer));
end;

function NormalDir(const DirName: string): string;
begin
  Result := DirName;
  if (Result <> '') and
    not (AnsiLastChar(Result)^ in ['\']) then
    Result := Result + '\';
end;

function RemoveFlash(const Value: PChar): Boolean;
var
  hFile, hDevInfo, hDrive, hDevInstance, hDevInstanceParent: THandle;
  sdn: TStorageDeviceNumber;
  dwDeviceNumber, dwBytesReturned, dwSize: DWORD;
  FlashGuid: TGUID;
  I: Integer;
  DeviceInfoData: SP_DEVINFO_DATA;
  DeviceInterfaceData: TSPDeviceInterfaceData;
  DeviceInterfaceDetailData: TSPDeviceInterfaceDetailData;
  nTryLockCount: Integer;
begin
  Result := False;

  hDevInstance := INVALID_HANDLE_VALUE;

  hFile := CreateFile(PChar('\\.\' + Value), 0,
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

  if hFile=INVALID_HANDLE_VALUE then
  begin
    Exit;
  end;
  try
    if not DeviceIoControl(hFile,
      ($0000002D shl 16)or
      ($00000000 shl 14)or($0420 shl 2)or $00000000, nil, 0, @sdn,
      SizeOf(TStorageDeviceNumber), dwBytesReturned, nil) then
    begin
      Exit;
    end;
    dwDeviceNumber := sdn.DeviceNumber;
    FlashGuid := GUID_DEVINTERFACE_DISK;
    hDevInfo := SetupDiGetClassDevsA(@FlashGuid, nil, 0,
      $00000002 or $00000010);
    if hDevInfo=INVALID_HANDLE_VALUE then
    begin
      Exit;
    end;
    try
      I := 0;
      DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
      while SetupDiEnumDeviceInterfaces(
        hDevInfo, nil, FlashGuid, I, DeviceInterfaceData) do
      begin

        Inc(I);
        SetupDiGetDeviceInterfaceDetailA(hDevInfo, @DeviceInterfaceData, nil, 0, dwSize, nil);
        if dwSize=0 then
        begin
          Exit;
        end;

        DeviceInfoData.cbSize := SizeOf(SP_DEVINFO_DATA);
        DeviceInterfaceDetailData.cbSize := 5;
        if not SetupDiGetDeviceInterfaceDetailA(hDevInfo, @DeviceInterfaceData,
          @DeviceInterfaceDetailData, dwSize, dwSize, @DeviceInfoData) then
        begin
          Exit;
        end;
        hDrive := CreateFile(PChar(@DeviceInterfaceDetailData.DevicePath[0]),
          0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
        if hFile=INVALID_HANDLE_VALUE then
        begin
          Exit;
        end;
        try
          if not DeviceIoControl(hDrive,
            ($0000002D shl 16)or($00000000 shl 14)or($0420 shl 2)or $00000000, nil, 0, @sdn,
            SizeOf(TStorageDeviceNumber), dwBytesReturned, nil) then
          begin
            Exit;
          end;
          if sdn.DeviceNumber=dwDeviceNumber then
          begin
            hDevInstance := DeviceInfoData.DevInst;
            Break;
          end;

        finally
          CloseHandle(hDrive);
        end;
      end;
    finally
      SetupDiDestroyDeviceInfoList(hDevInfo);
    end;
  finally
    CloseHandle(hFile);
  end;
  if hDevInstance <> INVALID_HANDLE_VALUE then
  begin
    if CM_Get_Parent(hDevInstanceParent, hDevInstance, 0) <> 0 then
    begin
      Exit;
    end;

    nTryLockCount := 0;
    while nTryLockCount < 5 do
    begin
      if CM_Request_Device_EjectA(
        hDevInstanceParent, Cardinal(nil), nil, 0, 0) = 0 then
        Break;
      Inc(nTryLockCount);
    end;
    if nTryLockCount = 5 then
    begin
      Exit;
    end
    else
      Result := True;
  end;
end;


P.S Относительно кода: Я знаю, что представленый вариант не самый лучший, НО... - но он работает (и только с флешкой) !

Автор: MetalFan 20.5.2011, 14:06
а что значит "некорректно работает"? ошибки выдает или как? отладка не помогает?

Код конечно "на высоте"...
Шедевр прям таки:
Цитата(Artem148 @  19.5.2011,  20:09 Найти цитируемый пост)
($0000002D shl 16)or
      ($00000000 shl 14)or($0420 shl 2)or $00000000



Автор: Artem148 20.5.2011, 18:02
Вылазиет сообщение: "Диск используется, ещё... пытайтесь позже" - хотя диск совершено пуст и ни одна программа его не использует

Автор: EndErr 21.5.2011, 09:38
Попрбуй Unlocker-ом посмотреть кто использует диск в этот момент.
У меня случалось что SYSTEM использовал $MFT в момент отключения.

Автор: Artem148 22.5.2011, 21:08
to EndErr: ни одна программа его не использует - ошибочка, поспешил сумничать  smile 

P.S: 1) это был explorer(при том ни одно обращение к диску), и запушен он был от системы.
       2) Не сочтите за наглость(  smile  ): Как этот код привести к компактному и цивильному виду?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)