| つくってみよう−タスクトレイアプリケーションの作成 |
では、実際にどうすればタスクバーに表示されないようになるか、やってみましょう。ここではダイアログベースにします。
まず最初に、VC++でダイアログベースのアプリケーション(MFC AppWizard(.exe))を新規に作成して下さい。
ここでは仮に「UKTaskTray」というプロジェクト名にします。
ディレクトリ(位置)は適当な所を指定し、その他はすべてデフォルトでかまいません。
アプリケーションを新規に作成できたら、
「ResourceView」からIDD_UKTASKTRAY_DIALOGというIDを持つダイアログリソースを開いてください。
ダイアログリソースを開いたら、「表示」−「プロパティ」メニューでプロパティを開きます。
この中にある「拡張スタイル」タブを選択し、「ツールウィンドウ」の項目をチェックします。
これで、ダイアログには拡張スタイルであるWS_EX_TOOLWINDOWスタイルが設定されたことになります。
さて、もう1つやることがあります。実はこのダイアログにはデフォルトでWS_EX_APPWINDOWというスタイルが設定されています。
このスタイルを削除してやらないと、タスクバーから表示を消すことができません。
ではどうやって削除するか?方法は2つです。1つは、リソースファイル(UKTaskTray.rc)をテキストエディタで開き、
WS_EX_APPWINDOWの記述を削除する方法です。VC++の「ファイル」−「開く」メニューでファイル選択ダイアログを開き、
リソースファイルを指定します。「OK」ボタンを押す前に、ダイアログの下部にある「用途」の欄を「テキスト」にしてください。
これで、テキスト形式でリソースファイルを開くことができます。
ファイル内に、以下のような部分がありますよね?この部分がダイアログIDD_UKTASKTRAY_DIALOGを現しています。
IDD_UKTASKTRAY_DIALOG DIALOGEX 0, 0, 319, 201 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_TOOLWINDOW | WS_EX_APPWINDOW CAPTION "UKTaskTray" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,260,7,50,14 PUSHBUTTON "キャンセル",IDCANCEL,260,23,50,14 LTEXT "TODO: ダイアログのコントロールをここに配置",IDC_STATIC,50,90,200,8 END3行目の赤い部分に注目してください。先ほど指定したWS_EX_TOOLWINDOWスタイルの指定がありますね。その後に、 WS_EX_APPWINDOWスタイルが指定されています。これを削除すればよいのです。「 | WS_EX_APPWINDOW」のところを削除すればOKです。
BOOL CUKTaskTrayDlg::OnInitDialog()
{
CDialog::OnInitDialog();
(中略)
// TODO: 特別な初期化を行う時はこの場所に追加してください。
ModifyStyleEx(WS_EX_APPWINDOW, 0); // この1行を追加する!
return TRUE; // TRUE を返すとコントロールに設定したフォーカスは失われません。
}
さて、これでビルドして実行すると、ダイアログが表示されてもタスクバーには何も表示されませんよね。
結構、簡単にできてしまうものです。
さて、クラスを作成したら、メインダイアログからこのダイアログを呼び出すようにしましょう。 「FileView」でUKTaskTrayDlg.cppファイルを開き、OnInitDialog関数を次のようにします。
// ヘッダのインクルードも忘れずに!
#include "ChildDlg.h"
BOOL CUKTaskTrayDlg::OnInitDialog()
{
CDialog::OnInitDialog();
(中略)
// TODO: 特別な初期化を行う時はこの場所に追加してください。
ModifyStyleEx(WS_EX_APPWINDOW, 0);
// 以下の2行を追加する
CChildDlg dlg;
int iID = dlg.DoModal();
return TRUE; // TRUE を返すとコントロールに設定したフォーカスは失われません。
}
さあ、これでビルドをして実行してみましょう。最初にCChildDlgクラスのダイアログが表示され、
これを閉じるとメインのダイアログが表示されますね。あとは、このメインのダイアログが表示されないようにすればよいのです。
それにはどうすればよいのでしょうか?EndDialog(iID);この1行を先ほど追加した2行の直後(DoModalの後ろ)に入れてください。 これでCUKTaskTrayDlgクラスのダイアログを閉じることができます。 ちなみにEndDialog関数の引数には、IDOKまたはIDCANCELのいずれかが入ります。 OKボタンを押した場合とキャンセルボタンを押した場合などでその後の処理が異なる場合には、 適時この引数を変更する必要があります。ここではCChildDlgクラスのダイアログの戻り値をそのまま使用しましょう。
このようにOnInitDialog関数の中でダイアログを終了させてしまえば、ダイアログが表示される前に終了するので、 結果としてダイアログは非表示になります。 ちなみにOnInitDialog関数終了後もメインダイアログを閉じずに非表示のまま生かしておくには、 EndDialog関数の代わりに次の1行を記述します。 (ただし今回はこの方法を使うと非表示のメインダイアログを閉じる手段がありませんので注意!)
SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_HIDEWINDOW);
// ヘッダファイルでの記述 --------------------
class CChildDlg : public CDialog
{
(中略)
private:
NOTIFYICONDATA m_stNotifyIcon;
};
// ソースファイルでの記述 --------------------
BOOL CChildDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: この位置に初期化の補足処理を追加してください
m_stNotifyIcon.cbSize = sizeof(NOTIFYICONDATA);
m_stNotifyIcon.uID = 0;
m_stNotifyIcon.hWnd = m_hWnd;
m_stNotifyIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
m_stNotifyIcon.hIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME );
m_stNotifyIcon.uCallbackMessage = 0;
lstrcpy( m_stNotifyIcon.szTip, "タスクトレイアプリのテスト" );
::Shell_NotifyIcon( NIM_ADD, &m_stNotifyIcon );
// この際なのでダイアログを常に最前面に表示するようにします
// (この処理はタスクトレイとは何の関係もありません)
SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
return TRUE; // コントロールにフォーカスを設定しないとき、戻り値は TRUE となります
// 例外: OCX プロパティ ページの戻り値は FALSE となります
}
void CChildDlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: この位置にメッセージ ハンドラ用のコードを追加してください
::Shell_NotifyIcon( NIM_DELETE, &m_stNotifyIcon );
}
これだけでOKです。やってることは、大してむずかしいことではありません。
NOTIFYICONDATA構造体を使用してタスクトレイアイコンのデータを設定し、
API関数Shell_NotifyIconでタスクトレイへの追加や削除を行っています。
まずポップアップメニュー用のメニューリソースを作成します。作成方法については、 「ポップアップメニューを表示する」 を参照してみてください。メニューの項目は何でもかまいません。リソースのIDは、IDR_MENU1としておきましょう。
次に、タスクトレイアイコンで右クリックされたことを知らせるためのメッセージを定義しなければなりません。 これはユーザーが定義する必要があります。まず、アプリケーションクラスのヘッダーファイルUKTaskTray.hを開き、 以下の1行を挿入します。
#include "resource.h" // メイン シンボル #define WM_USER_POPUP WM_USER+100 // この1行を追加!これで「WM_USER_POPUP」というメッセージを定義することができました。では、タスクトレイアイコンからこのメッセージを発行させるにはどうすればよいのでしょうか?
m_stNotifyIcon.uCallbackMessage = 0;これを、次のように書きかえればよいのです。
m_stNotifyIcon.uCallbackMessage = WM_USER_POPUP;今度は、このメッセージを受け取る処理が必要です。このメッセージはCWnd::WindowProc関数で受け取ることにしましょう。 「ClassWizard」を使って、WindowProc関数を定義します。
LRESULT CChildDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
if( message == WM_USER_POPUP ) {
// カーソルの現在位置を取得
POINT point;
::GetCursorPos(&point);
switch(lParam) {
case WM_RBUTTONDOWN:
SetForegroundWindow(); // この処理を忘れずに!
CMenu menu;
VERIFY(menu.LoadMenu(IDR_MENU1));
CMenu* pPopup = menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
PostMessage(WM_NULL); // この処理も忘れずに!
break;
}
}
return CDialog::WindowProc(message, wParam, lParam);
}
WM_USER_POPUPメッセージは、タスクトレイアイコン上でカーソルの移動、左クリック、
右クリックなどの処理が行われると発行されます。どの処理が行われたかは、
CWnd::WindowProc関数の引数lParamを参照すればわかるようになっています。尚、wParamは常に0になっているようです。