第256章 アプリケーションに関連づける


今まで、作ってきたチャットプログラムは終了するとそれっきりで、チャットの内容は 消えてなくなりました。そこで、今回はチャットの内容をファイルに保存するようにします。 単なる*.txtファイルではつまらないので、新しい文書タイプを作ります。 また、この文書タイプにアプリケーションを関連づけて、文書ファイルにもアイコンが 表示されるようにします。



今回、新しく作る文書タイプは、「*.cht」です。多分このような拡張子はないと思いますが 万一すでに存在している場合は違う拡張子にしてください。これに、今回作るmailslot07.exe を関連づけます。この新しい文書タイプにアイコンも関連づけます。左の図で「Chat1.cht」と いうのが新しく作った文書タイプのファイルです。



このファイルを右クリックすると左のようなメニューが出てきます。「猫チャット(C)」を選択すると mailslot07.exeが起動して、このファイルを読み込みます。また、*.chtファイルを左クリック (または、左ダブルクリック)するとメニューが出ずにいきなり、アプリケーションが立ち上がり このファイルを開きます。



さて、このようなファイルの関連づけはどのようにすればよいのでしょうか。 レジストリ操作が必要なことは、うすうす気がついていますね。 次のようにします。

1.HKEY_CLASSES_ROOTの下に拡張子のキー(.cht)を作る  このキーに新しい文書タイプの名前(何でもよい、ここでは(MyChat))を値として保存 2.HKEY_CLASSES_ROOTの下に「新しいタイプ(MyChat)\DefaultIcon」キーを作ります。  DefaultIconキーに「x:\xx\x.exe,n」(nは何番目のアイコンを使うか)を保存 3.HKEY_CLASSES_ROOT\新タイプ\shell\メニュー項目名\commandキーを作ります  このキーにアプリケーション起動のコマンドラインを保存します  (x:\xx\xx\xx\abc %1など)

これだけで実現できます。いきなりプログラムを作らずにレジストリエディタなどで 手動で実験してみるとよくわかります。なお、レジストリに値を保存した後、再起動しないと アイコンが表示されません。言うまでもありませんがレジストリ操作は危険を伴うので 必ずバックアップをとってから作業をしてください。

さて、ここでお気づきと思いますが文書ファイルに関連づけられたアプリケーションは コマンドライン引数を処理しなくてはいけません。コマンドラインから 「アプリケーション名 ファイル名」を実行するとアプリケーションは引数となっている ファイルを開きます。

では、プログラムを見てみましょう。

// mailslot07.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "開く(&O)...", IDM_OPEN MENUITEM "上書き保存(&S)", IDM_SAVE MENUITEM "名前を付けて保存(&A)...", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "タスクトレーに格納(&T)", IDM_TASK MENUITEM SEPARATOR MENUITEM "終了(&X)...", IDM_END END POPUP "表示(&V)" BEGIN MENUITEM "書き込み用ウィンドウ", IDM_WRITE END POPUP "オプション(&O)" BEGIN MENUITEM "IDを表示する(&I)", IDM_ID MENUITEM SEPARATOR MENUITEM ".chtを関連づける(&R)", IDM_REG END END RTRAYMENU MENU DISCARDABLE BEGIN POPUP " ダミーです" BEGIN MENUITEM "タスクトレーから出す", IDM_SHOW MENUITEM "終了", IDM_END END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MAILSLOTNAME DIALOG DISCARDABLE 0, 0, 118, 61 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "メールスロット名" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,7,19,104,16,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,34,39,50,14 LTEXT "自分のメールスロットの名前",IDC_STATIC,7,7,83,8 END MYWRITE DIALOG DISCARDABLE 0, 0, 163, 153 STYLE DS_MODALFRAME | WS_CHILD FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT3,70,23,86,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,70,44,86,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT1,7,69,149,56,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN DEFPUSHBUTTON "送信",IDOK,56,132,50,14 LTEXT "相手のメールスロット",IDC_STATIC,7,45,62,8 LTEXT "送信先コンピュータ",IDC_STATIC,7,25,56,8 CONTROL "他のコンピュータにメッセージを送る",IDC_CHECK1,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,7,7,119,10 END ///////////////////////////////////////////////////////////////////////////// // // Icon // MYICON ICON DISCARDABLE "myicon.ico"

メニューに「開く」「上書き保存」「名前を付けて保存」の各項目が増えました。

書き込み用ダイアログは「表示」の下に「書き込み用ウィンドウ」という項目を 作り、これが表示されていればチェックをつけるように変えました。

「オプション」に「.chtを関連づける」という項目を追加しました。

メールスロットの名前を尋ねるダイアログはキャンセルボタンをなくしました。 メニューから選択するのではなく、プログラムが開始されるとすぐに自分の 名前を聞いてくるようにしました。

書き込みダイアログの表示・非表示はメニューのチェックで行うので「閉じる」ボタンは 廃止しました。

アイコンは今まで通りです。これを関連づけられた*.chtファイルに割り当てます。

// mailslot07.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include "resource.h" #define MYMSG_TRAY WM_USER #define ID_MYTRAY 101 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyNameProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyWriteProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); DWORD WINAPI ReadFunc(LPVOID); HANDLE MakeMySlot(HWND, BOOL *); void MyTaskTray(HWND, PNOTIFYICONDATA); int MakeRTrayMenu(HWND); int SaveAsMyFile(HWND); int OverWriteMyFile(HWND); int OpenMyFile(HWND); int OpenMyFile2(HWND); int MyReg(void); char szClassName[] = "mailslot07"; //ウィンドウクラス char szMailSlot[128];//メールスロットの名前(フルパス付き) char szSlotName[64];//メールスロットの名前(パスなし) HANDLE hSlot; HINSTANCE hInst; HWND hMainEdit; BOOL bID = FALSE;//メッセージIDを表示するかどうか BOOL bIsTask = FALSE;//タスクトレーに格納されているかどうか HWND hWriteDlg;//書き込み用モードレスダイアログボックス int DlgX, DlgY;//書き込み用ダイアログの大きさ BOOL bWriteDlg = FALSE;//書き込み用ダイアログが表示されいるかどうか char szFileName[MAX_PATH];//パス付きファイル名 char szFile[64];//ファイルタイトル BOOL bCmdLine;//コマンドライン引数があるかどうか

いくつかの関数と、グローバル変数が増えました。コメントなどを参照してください。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (strcmp(lpsCmdLine, "") != 0) { strcpy(szFileName, lpsCmdLine); GetFileTitle(lpsCmdLine, szFile, sizeof(szFile)); bCmdLine = TRUE; } if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { if (hWriteDlg == 0 || !IsDialogMessage(hWriteDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } MessageBox(NULL, "メッセージループを抜けました", "OK", MB_OK); return msg.wParam; }

いつもとちょっと違います。コマンドライン引数はWinMain関数の3番目の引数で 渡されます。コマンドライン引数がある場合はこれをオープンするフルパス付きファイル名 としてグローバル変数に格納します。また、ファイルタイトルはGetFileTitle関数で取得して やはりグローバル変数に格納しておきます。さらに、bCmdLineをTRUEにしておきます。 GetFileTitle関数はすでに 第136章などで出てきましたがまだ解説を書いていませんでした。

short GetFileTitle( LPCTSTR lpszFile, // フルパス付きファイル名 LPTSTR lpszTitle, // ファイルタイトルを受け取るバッファのポインタ WORD cbBuf // バッファの長さ );

使い方の説明は不要と思いますが、成功すれば0,ファイル名が無効の時はマイナスの値が 戻ります。バッファサイズが小さすぎるときは必要なサイズが返されます。

よけいなことですがプログラムの終了時、メッセージループを抜けた時、これを メッセージボックスで知らせるようにしました。いろいろな場合で試して必ず ループを抜けてWinMain関数が終了することが確認されれば消してください。

//ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); 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; wc.hIconSm = LoadIcon(hInst, "MYICON");//アイコン return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるメールスロット", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 600, //幅 281, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル 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; static BOOL bEnd; static HANDLE hThread; static HMENU hMenu; MENUITEMINFO mi; static NOTIFYICONDATA ni; RECT rc; MINMAXINFO *lpmm; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); EnableMenuItem(hMenu, IDM_WRITE, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_TASK, MF_BYCOMMAND | MF_GRAYED); if (!bCmdLine) EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_GRAYED); hMainEdit = CreateWindow("EDIT", "", WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | WS_HSCROLL | WS_VSCROLL, 0, 0, 0, 0, hWnd, (HMENU)100, hInst, NULL); DialogBox(hInst, "MAILSLOTNAME", hWnd, (DLGPROC)MyNameProc); bEnd = FALSE; hThread = MakeMySlot(hWnd, &bEnd); if (bCmdLine) OpenMyFile2(hWnd); break; case WM_SIZE: if (!bWriteDlg) MoveWindow(hMainEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); else { MoveWindow(hMainEdit, 0, 0, LOWORD(lp) - DlgX, HIWORD(lp), TRUE); MoveWindow(hWriteDlg, LOWORD(lp) - DlgX, 0, DlgX, HIWORD(lp), TRUE); } break; case WM_GETMINMAXINFO: if (bWriteDlg) { lpmm = (MINMAXINFO *)lp; lpmm->ptMinTrackSize.y = 281; lpmm->ptMinTrackSize.x = 450; } else { return (DefWindowProc(hWnd, msg, wp, lp)); } break; case MYMSG_TRAY: if (wp == ID_MYTRAY) { switch (lp) { case WM_RBUTTONDOWN: MakeRTrayMenu(hWnd); break; case WM_LBUTTONDBLCLK: bIsTask = FALSE; Shell_NotifyIcon(NIM_DELETE, &ni); ShowWindow(hWnd, SW_SHOWNORMAL); default: break; } } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_WRITE: if (!IsWindow(hWriteDlg)) { hWriteDlg = CreateDialog(hInst, "MYWRITE", hWnd, (DLGPROC)MyWriteProc); if (hWriteDlg != NULL) { bWriteDlg = TRUE; ShowWindow(hWriteDlg, SW_SHOW); GetWindowRect(hWriteDlg, &rc); DlgX = rc.right - rc.left; DlgY = rc.bottom - rc.top; GetClientRect(hWnd, &rc); MoveWindow(hWriteDlg, rc.right - rc.left -DlgX, rc.top, DlgX, rc.bottom -rc.top, TRUE); MoveWindow(hMainEdit, 0, 0, rc.right - rc.left - DlgX, rc.bottom - rc.top, TRUE); memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; mi.fState = MFS_CHECKED; SetMenuItemInfo(hMenu, IDM_WRITE, FALSE, &mi); } else { MessageBox(hWnd, "ダイアログボックス作成失敗です", "Error", MB_OK); return 0; } } else { DestroyWindow(hWriteDlg); bWriteDlg = FALSE; GetClientRect(hWnd, &rc); MoveWindow(hMainEdit, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE); memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; mi.fState = MFS_UNCHECKED; SetMenuItemInfo(hMenu, IDM_WRITE, FALSE, &mi); } break; case IDM_OPEN: OpenMyFile(hWnd); break; case IDM_SAVE: OverWriteMyFile(hWnd); break; case IDM_SAVEAS: SaveAsMyFile(hWnd); break; case IDM_ID: memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; if (!bID) { mi.fState = MFS_CHECKED; SetMenuItemInfo(hMenu, IDM_ID, FALSE, &mi); bID = TRUE; break; } else { mi.fState = MFS_UNCHECKED; SetMenuItemInfo(hMenu, IDM_ID, FALSE, &mi); bID = FALSE; } break; case IDM_TASK: MyTaskTray(hWnd, &ni); ShowWindow(hWnd, SW_HIDE); bIsTask = TRUE; break; case IDM_SHOW: Shell_NotifyIcon(NIM_DELETE, &ni); ShowWindow(hWnd, SW_SHOWNORMAL); bIsTask = FALSE; break; case IDM_REG: MyReg(); break; } break; case WM_CLOSE: if (SendMessage(hMainEdit, EM_GETMODIFY, 0, 0)) { id = MessageBox(hWnd, "チャットの内容が更新されています。\n" "保存せずに終了しますか", "確認", MB_YESNO | MB_ICONQUESTION); if (id == IDNO) break; } id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hThread) { bEnd = TRUE; WaitForSingleObject(hThread, INFINITE); if (CloseHandle(hThread)) MessageBox(hWnd, "hThreadをクローズしました", "OK", MB_OK); } if (hSlot) { if (CloseHandle(hSlot)) MessageBox(hWnd, "メールスロットを削除しました", "OK", MB_OK); } if (bIsTask) Shell_NotifyIcon(NIM_DELETE, &ni); DestroyWindow(hMainEdit); if (hWriteDlg) DestroyWindow(hWriteDlg); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

メインウィンドウのプロシージャです。

WM_CREATEメッセージが来た時、bCmdLineがFALSEならば、メニューのIDM_SAVE(上書き保存)を 選択不能にしておきます。

また、今まではメニューから自分の名前を決めるダイアログを出していましたが、 プログラム開始時に自動的に出すようにしました。さらに、コマンドライン引数があるときは これをOpenMyFile2関数(後述)で開いてエディットコントロールに表示するようにしました。

メニューからIDM_CREATESLOTはもう来ないので、この部分は削除しました。

メニューからIDM_WRITEが選択されたとき、ダイアログボックスのハンドルが無効なら ダイアログボックスを作り、有効ならこれを破棄するようにしました。また、それぞれの 状態をチェックで表示します。メニュー項目にチェックをつけるには、SetMenuItemInfo関数を 使います。第181章などを参照してみてください。

メニューからIDM_OPEN, IDM_SAVE, IDM_SAVEASが選択されたら、それぞれ OpenMyFile, OverWriteMyFile, SaveAsMyFile関数を呼びます。

また、メニューから「.chtを関連づける」(IDM_REG)が選択されたらMyReg関数を呼びます。

終了時にもし、エディットコントロールの内容が更新されていたら保存するかどうかを 確かめるようにします。

HANDLE MakeMySlot(HWND hWnd, BOOL *lpbEnd) { HMENU hMenu; hMenu = GetMenu(hWnd); char szWinTitle[64], *szTitle_Org = "猫でもわかるメールスロット[%s][%s]"; DWORD dwThreadID; HANDLE hThread; hSlot = CreateMailslot( szMailSlot, 0, MAILSLOT_WAIT_FOREVER, NULL); if (hSlot == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "メールスロット作成失敗!\n名前を変更してみてください", "Error", MB_OK); DialogBox(hInst, "MAILSLOTNAME", hWnd, (DLGPROC)MyNameProc); MakeMySlot(hWnd, lpbEnd); } else { EnableMenuItem(hMenu, IDM_WRITE, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, IDM_TASK, MF_BYCOMMAND | MF_ENABLED); wsprintf(szWinTitle, szTitle_Org, szSlotName, szFile); SetWindowText(hWnd, szWinTitle); hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadFunc, (LPVOID)lpbEnd, 0, &dwThreadID); if (hThread == NULL) MessageBox(hWnd, "CreateThread Error", "Error", MB_OK); } return hThread; }

この関数にもちょっと変更があります。
ウィンドウタイトルを「猫でもわかるメールスロット[自分のメールスロット名][ファイル名]」に 変更しました。表示のためのプログラムもすっきりするよう変更しました。

また、今まではメールスロットの作成に失敗した場合(ほとんどの場合すでに存在している名前を付けようとした時) そのことをメッセージボックスで知らせただけでしたが、今回は再度メールスロットの名前を付けるところから やり直させるようにしました。

LRESULT CALLBACK MyNameProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { HWND hParent; static HWND hEdit; switch (msg) { case WM_INITDIALOG: hParent = GetParent(hDlg); hEdit = GetDlgItem(hDlg, IDC_EDIT1); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(hEdit, szSlotName, sizeof(szSlotName)); if (strcmp(szSlotName, "") == 0) { MessageBox(hDlg, "無効な名前です", "Error", MB_OK | MB_ICONEXCLAMATION); SetFocus(hEdit); return TRUE; } wsprintf(szMailSlot, "\\\\.\\mailslot\\%s", szSlotName); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: MessageBox(hDlg, "メールスロットの名前を決めて下さい", "注意", MB_OK | MB_ICONEXCLAMATION); return TRUE; } return FALSE; } return FALSE; }

自分のメールスロットの名前を入力するダイアログのプロシージャです。

何も入力せずに間違って「OK」ボタンを押したときは「無効な名前です」というメッセージボックスを 出して、再度入力を求めるようにしました。

キャンセルボタンはなくなりましたが、ばってんボタン?を押されたときは、名前を決めるよう 注意のメッセージボックスを出すようにしました。

LRESULT CALLBACK MyWriteProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hEdit, hEditTo, hCheck, hComputor; char szBuf[1024], szBuf2[1024], szTo[64], *lpszEdit, szID[32], szFrom[32]; HGLOBAL hMem; static char szShortTo[64], szComputor[64]; HANDLE hMail; DWORD dwWritten, dwTickCount; BOOL bResult; static BOOL bOtherComputor; int iSize, iLine, i; switch (msg) { case WM_INITDIALOG: hCheck = GetDlgItem(hDlg, IDC_CHECK1); hEdit = GetDlgItem(hDlg, IDC_EDIT1); hEditTo = GetDlgItem(hDlg, IDC_EDIT2); hComputor = GetDlgItem(hDlg, IDC_EDIT3); if (bOtherComputor) { Button_SetCheck(hCheck, BST_CHECKED); EnableWindow(hComputor, TRUE); } else { EnableWindow(hComputor, FALSE); } Edit_SetText(hComputor, szComputor); Edit_SetText(hEditTo, szShortTo); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDC_CHECK1: if (HIWORD(wp) == BN_CLICKED) { if (Button_GetCheck(hCheck) == BST_CHECKED) { EnableWindow(hComputor, TRUE); SetFocus(hComputor); bOtherComputor = TRUE; } else { EnableWindow(hComputor, FALSE); SetFocus(hEditTo); bOtherComputor = FALSE; } } else return FALSE; return TRUE; case IDOK: Edit_GetText(hEditTo, szShortTo, sizeof(szShortTo)); if (!bOtherComputor) { wsprintf(szTo, "\\\\.\\mailslot\\%s", szShortTo); } else { Edit_GetText(hComputor, szComputor, sizeof(szComputor)); wsprintf(szTo, "\\\\%s\\mailslot\\%s", szComputor, szShortTo); } dwTickCount = GetTickCount(); wsprintf(szID, "[Message ID = %d]\r\n", dwTickCount); Edit_GetText(hEdit, szBuf2, sizeof(szBuf2)); hMail = CreateFile(szTo, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hMail == INVALID_HANDLE_VALUE) { MessageBox(hDlg, "CreateFile Error", "Error", MB_OK); return TRUE; } strcpy(szBuf, szID); wsprintf(szFrom, "%s>", szSlotName); strcat(szBuf, szFrom); strcat(szBuf, szBuf2); bResult = WriteFile(hMail, szBuf, strlen(szBuf) + 1, &dwWritten, NULL); if (!bResult) { MessageBox(hDlg, "WriteFile Error", "Error", MB_OK); } CloseHandle(hMail); iSize = GetWindowTextLength(hMainEdit); hMem = GlobalAlloc(GHND, iSize + 1024); if (hMem == NULL) { MessageBox(hDlg, "メモリ確保に失敗しました", "Error", MB_OK); return TRUE; } lpszEdit = (char *)GlobalLock(hMem); Edit_GetText(hMainEdit, lpszEdit, iSize + 1024); strcat(lpszEdit, "\r\n"); if (bID) strcat(lpszEdit, szID); strcat(lpszEdit, szFrom); strcat(lpszEdit, szBuf2); Edit_SetText(hMainEdit, lpszEdit); iLine = Edit_GetLineCount(hMainEdit); for (i = 0; i < iLine; i++) SendMessage(hMainEdit, EM_SCROLL, (WPARAM)SB_LINEDOWN, 0); GlobalUnlock(hMem); GlobalFree(hMem); Edit_SetText(hEdit, ""); SetFocus(hEdit); return TRUE; } return FALSE; } return FALSE; }

書き込み用ダイアログボックスのプロシージャです。キャンセルボタンが廃止されたので case IDCANCEL:の部分がそっくり不要になりました。これに関連したrc, hMenu変数も 不要となりました。

DWORD WINAPI ReadFunc(LPVOID lp) { BOOL *lpbEnd; DWORD dwNextSize, dwCount, dwRead; static char szBuf1[1024], szBuf2[1024]; HGLOBAL hMem; char *lpszTxt, *lpszFirst; int iLength, iLine, i; lpbEnd = (BOOL *)lp; while (1) { if (*lpbEnd) { MessageBox(NULL, "*lpbEndがTRUEとなりました", "LOOP END", MB_OK); break; } GetMailslotInfo(hSlot, NULL, &dwNextSize, &dwCount, NULL); if (dwNextSize != MAILSLOT_NO_MESSAGE) { while (dwCount) { ReadFile(hSlot, szBuf1, dwNextSize, &dwRead, NULL); if (strcmp(szBuf1, szBuf2) != 0) { iLength = Edit_GetTextLength(hMainEdit); hMem = GlobalAlloc(GHND, iLength + dwRead +256); if (hMem == NULL) { MessageBox(NULL, "メモリ確保に失敗しました", "Error", MB_OK); break; } lpszTxt = (char *)GlobalLock(hMem); Edit_GetText(hMainEdit, lpszTxt, iLength + dwRead +256); strcat(lpszTxt, "\r\n"); if (!bID) { lpszFirst = strstr(szBuf1, "\r\n"); strcat(lpszTxt, lpszFirst + 2); } else { strcat(lpszTxt, szBuf1); } Edit_SetText(hMainEdit, lpszTxt); SendMessage(hMainEdit, EM_SETMODIFY, (WPARAM)TRUE, 0); iLine = Edit_GetLineCount(hMainEdit); SendMessage(hMainEdit, EM_SCROLL, (WPARAM)SB_LINEDOWN, 0); iLine = Edit_GetLineCount(hMainEdit); for (i = 0; i < iLine; i++) SendMessage(hMainEdit, EM_SCROLL, (WPARAM)SB_LINEDOWN, 0); GlobalUnlock(hMem); if (GlobalFree(hMem)) { MessageBox(NULL, "メモリ解放失敗", "Error", MB_OK); break; } if (bIsTask) { MessageBox(NULL, "メッセージが来ました", "メッセージの到着", MB_OK | MB_SETFOREGROUND); } } strcpy(szBuf2, szBuf1); GetMailslotInfo(hSlot, NULL, &dwNextSize, &dwCount, NULL); } } Sleep(100); } MessageBox(NULL, "ループを抜けました", "OK", MB_OK); return 0; }

メールスロットにメッセージが来ていれば、これをエディットコントロールに書き出すスレッド関数です。 今までとほとんど同じですが、メッセージをエディットコントロールに書き出したらEM_SETMODIFYメッセージを 送って変更が有ったことを知らせます。直接エディットコントロールに入力した場合は不要ですが Edit_SetTextマクロなどで入力した場合は知らせる必要があります。

void MyTaskTray(HWND hWnd, PNOTIFYICONDATA lpni) { HICON hIcon; hIcon = LoadIcon(hInst, "MYICON"); lpni->cbSize = sizeof(NOTIFYICONDATA); lpni->hIcon = hIcon; lpni->hWnd = hWnd; lpni->uCallbackMessage = MYMSG_TRAY; lpni->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; lpni->uID = ID_MYTRAY; strcpy(lpni->szTip, "猫でもわかるメールスロット"); Shell_NotifyIcon(NIM_ADD, lpni); return; } int MakeRTrayMenu(HWND hWnd) { HMENU hMenu, hSubMenu; POINT pt; hMenu = LoadMenu(hInst, "RTRAYMENU"); hSubMenu = GetSubMenu(hMenu, 0); GetCursorPos(&pt); SetForegroundWindow(hWnd); TrackPopupMenu( hSubMenu, TPM_BOTTOMALIGN, pt.x, pt.y, 0, hWnd, NULL); DestroyMenu(hMenu); return 0; }

これは、第254章と同じです。

int SaveAsMyFile(HWND hWnd) { OPENFILENAME ofn; HANDLE hFile; HGLOBAL hMem; DWORD dwAccBytes; char szWinTitle[64], *szTitle_org = "猫でもわかるメールスロット[%s][%s]"; char *lpszBuf; int nLen; HMENU hMenu; nLen = GetWindowTextLength(hMainEdit); hMem = GlobalAlloc(GHND, nLen + 1); if (hMem == NULL) { MessageBox(hWnd, "メモリの確保に失敗しました", "Error", MB_OK); return -1; } lpszBuf = (char *)GlobalLock(hMem); GetWindowText(hMainEdit, lpszBuf, nLen + 1); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "chat(*.cht)\0*.cht\0text(*.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 = "cht"; ofn.lpstrTitle = "チャットを名前を付けて保存する"; if(GetSaveFileName(&ofn) == 0) return -1; hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WriteFile(hFile, lpszBuf, strlen(lpszBuf) + 1, &dwAccBytes, NULL); SetEndOfFile(hFile); wsprintf(szWinTitle, szTitle_org, szSlotName, szFile); SetWindowText(hWnd, szWinTitle); if(CloseHandle(hFile) == 0) MessageBox(hWnd, "Error CloseHandle", "Error", MB_OK); if (GlobalUnlock(hMem) != 0) MessageBox(hWnd, "ロックカウントが0ではありません", "Warning", MB_OK); if (GlobalFree(hMem) != NULL) MessageBox(hWnd, "メモリ解放失敗に失敗しました", "Error", MB_OK); SendMessage(hMainEdit, EM_SETMODIFY, FALSE, 0); hMenu = GetMenu(hWnd); EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_ENABLED); return 0; }

名前を付けて保存する関数です。基本的には第75章の関数と同じです。 違う点はバッファを動的に確保している点です。また、名前を付けて保存する場合 上書き保存になる場合もあります。このとき今回保存するよりも既存のファイルの方が 大きい場合、よけいなものがファイルに残ることがあります。これを防ぐために WriteFile関数の実行後にSetEndOfFile関数を実行しておくと安心です。

BOOL SetEndOfFile( HANDLE hFile // ファイルハンドル );

ファイルの終端(EOF)を現在のファイルポインタの位置に移動する関数です。
失敗すると0が返され、成功すると0以外が返されます。

int OverWriteMyFile(HWND hWnd) { HANDLE hFile; DWORD dwAccBytes; int nLen; HGLOBAL hMem; char *lpszBuf; nLen = GetWindowTextLength(hMainEdit); hMem = GlobalAlloc(GHND, nLen + 1); if (hMem == NULL) { MessageBox(hWnd, "メモリを確保できません", "Error", MB_OK); return -1; } lpszBuf = (char *)GlobalLock(hMem); GetWindowText(hMainEdit, lpszBuf, nLen + 1); hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WriteFile(hFile, lpszBuf, nLen + 1, &dwAccBytes, NULL); SetEndOfFile(hFile); SendMessage(hMainEdit, EM_SETMODIFY, FALSE, 0); if (CloseHandle(hFile) == 0) { MessageBox(hWnd, "Error CloseHandle", "Error", MB_OK); return -2; } if (GlobalUnlock(hMem) != 0) MessageBox(hWnd, "ロックカウントが0ではありません", "Warning", MB_OK); if (GlobalFree(hMem) != NULL) MessageBox(hWnd, "メモリの解放に失敗しました", "Error", MB_OK); return 0; }

この関数も基本的には第75章で作りました。 バッファを動的に確保している点、上書き後SetEndOfFile関数を実行している点が異なります。

int OpenMyFile(HWND hWnd) { int id; OPENFILENAME ofn; if(SendMessage(hMainEdit, EM_GETMODIFY, 0, 0) == TRUE) { id = MessageBox(hWnd, "チャットの内容が変更されています。保存しますか?", "注意!", MB_OKCANCEL); if (id == IDOK) SaveAsMyFile(hWnd); } memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "chat(*.cht)\0*.cht\0text(*.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 = "cht"; ofn.lpstrTitle = "ファイルオープン!"; if(GetOpenFileName(&ofn) == 0) return -1; OpenMyFile2(hWnd); return 0; }

オープンするファイルの名前を取得する関数です。取得後はOpenMyFile2関数を呼びます。

int OpenMyFile2(HWND hWnd) { HGLOBAL hMem; DWORD dwAccBytes; HMENU hMenu; HANDLE hFile; char szWinTitle[64], *szTitle_org = "猫でもわかるメールスロット[%s][%s]"; char *lpszBuf; DWORD dwSize = 0L; 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; } hMem = GlobalAlloc(GHND, dwSize); if (hMem == NULL) { MessageBox(hWnd, "メモリ確保に失敗しました", "Error", MB_OK); CloseHandle(hFile); return -2; } lpszBuf = (char *)GlobalLock(hMem); SetFilePointer(hFile, 0, 0, FILE_BEGIN); ReadFile(hFile, lpszBuf, dwSize, &dwAccBytes, NULL); lpszBuf[dwAccBytes] = '\0'; Edit_SetText(hMainEdit, lpszBuf); wsprintf(szWinTitle, szTitle_org, szSlotName, szFile); SetWindowText(hWnd, szWinTitle); GlobalUnlock(hMem); GlobalFree(hMem); CloseHandle(hFile); hMenu = GetMenu(hWnd); EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_ENABLED); return 0; }

グローバル変数szFileNameに格納されているファイルを読み出して、エディットコントロールに 表示する関数です。これも基本的には第75章で作りました。

int MyReg() { HKEY hKey; DWORD dwDisposition; LONG lResult; char szCommand[MAX_PATH]; char szIcon[MAX_PATH]; GetCurrentDirectory(sizeof(szCommand), szCommand); strcat(szCommand, "\\mailslot07 %1"); GetCurrentDirectory(sizeof(szIcon), szIcon); strcat(szIcon, "\\mailslot07.exe,0"); lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, "MyChat\\shell\\猫チャット(&C)\\command", NULL,//予約済み "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition); if (lResult != ERROR_SUCCESS) { MessageBox(NULL, "RegCreateKeyExでエラーが発生しました", "Error", MB_OK); return -1; } lResult = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szCommand, strlen(szCommand)); RegCloseKey(hKey); lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, ".cht", NULL, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition); if (lResult != ERROR_SUCCESS) { MessageBox(NULL, "RegCreateKeyExでエラーが発生しました", "Error", MB_OK); return -1; } lResult = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)"MyChat", strlen("MyChat")); RegCloseKey(hKey); lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, "MyChat\\DefaultIcon", NULL, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition); if (lResult != ERROR_SUCCESS) { MessageBox(NULL, "RegCreateKeyExでエラーが発生しました", "Error", MB_OK); return -1; } lResult = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szIcon, strlen(szIcon)); RegCloseKey(hKey); return 0; }

文書ファイルをアプリケーションに関連づけたり、アイコンを割り当てたりする関数です。

RegCreateKeyEx, RegSetValueEx, RegCloseKey関数についてはすでに第129章で 解説してあるのでそちらを見てください。

szCommandに"e:\\project\\mailslot07\\Release\\mailslot07 %1"などと具体的なパス名を指定すると このプログラムを他のところに持っていったときに設定が無効になるので注意してください。 また、VC++6.0上から実行されるとexeは..\Releaseのディレクトリに存在するのにカレントディレクトリは その1つ上になってしまうので注意してください。

GetCurrentDirectory関数については第219章に解説があるので参照してください。

これで、すこし格好が付いてきました。しかし、メールスロットはチャットをするためにあるのではなくて 本来はもっと別の用途に使われるものであることを忘れないでください。


[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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