• ベストアンサー

WinAPIで電卓をクリック

現在、WinAPIを勉強しており、練習としてVBAを用いて、電卓アプリのボタンをクリックしようとしています。 キーを送るのではなく、クリックで行いたいたいと 考えています。 ボタンのハンドルを取得するところまではできましたが、sendMessageでクリックできず、EditBoxに数字が 入りません。 どのようにすればよいのかご教授ください。 よろしくお願い致します。 環境: WinXP home、 Excel2002、Win付属アプリの電卓v5.1 ---作成したプログラム---- '標準モジュールの中身 Option Explicit Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Private Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hWnd As Long) As Long Private Const WM_LBUTTONDOWN = &H201 Private Const WM_LBUTTONUP = &H202 Sub Main() Dim lngWindWnd As Long 'ウィンドウハンドル Dim ret As Long Dim hCalc As Long 'アプリケーションタイトルより、ウィンドウハンドルを得ます lngWindWnd = FindWindow(vbNullString, "電卓") '8ボタンのハンドル(確実に取れていることを確認 hCalc = FindWindowEx(lngWindWnd, 0, "Button", "8") ret = SetForegroundWindow(lngWindWnd) ret = SendMessage(hCalc, WM_LBUTTONDOWN, 0, 0) End Sub

質問者が選んだベストアンサー

  • ベストアンサー
  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.6

>ret = SendMessage(hCalc, WM_LBUTTONDOWN, 0, 0) >ret = SendMessage(hCalc, WM_LBUTTONUP, 0, 0) > >の順に送ってみましたが,電卓ウィンドウが前面に出>てくるだけで,EditBoxに変化が起きませんでした。 まず、PostMessageにしましょう。 しなくても動くかもしれませんが、するべきです。 で、第3引数には、左ボタンをあらわすMK_LBUTTON、 第4引数は、ByVal 0とする必要があります。 #Declareステートメントで、As Anyと宣言しているため。 Win32api.txtのPostMessageの宣言をそのまま使うんだったら、ByVal lParam As Longなので、そのまま0でかまいません。 Const MK_LBUTTON = &H1 ret = PostMessage(hCalc, WM_LBUTTONDOWN, MK_LBUTTON, ByVal 0) ret = PostMessage(hCalc, WM_LBUTTONUP, MK_LBUTTON, ByVal 0)

0shiete
質問者

お礼

ご回答有難うございます。 この#6で教えていただいたとおり やってみたら、できました。 有難うございます。 自分の勉強のためにも、 他の回答もじっくり読ませていただいてから 質問を締め切ろうと思っています。 ご了承ください。

その他の回答 (10)

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.11

っていうかよく読むと、、、 「VBA」 でしたね。。。今気づきました。。。 下の長文サンプル無視で、GetDlgCtrlIDを使用するってことだけ読み取ってください。 (taka_tetsuさん。またよろしくです。) (とーどーあにぃも、またよろしくです。) また忙しくなるのでロムる予感。

0shiete
質問者

お礼

ご回答有難うございます。 >下の長文サンプル無視で、GetDlgCtrlIDを使用するってことだけ読み取ってください。 VBAでコントロール配列を実現しなきゃいけないと 思ってました(^^; GetDlgCtrlIDを使用するとよいのですね。 了解しました。

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.10

wParamの上位と下位、逆でしたね(^^;;

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.9

>WM_COMMANDにしてるのはコマンドボタンです。 >VBがやってるわけではありません。 言葉が足りなかったようですね^^; 私はこの説明を受けたとき、VB製のコントロールを含め、VBと捉えております。 それと、補足ありがとうございます。 以前の書き込みの時は、VB製のアプリ同士の連携しかしたことがなかったので、スキル不足でした。^^; それとコントロールIDは下位の方だと思うのですが・・・ いかがでしょう?

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.8

>「VBがWM_LBUTTONDOWNとWM_LBUTTONUPを認識して、内部でWM_COMMANDを発行してCLICKイベントが発生するので、WM_COMMANDでクリックを直接呼べばよい」 WM_COMMANDにしてるのはコマンドボタンです。 VBがやってるわけではありません。 >VBはコントロールIDで管理していないので、コントロールIDが常に「0」です。 >んでもって定数:BN_CLICKEDは「0」です。 今回は電卓がターゲットなので、VBアプリではありません。コントロールIDは持っているはずです。 #XPで8のボタンは&H84でした。 なので、 >SendMessageの行を >Call SendMessage(lngWindWnd, WM_COMMAND, BN_CLICKED, ByVal hCalc) ということなんで、wParamの指定の仕方が違いますね。 コントロールIDをGetDlgCtrlIDで取得して、上位16ビットにセットする必要があります。 Call SendMessage(lngWindWnd, WM_COMMAND, BN_CLICKED + GetDlgCtrlID(lngWindWnd) * &H10000, ByVal hCalc) ですね。 >WM_COMMANDはボタンクリックのロジックを、直でたたき呼びます。 ということがおわかりでしたら、親ウィンドウでの処理の振り分けに何を使っているかがTAGOSAKU7さんなら想像つきますよね。 コントロールIDとウィンドウハンドルしかどのボタンから来たメッセージか判断する方法がないんですから。

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.7

PostMessage SendMessage について触れられているので、ついでに発言。。。 WM_LBUTTONDOWN WM_LBUTTONUP の順で送るなら、確かにPostMessageかと思います。 「マウスで押したことにする」という命令だから、その他のグラフィカルな部分にも影響するだろうし。。。 WM_COMMANDで送るならどちらでもよいかと。。。 私の場合は、割り込みをさせないためにWM_COMMANDの時はSendMessageを多用しております。 WM_COMMANDはボタンクリックのロジックを、直でたたき呼びます。 Spy++で見ると一目瞭然です。 それと今回は電卓なので、どちらでもよいかと思うのですが。。。 勝手にtaka_tetsuさんの発言に対しての補足させていただきます。。。 (決して悪意はありません。私の質問にあたなは答えてくれたことがあります。) 私も以前、マウスダウンとマウスアップで、他のアプリケーションを制御しようとしていました。 しかしVBでコマンドボタンのある画面を作り、そのコマンドボタンをtaka_tetsuさんと同様の方式を取ったところ、2回に1回だけ成功するというような状況に陥りました。 個人で登録しているメーリングリストに状況を質問をすると、 「VBがWM_LBUTTONDOWNとWM_LBUTTONUPを認識して、内部でWM_COMMANDを発行してCLICKイベントが発生するので、WM_COMMANDでクリックを直接呼べばよい」 とのレスを戴きました。

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.5

えーとですね。。。 VCとかで画面を作ったらわかるのですが、コントロール一つ一つにコントロールIDというのがあります。 VBはコントロールIDで管理していないので、コントロールIDが常に「0」です。 んでもって定数:BN_CLICKEDは「0」です。 本当はコントロールIDをSnedMessageの第3引数に渡します。 SendDlgItemMessage関数を利用する手もあります。(内部ではSendMessageで同じことをしています。) でわ ※構成 Project1  └Form1   └Command1 ← 「Index = 0」にして、コントロール配列にしてください   ※以下サンプル Option Explicit Private plngCalWnd As Long Private Const DEF_CALC_CAP As String = "電卓" Private Const WM_COMMAND As Long = &H111 Private Const BN_CLICKED As Long = &H0& Private Type RECT   Left  As Long   Top   As Long   Right  As Long   Bottom As Long End Type Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As Any, ByVal cch As Long) As Long Private Declare Function GetDlgCtrlID Lib "user32" (ByVal hwnd As Long) As Long Private Declare Function MoveWindow Lib "user32" (ByVal hwnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Private Sub Command1_Click(Index As Integer)   Dim varWk    As Variant   Dim lngBtnWnd  As Long   Dim lngDlgCtlID As Long      varWk = Split(Command1(Index).Tag, vbTab)   lngBtnWnd = varWk(0)   lngDlgCtlID = varWk(1)   Call SendMessage(plngCalWnd, WM_COMMAND, lngDlgCtlID, lngBtnWnd) End Sub Private Sub Form_Load()   '電卓取得   If Not GetCalc(plngCalWnd) Then     MsgBox "電卓起動失敗"     End   End If      '画面初期設定   Call InitForm      '電卓前面   Call AppActivate(DEF_CALC_CAP) End Sub '画面初期設定 Private Sub InitForm()   Dim rectCalc  As RECT   Dim lngWnd   As Long      '基本のボタンを見せない   Me.Command1(0).Visible = False      '電卓のサイズを取得   Call GetWindowRect(plngCalWnd, rectCalc)      '自分自身を電卓と同じサイズに変更   With rectCalc     Call MoveWindow(Me.hwnd, 0, 0, (.Right - .Left), (.Bottom - .Top), 1)          Do       'ボタンハンドルを取得       lngWnd = FindWindowEx(plngCalWnd, lngWnd, "Button", vbNullString)       '取得できないなら抜ける       If (lngWnd = 0) Then         Exit Do       End If              '電卓と同じボタンを作成       Call CreSameBtn(lngWnd, .Left, .Top + 23)     Loop   End With End Sub '指定ハンドルと同じキャプションのボタンを作成 Private Sub CreSameBtn(ByVal inBtnWnd As Long, ByVal inVectH As Long, ByVal inVectV As Long)   Const DEF_BUF_SIZE As Long = &HFF      Dim rectBtn   As RECT   Dim lngBtnIndex As Long   Dim btnWk    As CommandButton      Dim lngDlgCtlID As Long      Dim lngLen   As Long   Dim strCap   As String   Dim bytBuf(DEF_BUF_SIZE - 1) As Byte      '新たなコマンドボタンを作成   lngBtnIndex = Command1.UBound + 1   Load Command1(lngBtnIndex)   Set btnWk = Command1(lngBtnIndex)   btnWk.Visible = True   btnWk.TabStop = False      'コントロールIDを取得   lngDlgCtlID = GetDlgCtrlID(inBtnWnd)      'ハンドルとコントロールIDをボタンのタグに保存   btnWk.Tag = inBtnWnd & vbTab & lngDlgCtlID      'ボタンのキャプションを取得   lngLen = GetWindowText(inBtnWnd, ByVal VarPtr(bytBuf(0)), DEF_BUF_SIZE)   strCap = LeftByte(StrConv(bytBuf, vbUnicode), lngLen)   btnWk.Caption = strCap      'ボタンのサイズを取得   Call GetWindowRect(inBtnWnd, rectBtn)   With rectBtn     Call MoveWindow(btnWk.hwnd, (.Left - inVectH), (.Top - inVectV), (.Right - .Left), (.Bottom - .Top), 1)   End With End Sub '電卓取得 Private Function GetCalc(Optional otCalcWnd As Long) As Boolean   On Error Resume Next   otCalcWnd = FindWindow(vbNullString, DEF_CALC_CAP)   If otCalcWnd = 0 Then     Call Shell("Calc.exe")     otCalcWnd = FindWindow(vbNullString, DEF_CALC_CAP)   End If   GetCalc = (otCalcWnd <> 0&) End Function 'LEFT for バイト長 Private Function LeftByte(inValue, ByVal inStart As Long) As String   LeftByte = StrConv(LeftB$(StrConv(inValue, vbFromUnicode), inStart), vbUnicode) End Function

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.4

#3 でできます。

0shiete
質問者

補足

早速のご回答ありがとうございます。 また、返信が遅れて申し訳ありません。 #3の方の補足に書かせていただいたのですが、 電卓ウィンドウが前面に出てくるところまでは よいのですが、EditBoxに変化が起きません。 また、ご教授頂ければ幸いです。

  • todo36
  • ベストアンサー率58% (728/1234)
回答No.3

BN_CLICKEDで出来るかも。

参考URL:
http://oshiete1.goo.ne.jp/kotaeru.php3?q=199357
0shiete
質問者

補足

早速のご回答ありがとうございます。 また、返信が遅れて申し訳ありません。 教えていただいた参考URLを参考にやってみましたが、 電卓ウィンドウは前面にでてくるのですが、EditBox は「0」のままで「8」が入ってくれません。 また、ご教授頂ければ幸いです。 ---変更した点--- Private Const WM_COMMAND = &H111 Private Const BN_CLICKED = &H0& を追加し、 SendMessageの行を Call SendMessage(lngWindWnd, WM_COMMAND, BN_CLICKED, ByVal hCalc) に変更しました。

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.2

まず、マウスメッセージはPostMessageで送りましょう。 次に、WM_LBUTTONDOWNのあとに、WM_LBUTTONUPを送る必要があります。

  • gatyan
  • ベストアンサー率41% (160/385)
回答No.1

マウスのボタンを押して離した時点でクリックと認識されるのでは? DOWN , UP の順でメッセージを送ってみました?

0shiete
質問者

補足

早速のご回答ありがとうございます。 ret = SendMessage(hCalc, WM_LBUTTONDOWN, 0, 0) ret = SendMessage(hCalc, WM_LBUTTONUP, 0, 0) の順に送ってみましたが,電卓ウィンドウが前面に出てくるだけで,EditBoxに変化が起きませんでした。 また、ご教授いただければ幸いです。