Windows 7 タスク バー関連の開発 - Application ID
みなさん、こんにちは。Windows 開発統括部の古内です。
1 ヶ月ほど前、Developing for Windows Blog に投稿されている Windows 7 タスク バー関連の記事を翻訳中であるとお知らせしましたが、ようやく一通り目処がついたので、順次ご紹介していきたいと思います。栄えある (?) 第 1 回目は、2009 年 6 月 18 日に投稿された 「Developing for the Windows 7 Taskbar – Application ID」 と、その補足である 2009 年 6 月19 日に投稿された 「Developing for the Windows 7 Taskbar – Application ID – Part 2」 の合併号です。2 年以上も昔の記事ですが、情報は今でも有効です!
Windows 7 タスク バー関連機能の開発 – アプリケーション ID
この記事は、Windows 7 タスク バーに関する新しいシリーズ記事の第一弾です。Windows 7 の数ある変更点の中で開発者の皆さんにまず注目していただきたいのが、新しい Windows タスク バーです。この機能を理解しておけば、アプリケーションとタスク バーをスムーズに連携させ、ユーザー エクスペリエンスの強化につなげることができます。
既に皆さんの多くは、Windows 7 タスク バーの基本機能を使いこなし、旧バージョンのタスク バーから変更された理由をお察しではないでしょうか。もし Windows 7 タスク バーを実際に使ったり、デモをご覧になったことがない方は、Channel 9 の Web キャスト「Windows 7 タスク バーの概要 (英語)」をご覧ください。また、Windows 7 で新しいタスク バーやデスクトップ機能を導入した理由については、E7 ブログ記事「Windows 7 タスク バー (英語)」で紹介されています。まずこれらのビデオや記事に目を通していただければ、今回説明する技術の背景をより深く理解していただけるはずです。
Windows 7 にログオンしてまず最初に目に付く変更点は、新しいタスク バーだと思います。Windows 7 タスク バーには、アプリケーション起動やウィンドウ切り替えのメカニズムが組み込まれ、クイック起動、最近使ったファイル、通知領域アイコン、デスクトップ ショートカット、アプリケーション ウィンドウの実行といった従来の Windows デスクトップの機能が統合されています。Windows 7 タスク バーでは、ジャンプ リスト、プレビュー ハンドラー、オーバーレイ アイコンなどの機能が追加されています。Windows 7 タスク バーの各機能を詳しくご紹介する前に、まずは基本事項を押さえながら、Windows 7 タスク バーの用語を確認していきましょう。
タスク バー ボタンは、Windows 7 タスク バーの基本となるコンポーネントであり、タスク バー上にアイコンとして表示されます。下の画像を見るとわかるように、タスク バーには複数のボタンが表示されます。ボタンの表示のされ方によって、アプリケーションの状態がわかるようになっています。
たとえば、Media Player ボタンはフレームや枠線がなく、シンプルな状態でタスク バーに表示されています。これは、Media Player が実行されていないことを示しています。このボタンはタスク バーに固定されているため、固定を解除しない限り、このままずっと表示されます。Windows Explorer ボタンには透明なフレームが付いており、下のタスク バーの色がほとんど透けて見えます。これは、アプリケーションは実行されていますが、アクティブな状態ではないことを示しています。また、Visual Studio アイコンの下には不透明な四角いフレームが表示されています。これはこのアプリケーションが現在使用中であることを示しています。さらに Word ボタンには、複数のアイコンが重なっているのがわかります。これは Word の複数のインスタンスまたはウィンドウが、このタスク バー ボタンにまとめて表示されていることを示しています。タスク バー ボタンの生成、割り当て、グループ化に関する基本的なロジックを理解することがきわめて重要です。
Windows 7 で実行されるアプリケーション (Office Word 2007 や Visual Studio 2008 など) の多くは、もともと Windows 7 のタスク バーに対応するようには設計されていませんでした。ではどうしてこれらのアプリケーションは、タスク バーと連携したり、複数のインスタンスをグループ化したり、Word のジャンプ リストを利用したりすることができるのでしょうか。簡単に言えば、アプリケーションを起動すると、Application ID (アプリケーション ID、以下 AppID) が自動的に生成され、アプリケーションに割り当てられるからです。実行されているアプリケーションにはすべて AppID が割り当てられています。Windows によって自動的に設定されるか、またはアプリケーションが自ら設定します。AppID は GUID ではなくただの文字列 (最長 128 文字) です。自分で設定するか、OS によって設定されます。同じ AppID を持つウィンドウとアプリケーション (ジャンプ リストも含む) は、すべて同じタスク バー ボタンにグループ化されます。プロセス、ショートカット、ウィンドウ、タスク バー ボタン、ドキュメントの種類 (登録されているファイルの種類のハンドラー) といった、アプリケーションのすべての構成要素に AppID が関連付けられていることを知っておいてください。
「AppID は何から生成されるのだろうか?」という疑問を持つ方もいると思います。前述したように、OS は非常にシンプルでありながら重要なヒューリスティックを使用して、アプリケーション用のアプリケーション ID を生成します。Windows 7 では AppID をウィンドウごとに個別に割り当てることができるため、OS はウィンドウから AppID を割り出そうとします。通常、アプリケーションはウィンドウを少なくとも 1 つ表示するので、OS はそのウィンドウに対して AppID を照会することができます。しかし、既存のアプリケーションのほとんどは、ウィンドウごとに AppID を設定していない (そもそも AppID 自体を設定していない) ため、OS はウィンドウが属するプロセスから AppID を割り出そうとフォールバックします。プロセスには、OS がチェック可能なプロパティ (プロセスの実行可能ファイルなど) がいくつかありますが、十分な粒度で分離されていない可能性があります。たとえば、同じ実行可能ファイルのショートカットごとに異なるスタートアップ コマンド ライン パラメーターを設定して、(“ランチャー” アプリケーションのように) 異なるアプリケーションを起動することができます。アプリケーションはすべて 1 つのタスク バー ボタンにグループ化されます。この場合、OS はアプリケーションを起動したショートカットを調べ、実行可能ファイルやコマンド ライン パラメーターなどの情報を取得します。レジスタ ファイルがある場合、レジストレーションはそのファイルをダブルクリックすると起動するアプリケーションを参照します。これが AppID を設定するもう 1 つの方法です。下の画像は、この自動設定処理を図解したものです。
Windows 7 タスク バーの内部処理について詳しく理解したい方は、Channel 9 にそのアーキテクチャを説明した、Rob Jarrett 氏と Ben Bets 氏による「Windows 7 タスク バー - その内部処理 (英語)」というわかりやすいビデオがあるのでそちらをご覧ください。また、アプリケーション ID の概要については「Windows 7 タスク バーのジャンプ リストの紹介 (英語)」というビデオの 29 分 30 秒 ~ 34 分40 秒あたりの説明をご覧ください。
AppID は OS によって自動的に設定されますが、アプリケーションの AppID や、アプリケーションの各ウィンドウの AppID を自ら制御したい場合もあるはずです。たとえば、あるアプリケーションが別のアプリケーションをホストおよび実行している場合 (Visual Studio でアプリケーションをデバッグする場合など) や、複数の異なるアプリケーションやプロセスを 1 つのタスク バー ボタンにまとめたい場合などが、それに当てはまると思います。そのような場合には、タスク バー API を使用することで、アプリケーション単位またはウィンドウ単位のアプリケーション ID を制御することができます。Windows 7 対応の新しいアプリケーションを作成する場合には必ず、独自にアプリケーション ID を用意されることを強く推奨します。ここからはその方法について説明します。
では、アプリケーションと AppID の関連付けを可能にする API の内容を詳しく見ていきましょう。
"プロセス" (プロセスが所有するすべてのウィンドウを含む) ごとに異なるタスク バー ボタンを使用したい場合は、プロセス全体に明示的な AppID を設定します。これにより、明示的な AppID が設定されていないプロセス内のウィンドウすべてにこの AppID が適用されます。プロセスに明示的な AppID を設定するには、SetCurrentProcessExplicitAppUserModelID 関数を呼び出すだけなので、とても簡単です (次のコード スニペットを参照)。
SetCurrentProcessExplicitAppUserModelID(c_rgszAppID[0]);
c_rgszAppID[0] は、文字列へのポインターです。SDK のドキュメントに、「このメソッドは、アプリケーションの最初のスタートアップ ルーチンで、ユーザー インターフェイス (UI) の表示またはジャンプ リストの操作を実行する前に呼び出してください。 (This method must be called during an application's initial startup routine before the application presents any user interface (UI) or makes any manipulation of its Jump Lists.) 」とある点に注意してください。
最新の Windows API コード パック ライブラリ (英語) のマネージ コードでは、Microsoft.WindowsAPICodePack.Shell.Taskbar 名前空間にある Taskbar オブジェクトの AppID プロパティを使用して、任意のアプリケーション用のアプリケーション ID を設定し、取得できます。
ウィンドウに AppID を設定する作業は、これよりも複雑になります (と言っても、ほんの少しです)。SHGetPropertyStoreForWindow 関数を呼び出した後、返された IPropertyStore オブジェクトを使用して、必要なプロパティを取得します (次のコード スニペットを参照)。
void SetAppID(HWND hWnd, int iAppID)
{
IPropertyStore *pps;
HRESULT hr = SHGetPropertyStoreForWindow(hWnd, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr))
{
PROPVARIANT pv;
if (iAppID >= 0)
{
hr = InitPropVariantFromString(c_rgszAppID[iAppID], &pv);
}
else
{
PropVariantInit(&pv);
}
if (SUCCEEDED(hr))
{
hr = pps->SetValue(PKEY_AppUserModel_ID, pv);
PropVariantClear(&pv);
}
pps->Release();
}
}
SHGetPropertyStoreForWindow を呼び出し、hWnd にウィンドウへの参照を設定することで、現在のウィンドウのプロパティ ストアを抽出できます。次に、バリアント型のプロパティである InitPropVariantFromString(c_rgszAppID[iAppID], &pv) をウィンドウの AppID を示す文字列で初期化します。最後に、新しいプロパティ ストアの値としてウィンドウを設定します。
残念なことに、現在の Windows API コード パックは、ウィンドウ単位の Application ID の設定をサポートしていません。これをサポートするには、次の関数を Taskbar.cs ファイルに追加します。
private static void SetWindowAppId(string appId)
{
Microsoft.WindowsAPICodePack.Shell.ShellNativeMethods.SetWindowAppId
(OwnerHandle, "name here");
}
Windows API コード パックにはソース コードが用意されているので、SetWindowAppId 関数の実装を自分で確認できます。上記サンプル コードの SetAppID にきわめて類似した処理内容であることがおわかりいただけると思います。また、完全修飾アセンブリ名 "Microsoft.WindowsAPICodePack.Shell" を使用する必要はありません。ここでは、Windows API コード パックの階層構造をわかりやすくするために使用しました。
なお、ウィンドウの AppID は動的に設定されるので、ウィンドウをタスク バー ボタンに表示した後に AppID を変更して、全く別のタスク バー ボタンに表示することが可能です。これは面白い効果をもたらします。たとえば、ジャンプ リストはタスク バー ボタンに (AppID を使用して) 関連付けられるので、全く別のタスク バー ボタンに関連付けると、同じウィンドウに異なるジャンプ リストが表示されることになります。ただしこれはユーザーを混乱させる恐れがあるので、ウィンドウの AppID を設定したらそれを使用し続けることをお勧めします。そうすることで、ウィンドウが表示されるたびに同じ方法で AppID を特定できます。
マネージ コードの Taskbar オブジェクトは、Windows API コード パック (英語) プロジェクトにあります。
[以下、6 月 19 日 に "Part 2" としてコード サンプルと新しいスクリーンショットを追加したものです。]
一部のコメントに応える形で、ダウンロード用リンクと、実行時に変化する Application ID の効果を示す新しいスクリーンショットを追加しました。また共通の理解が得られるように、AppID の有用性を示す 2 つの画像を追加しました。同じ AppID を持つ 1 つのアプリケーションで 2 種類のタスク バー ボタンを使用できることが、おわかりいただけると思います。このアプリケーションのコードは、こちらからダウンロードできます。
これはコメントの 1 つに対する回答です。もちろん、異なる AppID を持つウィンドウの間に別のタスク バー ボタンを配置することもできます。
このアプリケーションのコードはこちらからダウンロードできます。
マネージ コードの Taskbar オブジェクトは、Windows API コード パック (英語) に含まれています。