ファイルの読み書き

今回はリッチエディットでファイルの読み書きをします。

リッチエディット1 - インコのWindowsSDK で作成したソースを修正します。

流れ 〜ファイルを開く

1.OPENFILENAME構造体をセット
OPENFILENAME構造体はコモンダイアログボックスを初期化するために使う情報が入っています。

typedef struct tagOFN { 
    DWORD lStructSize;        // 構造体サイズ(=76)
    HWND hwndOwner;           // ダイアログボックスのオーナーウィンドウのハンドル
    HINSTANCE hInstance; 
    LPCTSTR lpstrFilter;      // ファイル名を格納したバッファのアドレス
    LPTSTR lpstrCustomFilter;
    DWORD nMaxCustFilter; 
    DWORD nFilterIndex; 
    LPTSTR lpstrFile;
    DWORD nMaxFile;           // lpstrFileメンバで指定されるバッファのサイズ
    LPTSTR lpstrFileTitle;
    DWORD nMaxFileTitle; 
    LPCTSTR lpstrInitialDir;
    LPCTSTR lpstrTitle;
    DWORD Flags;              // ダイアログボックスの初期化に使用されるビットフラグ
    WORD nFileOffset; 
    WORD nFileExtension; 
    LPCTSTR lpstrDefExt;      // デフォルトの拡張子を格納したバッファのアドレス
    LPARAM lCustData; 
    LPOFNHOOKPROC lpfnHook; 
    LPCTSTR lpTemplateName;
    void * pvReserved;
    DWORD dwReserved;
    DWORD FlagsEx;
} OPENFILENAME, *LPOPENFILENAME;

lStructSize OPENFILENAME構造体のサイズ
hwndOwner ダイアログボックスを所有するウィンドウのハンドル
hInstance ・FlagsメンバでOFN_ENABLETEMPLATEHANDLEフラグが設定されている場合・・・
ダイアログボックステンプレートが格納されているメモリオブジェクトのハンドル
・FlagsメンバでOFN_ENABLETEMPLATEフラグが設定されている場合・・・
lpTemplateNameメンバによって指定されたダイアログテンプレートが格納されているモジュールへのハンドル
・FlagsメンバでOFN_EXPLORERフラグが設定されている場合・・・
システムはエクスプローラスタイルのダイアログを作成するために、指定されたテンプレートを使用
lpstrFilter 「ファイルの種類」コンボボックスに表示される、ファイルのフィルタ
例: "*.txt;*.doc;*.bak"
「種類の説明1 \0 拡張子1 \0 種類の説明2 \0 拡張子2 \0 \0」の形式で記載します。(最後に\0が2つ続きます)
lpstrCustomFilter フィルタパターン文字列を保存するためのバッファ
nMaxCustFilter lpstrCustomFilterメンバで指定されるバッファのサイズ
lpstrFile ファイル名の初期値
nMaxFile lpstrFileメンバで指定されるファイル名の最大文字数
lpstrFileTitle (パス情報を除く)ファイル名および拡張子を格納するための、バッファ
この情報が不要の場合には、このメンバに0(NULL)を指定
nMaxFileTitle lpstrFileTitleメンバで示されるバッファのサイズ
lpstrTitle ダイアログボックスのタイトルバーに表示される文字列
このメンバに0(NULL)を指定すると、デフォルトのタイトル(『開く』および『名前を付けて保存』)
Flags ダイアログボックスの初期化に使用されるビットフラグを設定
OFN_READONLY 「書込み禁止」チェックボックスを、初期状態からチェックがついた状態にする
OFN_OVERWRITEPROMPT 「名前をつけて保存」ダイアログボックスで、選択されたファイルがすでに存在する場合にはメッセージボックスを表示
OFN_HIDEREADONLY 「書込み禁止」チェックボックスを無効
OFN_ENABLETEMPLATE hInstanceメンバの機能切り替え
OFN_ENABLETEMPLATEHANDLE hInstanceメンバの機能切り替え
OFN_ALLOWMULTISELECT 「ファイル名」リストボックスで複数の選択ができるようにする
OFN_PATHMUSTEXIST ユーザーが有効なパスとディレクトリしか入力できないようになる。無効なパスやファイル名を入力すると 警告のメッセージボックスが出るようになる。
OFN_CREATEPROMPT 存在しないファイルをユーザーが指定した場合、ファイルを作成するかどうかの確認ダイアログを表示。
OFN_FORCESHOWHIDDEN システムファイルまたは隠し属性ファイルを表示。ただし、システム属性・隠し属性の両方の属性を持つファイルは表示しない。
lpstrDefExt デフォルトの拡張子をセット
拡張子に.(ピリオド)は不要
lpTemplateName ダイアログテンプレートリソースのポインタ
FlagsメンバにOFN_ENABLETEMPLATEが設定されているときのみ有効
pvReserved 0(NULL)固定
dwReserved 0固定

2.CreateFile()でファイルを開く

HANDLE CreateFile(
    LPCTSTR lpFileName,                         // ファイル名
    DWORD dwDesiredAccess,                      // アクセス指定
    DWORD dwShareMode,                          // 共有方法
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, // セキュリティ属性
    DWORD dwCreationDisposition,                // 動作指定
    DWORD dwFlagsAndAttributes,                 // フラグと属性
    HANDLE hTemplateFile                        // テンプレートファイルのハンドル
);

lpFileName ファイル名
dwDesiredAccess アクセス指定
0 ファイルの読み書きをしない
属性を取得する際に使用
GENERIC_WRITE 書き込み
GENERIC_READ 読み取り
dwShareMode 共有方法
0 共有しない
FILE_SHARE_READ これ以降、オープン操作で読み取りアクセスが要求された場合、そのオープンを許可
FILE_SHARE_WRITE これ以降、オープン操作で書き込みアクセスが要求された場合、そのオープンを許可
FILE_SHARE_DELETE これ以降、オープン操作で削除アクセスが要求された場合、そのオープンを許可
lpSecurityAttributes セキュリティ属性
0(NULL) デフォルトのセキュリティ
dwCreationDisposition 動作指定
CREATE_NEW 新しいファイルを作成
指定したファイルが既に存在している場合、この関数は失敗。
CREATE_ALWAYS 新しいファイルを作成
そのファイルを上書きし、既存の属性を消去。
OPEN_EXISTING ファイルを開きます
指定したファイルが存在していない場合、この関数は失敗。
OPEN_ALWAYS ファイルを開きます
指定したファイルが存在していない場合、新しいファイルを作成。
TRUNCATE_EXISTING ファイルを開き、ファイルのサイズを 0 バイトにします
指定したファイルが存在していない場合、この関数は失敗。
dwFlagsAndAttributes ファイルの属性とフラグ
FILE_ATTRIBUTE_READONLY 読み取り専用
FILE_ATTRIBUTE_HIDDEN 隠しファイル
FILE_ATTRIBUTE_SYSTEM システムファイル
FILE_ATTRIBUTE_NORMAL 属性なし
hTemplateFile テンプレートファイルのハンドル

3.EDITSTREAM構造体をセット

typedef struct _editstream {
    DWORD_PTR dwCookie;              // アプリケーション定義値
    DWORD dwError;                   // エラーコード
    EDITSTREAMCALLBACK pfnCallback;  // コールバック関数アドレス
} EDITSTREAM;

3.1.dwCookieにファイルハンドルを渡す
3.2.dwErrorは、コールバック関数の戻り値が入りますが、 初期値0をセットします。
3.3.pfnCallbackにコールバック関数のアドレスをセット

コールバック関数・・・
プログラム中で、呼び出し先の関数の実行中に実行されるように、あらかじめ指定しておく関数。

4.EM_STREAMINで、リッチエディットにEDITSTREAM構造体をセット

lResult = SendMessage(
    (HWND) hWndControl, // ウィンドウハンドル
    (UINT) EM_STREAMIN,
    (WPARAM) wParam,    // データフォーマットと置きかえオプションを示すフラグ
    (LPARAM) lParam     // EDITSTREAM構造体のアドレス
);

・wParamは、データフォーマットと置きかえオプションを示すフラグを指定。
SF_TEXTは、データフォーマットが、テキスト形式

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

lResult = SendMessage(
    (HWND) hWndControl, // ウィンドウハンドル
    (UINT) EM_SETMODIFY,
    (WPARAM) FALSE,
    0
);

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

BOOL CloseHandle(
    HANDLE hObject   // オブジェクトのハンドル
);

● コールバック関数
DWORD CALLBACK ******(DWORD, LPBYTE, LONG, LONG)

7.ReadFile()でデータの読み込み

BOOL ReadFile(
    HANDLE hFile,                // ファイルのハンドル
    LPVOID lpBuffer,             // ファイルから読み取ったデータを格納するためのバッファアドレス
    DWORD nNumberOfBytesToRead,  // 読み取り対象のバイト数
    LPDWORD lpNumberOfBytesRead, // 実際に読み取られたバイト数を格納するための変数
    LPOVERLAPPED lpOverlapped    // OVERLAPPED構造体またはNULL
);

流れ 〜ファイルに書き込む

1.CreateFile()でファイルを開く

2.EDITSTREAM構造体をセット

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

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

● コールバック関数
DWORD CALLBACK ******(DWORD, LPBYTE, LONG, LONG)

5.WriteFile()でデータの書き込み

BOOL WriteFile(
    HANDLE hFile,                    // ファイルのハンドル
    LPCVOID lpBuffer,                // 書き込むデータが入ったバッファアドレス
    DWORD nNumberOfBytesToWrite,     // 書き込み対象のバイト数
    LPDWORD lpNumberOfBytesWritten,  // 実際に書き込まれたバイト数を格納するための変数
    LPOVERLAPPED lpOverlapped        // OVERLAPPED構造体またはNULL
);

ソースコードの入力

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

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("");
    LPCTSTR szFile = TEXT("sample.txt");
    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");
                    hFile = CreateFile(szFile, // ファイル名
                        GENERIC_READ,          // アクセス指定 GENERIC_READ:読み取り
                        0,                     // 共有方法 0:共有しない
                        NULL,                  // セキュリティ属性 0:デフォルト
                        OPEN_ALWAYS,           // 動作指定 OPEN_ALWAYS:ファイルをオープン。
                                               //  ファイルが存在していない場合、
                                               //  新しいファイルを作成
                        FILE_ATTRIBUTE_NORMAL, // フラグと属性
                                               // FILE_ATTRIBUTE_NORMAL:属性設定なし
                        NULL);                 // テンプレートファイル
                    if (hFile == INVALID_HANDLE_VALUE)
                    {
                        MessageBox(hRichEdit,
                            TEXT("ファイルがありません"),
                            TEXT("Error"),
                            MB_OK);
                        break;
                    }
                    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);
                    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_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_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 "アプリケーションの終了(&X)", IDM_EXIT
    END
    POPUP "ヘルプ(&H)"
    BEGIN
        MENUITEM "バージョン情報(&A)...", IDM_ABOUT
    END
END

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