すっかり間があきました。Windows 7もRTMが出て、MSDNやTechNetなどではダウンロード可能となったようです。また、Windows 7 SDKの正式版も出ました。というわけで、Windows 7の機能を使ってみたいと思います。今回取り上げるのは次の2つの機能です。

  • タスクバー上でのタブ切り替え(Windows 7のIE 8にはタッチ機能 – ITmedia Newsに書かれています):Internet Explorerのほか、エクスプローラのフォルダ表示、Safari 4やExcel 2010もこれに対応しています(Excel 2000から2007までも実現できていますが、旧来のAPIを使っているため、次のエアロプレビューに対応していません)。
  • エアロプレビュー(旧称エアロピーク):Vistaからさらに進化したWindows 7の新GUI - @ITの「グラフィックス機能を駆使したアプリケーションの切り替え表示」にあるようにタスクバーのアイコンやライブサムネイル上をマウスでポイントするだけで対応するウィンドウが一時的に手前に表示される機能です。

ようするに、1番目、タブやMDIの中身をタスクバーボタンで選べるようにすることをAB5のProjectEditorでもやりたかったのです。

ソースコードはこちらです: MdiTaskbarTest.cpp。なお、Windows 7 RCとWindows 7 SDK (正式版)で動作を確かめました。

WTLを使った何の変哲もないMDIアプリケーションです。メニューバー上の「子ウィンドウ作成(M)」をクリックすると、エディットボックスが載ったMDI子ウィンドウが作られます。なお、MDIフレームウィンドウ・クライアントウィンドウはMainFrameクラス、MDI子ウィンドウはChildFrameクラスが対応します。さて、このエディットボックスが載ったMDI子ウィンドウをIEのタブのようにタスクバー上で選べるようにしよう、そしてこれをエアロプレビュー対応させようというのが今回の趣旨です。

通常、MDI子ウィンドウに対してタスクバー上のボタンが作られることはありません。そこで、ITaskbarList3です。これのRegisterTabとUnregisterTabで任意のウィンドウをタスクバーのボタンに登録・解除できるようになります。以前のもの(ITaskbarList)よりMDIでは使いやすくなったと思います。

RegisterTabで登録しただけでは残念ながらライブサムネイルもエアロプレビューもできません。しかし、それらを表示させることは簡単です。DwmSetIconicThumbnailDwmSetIconicLivePreviewBitmapという関数でビットマップを渡してやるだけでよいのです。ただし、ライブサムネイル用のビットマップはキャッシュされるので、MDI子ウィンドウの内容が書き換わったときにはDwmInvalidateIconicBitmapsで通知しないといけません。

ここから、問題に行き着くまで、しばらく逆方向に芋づるを辿って話を進めます。DwmSetIconicThumbnailとDwmSetIconicLivePreviewBitmapは、それぞれWM_DWMSENDICONICTHUMBNAILWM_DWMSENDICONICLIVEPREVIEWBITMAPのメッセージを受け取ったときに呼ぶのが良いとされています。この2つのメッセージを受け取るには、DwmSetWindowAttributeでそれぞれDWMWA_FORCE_ICONIC_REPRESENTATIONとDWMWA_HAS_ICONIC_BITMAPのフラグを有効にしてやる必要があります。ところが、WS_CHILD属性を持つウィンドウにDwmSetWindowAttributeを使うとE_HANDLEのエラーを返してきます。つまり、MDI子ウィンドウにDwmSetWindowAttributeは使えないのです。

そこでどうするのかというと、WM_DWMナントカを受け取るためだけのダミーのウィンドウを作ることにしました。きちんと確かめたわけではありませんが、Spy++で見る限り、Safari 4もExcel 2010もそうしているようなのです。このプログラムでも、そのようなウィンドウであるDummyWindowForTaskbarButtonクラスのtaskbarButtonというメンバがChildFrameに設け、ChildFrameとDummyWindowForTaskbarButtonは1対1に対応するようにしています。これに伴い、ITaskbarList3::RegisterTabもこのダミーウィンドウを登録するようにしています(RegisterTabは子ウィンドウでも成功するのですが)。あと、タスクバーでライブサムネイルがクリックされたときも(おそらくRegisterTabで登録したため)ダミーウィンドウへWM_ACTIVATEがやってくるので、本来のMDI子ウィンドウに飛ばすという処理も行っています (DummyWindowForTaskbarButton::OnActivate)。

ちなみに、DwmSetIconicThumbnail用のビットマップを作るとき (DummyWindowForTaskbarButton::SetIconicThumbnail)、ライブサムネイルの大きさとして指定された大きさ目いっぱいになるように単純にStretchBltで拡大・縮小していますが、実際には元画像の縦横比を崩さないように拡大・縮小するほうがきれいになると思います。ビットマップはα付きビットマップなので、ライブサムネイルの縦横比とあわない分は透明にしてしまえばよいのです。

私は、APIを使ってプログラムする立場から、このようなダミーのウィンドウを作らなくて済む仕組みにしたほうが簡単だったのにと思っています。もう少しなんとかならなかったのでしょうかねえ。

もう数回、このプログラムの解説を書きたいと思っています。そのため、(1)としました。


2011年5月10日: 誤字修正およびWindows APIの関数・インタフェースについてMSDNライブラリへのリンクを追加しました。


スポンサード リンク

この記事のカテゴリ

  • ⇒ Windows 7タスクバー対応 (1) MDI子窓をタスクバーに出現させる
  • ⇒ Windows 7タスクバー対応 (1) MDI子窓をタスクバーに出現させる