65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (44投票s)

2009年9月14日

CPOL

6分钟阅读

viewsIcon

152187

downloadIcon

3891

介绍如何将任务栏进度条和覆盖图标与您的 Windows 7 应用程序一起使用。

目录

引言

Windows 7 任务栏操作的主要变化之一是微软称为“外围状态”的区域。这包括两种状态指示器:长操作的进度,以及重要通知的图标。应用程序可以在长时间操作期间继续使用进度对话框,但 Windows 7 任务栏还允许应用程序在其任务栏按钮上显示进度条,这样用户就可以一目了然地看到进度指示器,而无需切换到应用程序。

许多应用程序还使用通知区域来传达重要的状态信息。例如,Outlook 会在您收到新电子邮件时显示一个图标。然而,在 Windows 7 中,通知区域图标默认是隐藏的,因此通知区域不再适用于显示此类状态。任务栏允许应用程序在其任务栏按钮的现有图标上显示一个 16x16 的图标。这可以防止通知区域过于拥挤,并使状态图标与创建它的应用程序保持视觉关联。

本文的示例应用程序是我在文章《使用 Internet Explorer 为您下载文件》中文件下载器的重写。该应用程序在其任务栏按钮中显示下载进度,并在对话框中显示传统的进度条。这个应用程序以前没有通知区域图标,但为了演示涉及的 API 调用,它还包含了显示状态图标的命令。

本文的示例代码是用 Visual Studio 2008、WTL 8.0Windows 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 是我们对话框的 HWNDullCompleted 表示到目前为止已完成的工作量,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 是我们对话框的 HWNDhIcon 是一个 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() 的图标,因此我们无需保留 HICONCIcon 的析构函数会为我们释放该图标。

修订历史

2009 年 9 月 14 日:文章首次发布

© . All rights reserved.