ファイルの読み書き

今回は下のようなコモンダイアログを使って、ファイルの読み書きをします。
Windowsのバージョンによって、違ったコモンダイアログになります。
下記は、Windows 8の「名前を付けて保存」コモンダイアログは、下記のようになります。

コモンダイアログ8

Windows Vistaの「名前を付けて保存」コモンダイアログは、下記のようになります。

コモンダイアログVista

Windows XPの「ファイルを開く」コモンダイアログです。 コモンダイアログXP

ファイルの読み書き1 - インコのWindowsSDK で作成したソースを修正します。

流れ 〜ファイルを開く

1.EnableMenuItem()で、上書き保存のメニューを選択できないようにします。
EnableMenuItem()の引数にメニューのハンドルが必要なので、GetMenu()でメニューのハンドルを取得します。

HMENU GetMenu(  // 戻り値:メニューのハンドル
    HWND hWnd  // ウィンドウのハンドル
);

BOOL EnableMenuItem(
    HMENU hMenu,         // メニューのハンドル
    UINT uIDEnableItem,  // メニューアイテムのID
    UINT uEnable         // オプション(MF_ENABLED:選択不可、MF_ENABLED:選択可)
);

●ファイルオープン時

2.GetOpenFileName()で、コモンダイアログの表示

BOOL GetOpenFileName(
    LPOPENFILENAME lpofn   // OPENFILENAME構造体のアドレス
);

3.SetWindowText()で、ウインドウタイトルをセット。 ここでは、ウインドウタイトルをファイル名にします。

BOOL SetWindowText(
  HWND hWnd,         // ウィンドウまたはコントロールのハンドル
  LPCTSTR lpString   // タイトルまたはテキスト
);

hWndウィンドウまたはコントロールのハンドル
lpString新しいウィンドウタイトルまたはコントロールのテキストとして使われる文字列

※ファイル名の変数の型を、MAX_PATH(260)バイト確保した、TCHAR型に変えています。(LPCTSTR型はconst型なので、変数の書き換えができないためです。)
確保したサイズが足りないと、実行時に強制終了されます。

4.EnableMenuItem()で、上書き保存のメニューを選択できるようにします。

●名前を選択して保存するとき

5.OPENFILENAME構造体のサイズをセット

6.GetSaveFileName()で、コモンダイアログの表示

BOOL GetSaveFileName(
    LPOPENFILENAME lpofn   // OPENFILENAME構造体のアドレス
);

7.CreateFile()でファイルを開きます。

8.EDITSTREAM構造体をセット

9.EM_SETMODIFYで、リッチエディットコントロールの変更フラグを解除

10.CloseHandle()でハンドルをクローズ

ソースコードの入力

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

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_COMMAND - アプリケーション メニューの処理
//  WM_DESTROY - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    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);               // ウィンドウ作成データ
            break;
        case WM_SIZE:
            MoveWindow(hRichEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            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

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