「MFCの基礎 - 常駐ソフトウェア」の版間の差分

(ページの作成:「== 概要 == タスクトレイに常駐するソフトウェアを開発する手順を記載する。<br> <br> 常駐ソフトウェアを開発するには、ソフト…」)
 
13行目: 13行目:
== 常駐ソフトウェア ==
== 常駐ソフトウェア ==
==== タスクバーに非表示のウィンドウを作成する ====
==== タスクバーに非表示のウィンドウを作成する ====
CView派生クラスを継承したCTrayWndクラスに、ウインドウを作成するメソッドを記述する。<br>
CDialog派生クラスまたはCView派生クラスを継承したCTrayWndクラスに、ウインドウを作成するメソッドを記述する。<br>
<br>
<br>
タスクバーに非表示のウィンドウを作成するため、Createメソッドを使用して、拡張ウィンドウスタイルとしてWS_EX_TOOLWINDOWを指定する。<br>
タスクバーに非表示のウィンドウを作成するため、Createメソッドを使用して、拡張ウィンドウスタイルとしてWS_EX_TOOLWINDOWを指定する。<br>
49行目: 49行目:
     delete this;
     delete this;
  }
  }
</syntaxhighlight>
<br>
==== タスクトレイにアイコン・ツールチップ・コールバックを登録・変更・削除する ====
上記で作成したCTrayWndクラスに、タスクトレイにアイコンを登録・変更・削除するメソッドを記述する。
このメソッドは、最終的には、Win32APIのShell_NotifyIcon関数が行う。
Shell_NotifyIcon関数の第1引数で、アイコンの登録・編集・削除を指定する。
Shell_NotifyIcon関数の第2引数のNOTIFYICONDATA構造体において、アイコン、ツールチップ文字列、コールバックを指定する。
<syntaxhighlight lang="c++">
BOOL CTrayWnd::NotifyIcon( DWORD dwMessage, HICON hIcon, LPCTSTR pszTip /*= NULL*/ )
{
    ASSERT(NIM_ADD == dwMessage || NIM_DELETE == dwMessage || NIM_MODIFY == dwMessage);
    NOTIFYICONDATA nid = {0};
    nid.cbSize = sizeof(NOTIFYICONDATA);
    nid.hWnd  = GetSafeHwnd();
    nid.uID    = ID_ICON;  // アイコンを1つだけサポートする
                          // タスクトレイのアイコンID
                          // 1つのプロセスで複数のアイコンをタスクトレイに表示する場合は、それぞれ異なるIDを指定する
                          // このIDはプロセスごとに独立しているので、他のプロセスが同じIDを指定していたとしても、それらが衝突することはない
    nid.uCallbackMessage = WM_USER_TRAYNOTIFYICON;
    nid.uFlags          = NIF_MESSAGE;
    if(NULL != hIcon && m_hIcon_current != hIcon)
    {  // アイコン変更
      nid.uFlags |= NIF_ICON;
      nid.hIcon  = hIcon;
      m_hIcon_current = hIcon;
    }
    if(NULL != pszTip)
    {  // ツールチップ表示
      nid.uFlags |= NIF_TIP;
      _tcsncpy_s(nid.szTip, sizeof(nid.szTip), pszTip, _TRUNCATE);
    }
    return Shell_NotifyIcon(dwMessage, &nid);
}
BOOL CTrayWnd::NotifyIcon( DWORD dwMessage, HICON hIcon, UINT nStringResource )
{
    // 表示メッセージを、ストリングリソースのリソースIDで指定する関数
    CString msg = _T("");
    VERIFY(msg.LoadString(nStringResource));
    return NotifyIcon( dwMessage, hIcon, msg );
}
</syntaxhighlight>
<br>
TrayWnd.cppファイルのヘッダ部に、ID_ICONおよびWM_USER_TRAYNOTIFYICONの定義を記述する。<br>
CTrayWndクラスのコンストラクタに、m_hIcon_currentメンバ変数の初期化処理を記述する。<br>
<syntaxhighlight lang="c++">
// TrayWnd.cpp : 実装ファイル
//
#include "stdafx.h"
#include "MyApplication.h"
#include "TrayWnd.h"
#define ID_ICON (100)
#define WM_USER_TRAYNOTIFYICON (WM_APP+100)
IMPLEMENT_DYNAMIC(CTrayWnd, CWnd)
CTrayWnd::CTrayWnd() : m_hIcon_current(NULL)
{
}
// ...略
</syntaxhighlight>
<br>
TrayWnd.hファイルに、メンバ変数m_hIcon_currentおよびNotifyIconメソッドの宣言を記述する。<br>
<syntaxhighlight lang="c++">
#pragma once
class CTrayWnd : public CWnd
{
    private:
      HICON m_hIcon_current;
    public:
      BOOL Create();
      BOOL NotifyIcon( DWORD dwMessage, HICON hIcon, LPCTSTR pszTip = NULL );
      BOOL NotifyIcon( DWORD dwMessage, HICON hIcon, UINT nStringResource );
      DECLARE_DYNAMIC(CTrayWnd)
      // ...略
}
</syntaxhighlight>
<br>
==== タスクトレイのアイコンにおけるイベント(常駐ソフトウェアの共通処理) ====
CTrayWndクラスに、タスクトレイのアイコンにおけるイベントの応答処理を記述する。<br>
<br>
イベントの応答処理は、早く処理を完了させる必要があるため、イベントに応じて、ウィンドウメッセージをポストする。<br>
<syntaxhighlight lang="c++">
LRESULT CTrayWnd::OnTrayNotify(UINT wParam, LONG lParam)
{
    UINT uiIconID  = (UINT)wParam;
    UINT uiMouseMsg = (UINT)lParam;
    if(ID_ICON != uiIconID)
    {
      return 0;
    }
    // ここで模造メッセージをポストする理由:
    // この通知関数からは、できるだけ早く抜け出す必要がある。
    // たとえば、OnLButtonDownでOLEコントロールを作成しようとすると、
    // 暗号のようなエラーコードRPC_E_CANTCALLOUT_ININPUTSYNCCALLをMFCから受け取ることになるだろう。
    // WinError.hによるとこのエラーの意味は「アプリケーションが入力同期呼び出しをディスパッチしているので、発信呼び出しはできない。」
    switch(uiMouseMsg)
    {
      case WM_LBUTTONDOWN:
          m_bFireDoubleClick = FALSE;
          SetTimer( ID_CLICKTIMER, GetDoubleClickTime(), NULL );
          break;
      case WM_LBUTTONUP:
          if(m_bFireDoubleClick)
          {
            PostMessage(WM_LBUTTONDBLCLK);
          }
          break;
      case WM_LBUTTONDBLCLK:
          m_bFireDoubleClick = TRUE;
          KillTimer(ID_CLICKTIMER);
          break;
      case WM_RBUTTONUP:
          PostMessage(WM_RBUTTONUP);
          break;
    }
    return 0;
}
</syntaxhighlight>
<br>
TrayWnd.cppファイルに、ID_CLICKTIMERの定義を記述する。<br>
CTrayWndクラスのコンストラクタに、メンバ変数m_bFireDoubleClickの初期化処理を記述する。<br>
<br>
また、CTrayWndクラスに、WM_TIMER、WM_LBUTTONUP、WM_RBUTTONUP、WM_LBUTTONDBLCLKメッセージに対応するイベントハンドラを記述する。<br>
<syntaxhighlight lang="c++">
// TrayWnd.cpp
#include "stdafx.h"
#include "MyApplication.h"
#include "TrayWnd.h"
#define ID_ICON                100
#define WM_USER_TRAYNOTIFYICON  WM_APP + 100
#define ID_CLICKTIMER          4
IMPLEMENT_DYNAMIC(CTrayWnd, CWnd)
ON_MESSAGE(WM_USER_TRAYNOTIFYICON, OnTrayNotify)
// ... 略
BEGIN_MESSAGE_MAP(CTrayWnd, CWnd)
    ON_MESSAGE(WM_USER_TRAYNOTIFYICON, OnTrayNotify)
END_MESSAGE_MAP()
// ...略
CTrayWnd::CTrayWnd() : m_hIcon_current(NULL), m_bFireDoubleClick(false)
{
}
// ...略
void CTrayWnd::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: ここにメッセージ ハンドラ コードを追加するか、既定の処理を呼び出します。
    if(ID_CLICKTIMER == nIDEvent)
    {
      KillTimer(nIDEvent);
      ::PostMessage(WM_LBUTTONUP);
    }
    // CWnd::OnTimer(nIDEvent);
}
void CTrayWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: ここにメッセージ ハンドラ コードを追加するか、既定の処理を呼び出します。
    LButtonClick();
    // CWnd::OnLButtonUp(nFlags, point);
}
void CTrayWnd::OnRButtonUp(UINT nFlags, CPoint point)
{
    // TODO: ここにメッセージ ハンドラ コードを追加するか、既定の処理を呼び出します。
    RButtonClick();
    // CWnd::OnRButtonUp(nFlags, point);
}
void CTrayWnd::OnLButtonDblClk(UINT nFlags, CPoint point)
{
    // TODO: ここにメッセージ ハンドラ コードを追加するか、既定の処理を呼び出します。
    LButtonDoubleClick();
    // CWnd::OnLButtonDblClk(nFlags, point);
}
void CTrayWnd::LButtonClick()
{
    POINT pt;
    GetCursorPos( &pt );
    CMenu menu;
    menu.LoadMenu(IDM_TRAY_L);
    CMenu *pPopup = menu.GetSubMenu(0);
    // SetForgroundWindowとPostMessageが必要な理由は、Knowledge Base (Q135788)参照のこと
    SetForegroundWindow();
    pPopup->TrackPopupMenu( TPM_RIGHTBUTTON, pt.x, pt.y, this );
    ::PostMessage(WM_NULL);
}
void CTrayWnd::RButtonClick()
{
    POINT pt = {0};
    GetCursorPos(&pt);
    CMenu menu;
    menu.LoadMenu(IDM_TRAY_R);
    CMenu *pPopup = menu.GetSubMenu(0);
    // SetForgroundWindowとPostMessageが必要な理由は、Knowledge Base (Q135788)参照のこと
    SetForegroundWindow();
    pPopup->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
    ::PostMessage(WM_NULL);
}
void CTrayWnd::LButtonDoubleClick()
{
    ::SendMessage(WM_COMMAND, ID_APP_ABOUT);
    // または
    CAboutDlg dlg;
    dlg.DoModal();
}
</syntaxhighlight>
<br>
TrayWnd.hファイルに、メンバ変数m_bFireDoubleClickおよびOnTrayNotifyメソッドの宣言を記述する。<br>
<syntaxhighlight lang="c++">
// TrayWnd.h
#pragma once
class CTrayWnd : public CWnd
{
    private:
      HICON m_hIcon_current;
      BOOL m_bFireDoubleClick;
    protected:
      virtual void LButtonClick();
      virtual void RButtonClick();
      virtual void LButtonDoubleClick();
    public:
      BOOL Create();
      BOOL NotifyIcon(DWORD dwMessage, HICON hIcon, LPCTSTR pszTip = NULL);
      BOOL NotifyIcon(DWORD dwMessage, HICON hIcon, UINT nStringResource);
      DECLARE_DYNAMIC(CTrayWnd)
    public:
      CTrayWnd();
      virtual ~CTrayWnd();
    protected:
      DECLARE_MESSAGE_MAP()
      virtual void PostNcDestroy();
      afx_msg LRESULT OnTrayNotify(UINT wParam, LONG lParam);
      afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
      afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
      afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
};
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>