在64位应用程序上流读/写错误

Who*_*ami 2 delphi winapi

我读了我的物理驱动器并遇到了一些问题.

64位应用程序:

  • 512字节:Stream read error/ Stream write error.
    • 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;
Run Code Online (Sandbox Code Playgroud)

如何在64位应用程序上读取512个字节?

更新: 我在另一台PC上运行此应用程序,它工作正常.我不懂为什么.

更新2: 感谢David Heffernan.下面的代码工作.但是为什么对于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;
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 5

根据文档,您需要确保您读入的内存是扇区对齐的.

用于读取和写入操作的文件访问缓冲区地址应该是物理扇区对齐的,这意味着在内存中的地址上对齐,该地址是卷的物理扇区大小的整数倍.根据磁盘,可能不会强制执行此要求.

分配两个值得记忆的扇区,然后在其中推进到扇区边界.

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

在此之后,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
Run Code Online (Sandbox Code Playgroud)

从评论来看,似乎存在一些混乱.让我看看我是否可以多说一点.需要对齐直接磁盘访问的两个方面.

  1. 磁盘指针和块大小必须是扇区对齐的.
  2. 内存缓冲区必须是扇区对齐的.

我指的是其中的第二个.假设磁盘扇区大小为512,则满足第一个要求.但是您未能满足第二个要求.

  • *根据磁盘的不同,可能不会强制执行此要求.* (2认同)