TMS SoftwareのDelphiとWindows Vista関連の記事

投稿者: : Hitoshi Fujii

概要: ボーランドテクノロジーパートナー(BTP)の TMS Softwareによる、Delphi 2006開発者がどのようにWindows Vista対応のアプリケーションを開発できるかについて説明した記事です。


ボーランドテクノロジーパートナー(BTP)のTMS Software が、Delphi 2006の開発者が、現在、どのようにWindows Vista Readyアプリケーションを開発することができるかを解説した 一連の記事 を掲載しています。原文では、以下の記事を紹介していますが、著者の了解のもと、翻訳記事についてはBDNに掲載しました。オリジナル記事ならびに関連情報については、リンク先を参照してください。

Using the new Windows Vista TaskDialog from Delphi 2006
The new File Open / Save dialogs in Windows Vista
Taking the new Windows Vista TaskDialog one step further


    Delphi 2006からWindows VistaのTask Dialogを使う

Windows Vistaは、ユーザーとの対話のための新しいダイアログTaskDialogを導入しました。最もシンプルなフォームでは、TaskDialogは、タイトルのカスタマイズや説明や内容を追加できるオプションを持った、より見た目がいいMessageBox相当のダイアログだといえます。

Windows Vista以前のダイアログ:

Hide image
Click to see full-sized image

上記に相当するWindows Vistaの基本的なTaskDialog:Hide image

基本的なTaskDialogのほかにも、Windows Vistaは、さまざまなダイアログ拡張機能を提供します。複数の選択肢に対するセレクタ、確認用チェックボックス、オプションで追加情報を隠したり、プログレスバーを表示したり。これらの拡張機能は、次の記事で説明します。

    Delphiから基本的なTaskDialog機能を使う

DelphiからTaskDialogを使用する方法は単純です。Windows Vistaは、新しいCOMCTL32.DLL v6 の Win32 APIコールとして TaskDialog を利用可能にしています。しかし、気をつけなければならないのは、Windows Vistaは、依然としてCOMCTL32.DLL v5をも提供しており、これがDelphiのアプリケーションが使用するデフォルトライブラリになっていることです。COMCTL32.DLL v6を使用し、TaskDialog APIを呼び出すには、あなたのアプリケーションであなたのアプリケーションの中に、マニフェストのTXPManifestか、TMS TWinXPコンポーネントがあることを確認してください。

TaskDialog API関数は、Windows Vista SDKで以下のように宣言されています。

HRESULT TaskDialog(HWND hWndParent,
    HINSTANCE hInstance,
    PCWSTR pszWindowTitle,
    PCWSTR pszMainInstruction,
    PCWSTR pszContent,
    TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons,
    PCWSTR pszIcon,
    int *pnButton
);

Delphiでは、次の関数に変換します。

 TaskDialog: function(HWND: THandle;
    hInstance: THandle;
    cTitle, cDescription, cContent: pwidechar;
    Buttons: Integer;
    Icon: integer;
    ResButton: pinteger): integer; 

新しいTaskDialogの欠点は、Windows Vista上でしか動かないことです。以前のバージョンのWindows OSは、まったくサポートしていません。そこで、Windows Vista上で基本的なTaskDialogを使う関数を作成することにします。これは、旧バージョンのOS上では、標準のMessageDlgを代替として使います。この方法により、あなたのアプリケーションは、最新のWindows Vistaのユーザーインターフェイスを用いつつ、旧バージョンのWindowsでも動作させることができます。新しいTaskDialogメソッドを含む完全なコードを以下に示します。また、もうひとつのTaskMessage関数は、DelphiのShowMessageのリプレースとして用意しました。

const
TD_ICON_BLANK = 100;
TD_ICON_WARNING = 101;
TD_ICON_QUESTION = 102;
TD_ICON_ERROR = 103;
TD_ICON_INFORMATION = 104;
TD_ICON_BLANK_AGAIN = 105;
TD_ICON_SHIELD = 106;

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;

function TaskDialog(AForm: TCustomForm; ATitle, ADescription, AContent: string; Buttons,Icon: integer): integer;
var
  VerInfo: TOSVersioninfo;
  DLLHandle: THandle;
  res: integer;
  wTitle,wDescription,wContent: array[0..1024] of widechar;
  Btns: TMsgDlgButtons;
  DlgType: TMsgDlgType;
  TaskDialogProc: function(HWND: THandle; hInstance: THandle; cTitle, cDescription, cContent: pwidechar;
       Buttons: Integer; Icon: integer; ResButton: pinteger): integer; cdecl stdcall;

begin
  Result := 0;

  VerInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  GetVersionEx(verinfo);

  if (verinfo.dwMajorVersion >= 6) then
  begin
    DLLHandle := LoadLibrary('comctl32.dll');
    if DLLHandle >= 32 then
    begin
      @TaskDialogProc := GetProcAddress(DLLHandle,'TaskDialog');
 
      if Assigned(TaskDialogProc) then
      begin
        StringToWideChar(ATitle, wTitle, sizeof(wTitle));
        StringToWideChar(ADescription, wDescription, sizeof(wDescription));
        StringToWideChar(AContent, wContent, sizeof(wContent));
        TaskDialogProc(AForm.Handle, 0, wTitle, wDescription, wContent, Buttons,Icon,@res);

        Result := mrOK;

        case res of
        DLGRES_CANCEL : Result := mrCancel;
        DLGRES_RETRY : Result := mrRetry;
        DLGRES_YES : Result := mrYes;
        DLGRES_NO : Result := mrNo;
        DLGRES_CLOSE : Result := mrAbort;
        end;
      end;
      FreeLibrary(DLLHandle);
    end;
  end
  else
  begin
    Btns := [];
    if Buttons and TD_OK = TD_OK then
      Btns := Btns + [MBOK];
  
    if Buttons and TD_YES = TD_YES then
      Btns := Btns + [MBYES];

    if Buttons and TD_NO = TD_NO then
      Btns := Btns + [MBNO];

    if Buttons and TD_CANCEL = TD_CANCEL then
      Btns := Btns + [MBCANCEL];

    if Buttons and TD_RETRY = TD_RETRY then
      Btns := Btns + [MBRETRY];

    if Buttons and TD_CLOSE = TD_CLOSE then
      Btns := Btns + [MBABORT];

    DlgType := mtCustom;

    case Icon of
    TD_ICON_WARNING : DlgType := mtWarning;
    TD_ICON_QUESTION : DlgType := mtConfirmation;
    TD_ICON_ERROR : DlgType := mtError;
    TD_ICON_INFORMATION: DlgType := mtInformation;
    end;

    Result := MessageDlg(AContent, DlgType, Btns, 0);
  end;
end;

procedure TaskMessage(AForm: TCustomForm; AMessage: string);
begin
  TaskDialog(AForm, '', '', AMessage, TD_OK, 0);
end;

以下のサンプルコードは、新しいTaskDialogとTaskMessage関数を使用する例です。

 if TaskDialog(self, 'Hello world','Ready to enjoy the new Vista task dialog ?',
   'The new Vista task dialog presents an easy to use and user-friendly replacement for messageboxes.', 
   TD_YES + TD_NO, TD_ICON_QUESTION) = mrYes then
     TaskMessage(self,'yes');

これらの基本関数によって、簡単にあなたのDelphiアプリケーションをVista Readyに近づけることができます。以降の記事で、Delphi開発者が利用可能なその他のWindows Vistaの機能及びTaskDialogの拡張機能について説明します。

    Windows Vistaの新しいFile Open / Save Dialogを使う

Windows Vistaでは、ファイルオープンと保存のコモンダイアログも、同様にその外観が変更されています。残念ながら、マイクロソフトは、この新しいコモンダイアログを、既存のGetOpenFileName、GetSaveFileName API呼び出しと、100%の後方互換性を保ちませんでした。OFN_ENABLEHOOKまたはOFN_ENABLETEMPLATEフラグが使われるやいなや、その機能は古い形態の表示に戻ってしまいます。残念なことに、Delphiは、デフォルトで(ダイアログをセンタリングするために)OFN_ENABLEHOOKを使用し、オプションでOFN_ENABLETEMPLATEも使います。このことは、TOpenDialogやTSaveDialogを使って新しいWindows Vistaスタイルのダイアログを利用できないことを意味します。Windows Vista上のDelphiアプリケーションでTOpenDialogを使うと、次のダイアログが表示されます。

Hide image

Windows Vistaの新しいダイアログは、次のようなスタイルです。

Hide image
Click to see full-sized image

そのため、Windows Vistaの新しいファイルオープン/保存ダイアログを、今Delphiから使おうとするには、VCLを変更する(これはボーランドが、将来のDelphiバージョンで行うと思います)か、代替関数を用いる必要があります。ここでは、Windows Vistaの新しい素敵なダイアログを使えるように、TOpenDialogとTSaveDialogの即席代替関数をお見せします。

function OpenSaveFileDialog(Parent: TWinControl; const DefExt, Filter, InitialDir, Title: string;
  var FileName: string; MustExist, OverwritePrompt, NoChangeDir, DoOpen: Boolean): Boolean;
var
  ofn: TOpenFileName;
  szFile: array[0..MAX_PATH] of Char;
begin
  Result := False;
  FillChar(ofn, SizeOf(TOpenFileName), 0);
  with ofn do
  begin
    lStructSize := SizeOf(TOpenFileName);
    hwndOwner := Parent.Handle;
    lpstrFile := szFile;
    nMaxFile := SizeOf(szFile);
    if (Title <> '') then
      lpstrTitle := PChar(Title);
    if (InitialDir <> '') then
      lpstrInitialDir := PChar(InitialDir);
    StrPCopy(lpstrFile, FileName);
    lpstrFilter := PChar(ReplaceStr(Filter, '|', #0)+#0#0);
    if DefExt <> '' then
      lpstrDefExt := PChar(DefExt);
  end;

  if MustExist then
    ofn.Flags := ofn.Flags or OFN_FILEMUSTEXIST;

  if OverwritePrompt then
    ofn.Flags := ofn.Flags or OFN_OVERWRITEPROMPT;

  if NoChangeDir then
    ofn.Flags := ofn.Flags or OFN_NOCHANGEDIR;

  if DoOpen then
  begin
    if GetOpenFileName(ofn) then
    begin
      Result := True;
      FileName := StrPas(szFile);
    end;
  end
  else
  begin
    if GetSaveFileName(ofn) then
    begin
      Result := True;
      FileName := StrPas(szFile);
    end;
  end
end;

    Windows Vistaの新しいTaskDialogをもっと使いこなす

先の記事(article 5)では、DelphiからVistaの新しいTaskDialogを使う基本APIを紹介しました。しかし、この基本APIは、従来のMessageBoxよりやや改良されたぐらいのことしかできません。新しいTaskDialogのより多くの機能を引き出すために、Vistaは単純なTaskDialogのバージョンよりもより多くの機能を利用できるTaskDialogIndirect APIを公開しています。TaskDialogIndirectの使用はやや複雑ですが、使用が簡単なTTaskDialogというDelphiコンポーネントにこれらすべてをカプセル化し、新機能をプロパティとイベントで利用できるようにしました。新しいTTaskDialogのソースコードは公開しているので、興味のある人はこれを見て学習することもできるでしょう。ソースファイルとパッケージは、Delphi 2006でビルドされテストしています。この記事は、このコンポーネントによって、Delphiからどのようにダイアログの新機能を使用するかを説明します。

以下のサンプルは、TTaskDialogをフォーム上にドロップして、プロパティを設定し、Executeを呼び出すことで使用できます。

    単純なダイアログから

このコードは、タイトルテキストと説明、内容を含むダイアログを表示する例です。

TaskDialog1.Title := 'Simple Vista TaskDialog';
TaskDialog1.Instruction := 'Starting to explore the new TaskDialog here';
TaskDialog1.Content := 'A simple text only TaskDialog';
TaskDialog1.CommonButtons := [cbOK];
TaskDialog1.Execute;

Hide image

    カスタムボタンつきダイアログ

最初の例では、コモンボタンのOKを選択しました。CustomButtonsプロパティを用いれば、ボタンに対して独自のテキストを設定できます。コモンボタンの場合、TaskDialog.Executeは共通のWindows値のOk、Cancel、Yes、Noなどを返しますが、最初のカスタムボタンは100、2番目は101のように値を返します。

TaskDialog1.Title := 'TaskDialog with custom buttons';
TaskDialog1.Icon := tiQuestion;
TaskDialog1.CustomButtons.Clear;
TaskDialog1.CustomButtons.Add('Save');
TaskDialog1.CustomButtons.Add('Don''t Save');
TaskDialog1.DefaultButton := 101;
TaskDialog1.Instruction := 'Save file to disk ?';
TaskDialog1.Content := 'If you do not save changes, these will be lost';
ShowMessage(inttostr(TaskDialog1.Execute));

Hide image

    コマンドボタンつきダイアログ

選択可能なアクションを際立たせるために、ボタンを以下の画面ショットのようなCommandButtonに変えることができます。コードは、前のサンプルに非常に似ています。TaskDialog.Optionsに、doCommandLinksの設定を追加するだけです。

TaskDialog1.Title := 'TaskDialog with command buttons';
TaskDialog1.Icon := tiWarning;
TaskDialog1.CustomButtons.Clear;
TaskDialog1.CustomButtons.Add('Exit application without saving');
TaskDialog1.CustomButtons.Add('Exit application with saving');
TaskDialog1.DefaultButton := 100;
TaskDialog1.Options := [doCommandLinks];
TaskDialog1.Execute;

Hide image

    拡張可能な領域、フッターテキスト、ハイパーリンクを持つTaskDialog

ダイアログをもっとクリアで簡潔にするために、オプションで、エキスパートユーザーが見るような詳細テキストを隠すことができます。この詳細テキストは、ExpandedTextプロパティに設定します。このプロパティに空でない文字列が設定されると、展開可能な矢印ボタンを押すとダイアログに表示されるようになります。同じように、ExpandControlText、CollapsControlTextプロパティによって、展開ボタンと折りたたみボタンのデフォルトテキストをオーバーライドすることができます。

TaskDialogテキスト中のハイパーリンクを有効にするには、TaskDialog.Optionsで、doHyperlinksをtrueに設定します。ハイパーリンクがクリックされると、イベントOnDialogHyperlinkClickが呼び出されます。

TaskDialog1.Options := [doHyperlinks];
TaskDialog1.Title := 'TaskDialog with expandable text & footer with hyperlink';
TaskDialog1.Instruction := 'Do you like the Windows Vista TaskDialog?';
TaskDialog1.Icon := tiQuestion;
TaskDialog1.Content := 'The new TaskDialog provides a standard & enhanced way for interacting with the user';
TaskDialog1.ExpandedText := 'Many new options make the TaskDialog very different from the old Windows MessageBox';
TaskDialog1.ExpandControlText := 'Click to hide';
TaskDialog1.CollapsControlText := 'Click to see more';
TaskDialog1.Footer := 'Brought to Delphi by <A href="http://www.tmssoftware.com">TMS software</A>';
TaskDialog1.FooterIcon := tfiWarning;
TaskDialog1.Execute;

Hide image

Hide image

    ラジオボタン、確認用チェックボックスを持つTaskDialog

TaskDialogには、ユーザーが項目を選択できるように、一連のラジオボタンを含めることができます。さらに、今後このダイアログを表示するかどうかを選択する、典型的なチェックボックスも追加できます。ラジオボタンの選択は、TaskDialog.RadioResultで取得できます。最初のラジオボタンは200、2番目は201のような結果が得られます。確認用チェックボックスの選択結果は、TaskDialog.VerifyResultで取得できます。

TaskDialog1.Title := 'TaskDialog with radiobutton & verification text';
TaskDialog1.RadioButtons.Clear;
TaskDialog1.RadioButtons.Add('Store settings in registry');
TaskDialog1.RadioButtons.Add('Store settings in XML file');
TaskDialog1.VerificationText := 'Do not ask for this setting next time';
TaskDialog1.Instruction := 'Saving application settings';
TaskDialog1.Execute;
case TaskDialog1.RadioButtonResult of
200: ShowMessage('Store in registry');
201: ShowMessage('Store in XML');
end;

if TaskDialog1.VerifyResult then
  ShowMessage('Do not ask for this setting next time');

Hide image

    プログレスバーつきTaskDialog

最後に、新しいTaskDialogは、進捗状況を表示することもできます。TaskDialogでプログレスバーを有効にするには、TaskDialog.OptionsのdoProgressBarをtrueに設定します。イベントTaskDialog.OnDialogProgressで、プログレスバーの位置を要求されます。デフォルトで、TaskDialogのプログレスバーの位置は、0から100の間にありますが、ProgressBarMinプロパティとProgressBarMaxプロパティによって、他の値に変更できます。以下のサンプルでは、プログレスバーつきTaskDialogと、プログレスバーを更新するシンプルなイベントハンドラコードをセットアップする方法を示しています。また、プログレスバーが100%のときにTaskDialog.ClickButton()を呼び出すことで、処理が完了したときに、自動的にダイアログが閉じられるようにします。

progresspos := 0;

TaskDialog1.Title := 'TaskDialog with progress bar';
TaskDialog1.Instruction := 'Downloading file from the Internet';
TaskDialog1.Options := [doProgressBar];
TaskDialog1.CommonButtons := [cbCancel];
TaskDialog1.OnDialogProgress := DoDialogProgress;
TaskDialog1.Execute;
procedure TForm1.DoDialogProgress(Sender: TObject; var Pos: Integer; var State: TTaskDialogProgressState);
begin
  if (progresspos < 100) then
  begin
    (Sender as TTaskDialog).EnableButton(1,false);
    inc(progresspos,2);
  end
  else
  begin
    (Sender as TTaskDialog).EnableButton(1,true);
    (Sender as TTaskDialog).ClickButton(1);
  end;
  Pos := progresspos;
  State := psNormal;
end;

Hide image

TTaskDialogのソースコードはこちらからダウンロードできます。

Server Response from: SC1