VC的托盤程序

原文:VC的托盤程序

見網友對VC中的托盤程序的評論很好,在這裏記錄一下以供以後可能參考:

http://search.csdn.net/Expert/topic/2394/2394557.xml?temp=.363598

 

回覆人: asp?sendto=yintongshun" target=_blank>yintongshun(左岸思雨) ( 兩星(中級)) 信譽:160 2003-10-25 18:47:23Z 得分:0
 
 
 

 幫你轉一下
VC編程製作系統托盤程序
Windows操作系統中的某些程序運行時不顯示運行窗口,只在任務欄上顯示一個圖標,表示程序正在運行,用戶可以通過鼠標與應用程序交互,比如金山毒霸等應用程序,我們有時也需要編制一些僅在後臺運行的類似程序,爲了不干擾前臺程序的運行界面和不顯示不必要的窗口,應使程序運行時的主窗口不可見。同時將一個圖標顯示在任務欄右端靜態通告區中並響應用戶的鼠標動作。下面介紹Visual C++開發這類程序的設計方法。

 


一、隱藏程序的主窗口

首先,要使程序的主窗口不可見,並且不在任務欄上出現任務按鈕,要做到這兩點,需分別設置主邊框窗口的風格和擴展風格:

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
 cs.style =WS_POPUP;//使主窗口不可見
 cs.dwExStyle |=WS_EX_TOOLWINDOW;//不顯示任務按鈕
 return CFrameWnd::PreCreateWindow(cs);
}

二、將表示程序運行的圖標加入任務欄

在主框架窗口的CMainFrame::OnCreate()函數中調用上述函數,就可以在任務條上顯示圖標這一步是利用系統API函數Shell_NotifyIcon()將一個圖標顯示在任務欄的通告區中。該函數的原型爲:在調用該函數之前,需要確定其參數的取值。其中Shell_NotifyIcon()函數的第一個參數是一個預定義的消息,可以取如下值之一:NIM_ADD、NIM_DELETE或NIM_MODIFY,分別表示添加圖標、刪除圖標或修改圖標。另一個參數爲指向NOTIFYICONDATA類型的指針。其原型爲:

typedef struct _NOTIFYICONDATA {
 DWORD cbSize;
 HWND hWnd;
 UINT uID;
 UINT uFlags;
 UINT uCallbackMessage;
 HICON hIcon;
 charszTip[64]; }
NOTIFYICONDATA

在該結構的成員中,cbSize爲該結構所佔的字節數,hWnd爲接受該圖標所發出的消息的窗口的句柄(鼠標在任務欄上程序圖標上動作時圖標將發出消息,這個消息用戶要自己定義),uID爲被顯示圖標的ID,uFlags指明其餘的幾個成員(hIcon、uCallBackMessage和szTip)的值是否有效,uCallbackMessage爲一個用戶自定義的消息,當用戶在該圖標上作用一些鼠標動作時,圖標將嚮應用程序的主框架窗口(hWnd成員中指定的窗口)發出該消息,。hIcon爲將在任務欄上被顯示圖標的句柄,szTip鼠標停留在該圖標上時顯示的字符串。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 NOTIFYICONDATA tnd;
 tnd.cbSize=sizeof(NOTIFYICONDATA);
 tnd.hWnd=this->m_hWnd;
 tnd.uID=IDR_MAINFRAME;
 tnd.uFlags=NIF_MESSAGE|NIF_ICON|NIF_TIP;
 tnd.uCallbackMessage=WM_MYMESSAGE;
 file://用戶自定義的消息,即鼠標在任務欄上程序圖標上動作時圖標發送的消息
 tnd.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
 strcpy(tnd.szTip,"測試程序");//圖標提示爲"測試程序"
 Shell_NotifyIcon(NIM_ADD,&tnd);//向任務欄添加圖標
}

三、用戶與程序交互的實現

用戶進行交互,也就是當用戶在該圖標上單擊或雙擊鼠標左鍵或右鍵時要執行相應的操作,至少也要響應用戶終止該程序的意願。上面已經提到,當用戶在圖標上進行鼠標動作時,將向hWnd成員中指定的窗口發出自定義的消息,該消息爲uCallbackMessage成員所指定的WM_MYESSAGE,取值爲WM_USER+101(如何自定義消息,我就不多說了)。我們要實現任務就是在hWnd窗口中響應該自定義消息:

void CMainFrame::OnMYMESSAGE(WPARAM wParam,LPARAM lParam)
{
 UINT uID;//發出該消息的圖標的ID
 UINT uMouseMsg;//鼠標動作
 POINT pt;
 uID=(UINT) wParam;
 uMouseMsg=(UINT) lParam;
 if(uMouseMsg==WM_RBUTTONDOWN)//如果是單擊右鍵
 {
  switch(uID)
  {
  case IDR_MAINFRAME://如果是我們的圖標
   GetCursorPos(&pt);//取得鼠標位置
   AfxGetApp( )-> m_pMainWnd->ShowWindow(SW_SHOWNORMAL);//顯示程序窗口
   break;
   default:
  }
 }
 return;
}

四、程序結束時刪除程序圖標

程序結束時,需要刪去通告區中的圖標,這時還應該調用Shell_NotifyIcon函數,只不過第一個參數是表示刪除圖標的NIM_DELETE了:

void CMainFrame::~CmainFrame()
{
 NOTIFYICONDATA tnid;
 tnid.cbSize=sizeof(NOTIFYICONDATA);
 tnid.hWnd=this->m_hWnd;
 tnid.uID=IDR_MAINFRAME;//保證刪除的是我們的圖標
 Shell_NotifyIcon(NIM_DELETE,&tnid);
}

 

Top
 
  回覆人: asp?sendto=comeonstuding" target=_blank>comeonstuding(howareyou) ( 三級(初級)) 信譽:100 2003-10-25 23:59:14Z 得分:50
 
 
 


系統托盤編程
 
2002-11-3 15:48:18   VCKBASE.COM   作者不詳   閱讀次數: 1999
    Windows 95以及後來的Windows版本允許你將程序圖標放入系統托盤。所謂系統托盤,通常指的是屏幕右下方顯示時間,音量等圖標的那個區域。這個區域主要用於顯示狀態信息或者當你運行的程序不可見時,允許你方便地訪問程序的主要特性。這個區域還可以用於顯示小程序的圖標,以便用戶容易訪問主程序,或者在預定的時間加載主程序。有些系統托盤圖標可以變化用以指示程序狀態,例如,瀏覽器的系統托盤圖標當modem接收和發送數據時顯示的是不同的圖標。把鼠標移到托盤圖標上停留一下常常會顯示一個提示,根據程序的狀態,它可能也會變化。在托盤圖標上單擊鼠標右鍵常常顯示一個程序菜單,而雙擊鼠標左鍵常常可以啓動主窗口或應用程序。訪問系統托盤的方法是通過Shell_NotifyIcon函數和NOTIFYICONDATA結構實現的。

typedef struct _NOTIFYICONDATA {

   DWORD cbSize;
   HWND hWnd;
   UINT uID;
   UINT uFlags;
   UINT uCallbackMessage;
   HICON hIcon;
   TCHAR szTip[64];
   DWORD dwState;         //Version 5.0
   DWORD dwStateMask;     //Version 5.0
   TCHAR szInfo[256];     //Version 5.0
   UINT uTimeout;         //Version 5.0
   TCHAR szInfoTitle[64]; //Version 5.0
   DWORD dwInfoFlags;     //Version 5.0
} NOTIFYICONDATA, *PNOTIFYICONDATA;
爲了要在系統托盤中顯示圖標,用NIM_ADD標誌調用Shell_NotifyIcon函數。
#define ID_TASKBARICON 100
#define WM_ICONNOTIFY           (WM_USER + 101)        //自定義消息

NOTIFYICONDATA nid;

// 初始化系統托盤圖標
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = ID_TASKBARICON;
nid.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP;
nid.uCallbackMessage = WM_ICONNOTIFY;
nid.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_TRAY1), IMAGE_ICON, 16, 16, 0);
strcpy(nid.szTip, "My Tooltip Text");

Shell_NotifyIcon(NIM_ADD, &nid);

cbSize成員是結構的大小(使用它主要是爲了支持將來這個結構大小增加)。

    hWnd是窗口句柄。當圖標發生某事件時(如單擊、雙擊等),Windows將向窗口發送uCallbackMessage成員指定的消息。
    uID成員指定與圖標關聯的ID。它不是很重要,除非你需要顯示並跟蹤幾個圖標。
    uFlag成員告訴Windows應該讀取哪個成員。當添加一個圖標時,應該包含這個結構的大多數成員。當更新圖標時,如只是需要改變圖標時,你只要設置相應的標誌就可以了。

    hIcon成員是你想顯示的圖標。
    最後,szTip成員是提示文本。設置好這些結構成員後,調用Shell_NotifyIcon函數。

    當與圖標關聯的事件發生時,Windows將發送uCallbackMessage成員指定的消息。IParam包含發送的消息。當獲得WM_LBUTTONDBLCLK消息時顯示主窗口或者啓動主程序。當獲得WM_RBUTTONUP消息時顯示菜單。

    注意:如果在系統托盤中單擊鼠標右鍵,有時會有一個彈出式(上下文菜單)菜單顯示/消失的怪現象,詳細信息擊解決辦法請參閱微軟知識庫文章Q135788,也可以參考下列代碼加以解決。

switch(nMsg) {
   case WM_ICONNOTIFY:
      switch(lParam) {
         case WM_LBUTTONDBLCLK:
            // Load main window here
            break;
         case WM_RBUTTONUP:
            {
               POINT point;
               HMENU hMenu, hSubMenu;
               // Get mouse position
               GetCursorPos(&point);
               // Popup context menu
               hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MYMENU));
               hSubMenu = GetSubMenu(hMenu, 0);
               SetMenuDefaultItem(hSubMenu, IDM_DEFAULTCMD, FALSE);
               SetForegroundWindow(hMainDlg);         // Per KB Article Q135788
               TrackPopupMenu(hSubMenu,
                  TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_LEFTALIGN,
                  point.x, point.y, 0, hWnd, NULL);
               PostMessage(hMainDlg, WM_NULL, 0, 0);   // Per KB Article Q135788
               DestroyMenu(hMenu);
            }
            break;
         default:
            return FALSE;
      }
}

不論什麼時候,你都可以用 NIM_MODIFY 調用Shell_NotifyIcon。
程序終止之前,用 NIM_DELETE 調用 Shell_NotifyIcon從托盤中清除圖標。

Shell_NotifyIcon(NIM_DELETE, &nid);

 

很多軟件例如:KV3000等在關閉之後會在托盤區有一個小的托盤圖標,這表示程序並沒有真正停止而在後臺運行。當我們單擊鼠標時可以使它在桌面上出現一個最大化窗口;而如果我們單擊程序右上角х,或者單擊文件菜單中的退出項時程序仍然在托盤上運行;只有我們右鍵單擊托盤圖標在出現的菜單中選擇退出才能夠真正退出程序等。

現在我們通過一個類CSystemTray和修改系統菜單來實現上述功能。

我們先來改變系統菜單。

 1、當我們在程序的標題欄上單擊鼠標右鍵時,只出現如圖的幾項。

而通過MFC生成的還會出現About一項,我們已經把它給去掉了。

我們調用函數GetSystemMenu()來取的系統菜單,然後調用DeleteMenu()來去掉不要的系統菜單項,在這裏我們去掉了“關閉”下面的分隔線和“about”項代碼如下:

CMenu *pSystemMenu=GetSystemMenu(FALSE);
pSystemMenu->DeleteMenu(8,MF_BYPOSITION);
pSystemMenu->DeleteMenu(7,MF_BYPOSITION);
其中的數字7和8表示系統菜單的第7項和第8項,注意分隔線在VC中也算是一項。(這裏和前面所講的修改系統菜單的方法是一樣的)。 當我們去掉“about”這項後就應該在系統命令中也去掉相應的相應代碼。我們找到函數OnSysCommand(UINT nID, LPARAM lParam)去掉其中截獲about對話框的代碼: if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
         CAboutDlg dlgAbout;
         dlgAbout.DoModal();
}
它對我們沒有任何用處,放在這裏只能給我們調試帶來麻煩。當然如果你不去掉它程序完全沒有一點錯誤。 那麼,爲什麼我們要去掉這兩項呢,因爲我們準備在托盤中實現相同的功能。 1, 我們再改變程序的關閉標誌“X”的功能,把它改爲隱藏而不是完全關閉。 我們來截獲關閉命令,把它改爲隱藏。if ((nID & 0xFFF0)==SC_CLOSE){
         //OnClose();本來這個是關閉的這裏也改爲隱藏。
          AnimateWindow(GetSafeHwnd(),1000,AW_HIDE|AW_BLEND);
          KillTimer(0);
          ShowWindow(SW_HIDE);//系統菜單的關閉也改爲隱藏。
}
在ShowWindow(SW_HIDE)前面的兩句是爲了實現一種特效,即是慢慢很溫和的隱藏,不過只有這兩句還是不行的還應該在stdafx.h的前面加上如下兩行代碼: #undef WINVER
#define WINVER 0X500
 2、我們來建立一個托盤菜單,ID號爲IDR_TUOPAN當我們在托盤上點擊右鍵時,會出現一個菜單,在菜單裏我們有:關於、顯示(隱藏)、換歌、退出四項。 我們先在頭文件VioletPlayDlg.h中自定義一個消息:
#define WM_USER_TRAY_NOTIFICATION (WM_USER+0x101)
注意它的格式。在文件中定義一個托盤變量CSystemTray m_trayIcon,有了這些後我們就可以先做一個托盤雛形了。在主文件VioletPlayDlg.cpp中來產生一個托盤了。我們用Creat()函數定義如下: m_trayIcon.Create(this, WM_USER_TRAY_NOTIFICATION, "VioletPlay", m_hIcon, IDR_TUOPAN);
其中第二個參數爲我們自定義的功能,即出現托盤圖標,第三個參數爲當我們把鼠標放在圖標上時出現的說明文字,第五個參數爲當我們單擊右鍵時出現的菜單,由於我們通過這個函數並不能響應右鍵,所以在這裏並不能出現這個菜單,(下文會講) 當我們的鼠標離開圖標的時候出現的說明文字VioletPlay會自動消失,我們通過一個條件語句來實現。首先我們定義一個BOOL變量BOOL start_minimized我們來判斷它的值: if (start_minimized)
 PostMessage(WM_CLOSE);
當我們鼠標移走的時候就發送一個CLOSE命令,關閉窗口。我們在文件VioletPlay.cpp中加上一句代碼時程序運行的時候即可以在托盤中顯示又可以在桌面上顯示: dlg.start_minimized=GetProfileInt(_T(""),_T("StartMinimized"),FALSE);
我們在這裏在顯示主程序的對話框之前加上這句代碼,起到一個“攔截”作用。 這樣我們就實現了最初的功能,即在托盤中出現了一個圖標,但是這還是遠遠不夠的,我們在來一步步實現其他的功能。

 3、雖然我們有了一個托盤菜單,但是我們到現在還不能通過單擊鼠標右鍵使它出現,因爲我們的程序還不能響應這個消息,下面我們通過重載函數:afx_msg LONG OnTrayNotification( WPARAM wparam, LPARAM lparam )並在裏面加上響應代碼來實現這個功能。
我們用switch…case…結構來響應鼠標右鍵這一消息,當然如果你願意也是可以用if語句來實現同樣的功能的。

代碼如下:

LONG CVioletPlayDlg::OnTrayNotification(WPARAM wparam, LPARAM lparam)
{  
 switch ( lparam )
 {
 case WM_RBUTTONDOWN:
 {// 用戶在托盤圖標上單擊鼠標右鍵,彈出上下文菜單隱藏/顯示對話框。
  CMenu oMenu;
  if (oMenu.LoadMenu(IDR_TUOPAN))       
  {
   CMenu* pPopup = oMenu.GetSubMenu(0);
   ASSERT(pPopup != NULL);
   CPoint oPoint;
   if (IsWindowVisible())// 根據對話框窗口的顯示/隱藏狀態修改菜單名稱
    oMenu.ModifyMenu(IDC_SHOW,MF_STRING,IDC_SHOW,"隱藏(&H)");
   else
    oMenu.ModifyMenu(IDC_SHOW,MF_STRING,IDC_SHOW,"顯示(&S)");
   // 確定鼠標位置以便在該位置附近顯示菜單
   GetCursorPos( &oPoint );
   SetForegroundWindow();
   pPopup->TrackPopupMenu(
    TPM_LEFTALIGN | TPM_RIGHTBUTTON,
    oPoint.x, oPoint.y, this);
  }
 }
 break;
 // 單擊/雙擊鼠標左鍵均顯示出對話框
 case WM_LBUTTONDBLCLK:
 case WM_LBUTTONDOWN:
  OnShow();
  break;
 }
 return 0;
}
在上述代碼中我們同時實現了動態改變菜單“顯示/隱藏”的功能,當程序主界面出現時,菜單中變爲“隱藏”表示如果我們再單擊它時程序主界面就隱藏起來了。如果爲隱藏狀態時菜單中變爲“顯示”表示當我們單擊它時可以顯示程序主界面。 另外,在上面代碼的最後三句表示不管我們是單擊還是雙擊鼠標左鍵它發送的命令都是一樣的即出現主程序界面,即OnShow()。
我們在對托盤菜單中的命令一個一個的分析。 這裏的“退出”菜單我們使程序真正的退出,因此它接受的命令爲 ::PostQuitMessage(0),使程序真正退出,注意前面我們也說了,其它的兩個“退出”皆爲隱藏。因爲程序的使用者也許並不知道這一點,所以我們有必要在這裏設計一個對話框進行提醒,如果要求這一點我們就把點擊“退出”菜單所發送的命令改爲調用一個AfxMessageBox,當是使用者點擊“是”才實現真正的退出。點擊“否”而不退出。
代碼如下: void CVioletPlayDlg::OnClose()
{
 if(AfxMessageBox("真的要退出嗎",MB_YESNO)==IDYES)
 {
  AnimateWindow(GetSafeHwnd(),1000,AW_HIDE|AW_BLEND);
  KillTimer(0);
  ::PostQuitMessage(0);
 }
}
當你點擊了文件菜單中的退出時也是同樣的道理只是當你點擊“是”時只能隱藏主界面,而當你點擊“否”時,不能隱藏主界面。我們只需要把上面代碼中的退出命令:::PostQuitMessage(0)改爲隱藏命令ShowWindow(SW_HIDE)就可以了。

到現在爲止我們實現了托盤圖標的所有功能。

注意:類CSystemTray繼承於CWnd是public形式,類的定義和實現可以參考文件:VioletPlay,文件名分別爲SystemTray.h和SystemTray.cpp 源文件見:VioletPlay (注,有一些代碼用的是VCKBASE網站中,我只對其進行了分析和總結。再此表示謝意和歉意

Top
 
  回覆人: asp?sendto=comeonstuding" target=_blank>comeonstuding(howareyou) ( 三級(初級)) 信譽:100 2003-10-26 00:01:24Z 得分:50
 
 
 

 有的網友開發了自己的托盤類,實現起來略微煩瑣。在這裏我向大家推薦一個實現簡單而十分有效的托盤類(是我在一本書中學來的)。
1、 把TrayIcon.cpp和TrayIcon.cpp拷貝到你的項目目錄,並添加到項目中。


2、 在DemoDlg.h中加入#include “TrayIcon.h”
3、 通過類嚮導向類CDemoDlg添加成員變量CTrayIcon m_CTrayIcon;
4、 建立菜單資源,使其ID爲:IDR_DEMO,設計菜單:

向demo1和exit添加事件處理程序


void CDemoDlg::OnFileDemo1()
{
 ShowWindow(SW_SHOW);
 m_TrayIcon.RemoveIcon();
}
 
void CDemoDlg::OnFileExit()
{
 m_TrayIcon.RemoveIcon();
 OnCancel();
}
5、 在DemoDlg.cpp中自定義消息 #define WM_ICON_NOTIFY WM_USER+10,並在聲明消息處聲明消息處理函數: BEGIN_MESSAGE_MAP(CDemoDlg, CDialog)
 ......
 ON_MESSAGE(WM_ICON_NOTIFY, OnTrayNotification)
 ……
END_MESSAGE_MAP()

在類CDemoDlg中增加成員函數:LRESULT OnTrayNotification(WPARAM wParam,LPARAM lParam);
實現部分:LRESULT CDemoDlg::OnTrayNotification(WPARAM wParam,LPARAM lParam)
{
 return m_TrayIcon.OnTrayNotification(wParam,lParam);
}

6、 在對話框添加“開始”按扭,並雙擊“開始”按扭編輯處理程序

void CDemoDlg::OnBnClickedButton1()
{
 m_TrayIcon.Create(this,WM_ICON_NOTIFY,"鼠標指向時顯示",m_hIcon,IDR_DEMO); //構造
 ShowWindow(SW_HIDE); //隱藏窗口
}

7、 可以靈活使用其它類成員函數。如:SetIcon改變圖標,可以通過Timer消息實現托盤圖標動畫效果。
8、 刪除托盤圖標:m_TrayIcon.RemoveIcon();


 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章