第75章 メモ帳を作る その2


今回は、「名前を付けて保存」「上書き保存」「新規作成」 などのメニューを加えます。また、前回作った部分のソースも 一部変更を加えます。

//リソーススクリプト ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "新規作成(&N)", IDM_NEW MENUITEM "開く(&O)", IDM_OPEN MENUITEM "上書き保存(&S)", IDM_UWAGAKI MENUITEM "名前を付けて保存(&A)", IDM_WRITE MENUITEM SEPARATOR MENUITEM "終了(&X)", IDM_END END END

メニューに追加があります。 その他の注意は前章と同じです。

// memo01.cpp #define STRICT #include <windows.h> #include <windowsx.h> // マクロを使うので必要 #include "resource.h" //リソースエジタ使用 #define ID_EDIT 100 // エジットコントロールのID LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int OpenMyFile(HWND); int WriteMyFile(HWND); int OverWriteMyFile(HWND); int MakeNewFile(HWND); char szClassName[] = "memo01"; //ウィンドウクラス char szFileName[256]; // オープンするファイル名(パス付き) char szFile[64] = "無題"; // ファイル名 HINSTANCE hInst; // インスタンスハンドル HWND hEdit; // エジットウィンドウのハンドル char szStr[1024 * 64]; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!hPrevInst) { if (!InitApp(hCurInst)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

前回は、ローカル変数だったものが一部グローバル変数になっているので 注意してください。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); }

これは、いつもと同じです。

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // インスタンスハンドルをグローバル変数にコピー hWnd = CreateWindow(szClassName, "猫でもわかるエジタ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInstance, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

これは、前回と同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; RECT rc; switch (msg) { case WM_CREATE: GetClientRect(hWnd, &rc); hEdit = CreateWindow( "EDIT", NULL, WS_CHILD | WS_VISIBLE | ES_WANTRETURN | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | ES_AUTOHSCROLL | WS_HSCROLL, 0, 0, rc.right, rc.bottom, hWnd, (HMENU)ID_EDIT, hInst, NULL); SendMessage(hEdit, EM_SETLIMITTEXT, (WPARAM)1024*64, 0); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_OPEN: OpenMyFile(hWnd); break; case IDM_WRITE: WriteMyFile(hWnd); break; case IDM_UWAGAKI: OverWriteMyFile(hWnd); break; case IDM_NEW: MakeNewFile(hWnd); break; } break; case WM_SIZE: GetClientRect(hWnd, &rc); MoveWindow(hEdit, rc.left, rc.top, rc.right, rc.bottom, TRUE); break; case WM_CLOSE: if (SendMessage(hEdit, EM_GETMODIFY, 0, 0) == TRUE) { id = MessageBox(hWnd, "文書が変更されています。保存しますか?", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) WriteMyFile(hWnd); } id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

前回に比べてWM_COMMANDのところが少し増えています。 (メニュー項目が増えた分だけ)

int OpenMyFile(HWND hWnd) { int id; DWORD dwSize = 0L; OPENFILENAME ofn; HANDLE hFile; DWORD dwAccBytes; char szTitle[64], *szTitle_org = "猫でもわかるエジタ[%s]"; if(SendMessage(hEdit, EM_GETMODIFY, 0, 0) == TRUE) { id = MessageBox(hWnd, "文書が変更されています。保存しますか?", "注意!", MB_OKCANCEL); if (id == IDOK) WriteMyFile(hWnd); } memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "text(*.txt)\0*.txt\0All files(*.*)\0*.*\0\0"; ofn.lpstrFile = szFileName; ofn.lpstrFileTitle = szFile; ofn.nMaxFile = MAX_PATH; ofn.nMaxFileTitle = sizeof(szFile); ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; ofn.lpstrDefExt = "txt"; ofn.lpstrTitle = "ファイルオープン!"; if(GetOpenFileName(&ofn) == 0) return -1; hFile = CreateFile(szFileName, GENERIC_READ, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); dwSize = GetFileSize(hFile, NULL); if (dwSize >= 1024*64) { MessageBox(hWnd, "ファイルサイズが大きすぎます", "Too Big File!", MB_OK); CloseHandle(hFile); dwSize = 0L; return -1; } SetFilePointer(hFile, 0, 0, FILE_BEGIN); ReadFile(hFile, szStr, dwSize, &dwAccBytes, NULL); szStr[dwAccBytes] = '\0'; Edit_SetText(hEdit, szStr); wsprintf(szTitle, szTitle_org, szFile); SetWindowText(hWnd, szTitle); CloseHandle(hFile); return 0; }

前章とほとんど同じですが、文章を更新したのに新しいファイルを開こうとすると 注意を促すようにしました。

EM_GETMODIFY wParam = 0; lParam = 0;

エジットコントロールの内容が変更されたかどうかを調べます。 変更されていたらTURE、されていなかったらFALSEを返します。

int WriteMyFile(HWND hWnd) { OPENFILENAME ofn; HANDLE hFile; DWORD dwAccBytes; char szTitle[64], *szTitle_org = "猫でもわかるエジタ[%s]"; int nLen; memset(szStr, '\0', sizeof(szStr)); nLen = GetWindowTextLength(hEdit); GetWindowText(hEdit, szStr, nLen + 1); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "text(*.txt)\0*.txt\0All files(*.*)\0*.*\0\0"; ofn.lpstrFile = szFileName; ofn.lpstrFileTitle = szFile; ofn.nFilterIndex = 1; ofn.nMaxFile = sizeof(szFileName); ofn.nMaxFileTitle = sizeof(szFile); ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; ofn.lpstrDefExt = "txt"; ofn.lpstrTitle = "名前を付けて保存する!"; if(GetSaveFileName(&ofn) == 0) return -1; hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WriteFile(hFile, szStr, strlen(szStr) + 1, &dwAccBytes, NULL); wsprintf(szTitle, szTitle_org, szFile); SetWindowText(hWnd, szTitle); if(CloseHandle(hFile) == 0) MessageBox(hWnd, "Error CloseHandle", "Error", MB_OK); SendMessage(hEdit, EM_SETMODIFY, FALSE, 0); return 0; }

OPENFILENAME構造体は、読み込みの場合と同じです。このとき、すでに存在する ファイル名を指定したとき注意を促すOFN_OVERWRITEPROMPTを付け加えています。

BOOL GetSaveFileName( LPOPENFILENAME lpofn );

使い方は想像できると思います。ユーザーがキャンセルボタンを 押したときは、何もしないで戻ることにしました。

BOOL WriteFile( HANDLE hFile, // ファイルハンドル LPCVOID lpBuffer, // ファイルに書き込むデータのポインタ DWORD nNumberOfBytesToWrite, // 書き込むバイト数 LPDWORD lpNumberOfBytesWritten, // 書き込まれたバイト数へのポインタ LPOVERLAPPED lpOverlapped // 今回は使用せず );

とこんな具合になっています。
書き込みが終わったら、

SendMessage(hEdit, EM_SETMODIFY, FALSE, 0);

で、エジットコントロールの変更フラグを「なし」にしておきます。

int OverWriteMyFile(HWND hWnd) { HANDLE hFile; DWORD dwAccBytes; int nLen; memset(szStr, '\0', sizeof(szStr)); nLen = GetWindowTextLength(hEdit); GetWindowText(hEdit, szStr, nLen + 1); hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WriteFile(hFile, szStr, strlen(szStr) + 1, &dwAccBytes, NULL); SendMessage(hEdit, EM_SETMODIFY, FALSE, 0); if (CloseHandle(hFile) == 0) { MessageBox(hWnd, "Error CloseHandle", "Error", MB_OK); return -1; } return 0; }

上書き保存の部分です。保存したらやはりエジットコントロールの変更をFALSEにしておきます。

int MakeNewFile(HWND hWnd) { int id; char szTitle[64], *szTitle_org = "猫でもわかるエジタ[%s]"; if (SendMessage(hEdit, EM_GETMODIFY, 0, 0) == TRUE) { id = MessageBox( hWnd, "文書が更新されています。保存しますか?", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) WriteMyFile(hWnd); } wsprintf(szTitle, szTitle_org, "無題"); SetWindowText(hWnd, szTitle); strcpy(szStr, ""); Edit_SetText(hEdit, szStr); return 0; }

新規作成部分です。やはり、エジットコントロールの変更があった場合は 注意を促して「名前を付けて保存」へ行けるようにしました。

今回でエジタの基本的機能は持つようになりました。 ざっと眺めてみるといろいろ不満はあります。 使うかどうかわからない大きな配列がどーんと最初にあります。 これは、何とかしたいですね。後の章で何とかする予定です。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Sep/16/1997 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。