I have ported this code to Delphi/Pascal with some much needed enhancements - much appreciated so returned respect was in order. Thanks Fyyre for sharing
Code: Select allunit uRawCopyFile;
{***************************************************************}
{ (uRawCopyFile.pas) }
{ }
{ Raw Copy File From Disk }
{ Win2K / WinXP / Win2K3 / Win2k8 / Vista / Win7 - }
{ }
{ ---> Full Credit to MS-REM <--- }
{ }
{ +++ Added Unicode function }
{ +++ Added Copying files with progress/percentage }
{ +++ Virtual memory handled by OS not RTL memory mgr }
{ }
{ By: Brock Williams - Torseq Technologies }
{ }
{***************************************************************}
interface
uses
Windows;
type
LPCOMPLETION_ROUTINE = ^COMPLETION_ROUTINE;
COMPLETION_ROUTINE = Procedure(dwProgress: ULONG); stdcall;
function CopyFileA(lpSrcName: LPCSTR;
lpDstName: LPCSTR;
ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
function CopyFileW(lpSrcName: LPCWSTR;
lpDstName: LPCWSTR;
ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
implementation
{$O+} // optimization enabled
{$Q-} // disable overflow checking
{$R-} // disable range checking
{$D-} // no debug info
{$L-} // no local symbol info
{$T-} // type-checked pointers off - @ references are generic
const
FILE_READ_ATTRIBUTES = $00000080;
FILE_DEVICE_FILE_SYSTEM = $00000009;
FILE_ANY_ACCESS = $00000000;
METHOD_NEITHER = $00000003;
FSCTL_GET_RETRIEVAL_POINTERS = ((FILE_DEVICE_FILE_SYSTEM shl 16) or
(FILE_ANY_ACCESS shl 14) or
(28 shl 2) or METHOD_NEITHER);
type
RPBExtends = record
NextVcn: LARGE_INTEGER;
Lcn: LARGE_INTEGER;
end;
type
STARTING_VCN_INPUT_BUFFER = record
StartingVcn: LARGE_INTEGER;
end;
type
RETRIEVAL_POINTERS_BUFFER = record
ExtentCount: ULONG;
StartingVcn: LARGE_INTEGER;
Extends: Array [0..0] of RPBExtends;
end;
type
PCLUSTER = ^Cluster;
CLUSTER = Array [0..0] of LONGLONG;
function GetFileClusters(lpFileName: LPCWSTR; ClusterSize: ULONG;
ClCount: PULONG; FileSize: PULONG): PCLUSTER;
var
hFile: ULONG;
OutSize: ULONG;
Bytes, Cls, CnCount, r: ULONG;
PrevVCN, Lcn: LARGE_INTEGER;
InBuf: STARTING_VCN_INPUT_BUFFER;
OutBuf: ^RETRIEVAL_POINTERS_BUFFER;
begin
Result := nil;
hFile := CreateFileW(lpFileName, FILE_READ_ATTRIBUTES,
(FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE), nil, OPEN_EXISTING, 0, 0);
if (hFile <> INVALID_HANDLE_VALUE) then
begin
FileSize^ := GetFileSize(hFile, nil);
OutSize := sizeof(RETRIEVAL_POINTERS_BUFFER) + (FileSize^ div ClusterSize) * sizeof(OutBuf^.Extends);
OutBuf := VirtualAlloc(nil, OutSize, MEM_COMMIT, PAGE_READWRITE);
InBuf.StartingVcn.QuadPart := 0;
if DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, @InBuf,
sizeof(InBuf), OutBuf, OutSize, Bytes, nil) then
begin
ClCount^ := ((FileSize^ + (ClusterSize - 1)) div ClusterSize);
Result := VirtualAlloc(nil, (ClCount^ * sizeof(Result^)), MEM_COMMIT, PAGE_READWRITE);
PrevVCN := OutBuf^.StartingVcn;
Cls := 0;
if (OutBuf^.ExtentCount > 0) then
for r := 0 to OutBuf^.ExtentCount -1 do
begin
Lcn := OutBuf^.Extends[r].Lcn;
CnCount := (OutBuf^.Extends[r].NextVcn.QuadPart - PrevVCN.QuadPart);
while (cnCount > 0) do
begin
Result^[Cls] := Lcn.QuadPart;
Inc(Lcn.QuadPart);
Inc(Cls);
Dec(CnCount);
end;
PrevVCN := OutBuf^.Extends[r].NextVcn;
end;
end;
VirtualFree(OutBuf, 0, MEM_RELEASE);
CloseHandle(hFile);
end;
end;
function _CopyFile(lpSrcName: LPCWSTR; lpDstName: LPCWSTR;
PfnCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var
ClusterSize, BlockSize: ULONG;
Clusters: PCLUSTER;
ClCount, FileSize, Bytes: ULONG;
hDrive, hFile: ULONG;
SecPerCl, BtPerSec, r: ULONG;
Buf: Pointer;
Offset: LARGE_INTEGER;
cDrv: Array [0..6] of WCHAR;
BytesWritten, iFileSize: ULONG;
PrevProgress, Progress: ULONG;
begin
cDrv[0] := lpSrcName[0];
cDrv[1] := ':';
cDrv[2] := #0;
GetDiskFreeSpaceW(cDrv, SecPerCl, BtPerSec, ULONG(nil^), ULONG(nil^));
ClusterSize := (SecPerCl * BtPerSec);
Clusters := GetFileClusters(lpSrcName, ClusterSize, @ClCount, @FileSize);
iFileSize := FileSize;
BytesWritten := 0;
if (Clusters <> nil) then
begin
cDrv[0] := '\';
cDrv[1] := '\';
cDrv[2] := '.';
cDrv[3] := '\';
cDrv[4] := lpSrcName[0];
cDrv[5] := ':';
cDrv[6] := #0;
hDrive := CreateFileW(cDrv, GENERIC_READ, (FILE_SHARE_READ or FILE_SHARE_WRITE),
nil, OPEN_EXISTING, 0, 0);
if (hDrive <> INVALID_HANDLE_VALUE) then
begin
hFile := CreateFileW(lpDstName, GENERIC_WRITE, 0, nil, CREATE_NEW, 0, 0);
if (hFile <> INVALID_HANDLE_VALUE) then
begin
Buf := VirtualAlloc(nil, ClusterSize, MEM_COMMIT, PAGE_READWRITE);
PrevProgress := 0;
if (ClCount > 0) then
for r := 0 to ClCount -1 do
begin
Offset.QuadPart := (ClusterSize * Clusters^[r]);
SetFilePointer(hDrive, Offset.LowPart, @Offset.HighPart, FILE_BEGIN);
ReadFile(hDrive, Buf^, ClusterSize, Bytes, nil);
if (FileSize < ClusterSize) then
BlockSize := FileSize
else
BlockSize := ClusterSize;
if WriteFile(hFile, Buf^, BlockSize, Bytes, nil) then
Inc(BytesWritten, Bytes);
Dec(FileSize, BlockSize);
Progress := Round((BytesWritten / iFileSize) * 100);
if (PrevProgress <> Progress) then
begin
Inc(PrevProgress);
if (PfnCallback <> nil) then
PfnCallback^(Progress);
end;
end;
VirtualFree(Buf, 0, MEM_RELEASE);
CloseHandle(hFile);
end;
CloseHandle(hDrive);
end;
VirtualFree(Clusters, 0, MEM_RELEASE);
end;
result := (BytesWritten > 0) and (iFileSize = BytesWritten);
end;
function CopyFileA(lpSrcName: LPCSTR; lpDstName: LPCSTR;
ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var lpCallback: LPCOMPLETION_ROUTINE;
begin
if ProgressCallback = nil then
lpCallback := nil
else
lpCallback := @ProgressCallback;
result := _CopyFile(LPCWSTR(WideString(lpSrcName)),
LPCWSTR(WideString(lpDstName)), lpCallback);
end;
function CopyFileW(lpSrcName: LPCWSTR; lpDstName: LPCWSTR;
ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var lpCallback: LPCOMPLETION_ROUTINE;
begin
if ProgressCallback = nil then
lpCallback := nil
else
lpCallback := @ProgressCallback;
result := _CopyFile(lpSrcName, lpDstName, lpCallback);
end;
end.