Exitイベントにメッセージ表示後のバグ?

解決


キリン  2007-03-06 02:53:29  No: 25149

いつもお世話になっております。
早速ですが質問です。

あるアプリケーションにてバグを発見し、解析にあたっていたところ
Delphiのバグではないか?という疑問を持ち始めてきました。
下記のものはDelphiのバグなのでしょうか?
検索サイト、過去ログを検索しましたが発見できなかったため質問させていただきます。
よろしくお願いします。

◇あるアプリケーションのバグ◇
Edit1のExitイベントに自作のメッセージダイアログにてYES、NOを聞くという処理が書かれています。
そこでEdit1にフォーカスがある状態でEdit2をマウスでクリックします。
Edit1のExitイベントが走り、ダイアログのボタンを押し、ダイアログを消します。
フォーカスはEdit2に入っていますが、キーボードを押下してもなにも文字が入力できなくなってしまいます。
もう一度フォーム上のどこかをクリックすると入力できるようになります。

まとめると
①元々フォーカスが入っているコンポーネント(以下A)からフォーカスをセットしたいコンポーネント(以下B)をマウスにてカーソル移動をさせる
②AのExitイベントのメッセージ表示
③Bにフォーカス
④Bが入力不可

とういうバグ?が発生してしまいます。Delphiのバグでなければ更に解析をしたいのですが、Delphiのバグなら諦めて回避処理を組もうと思っております。駄文で申し訳ありません。よろしくお願いします。


orz  2007-03-06 03:29:31  No: 25150

これでない?
http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=073218

関連記事検索
http://leed.issp.u-tokyo.ac.jp/~takeuchi/delphi/nsearch.cgi?key=OnExit+ShowMessage


キリン  2007-03-06 19:52:49  No: 25151

>>orz様
レスありがとうございます。

有名なバグなのですね。これでやっとすっきりしました。
貼って頂いたリンクを見て対応を考えて行きたいと思います。
迅速なレスありがとうございました。


えーと  2007-03-06 20:38:57  No: 25152

> 有名なバグなのですね。

バグじゃないですよ。フォーカスが切り替わるタイミングでフォーカスを奪う
モーダルダイアログを出すのはおかしい、ってことです。


キリン  2007-03-07 20:47:30  No: 25153

>>えーと様レスありがとうございます。

>バグじゃないですよ。フォーカスが切り替わるタイミングでフォーカスを奪う
>モーダルダイアログを出すのはおかしい、ってことです。

出すのはおかしいということはフォーカスが切り替わるタイミングにダイアログを表示させようとする開発者がおかしいという意味なのでしょうか?

もしそうであっても、ソフトの仕様上仕方がない事なのですよね。
今後の開発、Delphiの勉強のためにもレス頂けたらありがたく思います。

よろしくお願いします。


えーと  2007-03-08 00:57:25  No: 25154

http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=034753

ここで中村氏が述べているように

---

原因は  フォーカスが移動するときにモーダルフォームを表示すると、
フォームが Disable されるので  コントロールが WM_LBUTTONUP を
受け取れなくなり  マウスのキャプチャが外れなくなる  ということだったと
思います。

対処は OnExit や OnEnter から一呼吸おいて処理することです。
OnExit や OnEnter の中で PostMessage でメッセージを投げて  それを
処理すればうまく行きます。5708 に対処例が載っています。

#フォーカスが移動するイベントの中で  さらにフォーカスを移動させる
#処理をするのはやめましょう。トラブルの元です。
---

がすべてです。バグである、とはどっかに書いてありましたっけ?

PostMessage() でルーチンを抜けてから、受け取ったハンドラで同じ処理を
するといいのですよね。デフォルトでマウスキャプチャをはずさないように
なっているVCLのバグである、という見方も出来るかもしれませんね。


aaa  2007-03-08 04:28:42  No: 25155

これは、DELPHI に限らず、
VB6,VC#,VB.NET でも起こります。

回避方法は、メッセージボックス関数を自作すること。
(CreateMessageDlg を使えばアホほど簡単に出来る)
ShowModal 出なく、Show しているとこがポイント。
戻り待ちの処理はShowModal と同じですが、
親フォームをdisableにするところが少し違います。

TForm の ShowModal は プログラム内に存在するすべてのフォームを
Disableにしますが、↓はメッセージ表示時にActiveだったフォームのみ
Disableにします。

Function MsgBox(Const Msg : string; Const Title : String ;DlgType: TMsgDlgType;
  Buttons : TMsgDlgButtons;DefButton : TMsgDlgBtn) : Integer;
Const
  BTNNAMES : Array [0..10] Of String = (
      'Yes',
      'No',
      'OK',
      'Cancel',
      'Abort',
      'Ignore',
      'Retry',
      'All',
      'NoToAll',
      'YesToAll',
      'Help'
  );

Var
  DForm : TMsgBox;
  DStrCaption : String;
  i : Integer;
  DBtn : TButton;
  ActiveWindow: HWnd;
Begin

  DForm := TMsgBox(CreateMessageDialog(Msg,DlgType,Buttons));
  DForm.Caption := Title;

  Case DefButton Of
    mbYes    : Begin
      DStrCaption := 'Yes';
    End;
    mbNo     : Begin
      DStrCaption := 'No';
    End;
    mbOK     : Begin
      DStrCaption := 'OK';
    End;
    mbCancel : Begin
      DStrCaption := 'Cancel';
    End;
    mbAbort  : Begin
      DStrCaption := 'Abort';
    End;
    mbIgnore : Begin
      DStrCaption := 'Ignore';
    End;
    mbRetry : Begin
      DStrCaption := 'Retry';
    End;
    mbAll      : Begin
      DStrCaption := 'All';
    End;
    mbNoToAll  : Begin
      DStrCaption := 'NoToAll';
    End;
    mbYesToAll : Begin
      DStrCaption := 'YesToAll';
    End;
    mbHelp : Begin
      DStrCaption := 'Help';
    End;
  End;

  DForm.ActiveControl := TWinControl(DForm.FindComponent(DStrCaption));
  DForm.BorderStyle := bsDialog;
  DForm.BorderIcons := [];

  For i := 0 To 10 Do
  Begin
    DBtn := TButton(DForm.FindComponent(BTNNAMES[i]));
    IF Assigned(DBtn) Then
    Begin
      DBtn.OnClick := DForm.BtnClick;
    End;
  End;

  ActiveWindow := GetActiveWindow;

  DForm.Show;

  IF ActiveWindow <> 0 Then
  Begin
    EnableWindow(ActiveWindow,False);
  end;

  while DForm.Visible Do
  Begin
    Application.HandleMessage;
  End;

  IF ActiveWindow <> 0 Then
  Begin
    EnableWindow(ActiveWindow,True);
  End;

  Result := DForm.ModalResult;
  DForm.Free;

End;


aaa  2007-03-08 04:31:05  No: 25156

あ、これもな。
上のソースのDForm変数の型(TMsgBox)
は以下のように定義します。

Type
  TMsgBox = Class(TForm)
  Public
    Procedure BtnClick(Sender : TObject);
End;

Procedure TMsgBox.BtnClick(Sender : TObject);
Begin
  SendMessage(Handle,WM_CLOSE,0,0);
End;


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

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






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