Ошибка чтения / записи потока в 64-битном приложении

I Stream читает ошибку моего физического диска, и у меня возникают некоторые проблемы.

64-битные приложения:

  • 512 байт: Stream write error/ Read.
    • Read: Работа
    • ReadBuffer: не работа
  • 38400 байт: работа.

32-разрядные приложения: работайте во всех случаях.

function setinact(diskid: string): boolean;
var
  hdevi:    THandleStream;
  hDevice:  THandle;
  mfile:    TMemoryStream;
  hbuff, mbuff:    array[0..511] of byte;
  i:        integer;
  BytesReturned: DWORD;
begin
  Result:=False;
  hDevice := CreateFile(Pchar('\.PHYSICALDRIVE'+diskid), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if hDevice <> INVALID_HANDLE_VALUE then
  begin
    try
      hdevi := THandleStream.Create(hDevice);
      try
        mfile:=TMemoryStream.Create();
        try
          hdevi.ReadBuffer(hbuff[0],length(hbuff));
          mfile.WriteBuffer(hbuff[0],Length(hbuff));
          mfile.Position:=0;
          mfile.ReadBuffer(mbuff[0],length(mbuff));
          mbuff[446]:=$00;
          mbuff[462]:=$00;
          mbuff[478]:=$00;
          mbuff[494]:=$00;
          hdevi.Position:=0;
          hdevi.WriteBuffer(mbuff[0],length(mbuff));
          Result:=True;
        finally
          mfile.Free;
        end;
      finally
        hdevi.Free;
        DeviceIoControl(hDevice, IOCTL_DISK_UPDATE_PROPERTIES, nil, 0, nil, 0, BytesReturned, nil);
      end;
    finally
      CloseHandle(hDevice);
    end;
  end;
end;

Как читать 512 байт в 64-битных приложениях?

Обновлено: я запустил это приложение на другом ПК, он сработал. Я не понимаю почему.

Обновлено 2: Спасибо Дэвиду Хеффернану. Ниже приведен код. Но почему для 32-битных приложений всегда получается первый код?

function setinact(diskid: string): boolean;
var
  hDevice:  THandle;
  hbuff:    PByte;
  i:        integer;
  hexstr: String;
  DISK_GEOMETRY : _DISK_GEOMETRY;
  BytesPerSector: Int64;
  BytesReturned: DWORD;
begin
  Result:=False;
  hDevice := CreateFile(Pchar('\.PHYSICALDRIVE'+diskid), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if hDevice = INVALID_HANDLE_VALUE then Exit;
  try
    GetMem(hbuff, 512);
    try
      if not DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY, Nil, 0, @DISK_GEOMETRY, sizeof(DISK_GEOMETRY), BytesReturned, nil) then Exit;
      BytesPerSector:=DISK_GEOMETRY.BytesPerSector;
      if not ReadFile(hDevice, hbuff^, 512, BytesReturned, nil) then Exit;

      .................

      SetFilePointer(hDevice, 0, nil, FILE_BEGIN);
      if not WriteFile(hDevice, hbuff^, 512, BytesReturned, nil) then Exit;
    finally
      FreeMem(hbuff, 512);
    end;
    Result:=True;
    DeviceIoControl(hDevice, IOCTL_DISK_UPDATE_PROPERTIES, nil, 0, nil, 0, BytesReturned, nil);
  finally
    CloseHandle(hDevice);
  end;
end;

delphi,winapi,

2

Ответов: 1


5 принят

В соответствии с документацией вам необходимо убедиться, что память, в которую вы читаете, выравнивается по секторам.

Адреса буфера доступа к файлам для операций чтения и записи должны быть выровнены по физическому сектору, что означает выравнивание по адресам в памяти, которые являются целыми кратными размеру физического сектора тома. В зависимости от диска это требование может не выполняться.

Выделите два сектора памяти и затем продвигайтесь в пределах этого сектора на границу сектора.

var
  buff: array [0..2*512-1] of Byte;
  ptr: Pointer;
.... 
ptr := Pointer((NativeInt(@buff) + 512) and not (512-1));

После этого ptrуказывает на выровненное местоположение в вашем избыточном буфере. Выполните прямой доступ к диску с памятью, начиная с этого выровненного местоположения.

В последнем предложении выдержки объясняется, что это требование не может быть применено, поэтому ваш код может работать на некоторых машинах, но не на других.

Или, действительно, вам может быть повезло с вашими 32-битными сборками, которые, как им кажется, дают вам адрес с выровненной секцией. Ваше предполагаемое исправление в вопросе редактирования не помогает, поскольку GetMemне имеет гарантии выравнивания по 512 байт. Если при вызове GetMemпроизойдет возврат адреса, выравниваемого по 512 байт, это просто шанс. Вы не можете полагаться на это.

        S                             S 
  B     |                             |
  +---------------------------------------------------------+
  |     ******************************                      |
  +---------------------------------------------------------+
        P                             |

S: sector boundary (multiple of 512) 
B: buffer (2*512 bytes in size!), not aligned to sector boundary
P: Ptr = buffer + offset to make ptr aligned to a sector boundary.
*: part of buffer you use (512 bytes), aligned to sector boundary

Судя по комментариям, возникает некоторая путаница. Позвольте мне посмотреть, могу ли я сказать это немного больше. Два аспекта прямого доступа к диску должны быть выровнены.

  1. Указатель диска и размер блока должны быть выровнены по секторам.
  2. Буфер памяти должен быть выровнен по секторам.

Я имею в виду второй из них. Вы отвечаете первому требованию, если размер сектора диска равен 512. Но вы не отвечаете второму требованию.

Дельфы, WinAPI,
Похожие вопросы