Прежде всего, я извиняюсь за то, что поднял эту старую ветку, но я внес некоторые существенные изменения в великий ответ, сделанный Дейви Лэндманом для моих собственных нужд. Изменения:
- Добавлена возможность использовать относительные пути (конечно, абсолютная и UNC-пути поддерживаются)
- Добавлена возможность обратного вызова, чтобы показать ход копирования на экране (продолжить чтение) или отменить процесс копирования
- Основной код был немного очищен. Я думаю, что поддержка Unicode была сохранена, но я действительно не знаю, так как я использую последнюю версию ANSI-компилятора Delphi (если кто-нибудь может это проверить?)
Чтобы использовать этот код, создайте файл FastCopy.pas в своем проекте, а затем скопируйте и вставьте содержимое:
{
FastCopyFile
By SiZiOUS 2014, based on the work by Davy Landman
www.sizious.com - @sizious - fb.com/sizious - sizious (at) gmail (dot) com
This unit was designed to copy a file using the Windows API.
It's faster than using the (old) BlockRead/Write and TFileStream methods.
Every destination file will be overwritten (by choice), unless you specify
the fcfmAppend CopyMode flag. In that case, the source file will be appened to
the destination file (instead of overwriting it).
You have the choice to use a normal procedure callback, method object callback
or no callback at all. The callback is used to cancel the copy process and to
display the copy progress on-screen.
Developed and tested under Delphi 2007 (ANSI).
If you are using a Unicode version of Delphi (greater than Delphi 2007), may
be you need to do some adapations (beware of the WideString type).
All credits flying to Davy Landman.
/377528/delphi-bystroe-kopirovanie-failov
}
unit FastCopy;
interface
uses
Windows, SysUtils;
type
TFastCopyFileMode = (fcfmCreate, fcfmAppend);
TFastCopyFileNormalCallback = procedure(const FileName: TFileName;
const CurrentSize, TotalSize: LongWord; var CanContinue: Boolean);
TFastCopyFileMethodCallback = procedure(const FileName: TFileName;
const CurrentSize, TotalSize: LongWord; var CanContinue: Boolean) of object;
// Simplest definition
function FastCopyFile(
const ASourceFileName, ADestinationFileName: TFileName): Boolean; overload;
// Definition with CopyMode and without any callbacks
function FastCopyFile(
const ASourceFileName, ADestinationFileName: TFileName;
CopyMode: TFastCopyFileMode): Boolean; overload;
// Definition with normal procedure callback
function FastCopyFile(
const ASourceFileName, ADestinationFileName: TFileName;
CopyMode: TFastCopyFileMode;
Callback: TFastCopyFileNormalCallback): Boolean; overload;
// Definition with object method callback
function FastCopyFile(
const ASourceFileName, ADestinationFileName: TFileName;
CopyMode: TFastCopyFileMode;
Callback: TFastCopyFileMethodCallback): Boolean; overload;
implementation
{ Dummy Callback: Method Version }
type
TDummyCallBackClient = class(TObject)
private
procedure DummyCallback(const FileName: TFileName;
const CurrentSize, TotalSize: LongWord; var CanContinue: Boolean);
end;
procedure TDummyCallBackClient.DummyCallback(const FileName: TFileName;
const CurrentSize, TotalSize: LongWord; var CanContinue: Boolean);
begin
// Nothing
CanContinue := True;
end;
{ Dummy Callback: Classical Procedure Version }
procedure DummyCallback(const FileName: TFileName;
const CurrentSize, TotalSize: LongWord; var CanContinue: Boolean);
begin
// Nothing
CanContinue := True;
end;
{ CreateFileW API abstract layer }
function OpenLongFileName(ALongFileName: string; DesiredAccess, ShareMode,
CreationDisposition: LongWord): THandle;
var
IsUNC: Boolean;
FileName: PWideChar;
begin
// Translate relative paths to absolute ones
ALongFileName := ExpandFileName(ALongFileName);
// Check if already an UNC path
IsUNC := Copy(ALongFileName, 1, 2) = '\\';
if not IsUNC then
ALongFileName := '\\?\' + ALongFileName;
// Preparing the FileName for the CreateFileW API call
FileName := PWideChar(WideString(ALongFileName));
// Calling the API
Result := CreateFileW(FileName, DesiredAccess, ShareMode, nil,
CreationDisposition, FILE_ATTRIBUTE_NORMAL, 0);
end;
{ FastCopyFile implementation }
function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName;
CopyMode: TFastCopyFileMode;
Callback: TFastCopyFileNormalCallback;
Callback2: TFastCopyFileMethodCallback): Boolean; overload;
const
BUFFER_SIZE = 524288; // 512KB blocks, change this to tune your speed
var
Buffer: array of Byte;
ASourceFile, ADestinationFile: THandle;
FileSize, BytesRead, BytesWritten, BytesWritten2, TotalBytesWritten,
CreationDisposition: LongWord;
CanContinue, CanContinueFlag: Boolean;
begin
FileSize := 0;
TotalBytesWritten := 0;
CanContinue := True;
SetLength(Buffer, BUFFER_SIZE);
// Manage the Creation Disposition flag
CreationDisposition := CREATE_ALWAYS;
if CopyMode = fcfmAppend then
CreationDisposition := OPEN_ALWAYS;
// Opening the source file in read mode
ASourceFile := OpenLongFileName(ASourceFileName, GENERIC_READ, 0, OPEN_EXISTING);
if ASourceFile <> 0 then
try
FileSize := FileSeek(ASourceFile, 0, FILE_END);
FileSeek(ASourceFile, 0, FILE_BEGIN);
// Opening the destination file in write mode (in create/append state)
ADestinationFile := OpenLongFileName(ADestinationFileName, GENERIC_WRITE,
FILE_SHARE_READ, CreationDisposition);
if ADestinationFile <> 0 then
try
// If append mode, jump to the file end
if CopyMode = fcfmAppend then
FileSeek(ADestinationFile, 0, FILE_END);
// For each blocks in the source file
while CanContinue and (LongWord(FileSeek(ASourceFile, 0, FILE_CURRENT)) < FileSize) do
begin
// Reading from source
if (ReadFile(ASourceFile, Buffer[0], BUFFER_SIZE, BytesRead, nil)) and (BytesRead <> 0) then
begin
// Writing to destination
WriteFile(ADestinationFile, Buffer[0], BytesRead, BytesWritten, nil);
// Read/Write secure code block (e.g. for WiFi connections)
if BytesWritten < BytesRead then
begin
WriteFile(ADestinationFile, Buffer[BytesWritten], BytesRead - BytesWritten, BytesWritten2, nil);
Inc(BytesWritten, BytesWritten2);
if BytesWritten < BytesRead then
RaiseLastOSError;
end;
// Notifying the caller for the current state
Inc(TotalBytesWritten, BytesWritten);
CanContinueFlag := True;
if Assigned(Callback) then
Callback(ASourceFileName, TotalBytesWritten, FileSize, CanContinueFlag);
CanContinue := CanContinue and CanContinueFlag;
if Assigned(Callback2) then
Callback2(ASourceFileName, TotalBytesWritten, FileSize, CanContinueFlag);
CanContinue := CanContinue and CanContinueFlag;
end;
end;
finally
CloseHandle(ADestinationFile);
end;
finally
CloseHandle(ASourceFile);
end;
// Check if cancelled or not
if not CanContinue then
if FileExists(ADestinationFileName) then
DeleteFile(ADestinationFileName);
// Results (checking CanContinue flag isn't needed)
Result := (FileSize <> 0) and (FileSize = TotalBytesWritten);
end;
{ FastCopyFile simple definition }
function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName): Boolean; overload;
begin
Result := FastCopyFile(ASourceFileName, ADestinationFileName, fcfmCreate);
end;
{ FastCopyFile definition without any callbacks }
function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName;
CopyMode: TFastCopyFileMode): Boolean; overload;
begin
Result := FastCopyFile(ASourceFileName, ADestinationFileName, CopyMode,
DummyCallback);
end;
{ FastCopyFile definition with normal procedure callback }
function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName;
CopyMode: TFastCopyFileMode;
Callback: TFastCopyFileNormalCallback): Boolean; overload;
var
DummyObj: TDummyCallBackClient;
begin
DummyObj := TDummyCallBackClient.Create;
try
Result := FastCopyFile(ASourceFileName, ADestinationFileName, CopyMode,
Callback, DummyObj.DummyCallback);
finally
DummyObj.Free;
end;
end;
{ FastCopyFile definition with object method callback }
function FastCopyFile(const ASourceFileName, ADestinationFileName: TFileName;
CopyMode: TFastCopyFileMode;
Callback: TFastCopyFileMethodCallback): Boolean; overload;
begin
Result := FastCopyFile(ASourceFileName, ADestinationFileName, CopyMode,
DummyCallback, Callback);
end;
end.
Основной метод называется FastCopyFile
, и у вас есть 4 перегруженные функции для удовлетворения любых потребностей. Ниже вы найдете два примера, показывающих, как играть с этим отрядом.
Первый самый простой: просто создайте Console Application
, а затем скопируйте и вставьте следующее содержимое:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
fastcopy in 'fastcopy.pas';
begin
try
WriteLn('FastCopyFile Result: ', FastCopyFile('test2.bin', 'test.bin'));
WriteLn('Strike the <ENTER> key to exit...');
ReadLn;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
Если хотите, я сделал приложение VCL, чтобы показать вам, как отображать ход копирования и возможность прерывания. Это приложение является многопоточным, чтобы избежать зависания графического интерфейса. Чтобы проверить этот более полный пример, создайте новое приложение VCL, а затем используйте код ниже:
Unit1.pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, ExtCtrls, FastCopy;
type
TFastCopyFileThread = class;
TForm1 = class(TForm)
Button1: TButton;
ProgressBar1: TProgressBar;
Label1: TLabel;
Button2: TButton;
RadioGroup1: TRadioGroup;
GroupBox1: TGroupBox;
Edit1: TEdit;
GroupBox2: TGroupBox;
Edit2: TEdit;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
Button3: TButton;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Déclarations privées }
fFastCopyFileThread: TFastCopyFileThread;
fFastCopyFileThreadCanceled: Boolean;
procedure ChangeControlsState(State: Boolean);
procedure FastCopyFileProgress(Sender: TObject; FileName: TFileName;
Value: Integer; var CanContinue: Boolean);
procedure FastCopyFileTerminate(Sender: TObject);
function GetStatusText: string;
procedure SetStatusText(const Value: string);
public
{ Déclarations publiques }
procedure StartFastCopyThread;
property StatusText: string read GetStatusText write SetStatusText;
end;
TFastCopyFileProgressEvent = procedure(Sender: TObject; FileName: TFileName;
Value: Integer; var CanContinue: Boolean) of object;
TFastCopyFileThread = class(TThread)
private
fSourceFileName: TFileName;
fDestinationFileName: TFileName;
fProgress: TFastCopyFileProgressEvent;
fCopyMode: TFastCopyFileMode;
procedure FastCopyFileCallback(const FileName: TFileName;
const CurrentSize, TotalSize: LongWord; var CanContinue: Boolean);
protected
procedure Execute; override;
public
constructor Create; overload;
property SourceFileName: TFileName
read fSourceFileName write fSourceFileName;
property DestinationFileName: TFileName
read fDestinationFileName write fDestinationFileName;
property CopyMode: TFastCopyFileMode read fCopyMode write fCopyMode;
property OnProgress: TFastCopyFileProgressEvent
read fProgress write fProgress;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
StartFastCopyThread;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
fFastCopyFileThread.Terminate;
fFastCopyFileThreadCanceled := True;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
with OpenDialog1 do
if Execute then
Edit1.Text := FileName;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
with SaveDialog1 do
if Execute then
Edit2.Text := FileName;
end;
procedure TForm1.ChangeControlsState(State: Boolean);
begin
Button1.Enabled := State;
Button2.Enabled := not State;
if State then
begin
if fFastCopyFileThreadCanceled then
StatusText := 'Aborted!'
else
StatusText := 'Done!';
fFastCopyFileThreadCanceled := False;
end;
end;
procedure TForm1.FastCopyFileProgress(Sender: TObject; FileName: TFileName;
Value: Integer; var CanContinue: Boolean);
begin
StatusText := ExtractFileName(FileName);
ProgressBar1.Position := Value;
end;
procedure TForm1.FastCopyFileTerminate(Sender: TObject);
begin
ChangeControlsState(True);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ChangeControlsState(True);
StatusText := 'Idle...';
end;
function TForm1.GetStatusText: string;
begin
Result := Label1.Caption;
end;
procedure TForm1.SetStatusText(const Value: string);
begin
Label1.Caption := Value;
end;
procedure TForm1.StartFastCopyThread;
begin
ChangeControlsState(False);
fFastCopyFileThread := TFastCopyFileThread.Create;
with fFastCopyFileThread do
begin
SourceFileName := Edit1.Text;
DestinationFileName := Edit2.Text;
CopyMode := TFastCopyFileMode(RadioGroup1.ItemIndex);
OnProgress := FastCopyFileProgress;
OnTerminate := FastCopyFileTerminate;
Resume;
end;
end;
{ TFastCopyFileThread }
constructor TFastCopyFileThread.Create;
begin
inherited Create(True);
FreeOnTerminate := True;
end;
procedure TFastCopyFileThread.Execute;
begin
FastCopyFile(SourceFileName, DestinationFileName, CopyMode,
FastCopyFileCallback);
end;
procedure TFastCopyFileThread.FastCopyFileCallback(const FileName: TFileName;
const CurrentSize, TotalSize: LongWord; var CanContinue: Boolean);
var
ProgressValue: Integer;
begin
CanContinue := not Terminated;
ProgressValue := Round((CurrentSize / TotalSize) * 100);
if Assigned(OnProgress) then
OnProgress(Self, FileName, ProgressValue, CanContinue);
end;
end.
Unit1.dfm
object Form1: TForm1
Left = 0
Top = 0
BorderStyle = bsDialog
Caption = 'FastCopyFile Example (Threaded)'
ClientHeight = 210
ClientWidth = 424
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 8
Top = 173
Width = 31
Height = 13
Caption = 'Label1'
end
object Button1: TButton
Left = 259
Top = 177
Width = 75
Height = 25
Caption = 'Start'
Default = True
TabOrder = 0
OnClick = Button1Click
end
object ProgressBar1: TProgressBar
Left = 8
Top = 188
Width = 245
Height = 13
TabOrder = 1
end
object Button2: TButton
Left = 340
Top = 177
Width = 75
Height = 25
Caption = 'Stop'
TabOrder = 2
OnClick = Button2Click
end
object RadioGroup1: TRadioGroup
Left = 4
Top = 110
Width = 410
Height = 57
Caption = ' Copy Mode: '
ItemIndex = 0
Items.Strings = (
'Create (Overwrite destination)'
'Append (Merge destination)')
TabOrder = 3
end
object GroupBox1: TGroupBox
Left = 4
Top = 4
Width = 412
Height = 49
Caption = ' Source: '
TabOrder = 4
object Edit1: TEdit
Left = 8
Top = 20
Width = 369
Height = 21
TabOrder = 0
Text = 'test.bin'
end
object Button3: TButton
Left = 383
Top = 20
Width = 21
Height = 21
Caption = '...'
TabOrder = 1
OnClick = Button3Click
end
end
object GroupBox2: TGroupBox
Left = 4
Top = 59
Width = 412
Height = 50
Caption = ' Destination: '
TabOrder = 5
object Edit2: TEdit
Left = 8
Top = 21
Width = 369
Height = 21
TabOrder = 0
Text = 'sizious.bin'
end
end
object Button4: TButton
Left = 387
Top = 80
Width = 21
Height = 21
Caption = '...'
TabOrder = 6
OnClick = Button4Click
end
object OpenDialog1: TOpenDialog
DefaultExt = 'bin'
Filter = 'All Files (*.*)|*.*'
Options = [ofHideReadOnly, ofFileMustExist, ofEnableSizing]
Left = 344
Top = 12
end
object SaveDialog1: TSaveDialog
DefaultExt = 'bin'
Filter = 'All Files (*.*)|*.*'
Options = [ofOverwritePrompt, ofHideReadOnly, ofEnableSizing]
Left = 344
Top = 68
end
end
Конечно, не забудьте добавить ссылку на файл FastCopy.pas на этот проект.
Вы должны получить это:
Выберите исходный файл, целевой файл, затем нажмите Пуск .
Все кредиты идут, конечно, Дэви Лэндман .