Autoit で大きなファイルをコピーする際、「進捗が分からず不安」「間違って上書きしてしまいそうで怖い」と感じたことはありませんか?
この記事では、その両方の問題を解決する、実用的なファイルコピーツールを紹介します。
Windows API を利用した圧倒的な速度と、プログレスバーによる進捗表示、そして安全な上書き確認機能を備えた、決定版とも言えるスクリプトです。
※ヘルプファイルのサンプルコードを元に作成しています。
完成したスクリプトの全コード
#include <APIFilesConstants.au3>
#include <Misc.au3>
#include <WinAPIError.au3>
#include <WinAPIFiles.au3>
#include <MsgBoxConstants.au3>
; ### メイン処理 ###
Local $hProgressProc = DllCallbackRegister('_ProgressProc', 'int', 'int64;int64;int64;int64;dword;dword;handle;handle;ptr')
Local $sSourceFile = FileOpenDialog("コピー元のファイルを選択してください", @MyDocumentsDir, "すべてのファイル (*.*)")
If @error Then
DllCallbackFree($hProgressProc)
Exit
EndIf
Local $sSourceName = StringMid($sSourceFile, StringInStr($sSourceFile, "\", 0, -1) + 1)
Local $sDestFile = FileSaveDialog("コピー先のファイルパスと名前を指定してください", @MyDocumentsDir, "すべてのファイル (*.*)", 0, $sSourceName)
If @error Then
DllCallbackFree($hProgressProc)
Exit
EndIf
; ### 上書き確認処理 ###
If FileExists($sDestFile) Then
Local $iResponse = MsgBox($MB_YESNO + $MB_ICONQUESTION, "上書き確認", "同じ名前のファイルが既に存在します。" & @CRLF & "上書きしますか?")
If $iResponse = $IDNO Then
DllCallbackFree($hProgressProc)
Exit
EndIf
EndIf
ProgressOn('ファイルコピー', 'ファイルをコピーしています...', '0%')
If Not _WinAPI_CopyFileEx($sSourceFile, $sDestFile, $COPY_FILE_RESTARTABLE, DllCallbackGetPtr($hProgressProc)) Then
_WinAPI_ShowLastError('ファイルのコピー中にエラーが発生しました: ' & $sSourceFile)
EndIf
ProgressOff()
DllCallbackFree($hProgressProc)
MsgBox(0, "完了", "処理が完了しました。")
; ### コピーの進捗報告を受け取るコールバック関数 ###
Func _ProgressProc($iTotalFileSize, $iTotalBytesTransferred, $iStreamSize, $iStreamBytesTransferred, $iStreamNumber, $iCallbackReason, $hSourceFile, $hDestinationFile, $pData)
#forceref $iStreamSize, $iStreamBytesTransferred, $iStreamNumber, $iCallbackReason, $hSourceFile, $hDestinationFile, $pData
Local $iPercent = Round($iTotalBytesTransferred / $iTotalFileSize * 100)
If $iPercent = 100 Then
ProgressSet($iPercent, '', '完了')
Else
ProgressSet($iPercent, $iPercent & '%')
EndIf
If _IsPressed('1B') Then
Return $PROGRESS_CANCEL
Else
Return $PROGRESS_CONTINUE
EndIf
EndFunc ;==>_ProgressProc
コードの詳しい解説
ファイルの選択
このスクリプトは、実行するとまずユーザーにファイルの選択を促します。
- コピー元の選択:
FileOpenDialogを使い、コピーしたいファイルをユーザーに選択させます。 - コピー先の選択:
FileSaveDialogを使い、コピー先の場所とファイル名を決定させます。ここでは、StringMid関数などを使ってコピー元のファイル名をあらかじめ入力欄にセットしておくことで、ユーザーの利便性を高めています。
上書きの確認処理
ここが、安全性を高めるための重要な部分です。
- 存在チェック:
FileExists関数を使い、ユーザーが指定したコピー先のパスに、既にファイルが存在するかどうかを確認します。 - 確認ダイアログ: もしファイルが存在した場合、
MsgBox関数を使って「上書きしますか?」と尋ねる「はい/いいえ」のダイアログボックスを表示します。 - 処理の分岐:
- ユーザーが「いいえ」 (
$IDNO) をクリックした場合、Exitでスクリプトはコピー処理を行わずに安全に終了します。 - ユーザーが「はい」 (
$IDYES) をクリックした場合、このIfブロックを抜け、後続のコピー処理が実行されます。
- ユーザーが「いいえ」 (
Windows API によるコピー実行
ユーザーが上書きを許可した場合、あるいはコピー先にファイルが存在しなかった場合に、この処理が実行されます。
_WinAPI_CopyFileEx: これは、Windows OS に「このファイルをコピーしてください」と作業を依頼するための関数です。実際のデータ移動は、最も効率的な方法で OS が実行します。- コールバック:
DllCallbackRegisterで登録した_ProgressProc関数を、OS がコピー中に呼び出して進捗を報告してくれます。これにより、OS ネイティブの速度と、進捗表示を両立しています。 - 進捗表示:
ProgressOnで表示したウィンドウの中身を、コールバック関数の中でProgressSetを使って更新しています。 - 中止機能: コールバック関数の中で
_IsPressed('1B')をチェックすることで、ユーザーが Esc キーを押したらコピーを中断する機能も実装しています。
コールバック関数の詳しい解説
コールバックとは何か
コールバックとは、直訳すると「後で呼び返す」という意味です。プログラミングにおいては、「ある処理(A)が終わった後や、その途中で、指定した別の処理(B)を自動的に実行してもらう」という仕組みを指します。
今回のスクリプトに例えると、
- 処理A:
_WinAPI_CopyFileExによる、OS が行うファイルコピー作業 - 処理B:
_ProgressProcという進捗報告(プログレスバー更新)
つまり、「OS にファイルコピーをお願いする際に、『作業の途中で、定期的に_ProgressProc関数を呼び出して進捗を報告してください』と、連絡先を渡しておく」というのがコールバックの考え方です。
なぜコールバックが必要か
_WinAPI_CopyFileExは、一度実行するとコピーが完了するまで制御が戻ってきません。
もしコールバックがなければ、私たちはコピーが終わるまで何もできず、進捗を知る術がありません。
コールバックがあることで、重い処理を OS に任せつつ、その途中経過をリアルタイムで受け取ることが可能になります。
実際のコードでの流れ
1. 「連絡先」の作成
Local $hProgressProc = DllCallbackRegister('_ProgressProc', 'int', 'int64;int64;...')
DllCallbackRegisterは、AutoIt で作成した_ProgressProc関数を、Windows API(C言語の世界)が理解できる形式の「連絡先」に変換し、登録する作業です。
戻り値として、その連絡先のハンドル(識別番号)が$hProgressProcに格納されます。
2. 「連絡先」を渡して作業を依頼
_WinAPI_CopyFileEx(..., DllCallbackGetPtr($hProgressProc))
_WinAPI_CopyFileExを呼び出す際、DllCallbackGetPtrで連絡先のアドレスを取得し、引数として渡します。
これにより、OS は「コピー作業中に、このアドレスにある関数を呼び出せばいいのだな」と認識します。
3. OS からの「呼び返し」と情報提供
Func _ProgressProc($iTotalFileSize, $iTotalBytesTransferred, ...)
コピー作業が始まると、OS は内部でデータの一部をコピーするたびに、登録された_ProgressProc関数を呼び出します。
その際、引数として$iTotalFileSize(全体のファイルサイズ)や$iTotalBytesTransferred(今までにコピーした量)といった、非常に詳細なライブ情報を渡してくれます。
スクリプトは、この渡された情報を使ってパーセンテージを計算し、ProgressSetでプログレスバーを更新しているのです。
このコールバックという仕組みこそが、OS の高速な処理能力と、AutoIt の柔軟な GUI機能を両立させる鍵となっています。
【AutoIt】#forceref と #AutoIt3Wrapper_AU3Check_Parameters について
コメント