File List を Copy & Paste する



Windows エクスプローラと相互に File List を Copy & Paste します。
クリップボードの扱い方が Copy & Paste と同じなので「Clipboard のプログラム」と合わせて参照して下さい。
Windows 98,ME で実行するときは WriteClip() 関数の第四引数を FALSE にして下さい。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. 新規プロジェクト(ListCopy)で空のプロジェクトを作成して下さい。
    ページ先頭の画面を参考にして DialogBox に[EditControl]と[ListBox]と[Button]を配置して下さい。
    キャプションとIDを次のように設定して下さい。(ID は規定値のままです)
    複数の項目を選択するので、ListBox のプロパティから「マルチ」を設定して下さい。
    BOX キャプション ID
    DialogBox Dialog Folder List DIALOG1
    EditControl IDC_EDIT1
    ListBox IDC_LIST1
    Button Read File List IDC_READ
    Button Write File List IDC_WRITE
    Button OK IDOK
  2. SHLOBJ.H を取り込んで下さい。
    Cnt は選択されたファイル数を格納する領域です。
    Fnum[20] は、選択されたファイル番号(ITEM 番号)を格納する領域で、最大20個に制限されています。
    Fname[20][MAX_PATH] は、選択されたファイル名を格納する領域です。
        #include <windows.h>
        #include <SHLOBJ.H>
        #include "resource.h"
    
        int     Cnt;
        int     Fnum[20];
        char    Fname[20][MAX_PATH];
        
  3. 関数の Prototype 宣言です。
    ReadClip() でクリップボードに格納されているファイルリストを ListBox に追加します。
    SetFileName() で ListBox で選択されているファイル名を取得して WriteClip() を呼びます。
    WriteClip() でクリップボードに書き込みます。
        LRESULT CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
        int     ReadClip(HWND hdlg);
        int     WriteClip(HWND hdlg, char FileName[][MAX_PATH], int cnt, BOOL fWide);
        int     SetFileName(HWND hdlg);
        
  4. ボタンがクリックされたときの処理です。
            case IDC_READ:
                ReadClip(hDlg);
                break;
            case IDC_WRITE:
                SetFileName(hDlg);
                break;
            case IDOK:
                EndDialog( hDlg, IDCANCEL );
                break;
        
  5. クリップボードから読み出す関数です。
    エクスプローラでコピーされたファイルリストは CF_HDROP 形式でクリップボードに格納されています。
    OpenClipboard() でクリップボードをオープンします。
    DragQueryFile(Drop,0XFFFFFFFF,NULL,0) で格納されているファイル数を取得します。
    DragQueryFile(Drop,i,buf,256) で i 番目のファイル名を取得します。
    SendMessage() で ListBox に追加します。
        //★ クリップボードから読み出す
        int ReadClip(HWND hdlg)
        {   HDROP   Drop;
            int     i,j;
            int     fcnt;
            char    buf[256],fname[64];
    
            OpenClipboard(hdlg);
            Drop = (HDROP)GetClipboardData(CF_HDROP);
            if (Drop == NULL)
            {   MessageBox(hdlg,"Clipbord Open Error","Copy & Paste", MB_OK);
                return -1;
            }
    
            fcnt= DragQueryFile(Drop,0XFFFFFFFF,NULL,0);    //ファイル数を取得
            for(i=0; i<fcnt; i++)
            {   DragQueryFile(Drop,i,buf,256);              //ファイル名を取得
                //MessageBox(hdlg,buf,"Copy File Name", MB_OK);
                for(j=strlen(buf-1); j>=0 && buf[j]!='\\' && buf[j]!=':'; j--);
                strcpy(fname,buf+j+1);
                SendMessage(GetDlgItem(hdlg,IDC_LIST1),LB_INSERTSTRING,0,(LPARAM)fname);  
            }
            CloseClipboard();
            InvalidateRect(hdlg, NULL, TRUE);
            return 0;
        }
        
  6. 選択された複数の ITEM 番号を取得してファイル名を配列に格納する関数です。
    SendMessage(..., LB_GETSELITEMS, ...) で選択された複数の ITEM 番号を取得します。
    SendMessage(..., LB_GETTEXT, ...) で ITEM 番号からファイル名を取得して配列に格納します。
    WriteClip(hdlg,Fname,Cnt,TRUE) でクリップボードに格納します。
        //★ 選択されたファイル名を取り出してクリップボードにコピーする
        int SetFileName(HWND hdlg)
        {   int i;
    
            Cnt= SendMessage(GetDlgItem(hdlg,IDC_LIST1),LB_GETSELITEMS,(WPARAM)20,(LPARAM)Fnum);
            if (Cnt==LB_ERR)
            {   MessageBox(hdlg,"Selct File Error","Copy & Paste", MB_OK);
                return -1;
            }
            for(i=0; i<Cnt; i++)
            {   SendMessage(GetDlgItem(hdlg,IDC_LIST1),LB_GETTEXT,(WPARAM)i,(LPARAM)Fname[i]);
            }
            if (WriteClip(hdlg,Fname,Cnt,TRUE)==0)
                MessageBox(hdlg,"Copy to Clipbord successful","Copy & Paste", MB_OK);
            return 0;
        }
        
  7. クリップボードに書きこむ関数です。
    エクスプローラに渡すには CF_HDROP 形式でクリップボードに書き込みます。
    CF_HDROP のメモリイメージ形式は LPDROPFILES 構造体とその後に続くファイルリストで構成します。
    領域のサイズを計算して GlobalAlloc() でメモリ領域を割り当てます。
    GlobalLock() でバッファのポインタを取得します。
    LPDROPFILES 構造体を設定して、その後にファイルリストを格納します。
    GlobalUnlock() でバッファのロックを解除します。
    OpenClipboard() でクリップボードをオープンします。
    EmptyClipboard() でクリップボードを空にします。
    SetClipboardData() でクリップボードにハンドルを設定します。
    CloseClipboard() でクリップボードをクローズします。
        //★ クリップボードに書き込む
        int WriteClip(HWND hdlg, char FileName[][MAX_PATH], int cnt, BOOL fWide)
        {   HDROP       hDrop;
            LPDROPFILES lpDropFile;
            int         wtotal, btotal, wlen;
            int         i;
    
            if (fWide)
            {   //ワイドキャラ
                wtotal = 0;
                for(i = 0; i < cnt; i++)
                {   wtotal += MultiByteToWideChar(CP_ACP, 0, FileName[i], -1, NULL, 0);
                }
                btotal = wtotal * sizeof(wchar_t);
            }
            else
            {   //マルチバイト
                btotal = 0;
                for(i = 0;i < cnt;i++)
                {   btotal += ::lstrlen(FileName[i]) + 1;
                }
            }
    
            hDrop = (HDROP)GlobalAlloc(GHND,sizeof(DROPFILES)+btotal+2);
            if (hDrop==NULL)
            {   MessageBox(hdlg,"GlobalAlloc Error","Copy & Paste", MB_OK);
                return -1;
            }
    
            lpDropFile = (LPDROPFILES)::GlobalLock(hDrop);
            lpDropFile->pFiles = sizeof(DROPFILES);     // ファイル名のリストまでのオフセット
            lpDropFile->pt.x = 0;
            lpDropFile->pt.y = 0;
            lpDropFile->fNC = FALSE;
            lpDropFile->fWide = fWide;                  // ワイドキャラの場合は TRUE
    
            // 構造体の後にファイル名のリストをコピー(ファイル名\0ファイル名\0ファイル名\0\0\0)
            if (fWide)
            {   // ワイドキャラ
                wchar_t *buf;
                buf = (wchar_t *)(&lpDropFile[1]);
                for(i = 0; i < cnt; i++)
                {   wlen = MultiByteToWideChar(CP_ACP, 0, FileName[i], -1, buf, wtotal);
                    wtotal -= wlen;
                    buf += wlen;
                }
                *buf = 0;
            }
            else
            {   // マルチバイト
                char *buf;
                buf = (char *)(&lpDropFile[1]);
                for(i = 0; i < cnt; i++)
                {   lstrcpy(buf,FileName[i]);
                    buf += lstrlen(FileName[i]) + 1;
                }
                *buf++ = 0;
                *buf = 0;
            }
            GlobalUnlock(hDrop);
     
            if (OpenClipboard(hdlg) == 0)
            {   GlobalFree(hDrop);
                MessageBox(hdlg,"Clipbord Open Error","Copy & Paste", MB_OK);
                return -1;
            }
            EmptyClipboard();
            SetClipboardData(CF_HDROP, hDrop);
            CloseClipboard();
            return  0;
        }
        
  8. CF_HDROP のメモリイメージ形式は LPDROPFILES 構造体とその後に続くファイルリストで構成します。
    SHLOBJ.H で宣言されている LPDROPFILES 構造体です。
          typedef struct _DROPFILES {
            DWORD pFiles;                       // offset of file list
            POINT pt;                           // drop point (client coords)
            BOOL fNC;                           // is it on NonClient area
                                                // and pt is in screen coords
            BOOL fWide;                         // WIDE character switch(UNI-CODE など)
          } DROPFILES, FAR * LPDROPFILES;
        
  9. ファイルリストは、昔はマルチバイトで格納されていましたが、最近ではワイドキャラが使われているようです。 (^_^;
    マルチバイトでは半角文字は1バイトで、全角文字は2バイトで構成されますが、ワイドキャラでは全ての文字を2バイトで格納します。
    MultiByteToWideChar() がマルチバイトとワイドキャラを変換する関数です。
    Windows 98,ME で実行するときは WriteClip() 関数の第四引数を FALSE(マルチバイト) に設定して下さい。
    上位互換を保つために WindowsXP でもマルチバイトが扱えるようになっていました。
  10. [ビルド]から[実行]を選択するとビルドに続いて、エラーが無ければプログラムが実行されます。
    サブフォルダーをダブルクリックすることにより、表示するフォルダーを切り替えることができます。
    エクスプローラで幾つかのファイルを選択して、右クリックから「コピー(C)」を実行して下さい。
    このプログラムに戻って Read File List ボタンをクリックすると選択されたファイルが ListBox に追加されます。
    ただし ListBox に追加されるだけで、ファイルが実際にコピーされる訳ではありません。
  11. 表示されているリストから幾つかのファイルを選択して、Write File List ボタンをクリックして下さい。
    エクスプローラ上で右クリックから「貼り付け」を選択するとファイルが List に追加されます。
    こちらはファイルが実際にコピーされるので、不要であれば削除して下さい。

【演習】

課題1

Windows エクスプローラと相互に Copy & Paste を実行してみて下さい。

課題2

エクスプローラからクリップボードに Copy したファイル名を取得して、実際にファイルを格納してみて下さい。

超初心者のプログラム入門(Win32API C++)