Windows 7 タスク バー関連の開発 - サムネイル ツール バー

みなさん、こんにちは。Windows 開発統括部の古内です。

今日も引き続き Windows 7 のタスク バーに関するブログ記事の翻訳をお届けします。今回は、2009 年 12 月 4 日に Developing for Windows Blog に 投稿された 「Developing for Windows 7 Taskbar - Thumbnail Toolbars」 の翻訳です。


Windows 7 タスク バー関連の開発 - サムネイル ツール バー

今回は、おなじみの Windows 7 API についての説明に戻ります。Windows 7 タスク バーの基本事項については既に 「Windows 7 タスク バー関連の開発 – Application ID」 という記事で紹介し、アプリケーションのジャンプ リストの作成については 「Windows 7 タスク バー関連の開発 – ジャンプ リストの活用 (その 1)」、「その 2」、「その 3」 で取り上げました。この記事では、エンド ユーザーの作業効率向上を実現するサムネイル ツール バーの使用を中心に説明します。また、サムネイル プレビューと共通する詳細についても触れます。

まずは、サムネイル ツール バーの概念を簡単に説明しましょう。サムネイル ツール バーがどのような機能かを既にご存知の方は、実際のコードでの説明が始まる中盤あたりまで読み飛ばしてください。

サムネイル ツール バーは、フォーカスが当たっていない場合 (または非表示の場合) でもアプリケーションの機能にアクセスできるようにする機能で、作業効率の向上にとても役立ちます。つまり、この機能があれば、アプリケーションにアクセスするのに、そのアプリケーションに切り替える (ウィンドウをアクティブにする) 必要はありません。サムネイル ツール バーは、最大 7 個のボタンを置くことができ、タスク バー アイコンのサムネイル プレビューの下部に表示されます。

このわかりやすい例が、Windows Media Player のサムネイル ツール バーです (隣の画像を参照してください)。アプリケーションの実行中にマウス ポインターを Media Player アイコンに重ねると、再生/一時停止、次の曲、前の曲の 3 つのボタンが表示されます。このボタンをクリックすると、各ボタンが示す処理が実行されます。

Taskbar_ThumbnailToolbar1

また、以前にこの Developing for Windows Blog で取り上げた Fishbowl for Facebook アプリケーション (英語) も良い例です。このアプリケーションは、Facebook ユーザーのだれもが行う一般的なタスクにすばやく簡単にアクセスできるようにするためにサムネイル プレビューを活用しています。ホームや友達のフィードの表示などの操作をサムネイル ツール バーで実行することが可能です。下の画像が示すように、Fishbowl サムネイル ツール バーにはボタンが 5 個あります (実際は 6 個ですが、これについては後で説明します)。

サムネイル ボタン

これらの例のように、この優れたタスク バー機能は多くの場面で必要とされています。では、この機能の実装について見ていきましょう。最初に、基本的なルールを説明します。

  • ボタンは 7 個まで表示できる。サイズは固定で、16 X 16 ピクセル (高 DPI の場合は最大 24 X 24 ピクセル) にあらかじめ定義されている。
  • サムネイル ツール バー ボタンは、アプリケーション内のアクティブ ウィンドウごとに設定できる。つまり、複数のウィンドウで構成されるアプリケーションの場合は、ウィンドウごとに異なるサムネイル ツール バーを表示することが前提となる。
  • ボタンをいったん設定したら (通常はタスク バー アイコンの生成時に設定する)、ツール バー上のボタンを追加または削除することはできない。ただし、ボタンを非表示にすることはできる。

いつものように、ネイティブ コード (Win32) の実装から説明します。次のサンプルは Windows 7 SDK にあります。

サムネイル ツール バー ボタンを追加するには、ITaskbarList4 インターフェイスを使用します。このインターフェイスには、タスク バーのアイテムを動的に追加、削除、有効化するメソッドが含まれています。サムネイル ツール バーを操作する場合、具体的には以下のメソッドを使用します。

  • ThumbBarAddButtons - 指定されたボタン群を持つサムネイル ツール バー (タスク バー ボタンのポップアップ ウィンドウとして表示されるサムネイル画像に埋め込むツール バー) を、タスク バー ボタンのポップアップ ウィンドウとして表示されるサムネイル画像に追加する。
  • ThumbBarSetImageList - タスク バー ボタンのポップアップ ウィンドウとして表示されるサムネイル画像に埋め込むツール バー内の、各ボタンに使われる画像を含む画像リストを指定する。
  • ThumbBarUpdateButtons - サムネイル ツール バーのボタンをウィンドウの現在の状態に応じて、有効化、無効化、非表示化する。

タスク バー アイコン関連の操作では常に操作対象のタスク バー アイコンが存在することを確認します。これには、RegisterWindowMessage メソッドを使用して “TaskbarCreatedMessage” を登録した後、メッセージが Win32 WndProc メソッドに届くのを待ちます。アプリケーションがこのメッセージを受信したら、サムネイル ツール バー ボタンの作成に進んでも安全です。この WndProc メソッドは、作成したサムネイル ボタンがクリックされたときに発生する WM_COMMAND メッセージも受信します。wParam パラメーターの下位ワードにはそのボタンのボタン ID が格納され、上位ワードは単純に THBN_CLICKED に設定されます。次のコード スニペットにこの処理を示します。

 LRESULT CALLBACK WndProc(
                     HWND hWnd, UINT message, 
                     WPARAM wParam, LPARAM lParam)
 {
     static UINT s_uTBBC = WM_NULL;
  
     if (s_uTBBC == WM_NULL)
     {
         // TaskbarButtonCreated メッセージの値を計算する
         s_uTBBC = RegisterWindowMessage(L"TaskbarButtonCreated");
  
         // 権限昇格による管理者特権でアプリケーションが実行される場合を考慮して、
         // TaskbarButtonCreated および WM_COMMAND メッセージの受信を許可する
         ChangeWindowMessageFilter(s_uTBBC, MSGFLT_ADD);
         ChangeWindowMessageFilter(WM_COMMAND, MSGFLT_ADD);
     }
  
     if (message == s_uTBBC) //wmTaskbarButtonCreated)
     {
         // TaskbarButtonCreated メッセージを取得したら、
         // ウィンドウのサムネイル ツール バーを作成する
         CreateThumbnailToolbar(hWnd);
     }
     else switch (message)
     {
         case WM_COMMAND:
         {
             int const wmId = LOWORD(wParam);
             switch (wmId)
             {
                 case IDTB_BUTTON1:
                 case IDTB_BUTTON2:
                 case IDTB_BUTTON3:
                 {
                     WCHAR szMsg[100];
                     StringCchPrintf(
                             szMsg, ARRAYSIZE(szMsg), 
                             L"Thumbnail toolbar button clicked, 
                             ID=%d", wmId);
  
                     MessageBox(hWnd, szMsg, L"Application", MB_OK);
                     break;
                 }
                 case IDM_EXIT:
                     DestroyWindow(hWnd);
                     break;
  
                 default:
                     return DefWindowProc(hWnd, message, wParam, lParam);
             }
             break;
         }
         case WM_DESTROY:
             PostQuitMessage(0);
             break;
         default:
             return DefWindowProc(hWnd, message, wParam, lParam);
     }
     return 0;
 }

TaskbarCreatedMessage メッセージをリッスンするよう登録した直後に ChangeWindowMessageFilter メソッドを呼び出している点に注意してください。このメソッドは、ユーザー インターフェイス特権の分離 (UIPI) メッセージ フィルターでフィルター処理するメッセージを追加または削除します。この処理は、ユーザーが権限を昇格して管理者特権でアプリケーションを実行している場合のために必要です。簡単に説明しましょう。タスク バーは explorer.exe プロセスの一部であり、中度の整合性レベルつまり標準ユーザーとして実行されます。Windows Vista と UIPI の導入以降、特権レベルの低いアプリケーションは、レベルの高いアプリケーションに Windows メッセージを送信できなくなりました。このため、整合性レベルの低い送信者からのメッセージを受信するにはフィルターを変更する必要があります。ここでは、TaskbarCreatedMessage および WM_COMMAND の結果を受信できるようにします。UIPI については、近く、別の記事で説明する予定です。

本題に戻ります。WM_COMMAND の処理はきわめて単純で、wParam パラメーターの下位ワードからボタン ID を抽出しているだけです。これは単なる例なので、実際にはその ID による処理は何も行っていません (switch 文はすべてのボタンで同じです)。

次に、サムネイル ツール バーを登録するのに必要な処理について説明します。先ほどのコード スニペットでは、CreateThumbnailToolbar ヘルパー メソッドを呼び出しています。以下にこのメソッドのコードを示します。

 HRESULT CreateThumbnailToolbar(HWND hWnd)
 {
     ITaskbarList4 *pTaskbarList;
     HRESULT hr = CoCreateInstance(
                     CLSID_TaskbarList, 
                     NULL, 
                     CLSCTX_INPROC_SERVER, 
                     IID_PPV_ARGS(&pTaskbarList));
  
     if (SUCCEEDED(hr))
     {
         hr = pTaskbarList->HrInit();
         if (SUCCEEDED(hr))
         {
             // サムネイル ツール バー ボタン用に、 
             // どのビットマップを使用するかを判断する
             // システムの「小さいアイコン」の幅を基準に決定する
             // これにより DPI 対応になる
             struct 
             {
                 PCWSTR pbmp;
                 int cx;
             } 
             const bitmaps[3] =
             {
                 { MAKEINTRESOURCE(IDB_BUTTONIMAGES_96),  16 },
                 { MAKEINTRESOURCE(IDB_BUTTONIMAGES_120), 20 },
                 { MAKEINTRESOURCE(IDB_BUTTONIMAGES_144), 24 }
             };
  
             int const cxButton = GetSystemMetrics(SM_CXSMICON);
  
             int iButtons = 0;
             for (int i = 0; i < ARRAYSIZE(bitmaps); i++)
             {
                 if (bitmaps[i].cx <= cxButton)
                 {
                     iButtons = i;
                 }
             }
  
             HIMAGELIST himl = ImageList_LoadImage(
                     g_hInstance, 
                     bitmaps[iButtons].pbmp,
                     bitmaps[iButtons].cx, 
                     0, 
                     RGB(255,0,255), 
                     IMAGE_BITMAP, 
                     LR_CREATEDIBSECTION);
  
             if (himl)
             {
                 hr = pTaskbarList->ThumbBarSetImageList(hWnd, himl);
                 if (SUCCEEDED(hr))
                 {
                     THUMBBUTTON buttons[3] = {};
  
                     // 1 つ目のボタン
                     buttons[0].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;
                     buttons[0].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
                     buttons[0].iId = IDTB_BUTTON1;
                     buttons[0].iBitmap = 0;
                     StringCchCopy(buttons[0].szTip, 
                                     ARRAYSIZE(buttons[0].szTip), L"Button 1");
  
                     // 2 つ目のボタン
                     buttons[1].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;
                     buttons[1].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
                     buttons[1].iId = IDTB_BUTTON2;
                     buttons[1].iBitmap = 1;
                     StringCchCopy(buttons[1].szTip, 
                                     ARRAYSIZE(buttons[1].szTip), L"Button 2");
  
                     // 3 つ目のボタン
                     buttons[2].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;
                     buttons[2].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
                     buttons[2].iId = IDTB_BUTTON3;
                     buttons[2].iBitmap = 2;
                     StringCchCopy(buttons[2].szTip, 
                                     ARRAYSIZE(buttons[2].szTip), L"Button 3");
  
                     // これらのボタンをサムネイル ツール バーに設定する
                     hr = pTaskbarList->ThumbBarAddButtons(
                                             hWnd, 
                                             ARRAYSIZE(buttons), 
                                             buttons);
                 }
                 ImageList_Destroy(himl);
             }
         }
         // ITaskbarList3 はここで解放してかまわない
         // サムネイル ツール バーは残る
         pTaskbarList->Release();
     }
     return hr;
 }

最初に、ITasbarList4 COM インターフェイスを CoCreate する必要があります。次に、標準的な方法に従って、HrInit を呼び出し Taskbar リスト オブジェクトを初期化します。このメソッドを呼び出した後、他の ITaskbarList メソッドが呼び出せるようになります。次に、使用するビットマップのリストを設定します。「小さいアイコン」に推奨される幅 (ピクセル単位) を確認する必要があります。この値は DPI 設定によって変わってきます。最後に、ThumbBarSetImageList を呼び出してボタンのアイコンを設定します。ソース コードをダウンロードしたら、この行をコメントアウトしてアプリケーションを実行するとどのような結果になるか、ぜひ試してみてください。

次に、3 つの THUMBBUTTON を含む配列を作成し、各ボタンのビットマップ、ツールヒント、フラグを設定します。これはツール バー ボタンの構成に使用します。たとえば、ビットマップ値は (THUMBBUTTON の) iBitmap フィールドの値が有効であることを示します。THB_FLAGS を使用すると、ボタンの状態や動作 (有効、無効、非表示など) を指定できます。SDK には他にもたくさんのオプションが用意されています。またこのサンプルでは、クリックされたボタンを識別できるようにボタン ID を設定しています。この ID は WM_COMMAND メッセージと一緒に送信されます。

最後に、ThumbBarAddButtons メソッドを呼び出して現在の hWnd とボタンの配列を渡します。これによりサムネイル ツール バーが登録されます。マウス ポインターをアプリケーションのタスク バー アイコンに重ねると、3 つの色付きボタンが表示されるようになります (下の画像を参照してください)。 Taskbar_ThumbnailToolbar2

マネージ コード アプリケーションのサムネイル ツール バーは、Windows API Code Pack を使用して作成できます。以下は、Channel 9 Learning Center (英語) にある Windows 7 トレーニング キットの一部です。Jaime Rodriguez が作成した TaskbarConcepts というデモは、WPF アプリケーションとして作成されており、開発者が利用できる Windows タスク バーの機能を網羅しています。ここではサムネイル ツール バーの部分のみ触れます。他の部分については各自でぜひお試しください。このデモは、WPF アプリケーションのマネージ コード サンプルのベースとして今後もたびたび使用する予定です。

Windows API Code Pack を使用する場合は、当然ながら、Windows メッセージの登録や処理に頭を悩ませることはありません。TaskbarManager を使用するだけで済みます。特に、ThumbnailToolbars が便利です。ここには、ThumbnailToolbarButton オブジェクトを追加するときに必要な AddButtons メソッドが用意されています。ThumbnailToolbarButton オブジェクトはタスク バー サムネイル ボタンを表し、これを通してタスク バー ボタンの有効化/無効化、アイコンやツールヒントの変更、表示/非表示の制御を行うことが可能です。また、マネージ コードなので、ご期待のとおり、ボタンの Click イベントにイベント ハンドラーを接続できます。手間のかかることもある Windows メッセージを操作する必要はありません。以下は、ToolbarButtons.xaml.cs ファイル内のコードの一部です。サムネイル ツール バーをマネージ コード アプリケーションに追加することがとても簡単であることがわかります。

 private void CreateToolbarButtons()
 {
     buttonFirst = new ThumbnailToolbarButton(
                         TaskbarConcepts.Resources.first, "First Image");
     buttonFirst.Enabled = false;
     buttonFirst.Click += buttonFirst_Click;
  
     buttonPrevious = new ThumbnailToolbarButton(
                         TaskbarConcepts.Resources.prevArrow, "Previous Image");
     buttonPrevious.Enabled = false;
     buttonPrevious.Click += buttonPrevious_Click;
  
     buttonNext = new ThumbnailToolbarButton(
                         TaskbarConcepts.Resources.nextArrow, "Next Image");
     buttonNext.Click += buttonNext_Click;
  
     buttonLast = new ThumbnailToolbarButton(
                         TaskbarConcepts.Resources.last, "Last Image");
     buttonLast.Click += buttonLast_Click;
  
     TaskbarManager.Instance.ThumbnailToolbars.AddButtons
     (
         new WindowInteropHelper(Application.Current.MainWindow).Handle,
         buttonFirst, 
         buttonPrevious, 
         buttonNext,     
         buttonLast
     );
 }

このファイルの残りのコードでは、さまざまなイベントのロジックと処理を行っています。これにより、以下の図にあるように、メイン アプリケーションに表示されるさまざまな画像を制御できるようになっています。

Taskbar_ThumbnailToolbar3

以上の説明で、サムネイル ツール バーを適切に導入できればメリットが大きいこと、またその実装が簡単であることを理解していただけたと思います。

ところで、Fishbowl のサムネイル ツール バーのボタンが 5 個ではなく 6 個である理由をまだ説明していませんでした (最初の画像を参照してください)。画像をよく見ると、左側の 4 個のボタンはアプリケーションの動作に直接影響しますが、右側に 1 個だけあるボタンは既定のブラウザーを起動して Facebook の Web サイトを自動的に読み込むだけであることがわかります。つまり、ボタンの種類に応じてボタンを分けて表示していることがわかります。アプリケーションはボタンを 6 個作成していますが、5 番目のボタンは非表示なのでボタンの間にスペースがあるように見えます。これを実現するには、Win32 アプリケーションの場合は ThumbBarUpdateButtons を使用します。

マネージ コード開発の場合は、ThubmnailButton を非表示にすることで簡単に実現できます。Windows API Code Pack を詳しく調べると、ThubmnailButton クラスに、ツール バー ボタンの表示状態を制御する Visible プロパティがあることがわかります。Visible Set セクションの終わりに UpdateThumbnailButton() 関数があります。これは、基本的にはネイティブの ThumbBarUpdateButtons メソッドのラッパーです。

Win32 ネイティブのサンプルはこちらからダウンロードできます。

マネージ コード (WPF) のサンプルはこちらからダウンロードできます。

Channel 9 Learning Center (英語) では、他の Windows 7 タスク バー関連のトレーニング キットも提供されています。