今回、新しく作る文書タイプは、「*.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;//コマンドライン引数があるかどうか
いつもとちょっと違います。コマンドライン引数はWinMain関数の3番目の引数で 渡されます。コマンドライン引数がある場合はこれをオープンするフルパス付きファイル名 としてグローバル変数に格納します。また、ファイルタイトルはGetFileTitle関数で取得して やはりグローバル変数に格納しておきます。さらに、bCmdLineをTRUEにしておきます。 GetFileTitle関数はすでに 第136章などで出てきましたがまだ解説を書いていませんでした。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; }
使い方の説明は不要と思いますが、成功すれば0,ファイル名が無効の時はマイナスの値が 戻ります。バッファサイズが小さすぎるときは必要なサイズが返されます。short GetFileTitle( LPCTSTR lpszFile, // フルパス付きファイル名 LPTSTR lpszTitle, // ファイルタイトルを受け取るバッファのポインタ WORD cbBuf // バッファの長さ );
よけいなことですがプログラムの終了時、メッセージループを抜けた時、これを メッセージボックスで知らせるようにしました。いろいろな場合で試して必ず ループを抜けて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」ボタンを押したときは「無効な名前です」というメッセージボックスを 出して、再度入力を求めるようにしました。
キャンセルボタンはなくなりましたが、ばってんボタン?を押されたときは、名前を決めるよう 注意のメッセージボックスを出すようにしました。
書き込み用ダイアログボックスのプロシージャです。キャンセルボタンが廃止されたので case IDCANCEL:の部分がそっくり不要になりました。これに関連したrc, hMenu変数も 不要となりました。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; }
メールスロットにメッセージが来ていれば、これをエディットコントロールに書き出すスレッド関数です。 今までとほとんど同じですが、メッセージをエディットコントロールに書き出したらEM_SETMODIFYメッセージを 送って変更が有ったことを知らせます。直接エディットコントロールに入力した場合は不要ですが Edit_SetTextマクロなどで入力した場合は知らせる必要があります。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; }
これは、第254章と同じです。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; }
名前を付けて保存する関数です。基本的には第75章の関数と同じです。 違う点はバッファを動的に確保している点です。また、名前を付けて保存する場合 上書き保存になる場合もあります。このとき今回保存するよりも既存のファイルの方が 大きい場合、よけいなものがファイルに残ることがあります。これを防ぐために WriteFile関数の実行後にSetEndOfFile関数を実行しておくと安心です。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; }
ファイルの終端(EOF)を現在のファイルポインタの位置に移動する関数です。BOOL SetEndOfFile( HANDLE hFile // ファイルハンドル );
この関数も基本的には第75章で作りました。 バッファを動的に確保している点、上書き後SetEndOfFile関数を実行している点が異なります。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; }
オープンするファイルの名前を取得する関数です。取得後はOpenMyFile2関数を呼びます。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; }
グローバル変数szFileNameに格納されているファイルを読み出して、エディットコントロールに 表示する関数です。これも基本的には第75章で作りました。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; }
文書ファイルをアプリケーションに関連づけたり、アイコンを割り当てたりする関数です。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章に解説があるので参照してください。
これで、すこし格好が付いてきました。しかし、メールスロットはチャットをするためにあるのではなくて 本来はもっと別の用途に使われるものであることを忘れないでください。
Update 13/Feb/2000 By Y.Kumei