いつもお世話になります。1点質問させてください。
メインのフォームにModalなダイアログが出ているかどうかを判定
したいのですが,どのようにすれば判定できるでしょうか。
(ダイアログには,MessageDlg,Application.MessageBox,OpenDialog,
ユーザー定義のダイアログ等,いろいろなものがあります。)
※1 今は暫定措置として,メインのフォームをアクティブにして,
IsChild(Handle, GetFocus)
で判定していますが,メインのフォームをアクティブにしなくても
判定可能な方法を探しています。
※2 環境はDelphi6 Personal とWinXPです。
訂正です。以下の部分を次のとおり直します。失礼いたしました。
※1 今は暫定措置として,メインのフォームをアクティブにして,
↓
※1 今は暫定措置として,Application.Handleをアクティブにして,
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も不可だろう。
Application.OnModalBegin
Application.OnModalEnd
このベントが使えるんじゃないかな。
Formsの中にあるFindTopMostWindowをカスタマイズしたらできない?
MessageBoxとかも含めて見つかるはず
自アプリなら、グローバルな整数変数をひとつ用意して
Modalなダイアログを
表示するたびに +1
消すたびに -1
すれば今何個表示されてるかまで、超省エネで確認取れる気がしますが。
一々検索するよりCPUにやさしいですよ。
みなさん,ご回答どうもありがとうございます。解決はまだですが,
複数の方からの回答をいただいたことで希望が持ててきました。
いただいたキーワードを元に,今取れる対応を検討中ですが,回答
文の中で少しわからないことがあります。可能であればお教えいただ
けますでしょうか。
①OnModalBegin について
ヘルプでOnModalBeginというイベントを探そうとしましたが,
TApplicationのイベント一覧には,OnModalBeginというものが掲載
されていませんでした。
また,IDEで「Application.」と入力すると,コード補完ウィンドウ
が出てきますが,その中にもこのイベントはないようでした。
これはDelphi6でも使用可能なのでしょうか。
(明確に記述した文献はGoogleではヒットしませんでしたが,もしか
したらバージョンの違いで使えないのでは?と考えています。)
②FindTopMostWindowについて
今,ヘルプでこの関数を引いてみましたが,うまくヒットしません
でした。Googleでも,どのような関数で,どのように改造すると
よいのかがよくわかりませんでした。(中国語?らしきサイトはた
くさんヒットしたのですが,私の目的にそった内容を書いているか
どうかはわからなかったのです。)
ヒントをいただいたところ,大変恐縮ではあるのですが,より細か
なヒントやサンプル等をご提示いただくことは可能でしょうか。
(名前からして,目的に合いそうな関数なので,もう少し調べて
みたいと思います。)
1,2は6perには無い。
再度のご回答,どうもありがとうございます。
Delphi 6 Personalには,上の関数はなかったのですね。せっ
かくお教えいただいたのに,使えないのは残念ですが,これで
「他の方法を探さなくてはならない」とはっきりわかったので
踏ん切りがつきました。
※ 先ほどから,
http://www.geocities.jp/asumaroyuumaro/program/winapi/window.html
のページ等も参考に,いろいろ組み合わせて試しているところ
です。まだ見込みは立っていませんが,もしうまくいったら再
度書き込みしてみたいと思います。
こんな感じ?
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のケースとかもあるかもしれないので
確認、カスタマイズして見てください。。マルチタスクとか?
ちなみにこの処理は実行ファイルか
シングルスレッドモデルのモジュールでしか成立しません。。
var
dlg: TForm;
begin
dlg := CreateMessageDialog('適当なメッセージ', mtCustom, [mbOK]);
dlg.FormStyle := fsStayOnTop;
dlg.ShowModal;
dlg.Free;
end;
FormStyle = fsStayOnTop のフォームを、ShowModalしちまうとアウトだ。
みふ。様,形而上様,細かいサンプルまで出していただいて
本当にありがとうございます。
今拝見したばかりですので,これから中に含まれているキー
ワードを吟味しつつ,望む結果が得られるかを確認していきた
いと思います。
※形而上様の書いてくださったコードは,みふ。様の関数の例
外事例ということでしょうか。これもこれから試してみたい
と思います。
まだ完璧ではありませんが,ご提示いただいたコードの意味を解釈し,実際に
サンプルプログラムに組み込んで使ってみました。参考までに,以下に
現時点での結果を示します。
まず,ベースとしては,みふ。様のご提示くださったコードを使用しま
した。ですが,そのまま使用した場合,以下のような問題が発生しまし
た。
・例外が発生した時に表示されるダイアログボックスが出ても,
「出てない」が表示されてしまう。
・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;
もにゃさんに一票。
自分のコードでモーダルダイアログを出すのに、それをわざわざ検証するって
ことに何の意味があるのかわかりません。
ご意見ありがとうございます。
それで,いただいた意見のとおり,最初は確かにそのようなことも考えて
いたのですが,例えばプログラムに,自分で把握しきれなかったバグ等があ
り,意図しないところでエラーダイアログが出てしまった場合などは,
+1,−1するタイミング把握が非常に難しいのではないかと考えていました。
そのため,必要なタイミングで,そのときの状態を確実に判定できる
ロジックがほしいと思い,ご相談させていただいたところです。
でしたら、入力許可状態(WS_DISABLED属性)を単純にチェックするだけで
いけるはずです。
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if IsWindowEnabled(Handle) then
Caption:='enable'
else
Caption:='no';
end;
さらにVCL対策でForm1.Enabledも必要に応じてチェックするのはどうでしょ?タイマーの発動はWS_DISABLED属性が加わったときにonしてください。
もにゃ様,先日に続いて再度の投稿ありがとうございます。いまチェッ
クしたところ,うまくいきそうです。これから動作をいろいろとチェック
して,解決としたいと思います。(数日間,ずっと考えていた問題でした
ので,単純明快な答えに行き着けてよかったです。本当にありがとう
ございました。)
それで,恐縮なのですが,もう1点だけお教えいただけないでしょうか。
ご指摘いただいた後段の部分の「VCL対策」というところは,単に下のよう
なことを指すという理解でよろしいでしょうか。
・Form1のEnabledプロパティがFalse (使用不可) である場合,
モーダルダイアログの有無にかかわらず,
IsWindowEnabled(Handle) = False
となるため,両方をチェックしておいた方がよい。
書きなぐりだったのでろくにチェックしないで投稿してしまいました。
Form1のEnabledプロパティはWS_DISABLEDを変更してますね。
ですので、チェックは二重になるため必要ありません。
私が気にかけてたのは、WS_DISABLEDを変更しないで入力不可状態っぽい振る舞いをするフォームの対策です。
例外があったらご一報ください。
実際の使用方法に沿った形で,いろいろと操作して試していたのですが,最
終的にもにゃ様のご提示くださった案で目的を達成したことを確認しました。
そのため,チェックマークをつけさせていただきます。皆様,本当にありがとうございました。
ツイート | ![]() |