Windows のコンテキストメニューにアイコンを表示する方法

下記のように,Windows でファイルなどを右クリックした際に表示されるコンテキストメニューにアイコンが表示されている事がありますが,これを綺麗に実現しようとすると凄く大変だったと言う話.@rofi が頑張ってくれたものですが.


概要

コンテキストメニューにアイコンを表示させる方法は 3通りあるのですが,それぞれが何らかの問題を持っていました.最初に,概要を簡単に書いておきます.

  1. システムに任せる(InsertMenuItem 呼び出しの時にメニューにビットマップを割り当てる)方法は,表示のされ方が汚い.
  2. DrawIconEx でオーナー描画する方法は,Windows Vista 以降の Visual Style が強制的に無効化されクラシックスタイルで描画されてしまう.
  3. .ico データをアルファ付き HBITMAP にしてメニューに割り当てるという方法は,Windows Vista 以降でしか使えない(Windows XP だとエラーが発生する).

尚,現状でのベスト解は 2. と 3. のハイブリッドと言う事のようです.

システムに任せる方法

システムに任せる方法は以下の 2点が問題となりました.

  1. ラシックスタイルの場合に特殊な方法で透過されるため,アイコンによっては非常に汚く表示されてしまう.
  2. Windows Vista 以降の Visual Style の場合に透過されない.

特に問題となったのは前者のクラシックスタイルの場合でした.クラシックスタイルの場合,透過のされ方が特殊で指定したアイコン画像によっては何か黒ずんで表示されてしまうと言う問題がありました.また,Windows Vista 以降の Visual Style の場合はそもそも透過されないので,透過部分があるアイコン画像の場合はこちらも表示が汚いと言う問題が発生しました.

どちらにしろ表示が汚いので,この方法で描画する場合は使用するアイコン画像をかなり注意して作成する必要があります.最初の画像の Microsoft Security Essentials のアイコンは(多分)この方法で描画しているようで,できるだけ黒っぽい色を使ってアイコンが作成されてあります.

オーナー描画の場合

オーナー描画の場合,下記のようにWindows Vista 以降の Visual Style が強制的に無効化されてしまうと言う問題がありました.

左側のアイコンが表示されている方はオーナー描画,右側のサブメニューはシステムによる描画です.選択されているときの色(左側は濃い水色,右側は半透過された水色)やアイコン表示領域とテキスト表示領域の間の境界線の有無など見た目が異なっている事が分かります.

アルファ付き HBITMAP にしてメニューに割り当てる方法

この方法の問題点はWindows Vista 以降でしか使えないと言う点です.Visual Style Menus - MSDN にこの方法のサンプルプログラムが掲載されてあったのですが,これを Windows XP で実行すると実行時エラーが発生しました(うまく修正すれば実行時エラーは回避できるのかもしれませんが・・・).

結局,どうするのか?

いろいろと試行錯誤した結果,結局,「実行時に Windows のバージョンをチェックして Windows XPWindows Vista 以降で描画方法を変える」と言う方法を取らざるを得ないようです.

TortoiseSVN が採用しているのがそのようなハイブリッドな方法です。すなわち、XP なら .ico データを DrawIconEx でオーナー描画し、Vista なら .ico データをアルファ付き HBITMAP にしてメニューに割り当てるという方法です。

コンテキストメニュー - Softgate

この問題については,Themed menu’s icons, a complete Vista and XP solution - nanoANT で詳しい説明がなされています.

何と言うか・・・凄く大変でした・・・