任务托盘应用程序框架






4.78/5 (3投票s)
系统托盘应用程序的框架。
问题所在
在我编写了一个控制面板小程序来控制我的 NT 服务后,我想到从任务托盘中的一个图标来控制该服务可能会很方便。由于点击任务托盘图标的结果通常与运行控制面板小程序类似,我希望能重用我的大部分代码。最终的结果是创建了一个任务栏小程序框架和一个派生类,该类管理控制面板小程序。然后,我让任务栏小程序加载我的控制面板小程序,并从任务托盘提供访问,无需任何代码更改。生成的类可以加载任何控制面板小程序到任务托盘,只需提供文件名和小程序索引...
任务托盘小程序有什么特别之处?
要将图标放入任务托盘,应用程序只需调用 Shell_NotifyIcon()
来提供图标、工具提示、回调消息和一个用于发送消息的窗口。创建一个仅仅是任务托盘小程序的应用,只需创建一个隐藏窗口,调用 Shell_NotifyIcon()
并处理消息。这些消息是标准的鼠标点击类型消息。这并不难,但需要一些时间。
CJBTaskTrayApplet 提供什么?
创建一个隐藏窗口,并使用一个能够正确响应任务托盘小程序消息并执行所有必要操作的 WndProc,如果只想从任务托盘弹出对话框小程序,可能会很麻烦。该框架允许您从一个基类派生,该基类会为您处理所有这些事务。您只需要重写几个虚函数,就可以弹出对话框!CJBTaskTrayApplet
还允许您在单个可执行文件中包含多个小程序(在任务托盘中显示多个独立图标),并提供自动右键菜单处理。
你的派生类
要实现你的任务托盘小程序,你需要做以下几点:
- 包含 "TaskBarApplet.hpp" 和 "TaskBarAppletMain.hpp",后者提供
WinMain()
实现。 - 公开地从
CJBTaskTrayApplet
派生一个类。 - 实现纯虚函数
GetTrayTip()
和 GetIcon() 来提供框架所需的必要信息。 - 处理
OnLeftDoubleClick()
,以便在用户双击时执行某些操作。通常您可以在此处显示一个对话框或类似内容。 - 在全局作用域创建一个小程序实例。
- 链接 TaskBarApplet.cpp。
如果需要,您还可以:
- 处理
GetMenu()
、HandleMenuCommand()
和ReleaseMenu()
来自定义菜单 - 默认情况下,在响应OnRightButtonUp()
时,您会得到一个包含“关闭”选项的菜单。 - 处理
InitInstance()
以在启动时分配资源。 - 处理
OnLeftButtonDown()
、OnLeftButtonUp()
、OnRightButtonDown()
、OnRightButtonUp()
、OnLeftDoubleClick()
、OnRightDoubleClick()
和OnMouseMove()
中的任意或全部,以自定义小程序的用户界面。
当我实现 CJBControlPanelApplet 时,我将多个小程序放在了一个 DLL 中。在为任务托盘小程序编写了大量相同的“基类有一个静态列表”代码后,我决定尝试创建一个通用的基类,该类包含并隐藏了所有“我是一个包含类实例的静态列表”的功能。这看起来很简单,直到我意识到为了类型安全并在派生类中删除大量的强制类型转换,基类需要了解派生类。毫不犹豫地,我使用了模板化基类,并让派生类从中继承,如下所示:
class CTaskBarApplet : public CLinkedClass<CTaskBarApplet>
然后我思考了一下,这似乎是一件非常奇怪的事情。它效果很好,没有任何警告,但它好吗?从那时起,我与几个人交谈过,并阅读了 Stroustrup 3e(他认为这是“一件好事”),现在我喜欢这个主意。不过,起初它确实显得非常奇怪……
一旦我有了将类实例链接在一起的标准方法,我就需要一种方法在运行时导航这个列表。经过几次失败的尝试,我最终得到了一个类似 STL 的迭代器。这允许您执行类似以下的操作:
for (Iterator addIt = Begin(); addIt != End(); addIt++) { addIt->AddIcon(); // call any derived class method here }
当然,由于我将通用功能移到了基类中,所以我从未在 CJBControlPanelApplet
中使用过基类,但我在许多其他地方都使用了它。
CPlTrayApplet 和 CPlAppletClient 类
到目前为止,我有一个任务托盘框架,但我仍然需要以某种方式重用我的控制面板小程序中的代码。由于 .cpl 本质上是一个 DLL,并且在我编写 CJBControlPanelApplet
时已经弄清楚了 CPlApplets 的工作原理,我决定编写一个包装类,该类可以加载一个控制面板小程序,向其发送所有正确的初始化消息,然后运行该小程序。一旦有了 CJBCPlAppletClient,我便从 CJBTaskTrayApplet 派生了 CJBCPlTrayApplet,并将两者连接起来。最终结果是类似这样的协作:
CJBCPlAppletClient
与任务托盘类没有任何链接。它仅由 CJBCPlTrayApplet
使用。您可以在任何需要以编程方式访问控制面板小程序的地方使用它。
在框架中使用 MFC
当我开始研究任务托盘小程序时,我尝试用 MFC 来编写一个。我见过别人这样做过,但你需要做很多工作才能让它正常工作。回调消息的消息映射条目、防止 CWinApp 的主窗口短暂弹出等等。我不需要 MFC,所以我编写了纯 Win32 解决方案。然后,我想要一个在任务托盘小程序中包含一个复杂的对话框,并且我希望使用 MFC 来处理对话框,而使用我的框架来处理小程序……直到我运行它,对话框因断言失败而崩溃,我没有 CWinApp 派生类,这是一个严重的要求……由于我没有时间将我的小程序框架转换为 CWinApp 派生框架,我作弊了。由于唯一的要求是存在一个 CWinApp 对象,以便对话框拥有一个消息泵来使用,因此我在 WinMain 中放置了一个普通的 CWinApp
对象,并直接调用了 AfxWinInit()
,一切都运行正常……这个版本的 WinMain()
周围的 #ifdef 意味着,通过一个 #define 的切换,您可以拥有启用 MFC 或精简高效版本的任务托盘小程序。
我确实一直想坐下来把 CJBTaskTrayApplet 做成一个真正的 CWinApp 派生类,但是,这个版本是有效的……
请参阅 Len 主页上的文章以获取最新更新。