用Win32API Shell_NotifyIcon进行系统托盘图标编程
示例:
附件: StealthDialog_demo.zip (2008-3-14 21:50:27, 13.25 K) 源码: 附件: StealthDialog_src.zip (2008-3-14 21:50:27, 9.86 K) 使用基础 托盘图标添加,编辑,删除包含如下两个步骤: 1.初使化NOTIFYICONDATA类型结构 2.调用Shell_NotifyIcon函数 初使化NOTIFYICONDATA结构
CODE:
// 结构内存用0初使化 - 注意: 一些Windows函数要求这么做,不过我记不得哪些需要,哪些不需要了:)
NOTIFYICONDATA niData; ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); // 得到Shell32的版本号,并依此设置结构成员cbSize的大小 - 注意:MSDN文档中关于这部分的说明有点模糊不清(见本文后面),所以我并不确定下面的代码是否完全正确 ULONGLONG ullVersion = GetDllVersion(_T("Shell32.dll")); if(ullVersion >= MAKEDLLVERULL(6,0,0,0)) niData.cbSize = sizeof(NOTIFYICONDATA); else if(ullVersion >= MAKEDLLVERULL(5,0,0,0)) niData.cbSize = NOTIFYICONDATA_V2_SIZE; else niData.cbSize = NOTIFYICONDATA_V1_SIZE; // 结构成员uID可以是任何UINT值,这个ID用来标志你的托盘图标,Shell_NotifyIcon函数后面将会用到这个值 niData.uID = MY_TRAY_ICON_ID; // 结构成员uFlags指出哪些结构成员是有效的。此处也可以设置提示窗口的样式,如冒泡提示:NIF_INFO niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; // 装载图标。 - 注意:调用完函数Shell_NotifyIcon后应销毁该图标。 niData.hIcon = (HICON)LoadImage( hInstance, MAKEINTRESOURCE(IDI_MY_ICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); // 设置接收托盘事件消息的窗口句柄。 niData.hWnd = hWnd; // 设置发送消息值。- 注意:消息值的大小应该在WM_APP和0xBFFF之间 niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; 调用Shell_NotifyIcon
CODE:
// NIM_ADD表示添加一个新的托盘图标
Shell_NotifyIcon(NIM_ADD,&niData); 隐藏对话框 我看到有很多人在问如何在启动对话框程序时就将程序最小化至系统托盘,这个很简单,创建一个非模态对话框就可以实现。
CODE:
HWND hWnd = CreateDialog( hInstance,
MAKEINTRESOURCE(MY_DIALOG), NULL, (DLGPROC)MyDlgProc ); 然后使用Shell_NotifyIcon把你的图标加到托盘。记住不要调用ShowWindow 菜单和消息 来自托盘的消息将被发送到NOTIFYICONDATA结构成员hWnd所指定的窗口上,并且该消息的ID值由成员uCallbackMessage指定(见上文)。而LPARAM来区分特定类型的消息
CODE:
INT_PTR CALLBACK MyDlgProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) { switch(message) { case MY_TRAY_ICON_MESSAGE: switch(lParam) { case WM_LBUTTONDBLCLK: ShowWindow(hWnd, SW_RESTORE); break; case WM_RBUTTONDOWN: case WM_CONTEXTMENU: ShowContextMenu(hWnd); } break; case... 如果你已经实现了一个上下文菜单,那么消息将通过WM_COMMAND接收,并且菜单项的ID保留在WPARAM的低位字中。
case WM_COMMAND:
switch (LOWORD(wParam)) { case MY_MENU_MSG1: ... break; case MY_MENU_MSG2: ... 注意:如果你已经实现了一个上下文菜单,此时很重要的一点就是,你应该在调用TrackPopupMenu之前把窗口移到最前,这样才能保证菜单被正常的关闭
CODE:
void ShowContextMenu(HWND hWnd)
{ ... HMENU hMenu; // 创建或加载菜单 ... SetForegroundWindow(hWnd); TrackPopupMenu(hMenu, ... 最后清理 在关闭应用程序之前,你应该调用Shell_NotifyIcon并用NIM_DELETE参数把系统托盘图标删除
CODE:
case WM_DESTROY:
Shell_NotifyIcon(NIM_DELETE,&niData); MSDN上关于NOTIFYICONDATA成员cbSize的说明: You can keep your application compatible with all Shell32.dll versions while still using the current header files by setting the size of the NOTIFYICONDATA structure appropriately. Before initializing the structure, use the DllGetVersion function to determine which Shell32.dll version is installed on the system. If it is version 5.0 or greater, initialize the cbSize member with: nid.cbSize = sizeof(NOTIFYICONDATA); Setting cbSize to this value enables all the version 5.0 and 6.0 enhancements. For earlier versions, the size of the pre-6.0 structure is given by the NOTIFYICONDATA_V2_SIZE constant and the pre-5.0 structure is given by the NOTIFYICONDATA_V1_SIZE constant. Initialize the cbSize member with: nid.cbSize = NOTIFYICONDATA_V2_SIZE; Using this value for cbSize enables your application to use NOTIFYICONDATA with earlier Shell32.dll versions, although without the version 6.0 enhancements: |
|
英文原文:
The basics Adding, modifying, hiding and deleting tray icons is accomplished in two steps: Initialize a NOTIFYICONDATA structure Call Shell_NotifyIcon Initialize a NOTIFYICONDATA structure Collapse// zero the structure - note: Some Windows funtions // require this but I can't be bothered to remember // which ones do and which ones don't. NOTIFYICONDATA niData; ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); // get Shell32 version number and set the size of the // structure note: the MSDN documentation about this is // a little dubious(see bolow) and I'm not at all sure // if the code bellow is correct ULONGLONG ullVersion = GetDllVersion(_T("Shell32.dll")); if(ullVersion >= MAKEDLLVERULL(6,0,0,0)) niData.cbSize = sizeof(NOTIFYICONDATA); else if(ullVersion >= MAKEDLLVERULL(5,0,0,0)) niData.cbSize = NOTIFYICONDATA_V2_SIZE; else niData.cbSize = NOTIFYICONDATA_V1_SIZE; // the ID number can be any UINT you choose and will // be used to identify your icon in later calls to // Shell_NotifyIcon niData.uID = MY_TRAY_ICON_ID; // state which structure members are valid // here you can also choose the style of tooltip // window if any - specifying a balloon window: // NIF_INFO is a little more complicated niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; // load the icon note: you should destroy the icon // after the call to Shell_NotifyIcon niData.hIcon = (HICON)LoadImage( hInstance, MAKEINTRESOURCE(IDI_MY_ICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); // set the window you want to recieve event messages niData.hWnd = hWnd; // set the message to send // note: the message value should be in the // range of WM_APP through 0xBFFF niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; Call Shell_NotifyIcon // NIM_ADD adds a new tray icon Shell_NotifyIcon(NIM_ADD,&niData); Stealth dialog I've seen more than a few posts asking how to begin a dialog app minimized to the system tray, hence the name Stealth Dialog. This can be accomplished simply by first creating a modeless dialog: HWND hWnd = CreateDialog( hInstance, MAKEINTRESOURCE(MY_DIALOG), NULL, (DLGPROC)MyDlgProc ); Then use Shell_NotifyIcon as shown above to add your icon to the tray. Do not call ShowWindow. Menus and messages Messages from the tray will go to the window specified by the hWnd member of the NOTIFYICONDATA struct and the message ID is specified by the uCallbackMessage member (see above). The specific message is in the LPARAM. INT_PTR CALLBACK MyDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case MY_TRAY_ICON_MESSAGE: switch(lParam) { case WM_LBUTTONDBLCLK: ShowWindow(hWnd, SW_RESTORE); break; case WM_RBUTTONDOWN: case WM_CONTEXTMENU: ShowContextMenu(hWnd); } break; case... If you implement a context menu, messages are received through WM_COMMAND and the menu item ID is contained in the low-order word of the WPARAM. case WM_COMMAND: switch (LOWORD(wParam)) { case MY_MENU_MSG1: ... break; case MY_MENU_MSG2: ... Important: If you implement a context menu, it's vital that you set your window to the foreground before calling TrackPopupMenu to ensure the menu closes properly. void ShowContextMenu(HWND hWnd) { ... HMENU hMenu; // create or load a menu ... SetForegroundWindow(hWnd); TrackPopupMenu(hMenu, ... Cleaning up Sometime before your app closes you should remove your tray icon by calling Shell_NotifyIcon with the NIM_DELETE flag. case WM_DESTROY: Shell_NotifyIcon(NIM_DELETE,&niData); Notes: The MSDN documentation says about the cbSize member of the NOTIFYICONDATA structure: "You can keep your application compatible with all Shell32.dll versions while still using the current header files by setting the size of the NOTIFYICONDATA structure appropriately. Before initializing the structure, use the DllGetVersion function to determine which Shell32.dll version is installed on the system. If it is version 5.0 or greater, initialize the cbSize member with: nid.cbSize = sizeof(NOTIFYICONDATA); Setting cbSize to this value enables all the version 5.0 and 6.0 enhancements. For earlier versions, the size of the pre-6.0 structure is given by the NOTIFYICONDATA_V2_SIZE constant and the pre-5.0 structure is given by the NOTIFYICONDATA_V1_SIZE constant. Initialize the cbSize member with: nid.cbSize = NOTIFYICONDATA_V2_SIZE; Using this value for cbSize enables your application to use NOTIFYICONDATA with earlier Shell32.dll versions, although without the version 6.0 enhancements:" Now maybe it's my neighbors Turkish tobacco or maybe I'm just not catching on here, but there seems to be an overlapping conflict between "5.0 or greater" and "pre-6.0". Anyway, if anybody can shed any light on this or anything else they care to shed light on, please shed. |