MFCアプリのメッセージボックスのOKボタンをVBアプリからクリックするには?

解決


丈太郎  2012-08-20 19:54:14  No: 147750  IP: [192.*.*.*]

既存のMFCアプリ(以下Aアプリ)を新規作成のVBアプリ(以下Bアプリ)で操作(クリック等)しようとしています。
OS:WindowsXP
A,BアプリともにVS2005で開発

Aアプリはダイアログベースアプリで、エラーが発生すると
メッセージボックス「AfxMessageBox(エラーメッセージ, MB_OK)」
でエラーメッセージを表示します。

このときにBアプリからメッセージボックスのOKボタンをAPIを使って
「PostMessage(メッセージボックスのウィンドウハンドル, BM_CLICK, 0, 0)」
でクリックしに行くのですが、OKボタン自体にフォーカスは当たるのですが
クリックされませんでした。ここで、クリックされないと判断したのはメッセージボックスが消えなかったためです。

そこで、Aアプリを変更してエラーメッセージを表示するために
専用ダイアログをつくりましたが、それでも同様にOKボタンにフォーカスは当たるのですがメッセージボックスは消えませんでした。
また、OKボタンのクリックイベントにブレイクを設定しても
そこまで実行されなかったのでクリックイベント自体発生していないのではないかと思います。

更にAアプリのエラーメッセージ表示専用ダイアログを変更しました。
ダイアログに最初からあるOKボタンのCaptionを"OLD_OK"と変更し、
新しく追加したボタンのCaptionを"OK"としました。
この変更ではOKボタンのクリックイベントが発生し、
OKボタンのイベントハンドラでOnOK()を記述していたのでエラー表示ダイアログは消えました。

Aアプリに変更を加えることなく、メッセージボックスのOKボタンをクリック
する方法があれば教えていただけませんでしょうか?

以上、よろしくお願いします。

編集 削除
オショウ  2012-08-20 23:10:18  No: 147751  IP: [192.*.*.*]

正しくメッセージのエミュレーションができていないから押せていない
と言うことです。SPY++でどんなメッセージが流れているのかログを取
って、それから必要最低限のメッセージを正しく順番通りにPostMessage
すれば、ボタンをクリックしたかのように正しく動作します。

因みに、メッセージボックスのウィンドウハンドルでけだへなく、
ボタンのウィンドウハンドルでもSPY++してみて下さい。
違いがあるはずです。

以上。参考まで

編集 削除
丈太郎  2012-08-21 12:18:38  No: 147752  IP: [192.*.*.*]

オショウ様アドバイスありがとうございます。

情報に間違いがありました。
PostMessage()のウィンドウハンドルは
メッセージボックス自体のウィンドウハンドルではなく
メッセージボックスのOKボタンのウィンドウハンドルにしています。

Bアプリは変更せずに、
(a)Aアプリをメッセージボックスでエラー表示(クリックできない)
(b)AアプリをメッセージダイアログでOKボタンを新しく作成(クリックできる)
の2種類でOKボタンのログを取りました。

(a)のログ
P BM_CLICK
S WM_LBUTTONDOWN fwKeys:0000 xPos:0 yPos:0
S WM_GETDLGCODE
R WM_GETDLGCODE fuDlgCode:DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON
S WM_GETDLGCODE
R WM_GETDLGCODE fuDlgCode:DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON
S BM_SETSTYLE dwStyle:BS_DEFPUSHBUTTON | BS_TEXT | 0000 | 0000 | 0000 fRedraw:True
S WM_STYLECHANGING wStyleType:GWL_EXSTYLE | GWL_STYLE lpss:0012BE74
R WM_STYLECHANGING
S WM_STYLECHANGED wStyleType:GWL_EXSTYLE | GWL_STYLE lpss:0012BE74
R WM_STYLECHANGED
R BM_SETSTYLE
S WM_IME_SETCONTEXT fSet:1 (LONG)iShow:C000000F
S WM_IME_NOTIFY dwCommand:00000002 dwData:00000000
R WM_IME_NOTIFY
R WM_IME_SETCONTEXT
S WM_SETFOCUS hwndLoseFocus:(null)
R WM_SETFOCUS
S WM_KILLFOCUS hwndGetFocus:00040FCC
S BM_SETSTATE fState:False
R BM_SETSTATE
S WM_CAPTURECHANGED hwndNewCapture:00000000
R WM_CAPTURECHANGED
R WM_KILLFOCUS
S WM_IME_SETCONTEXT fSet:0 (LONG)iShow:C000000F
R WM_IME_SETCONTEXT
S WM_IME_SETCONTEXT fSet:1 (LONG)iShow:C000000F
R WM_IME_SETCONTEXT
S WM_SETFOCUS hwndLoseFocus:00040FCC
R WM_SETFOCUS
R WM_LBUTTONDOWN
S WM_LBUTTONUP fwKeys:0000 xPos:0 yPos:0
R WM_LBUTTONUP
P WM_PAINT hdc:00000000
S WM_ERASEBKGND hdc:060116AF
R WM_ERASEBKGND fErased:True
S WM_GETTEXTLENGTH
R WM_GETTEXTLENGTH cch:2
S WM_GETTEXT cchTextMax:6 lpszText:0012BB2C
R WM_GETTEXT cchCopied:2 lpszText:0012BB2C ("")

(b)のログ
P BM_CLICK
S WM_LBUTTONDOWN fwKeys:0000 xPos:0 yPos:0
S WM_GETDLGCODE
R WM_GETDLGCODE fuDlgCode:DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON
S WM_IME_SETCONTEXT fSet:1 (LONG)iShow:C000000F
R WM_IME_SETCONTEXT
S WM_SETFOCUS hwndLoseFocus:00010C4C
R WM_SETFOCUS
S BM_SETSTATE fState:True
R BM_SETSTATE
R WM_LBUTTONDOWN
S WM_LBUTTONUP fwKeys:0000 xPos:0 yPos:0
S BM_SETSTATE fState:False
R BM_SETSTATE
S WM_CAPTURECHANGED hwndNewCapture:00000000
R WM_CAPTURECHANGED
S WM_KILLFOCUS hwndGetFocus:00010C4A
R WM_KILLFOCUS
S WM_IME_SETCONTEXT fSet:0 (LONG)iShow:C000000F
R WM_IME_SETCONTEXT
R WM_LBUTTONUP
S WM_DESTROY
R WM_DESTROY
S WM_NCDESTROY
R WM_NCDESTROY

BアプリはSetForegroundWindow()をBM_CLICKのポスト前に呼んでいるのですが
その部分は省略しています。

違いで気になるのは
(b)でWM_LBUTTONDOWNの応答が遅いのとBM_SETSTATE fState:Trueを
発行していないことです。

これらの違いをBアプリの変更で対応できるのでしょうか?

編集 削除
オショウ  2012-08-21 13:55:38  No: 147753  IP: [192.*.*.*]

先頭にBM_CLICKからログが始まってますが・・・
それ以前は?そこが重要なんですが。

基本的には、クリックした後のメッセージなんて不要なん
です。クリックしたつもり(メッセージのエミュレーション)
でクリック動作が為されていないのは、BM_CLICK以前の
エミュレーションすべきメッセージが無いのでと思われ
ます。

それと、ボタンのウィンドウハンドルでSPY++したものと
メッセージダイアログのウィンドウハンドルでSPY++した
ものの差異も確認した方がよいかも。

※  厳密に言うとBM_CLICKの後、WM_LBUTTONUPは必要な
    はずですが、メッセージダイアログボックスが無く
    なるので、正常に動作したら解らないかも。

以上。

編集 削除
魔界の仮面弁士  2012-08-21 18:27:38  No: 147754  IP: [192.*.*.*]

> 既存のMFCアプリ(以下Aアプリ)を新規作成のVBアプリ(以下Bアプリ)で操作(クリック等)しようとしています。

AccessibleObjectFromWindow API を用いて、操作対象ボタンの
IAccessible インターフェイスを取得し、そこから
同インターフェイスの accDoDefaultAction メソッドを呼び出すことで
ボタンクリック操作を行えるかも知れません。(未検証)

編集 削除
丈太郎  2012-09-04 19:25:06  No: 147755  IP: [192.*.*.*]

オショウ様、実験が遅くなり申し訳ありません。
BM_CLICKの前のログを取得しました。

■ボタンのウィンドウハンドル
(a)Aアプリをメッセージボックスでエラー表示(クリックできない)
(b)AアプリをメッセージダイアログでOKボタンを新しく作成(クリックできる)

(a)のログ
S WM_GETTEXT cchTextMax:255 lpszText:0012BF68
R WM_GETTEXT cchCopied:2 lpszText:0012BF68 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012BF68
R WM_GETTEXT cchCopied:2 lpszText:0012BF68 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012BF68
R WM_GETTEXT cchCopied:2 lpszText:0012BF68 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012BF68
R WM_GETTEXT cchCopied:2 lpszText:0012BF68 ("")
P BM_CLICK
S WM_LBUTTONDOWN fwKeys:0000 xPos:0 yPos:0
S WM_GETDLGCODE
R WM_GETDLGCODE fuDlgCode:DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON
S WM_GETDLGCODE
R WM_GETDLGCODE fuDlgCode:DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON
S BM_SETSTYLE dwStyle:BS_DEFPUSHBUTTON | BS_TEXT | 0000 | 0000 | 0000 fRedraw:True
S WM_STYLECHANGING wStyleType:GWL_EXSTYLE | GWL_STYLE lpss:0012BE74
R WM_STYLECHANGING
S WM_STYLECHANGED wStyleType:GWL_EXSTYLE | GWL_STYLE lpss:0012BE74
R WM_STYLECHANGED
R BM_SETSTYLE
S WM_IME_SETCONTEXT fSet:1 (LONG)iShow:C000000F
S WM_IME_NOTIFY dwCommand:00000002 dwData:00000000
R WM_IME_NOTIFY
R WM_IME_SETCONTEXT
S WM_SETFOCUS hwndLoseFocus:(null)
R WM_SETFOCUS
S WM_KILLFOCUS hwndGetFocus:00110E88
S BM_SETSTATE fState:False
R BM_SETSTATE
S WM_CAPTURECHANGED hwndNewCapture:00000000
R WM_CAPTURECHANGED
R WM_KILLFOCUS
S WM_IME_SETCONTEXT fSet:0 (LONG)iShow:C000000F
R WM_IME_SETCONTEXT
S WM_IME_SETCONTEXT fSet:1 (LONG)iShow:C000000F
R WM_IME_SETCONTEXT
S WM_SETFOCUS hwndLoseFocus:00110E88
R WM_SETFOCUS
R WM_LBUTTONDOWN
S WM_LBUTTONUP fwKeys:0000 xPos:0 yPos:0
R WM_LBUTTONUP
P WM_PAINT hdc:00000000
S WM_ERASEBKGND hdc:090110B6
R WM_ERASEBKGND fErased:True
S WM_GETTEXTLENGTH
R WM_GETTEXTLENGTH cch:2

(b)のログ
S WM_GETTEXT cchTextMax:255 lpszText:0012C658
R WM_GETTEXT cchCopied:2 lpszText:0012C658 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012C658
R WM_GETTEXT cchCopied:2 lpszText:0012C658 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012C658
R WM_GETTEXT cchCopied:2 lpszText:0012C658 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012C658
R WM_GETTEXT cchCopied:2 lpszText:0012C658 ("")
S WM_GETDLGCODE
R WM_GETDLGCODE fuDlgCode:DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON
S WM_GETDLGCODE
R WM_GETDLGCODE fuDlgCode:DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON
S BM_SETSTYLE dwStyle:BS_DEFPUSHBUTTON | BS_TEXT | 0000 | 0000 | 0000 fRedraw:True
S WM_STYLECHANGING wStyleType:GWL_EXSTYLE | GWL_STYLE lpss:0012C8A8
R WM_STYLECHANGING
S WM_STYLECHANGED wStyleType:GWL_EXSTYLE | GWL_STYLE lpss:0012C8A8
R WM_STYLECHANGED
R BM_SETSTYLE
S WM_IME_SETCONTEXT fSet:1 (LONG)iShow:C000000F
S WM_IME_NOTIFY dwCommand:00000002 dwData:00000000
R WM_IME_NOTIFY
R WM_IME_SETCONTEXT
S WM_SETFOCUS hwndLoseFocus:(null)
R WM_SETFOCUS
P BM_CLICK
S WM_LBUTTONDOWN fwKeys:0000 xPos:0 yPos:0
S BM_SETSTATE fState:True
R BM_SETSTATE
R WM_LBUTTONDOWN
S WM_LBUTTONUP fwKeys:0000 xPos:0 yPos:0
S BM_SETSTATE fState:False
R BM_SETSTATE
S WM_CAPTURECHANGED hwndNewCapture:00000000
R WM_CAPTURECHANGED
S WM_KILLFOCUS hwndGetFocus:00020E16
R WM_KILLFOCUS
S WM_IME_SETCONTEXT fSet:0 (LONG)iShow:C000000F
R WM_IME_SETCONTEXT
R WM_LBUTTONUP
S WM_DESTROY
R WM_DESTROY
S WM_NCDESTROY
R WM_NCDESTROY

(b)はBM_CLICKの前にWM_SETFOCUSがあるが、
(a)はBM_CLICKの後にWM_SETFOCUSがありました。
SetForegroundWindow()を呼び出しているのでフォーカスを取れていると
思っていたのですが実際はフォーカスが取れていませんでした。

そこで、SetForegroundWindow()を呼び出した後、GetForegroundWindow()で
メッセージボックスにフォーカスが取れているかを確認(※)してから
BM_CLICKをポストするとメッセージボックスでもクリックすることが出来ました。(※取れていなければ再度SetForegroundWindow()を呼び出し)

ちなみにメッセージボックスのウィンドウハンドルは以下の通りでした。

■メッセージボックスのウィンドウハンドル
(a)のログ
00030E20 S WM_GETTEXT cchTextMax:255 lpszText:0012BF68
00030E20 R WM_GETTEXT cchCopied:7 lpszText:0012BF68 ("")
00030E20 S WM_WINDOWPOSCHANGING lpwp:0012C400
00030E20 R WM_WINDOWPOSCHANGING
00030E20 S WM_WINDOWPOSCHANGED lpwp:0012C400
00030E20 S WM_GETICON fType:True
00030E20 R WM_GETICON hicon:00000000
00030E20 S WM_GETICON fType:False
00030E20 R WM_GETICON hicon:00000000
00030E20 S WM_GETICON fType:True
00030E20 R WM_GETICON hicon:00000000
00030E20 R WM_WINDOWPOSCHANGED
00030E20 S WM_ACTIVATEAPP fActive:True dwThreadID:00000000
00030E20 R WM_ACTIVATEAPP
00030E20 S WM_NCACTIVATE fActive:True
00030E20 S WM_GETICON fType:True
00030E20 R WM_GETICON hicon:00000000
00030E20 S WM_GETICON fType:False
00030E20 R WM_GETICON hicon:00000000
00030E20 S WM_GETICON fType:True
00030E20 R WM_GETICON hicon:00000000

(b)のログ
S WM_GETTEXT cchTextMax:255 lpszText:0012C658
R WM_GETTEXT cchCopied:7 lpszText:0012C658 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012C658
R WM_GETTEXT cchCopied:7 lpszText:0012C658 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012C658
R WM_GETTEXT cchCopied:7 lpszText:0012C658 ("")
S WM_GETTEXT cchTextMax:255 lpszText:0012C658
R WM_GETTEXT cchCopied:7 lpszText:0012C658 ("")
S WM_WINDOWPOSCHANGING lpwp:0012CE80
R WM_WINDOWPOSCHANGING
S WM_WINDOWPOSCHANGED lpwp:0012CE80
S WM_GETICON fType:True
R WM_GETICON hicon:00000000
S WM_GETICON fType:False
R WM_GETICON hicon:00000000
S WM_GETICON fType:True
R WM_GETICON hicon:00000000
R WM_WINDOWPOSCHANGED
S WM_ACTIVATEAPP fActive:True dwThreadID:00001C7C
R WM_ACTIVATEAPP
S WM_NCACTIVATE fActive:True
S WM_GETICON fType:True
R WM_GETICON hicon:00000000
S WM_GETICON fType:False
R WM_GETICON hicon:00000000
S WM_GETICON fType:True
R WM_GETICON hicon:00000000

WM_ACTIVATEAPPのdwThreadIDが(a)の場合0で送っているのが問題だと思う
のですが、なぜそんな風に送っているのかはわかりませんでした。

メッセージボックスの場合はSetForegroundWindow()の1回の呼び出しで
フォアグラウンドに切り替わらないのか分からないなどありますが、
クリックできる方法は分かりましたので、解決済みとさせていただきます。
オショウ様ありがとうございました。

魔界の仮面弁士様もアドバイスありがとうございます。
おっしゃっている内容が難しいので実験できていませんが
時間があるときにトライして見ます。

編集 削除