タスクトレーに格納といっても、 実際はタスクトレーにアイコンを表示して、ウィンドウを見えなくしているだけです。 こうすることによってユーザーはウィンドウが小さくなって、タスクトレーに格納されたような 錯覚に陥ります。
タスクトレー中の一番右側の赤丸が今回作ったmailslot05.exeのアイコンです。
これを右クリックすると「タスクトレーから出す」「終了」などのポップアップメニューが出てきます。
では、タスクトレーにアイコンを格納するにはどうすればよいのでしょうか。
と、これだけで実現します。NOTIFYICONDATA構造体は 次のように定義されています。1.NOTIFYICONDATA構造体を設定 2.Shell_NotifyIcon関数を実行
cbSizeは、この構造体の大きさです。typedef struct _NOTIFYICONDATA { DWORD cbSize; HWND hWnd; UINT uID; UINT uFlags; UINT uCallbackMessage; HICON hIcon; char szTip[64]; } NOTIFYICONDATA, *PNOTIFYICONDATA;
hWndは、タスクトレーにあるアイコンからの通知メッセージを 受け取るウィンドウのハンドルを指定します。
uIDは、アイコンのIDです。
uFlagsは、次の中の組み合わせで指定します。
NIF_ICON | hIconメンバが有効です |
NIF_MESSAGE | NIF_uCallbackMessageメンバが有効です |
NIF_TIP | szTipメンバが有効です |
uCallbackMessageは、アプリケーション定義のメッセージ識別子です。 この識別子をhWndに通知メッセージとして送ります。アイコンの マウスイベントが起こったときに通知が送られます。
hIconは、アイコンハンドルです。
szTipは、アイコンのツールチップテキストです。(アイコンをマウスで ポイントすると表示されます。)
アイコン上でマウスイベントが起こるとhWndメンバで指定された ウィンドウにuCallbackMessageで指定したメッセージが送られます。 このときlParamにはマウスメッセージ、wParamにはアイコン識別子が 格納されています。
タスクバーのステータス・エリアにアイコンを追加したり、アイコンを変更したり、 削除したりします。WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage, PNOTIFYICONDATA pnid );
dwMessageは、次のうちの一つを選択します。
NIM_ADD | ステータス・エリアにアイコンを追加します |
NIM_DELETE | ステータス・エリアからアイコンを削除します |
NIM_MODIFY | ステータス・エリアのアイコンを変更します |
pnidには、NOTIFYICONDATA構造体のアドレスを指定します。
では、プログラムを見てみましょう。
メニューに「タスクトレーに格納」(IDM_TASK)が増えました。//mailslot05.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "書き込み(&W)...", IDM_WRITE MENUITEM "タスクトレーに格納(&T)", IDM_TASK MENUITEM SEPARATOR MENUITEM "終了(&X)...", IDM_END END POPUP "メールスロット(&M)" BEGIN MENUITEM "作成(&C)", IDM_CREATESLOT END POPUP "オプション(&O)" BEGIN MENUITEM "IDを表示する(&I)", IDM_ID 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,7,39,50,14 PUSHBUTTON "キャンセル",IDCANCEL,60,39,50,14 LTEXT "自分のメールスロットの名前",IDC_STATIC,7,7,83,8 END MYWRITE DIALOG DISCARDABLE 0, 0, 163, 153 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "メールスロットに書き込み" 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 "OK",IDOK,7,132,50,14 PUSHBUTTON "キャンセル",IDCANCEL,104,132,50,14 LTEXT "メールスロット名",IDC_STATIC,7,45,48,8 LTEXT "コンピュータ名",IDC_STATIC,7,25,43,8 CONTROL "他のコンピュータにメッセージを送る",IDC_CHECK1,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,7,7,119,10 END ///////////////////////////////////////////////////////////////////////////// // // Icon // MYICON ICON DISCARDABLE "myicon.ico"
また、アイコンを右クリックしたときに出てくるポップアップメニュー(RTRAYMENU) も増えました。
アイコンも増えました。アイコンは適当なものを作っておいてください
メインウィンドウに送られてくる通知メッセージMYMSG_TRAYを定義しています。// mailslot05.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); char szClassName[] = "mailslot05"; //ウィンドウクラス char szMailSlot[128];//メールスロットの名前(フルパス付き) char szSlotName[64];//メールスロットの名前(パスなし) HANDLE hSlot; HINSTANCE hInst; HWND hMainEdit; BOOL bID = FALSE;//メッセージIDを表示するかどうか BOOL bIsTask = FALSE;//タスクトレーに格納されているかどうか
また、アイコンのIDも定義しています。
新たにMyTaskTray, MakeRTrayMenu関数を作りました(後述)。
グローバル変数にbIsTaskを作りました。これがTRUEならタスクトレーに 格納されています。
今までとほとんど同じですが、せっかくアイコンを作ったのでウィンドウクラスに このアイコンを登録しておきました。int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 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座標 300, //幅 250, //高さ 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; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); EnableMenuItem(hMenu, IDM_WRITE, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_TASK, 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); break; case WM_SIZE: MoveWindow(hMainEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); 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_CREATESLOT: id = DialogBox(hInst, "MAILSLOTNAME", hWnd, (DLGPROC)MyNameProc); if (id == IDOK) { bEnd = FALSE; hThread = MakeMySlot(hWnd, &bEnd); } else { MessageBox(hWnd, "メールスロットは作成されませんでした", "中止", MB_OK); } break; case IDM_WRITE: DialogBox(hInst, "MYWRITE", hWnd, (DLGPROC)MyWriteProc); 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; } break; case WM_CLOSE: 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); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
メールスロットがまだできていない時、IDM_TASK(タスクトレーに格納)が実行されては まずいのでWM_CREATEメッセージが来たときに、これを無効にしています。
タスクトレーのアイコンから通知メッセージ(MYMSG_TRAY)がきたら、アイコンのIDを確認して lParam(マウスメッセージ)で場合分けします。 右クリックならMakeRTrayMenu関数を呼びます。ダブルクリックならアイコンを削除して メインウィンドウを見えるようにします。
メニューからIDM_TASKが選択されたら、MyTaskTray関数を呼んでアイコンをステータス・エリア に格納します。そして、メインウィンドウを非表示にします。
ポップアップメニューのIDM_SHOWが選択されたら、アイコンを削除してメインウィンドウを 見えるようにします。
プログラム終了時にbIsTaskがTRUEなら、アイコンを削除します。実際にこのような ことが起こるのは、ポップアップメニューから「終了」が選択されたときのみです。
前章とほとんど同じですが、メールスロットが作られたらIDM_TASKを有効にしています。HANDLE MakeMySlot(HWND hWnd, BOOL *lpbEnd) { HMENU hMenu; hMenu = GetMenu(hWnd); char szBuf[64]; DWORD dwThreadID; HANDLE hThread; hSlot = CreateMailslot( szMailSlot, 0, MAILSLOT_WAIT_FOREVER, NULL); if (hSlot == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "メールスロット作成失敗", "Error", MB_OK); } else { EnableMenuItem(hMenu, IDM_CREATESLOT, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_WRITE, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, IDM_TASK, MF_BYCOMMAND | MF_ENABLED); GetWindowText(hWnd, szBuf, sizeof(szBuf)); strcat(szBuf, "["); strcat(szBuf, szSlotName); strcat(szBuf, "]"); SetWindowText(hWnd, szBuf); hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadFunc, (LPVOID)lpbEnd, 0, &dwThreadID); if (hThread == NULL) MessageBox(hWnd, "CreateThread Error", "Error", MB_OK); } return hThread; }
MyNameProc, MyWriteProc関数は前章と全く同じです
前章までとほとんど同じですが、bIsTaskがTRUEの時にメッセージが来たらメッセージボックス でそのことをユーザーに知らせるようにしてあります。このときMessageBox関数の最後の引数に MB_SETFORGROUNDが加わっていることに注意してください。これがないとメッセージボックスが 表に出てこないことがあります。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); 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; }
タスクトレーにアイコンを追加する関数です。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; }
TrackPopupMenu関数については第44章を参照してください。
また、この関数を実行する前にSetForgroundWindow関数でhWndをフォアグラウンドウィンドウにして おいてください。これをしないと、ポップアップメニューを出して、その項目を選択せずに 他の場所をクリックしたとき、ポップアップメニューがいつまでも表示され続けるという 不具合が生じます。
ウィンドウをフォアグラウンドにして、そのウィンドウをアクティブにします。BOOL SetForegroundWindow( HWND hWnd );
hWndには、アクティブにするウィンドウのハンドルを指定します。
今回は、メールスロットとは直接関係のないプログラムでしたが、タスクトレーにアイコンを 格納する方法を知っていると、いろいろな場面で応用できます。
Update 05/Feb/2000 By Y.Kumei