CopyFileExを使用した状況表示付きファイルコピー(32/64bit)

icon 項目のみ表示/展開表示の切り替え

概要

CopyFileEx APIを使用し、1個のファイルをコピーします。コピー中にダイアログボックスを表示し残り時間、転送速度等の状況表示および中止、一時停止ができます。
コピー元のフォルダー名、コピー先のフォルダー名、ファイル名は、ソースコードの変数で定義しています。
本プログラムではD:\1.wmaをD:\temp\1.wmaにコピーします。D:\tempフォルダーはあらかじめ作成しておいてください。

テスト環境

コンパイラ

Visual C++ 2008/2013 Express 32/64bit マルチバイト/UNICODE

実行環境

Windows XP Professional Service Pack 3 32bit(Virtual Box上の仮想マシーン)
Windows 7 Enterprise Service Pack 1 64bit

動作例

プログラムソースの概要

copyfileex2.cpp

_tWinMain

進行状況を表示するモードレスダイアログボックスを表示します。その後、CopyFileExを呼び出してコピーを開始します。コピー中のダイアログへのメッセージは、CopyFileExの引数で指定した、関数の中で処理しています。
コピーが終了またはキャンセルされた場合は、CopyFileExから戻るので、ダイアログボックスへのメッセージを_tWinMain内のメッセージループで処理します。ダイアログボックスでPostQuitMessagが呼び出されるとメッセージループを抜けてプログラムを終了します。

DlgProc1

WM_INITDIALOGメッセージ
ダイアログボックスの初期化時に呼び出されます。
コピー元フォルダー、コピー先フォルダー、ファイル名の表示、プログレスバーの範囲の設定を行います。
転送速度を計算するために、プロセッサ時間を取得しcpy->startに保存します。
WM_COMMANDメッセージ
IDC_CPY_CHG
CopyProgressRoutine関数から呼び出されます。引数lParamよりコピーしたバイト数及びファイルのサイズを取得します。
現在のプロセッサ時間から開始時のプロセッサ時間を減じ、転送速度を計算します。
これらの情報をダイアログボックスに表示します。
IDOK
コピー終了後にOKボタンをクリックした場合に呼び出されます。
ダイアログボックスを閉じ、PostQuitMessagを呼び出しプログラムを終了させます。
IDC_CPY_PAUSE_BUTTON
コピーを開始しているときは、ボタンに一時停止を表示しており、クリックすると呼び出されます。ボタンに再開を表示させ::pause_f=TRUEに設定します。
コピーを中断しているときは、ボタンに再開を表示しており、クリックすると呼び出されます。ボタンに一時停止を表示させ::pause_f=FALSEに設定します。
IDCANCEL
キャンセルボタンをクリックすると呼び出されます。
::cansel_f=TRUEに設定し、コピーをキャンセルさせます。

CopyProgressRoutine

CopyFileExから呼び出されます。コピーしたバイト数及びファイルのサイズを取得し、SendMessageによりDlgProc1関数にWM_COMMANDメッセージを発行しダイアログボックスに状況を表示させます。
一時停止及びキャンセルボタンからのメッセージを有効にするためにメッセージループをサポートしています。
一時停止中でない場合は、PeekMessageによりメッセージの有無を確認し有効なメッセージがある場合は処理します。
一時停止中の場合は、再開ボタン及びキャンセルボタンのメッセージを処理するためにGetMessageによりメッセージが発生するまで待機します。メッセージが発生すればそれを処理します。一時停止中の場合は、一時停止が解除されない限りGetMessageからの処理を繰り返します。

ソースコード

copyfileex2.cpp

// ファイルコピーサンプル
// モードレスダイアログボックスにより状況表示を行う
// キャンセル及び一時停止機能付き

#include <windows.h>
#include <shlwapi.h>
#include <commctrl.h>
#include <time.h>
#include <stdio.h>
#include <tchar.h>
#include "resource.h"

#pragma comment(lib,"shlwapi.lib")

INT_PTR CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); // ダイアログボックスプロシージャー
DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize,  LARGE_INTEGER TotalBytesTransferred,  LARGE_INTEGER StreamSize,   LARGE_INTEGER StreamBytesTransferred,  DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData );

HWND hDlg=0;
BOOL cancel_f=FALSE;    //      キャンセルを選択するとTRUE
BOOL pause_f=FALSE;     //      一時停止を選択するとTRUE
HINSTANCE hInst;

TCHAR* src=_TEXT("D:");                       //      コピー元フォルダー
TCHAR* dtc=_TEXT("D:\\temp"); //      コピー先フォルダー
TCHAR* fname=_TEXT("1.wma");  //      コピーするファイル名

struct CpyData{
        LARGE_INTEGER TotalFileSize;    //      コピーされたバイト数
        LARGE_INTEGER TotalBytesTransferred;    //      ファイルサイズ
        TCHAR* src;
        TCHAR* dtc;
        TCHAR* fname;
        clock_t start;
};

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, TCHAR* lpszCmdLine, int nCmdShow){
        MSG msg;
        CpyData cpy;
        TCHAR srcname[MAX_PATH];        //      コピー元ファイル名
        TCHAR dtcname[MAX_PATH];        //      コピー先ファイル名

        ::hInst=hInstance;

        _stprintf_s(srcname,sizeof(srcname)/sizeof(TCHAR),_TEXT("%s\\%s"),src,fname);
        _stprintf_s(dtcname,sizeof(dtcname)/sizeof(TCHAR),_TEXT("%s\\%s"),dtc,fname);

        cpy.src=::src;
        cpy.dtc=::dtc;
        cpy.fname=::fname;

//      コピーの進行状況を示すモードレスダイアログボックスを表示する
        ::hDlg=CreateDialogParam(::hInst,_TEXT("CPY_DLG"),0,DlgProc1,(LPARAM)&cpy);

        CopyFileEx(srcname,dtcname,CopyProgressRoutine,&cpy,&cancel_f,0);

        EnableWindow(GetDlgItem(hDlg,IDOK),TRUE);
        EnableWindow(GetDlgItem(hDlg,IDCANCEL),FALSE);
        EnableWindow(GetDlgItem(hDlg,IDC_CPY_PAUSE_BUTTON),FALSE);
        
//      コピー終了後またはコピーがキャンセルされた後のダイアログボックスのメッセージループ

        while(::hDlg){
                while(GetMessage(&msg , 0 , 0 , 0 )){
                        if(IsDialogMessage(::hDlg , &msg)==0){
                                TranslateMessage(&msg);
                                DispatchMessage(&msg);
                        }
                }
        }
        return 0;
}

//      CopyFileEx関数から呼び出される。進行状況が知らされる

DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize,  LARGE_INTEGER TotalBytesTransferred,  LARGE_INTEGER StreamSize,   LARGE_INTEGER StreamBytesTransferred,  DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData ){
        MSG msg;
        CpyData* cpy=(CpyData*)lpData;
        cpy->TotalFileSize=TotalFileSize;    //      コピーしたバイト数
        cpy->TotalBytesTransferred=TotalBytesTransferred;    //      ファイルのサイズ数

//      CopyFileEx実行中のダイアログボックスのメッセージ処理

        do{
                if(::pause_f==FALSE){   //      一時停止中でないためメッセージがある場合のみ処理する
                        if(PeekMessage(&msg , ::hDlg , 0 , 0 , PM_REMOVE)){
                                if (::hDlg && IsDialogMessage(::hDlg , &msg)==0){
                                        TranslateMessage(&msg);
                                        DispatchMessage(&msg);
                                }
                        }
                }else{  //      一時停止中なのでメッセージが発生するまで待機して処理する
                        GetMessage(&msg , ::hDlg , 0 , 0 );
                        if (::hDlg && IsDialogMessage(::hDlg , &msg)==0){
                                TranslateMessage(&msg);
                                DispatchMessage(&msg);
                        }
                }
        }while( ::pause_f==TRUE);       //      一時停止中に再開ボタンを検出するためにメッセージが発生(::pause_f==FALSE)となるまでループする
        if(::hDlg)
                SendMessage(::hDlg,WM_COMMAND,IDC_CPY_CHG,(LPARAM)cpy); // 進行状況をダイアログボックスに送信する
//      Sleep(1000);    //デバック用に進行状況をゆっくり表示する場合に使用
        return PROGRESS_CONTINUE;
}

 // ダイアログボックスプロシージャー

INT_PTR CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
        CpyData* cpy;
        switch (msg) {
        case WM_INITDIALOG:{
                EnableWindow(GetDlgItem(hDlg,IDOK),FALSE);
                cpy=(CpyData*)lParam;
                SetWindowText(GetDlgItem(hDlg,IDC_CPY_SRCDIR_LABEL),cpy->src);
                SetWindowText(GetDlgItem(hDlg,IDC_CPY_NAME_LABEL),cpy->fname);
                SetWindowText(GetDlgItem(hDlg,IDC_CPY_DTCDIR_LABEL),cpy->dtc);
                SendMessage(GetDlgItem(hDlg,IDC_CPY_PROGBAR), PBM_SETRANGE, (WPARAM)0, MAKELPARAM(0, 100));
                cpy->start=clock();
                return TRUE;
        }
        case WM_COMMAND:
                switch (LOWORD(wParam)) {
                case IDC_CPY_CHG:{      //      コピーの進行状況の表示
                        TCHAR buf[256];
                        cpy=(CpyData*)lParam;
                        clock_t end=clock();
                        double t=double(end - cpy->start)*1000.0/CLOCKS_PER_SEC;
                        double speed=double(cpy->TotalBytesTransferred.QuadPart)/(t/1000);

                        if(0<cpy->TotalBytesTransferred.QuadPart){        //      コピーされたデーターがある場合
                                if(cpy->TotalBytesTransferred.QuadPart==cpy->TotalFileSize.QuadPart){
                                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_TIME_LABEL),_TEXT("コピーが終了しました"));
                                        EnableWindow(GetDlgItem(hDlg,IDOK),TRUE);
                                }else{
                                        double t2;
                                        t2=(cpy->TotalFileSize.QuadPart - cpy->TotalBytesTransferred.QuadPart)/speed;
                                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("約%.1f秒"),t2);
                                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_TIME_LABEL),buf);       //      残り時間
                                }
                        }else
                                SetWindowText(GetDlgItem(::hDlg,IDC_CPY_TIME_LABEL),_TEXT("コピーを開始しました"));

                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%lluKB/%lluKB"),(cpy->TotalFileSize.QuadPart-cpy->TotalBytesTransferred.QuadPart)/1024 , cpy->TotalFileSize.QuadPart/1024);
                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_COUNT_LABEL),buf);
                        int pos=(int)(cpy->TotalBytesTransferred.QuadPart*100/cpy->TotalFileSize.QuadPart);
                        SendMessage(GetDlgItem(::hDlg,IDC_CPY_PROGBAR) , PBM_SETPOS , pos,0); //        プログレスバー位置設定

                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%.1fKB/秒"),speed/1024);
                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_SPEED_LABEL),buf);      //      転送速度

                        return TRUE;
                }
                case IDOK:{     //      ダイアログボックスを閉じる
                        ::hDlg=0;
                        DestroyWindow(hDlg);
                        PostQuitMessage(0);
                        return TRUE;
                }
                case IDC_CPY_PAUSE_BUTTON:{     //      一時停止または再開が押された場合
                        if(::pause_f == FALSE){
                                ::pause_f=TRUE;
                                SetWindowText(GetDlgItem(hDlg,IDC_CPY_PAUSE_BUTTON) , _TEXT("再開"));
                        }else{
                                ::pause_f=FALSE;
                                SetWindowText(GetDlgItem(hDlg,IDC_CPY_PAUSE_BUTTON) , _TEXT("一時停止"));
                        }
                        return TRUE;
                }
                case IDCANCEL:  //      キャンセルが押された場合
                        ::cancel_f=TRUE;
                        ::pause_f=FALSE;
                        return FALSE;
                default:
                        return FALSE;
                }
        default:
                return FALSE;
        }
        return FALSE;
}

resoucr.h

#define IDC_CPY_SRCDIR_LABEL    100
#define IDC_CPY_NAME_LABEL              101
#define IDC_CPY_DTCDIR_LABEL            102
#define IDC_CPY_TIME_LABEL              103
#define IDC_CPY_COUNT_LABEL             104
#define IDC_CPY_SPEED_LABEL             105
#define IDC_CPY_PAUSE_BUTTON            107
#define IDC_CPY_PROGBAR         108
#define IDC_CPY_CHG 2010

resource.rc

#include        <windows.h>
#include        "resource.h"

//----------------------------------
// ダイアログ (IDD_)
//----------------------------------
CPY_DLG DIALOG DISCARDABLE 0, 0, 413, 160
STYLE WS_POPUP | WS_CAPTION | DS_SETFONT | WS_VISIBLE
CAPTION "コピー中"
FONT 9, "MS Pゴシック"
{
 CONTROL "名前", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 15, 192, 12
 CONTROL "", IDC_CPY_NAME_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 15, 319, 12

 CONTROL "元のフォルダー", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 29, 192, 12
 CONTROL "", IDC_CPY_SRCDIR_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 29, 319, 12

 CONTROL "対象", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 43, 192, 12
 CONTROL "", IDC_CPY_DTCDIR_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 43, 319, 12

 CONTROL "残りの時間", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 57, 192, 12
 CONTROL "", IDC_CPY_TIME_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 57, 319, 12

 CONTROL "残りの項目", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 71, 192, 12
 CONTROL "", IDC_CPY_COUNT_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 71, 319, 12
 
 CONTROL "スピード", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 85, 192, 12
 CONTROL "", IDC_CPY_SPEED_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 85, 319, 12

 CONTROL "ProgressBar", IDC_CPY_PROGBAR, "MSCTLS_PROGRESS32", WS_CHILD | WS_VISIBLE | WS_BORDER, 14, 106, 385, 12

 CONTROL "OK(&O)", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 235, 132, 50, 14
 CONTROL "一時停止(&P)", IDC_CPY_PAUSE_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 292, 132, 50, 14
 CONTROL "キャンセル(&C)", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 349, 132, 50, 14

}

実行ファイルとソースファイルのダウンロード