ダイアログが出ていることを確認するには?

解決


まこと  2007-07-17 09:59:40  No: 27079

いつもお世話になります。1点質問させてください。
  メインのフォームにModalなダイアログが出ているかどうかを判定
したいのですが,どのようにすれば判定できるでしょうか。
(ダイアログには,MessageDlg,Application.MessageBox,OpenDialog,
ユーザー定義のダイアログ等,いろいろなものがあります。)

※1  今は暫定措置として,メインのフォームをアクティブにして,
    IsChild(Handle, GetFocus)
  で判定していますが,メインのフォームをアクティブにしなくても
  判定可能な方法を探しています。

※2  環境はDelphi6 Personal とWinXPです。


まこと  2007-07-17 10:02:05  No: 27080

訂正です。以下の部分を次のとおり直します。失礼いたしました。

※1  今は暫定措置として,メインのフォームをアクティブにして,
          ↓
※1  今は暫定措置として,Application.Handleをアクティブにして,


半人前  2007-07-17 19:55:56  No: 27081

ShowModal 限定・・・

uses
  Forms;

function ExistModalForm: Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := 0 to Screen.FormCount -1 do begin
    if fsModal in Screen.Forms[i].FormState then begin
      Result := True;
      Break;
    end
  end;
end;

TApplication.MessageBox は、不可。
OpenDialogも不可だろう。


風来坊  2007-07-17 22:22:46  No: 27082

Application.OnModalBegin
Application.OnModalEnd
このベントが使えるんじゃないかな。


みふ。  2007-07-18 01:10:05  No: 27083

Formsの中にあるFindTopMostWindowをカスタマイズしたらできない?
MessageBoxとかも含めて見つかるはず


もにゃ  2007-07-18 03:15:31  No: 27084

自アプリなら、グローバルな整数変数をひとつ用意して
Modalなダイアログを
  表示するたびに +1
  消すたびに -1
すれば今何個表示されてるかまで、超省エネで確認取れる気がしますが。
一々検索するよりCPUにやさしいですよ。


まこと  2007-07-18 04:10:50  No: 27085

みなさん,ご回答どうもありがとうございます。解決はまだですが,
複数の方からの回答をいただいたことで希望が持ててきました。

  いただいたキーワードを元に,今取れる対応を検討中ですが,回答
文の中で少しわからないことがあります。可能であればお教えいただ
けますでしょうか。

①OnModalBegin について
  ヘルプでOnModalBeginというイベントを探そうとしましたが,
  TApplicationのイベント一覧には,OnModalBeginというものが掲載
  されていませんでした。
  また,IDEで「Application.」と入力すると,コード補完ウィンドウ
  が出てきますが,その中にもこのイベントはないようでした。
  これはDelphi6でも使用可能なのでしょうか。
  (明確に記述した文献はGoogleではヒットしませんでしたが,もしか
  したらバージョンの違いで使えないのでは?と考えています。)

②FindTopMostWindowについて
  今,ヘルプでこの関数を引いてみましたが,うまくヒットしません
  でした。Googleでも,どのような関数で,どのように改造すると
  よいのかがよくわかりませんでした。(中国語?らしきサイトはた
  くさんヒットしたのですが,私の目的にそった内容を書いているか
  どうかはわからなかったのです。)
  ヒントをいただいたところ,大変恐縮ではあるのですが,より細か
  なヒントやサンプル等をご提示いただくことは可能でしょうか。
  (名前からして,目的に合いそうな関数なので,もう少し調べて
  みたいと思います。)


  2007-07-18 06:47:30  No: 27086

1,2は6perには無い。


まこと  2007-07-18 06:59:58  No: 27087

再度のご回答,どうもありがとうございます。

  Delphi 6 Personalには,上の関数はなかったのですね。せっ
かくお教えいただいたのに,使えないのは残念ですが,これで
「他の方法を探さなくてはならない」とはっきりわかったので
踏ん切りがつきました。

※  先ほどから,
http://www.geocities.jp/asumaroyuumaro/program/winapi/window.html
のページ等も参考に,いろいろ組み合わせて試しているところ
です。まだ見込みは立っていませんが,もしうまくいったら再
度書き込みしてみたいと思います。


みふ。  2007-07-18 20:09:29  No: 27088

こんな感じ?

var
  TopMostWindow: HWnd = 0;

function DoFindWindow(Window: HWnd; Param: Longint): Boolean; stdcall;
begin
  if (Window <> Application.Handle) and
    IsWindowVisible(Window) and
    IsWindowEnabled(Window) and
    (GetWindowLong(Window, GWL_EXSTYLE) and WS_EX_TOPMOST  = 0) then
    TopMostWindow := Window;
  result := True;
end;

function IsShowModal(MainWindow: HWnd): Boolean;
begin
  result := False;
  TopMostWindow := 0;
  if not IsWindowEnabled(MainWindow) then
    EnumThreadWindows(GetCurrentThreadID, @DoFindWindow, 0);
  if TopMostWindow <> 0 then
    result := Getwindow(TopMostWindow,GW_HWNDNEXT) = MainWindow;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if IsShowModal(Handle) then
    Caption := '出てる'
  else
    Caption := '出てない';
end;

見落としてるZorderのケースとかもあるかもしれないので
確認、カスタマイズして見てください。。マルチタスクとか?
ちなみにこの処理は実行ファイルか
シングルスレッドモデルのモジュールでしか成立しません。。


形而上  2007-07-18 23:07:41  No: 27089

var
  dlg: TForm;
begin
  dlg := CreateMessageDialog('適当なメッセージ', mtCustom, [mbOK]);
  dlg.FormStyle := fsStayOnTop;
  dlg.ShowModal;
  dlg.Free;
end;

FormStyle = fsStayOnTop のフォームを、ShowModalしちまうとアウトだ。


まこと  2007-07-19 06:35:37  No: 27090

みふ。様,形而上様,細かいサンプルまで出していただいて
本当にありがとうございます。

  今拝見したばかりですので,これから中に含まれているキー
ワードを吟味しつつ,望む結果が得られるかを確認していきた
いと思います。

※形而上様の書いてくださったコードは,みふ。様の関数の例
  外事例ということでしょうか。これもこれから試してみたい
  と思います。


まこと  2007-07-21 08:43:07  No: 27091

まだ完璧ではありませんが,ご提示いただいたコードの意味を解釈し,実際に
サンプルプログラムに組み込んで使ってみました。参考までに,以下に
現時点での結果を示します。

まず,ベースとしては,みふ。様のご提示くださったコードを使用しま
した。ですが,そのまま使用した場合,以下のような問題が発生しまし
た。

・例外が発生した時に表示されるダイアログボックスが出ても,
  「出てない」が表示されてしまう。
・TOpenDialog等で,ダイアログボックスからさらにダイアログが表示
  された場合(「指定されたファイルは存在しません」等のメッセージ
  です。)にも,「出てない」が表示される。

  今作成しているプログラムは,ただ1つのフォームしか持ちませんの
で,モーダルなダイアログが1つでも出ているときは,メインフォーム
にモーダルなダイアログが出ているということになります。そのため,
以下のようにコードをカスタマイズしました。コードは今書いて,一
通り動かしてみただけなので,さらに十分な検証を終えた時点で,「解決」
としたいと思います。

※ただ,このような分野を扱ったのは初めてですので,もしどなたか
  お気づきの点がありましたら,ご指摘いただくと本当に助かります。

// 判定用関数本体。Unit1に記述して使用した。

var
  TopMostWindow: HWnd = 0;

function DoFindWindow(Window: HWnd; Param: Longint): Boolean; stdcall;
begin
  if (Window <> Application.Handle) and
    IsWindowVisible(Window) and
    // IsWindowEnabled(Window) and
    // ↑これがあると,OpenDialog1のFileMustExistsの関係でエラーメッセージ
    // が出たとき,まれに 「モーダルダイアログ無し」が返ってきてしまうので,コメントアウト。
    (GetWindowLong(Window, GWL_EXSTYLE) and WS_EX_TOPMOST = 0) then
    TopMostWindow := Window;
  Result := True;
end;

function IsShowModal(MainWindow: HWnd): Boolean;
begin
  Result := False;
  TopMostWindow := 0;
  if not IsWindowEnabled(MainWindow) then
    EnumThreadWindows(GetCurrentThreadID, @DoFindWindow, 0);
  Result := TopMostWindow <> 0;
  // ↑最前面表示のウィンドウが1つでもあったらモーダル有りと判定。
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Listbox1.Items.Clear;
  if IsShowModal(Handle) then
    Caption := 'モーダルダイアログ有り'
  else
    Caption := 'モーダルダイアログ無し';
end;

// 以下は単に検証に用いたコード。参考までに提示しておきます。1通り,結果は良好でした。

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Form2はNormalとStayOnTopで検証。→どちらも正しく「モーダルダイアログ有り」を返した。
  Form2.ShowModal;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  A: Integer;
begin
  // わざとエラーを起こしてみる。
  A := 0;
  ShowMessage(IntToStr(1 div A));
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  ShowMessage('');
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  MessageDlg('', mtError, [mbOK], 0);
end;

procedure TForm1.Button5Click(Sender: TObject);
begin
  // OpenDialogはOptionsにFileMustExistsを設定して,エラーダイアログを出して様子を見る。
  if OpenDialog1.Execute then;
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  A: String;
begin
  // 開発時によく使用する文字列入力ダイアログ。
  A := '1';
  InputQuery(A, A, A);
end;

procedure TForm1.Button7Click(Sender: TObject);
begin
  Application.MessageBox('', '', 0);
end;


えーと  2007-07-21 14:05:51  No: 27092

もにゃさんに一票。

自分のコードでモーダルダイアログを出すのに、それをわざわざ検証するって
ことに何の意味があるのかわかりません。


まこと  2007-07-21 17:04:06  No: 27093

ご意見ありがとうございます。
  それで,いただいた意見のとおり,最初は確かにそのようなことも考えて
いたのですが,例えばプログラムに,自分で把握しきれなかったバグ等があ
り,意図しないところでエラーダイアログが出てしまった場合などは,
+1,−1するタイミング把握が非常に難しいのではないかと考えていました。
  そのため,必要なタイミングで,そのときの状態を確実に判定できる
ロジックがほしいと思い,ご相談させていただいたところです。


もにゃ  2007-07-21 18:56:55  No: 27094

でしたら、入力許可状態(WS_DISABLED属性)を単純にチェックするだけで
いけるはずです。
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if IsWindowEnabled(Handle) then
  Caption:='enable'
  else
  Caption:='no';
end;

さらにVCL対策でForm1.Enabledも必要に応じてチェックするのはどうでしょ?タイマーの発動はWS_DISABLED属性が加わったときにonしてください。


まこと  2007-07-21 19:49:20  No: 27095

もにゃ様,先日に続いて再度の投稿ありがとうございます。いまチェッ
クしたところ,うまくいきそうです。これから動作をいろいろとチェック
して,解決としたいと思います。(数日間,ずっと考えていた問題でした
ので,単純明快な答えに行き着けてよかったです。本当にありがとう
ございました。)

  それで,恐縮なのですが,もう1点だけお教えいただけないでしょうか。
ご指摘いただいた後段の部分の「VCL対策」というところは,単に下のよう
なことを指すという理解でよろしいでしょうか。

  ・Form1のEnabledプロパティがFalse (使用不可) である場合,
    モーダルダイアログの有無にかかわらず,

      IsWindowEnabled(Handle) = False  

    となるため,両方をチェックしておいた方がよい。


もにゃ  2007-07-21 23:52:12  No: 27096

書きなぐりだったのでろくにチェックしないで投稿してしまいました。
Form1のEnabledプロパティはWS_DISABLEDを変更してますね。
ですので、チェックは二重になるため必要ありません。
私が気にかけてたのは、WS_DISABLEDを変更しないで入力不可状態っぽい振る舞いをするフォームの対策です。
例外があったらご一報ください。


まこと  2007-07-27 08:05:23  No: 27097

実際の使用方法に沿った形で,いろいろと操作して試していたのですが,最
終的にもにゃ様のご提示くださった案で目的を達成したことを確認しました。
  そのため,チェックマークをつけさせていただきます。皆様,本当にありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加