Windows APIメモ CopyFileEx, CopyFileExW
Windows APIのメモ。
CopyFileEx, CopyFileExW。
参考サイト
function CopyFileEx(
lpExistingFileName,
lpNewFileName : PChar;
lpProgressRoutine : TFNProgressRoutine;
lpData : Pointer;
pbCancel : PBool;
dwCopyFlags : DWORD
): BOOL; stdcall;
ファイルコピー中に進捗状況を表示させるためにはCopyFileEx APIを使います。
ちょっと難しそうに思えますが第3引数にコピーの進捗状況を表示するコールバック関数のアドレスを指定するだけなのでそう難しいことはありません。
第4引数でポインタを介してコールバック関数へデータを渡すこともできますがよく分からなければそれにこだわることなくグローバル変数を使ってやり取りすればOKです。
下の例ではグローバル変数のForm1を使ってForm1のキャプションに進捗状況を表示しています。
//MoveFileWithProgress, CopyFileExから呼ばれるコールバックルーチン
function CopyProgressRoutine(
iTotalFileSize : Int64;
iTotalBytesTransferred : Int64;
iStreamSize : Int64;
iStreamBytesTransferred : Int64;
dwStreamNumber : DWORD;
dwCallbackReason : DWORD;
hSourceFile : THandle;
hDestinationFile : THandle;
lpData
: Pointer
): DWORD;
stdcall;
begin
if (iTotalFileSize > 0)
then begin
//0で割るエラーを回避
Form1.Caption :=
Format('コピーしています %d%%', [Trunc(iTotalBytesTransferred / iTotalFileSize
* 100)]);
end else begin
Form1.Caption := 'コピーしています';
end;
Application.ProcessMessages;
Result := PROGRESS_CONTINUE;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
//コピー元ファイルを選択
if (OpenDialog1.Execute)
then begin
//コピー先ファイルを指定
SaveDialog1.FileName := OpenDialog1.FileName;
if (SaveDialog1.Execute)
then begin
if (CopyFileEx(
PChar(OpenDialog1.FileName),
PChar(SaveDialog1.FileName),
@CopyProgressRoutine,
nil,
nil,
//特に必要がなければnilでOK
0
))
then begin
Caption := 'コピーは成功しました';
end else begin
Caption := 'コピーは失敗しました';
end;
end;
end;
end;
CopyFileEx関数の第5引数には特に必要がなければnilを指定してもかまわないようです。
必要な場合はグローバル変数かフォームのプライベート変数を指定すると良いでしょう。
var
bCancel: Boolean;
procedure TForm1.Button1Click(Sender: TObject);
begin
//コピー元ファイルを選択
if (OpenDialog1.Execute) then begin
//コピー先ファイルを指定
SaveDialog1.FileName := OpenDialog1.FileName;
if (SaveDialog1.Execute) then begin
bCancel := False;
if (CopyFileEx(
PChar(OpenDialog1.FileName),
PChar(SaveDialog1.FileName),
@CopyProgressRoutine,
nil,
@bCancel,
0
)) then begin
Caption := 'コピーは成功しました';
end else begin
Caption := 'コピーは失敗しました';
end;
end;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
//コピー中止
begin
bCancel := True;
end;
Form1のprivate変数でもOKです。
private
{ Private 宣言 }
F_bCancel: Boolean;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
//コピー元ファイルを選択
if (OpenDialog1.Execute) then begin
//コピー先ファイルを指定
SaveDialog1.FileName := OpenDialog1.FileName;
if (SaveDialog1.Execute) then begin
F_bCancel := False;
if (CopyFileEx(
PChar(OpenDialog1.FileName),
PChar(SaveDialog1.FileName),
@CopyProgressRoutine,
nil,
@F_bCancel,
0
)) then begin
Caption := 'コピーは成功しました';
end else begin
Caption := 'コピーは失敗しました';
end;
end;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
//コピー中止
begin
F_bCancel := True;
end;
Unicode対応版のCopyFileExWも第1引数と第2引数の型がPWideCharになっているだけで使い方はCopyFileExと一緒です。
WideString型の変数ならPWideCharにキャストして渡します。
AnsiString型の変数の場合ならまずWideStringでキャストしてからPWideCharにキャストする点にだけ気をつければOKです。
//MoveFileWithProgress, CopyFileExから呼ばれるコールバックルーチン
function CopyProgressRoutine(
iTotalFileSize : Int64;
iTotalBytesTransferred : Int64;
iStreamSize : Int64;
iStreamBytesTransferred : Int64;
dwStreamNumber : DWORD;
dwCallbackReason : DWORD;
hSourceFile : THandle;
hDestinationFile : THandle;
lpData
: Pointer
): DWORD; stdcall;
begin
if (iTotalFileSize > 0) then begin
//0で割るエラーを回避
Form1.Caption := Format('コピーしています %d%%', [Trunc(iTotalBytesTransferred / iTotalFileSize * 100)]);
end else begin
Form1.Caption := 'コピーしています';
end;
Application.ProcessMessages;
Result := PROGRESS_CONTINUE;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
//コピー元ファイルを選択
if (OpenDialog1.Execute) then begin
//コピー先ファイルを指定
SaveDialog1.FileName := OpenDialog1.FileName;
if (SaveDialog1.Execute) then begin
if (CopyFileExW(
PWideChar(WideString(OpenDialog1.FileName)),
PWideChar(WideString(SaveDialog1.FileName)),
@CopyProgressRoutine,
nil,
nil, //特に必要がなければnilでもOK
0
)) then begin
Caption := 'コピーは成功しました';
end else begin
Caption := 'コピーは失敗しました';
end;
end;
end;
end;
この例だとわざわざUnicode対応版のAPIを使う必要もないのですが、まぁ一応例示のためということで。