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

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

概要

指定されたフォルダーに含まれるフォルダー・ファイルをコピーします。CopyFileEx APIは1つのファイルしかコピーできないので、フォルダーに含まれるフォルダー・ファイルのリストを作成し、CopyFileExを使いファイルを1個ずつコピーします。コピー中にダイアログボックスを表示し残り時間、転送速度等の状況表示および中止、一時停止ができます。
コピー元のフォルダー名、コピー先のフォルダー名の初期値は、ソースコードの変数で定義しています。参照ボタンにより実際にコピー元・コピー先をフォルダーを変更することができます。コピー先に同名のファイルがあれば上書きします。

テスト環境

コンパイラ

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

実行環境

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

動作例

プログラムソースの概要

copyfileex3.cpp

_tWinMain

進行状況を表示するモードレスダイアログボックスを表示します。コピーボタンをクリックするとコピー元のフォルダー・ファイルのリスト(FILE_LIST::find関数)を作成します。その後、FILE_LIST::copy_file関数を呼び出しフォルダーのコピーします。
FILE_LIST::copy_file関数はファイルリストより1個ずつファイルを取り出してCopyFileExを呼び出してコピーを行います。フォルダーの場合、フォルダーを作成し、FILE_LIST::copy_file関数を再帰呼び出します。コピー中のダイアログへのメッセージは、CopyFileExの引数で指定した、関数の中で処理しています。
コピーが終了またはキャンセルされた場合は、CopyFileExから戻るので、ダイアログボックスへのメッセージを_tWinMain内のメッセージループで処理します。ダイアログボックスでPostQuitMessagが呼び出されるとメッセージループを抜けてプログラムを終了します。

DlgProc1

WM_INITDIALOGメッセージ
ダイアログボックスの初期化時に呼び出されます。
コピー元フォルダー、コピー先フォルダー、ファイル名の表示、プログレスバーの範囲の設定を行います。
WM_COMMANDメッセージ
IDC_CPY_CHG
CopyProgressRoutine関数から呼び出されます。引数lParamよりコピーしたバイト数及びファイルのサイズを取得します。
現在のプロセッサ時間から開始時のプロセッサ時間を減じ、転送速度を計算します。
これらの情報をダイアログボックスに表示します。
IDC_CPY_START
コピーボタンをクリックすると呼び出されます。
エディットボックスからコピー元・コピー先のフォルダー名を取得します。
FILE_LIST::find関数によりコピー元のフォルダー・ファイル名のリストを作成します。
FILE_LIST::file_copy関数に渡すCpyData構造体を初期化し、FILE_LIST::file_copy関数を呼び出します。
IDC_CPY_PAUSE_BUTTON
コピーを開始しているときは、ボタンに一時停止を表示しており、クリックすると呼び出されます。ボタンに再開を表示させ::pause_f=TRUEに設定します。
コピーを中断しているときは、ボタンに再開を表示しており、クリックすると呼び出されます。ボタンに一時停止を表示させ::pause_f=FALSEに設定します。
IDCANCEL
終了ボタンをクリックすると呼び出されます。
ダイアログボックスを閉じ、PostQuitMessageを呼び出しプログラムを終了させます。

CopyProgressRoutine

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

file_list.cpp,file_list.h

FILE_LINK構造体

1ファイル分のファイル名及びサイズ・日時及び属性を格納します。
対象がフォルダーの場合、childメンバーにフォルダーに含まれるファイル・フォルダーのリストへのアドレス(FILE_LIST構造体)を格納します。
メンバー関数には、ファイル同士の日付の比較、リストビューへファイル名・日時の表示等をサポートする関数があります。

FILE_LIST構造体

複数のファイル(FILE_LINK構造体)をリストで管理します。
ファイル数、フォルダー数、ファイルサイズ数を格納しています。
指定したフォルダーからフォルダー名・ファイル名のリスト作成、検索、FILE_LIST同士の比較、コピー、ファイルコピー、ファイル削除等をサポートしています。

CpyData構造体

FILE_LIST::file_copy関数への引数で使用します。
コピー中の状況表示のためのファイル数、コピーしたバイト数等が格納されています。

ソースコード

copyfileex3.cpp


// 指定したフォルダーをコピーするサンプル(進行状況付き)

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include "file_list.h"
#include "resource.h"


//      ディレクトリ名を取得するコモンダイアログを表示する

bool GetDir(HWND hWnd, const TCHAR* def_dir,TCHAR* path);

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

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 );

#define MAX_PASS 1024

TCHAR src_def_dir[MAX_PASS];    //      コピー元フォルダー
TCHAR dtc_def_dir[MAX_PASS];    //      コピー先フォルダー
TCHAR def_dir[MAX_PASS];

HWND hDlg=0;                    //      ダイアログボックスのハンドル
BOOL cancel_f=FALSE;    //      キャンセルを選択するとTRUE
BOOL pause_f=FALSE;     //      一時停止を選択するとTRUE
HINSTANCE hInst;

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, TCHAR* lpszCmdLine, int nCmdShow){
        MSG msg;
        CpyData cpy;
        ::hInst=hInstance;

//      コピー元・コピー先フォルダーの初期値
        _tcscpy_s(src_def_dir,sizeof(src_def_dir)/sizeof(TCHAR), _TEXT("D:\\src"));
        _tcscpy_s(dtc_def_dir,sizeof(src_def_dir)/sizeof(TCHAR), _TEXT("D:\\dtc"));

//      コピーの進行状況を示すモードレスダイアログボックスを表示する
        ::hDlg=CreateDialogParam(::hInst,_TEXT("DLG1"),0,DlgProc1,(LPARAM)&cpy);
        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.QuadPart;   //      コピーしたバイト数
        cpy->TotalBytesTransferred=TotalBytesTransferred.QuadPart;   //      ファイルのサイズ数
        if(cpy->TotalBytesTransferred){
                UINT64 s=cpy->TotalBytesTransferred - cpy->reTotalBytesTransferred;
                cpy->AllTotalBytesTransferred += s;
        }else{
                if(cpy->num==0)      //      全体のコピーが開始された
                        cpy->file_start = cpy->start = clock();
                else    //      個別のファイルのコピーが開始された
                        cpy->file_start = clock();
        }
        cpy->reTotalBytesTransferred = cpy->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(100);     //デバック用に進行状況をゆっくり表示する場合に使用
        return PROGRESS_CONTINUE;
}

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

INT_PTR CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
        TCHAR buf[MAX_PASS];
        static FILE_LIST src_file_list;
        static HCURSOR wait_icon=0;
        static HCURSOR org_icon=0;
        static double file_speed;
        static CpyData* cpy=0;

        switch (msg) {
        case WM_INITDIALOG:{    //      ダイアログボックス初期化
                cpy=(CpyData*)lParam;
                ::hDlg = hDlg;
                file_speed=0;
                SetWindowText(GetDlgItem(hDlg, IDC_SRC_DIR_EDIT), src_def_dir);
                SetWindowText(GetDlgItem(hDlg, IDC_DTC_DIR_EDIT), dtc_def_dir);
                wait_icon=LoadCursor(NULL,IDC_WAIT);
                EnableWindow(GetDlgItem(hDlg,IDC_CPY_PAUSE_BUTTON),FALSE);
                ShowWindow(hDlg,TRUE);
                return TRUE;
        }
        case WM_COMMAND:
                switch (LOWORD(wParam)) {
                case IDC_SRC_DIR_BUTTON:        //      コピー元フォルダーの参照ボタンがクリックされた場合
                        GetWindowText(GetDlgItem(hDlg, IDC_SRC_DIR_EDIT), def_dir,sizeof(def_dir)/sizeof(TCHAR));
                        GetDir(hDlg, def_dir,buf);
                        SetWindowText(GetDlgItem(hDlg, IDC_SRC_DIR_EDIT), buf);
                        return TRUE;
                case IDC_DTC_DIR_BUTTON:        //      コピー先フォルダーの参照ボタンがクリックされた場合
                        GetWindowText(GetDlgItem(hDlg, IDC_DTC_DIR_EDIT), def_dir,sizeof(def_dir)/sizeof(TCHAR));
                        GetDir(hDlg, def_dir,buf);
                        SetWindowText(GetDlgItem(hDlg, IDC_DTC_DIR_EDIT), buf);
                        return TRUE;
                case IDC_CPY_CHG:{      //      コピー状況を表示
                        clock_t end=clock();
                        double t=double(end - cpy->start)*1000.0/CLOCKS_PER_SEC;
                        double speed= t ? double(cpy->AllTotalBytesTransferred)/(t/1000) : 0;

                        double file_t=double(end - cpy->file_start)*1000.0/CLOCKS_PER_SEC;
                        file_speed= file_t ? double(cpy->TotalBytesTransferred)/(file_t/1000) : file_speed;

                        if(0<cpy->AllTotalBytesTransferred){      //      コピーされたデーターがある場合
                                if(cpy->AllTotalBytesTransferred==cpy->AllTotalFileSize){ //      コピーが終了
                                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("コピーが終了しました(%.1f秒)"),t/1000);
                                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_TIME_LABEL),buf);
                                        EnableWindow(GetDlgItem(hDlg,IDOK),TRUE);
                                        cpy->num = cpy->AllNum;
                                }else{  //      コピー中
                                        double t2;
                                        t2=(cpy->AllTotalFileSize- cpy->AllTotalBytesTransferred)/speed;
                                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("約%.1f秒"),t2);
                                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_TIME_LABEL),buf);       //      残り時間
                                }
                        }else{
                                file_speed=0;
                                SetWindowText(GetDlgItem(::hDlg,IDC_CPY_TIME_LABEL),_TEXT("コピーを開始しました"));
                        }
                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%lluKB/%lluKB"),(cpy->AllTotalFileSize-cpy->AllTotalBytesTransferred)/1024 , cpy->TotalFileSize/1024);
                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_COUNT_LABEL),buf);
                        int pos=(int)(cpy->AllTotalBytesTransferred*100/cpy->AllTotalFileSize);
                        SendMessage(GetDlgItem(::hDlg,IDC_CPY_PROGBAR) , PBM_SETPOS , pos,0); //        プログレスバー位置設定

                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("全体 %.1fKB/秒,ファイル %.1fKB/秒") , speed/1024 , file_speed/1024 );
                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_SPEED_LABEL),buf);      //      転送速度

                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("全体 %i個/%i個(%llukB/%llukB),ファイル %llukB/%llukB"),cpy->AllNum-cpy->num , cpy->AllNum ,  (cpy->AllTotalFileSize - cpy->AllTotalBytesTransferred)/1024 ,cpy->AllTotalFileSize/1024 , (cpy->TotalFileSize - cpy->TotalBytesTransferred)/1024, cpy->TotalBytesTransferred/1024);
                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_COUNT_LABEL),buf);      //      残り個数

                        SetWindowText(GetDlgItem(::hDlg,IDC_CPY_NAME_LABEL),cpy->fname);     //      ファイル名

                        return TRUE;
                }

                case IDC_CPY_START:{    //      コピー開始
                        EnableWindow(GetDlgItem(hDlg,IDC_CPY_START),FALSE);
                        org_icon=GetCursor();
                        SetCursor(wait_icon);
                        src_file_list.clear();
                        GetWindowText(GetDlgItem(hDlg, IDC_SRC_DIR_EDIT), src_def_dir,sizeof(src_def_dir)/sizeof(TCHAR));
                        src_file_list.find(src_def_dir, sizeof(src_def_dir) / sizeof(TCHAR), _TEXT("*.*"));
                        GetWindowText(GetDlgItem(hDlg, IDC_DTC_DIR_EDIT), dtc_def_dir,sizeof(dtc_def_dir)/sizeof(TCHAR));

                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("ファイル数 %i個,フォルダー数 %i個,ファイルサイズ %liByte"),src_file_list.file_num , src_file_list.dir_num , src_file_list.file_size );
                        SetWindowText(GetDlgItem(hDlg, IDC_CPY_COUNT_LABEL) , buf);

                        EnableWindow(GetDlgItem(hDlg,IDC_CPY_PAUSE_BUTTON),TRUE);

                        cpy->pass = src_def_dir + _tcslen(src_def_dir)+1;
                        cpy->AllTotalFileSize = src_file_list.file_size;
                        cpy->AllTotalBytesTransferred = 0;
                        cpy->AllNum = src_file_list.file_num;
                        cpy->AllTotalBytesTransferred = 0;
                        cpy->num = 0;

                        src_file_list.copy_file(dtc_def_dir,sizeof(dtc_def_dir)/sizeof(TCHAR),src_def_dir,sizeof(src_def_dir)/sizeof(TCHAR),CopyProgressRoutine,cpy,&cancel_f,0);

                        EnableWindow(GetDlgItem(hDlg,IDC_CPY_PAUSE_BUTTON),FALSE);
                        EnableWindow(GetDlgItem(hDlg,IDC_CPY_START),TRUE);

                        SetCursor(org_icon);
                        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:  //      終了
                        ::hDlg=0;
                        DestroyWindow(hDlg);
                        PostQuitMessage(0);
                        return FALSE;
                default:
                        return FALSE;
                }
        default:
                return FALSE;
        }
        return TRUE;
}


//      ディレクトリ名を取得するコモンダイアログを表示する
//      GetDir(親ウィンドウのハンドル,初期時選択フォルダー,選択されたフォルダー名);
//      trueが返された場合有効なフォルダー名が取得できた場合

int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);


bool GetDir(HWND hWnd,const TCHAR* def_dir,TCHAR* path){
        BROWSEINFO bInfo;
        LPITEMIDLIST pIDList;

        memset(&bInfo,0,sizeof(bInfo));
        bInfo.hwndOwner = hWnd; // ダイアログの親ウインドウのハンドル 
        bInfo.pidlRoot = NULL; // ルートフォルダをデスクトップフォルダとする 
        bInfo.pszDisplayName = path; //フォルダ名を受け取るバッファへのポインタ 
        bInfo.lpszTitle = TEXT("フォルダの選択"); // ツリービューの上部に表示される文字列 
        bInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_EDITBOX | BIF_VALIDATE | BIF_NEWDIALOGSTYLE; // 表示されるフォルダの種類を示すフラグ 
        bInfo.lpfn = BrowseCallbackProc; // BrowseCallbackProc関数のポインタ 
        bInfo.lParam = (LPARAM)def_dir;
        pIDList = ::SHBrowseForFolder(&bInfo);
        if (pIDList == NULL){
                path[0] = _TEXT('\0');
                return false; //何も選択されなかった場合 
        }
        else{
                if (!SHGetPathFromIDList(pIDList, path))
                        return false;//変換に失敗 
                CoTaskMemFree(pIDList);// pIDListのメモリを開放 
                return true;
        }
}

//      上記ダイアログのコールバック関数

int __stdcall BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData){
    TCHAR dir[MAX_PASS];
    ITEMIDLIST *lpid;
        HWND hEdit;

        switch (uMsg){
        case BFFM_INITIALIZED:  //      ダイアログボックス初期化時
                if(lpData)
                        SendMessage(hWnd, BFFM_SETSELECTION, (WPARAM)TRUE, lpData);     //      コモンダイアログの初期ディレクトリ
                break;
        case BFFM_VALIDATEFAILED:       //      無効なフォルダー名が入力された
                MessageBox(hWnd,(TCHAR*)lParam,_TEXT("無効なフォルダー名が入力されました"),MB_OK);
                hEdit=FindWindowEx(hWnd,NULL,_TEXT("EDIT"),NULL);     //      エディットボックスのハンドルを取得する
                SetWindowText(hEdit,_TEXT(""));
                return 1;       //      ダイアログボックスを閉じない
                break;
        case BFFM_IUNKNOWN:
                break;
        case BFFM_SELCHANGED:   //      選択フォルダーが変化した場合
                lpid=(ITEMIDLIST *)lParam;
                SHGetPathFromIDList(lpid,dir);
                hEdit=FindWindowEx(hWnd,NULL,_TEXT("EDIT"),NULL);     //      エディットボックスのハンドルを取得する
                SetWindowText(hEdit,dir);
                break;
        }
        return 0;
}

file_list.h


#ifndef FILE_LIST_H

#define FILE_LIST_H

//      サブフォルダーを含めたファイルの一覧および比較・コピー等をサポート
//      2014/06/22

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <shlobj.h>
#include <time.h>
#include "list.hpp"


// ファイルコピー中に呼び出される関数の定義
typedef DWORD (CALLBACK  *CpyProgFunc)(LARGE_INTEGER ,  LARGE_INTEGER ,  LARGE_INTEGER ,   LARGE_INTEGER ,  DWORD , DWORD , HANDLE , HANDLE , LPVOID  );


//      ファイルの情報を保存する構造体

struct FILE_LINK{
        friend struct FILE_LIST;
        TCHAR* name;                    //      ファイル名
        FILE_LINK* next;        //      次のファイル名へのポインタ
        FILE_LIST* child;       //      チャイルドフォルダー

        FILETIME ftCreationTime;        // 作成日時 ローカルタイム
    FILETIME ftLastAccessTime;  // 最終アクセス日時 ローカルタイム
    FILETIME ftLastWriteTime;   // 最終更新日時 ローカルタイム
        UINT64 FileSize;        //      ファイルサイズ
        DWORD Attr;     //      ファイル属性
        void* userP;            //      ユーザー用データー保存用
        UINT32 userD;

        FILE_LINK(){
                name = 0;
                next = 0;
                child = 0;
                userP = 0;
                userD = 0;
        }
        FILE_LINK& operator=(WIN32_FIND_DATA& src){
                Attr=src.dwFileAttributes;
                FileSize=(((UINT64)src.nFileSizeHigh)<<32)+src.nFileSizeLow;
                FileTimeToLocalFileTime(&src.ftCreationTime,&ftCreationTime);
                FileTimeToLocalFileTime( &src.ftLastAccessTime,&ftLastAccessTime);
                FileTimeToLocalFileTime( &src.ftLastWriteTime,&ftLastWriteTime);
                return *this;
        }
        INT64 data_cmp(FILE_LINK& src){     //      更新日時の比較
                INT64 d_dtc=*(INT64*)&ftLastWriteTime;
                INT64 d_src=*(INT64*)&src.ftLastWriteTime;
                INT64 a=d_dtc-d_src;
                return a;
        }
        void listview_header(HWND hList){
                LV_COLUMN lvcol;
                lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
            lvcol.fmt = LVCFMT_LEFT;
            lvcol.cx = 128;
            lvcol.pszText = TEXT("名前");
            lvcol.iSubItem = 0;
            ListView_InsertColumn(hList, 0, &lvcol);

            lvcol.cx = 48;
            lvcol.fmt = LVCFMT_RIGHT;
                        lvcol.pszText = TEXT("サイズ");
            lvcol.iSubItem = 1;
            ListView_InsertColumn(hList, 1, &lvcol);

            lvcol.cx = 128;
            lvcol.fmt = LVCFMT_LEFT;
            lvcol.pszText = TEXT("更新日付");
            lvcol.iSubItem = 2;
            ListView_InsertColumn(hList, 2, &lvcol);

            lvcol.cx = 128;
            lvcol.pszText = TEXT("アクセス日付");
            lvcol.iSubItem = 3;
            ListView_InsertColumn(hList, 3, &lvcol);

                        lvcol.cx = 128;
            lvcol.pszText = TEXT("作成日付");
            lvcol.iSubItem = 4;
            ListView_InsertColumn(hList, 4, &lvcol);

        }
        void listview_header2(HWND hList){      
                LV_COLUMN lvcol;
                lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
            lvcol.fmt = LVCFMT_LEFT;
            lvcol.cx = 128;
            lvcol.pszText = TEXT("名前");
            lvcol.iSubItem = 0;
            ListView_InsertColumn(hList, 0, &lvcol);

            lvcol.cx = 48;
            lvcol.fmt = LVCFMT_RIGHT;
                        lvcol.pszText = TEXT("サイズ");
            lvcol.iSubItem = 1;
            ListView_InsertColumn(hList, 1, &lvcol);


            lvcol.cx = 128;
            lvcol.fmt = LVCFMT_LEFT;
            lvcol.pszText = TEXT("src更新日付");
            lvcol.iSubItem = 2;
            ListView_InsertColumn(hList, 2, &lvcol);

            lvcol.cx = 128;
            lvcol.pszText = TEXT("dtc更新日付");
            lvcol.iSubItem = 3;
            ListView_InsertColumn(hList, 3, &lvcol);

            lvcol.cx = 128;
            lvcol.pszText = TEXT("フォルダー");
            lvcol.iSubItem = 4;
            ListView_InsertColumn(hList, 4, &lvcol);
        }
        void listview_add2(HWND hList,int i,TCHAR* pass){
                TCHAR buf[64];
                LV_ITEM item;
                SYSTEMTIME st;

                item.mask = LVIF_TEXT  | LVIF_PARAM;
        item.pszText =name;
        item.iItem = i;
        item.iSubItem = 0;
            item.lParam = i;
        ListView_InsertItem(hList, &item);

                _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%li"),FileSize);
        item.mask = LVIF_TEXT;
                item.pszText = buf;
        item.iItem = i;
        item.iSubItem = 1;
        ListView_SetItem(hList, &item);

                FileTimeToSystemTime(&ftLastWriteTime,&st);     //      FILETIMEをSYSTEMTIMEに変換
                _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%.4i/%.2i/%.2i %.2i:%.2i:%.2i"),(int)st.wYear,(int)st.wMonth,(int)st.wDay,(int)st.wHour,(int)st.wMinute,(int)st.wSecond);
        item.mask = LVIF_TEXT;
                item.pszText = buf;
        item.iItem = i;
        item.iSubItem = 2;
        ListView_SetItem(hList, &item);

                if(userP){
                        FILE_LINK* dtc=(FILE_LINK*)userP;
                        FileTimeToSystemTime(&dtc->ftLastWriteTime,&st);     //      FILETIMEをSYSTEMTIMEに変換
                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%.4i/%.2i/%.2i %.2i:%.2i:%.2i"),(int)st.wYear,(int)st.wMonth,(int)st.wDay,(int)st.wHour,(int)st.wMinute,(int)st.wSecond);
                        item.mask = LVIF_TEXT;
                        item.pszText = buf;
                        item.iItem = i;
                        item.iSubItem = 3;
                        ListView_SetItem(hList, &item);
                }
                item.mask = LVIF_TEXT;
                item.pszText = pass;
                item.iItem = i;
                item.iSubItem = 4;
                ListView_SetItem(hList, &item);

        }
        void listview_add(HWND hList,int i){
                TCHAR buf[64];
                LV_ITEM item;
                SYSTEMTIME st;

                item.mask = LVIF_TEXT  | LVIF_PARAM;
        item.pszText =name;
        item.iItem = i;
        item.iSubItem = 0;
            item.lParam = i;
        ListView_InsertItem(hList, &item);

                _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%li"),FileSize);
        item.mask = LVIF_TEXT;
                item.pszText = buf;
        item.iItem = i;
        item.iSubItem = 1;
        ListView_SetItem(hList, &item);

                FileTimeToSystemTime(&ftLastWriteTime,&st);     //      FILETIMEをSYSTEMTIMEに変換
                _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%.4i/%.2i/%.2i %.2i:%.2i:%.2i"),(int)st.wYear,(int)st.wMonth,(int)st.wDay,(int)st.wHour,(int)st.wMinute,(int)st.wSecond);
        item.mask = LVIF_TEXT;
                item.pszText = buf;
        item.iItem = i;
        item.iSubItem = 2;
        ListView_SetItem(hList, &item);


                FileTimeToSystemTime(&ftLastAccessTime,&st);    //      FILETIMEをSYSTEMTIMEに変換
                _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%.4i/%.2i/%.2i %.2i:%.2i:%.2i"),(int)st.wYear,(int)st.wMonth,(int)st.wDay,(int)st.wHour,(int)st.wMinute,(int)st.wSecond);
        item.mask = LVIF_TEXT;
                item.pszText = buf;
        item.iItem = i;
        item.iSubItem = 3;
        ListView_SetItem(hList, &item);

                FileTimeToSystemTime(&ftCreationTime,&st);      //      FILETIMEをSYSTEMTIMEに変換
                _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%.4i/%.2i/%.2i %.2i:%.2i:%.2i"),(int)st.wYear,(int)st.wMonth,(int)st.wDay,(int)st.wHour,(int)st.wMinute,(int)st.wSecond);
        item.mask = LVIF_TEXT;
                item.pszText = buf;
        item.iItem = i;
        item.iSubItem = 4;
        ListView_SetItem(hList, &item);
        }
};

//      ファイルコピー時に状況を保存する構造体

struct CpyData{
        LONGLONG TotalFileSize; //      コピーされたバイト数
        LONGLONG TotalBytesTransferred; //      ファイルサイズ
        
        LONGLONG AllTotalFileSize;      //      全ファイルのサイズ
        LONGLONG AllTotalBytesTransferred;      //      全ファイルサイズ
        LONGLONG reTotalBytesTransferred;       //      前回呼び出し時のコピーされたファイルのバイト数

        TCHAR* src;     //      コピー元ファイル名
        TCHAR* dtc;     //      コピー先ファイル名       
        TCHAR* pass;    //      コピーファイル名 srcとdtcで共通な名称部分
        TCHAR* fname;   //      コピーファイル名 フォルダー名を含まない
        clock_t start;  //      コピーが開始されたときの時間
        clock_t file_start;     //      ファイルが変更されたときの開始時間

        int     AllNum; //      全ファイルの個数
        int num;        //      コピーされたファイルの個数
};



//      ファイル名を管理する構造体

struct FILE_LIST : public LIST{
        TCHAR* name;    //      親フォルダー名
        long long       file_size;      //      ファイルの合計サイズ数
        FILE_LIST* parent;      //      親フォルダーのFILE_LIST
        int file_num;   //      ファイル数
        int dir_num;    //      フォルダー数

        FILE_LIST() : LIST() {
                name = 0;
                parent = 0;
                file_num=dir_num=0;
                file_size=0;
        }
        LINK* append(FILE_LINK* p){     //      ファイルを追加
                return LIST::append((void*)p);
        }
        FILE_LINK* next(void){  //      次のファイルを検索       
                return (FILE_LINK*)LIST::next();
        }
        FILE_LINK* first(void){ //      最初のファイルを検索
                return (FILE_LINK*)LIST::first();
        }
        int find(TCHAR* pass, int sz, TCHAR* card);     // passで示されるフォルダーのcardで示されるワイルカードに合致するファイルを検索しFILE_LISTに登録する
        FILE_LINK* dupchk(TCHAR* fullpass);     // fullpassがFILE_LISTに登録されているか検索
        void short_puts(TCHAR* pass, int num);  // ファイルの一覧を標準出力に出力する。フォルダー階層ごとにインデントされる。
        void put_listview(HWND hList,int* num);
        void put_listview2(HWND hList,int* num,TCHAR* pass,int sz);
        void full_rename(TCHAR* pass, int num); // フォルダー・ファイル名を全角から半角に変換する
        void put_treeview(HWND hWnd,HTREEITEM t); // フォルダー・ファイル名を全角から半角に変換する
        int cmp(FILE_LIST& dtc,FILE_LIST& src); //      フォルダーdtcとsrcを比較し差分(dtcが更新する必要があるもの)をthisに作成する。
        void cpy(FILE_LIST& src);

        FILE_LINK* find_name(TCHAR* name){      //      チャイルドフォルダーを含まず一致する名前のFILE_LINK*を返す。
                FILE_LINK* p = first();
                while (p){
                        if (_tcscmp(name, p->name) == 0){
                                return p;
                        }
                        p = next();
                }
                return 0;
        }

        void clear(void){       //      FILE_LISTに登録されているフォルダー・ファイルをクリアする。
                FILE_LINK* p = first();
                FILE_LINK* np;
                while (p){
                        np = next();
                        if (p->child){
                                p->child->clear();
                        }
                        delete p->name;
                        delete p;
                        p = np;
                }
                LIST::all_del();
        }
        //      ファイル数・フォルダー数を返す
        void info(int* AllFile,int* NewFile,int* AllDir,int* NewDir);
        //      ファイル・フォルダーの削除
        void del_file(TCHAR* pass,int sz);
        //      ファイル・フォルダーのコピー
        void copy_file(TCHAR* dtc_pass,int dtc_sz,TCHAR* src_pass,int src_sz,LPPROGRESS_ROUTINE func=0, CpyData* cpy=0, LPBOOL pbCancel=FALSE, DWORD dwCopyFlags=0);
};

#endif

file_list.cpp


//      サブフォルダーを含めたファイルの一覧および比較・コピー等をサポート
//      2014/06/22

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

//      ファイル・フォルダーのリストをコピーする

void FILE_LIST::cpy(FILE_LIST& src){
        FILE_LINK* p = src.first();
        FILE_LIST* child_p=0;
        FILE_LINK* dtc_p;
        while (p){
                dtc_p= new FILE_LINK;
                dtc_p->name = p->name;
                dtc_p->next = 0;
                dtc_p->child=0;
                append(dtc_p);
                if(p->child){
                        child_p=new FILE_LIST;
                        dtc_p->child=child_p;
                        child_p->parent=this;
                        dtc_p->child->cpy(*(p->child));        
                }
                p = next();
        }
}

//      フォルダーdtcとsrcを比較し差分(dtcを更新する必要があるもの)をthisに登録する。

int FILE_LIST::cmp(FILE_LIST& dtc,FILE_LIST& src){
        FILE_LINK* src_p = src.first();
        FILE_LINK* dtc_p;
        FILE_LIST* child_p=0;
        FILE_LINK* p;
        int num=0;      //      差分ファイル数
        int child_num;

        while (src_p){
                child_num=0;
                if(&dtc==0)
                        dtc_p=0;
                else
                        dtc_p=dtc.find_name(src_p->name);
                if(dtc_p){      //      同一名のフォルダまたはファイルが存在する
                        if(src_p->child){
                                p= new FILE_LINK;
                                p->name = src_p->name;
                                p->next = 0;
                                p->userP = dtc_p;
                                LINK* lp=append(p);
                                child_p=new FILE_LIST;
                                p->child=child_p;
                                child_p->parent=this;
                                child_num = child_p->cmp(*(dtc_p->child),*(src_p->child));
                                if(child_num==0){       //      差分ファイルが存在しないのでフォルダーをリストから削除する
                                        LIST::del(lp);
                                        delete p;
                                        delete child_p;
                                }
                        }else{  //      存在するファイル
                                if(dtc_p->data_cmp(*src_p)<0){    // *src_pの方が新しいファイルの場合
                                        p= new FILE_LINK;
                                        *p=*src_p;
                                        p->next = 0;
                                        p->child=0;
                                        p->userP = dtc_p;
                                        append(p);
                                        ++num;
                                }
                        }
                }else{  //      同一名のフォルダまたはファイルが存在しないので作成する
                        p= new FILE_LINK;
                        *p=*src_p;
                        p->next = 0;
                        p->child=0;
                        p->userP = 0;
                        append(p);
                        if(src_p->child){
                                child_p=new FILE_LIST;
                                p->child=child_p;
                                child_p->parent=this;
                                FILE_LIST* fp=0;
                                child_num=child_p->cmp(*(fp),*(src_p->child));
                        }else
                                ++num;
                }
                num+=child_num;
                src_p = src.next();
        }
        return num;
}

//      相対パスが登録されているか確認し、登録されていればFILE_LINK*を返す。登録されていなければ0を返す。

FILE_LINK* FILE_LIST::dupchk(TCHAR* fullpass){
        FILE_LIST* flp = this;
        FILE_LINK* p;
        TCHAR* sp = fullpass;
        TCHAR* tp = fullpass;
        FILE_LINK* np;
        while (*sp){
                if (*sp == _T('\\')){
                        sp[0] = _T('\0');
                        p = flp->first();
                        np = 0;
                        while (p){
                                if (_tcscmp(tp, p->name) == 0){
                                        np = p;
                                        flp = p->child;
                                        break;
                                }
                                p = flp->next();
                        }
                        if (np == 0)
                                return 0;
                        tp = sp + 1;
                }
                ++sp;
        }

        p = flp->first();
        while (p){
                if (_tcscmp(tp, p->name) == 0){
                        return p;
                }
                p = flp->next();
        }
        return 0;
}


void FILE_LIST::short_puts(TCHAR* pass, int num){
        FILE_LINK* p = first();
        while (p){
                for (int i = 0; i<num; i++)
                        _tprintf(_TEXT("\t"));
                if (p->child){
                        _tprintf(_TEXT("[%s]\n"), p->name);
                        p->child->short_puts(pass, num + 1);
                }
                else
                        _tprintf(_TEXT("%s\n"), p->name);
                p = next();
        }

}

void FILE_LIST::put_listview(HWND hList,int* num){
        FILE_LINK* p = first();
        while (p){
                if (p->child){
                        p->child->put_listview(hList, num);
                }
                else
                        p->listview_add(hList,(*num)++);
                p = next();
        }

}

//      ファイル数・フォルダー数を返す
void FILE_LIST::info(int* AllFile,int* NewFile,int* AllDir,int* NewDir){
        FILE_LINK* p = first();
        while (p){
                if (p->child){
                        ++(*AllDir);
                        p->child->info( AllFile,NewFile,AllDir,NewDir);
                        if(p->userP==0)
                                ++(*NewDir);
                }
                else{
                        ++(*AllFile);
                        if(p->userP==0)
                                ++(*NewFile);
                }
                p = next();
        }
}

void FILE_LIST::put_listview2(HWND hList,int* num,TCHAR* pass,int sz){
        int len=(int)_tcslen(pass);
        TCHAR* pass_end=pass+len;
        pass_end[0]=_T('\\');
        FILE_LINK* p = first();
        while (p){
                pass_end[1]=_T('\0');
                if (p->child){
                        _tcscpy_s(pass_end+1 , sz-len-1 , p->name);
                        p->child->put_listview2(hList, num,pass,sz);
                }
                else
                        p->listview_add2(hList,(*num)++,pass);
                p = next();
        }
        pass_end[0]=_T('\0');
}

void FILE_LIST::put_treeview(HWND hWnd,HTREEITEM parent){
        HTREEITEM htv;
        TV_INSERTSTRUCT tv;
        memset(&tv,0,sizeof(tv));
        tv.hInsertAfter=TVI_LAST;
        tv.item.mask=TVIF_TEXT;
        tv.hParent=parent;

        FILE_LINK* p = first();
        while (p){
                tv.item.pszText=p->name;
                htv=TreeView_InsertItem(hWnd,&tv);
                if (p->child){
                        p->child->put_treeview(hWnd,htv);
                }else{
                }
                p = next();
        }
}

//      再帰呼び出しによりサブディレクトリ内の指定ファイルを検索する
//      含まれるファイル数を返す。

int FILE_LIST::find(TCHAR* pass, int sz, TCHAR* card){
        WIN32_FIND_DATA FindFileData;
        HANDLE hFind;
        FILE_LIST* child;
        int num = 0;    //      見つかったファイル数
        int dir = 0;    //      フォルダー数
        long long size=0;       //      ファイルサイズ計

        TCHAR* pass_end = pass + _tcslen(pass); //      パス名の最後の位置
        TCHAR* pass_add;        //      パス名の最後の位置(\を含む)

        if (*(pass_end - 1) == _T(':')){        //      ドライブ名のみ指定された場合
                pass_add = pass_end;
        }
        else{   //      フォルダー名が含まれている場合
                pass_add = pass_end + 1;
                pass_end[0] = _T('\\');
        }
        int memsize=(int)(sz - (pass_add - pass + 1));
        if(memsize<0){
                MessageBox(0,_TEXT("ファイル名を保存するのに必要なバッファが確保されていません。"),_TEXT("エラー"),MB_OK);

        }
        _tcscpy_s(pass_add, memsize, _TEXT("*.*"));   //      サブディレクトリの検索

        hFind = FindFirstFile(pass, &FindFileData);
        if (hFind != INVALID_HANDLE_VALUE){
                do{
                        if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
                                if (_tcscmp(FindFileData.cFileName, _TEXT("..")) != 0 && _tcscmp(FindFileData.cFileName, _TEXT(".")) != 0){ //      親ディレクトリ及び現在のディレクトリは無視する
                                        _tcscpy_s(pass_add, sz - (pass_add - pass + 1), FindFileData.cFileName);
                                        size_t len = _tcslen(FindFileData.cFileName);
                                        FILE_LINK* tp = find_name(FindFileData.cFileName);
                                        if (tp == 0){   //      フォルダが登録されていない場合
                                                TCHAR* n = new TCHAR[len + 1];
                                                _tcscpy_s(n, len + 1, FindFileData.cFileName);
                                                FILE_LINK* p = new FILE_LINK;
                                                p->name = n;
                                                p->next = 0;
                                                *p=FindFileData;
                                                append(p);
                                                p->child = new FILE_LIST;
                                                child = p->child;
                                                child->parent = this;        //      親フォルダーを登録
                                                ++dir;
                                        }
                                        else{   //      フォルダがすでに登録されている場合
                                                child = tp->child;
                                        }
                                        num += child->find(pass, int(sz - (pass_add - pass + 1)), card);     //      サブディレクトリの中を検索
                                        size += child->file_size;
                                        dir += child->dir_num;
                                }
                        }
                } while (FindNextFile(hFind, &FindFileData));
        }
        FindClose(hFind);
        memsize=(int)(sz - (pass_add - pass + 1));
        if(memsize<0){
                MessageBox(0,_TEXT("ファイル名を保存するのに必要なバッファが確保されていません。"),_TEXT("エラー"),MB_OK);

        }
        _tcscpy_s(pass_add, memsize, card);
        hFind = FindFirstFile(pass, &FindFileData);
        if (hFind != INVALID_HANDLE_VALUE){

                do{
                        if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
                                FILE_LINK* tp = find_name(FindFileData.cFileName);
                                if (tp == 0){   //ファイルが登録されていないので登録する
                                        ++num;
                                        size_t len = _tcslen(FindFileData.cFileName);
                                        TCHAR* n = new TCHAR[len + 1];
                                        _tcscpy_s(n, len + 1, FindFileData.cFileName);
                                        FILE_LINK* p = new FILE_LINK;
                                        p->name = n;
                                        p->next = 0;
                                        p->child = 0;
                                        *p=FindFileData;
                                        size += p->FileSize;
                                        append(p);
                                }
                        }
                } while (FindNextFile(hFind, &FindFileData));
                pass_end[0] = _T('\0');
                FindClose(hFind);
                file_size = size;
                dir_num = dir;
                return file_num=num;
        }
        else{
                pass_end[0] = _T('\0');
                FindClose(hFind);
                file_size = size;
                dir_num = dir;
                return file_num=num;
        }
}

void FILE_LIST::del_file(TCHAR* pass,int sz){
        int len=(int)_tcslen(pass);
        TCHAR* pass_end=pass+len;
        pass_end[0]=_T('\\');
        FILE_LINK* p = first();
        while (p){
                pass_end[1]=_T('\0');
                _tcscpy_s(pass_end+1, sz-len-1 , p->name);
                if (p->child){
                        p->child->del_file(pass,sz);
                        if(p->userP==0)
                                RemoveDirectory(pass);
                }
                else{
                        if(p->userP==0)
                                DeleteFile(pass);
                }
                p = next();
        }
        pass_end[0]=_T('\0');
}


//      ファイル・フォルダーのコピー
//      TCHAR* dtc_pass;        コピー先フォルダー フォルダーに含まれるフォルダーおよびファイル名に必要な領域が確保されている必要がある
//      int dtc_sz;                     上記領域のサイズ
//      TCHAR* src_pass;        コピー元フォルダー フォルダーに含まれるフォルダーおよびファイル名に必要な領域が確保されている必要がある
//      int src_sz;                     上記領域のサイズ
//      LPPROGRESS_ROUTINE func,;       コピー状況を受け取る関数
//      CpyData* cpy;   コピー状況を格納する構造体
//      LPBOOL pbCancel;        コピーをキャンセルする場合TRUEにセットする CopyFileEx API参照
//      DWORD dwCopyFlags;      CopyFileEx API参照


void FILE_LIST::copy_file(TCHAR* dtc_pass,int dtc_sz,TCHAR* src_pass,int src_sz,LPPROGRESS_ROUTINE func, CpyData* cpy, LPBOOL pbCancel, DWORD dwCopyFlags){
        int src_len=(int)_tcslen(src_pass);
        TCHAR* src_pass_end=src_pass+src_len;
        src_pass_end[0]=_T('\\');

        int dtc_len=(int)_tcslen(dtc_pass);
        TCHAR* dtc_pass_end=dtc_pass+dtc_len;
        dtc_pass_end[0]=_T('\\');


        FILE_LINK* p = first();
        while (p){

                if(*pbCancel==TRUE)
                        return;
                src_pass_end[1]=_T('\0');
                _tcscpy_s(src_pass_end+1,src_sz-src_len-1,p->name);
                dtc_pass_end[1]=_T('\0');
                _tcscpy_s(dtc_pass_end+1,dtc_sz-dtc_len-1,p->name);

                if (p->child){
                        if(CreateDirectory(dtc_pass,NULL)==0){  //      フォルダー作成

                        }
                        
                        p->child->copy_file(dtc_pass,dtc_sz,src_pass,src_sz,func,cpy,pbCancel,dwCopyFlags);
                }
                else{
                        cpy->fname=cpy->pass;
                        cpy->src=src_pass;
                        cpy->dtc=dtc_pass;
                        CopyFileEx(src_pass,dtc_pass,func,cpy,pbCancel,dwCopyFlags);
                        cpy->num++;
                }
                p = next();
        }
        src_pass_end[0]=_T('\0');
        dtc_pass_end[0]=_T('\0');
}

list.hpp


//      2014/06/09


#ifndef LIST_HPP
#define LIST_HPP 1


class LINK{
        LINK* next;
        LINK* back;
        void* data;
protected:
        LINK(){
                next=0;
                back=0;
                data=0;
        }
        void set(void* d){
                data=d;
        }
        friend class LIST;
        friend int cmp(const void* d,const void* s){
                if((char*)d>(char*)s)
                        return 1;
                if((char*)d<(char*)s)
                        return -1;
                return 0;
                
        }
};

typedef void* LPVOID;

class LIST{
        LINK* top;      //      リストの先頭
        LINK* end;      //      リストの最後尾
        LINK* pos;      //      リストのカレントポジション
        int max;        //      リストの要素数
protected:
        LIST(){
                pos=top=end=0;
                max=0;
        }
        LINK* append(void* d){  //      リストの最後尾にデータを追加
                LINK* p=new LINK;
                ++max;
                p->data=d;
                if(top==0){
                        pos=top=end=p;
                }else{
                        end->next=p;
                        p->back=end;
                        end=p;
                }
                return p;
        }
        void* next(void){       //      カレントポジジョンからデータを得た後、カレントポジションを1個進める。
                LINK* t=pos;
                if(pos==0){
                        return 0;
                }else{
                        pos=pos->next;
                        return t->data;
                }
        }
        void* first(void){      //      カレントポジションを先頭に移動させ先頭データを得た後、カレントポジションを1個進める。
                pos=top;
                return next();
        }
        void all_del(void){     //      リスト全部を削除する
                while(top){
                        LINK* t=top->next;
                        delete top;
                        top=t;
                }
                top=end=pos=0;
                max=0;
        }
        void repeat_del(int(*cmp )(const void* d,const void* s)){       //      ソートされたリストから重複する要素を削除
                LINK* lp=top;
                if(lp){
                        LINK* bp=0;
                        while(lp){
                                if(bp){
                                        if( (*cmp)((void**)&lp->data,(void**)&bp->data) ){        //      前の要素と異なる場合
                                                bp=lp;
                                        }else{  //      前の要素と同じ場合
                                                LINK* t=lp->next;
                                                del(lp);
                                                --max;
                                                lp=t;
                                                continue;
                                        }
                                }else
                                        bp=lp;
                                lp=lp->next;
                        }
                }
        }       
        void qsort(int(*cmp )(const void* d,const void* s)){
                if(max){
                        int n=0;
                        LPVOID* vec=new LPVOID[max];    //      リストの要素を配列にコピーする
                        if(vec==0)
                                return;
                        void* p;
                        p=first();
                        while(p){
                                vec[n++]=p;
                                p=next();
                        }
                        ::qsort((void*)vec,max,sizeof(LPVOID),cmp);
                        LINK* lp=top;                                           //      配列をリストにコピー
                        n=0;
                        while(lp){
                                lp->set(vec[n++]);
                                lp=lp->next;
                        }
                        delete vec;
                }
        }
        void del(LINK*  p){     // リストからpを削除する
                if(p){
                        --max;
                        if(p==top){     //      pは先頭である
                                LINK* t=top;
                                top=top->next;
                                if(top){
                                        top->back=0;
                                        if(pos==p){
                                                pos=p->next;
                                        }
                                }else
                                        end=pos=0;
                                delete p;
                                return;
                        }
                        if(p==end){
                                LINK* t=end;
                                end=end->back;
                                end->next=0;
                                delete p;
                                return;
                        }
                        LINK* t=p;
                        p->back->next=p->next;
                        p->next->back=p->back;
                        if(pos==p){
                                pos=p->next;
                        }
                        delete t;
                }

        }
public:
        int get_max(void){
                return max;
        }
};

#endif

resource.h


#define IDC_SRC_DIR_EDIT 2010
#define IDC_SRC_DIR_BUTTON 2020
#define IDC_DTC_DIR_EDIT 2110
#define IDC_DTC_DIR_BUTTON 2120

#define IDC_CPY_NAME_LABEL      2210
#define IDC_CPY_TIME_LABEL      2220
#define IDC_CPY_COUNT_LABEL     2230
#define IDC_CPY_SPEED_LABEL     2240
#define IDC_CPY_PROGBAR         2250

#define IDC_CPY_START 2310
#define IDC_CPY_PAUSE_BUTTON    2320
#define IDC_CPY_CHG 2330

resource.rc


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


DLG1 DIALOG DISCARDABLE  0, 0, 380, 257
STYLE DS_MODALFRAME | WS_POPUP
CAPTION "フォルダーコピー"
FONT 9, "MS Pゴシック"
BEGIN
        
        CONTROL "コピー元フォルダー", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 12 , 96, 18
        CONTROL "参照(&B)", IDC_SRC_DIR_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 110,7 , 50, 14
        CONTROL "",IDC_SRC_DIR_EDIT, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL, 14, 30 , 353, 15

        CONTROL "コピー先フォルダー", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 59 , 96, 18
        CONTROL "参照(&B)", IDC_DTC_DIR_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 110,59 , 50, 14
        CONTROL "",IDC_DTC_DIR_EDIT, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL, 14, 86 , 353, 15

        CONTROL "名前", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 127, 192, 12
        CONTROL "", IDC_CPY_NAME_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 127, 319, 12

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

        CONTROL "残りの項目", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 165, 192, 12
        CONTROL "", IDC_CPY_COUNT_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 165, 319, 12

        CONTROL "スピード", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 14, 184, 192, 12
        CONTROL "", IDC_CPY_SPEED_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 80, 184, 319, 12

        CONTROL "ProgressBar", IDC_CPY_PROGBAR, "MSCTLS_PROGRESS32", WS_CHILD | WS_VISIBLE | WS_BORDER, 14, 203, 352, 12

        CONTROL "コピー(&C)", IDC_CPY_START, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 209, 236, 50, 14
        CONTROL "一時停止(&P)", IDC_CPY_PAUSE_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 266, 236, 50, 14
        CONTROL "終了(&Q)", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 323, 236, 50, 14

END

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