ファイルの読み書き:Drag & Drop

今回はドラッグ・アンド・ドロップで、ファイルの読み込みをします。

ファイルの読み書き2:コモンダイアログ - インコのWindowsSDK で作成したソースを修正します。

流れ

WM_CREATE

1.WM_DROPFILESメッセージを処理するようにする[DragAcceptFiles()]

VOID DragAcceptFiles(
    HWND hWnd,   // ウィンドウのハンドル
    BOOL fAccept // 受け入れの有無
);

fAcceptがtrueのときは、ファイルがドロップされたときにWM_DROPFILESメッセージが送られてくるようになります。

WM_DROPFILES

2.WndProc()にファイルをドロップされたときの処理[WM_DROPFILES]を追加

3.DragQueryFile()で、ドロップされたファイル数を取得。

UINT DragQueryFile(
    HDROP hDrop,     // ファイル名構造体のハンドル
    UINT iFile,      // 取得するファイルのインデックス番号
    LPTSTR lpszFile, // 取得したファイル名を格納するバッファ
    UINT cch         // 取得したファイル名を格納するバッファのサイズ
);

・hDrop:WM_DROPFILES メッセージ発生時の wParam パラメータ[WndProc()の第3引数]。
・iFileを-1にすることにより、戻り値がドロップされたファイル数になります。

4.ファイル数が2以上のときは、エラー表示をして、以下5と6の処理はしない。

5.DragQueryFile()で、ドロップされたファイル名を取得。

6. ファイルの読み書き1 - インコのWindowsSDK で行ったように、ファイルを開く処理をする。

7.ファイル名を転送するためにシステムが割り当てたメモリを解放[DragFinish()]

VOID DragFinish(
    HDROP hDrop // ファイル名構造体のハンドル
);

ソースコードの入力

ソースコードは下記のように入れてください。

test.cpp
#include <windows.h>
#include <richedit.h>
#include "resource.h"

#define TEXTMAX 10000

// グローバル変数:
HINSTANCE hInst;                          // 現在のインターフェイス
// リッチエディットの使用するために用いる変数宣言
TCHAR strPath[MAX_PATH + 1];    // DLLのパス
HINSTANCE hRtLib;               // インスタンスハンドル
HWND hRichEdit;                 // ウィンドウハンドル

// このコード モジュールに含まれる関数の宣言を転送します:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
DWORD CALLBACK MySaveProc(DWORD, LPBYTE, LONG, LONG *);
DWORD CALLBACK MyReadProc(DWORD, LPBYTE, LONG, LONG *);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
    MSG msg;
    MyRegisterClass(hInstance);

    // アプリケーションの初期化を実行します:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    // メイン メッセージ ループ:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int) msg.wParam;
}

//
//  関数: MyRegisterClass()
//
//  目的: ウィンドウ クラスを登録します。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL , IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = MAKEINTRESOURCE(IDC_HP);
    wcex.lpszClassName = TEXT("HP");
    wcex.hIconSm = LoadIcon(NULL , IDI_APPLICATION);

    return RegisterClassEx(&wcex);
}

//
//   関数: InitInstance(HINSTANCE, int)
//
//   目的: メイン ウィンドウを作成します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // グローバル変数にインスタンス処理を格納します。

   hWnd = CreateWindow(TEXT("HP"), TEXT("HP"), WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:  メイン ウィンドウのメッセージを処理します。
//
//  WM_CREATE  - ウインドウ作成時の処理
//  WM_SIZE    - ウインドウサイズ変更時の処理
//  WM_DROPFILES - ファイルをドロップされたときの処理
//  WM_COMMAND - アプリケーション メニューの処理
//  WM_DESTROY - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    HDROP hDrop;
    UINT uFileNo;
    OPENFILENAME of; // OPENFILENAME構造体
    TCHAR szText[TEXTMAX] = TEXT("");
    static TCHAR szFile[MAX_PATH] = TEXT("");
    HANDLE hFile;
    EDITSTREAM eds; // EDITSTREAM構造体

    switch (message)
    {
        case WM_CREATE:
            // DLLのロード
            GetSystemDirectory(strPath , MAX_PATH + 1);
            wsprintf(strPath, TEXT("%s\\%s"), strPath, TEXT("RICHED20.DLL"));
            hRtLib = LoadLibrary((LPCTSTR)strPath);

            hRichEdit = CreateWindowEx(WS_EX_CLIENTEDGE, // 拡張ウィンドウスタイル
                TEXT("RichEdit20A"), // 登録されているクラス名
                TEXT(""),            // ウィンドウ名
                WS_CHILD             // 子ウィンドウを作成
                | WS_VISIBLE         // 初期状態で可視のウィンドウを作成
                | WS_BORDER          // 境界を持つウィンドウを作成
                | ES_MULTILINE       // 複数行入力
                | WS_HSCROLL         // 水平スクロールバーを持つウィンドウ
                | WS_VSCROLL         // 垂直スクロール バーを持つウィンドウ
                | ES_AUTOVSCROLL     // 自動縦スクロール(ES_MULTILINE のときのみ有効)
                | ES_NOHIDESEL,      // 選択されているテキストは、コントロールが...
                                     //  フォーカス持っていない場合も反転表示
                0,                   // ウィンドウの横方向の位置
                0,                   // ウィンドウの縦方向の位置
                0,                   // ウィンドウの幅
                0,                   // ウィンドウの高さ
                hWnd,                // 親ウィンドウまたはオーナーウィンドウのハンドル
                (HMENU)IDC_RICHEDIT, // メニューハンドルまたは子識別子
                hInst,               // アプリケーションインスタンスのハンドル
                NULL);               // ウィンドウ作成データ
            // WM_DROPFILESメッセージを処理するようにする
            DragAcceptFiles(hWnd, TRUE);
            break;
        case WM_SIZE:
            MoveWindow(hRichEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;
        case WM_DROPFILES:
            hDrop = (HDROP)wParam;
            // ドロップされたファイル数を取得
            uFileNo = DragQueryFile((HDROP)wParam, -1, NULL, 0);
            if(uFileNo > 1)
            {
                MessageBox(hWnd, TEXT("ファイルを開けませんでした"), TEXT("失敗"), MB_OK);
            }
            else
            {
                DragQueryFile(hDrop, 0, szFile, sizeof(szFile));
                hFile = CreateFile(szFile, // ファイル名
                    GENERIC_READ,          // アクセス指定 GENERIC_READ:読み取り
                    0,                     // 共有方法 0:共有しない
                    NULL,                  // セキュリティ属性 0:デフォルト
                    OPEN_EXISTING,         // 動作指定 OPEN_EXISTING:ファイルをオープン。
                                           //  ファイルが存在していない場合、
                                           //  関数が失敗
                    FILE_ATTRIBUTE_NORMAL, // フラグと属性
                                           // FILE_ATTRIBUTE_NORMAL:属性設定なし
                    NULL);                 // テンプレートファイル
                if (hFile != INVALID_HANDLE_VALUE)
                {
                    eds.dwCookie = (DWORD)hFile; // ファイルハンドルを渡す
                    eds.dwError = 0; // 読み込み操作の結果 0はエラーなし
                    eds.pfnCallback = MyReadProc; // コールバック関数アドレス
                    SendMessage(hRichEdit,
                        EM_STREAMIN,   // 読み込み
                        SF_TEXT,       // テキスト形式
                        (LPARAM)&eds); // EDITSTREAM 構造体のアドレス
                    SendMessage(hRichEdit,
                        EM_SETMODIFY,  // エディットコントロールの変更フラグを設定または解除
                        (WPARAM)FALSE, // 解除
                        0);
                    CloseHandle(hFile);
                    // ウインドウタイトルをファイル名にする
                    SetWindowText(hWnd, (LPCTSTR)szFile);
                    // メニューの選択可
                    EnableMenuItem(GetMenu(hWnd), IDM_SAVE, MF_ENABLED);
                }
            }
            DragFinish(hDrop);
            break;
        case WM_COMMAND:
            wmId = LOWORD(wParam);
            wmEvent = HIWORD(wParam);
            // 選択されたメニューの解析:
            switch (wmId)
            {
                case IDM_OPEN:
                    // OPENFILENAME構造体のサイズをセット
                    memset(&of, 0, sizeof(OPENFILENAME));
                    of.lStructSize = sizeof(OPENFILENAME);
                    // ダイアログボックスを所有するウィンドウへのハンドル
                    of.hwndOwner = hRichEdit;
                    of.lpstrFilter = TEXT("TEXT(*.txt)\0 *.txt\0RTF(*.rtf)\0 *.rtf\0\0");
                    // ファイル名を格納したバッファのアドレス
                    of.lpstrFile = (LPTSTR)szFile; 
                    // lpstrFileメンバで指定されるバッファのサイズ
                    of.nMaxFile = MAX_PATH;
                    // ダイアログボックスの初期化に使用されるビットフラグ
                    // OFN_PATHMUSTEXIST:ユーザーが有効なパス
                    //  およびファイル名のみを入力できるように指定
                    of.Flags = OFN_PATHMUSTEXIST;
                    // デフォルトの拡張子を格納したバッファのアドレス
                    of.lpstrDefExt = TEXT("txt");
                    // コモンダイアログの表示
                    GetOpenFileName( &of );
                    hFile = CreateFile(szFile, // ファイル名
                        GENERIC_READ,          // アクセス指定 GENERIC_READ:読み取り
                        0,                     // 共有方法 0:共有しない
                        NULL,                  // セキュリティ属性 0:デフォルト
                        OPEN_EXISTING,         // 動作指定 OPEN_EXISTING:ファイルをオープン。
                                               //  ファイルが存在していない場合、
                                               //  関数が失敗
                        FILE_ATTRIBUTE_NORMAL, // フラグと属性
                                               // FILE_ATTRIBUTE_NORMAL:属性設定なし
                        NULL);                 // テンプレートファイル
                    if (hFile != INVALID_HANDLE_VALUE)
                    {
                        eds.dwCookie = (DWORD)hFile; // ファイルハンドルを渡す
                        eds.dwError = 0; // 読み込み操作の結果 0はエラーなし
                        eds.pfnCallback = MyReadProc; // コールバック関数アドレス
                        SendMessage(hRichEdit,
                            EM_STREAMIN,   // 読み込み
                            SF_TEXT,       // テキスト形式
                            (LPARAM)&eds); // EDITSTREAM 構造体のアドレス
                        SendMessage(hRichEdit,
                            EM_SETMODIFY,  // エディットコントロールの変更フラグを設定または解除
                            (WPARAM)FALSE, // 解除
                            0);
                        CloseHandle(hFile);
                        // ウインドウタイトルをファイル名にする
                        SetWindowText(hWnd, (LPCTSTR)szFile);
                        // メニューの選択可
                        EnableMenuItem(GetMenu(hWnd), IDM_SAVE, MF_ENABLED);
                    }
                    break;
                case IDM_SAVE:
                    hFile = CreateFile(szFile,    // ファイル名
                        GENERIC_WRITE,            // アクセス指定 GENERIC_WRITE:書き込み
                        0,                        // 共有方法 0:共有しない
                        NULL,                     // セキュリティ属性 0:デフォルト
                        OPEN_ALWAYS,              // 動作指定 OPEN_ALWAYS:ファイルをオープン。
                                                  //  ファイルが存在していない場合、
                                                  //  新しいファイルを作成
                        FILE_ATTRIBUTE_NORMAL     // フラグと属性
                                                  //  FILE_ATTRIBUTE_NORMAL:属性設定なし
                        | FILE_FLAG_WRITE_THROUGH,// データをそのまま直接ディスクに書き込む
                        NULL);                    // テンプレートファイル
                    eds.dwCookie = (DWORD)hFile; // ファイルハンドルを渡す
                    eds.dwError = 0; // 書き込み操作の結果 0はエラーなし
                    eds.pfnCallback = MySaveProc; // コールバック関数アドレス
                    SendMessage(hRichEdit,
                        EM_STREAMOUT,    // 書き込み
                        (WPARAM)SF_TEXT, // テキスト形式
                        (LPARAM)&eds); // EDITSTREAM 構造体のアドレス
                    SendMessage(hRichEdit,
                        EM_SETMODIFY,  // エディットコントロールの変更フラグを設定または解除
                        (WPARAM)FALSE, // 解除
                        0);
                    CloseHandle(hFile);
                    break;
                case IDM_SAVE2:
                    // OPENFILENAME構造体のサイズをセット
                    memset(&of, 0, sizeof(OPENFILENAME));
                    of.lStructSize = sizeof(OPENFILENAME);
                    // ダイアログボックスを所有するウィンドウへのハンドル
                    of.hwndOwner = hRichEdit;
                    of.lpstrFilter = TEXT("TEXT(*.txt)\0 *.txt\0RTF(*.rtf)\0 *.rtf\0\0");
                    // ファイル名を格納したバッファのアドレス
                    of.lpstrFile = (LPTSTR)szFile; 
                    // lpstrFileメンバで指定されるバッファのサイズ
                    of.nMaxFile = MAX_PATH;
                    of.Flags = OFN_OVERWRITEPROMPT;
                    // デフォルトの拡張子を格納したバッファのアドレス
                    of.lpstrDefExt = TEXT("txt");
                    // コモンダイアログの表示
                    GetSaveFileName( &of );
                    hFile = CreateFile(szFile,    // ファイル名
                        GENERIC_WRITE,            // アクセス指定 GENERIC_WRITE:書き込み
                        0,                        // 共有方法 0:共有しない
                        NULL,                     // セキュリティ属性 0:デフォルト
                        OPEN_ALWAYS,              // 動作指定 OPEN_ALWAYS:ファイルをオープン。
                                                  //  ファイルが存在していない場合、
                                                  //  新しいファイルを作成
                        FILE_ATTRIBUTE_NORMAL     // フラグと属性
                                                  //  FILE_ATTRIBUTE_NORMAL:属性設定なし
                        | FILE_FLAG_WRITE_THROUGH,// データをそのまま直接ディスクに書き込む
                        NULL);                    // テンプレートファイル
                    eds.dwCookie = (DWORD)hFile; // ファイルハンドルを渡す
                    eds.dwError = 0; // 書き込み操作の結果 0はエラーなし
                    eds.pfnCallback = MySaveProc; // コールバック関数アドレス
                    SendMessage(hRichEdit,
                        EM_STREAMOUT,    // 書き込み
                        (WPARAM)SF_TEXT, // テキスト形式
                        (LPARAM)&eds); // EDITSTREAM 構造体のアドレス
                    SendMessage(hRichEdit,
                        EM_SETMODIFY,  // エディットコントロールの変更フラグを設定または解除
                        (WPARAM)FALSE, // 解除
                        0);
                    CloseHandle(hFile);
                    break;
                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    return 0;
}

DWORD CALLBACK MySaveProc(DWORD dwCookie, LPBYTE pbbuf, LONG cb, LONG *pcb)
{
    if (WriteFile((HANDLE)dwCookie, pbbuf, cb, (LPDWORD)pcb, NULL))
    {
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}

DWORD CALLBACK MyReadProc(DWORD dwCookie, LPBYTE pbBuf, LONG cb, LONG *pcb)
{
    ReadFile((HANDLE)dwCookie, pbBuf, cb, (LPDWORD)pcb, NULL);
    return(FALSE);
}

上記の太線で示している箇所のみ追加です。

resource.h (変更なし)
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDM_SAVE 110
#define IDM_SAVE2 112
#define IDM_OPEN 111
#define IDC_HP 109
#define IDC_RICHEDIT 101

test.rc リソースファイル (変更なし)
#include "resource.h"

/////////////////////////////////////////////////////////////////////////////
//
// メニュー
//

IDC_HP MENU
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "開く(&O)", IDM_OPEN
        MENUITEM "上書き保存(&S)", IDM_SAVE
        MENUITEM "名前を付けて保存(&A)", IDM_SAVE2
        MENUITEM "アプリケーションの終了(&X)", IDM_EXIT
    END
    POPUP "ヘルプ(&H)"
    BEGIN
        MENUITEM "バージョン情報(&A)...", IDM_ABOUT
    END
END