Wednesday, June 16, 2004

I wrote a small copy file routine that support overlaped reading and writing, it suppose to has better performance.

procedure RSICopyFile(ASourceFileName, ADestFileName: string; ABuffSize: integer; AOverLaped: boolean);
const
MaxBufSize = 1024 * 1024 * 50; // 50mb
var
FReadhandle: THandle;
FWritehandle: THandle;
FBuffer: Pointer;
BufSize, FBytesInBuff, FFilePos, FBytesRead, FBytesWrite: Integer;
FFileSize: Integer;
FRead_os, FWrite_os: TOverlapped;
ErrorFlag: Dword;

begin
//open the source file
if not AOverLaped then
FReadhandle := CreateFile(PChar(ASourceFileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0)
else
begin
FReadhandle := CreateFile(PChar(ASourceFileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);

FillChar(FRead_os, SizeOf(FRead_os), 0);
FRead_os.hEvent := CreateEvent(nil, True, False, nil);
FRead_os.Offset := 0;
FRead_os.OffsetHigh := 0;
end;

if FReadhandle = 0 then
begin
raise exception.create('Cannot open the readding file');
end;

// get the size of source file
FFileSize := GetFileSize(FReadhandle, 0);

//open the dest file, create anew one if not exist
if not AOverLaped then
FWritehandle := CreateFile(PChar(ADestFileName), GENERIC_READ or GENERIC_WRITE,
0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
else
begin
FWritehandle := CreateFile(PChar(ADestFileName), GENERIC_READ or GENERIC_WRITE,
0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);

FillChar(FWrite_os, SizeOf(FWrite_os), 0);
FWrite_os.hEvent := CreateEvent(nil, True, False, nil);
FWrite_os.Offset := 0;
FWrite_os.OffsetHigh := 0;
end;

if FWritehandle = 0 then
begin
raise exception.create('Cannot open the writting file ');
end;

//Set the buffer size
if ABuffSize > MaxBufSize then
BufSize := MaxBufSize
else
BufSize := Integer(ABuffSize);

FBuffer := VirtualAlloc(0, BufSize, Mem_ReServe or Mem_Commit, PAGE_READWRITE);
try
if FFileSize > 0 then
begin
if FFileSize > MaxInt then
FFilePos := MaxInt
else
FFilePos := FFileSize;
while FFilePos > 0 do
begin
if FFilePos > BufSize then FBytesInBuff := BufSize else FBytesInBuff := FFilePos;
//clean up before read
FillChar(FRead_os, SizeOf(FRead_os), 0);
//Read file to buff
if not AOverLaped then
begin
if not ReadFile(FReadhandle, FBuffer^, FBytesInBuff, LongWord(FBytesRead), nil) then
begin
raise exception.create('file cannot be read');
end;
end
else
begin
if not ReadFile(FReadhandle, FBuffer^, FBytesInBuff, LongWord(FBytesRead), @FRead_os) then
begin
ErrorFlag := GetLastError;

if ErrorFlag <> 0 then
begin
if ErrorFlag = ERROR_IO_PENDING then
begin
if WaitForSingleObject(FRead_os.hEvent, INFINITE) = WAIT_OBJECT_0 then
if not GetOverlappedResult(FReadhandle, FRead_os,
LongWord(FBytesRead), False) then
begin
ErrorFlag := GetLastError;
if ErrorFlag = ERROR_HANDLE_EOF then
begin
exit; // The end of the stream was hit.
end;
end;

end
else if ErrorFlag = ERROR_HANDLE_EOF then
begin
exit; // The end of the stream was hit.
end
else
begin
raise exception.create('file cannot be read');
end;
end;
end;
inc(FRead_os.Offset, FBytesRead);
end;


//Write buff to file
if not AOverLaped then
begin
if FBytesRead = 0 then
begin
exit; // The end of the stream was hit.
end;
if not WriteFile(FWritehandle, FBuffer^, FBytesInBuff, LongWord(FBytesWrite), nil) then
begin
raise exception.create('file cannot be written');
end;
end
else
begin

if not WriteFile(FWritehandle, FBuffer^, FBytesInBuff, LongWord(FBytesWrite), @FWrite_os) then
begin
ErrorFlag := GetLastError;
if ErrorFlag <> 0 then
begin
if ErrorFlag = ERROR_IO_PENDING then
begin
if WaitForSingleObject(FWrite_os.hEvent, INFINITE) = WAIT_OBJECT_0 then
if not GetOverlappedResult(FWritehandle, FWrite_os,
LongWord(FBytesWrite), False) then
begin
ErrorFlag := GetLastError;
if ErrorFlag = ERROR_HANDLE_EOF then
begin
exit; // The end of the stream was hit.
end;
end;
end
else if ErrorFlag = ERROR_HANDLE_EOF then
begin
exit; // The end of the stream was hit.
end
else
begin
raise exception.create('file cannot be writeen');
end;
end;
end;
Inc(FWrite_os.Offset, FBytesWrite);
end;

//Change the position
Dec(FFilePos, FBytesRead);
end;
end;
finally
VirtualFree(FBuffer, BufSize, Mem_DeCommit or Mem_Release);
CloseHandle(FReadhandle);
CloseHandle(FWritehandle);
end;

end;
lib kernel32.dll reference for Windows Api programming in C# , VB.NET, and VB6. Examples of P/Invoke and DllImport with Source code
overlapped