概要

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

}

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