いつもお世話になっております。
早速ですが質問です。
あるアプリケーションにてバグを発見し、解析にあたっていたところ
Delphiのバグではないか?という疑問を持ち始めてきました。
下記のものはDelphiのバグなのでしょうか?
検索サイト、過去ログを検索しましたが発見できなかったため質問させていただきます。
よろしくお願いします。
◇あるアプリケーションのバグ◇
Edit1のExitイベントに自作のメッセージダイアログにてYES、NOを聞くという処理が書かれています。
そこでEdit1にフォーカスがある状態でEdit2をマウスでクリックします。
Edit1のExitイベントが走り、ダイアログのボタンを押し、ダイアログを消します。
フォーカスはEdit2に入っていますが、キーボードを押下してもなにも文字が入力できなくなってしまいます。
もう一度フォーム上のどこかをクリックすると入力できるようになります。
まとめると
①元々フォーカスが入っているコンポーネント(以下A)からフォーカスをセットしたいコンポーネント(以下B)をマウスにてカーソル移動をさせる
②AのExitイベントのメッセージ表示
③Bにフォーカス
④Bが入力不可
とういうバグ?が発生してしまいます。Delphiのバグでなければ更に解析をしたいのですが、Delphiのバグなら諦めて回避処理を組もうと思っております。駄文で申し訳ありません。よろしくお願いします。
これでない?
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
>>orz様
レスありがとうございます。
有名なバグなのですね。これでやっとすっきりしました。
貼って頂いたリンクを見て対応を考えて行きたいと思います。
迅速なレスありがとうございました。
> 有名なバグなのですね。
バグじゃないですよ。フォーカスが切り替わるタイミングでフォーカスを奪う
モーダルダイアログを出すのはおかしい、ってことです。
>>えーと様レスありがとうございます。
>バグじゃないですよ。フォーカスが切り替わるタイミングでフォーカスを奪う
>モーダルダイアログを出すのはおかしい、ってことです。
出すのはおかしいということはフォーカスが切り替わるタイミングにダイアログを表示させようとする開発者がおかしいという意味なのでしょうか?
もしそうであっても、ソフトの仕様上仕方がない事なのですよね。
今後の開発、Delphiの勉強のためにもレス頂けたらありがたく思います。
よろしくお願いします。
http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=034753
ここで中村氏が述べているように
---
原因は フォーカスが移動するときにモーダルフォームを表示すると、
フォームが Disable されるので コントロールが WM_LBUTTONUP を
受け取れなくなり マウスのキャプチャが外れなくなる ということだったと
思います。
対処は OnExit や OnEnter から一呼吸おいて処理することです。
OnExit や OnEnter の中で PostMessage でメッセージを投げて それを
処理すればうまく行きます。5708 に対処例が載っています。
#フォーカスが移動するイベントの中で さらにフォーカスを移動させる
#処理をするのはやめましょう。トラブルの元です。
---
がすべてです。バグである、とはどっかに書いてありましたっけ?
PostMessage() でルーチンを抜けてから、受け取ったハンドラで同じ処理を
するといいのですよね。デフォルトでマウスキャプチャをはずさないように
なっているVCLのバグである、という見方も出来るかもしれませんね。
これは、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;
あ、これもな。
上のソースの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;
ツイート | ![]() |