本記事の原文は、http://www.installationexcellence.com/articles/VistaWithDelphi/Index.html に掲載されていますが、著者の了解のもと、翻訳記事についてはBDNに掲載しました。オリジナル記事ならびに関連情報については、上記URLを参照してください。
まず、皆さんのアプリケーションは「動きます」
皆さんは、恐らく、責任あるアプリケーション開発者として、もうすでに、マイクロソフトの次期OS Windows Vistaのパブリックベータリリースをいくつかダウンロードして、皆さんのアプリケーションが新しいOS下で動作することを確認していることと思います。
ちゃんと書かれたDelphiアプリケーションの99%が、Vista環境でちゃんと動作するとはいえ(管理者権限、昇格、UACといった問題は、この記事の範疇ではありません)、それらは、やはり、Windows Vistaで強化された新しいユーザーエクスペリエンスを享受していません。実際、Vistaの新しいユーザーエクスペリエンス機能を調べれば調べるほど、そのアプリケーションがネックになっていると痛感するはずです。
でも、心配無用です。これは、完全に対応可能で、方法もいたって簡単です。これらの不一致を解決し、Windows Vistaで見ることのできる新しいユーザーエクスペリエンスに、アプリケーションのルックアンドフィールを100%適合させることができるのです。では、Delphiで作られたアプリケーションに見られるいくつかの癖を分析し、これを解決する方法と、私たちのアプリケーションを適合させるために必要な追加のステップを調べましょう。
サンプルアプリケーション
私たちがこのチュートリアルで扱うサンプルアプリケーションは、2つのフォームといくつかのアクションがあるだけの単純なノートパッドのようなプログラムです。それは、テキストを読み込み、保存でき、Aboutダイアログを表示するものです。また、ドキュメントが変更されているときには、ファイルを開こうとしたり、アプリケーションを閉じようとすると、変更を保存するかどうかを確認します。

サンプルアプリケーションのステップ1は、こちらからダウンロードできます。
さて、このサンプルアプリケーションは、Windows Vista上で問題なく動作していますが、Vistaのユーザーエクスペリエンスに適合させようとすると、いくつもの問題が顕在化してきます。
新しいフォントに関する問題
最初の問題、最も簡単に解決できるけれども、フルサイズのアプリケーションで最も時間を消費するのが、フォントに関する問題です。Windows Vistaでは、ユーザーインターフェイス用の新しいデフォルトフォントとして、Segoe UIが導入されています。ここで問題になるのは、半分だけです。興味深い点は、新しいデフォルトフォントがあるということだけでなく、そのデフォルトフォントのサイズも大きくなっているということです。Windows XPでは、デフォルトで、アプリケーションはTahoma 8を使います。しかし、Windows Vistaに適合させようとするアプリケーションでは、Segoe UI 9を使います。このことは、(Windows Vistaで実行しようとした場合)フォントを変更するだけでなく、それぞれの画面ですべてのテキストが依然として表示されているかを確認し、必要に応じて調整しなければならないということを意味しています。
では、まずuVistaFuncs.pasと呼ばれる新しいユニットを作成することから始めましょう。このユニットには、Vista固有のメソッドを追加します。Windows Vista上で実行されているかどうかをチェックするためのメソッドと、適切なユーザーインターフェイスフォントとサイズを指定されたフォームに適用するためのメソッドを追加します。そして、メインフォームとAboutダイアログフォームの両方のFormCreateイベントに、SetVistaFonts(Self) を単純に呼び出すようにします。Uses節には、uVistaFuncsを追加する必要があります。
Aboutダイアログのタイトルでは、まだデフォルトフォントのTahomaが使われていることに気がつくと思います。これは、設計時にフォントを変更し、ParentFontプロパティがFalseになっているからです。恐らく、アプリケーションのあちこちでこれを見ることでしょう。これを解決する最良の方法は、uVistaFuncsを編集して ”Segoe UI” を ”Segoe Script” に変更することです。これにより、実行アプリケーション内で、どこに追加の変更が必要なのかが、より明らかになります。この例では、単純に、SetVistaFontsを呼び出した後に、TitleLabe.Font.Nameを設定します。

サンプルアプリケーションのステップ2は、こちらからダウンロードできます。
秘密のウィンドウに対する対処
Windows Vistaは、Inductive UIのエクスペリエンスを容易にするいくつかの要素が特徴的です。Inductive UI(誘導型ユーザーインターフェイス)については、(古いものですが)この記事を参照してください。基本的に、Inductive UIは、ユーザーが画面に表示されるパラダイムを理解するのを積極的に助けます。
Windows Vistaのこうした例の中には、アプリケーションを最小化/最大化するときに見ることができる新しいアニメーションが含まれています。Ales Holecekは、こちらのインタビューで、多くのユーザーが、ウィンドウとそれに対応するタスクバーのボタンの概念を理解していないことに言及しています。彼らは、ウィンドウが最小化されたときに、それがどこへ行ったのか、また、それがどのタスクバーのボタンと結びついているか分かりません。Vistaでは、これを改善するために(また、3D UI Aeroを引き立たせるために)、ウィンドウを最小化/最大化するときに、ウィンドウは、タスクバーへ、またタスクバーから、3Dでスムースにアニメーションします。
では、実際に試してみてください。でも、Delphiアプリケーション以外で。Delphiで作られたアプリケーションをVista上で最小化/最大化すると、アニメーションは、ウィンドウを閉じるか表示するように見えます。ウィンドウは、後方に消えていくときにフェードアウトするか、表示されるときにフェードバックします。この理由は、すべてのDelphiアプリケーションが、隠れた秘密の「アプリケーションフォーム」を持っているからです。この隠しフォームは、Delphiアプリケーションに見られるタスクバーボタンのオーナーです(これは、Delphiアプリケーションのタスクバーコンテキストメニューが、他のアプリケーションとは違う理由でもあります)。Delphiで、フォームを最小化したり復元すると、Delphi内部のマジックにより、これらのメッセージをフックして、代わりに現在アクティブなフォームを単純に隠したり、表示します。つまり、フォームは本当の意味で、最小化されたり最大化されることはありません。
Windows Vistaのもうひとつの新機能について議論するために、ちょっと脇道にそれたいと思います。でも、すぐに秘密のウィンドウの話に戻ってきます。ここで話題にするVistaの新機能は、Inductive UIの要素のひとつであり、アプリケーションタスクバーの上でホバーリングすることで、ウィンドウのプレビューをシンプルに見ることができるようにする機能です。これは、どのタスクバーボタンをクリックすればよいかを簡単に探せるようにする、(異論はあるものの)なかなかかっこいい機能です。これと同じ技術が、新しいFlip 3D(従来のALT+TABで表示される画面の3D表示版)を描画するためにも使われています。もし、通常の、つまり最小化されていない状態のDelphiアプリケーションでこれを試してみると、完全にうまく動作します。しかし、最小化されたDelphiアプリケーションでこれを行うと、実際のプレビューではなく、アプリケーションアイコンが表示された空のウィンドウが表示されます。
ウィンドウ表示状態でのサムネイルプレビュー

最小化状態でのサムネイルプレビュー

最小化されたDelphiアプリケーションのFlip 3D

なぜDelphiアプリケーションだと異なる動作になるのでしょうか?何かの謀略でしょうか?いえ、とんでもない。これが、秘密のウィンドウなのです。タスクバーのボタン上でホバーリングさせ、フォームを復元させると、プレビューとしてアプリケーションのアクティブフォームが表示されます。しかし、すべてのフォームが最小化されているときには、隠し「アプリケーションフォーム」がプレビューとして表示されてしまうのです。
では、この問題を解決しましょう!ここでは、Peter Belowがこちらで説明しているテクニックを用いることにします。はじめに、隠し「アプリケーションフォーム」のタスクバーボタンを取り除くことにしましょう。アプリケーションのFormCreateで、アプリケーションフォームのフラグを調整します。
ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE) and not WS_EX_APPWINDOW
or WS_EX_TOOLWINDOW);
ShowWindow(Application.Handle, SW_SHOW);
次に、それ自身のタスクバーボタンを持たせたいそれぞれのフォーム(少なくともメインフォーム)に対して、CreateParamsをオーバーライドする必要があります。
procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle and not WS_EX_TOOLWINDOW or
WS_EX_APPWINDOW;
end;
最後に、タスクバーボタンと最小化機能をそれぞれのフォームに持たせるために。WindowsメッセージのWM_SYSCOMMANDをハンドルする必要があります。
interface
…
protected
procedure WMSyscommand(var Message: TWmSysCommand); message WM_SYSCOMMAND;
…
implementation
…
procedure TMainForm.WMSyscommand(var Message: TWmSysCommand);
begin
case (Message.CmdType and $FFF0) of
SC_MINIMIZE:
begin
ShowWindow(Handle, SW_MINIMIZE);
Message.Result := 0;
end;
SC_RESTORE:
begin
ShowWindow(Handle, SW_RESTORE);
Message.Result := 0;
end;
else
inherited;
end;
end;
これにより、フォームの最小化/最大化をオーバーライドしてDelphi内部での処理が働くことを抑止し、かわりにそれらが表示されたり隠されたりするようにします。これで、フォームは正しく振舞うはずです。フォームは、最小化/最大化するときに、正しくアニメーションされます。また、タスクバーに最小化したときでも、サムネイルは正しく表示されます。
しかし、もうひとつ重要な問題が残っています。今回のサンプルアプリケーションを実行し、Helpをクリックして、Aboutを表示します。そして、フォームのタスクバーボタンをクリックしたとします。すると、フォームは、Aboutダイアログの前に表示されます。これは、Delphiは、デフォルトで、モーダルフォームの親に隠し「アプリケーションフォーム」を設定するからです。これを解決するには、Form.ShowModalを呼び出す前にForm.PopupParentを必ず設定しなければなりません。
procedure TMainForm.AboutActionExecute(Sender: TObject);
begin
AboutForm := TAboutForm.Create(Self);
try
AboutForm.PopupParent := Self;
AboutForm.ShowModal;
finally
FreeAndNil(AboutForm);
end;
end;
サンプルアプリケーションのステップ3は、こちらからダウンロードできます。
アプリケーションの透明化
おそらく皆さんは、(3Dグラフィックアクセラレータを搭載したマシンを使って)Windows Vista上でアプリケーションを実行すると、透明で、ガラスのようなボーダーと非クライアント領域が描画されることをご存知だと思います。これは、まさしくそのままGlassと呼ばれています。DMW(Desktop Windows Manager)には、このGlassサーフェイスをアプリケーションのクライアント領域にまで広げるAPI呼び出しが用意されています。必須事項ではないにしても、これは、皆さんのアプリケーションが「Vista」アプリケーションとしてフィットする助けになるでしょう。
では、uVistaFuncs.pasファイルに、CompositingEnabledとExtendGlassという新しい関数を追加することからはじめましょう。これらのメソッドは、ここにあるコードをベースにしています。CompositingEnabledは、3D機能が現在有効かどうかを返します。また、ExtendGlassは、Glassサーフェイスをフォームのクライアント領域にまで拡張できるようにします。次に、フォームの下部にパネルを追加します。これは、Glassサーフェイスを拡張するDWM API呼び出しでは、その描画用にブラックサーフェイスを必要とするからです。最後に、メインフォームに新しいメソッドを追加します。このメソッドは、合成が可能かどうかをチェックし、可能なら、メインフォームにGlassを描画するために、パネルを黒色に設定してExtendGlassを呼び出します。

サンプルアプリケーションのステップ4は、こちらからダウンロードできます。
タスクダイアログのプロンプト
Windows Vistaは、タスクダイアログと呼ばれる新しいユーザーエクスペリエンスの要素を導入しています。これにより、単一のAPIによって複数の異なるダイアログを可能とし、一貫したユーザーエクスペリエンスを提供します。Task Dialog APIを使って極めて複雑なダイアログを作成することもできますが、MessageDlgの単純なリプレースからはじめることにします。われわれのメソッドは、Vistaのタスクダイアログが有効なら、これを使います。また、必要ならばMessageDlgを呼び出します。こちらは、これを実装した大変すばらしい例です。
一旦、uVistaFuncs.pasユニットにタスクダイアログのための新しいメソッドを追加したら、SaveHandledメソッドへの単純な変更によって、アプリケーションのプロンプトをVistaのユーザーエクスペリエンスによりフィットさせ、エンドユーザーにより完全なフィードバックを提供することができます。
従来スタイルのメッセージダイアログ

新しいWindows Vistaタスクダイアログ

サンプルアプリケーションのステップ5は、こちらからダウンロードできます。
イメージがすべて
さあ、あと一息です。この変更は、実際、Windows 2000からWindows XPにアプリケーションの外観をポーティングするときに、行わなければなりませんでした。しかし、このサンプルでは、そのアプリケーションインターフェイスはかなり時代遅れです。次に私たちが行わなければならないのは、アプリケーション中のフラットでいまいちのイメージを、もっとよい、アンチエイリアスで、できれば影がついて、アルファブレンドされたイメージにリプレースすることです。私は、最良の方法を見つけました。それは、ここにあるように、PngComponentsとTPngImageListを使うことです。この、TImageListの単純なリプレースによって、アルファブレンドされたPNGイメージをイメージリストにロードし、アプリケーションで利用できるようになります。

サンプルアプリケーションのステップ6は、こちらからダウンロードできます。
新しいコモンダイアログ
Windows Vistaでは、デザイン変更されたオープンダイアログと保存ダイアログを含む、新しいコモンダイアログを導入しています。これらのコモンダイアログには、Windows Vistaの新しい特徴、(右クリックのコンテキストメニューでカスタマイズ可能な)新しい「お気に入り」リンク領域、新しいビュー、折りたたみ可能な保存ダイアログなどを含んでいます。
旧スタイルの保存ダイアログ

新しいスタイルの保存ダイアログ – 折りたたんだところ

新しいスタイルの保存ダイアログ – 展開したところ

残念ながら、Delphiで書かれたアプリケーションは、自動的に新しいスタイルのオープンダイアログや保存ダイアログを使うようにはなりません。これは、Dialog.pasのソースコードで使われているフラグ、OFN_ENABLEHOOKおよびOFN_ENABLETEMPLATEが原因です。詳しくは、Quality Centralでのレポートを参照してください。
この問題のワークアラウンドは以下のとおりです。
・まず、Dialog.pasをコピーを作成し、これをアプリケーションのソースコードディレクトリか、ライブラリパスが通っているどこかに置いてください。
・次に、Dialog.pasのコピーファイル中で、”OFN_ENABLEHOOK” を検索します。そして、”Flags := OFN_ENABLEHOOK” と記述された箇所をコメントアウトします。
・コメントアウトしたコードの下に、”Flags := 0;” という行を追加します。
・同じように、”OFN_ENABLETEMPLATE” を検索し、最初に見つかる OFN_ENABLETEMPLATE の2行上にある"if Template <> nil then" から始まって "hWndOwner := Application.Handle" で終わる20行ほどのコードをコメントアウトします。
・そして、コメントアウトした行の後に、”hWndOwner := ParentWnd” という行を追加します。
・TCommonDialog.Executeメソッドに移動します。 “if”、“begin”、“end”、“else”、そして、 “ParentWnd := Application.Handle” をコメントアウトします。これにより、ParentWndをアクティブフォームハンドルかApplication.Handleに常に設定しようとするコードになります。
・コピーしたDialog.pasを保存します。
これで、サンプルアプリケーションで、新しい保存ダイアログとオープンダイアログを使うようにできます。最初のステップでは、オリジナルのVCLファイルを変更することなく、変更されたVCLのコピーを使用できるようにします。2番目のステップでは、最初の問題となるフラグを除去しますが、同時にダイアログがセンタリングされないことも意味します。3番目のステップは、2番目のステップを拡張しただけです。フラグの初期値として 0 を設定しています。4番目のステップでは、2番目の問題となるフラグを除去します。また、ダイアログをセンタリングするコードを無効にします。5番目と6番目のステップでは、ダイアログをセンタリングする機能を取り除いたことに対処します。そして、ダイアログはアクティブフォームが親になります。
このセクションはVCLソースの変更に関係しているので、そのソースをポストすることができません。しかし、上記のステップに従えば、アプリケーションのソースコードをまったく変更することなく、新しいオープンダイアログと保存ダイアログを使用できるようになります。もし、Dialog.pasに変更を加えたくない場合には、API呼び出しによって、新しいオープン/保存ダイアログを表示する方法を説明した、こちらのページを参照してください。
最終的なサンプルアプリケーションの実行形式は、こちらからダウンロードできます。
まとめ