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

时间:2016-10-17 02:14:01

标签: 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;

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

1 个答案:

答案 0 :(得分:5)

根据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

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

  1. 磁盘指针和块大小必须是扇区对齐的。
  2. 内存缓冲区必须是扇区对齐的。
  3. 我指的是其中的第二个。假设磁盘扇区大小为512,则满足第一个要求。但是您未能满足第二个要求。

相关问题