Windows Vista対応アプリを作る

・イントロダクション
  まずは概要を知る必要があります。"Windows Vista デベロッパ ストーリー: アプリケーション互換性解説書" をナナメ読みして下さい。

  詳細情報が幾つか CodeGear に載せられています。

  なお、上記記事中にある TaskDialog のアイコン指定用引数に指定すべき定数の値が間違っています。本来は Icon は PWideChar で、"MakeIntResource(C++ではマクロ)" を使って指定しなくてはなりません。以下の定数を用いないと、全然関係のないアイコンが表示されてしまうことでしょう。

TD_ICON_BLANK          = 32512;
TD_ICON_WARNING        = 32515;
TD_ICON_QUESTION       = 32514;
TD_ICON_ERROR          = 32513;
TD_ICON_INFORMATION    = 32516;
TD_ICON_BLANK_AGAIN    = 32517;
TD_ICON_SHIELD         = 32518;

他にもあります。
 Vista での障害情報は "Borland Developer Network(BDN)" の "Quality Central" で探す事ができます。
また、このサイトにある
も参考になると思います。とりあえずは XP 対応から始める必要があります。

 Delphi 7 以降であれば、メインフォームに "XPMan" を uses してください(または TXPManifest を貼り付けて下さい)。XP 対応化を行うだけで、幾つかのコモンコントロールの機能がグレードアップされます。

 例えば "TDateTimePicker" は XPMan を uses した時とそうでない時で挙動が異なりますし、不完全ではありますがメッセージダイアログが Vista スタイルのものに変化します。これらの理由により、Vista 対応アプリケーション作成には Delphi 7 以降の開発環境を推奨します。

・致命的な問題
 Delphi で作成したアプリケーションを Vista 上で動作させるにあたり、致命的な問題があります。Delphi で作成したアプリケーションをお持ちなら、それを起動し Alt キーを押してみましょう。単に Alt キーを押すだけです。この動作を行うだけで標準的なコントロールが消えてしまうのではないかと思います。

[設計時]   [表示時]   [Alt押下時]
設計時 最初に表示された時はいいのですが... Alt キーを押すと消えてしまいます(ToT)

 Delphi 7またはそれ以降であれば、"Fix for QC report 37403" にあるコンポーネントをインストールし、メインフォームに VistaAltFixUnit を uses し、FormCreate で "TVistaAltFix.Create(Self);" と一行記述するだけで、この問題を回避する事ができます。現在 Vista をお使いでなくても、このコンポーネントの導入を強く推奨します。この現象は "XPMan" を uses した時、またはマニフェストファイル (*.manifest) を利用してビジュアルスタイルに対応している場合に発生します。

・致命的な問題 (その2)
 Delphi7でのみ確認している問題です (少なくとも BDS 2006 / Delphi 2007 では発生しません)。 TScrollBox に TLabel を幾つか貼ります。実行するとちゃんと TLabel は見えます。TScrollBox にさらに TEdit を追加します。実行すると...ラベルが見えなくなります

[設計時]   [表示時]    [TEeit貼付時]
設計時 ラベルだけなら表示できますが、TEdit を貼ると... ラベルが消えてしまいます(ToT)

 この現象は "XPMan" を uses した時、またはマニフェストファイル (*.manifest) を利用してビジュアルスタイルに対応している場合に発生し、VistaAltFixUnit でも解決できません。TCustomEdit から派生したコントロールを貼るとラベルが見えなくなってしまうようです。 VistaAltFixUnit を導入しない場合、Alt キーを押せば一旦描画されますが、画面の外にウィンドウを持っていくとまたラベルが消えてしまいます。回避するには、TLabel を TStaticText で置換するしかなさそうです。置換したら置換したでフォントの色が指定できなくなってしまう問題が出てきますが。

・ヘルプシステム
 Windows 95 から使えていた、旧来の WinHelp は使えなくなりました (記事)。 これにより、Windows 95 ~ Vista まで一貫して使える Help システムは存在しない事になります。 次に OS サポートの多い Help システムは HTML-Help1.0 (*.chm) です。 "HTML-Helpを呼び出す"を参考にして下さい。但し、chm 形式のヘルプシステムですら、Vista ではサポート打ち切りの予定だったようですから、今後はプレーンな HTML を用いてヘルプファイルを構築した方がいいのかもしれません。 次世代ヘルプシステムは迷走しており、とても使えるレベルではありません。HTML-Help 2.0 (*.HxS) は Visual Studio の一機能として実装されているフシがあり、スタンドアロンなヘルプコンパイラだけでは HTML-Help 2.0 を使ったヘルプを利用するのは難しいでしょう (どうも開発は継続されない模様です)。Vistaの "AP Help" に至っては SDK すら配布が行われていませんし、その配布時期/方法等も一切未定です。

  なお、WinHelp は Vista 版の提供が予定されています。 但し、この Vista 版 WinHelp をアプリケーションに同梱して配布する事は禁じられています。 Vista で WinHelp が本当に動作しないかと言えばそうではなく、古い OS の %SystemRoot% にある "winhlp32.exe" を Vista で動作させる事ができます (System32 に存在する winhlp32.exe はスタブです)。

 (2007/03/09追記) Windows Vista 用 winhlp32.exe がリリースされました。
 (2009/10/15追記) Windows 7 用 winhlp32.exe がリリースされました。

・アイコン
 Windows のデスクトップに表示されるアイコンは 32×32 と相場が決まっていましたが、Vista では 48×48 が標準サイズです。 旧来のサイズのアイコンしか持っていない場合には、1.5 倍に拡大されるため、キレイに表示させる事ができません。 しかも、Explorer を含め、32×32 サイズのアイコンを見る機会が殆どありません。 できる限りアプリケーションに 48×48 サイズのアイコンを含めるようにしましょう。

 Vista では 128×128 以上のアイコンを作る事ができます。 256×256 なんていうものもありますが、これには要注意です。 画像のフォーマットが PNG になっているからです。 128×128 以上の BMP 形式のアイコンを旧来のアイコンフォーマットに則った形で作成しても正しく表示されないので注意が必要です。

 Delphi 2005 / 2006 用なら "ResEd (http://www.theunknownones.net/)" というリソースエディタが利用できます。これを使って Vista アイコンを埋め込む事が可能です。

・TAnimation
 OS 側で用意されていたアニメーション AVI が利用できなくなりました。アニメーション AVI は自前で用意する必要があります。

・シェルフォルダ
  シェルフォルダの名称が一部変更されています。シェルフォルダは決め打ちではなく API から取得するようにしましょう。 以下に 2 つの関数を用意しました (ShlObj, ActiveX を uses して使って下さい)。

// Windowsのフォルダを取得
function GetFolderPath(var Path: String; nFolder: Integer): Boolean;
var
  Buf: PChar;
begin
  Buf := StrAlloc(MAX_PATH);
  try
    ZeroMemory(Buf, MAX_PATH);
    case nFolder of
      0// カレントフォルダ
        result := (GetCurrentDirectory(MAX_PATH, Buf) <> 0);
      1// Windowsフォルダ
        result := (GetWindowsDirectory(Buf, MAX_PATH) <> 0);
      2// Windowsシステムフォルダ
        result := (GetSystemDirectory(Buf, MAX_PATH) <> 0);
      3// テンポラリフォルダ
        result := (GetTempPath(MAX_PATH, Buf) <> 0);
    else
      result := False;
    end;
    if result then
      Path := ExcludeTrailingPathDelimiter(StrPas(Buf));
  finally
    StrDispose(Buf);
  end;
end;

// Windowsの特殊フォルダを取得
function GetSpecialFolderPath(hwndOwner: THandle; var Path: String; nFolder: Integer): Boolean;
var
  Buf: PChar;
  { _GetSpecialFolderPath begin }
  function _GetSpecialFolderPath(hwndOwner: THandle; var lpszPath: PChar; nFolder: Integer; fCreate: Boolean): Boolean;
  var
    pidl: PItemIDList;
    pMalloc: IMalloc;
  begin
    result := False;
    if SHGetMalloc(pMalloc) <> NOERROR then
      Exit;
    if SHGetSpecialFolderLocation(hwndOwner, nFolder, pidl) = NOERROR then
      Exit;
    if SHGetPathFromIDList(pidl, lpszPath) then
      result := True;
    pMalloc.Free(pidl);
  end;
{ _GetSpecialFolderPath end }
begin
  result := True;
  Buf := StrAlloc(MAX_PATH);
  try
    if SHGetSpecialFolderPath(hwndOwner, Buf, nFolder, False) then
      Path := ExcludeTrailingPathDelimiter(StrPas(Buf))
    else if _GetSpecialFolderPath(hwndOwner, Buf, nFolder, False) then
      Path := ExcludeTrailingPathDelimiter(StrPas(Buf))
    else
      result := False;
  finally
    StrDispose(Buf);
  end;
end;

   "GetFolderPath" は一般的な Windows のフォルダを取得します。 "GetSpecialFolderPath" はもっと詳細なフォルダを取得します。 どちらの関数も "nFolder" に取得したいフォルダのインデックス値を入れると "Path" にフォルダパスを返します。 インデックスが定義されていないか、正しくフォルダパスを取得できなかった場合には戻り値に False が返ります。"GetFolderPath" のインデックス値については関数内のコメントをご覧下さい。

 
"GetSpecialFolderPath" のインデックス値は CSIDL 値と呼ばれるものです。CSIDL 値と、それが返すフォルダについては こちらをご覧下さい。 また、Visual Studio や Win32 用 gcc、Wine 等から "shlobj.h" を検索すると最新の CSIDL 値を調べる事ができます。 CSIDL 値の "実際の値" と "それが示す実際のパス" を簡単に知りたいのであれば、このサイトにある "SHSFP" を DL し、コマンドラインから "shsfp -v" と叩いてみて下さい。

 パスを環境設定ファイルに保存する場合には、"環境変数文字列を含むパス" で保存しておき、読み込む際に絶対パスに変換してやるようにすると問題が少なくなると思われます。 以下の関数は "環境変数文字列を含むパス" を絶対パスに変換するものです。

function ExpandEnvironmentString(S: String): String;
var
  n: Integer;
  Dest: String;
begin
  n := ExpandEnvironmentStrings(PChar(S), nil0);
  SetLength(Dest, n * 2);
  ExpandEnvironmentStrings(PChar(S), PChar(Dest), n * 2);
  SetLength(Dest, StrLen(PChar(Dest)));
  result := Dest;
end;

   この関数は先述の特殊フォルダ取得の代わりに使う事もできます。 例えば Windows フォルダを取得したいのであれば "ExpandEnvironmentString('%SystemRoot%')"、 アプリケーションデータフォルダを取得したい場合には "ExpandEnvironmentString('%AppData%')"とすればいい事になります。

 Vista 限定の話からは外れますが、以下に "CSIDL / 環境変数 / 各 OS での実際のパス" を一覧表にしてみました。 Windows 95 ~ Vista まで問題なく動作するアプリケーションを作る際の手助けになれば幸いです。 古い OS (95 / NT4.0 等) で特殊フォルダを取得するには "最新の SP と IE 4.0 以上が必要" となります。

CSIDL 環境変数 (9x) 環境変数 (NT) Windows 95 with IE5.5 SP2 Windows 98/98SE with IE6 Windows MilleniumEdition with IE6 Windows NT4.0 SP6a with IE6 Windows 2000 SP4 with IE6 Windows XP SP2 Windows Vista
CSIDL_DESKTOP 0x0000     C:\WINDOWS\デスクトップ C:\WINDOWS\デスクトップ C:\WINDOWS\デスクトップ C:\WINNT\Profiles\(USER)\デスクトップ C:\Documents and Settings\(USER)\デスクトップ C:\Documents and Settings\(USER)\デスクトップ C:\Users\(USER)\Desktop
CSIDL_PROGRAMS 0x0002     C:\WINDOWS\スタート メニュー\プログラム C:\WINDOWS\スタートメニュー\プログラム C:\WINDOWS\スタート メニュー\プログラム C:\WINNT\Profiles\(USER)\スタート メニュー\プログラム C:\Documents and Settings\(USER)\スタート メニュー\プログラム C:\Documents and Settings\(USER)\スタート メニュー\プログラム C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
CSIDL_PERSONAL 0x0005     C:\My Documents C:\My Documents C:\My Documents C:\WINNT\Profiles\(USER)\Personal C:\Documents and Settings\(USER)\My Documents C:\Documents and Settings\(USER)\My Documents C:\Users\(USER)\Documents
CSIDL_FAVORITES 0x0006     C:\WINDOWS\Favorites C:\WINDOWS\Favorites C:\WINDOWS\Favorites C:\WINNT\Profiles\(USER)\Favorites C:\Documents and Settings\(USER)\Favorites C:\Documents and Settings\(USER)\Favorites C:\Users\(USER)\Favorites
CSIDL_STARTUP 0x0007     C:\WINDOWS\スタート メニュー\プログラム\スタートアップ C:\WINDOWS\スタート メニュー\プログラム\スタートアップ C:\WINDOWS\スタート メニュー\プログラム\スタートアップ C:\WINNT\Profiles\(USER)\スタート メニュー\プログラム\スタートアップ C:\Documents and Settings\(USER)\スタート メニュー\プログラム\スタートアップ C:\Documents and Settings\(USER)\スタート メニュー\プログラム\スタートアップ C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
CSIDL_RECENT 0x0008     C:\WINDOWS\Recent C:\WINDOWS\Recent C:\WINDOWS\Recent C:\WINNT\Profiles\(USER)\Recent C:\Documents and Settings\(USER)\Recent C:\Documents and Settings\(USER)\Recent C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Recent
CSIDL_SENDTO 0x0009     C:\WINDOWS\SendTo C:\WINDOWS\SendTo C:\WINDOWS\SendTo C:\WINNT\Profiles\(USER)\SendTo C:\Documents and Settings\(USER)\SendTo C:\Documents and Settings\(USER)\SendTo C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\SendTo
CSIDL_STARTMENU 0x000B     C:\WINDOWS\スタート メニュー C:\WINDOWS\スタート メニュー C:\WINDOWS\スタート メニュー C:\WINNT\Profiles\(USER)\スタート メニュー C:\Documents and Settings\(USER)\スタート メニュー C:\Documents and Settings\(USER)\スタート メニュー C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Start Menu
CSIDL_MYMUSIC 0x000D               C:\Documents and Settings\(USER)\My Documents\My Music C:\Users\(USER)\Music
CSIDL_MYVIDEO 0x000E               C:\Documents and Settings\(USER)\My Documents\My Videos C:\Users\(USER)\Videos
CSIDL_DESKTOPDIRECTORY 0x0010     C:\WINDOWS\デスクトップ C:\WINDOWS\デスクトップ C:\WINDOWS\デスクトップ C:\WINNT\Profiles\(USER)\デスクトップ C:\Documents and Settings\(USER)\デスクトップ C:\Documents and Settings\(USER)\デスクトップ C:\Users\(USER)\Desktop
CSIDL_NETHOOD 0x0013     C:\WINDOWS\NetHood C:\WINDOWS\NetHood C:\WINDOWS\NetHood C:\WINNT\Profiles\(USER)\NetHood C:\Documents and Settings\(USER)\NetHood C:\Documents and Settings\(USER)\NetHood C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Network Shortcuts
CSIDL_FONTS 0x0014     C:\WINDOWS\FONTS C:\WINDOWS\FONTS C:\WINDOWS\Fonts C:\WINNT\Fonts C:\WINNT\Fonts C:\WINDOWS\Fonts C:\Windows\Fonts
CSIDL_TEMPLATES 0x0015     C:\WINDOWS\ShellNew C:\WINDOWS\ShellNew C:\WINDOWS\Templates C:\WINNT\ShellNew C:\Documents and Settings\(USER)\Templates C:\Documents and Settings\(USER)\Templates C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Templates
CSIDL_COMMON_STARTMENU 0x0016           C:\WINNT\Profiles\All Users\スタート メニュー C:\Documents and Settings\All Users\スタート メニュー C:\Documents and Settings\All Users\スタート メニュー C:\ProgramData\Microsoft\Windows\Start Menu
CSIDL_COMMON_PROGRAMS 0x0017           C:\WINNT\Profiles\All Users\スタート メニュー\プログラム C:\Documents and Settings\All Users\スタート メニュー\プログラム C:\Documents and Settings\All Users\スタート メニュー\プログラム C:\ProgramData\Microsoft\Windows\Start Menu\Programs
CSIDL_COMMON_STARTUP 0x0018         C:\WINDOWS\All Users\スタート メニュー\プログラム\スタートアップ C:\WINNT\Profiles\All Users\スタート メニュー\プログラム\スタートアップ C:\Documents and Settings\All Users\スタート メニュー\プログラム\スタートアップ C:\Documents and Settings\All Users\スタート メニュー\プログラム\スタートアップ C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
CSIDL_COMMON_DESKTOPDIRECTORY 0x0019         C:\WINDOWS\All Users\デスクトップ C:\WINNT\Profiles\All Users\デスクトップ C:\Documents and Settings\All Users\デスクトップ C:\Documents and Settings\All Users\デスクトップ C:\Users\Public\Desktop
CSIDL_APPDATA 0x001A   %APPDATA% C:\WINDOWS\Application Data C:\WINDOWS\Application Data C:\WINDOWS\Application Data C:\WINNT\Profiles\(USER)\Application Data C:\Documents and Settings\(USER)\Application Data C:\Documents and Settings\(USER)\Application Data C:\Users\(USER)\AppData\Roaming
CSIDL_PRINTHOOD 0x001B       C:\WINDOWS\PrintHood C:\WINDOWS\PrintHood C:\WINNT\Profiles\(USER)\PrintHood C:\Documents and Settings\(USER)\PrintHood C:\Documents and Settings\(USER)\PrintHood C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Printer Shortcuts
CSIDL_LOCAL_APPDATA 0x001C         C:\WINDOWS\Application Data   C:\Documents and Settings\(USER)\Local Settings\Application Data C:\Documents and Settings\(USER)\Local Settings\Application Data C:\Users\(USER)\AppData\Local
CSIDL_ALTSTARTUP 0x001D                 C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
CSIDL_COMMON_ALTSTARTUP 0x001E                 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
CSIDL_COMMON_FAVORITES 0x001F             C:\Documents and Settings\All Users\Favorites C:\Documents and Settings\All Users\Favorites C:\Users\(USER)\Favorites
CSIDL_INTERNET_CACHE 0x0020     C:\WINDOWS\Temporary Internet Files C:\WINDOWS\Temporary Internet Files C:\WINDOWS\Temporary Internet Files C:\WINNT\Profiles\(USER)\Temporary Internet Files C:\Documents and Settings\(USER)\Local Settings\Temporary Internet Files C:\Documents and Settings\(USER)\Local Settings\Temporary Internet Files C:\Users\(USER)\AppData\Local\Microsoft\Windows\Temporary Internet Files
CSIDL_COOKIES 0x0021     C:\WINDOWS\Cookies C:\WINDOWS\Cookies C:\WINDOWS\Cookies :C:\WINNT\Profiles\(USER)\Cookies C:\Documents and Settings\(USER)\Cookies C:\Documents and Settings\(USER)\Cookies C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Cookies
CSIDL_HISTORY 0x0022     C:\WINDOWS\History C:\WINDOWS\History C:\WINDOWS\History C:\WINNT\Profiles\(USER)\History C:\Documents and Settings\(USER)\Local Settings\History C:\Documents and Settings\(USER)\Local Settings\History C:\Users\(USER)\AppData\Local\Microsoft\Windows\History
CSIDL_COMMON_APPDATA 0x0023         C:\WINDOWS\All Users\Application Data   C:\Documents and Settings\All Users\Application Data C:\Documents and Settings\All Users\Application Data C:\ProgramData
CSIDL_WINDOWS 0x0024 %WINDIR%
%WINBOOTDIR%
%SYSTEMROOT
%WINDIR%
    C:\WINDOWS   C:\WINNT C:\WINDOWS C:\Windows
CSIDL_SYSTEM 0x0025         C:\WINDOWS\SYSTEM   C:\WINNT\system32 C:\WINDOWS\system32 C:\Windows\system32
CSIDL_PROGRAM_FILES 0x0026   %PROGRAMFILES%     C:\Program Files   C:\Program Files C:\Program Files C:\Program Files
CSIDL_MYPICTURES 0x0027         C:\My Documents\My Pictures   C:\Documents and Settings\(USER)\My Documents\My Pictures C:\Documents and Settings\(USER)\My Documents\My Pictures C:\Users\(USER)\Pictures
CSIDL_PROFILE 0x0028   %USERPROFILE%
%HOMEPATH%
        C:\Documents and Settings\(USER) C:\Documents and Settings\(USER) C:\Users\(USER)
CSIDL_SYSTEMX86 0x0029         C:\WINDOWS\SYSTEM   C:\WINNT\system32 C:\WINDOWS\system32 C:\Windows\system32
CSIDL_PROGRAM_FILESX86 0x002A                 C:\Program Files
CSIDL_PROGRAM_FILES_COMMON 0x002B         C:\Program Files\Common Files   C:\Program Files\Common Files C:\Programs\Common Files C:\Program Files\Common Files
CSIDL_COMMON_TEMPLATES 0x002D             C:\Documents and Settings\All Users\Templates C:\Documents and Settings\All Users\Templates C:\ProgramData\Microsoft\Windows\Templates
CSIDL_COMMON_DOCUMENTS 0x002E         C:\WINDOWS\All Users\Documents   C:\Documents and Settings\All Users\Documents C:\Documents and Settings\All Users\Documents C:\Users\Public\Documents
CSIDL_COMMON_ADMINTOOLS 0x002F             C:\Documents and Settings\All Users\スタート メニュー\プログラム\管理ツール C:\Documents and Settings\All Users\スタート メニュー\プログラム\管理ツール C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools
CSIDL_ADMINTOOLS 0x0030               C:\Documents and Settings\(USER)\スタート メニュー\プログラム\管理ツール C:\Users\(USER)\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools
CSIDL_COMMON_MUSIC 0x0035               C:\Documents and Settings\All Users\Documents\My Music C:\Users\Public\Music
CSIDL_COMMON_PICTURES 0x0036               C:\Documents and Settings\All Users\Documents\My Pictures C:\Users\Public\Pictures
CSIDL_COMMON_VIDEO 0x0037               C:\Documents and Settings\All Users\Documents\My Videos C:\Users\Public\Videos
CSIDL_RESOURCES 0x0038               C:\WINDOWS\resources C:\Windows\resources
CSIDL_CDBURN_AREA 0x003B               C:\Documents and Settings\(USER)\Local Settings\Application Data\Microsoft\CD Burning C:\Users\(USER)\AppData\Local\Microsoft\Windows\Burn\Burn

  また、Vista では "%ProgramFiles%" へのアクセスが制限されています。"%ProgramFiles%" へのアクセスには UAC 無効化が必要ですので、インストールフォルダは自由に指定できるようするべきです。

・メッセージボックス
  上で少し触れたメッセージボックスですが、以下に MessageBox() / TaskDialog() を意識せずに使える MessageBoxNew を掲載しておきます。

function MessageBoxNew(hWnd: THandle; ATitle, ADescription, AContent: String; AType: DWORD): Integer;
label
  OLDDLG;
const
  TD_ICON_BLANK = 32512;
  TD_ICON_WARNING = 32515;
  TD_ICON_QUESTION = 32514;
  TD_ICON_ERROR = 32513;
  TD_ICON_INFORMATION = 32516;
  TD_ICON_BLANK_AGAIN = 32517;
  TD_ICON_SHIELD = 32518;
  TD_OK = 1;
  TD_YES = 2;
  TD_NO = 4;
  TD_CANCEL = 8;
  TD_RETRY = 16;
  TD_CLOSE = 32;
  DLGRES_OK = 1;
  DLGRES_CANCEL = 2;
  DLGRES_RETRY = 4;
  DLGRES_YES = 6;
  DLGRES_NO = 7;
  DLGRES_CLOSE = 8;
var
  DLLHandle: THandle;
  Version: TOSVERSIONINFO;
  Buttons: Integer;
  ICon: Integer;
  Dmy: String;
  res: Integer;
  wTitle, wDescription, wContent: array [0 .. 1024of widechar;
  TaskDialogProc: function(hWnd: THandle; hInstance: THandle; cTitle, cDescription,
    cContent: pwidechar; Buttons: Integer; ICon: Integer; ResButton: pinteger): Integer;
cdecl stdcall;
begin // OSバージョンがVista以降か?
  Version.dwOSVersionInfoSize := SizeOf(Version);
  if not GetVersionEx(Version) then
    goto OLDDLG;
  if (Version.dwMajorVersion < 6then
    goto OLDDLG;
  // comctl32.dllがロードできるか?
  DLLHandle := LoadLibrary('comctl32.dll');
  if DLLHandle < 32 then
    goto OLDDLG;
  // TaskDialogが利用可能か?
  @TaskDialogProc := GetProcAddress(DLLHandle, 'TaskDialog');
  if not Assigned(TaskDialogProc) then
    goto OLDDLG;
  // TaskDialogを表示  Buttons := 0;
  if ((AType and MB_YESNOCANCEL) = MB_YESNOCANCEL) then
    begin
      Buttons := Buttons or TD_YES;
      Buttons := Buttons or TD_NO;
      Buttons := Buttons or TD_CANCEL;
    end
  else if ((AType and MB_RETRYCANCEL) = MB_RETRYCANCEL) then
    begin
      Buttons := Buttons or DLGRES_RETRY;
      Buttons := Buttons or TD_CANCEL;
    end
  else if ((AType and MB_ABORTRETRYIGNORE) = MB_ABORTRETRYIGNORE) then
    begin
      Buttons := Buttons or DLGRES_CLOSE;
      Buttons := Buttons or DLGRES_RETRY;
      Buttons := Buttons or TD_CANCEL;
    end
  else if ((AType and MB_OKCANCEL) = MB_OKCANCEL) then
    begin
      Buttons := Buttons or TD_OK;
      Buttons := Buttons or TD_CANCEL;
    end
  else if ((AType and MB_YESNO) = MB_YESNO) then
    begin
      Buttons := Buttons or TD_YES;
      Buttons := Buttons or TD_NO;
    end
  else if ((AType and MB_OK) = MB_OK) then
    begin
      Buttons := Buttons or TD_OK;
    end;
  if (AType and MB_ICONERROR) = MB_ICONERROR then
    ICon := TD_ICON_ERROR
  else if (AType and MB_ICONQUESTION) = MB_ICONQUESTION then
    ICon := TD_ICON_QUESTION
  else if (AType and MB_ICONINFORMATION) = MB_ICONINFORMATION then
    ICon := TD_ICON_INFORMATION
  else if (AType and MB_ICONEXCLAMATION) = MB_ICONEXCLAMATION then
    ICon := TD_ICON_WARNING
  else
    ICon := TD_ICON_BLANK;
  StringToWideChar(ATitle, wTitle, SizeOf(wTitle));
  StringToWideChar(ADescription, wDescription, SizeOf(wDescription));
  StringToWideChar(AContent, wContent, SizeOf(wContent));
  TaskDialogProc(hWnd, 0, wTitle, wDescription, wContent, Buttons, ICon, @res);
  result := ID_YES;
  case res of
    DLGRES_OK:
      result := ID_OK;
    DLGRES_CANCEL:
      result := ID_CANCEL;
    DLGRES_RETRY:
      result := ID_RETRY;
    DLGRES_YES:
      result := ID_YES;
    DLGRES_NO:
      result := ID_NO;
    DLGRES_CLOSE:
      result := ID_ABORT;
  end;
  Exit;
OLDDLG:;
  // MessageBoxを表示
  Dmy := ADescription;
  if AContent <> '' then
    begin
      if Dmy <> '' then
        Dmy := Dmy + #$D#$A + #$D#$A;
      Dmy := Dmy + AContent;
    end;
  result := MessageBox(hWnd, PChar(Dmy), PChar(ATitle), AType);
end;

 なお、 TaskDialog() は MessageBox() の代わりはできますが、MessageDlg() の代わりには使えません。 何故なら、TaskDialog() には "すべてはい / すべていいえ" 等のボタンが用意されていないからです。 MessageDlg() の代わりになるものを Vista スタイルで作るには TaskDialogIndirect() でどうにかするしかありません。

  以下に Vista スタイルで MessageDlg() のように使える MessageDlgNew を掲載します。

function MessageDlgNew(AHWND: THandle; const ATitle, ADescription, AContent: String;
  ADlgType: TMsgDlgType; AButtons: TMsgDlgButtons; AHelpCtx: Longint): Integer;
label OLDDLG;
const // アイコン定数
  TD_ICON_BLANK = 32512;
  TD_ICON_WARNING = 32515;
  TD_ICON_QUESTION = 32514;
  TD_ICON_ERROR = 32513;
  TD_ICON_INFORMATION = 32516;
  TD_ICON_BLANK_AGAIN = 32517;
  TD_ICON_SHIELD = 32518;
  // ボタン用定数
  MD_Help = 100;
  // ボタン文字列用配列
  BtnTable: array [0 .. 10of string =
    ('はい(&Y)''いいえ(&N)''OK''キャンセル''中止(&A)''再試行(&R)',
     '無視(&I)''すべて(&A)''すべていいえ(&O)''すべてはい(&A)''ヘルプ(&H)');
  // 戻り値用配列
  RetTable: array [0 .. 10of WORD =
    (mrYes, mrNo, mrOk, mrCancel, mrAbort, mrRetry,
     mrIgnore, mrAll, mrNoToAll, mrYesToAll, MD_Help);
type
  TTASKDIALOGCONFIG =
    packed record
      cbSize: uint;
      hwndParent: HWND;
      hInstance: longword;
      dwFlags: dword;
      dwCommonButtons: dword;
      pszWindowTitle: PWideChar;
      case Integer of
        0:(hMainIcon: HICON);
        1:(pszMainIcon: Integer;
            pszMainInstruction: PWideChar;
            pszContent: PWideChar;
            cButtons: uint;
            pButtons: pointer;
            iDefaultButton: Integer;
            cRadioButtons: uint;
            pRadioButtons: pointer;
            iDefaultRadioButton: Integer;
            pszVerificationText, pszExpandedInformation, pszExpandedControlText, pszCollapsedControlText: PWideChar;
            case Integer of
              0: (hFooterIcon: HICON);
              1: (pszFooterIcon: Integer; pszFooterText: PWideChar; pfCallback: pointer; lpCallbackData: pointer; cxWidth: uint;));
      end;

  PTASKDIALOGCONFIG = ^TTASKDIALOGCONFIG;

  TTASKDIALOG_BUTTON =
    packed record
      nButtonId: Integer;
      pszButtonText: PWideChar;
    end;

  BtnCaption = array [0 .. 31of WideChar;
var
  Btn: WORD;
  Dmy: String;
  DLLHandle: THandle;
  Version: TOSVERSIONINFO;
  TDC: TTASKDIALOGCONFIG;
  Buttons: array of TTASKDIALOG_BUTTON;
  BtnCaptions: array of BtnCaption;
  BtnCnt: Integer;
  res: Integer;
  i: Integer;
  wTitle, wDescription, wContent: array [0 .. 1024of WideChar;
  TaskDialogIndirectProc: function(ptc: PTASKDIALOGCONFIG; pnButton: PInteger; pnRadioButton: PInteger;
    pfVerificationFlagChecked: PBool): HRESULT cdecl stdcall;
begin
  // OSバージョンがVista以降か?
  Version.dwOSVersionInfoSize := SizeOf(Version);
  if not GetVersionEx(Version) then
    goto OLDDLG;
  if (Version.dwMajorVersion < 6then
    goto OLDDLG;
  // comctl32.dllがロードできるか?
  DLLHandle := LoadLibrary('comctl32.dll');
  if DLLHandle < 32 then
    goto OLDDLG;
  // TaskDialogIndirectが利用可能か?
  @TaskDialogIndirectProc := GetProcAddress(DLLHandle, 'TaskDialogIndirect');
  if not Assigned(TaskDialogIndirectProc) then
    goto OLDDLG;
  // TaskDialogIndirectを表示
  ZeroMemory(@TDC, SizeOf(TDC));
  TDC.cbSize := SizeOf(TDC);
  TDC.hwndParent := AHWND;
  TDC.hInstance := 0;
  StringToWideChar(ATitle, wTitle, SizeOf(wTitle));
  TDC.pszWindowTitle := wTitle;
  StringToWideChar(ADescription, wDescription, SizeOf(wDescription));
  TDC.pszMainInstruction := wDescription;
  StringToWideChar(AContent, wContent, SizeOf(wContent));
  TDC.pszContent := wContent;
  case ADlgType of
    mtWarning:
      TDC.pszMainIcon := TD_ICON_WARNING;
    mtError:
      TDC.pszMainIcon := TD_ICON_ERROR;
    mtInformation:
      TDC.pszMainIcon := TD_ICON_INFORMATION;
    mtConfirmation:
      TDC.pszMainIcon := TD_ICON_QUESTION;
    mtCustom:
      TDC.pszMainIcon := TD_ICON_BLANK;
  end;
  TDC.dwCommonButtons := 0;
  BtnCnt := 0;
  for i := WORD(mbYes) to WORD(mbHelp) do
    begin
      Btn := (1 shl i);
      if (WORD(AButtons) and Btn) = Btn then
        begin
          Inc(BtnCnt);
          SetLength(Buttons, BtnCnt);
          SetLength(BtnCaptions, BtnCnt);
          Buttons[BtnCnt - 1].nButtonId := RetTable[i];
          StringToWideChar(BtnTable[i], BtnCaptions[BtnCnt - 1], SizeOf(BtnCaption));
          Buttons[BtnCnt - 1].pszButtonText := BtnCaptions[BtnCnt - 1];
        end;
    end;
  TDC.cButtons := BtnCnt;
  TDC.pButtons := @Buttons[0];
  TaskDialogIndirectProc(@TDC, @res, nilnil);
  case res of
    MD_Help:
      ; // Helpボタン押下時の処理
  else
    result := res;
  end;
  Exit;
OLDDLG:;
  // MessageDlgを表示
  Dmy := ADescription;
  if AContent <> '' then
    begin
      if Dmy <> '' then
        Dmy := Dmy + #$D#$A + #$D#$A;
      Dmy := Dmy + AContent;
    end;
  result := MessageDlg(Dmy, ADlgType, AButtons, AHelpCtx);
end;

 上記の関数を使う場合にはロケールによって自動的にボタン文字列が変わる事はないため、多言語化の際には注意が必要です。 dwCommonButtons に標準ボタンをセットすればいいような気がするでしょうが、それをやってしまうとボタンの並びが MessageDlg() と異なる事になります (実際にやってみると解ります)。

・使用許諾登録
 "Windows Vista 上で Borland Developer Stuio 2006 および Delphi 7 の使用許諾を行うには(CodeGear)" を御覧下さい。



  以上の事をあれこれ考える位なら "Delphi 2007 for Win32" を購入した方が簡単でいいかもしれません。 Delphi 2007 では TaskDialog もスマートに VCL 化されており、Alt 問題も VistaAltFixUnit のような力技ではなく、個々の VCL で最初から考慮されているので描画がスムーズです。

 BACK