的问题是,当我禁用缓存(设置FILE_FLAG_NO_BUFFERING在CreateFile)我必须通过读取和写入功能的字节数是512(扇区大小)的倍数.我使用10MB的缓冲区,一切都没问题......直到最后一次缓冲操作.最后一个缓冲区没有512个字节的倍数.那么,我如何读写这个文件的最后部分呢?
这是我到目前为止写的......
procedure MarusCopyFileNoCache(SrcName,DestName:string);
const BufSize = 10485760; {10MB}
var Src,Dest:THandle;
Buffer:Pointer;
Bufs,x:integer;
AllSize:int64;
N,junk:DWord;
begin
Src:=CreateFileW(PWideChar('\\?\'+SrcName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
Dest:=CreateFileW(PWideChar('\\?\'+DestName), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, 0);
try
AllSize:=MFileSize(Src); {this is my function to get the int64 file size}
Bufs:=Ceil(AllSize/BufSize);
GetMem(Buffer,BufSize);
try
for x:=1 to Bufs do begin
ReadFile(Src, Buffer^, BufSize, N, nil);
WriteFile(Dest, Buffer^, N, junk, nil);
end;
finally
FreeMem(Buffer,BufSize);
end;
finally
CloseHandle(Src);
CloseHandle(Dest);
end;
end;
Run Code Online (Sandbox Code Playgroud)
使用时FILE_FLAG_NO_BUFFERING,您必须读取和写入完整扇区,但最后一个缓冲区将是部分扇区(ReadFile()将报告读取的字节数少于完整扇区大小).当您编写最后一个缓冲区时,仍然必须将其写为完整扇区,否则WriteFile()将失败.显然输出文件的大小可能太大了.但是,由于您确实知道所需的文件大小,因此可以SetFileInformationByHandle()在完成写入后以及关闭其句柄之前设置输出文件的最终大小.
例如:
type
FILE_INFO_BY_HANDLE_CLASS = (
FileBasicInfo = 0,
FileStandardInfo = 1,
FileNameInfo = 2,
FileRenameInfo = 3,
FileDispositionInfo = 4,
FileAllocationInfo = 5,
FileEndOfFileInfo = 6,
FileStreamInfo = 7,
FileCompressionInfo = 8,
FileAttributeTagInfo = 9,
FileIdBothDirectoryInfo = 10, // 0xA
FileIdBothDirectoryRestartInfo = 11, // 0xB
FileIoPriorityHintInfo = 12, // 0xC
FileRemoteProtocolInfo = 13, // 0xD
FileFullDirectoryInfo = 14, // 0xE
FileFullDirectoryRestartInfo = 15, // 0xF
FileStorageInfo = 16, // 0x10
FileAlignmentInfo = 17, // 0x11
FileIdInfo = 18, // 0x12
FileIdExtdDirectoryInfo = 19, // 0x13
FileIdExtdDirectoryRestartInfo = 20, // 0x14
MaximumFileInfoByHandlesClass);
FILE_END_OF_FILE_INFO = record
EndOfFile: LARGE_INTEGER;
end;
function SetFileInformationByHandle(
hFile: THandle;
FileInformationClass: FILE_INFO_BY_HANDLE_CLASS;
lpFileInformation: Pointer;
dwBufferSize: DWORD
): BOOL; stdcall; external 'kernel32.dll' delayed;
Run Code Online (Sandbox Code Playgroud)
procedure MarcusCopyFileNoCache(SrcName, DestName: string);
const
BufSize = 10485760; {10MB}
var
Src, Dest: THandle;
Buffer: PByte;
FinalSize: Int64;
SectorSize, N, ignored: DWORD;
eof: FILE_END_OF_FILE_INFO;
begin
Src := CreateFile(PChar('\\?\'+SrcName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
if Src = INVALID_HANDLE_VALUE then RaiseLastOSError;
try
Dest := CreateFile(PChar('\\?\'+DestName), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, 0);
if Dest = INVALID_HANDLE_VALUE then RaiseLastOSError;
try
try
FinalSize := 0;
SectorSize := 512; // <-- TODO: determine this dynamically at runtime
GetMem(Buffer, BufSize);
try
repeat
if not ReadFile(Src, Buffer^, BufSize, N, nil) then RaiseLastOSError;
if N = 0 then Break; // EOF reached
Inc(FinalSize, N);
// round up the number of bytes read to the next sector boundary for writing
N := (N + (SectorSize-1)) and (not (SectorSize-1));
if not WriteFile(Dest, Buffer^, N, ignored, nil) then RaiseLastOSError;
until False;
finally
FreeMem(Buffer, BufSize);
end;
// now set the final file size
eof.EndOfFile.QuadPart := FinalSize;
if not SetFileInformationByHandle(Dest, FileEndOfFileInfo, @eof, SizeOf(eof)) then RaiseLastOSError;
finally
CloseHandle(Dest);
end;
except
DeleteFile(PChar(DestName));
raise;
end;
finally
CloseHandle(Src);
end;
end;
Run Code Online (Sandbox Code Playgroud)