C++ 中的 Windows 7 实用程序:任务栏进度和状态指示器






4.97/5 (44投票s)
介绍如何将任务栏进度条和覆盖图标与您的 Windows 7 应用程序一起使用。
目录
引言
Windows 7 任务栏操作的主要变化之一是微软称为“外围状态”的区域。这包括两种状态指示器:长操作的进度,以及重要通知的图标。应用程序可以在长时间操作期间继续使用进度对话框,但 Windows 7 任务栏还允许应用程序在其任务栏按钮上显示进度条,这样用户就可以一目了然地看到进度指示器,而无需切换到应用程序。
许多应用程序还使用通知区域来传达重要的状态信息。例如,Outlook 会在您收到新电子邮件时显示一个图标。然而,在 Windows 7 中,通知区域图标默认是隐藏的,因此通知区域不再适用于显示此类状态。任务栏允许应用程序在其任务栏按钮的现有图标上显示一个 16x16 的图标。这可以防止通知区域过于拥挤,并使状态图标与创建它的应用程序保持视觉关联。
本文的示例应用程序是我在文章《使用 Internet Explorer 为您下载文件》中文件下载器的重写。该应用程序在其任务栏按钮中显示下载进度,并在对话框中显示传统的进度条。这个应用程序以前没有通知区域图标,但为了演示涉及的 API 调用,它还包含了显示状态图标的命令。
本文的示例代码是用 Visual Studio 2008、WTL 8.0 和 Windows 7 RC SDK 构建的。
使用新的任务栏接口
这是修改后的下载器应用程序的屏幕截图
与跳转列表一样,进度指示器通过新的 COM 接口公开。
ITaskbarList3 接口
在我们的应用程序可以使用外围状态功能之前,我们必须创建一个 ITaskbarList3
接口。ITaskbarList3
是 Windows 7 中的一个新接口,它提供了对许多新任务栏功能的访问。然而,这比仅仅调用 CoCreateInstance()
要多一些。我们必须等到 Windows 告诉我们任务栏按钮已经创建。Windows 通过向我们的主窗口发送一个名为 TaskbarButtonCreated
的注册消息来实现这一点。一旦我们收到该消息,就可以安全地创建 ITaskbarList3
接口并调用其方法。
主对话框类有一个私有数据成员,用于保存此消息的 ID。该成员在应用程序的启动序列期间初始化,并在消息映射中使用。
class CMainDlg : public CDialogImpl<CMainDlg> { public: BEGIN_MSG_MAP(CMainDlg) MESSAGE_HANDLER_EX(m_uTaskbarBtnCreatedMsg, OnTaskbarBtnCreated) // other message handlers... END_MSG_MAP() LRESULT OnTaskbarBtnCreated ( UINT uMsg, WPARAM wParam, LPARAM lParam ); private: static const UINT m_uTaskbarBtnCreatedMsg; }; const UINT CMainDlg::m_uTaskbarBtnCreatedMsg = RegisterWindowMessage ( _T("TaskbarButtonCreated") );
CMainDlg
还有一个成员 m_pTaskbarList
,它是一个 CComPtr<ITaskbarList3>
。TaskbarButtonCreated
消息的消息处理程序调用 CoCreateInstance()
来初始化此接口。
LRESULT CMainDlg::OnTaskbarBtnCreated ( UINT uMsg, WPARAM wParam, LPARAM lParam ) { m_pTaskbarList.Release(); m_pTaskbarList.CoCreateInstance ( CLSID_TaskbarList ); return 0; }
任务栏可能多次发送此消息。例如,如果 Explorer 崩溃并重新启动,它将重新创建任务栏,并再次通知所有正在运行的应用程序其按钮已创建。为了处理我们多次收到 TaskbarButtonCreated
消息的情况,我们会释放我们可能拥有的任何现有接口,然后创建一个新的。
如果您的应用程序将在 Windows 7 之前的 Windows 版本上运行,请注意,任何人都可以注册一个名为 TaskbarButtonCreated
的窗口消息并广播它。为了防止这种情况,如果操作系统版本低于 Windows 7,您应该忽略该消息。这是检查操作系统版本的更新后的消息处理程序。
LRESULT CMainDlg::OnTaskbarBtnCreated ( UINT uMsg, WPARAM wParam, LPARAM lParam ) { DWORD dwMajor = LOBYTE(LOWORD(GetVersion())); DWORD dwMinor = HIBYTE(LOWORD(GetVersion())); // Check that the OS is Win 7 or later (Win 7 is v6.1). if ( dwMajor > 6 || ( dwMajor == 6 && dwMinor > 0 ) ) { m_pTaskbarList.Release(); m_pTaskbarList.CoCreateInstance ( CLSID_TaskbarList ); } return 0; }
还有最后一件事情需要处理:如果我们的应用程序以提升权限运行,我们必须告诉 Windows 允许将 TaskbarButtonCreated
消息发送到我们的窗口。我们在 WM_INITDIALOG
处理程序中执行此操作。
BOOL CMainDlg::OnInitDialog ( HWND hwndFocus, LPARAM lParam ) { CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) }; ChangeWindowMessageFilterEx ( m_hWnd, m_uTaskbarBtnCreatedMsg, MSGFLT_ALLOW, &cfs ); // other initialization steps... return TRUE: }
我们可以一直这样做,因为如果我们的应用程序不是以提升权限运行,ChangeWindowMessageFilterEx()
调用将不会产生任何效果。
显示进度条
既然我们已经了解了如何创建 ITaskbarList3
接口,让我们来看看显示进度条的方法。有两个方法控制进度条的位置和外观:
SetProgressState()
:控制条是否可见,以及其状态:正常、不确定、暂停或错误。SetProgressValue()
:设置条的位置。
SetProgressState()
的原型是:
HRESULT SetProgressState ( HWND hwnd, TBPFLAG tbpFlags );
hwnd
是我们窗口的 HWND
。我们必须传递我们的对话框的 HWND
,以便任务栏知道要为哪个按钮显示进度指示器。tbpFlags
是一个 TBPFLAG
值,表示状态。
TBPF_NOPROGRESS
:进度条不可见。TBPF_INDETERMINATE
:进度条通过以滚动方式显示不确定的进度。TBPF_NORMAL
:正常状态(在默认主题中为绿色)。TBPF_PAUSED
:暂停状态(在默认主题中为黄色)。TBPF_ERROR
:错误状态(在默认主题中为红色)。
最后三个状态类似于 Vista 中的进度条控件添加的状态。
另一个方法 SetProgresValue()
在条可见时设置条的位置。其原型是:
HRESULT SetProgressValue ( HWND hwnd, ULONGLONG ullCompleted, ULONGLONG ullTotal );
与之前一样,hwnd
是我们对话框的 HWND
。ullCompleted
表示到目前为止已完成的工作量,ullTotal
表示总工作量。任务栏会将 ullCompleted
转换为百分比,进度条将显示该百分比。如果条当前处于不确定状态,调用 SetProgressValue()
将自动将条置于正常状态。
示例项目如何使用进度条
由于对话框也有一个进度条,因此该进度条和任务栏进度条通常处于相同状态并显示相同信息。例如,下载开始时,我们重置进度条并清除任何现有的进度指示器。
void CMainDlg::OnStart ( UINT uCode, int nID, HWND hwndCtrl ) { // Clear the taskbar progress bar. if ( m_pTaskbarList ) m_pTaskbarList->SetProgressState ( m_hWnd, TBPF_NOPROGRESS ); // m_cProgress is a CProgressBarCtrl attached to the progress bar. m_cProgress.SetState ( PBST_NORMAL ); m_cProgress.SetPos(0); }
当下载线程报告进度时,对话框会相应地更新两个进度指示器。
LRESULT CMainDlg::OnDownloadProgress ( UINT uMsg, WPARAM wParam, LPARAM lParam ) { ULONG ulProgress = (ULONG) wParam, ulProgressMax = (ULONG) lParam; bool bIsMarquee = (m_cProgress.GetStyle() & PBS_MARQUEE) != 0; if ( 0 == ulProgressMax ) { // We don't know the size of the file, so put the progress bars into // marquee mode to indicate an indeterminate state. if ( m_pTaskbarList ) m_pTaskbarList->SetProgressState ( m_hWnd, TBPF_INDETERMINATE ); if ( !bIsMarquee ) { m_cProgress.ModifyStyle ( 0, PBS_MARQUEE ); m_cProgress.SetMarquee ( TRUE, 100 ); } } else { // SetProgressValue automatically cancels the TBPF_INDETERMINATE state if // the progress bar is in that state. if ( m_pTaskbarList ) m_pTaskbarList->SetProgressValue ( m_hWnd, ulProgress, ulProgressMax ); if ( bIsMarquee ) m_cProgress.ModifyStyle ( PBS_MARQUEE, 0 ); m_cProgress.SetPos ( MulDiv ( ulProgress, 100, ulProgressMax ) ); } return 0; }
下载期间的进度条外观如下:
该应用程序还使用暂停状态来指示下载已超时,并使用错误状态来指示下载因错误而停止。例如,如果您取消下载,进度条如下所示:
您可以查看示例代码中的 CMainDlg::OnThreadExited()
函数,了解如何实现这一点。
显示叠加状态图标
应用程序可以通过调用 SetOverlayIcon()
在其任务栏按钮上添加叠加图标。
HRESULT SetOverlayIcon ( HWND hwnd, HICON hIcon, LPCWSTR pszDescription );
与进度指示器方法一样,hwnd
是我们对话框的 HWND
。hIcon
是一个 16x16 的图标。您也可以传递 NULL
来完全删除叠加图标。pszDescription
是状态指示器的文本版本;此字符串将由辅助功能应用程序使用,以向无法看到图标的用户传达状态。任务栏会复制该图标,因此调用者可以立即销毁其 HICON
。
使用叠加图标时有两点需要注意:
- 如果任务栏设置为显示小图标,则根本不会显示叠加图标。
- 如果多个窗口分组到一个任务栏按钮中,一次只能显示一个图标 - 最后调用
SetOverlayIcon()
的应用程序获胜。但是,如果组中的一个窗口删除了其叠加图标,并且之前的图标没有被删除,则会再次显示它。
要尝试在示例应用程序中使用叠加图标,请单击“任务栏图标”按钮。您会得到一个包含三个命令的菜单:“绿色”、“红色”和“无”。“绿色”和“红色”分别设置不同的图标,“无”则删除图标。绿色的叠加图标外观如下:
这是处理“绿色”菜单项的代码:
void CMainDlg::OnGreenIcon ( UINT uCode, int nID, HWND hwndCtrl ) { CIcon icon; if ( NULL != icon.LoadIcon ( IDI_GREEN, 16, 16 ) && m_pTaskbarList ) m_pTaskbarList->SetOverlayIcon ( m_hWnd, icon, L"Green overlay icon" ); }
由于任务栏会复制我们传递给 SetOverlayIcon()
的图标,因此我们无需保留 HICON
。CIcon
的析构函数会为我们释放该图标。
修订历史
2009 年 9 月 14 日:文章首次发布