我读了物理驱动器,遇到了一些问题。

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;


如何在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;

最佳答案

根据documentation,您需要确保读取的内存是扇区对齐的。


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


分配两个值得访问的扇区,然后在其中进行到扇区边界。

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




从评论来看,似乎有些混乱。让我看看是否可以再说清楚一点。直接磁盘访问的两个方面需要对齐。


磁盘指针和块大小必须对齐。
内存缓冲区必须对齐。


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

关于delphi - 流在64位应用程序上的读/写错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40077569/

10-11 23:00
查看更多