[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『SendMessageでダイアログにファイルパスを送りたい』(VBA勉強中)
ローカルアプリをVBAで操作できるようにしたいです。
WndowsAPIを使用して、ローカルアプリを操作しています。
ローカルアプリ内のダイアログにテキストファイルAのフルパスをSendMessageで送ろうとしていますが、
下記のコードを実行してもフルパスを送る事ができません。
Private Const WM_SETTEXT As String = &HC
戻り値=SendMessage(エディタのハンドル, WM_SETTEXT, 0, フルパス) ←nullが送られる。戻り値=1
戻り値=SendMessage(エディタのハンドル, WM_SETTEXT, 0, Byval フルパス) ←反応なし。戻り値=0
エディタのハンドル、フルパス共にdebug.printで確認しても問題はなさそうです。
原因・解決方法をご教授いただきたいです。
< 使用 Excel:Microsoft365、使用 OS:Windows10 >
勘ですけど、 > エディタのハンドル
ってのが > ローカルアプリ内のダイアログ を正確に指せてないのではないかなぁと。(ローカルアプリ自体のハンドルだったりとかね)
例えば[メモ帳]の編集領域に文字列を送り付けようと思ったら、 [メモ帳]のハンドルじゃなくって、その配下の[Edit]クラスの子ウインドウを指定しないとダメですよね? (さもないと編集領域に文字が入らず、[メモ帳]のタイトルバーが書き変わってしまったりとか)
そこら辺は大丈夫ですかね? (こればっかりはご自身で調べて確認するしかないですから...)
命令文の方は Declare で第4引数[lParam]を「(ByVal無し) As Any」で宣言しているなら > 戻り値=SendMessage(エディタのハンドル, WM_SETTEXT, 0, ByVal フルパス) ↑こっちの方が正しいっぽいです。 ByVal付けなかったら指定通りの文字列が届かない模様。
(白茶) 2023/06/07(水) 19:16:07
>命令文の方は
ありがとうございます。
Byvalなしで宣言しているので、ByVal フルパス ということですね。
(VBA勉強中) 2023/06/08(木) 10:09:27
そうなんですね
まあ、私でお力になれる事はなさそうなのは承知の上で、 とは言え、せっかくだしちょっとだけ「にぎやかし」のつもりで、^^; 私が愛用してるエディタ(TeraPad)で再現実験を行ってみました。
TeraPad公式サイト
https://tera-net.com/
▼こんなモジュールを準備しておく
Rem ************************************************************************************************************************ Rem 標準モジュール Rem ************************************************************************************************************************ Option Explicit Private Type POINTAPI X As Long Y As Long End Type Private Declare PtrSafe Sub Sleep Lib "KERNEL32.dll" (ByVal dwMilliseconds As Long) Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long #If Win64 Then Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal Point As LongLong) As LongPtr #Else Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As LongPtr #End If Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long Private Declare PtrSafe Function EnumChildWindows Lib "user32" (ByVal hWndParent As LongPtr, ByVal lpEnumFunc As LongPtr, ByVal lParam As Collection) As Long 'ByVal lParam As LongPtr) As Long Private Declare PtrSafe Function GetAsyncKeyState Lib "user32.dll" (ByVal vkey As Long) As Long Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr Rem Window Messages Private Const WM_SETTEXT = &HC& Rem Edit Control Messages Private Const EM_REPLACESEL = &HC2& Rem ================================================================================================================= Private aCurPos As POINTAPI, sw As Boolean Rem ================================================================================================================= #If Win64 Then Private Function PointToLongLong(Point As POINTAPI) As LongLong Dim ll As LongLong Dim cbLongLong As LongPtr cbLongLong = LenB(ll) If LenB(Point) = cbLongLong Then CopyMemory ll, Point, cbLongLong End If PointToLongLong = ll End Function #End If Rem ================================================================================================================= Property Get PointWindow() As LongPtr #If Win64 Then PointWindow = WindowFromPoint(PointToLongLong(aCurPos)) #Else PointWindow = WindowFromPoint(aCurPos.X, aCurPos.Y) #End If End Property Private Sub Observer() Dim h As LongPtr, s As String [A1].Value = "監視中..." [B1:D1].Value = [{"hWnd","WindowText","ClassName"}] [A2].Value = "PointWindow" Do While sw GetCursorPos aCurPos h = PointWindow [B2] = h s = String$(&H20, vbNullChar) GetWindowText h, s, &H20 [C2] = s s = String$(&H20, vbNullChar) GetClassName h, s, &H20 [D2] = s If GetAsyncKeyState(vbKeyEscape) <> 0 Then sw = False DoEvents Sleep &H10& Loop [A1].Value = Empty End Sub Rem ================================================================================================================= Private Function EnumChildProc(ByVal hwnd As LongPtr, ByVal lParam As Collection) As Boolean lParam.Add hwnd EnumChildProc = True End Function Sub GetChildWindows(TargethWnd As LongPtr) Dim c As Collection Set c = New Collection Call EnumChildWindows(TargethWnd, AddressOf EnumChildProc, c) If c.Count = 0 Then Exit Sub Dim i As Long, s As String, t As String ReDim v(-1 To c.Count, 0 To 3) v(-1, 1) = "hWnd" v(-1, 2) = "WindowText" v(-1, 3) = "ClassName" v(0, 0) = "PointWindow" v(0, 1) = TargethWnd s = String$(&H20, vbNullChar) t = s GetClassName TargethWnd, s, &H20 GetWindowText TargethWnd, t, &H20 v(0, 2) = t v(0, 3) = s For i = 1 To c.Count s = String$(&H20, vbNullChar) t = s GetClassName c(i), s, &H20 GetWindowText c(i), t, &H20 v(i, 0) = i v(i, 1) = c(i) v(i, 2) = t v(i, 3) = s Next Workbooks.Add [A1:D3].Resize(c.Count + 2).Value = v End Sub Rem ================================================================================================================= Sub Btn1_Click() sw = Not sw If sw Then Call Observer End Sub Sub Btn2_Click() If [B2].Value = 0 Then Exit Sub GetChildWindows CLngPtr([B2].Value) End Sub Sub Btn3_Click() Debug.Print SendMessage(CLngPtr(ActiveCell.Value), EM_REPLACESEL, 0&, ByVal "フルパス" & vbCrLf) End Sub Rem =================================================================================================================
▼[Btn1_Click]を実行し「TeraPad」を起動してマウスを「TeraPad」のタイトルバーにポイントして[Esc]を押す ↓こんな感じでTeraPadのhWndをゲット
[___]|_____A_____|___B___|_______C________|________D________| [ 1]| |hWnd |WindowText |ClassName | [ 2]|PointWindow| 133134|無題 * - TeraPad|TTeraPadMainForm |
▼[Btn2_Click]を実行して「TeraPad」の子ウインドウを列挙してみる↓
[___]|_____A_____|____B____|_______________C_______________|_________D_________| [ 1]| |hWnd |WindowText |ClassName | [ 2]|PointWindow| 133134|無題 * - TeraPad |TTeraPadMainForm | [ 3]| 1| 5770786|*:\********\TeraPad\usr\******\|TTeraPadIniFilePath| [ 4]| 2| 1117756|*:\********\TeraPad\TeraPad.exe|TTeraPadExeName | [ 5]| 3| 12586572| |TEdit | [ 6]| 4| 13042662| |TToolBar | [ 7]| 5| 3608138| |TEditor | [ 8]| 6| 986324| |TStatusBar |
▼ClassName「TEditor」が編集領域である事は[Btn1_Click]の実行中にも目視できた事だし、 ターゲットとなるハンドルは[B7]セルの値で間違いなさそうだ。
▼[B7]セルをアクティブにして[Btn3_Click]を実行してみると TeraPadに文字列「フルパス」が届いた (実行する度に文字列「フルパス」が届く事も確認できた)
...ていう感じでした。
(参考文献) エディットメッセージの確認 https://www.tokovalue.jp/EditMessage_U.htm [VBA]広域変数を使用せずに、EnumChildWindowsの結果を取得する - Qiita https://qiita.com/nukie_53/items/d4a803080d4c1f3ab35d
(白茶) 2023/06/08(木) 15:58:29
よくよく見返してみたら > ローカルアプリ内のダイアログ ダイアログかぁ... それでメンドイ事になってるのかなぁ...
...などと妄想を膨らませ中。
それに実際にはマウスでポイントしてハンドル探す訳でもないし... ダイアログのルートハンドルから、その下にある目的のEdit系コントロールまでの辿り方を探す必要があるって事ですよね。
てな訳で、引き続きTeraPadを題材にして追加実験してみました。 TeraPadの「オプション」ダイアログの中にある「デフォルトフォルダ」を指定するEditのハンドルを取ってみる。
▼こんなモジュールを準備しておく(改)
Rem ************************************************************************************************************************ Rem 標準モジュール Rem ************************************************************************************************************************ Option Explicit Private Type POINTAPI x As Long Y As Long End Type Private Declare PtrSafe Sub Sleep Lib "KERNEL32.dll" (ByVal dwMilliseconds As Long) Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long #If Win64 Then Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal Point As LongLong) As LongPtr #Else Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As LongPtr #End If Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long Private Declare PtrSafe Function EnumChildWindows Lib "user32" (ByVal hWndParent As LongPtr, ByVal lpEnumFunc As LongPtr, ByVal lParam As Collection) As Long 'ByVal lParam As LongPtr) As Long Private Declare PtrSafe Function GetAsyncKeyState Lib "user32.dll" (ByVal vkey As Long) As Long Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr Private Declare Function GetAncestor Lib "user32" (ByVal hWnd As LongPtr, ByVal gaFlags As Long) As LongPtr Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr Rem Window Messages Private Const WM_SETTEXT = &HC& Rem Edit Control Messages Private Const EM_REPLACESEL = &HC2& Private Const GA_PARENT As Long = &H1 '直接の親ウィンドウ Private Const GA_ROOT As Long = &H2 '親をたどっていったルート・ウィンドウ Private Const GA_ROOTOWNER As Long = &H3 'オーナー・ウィンドウのルート・ウィンドウ Rem ================================================================================================================= Private aCurPos As POINTAPI, sw As Boolean Rem ================================================================================================================= #If Win64 Then Private Function PointToLongLong(Point As POINTAPI) As LongLong Dim ll As LongLong Dim cbLongLong As LongPtr cbLongLong = LenB(ll) If LenB(Point) = cbLongLong Then CopyMemory ll, Point, cbLongLong End If PointToLongLong = ll End Function #End If Rem ================================================================================================================= Property Get PointWindow() As LongPtr #If Win64 Then PointWindow = WindowFromPoint(PointToLongLong(aCurPos)) #Else PointWindow = WindowFromPoint(aCurPos.x, aCurPos.Y) #End If End Property Private Sub Observer() Dim buff As String buff = String$(&H20, vbNullChar) Dim h As LongPtr, s As String [A1].Value = "監視中..." [B1:D1].Value = [{"hWnd","WindowText","ClassName"}] [A2].Value = "PointWindow" Do While sw GetCursorPos aCurPos h = PointWindow [B2] = h s = buff GetWindowText h, s, &H20 [C2] = s s = buff GetClassName h, s, &H20 [D2] = s If GetAsyncKeyState(vbKeyEscape) <> 0 Then sw = False DoEvents Sleep &H10& Loop [A1].Value = Empty End Sub Rem ================================================================================================================= Private Function EnumChildProc(ByVal hWnd As LongPtr, ByVal lParam As Collection) As Boolean lParam.Add hWnd EnumChildProc = True End Function Sub GetChildWindows(TargethWnd As LongPtr) Dim c As Collection Set c = New Collection Call EnumChildWindows(TargethWnd, AddressOf EnumChildProc, c) If c.Count = 0 Then Exit Sub Dim buff As String buff = String$(&H20, vbNullChar) Dim i As Long, s As String, t As String ReDim v(-1 To c.Count, 0 To 6) v(-1, 1) = "hWnd" v(-1, 2) = "WindowText" v(-1, 3) = "ClassName" v(-1, 4) = "Parent" v(-1, 5) = "Root" v(-1, 6) = "RootOwner" v(0, 0) = "PointWindow" v(0, 1) = TargethWnd s = buff t = s GetClassName TargethWnd, s, &H20 GetWindowText TargethWnd, t, &H20 v(0, 2) = t v(0, 3) = s v(0, 4) = GetAncestor(TargethWnd, GA_PARENT) v(0, 5) = GetAncestor(TargethWnd, GA_ROOT) v(0, 6) = GetAncestor(TargethWnd, GA_ROOTOWNER) For i = 1 To c.Count s = buff t = s GetClassName c(i), s, &H20 GetWindowText c(i), t, &H20 v(i, 0) = i v(i, 1) = c(i) v(i, 2) = t v(i, 3) = s v(i, 4) = GetAncestor(c(i), GA_PARENT) v(i, 5) = GetAncestor(c(i), GA_ROOT) v(i, 6) = GetAncestor(c(i), GA_ROOTOWNER) Next Workbooks.Add With [A1:G3].Resize(c.Count + 2) .Columns("C:D").NumberFormat = "@" .Value = v .EntireColumn.AutoFit End With End Sub Rem ================================================================================================================= Sub Btn1_Click() sw = Not sw If sw Then Call Observer End Sub Sub Btn2_Click() If [B2].Value = 0 Then Exit Sub GetChildWindows CLngPtr([B2].Value) End Sub Sub Btn3_Click() Debug.Print SendMessage(CLngPtr(ActiveCell.Value), EM_REPLACESEL, 0&, ByVal "フルパス" & vbCrLf) End Sub Rem =================================================================================================================
▼[Btn1_Click]を実行し「TeraPad」を起動して「オプション」ダイアログを出しておく。 マウスで「オプション」ダイアログのタイトルバーをつかんだ状態で[Esc]を押す。(ポイントだとダイアログ閉じちゃう...[Esc]で orz) ↓こんな感じでダイアログのhWndをゲット
[___]|_____A_____|___B____|____C_____|_____D______| [ 1]| |hWnd |WindowText|ClassName | [ 2]|PointWindow| 1181560|オプション|TOptionForm |
▼[Btn2_Click]を実行してダイアログの子ウインドウを列挙してみる↓
[___]|_____A_____|___B____|_______________C_______________|_______D________|___E____|___F____|____G_____| [ 1]| |hWnd |WindowText |ClassName |Parent |Root |RootOwner | [ 2]|PointWindow| 1181560|オプション |TOptionForm | 65552| 1181560| 2623750| [ 3]| 1| 2361656| |TPanel | 1181560| 1181560| 2623750| [ 4]| 2| 4852022|一時反映(&N) |TButton | 2361656| 1181560| 2623750| [ 5]| 3| 1181532|OK |TButton | 2361656| 1181560| 2623750| [ 6]| 4| 1902216|キャンセル |TButton | 2361656| 1181560| 2623750| [ 7]| 5| 1247186| |TPanel | 1181560| 1181560| 2623750| [ 8]| 6| 1116006| |TPageControl | 1247186| 1181560| 2623750| [ 9]| 7| 1312786|フォルダ |TXPTabSheet | 1116006| 1181560| 2623750| [ 10]| 8| 1181932|デフォルトフォルダ |TXPGroupBox | 1312786| 1181560| 2623750| [ 11]| 9| 1050568|... |TButton | 1181932| 1181560| 2623750| [ 12]| 10| 3082460|D:\Documents |TEdit | 1181932| 1181560| 2623750| [ 13]| 11| 2296066|デフォルトフォルダを指定する(&D|TCheckBox | 1181932| 1181560| 2623750| [ 14]| 12| 1181568|文字コード |TXPTabSheet | 1116006| 1181560| 2623750| [ 15]| 13| 5770290|文字コードの設定 |TXPGroupBox | 1181568| 1181560| 2623750| [ ] (以下省略)
▼[Btn4_Click]を実行して[TEdit]のハンドルまで辿ってみる (FindWindowExは直接の子ウインドウしか見つけられないから、素直に上から順に辿ってってみた)
Sub Btn4_Click() Dim h As LongPtr, x As LongPtr h = CLngPtr(ActiveCell.Worksheet.[B2].Value) '<---------------------- h:= 1181560 If h Then x = FindWindowEx(h, 0&, "TPanel", vbNullString) '<--------- x:= 2361656 If h Then h = FindWindowEx(h, x, "TPanel", vbNullString) '<---------- h:= 1247186 If h Then h = FindWindowEx(h, 0&, "TPageControl", vbNullString) '<--- h:= 1116006 If h Then h = FindWindowEx(h, 0&, "TXPTabSheet", vbNullString) '<---- h:= 1312786 If h Then h = FindWindowEx(h, 0&, "TXPGroupBox", vbNullString) '<---- h:= 1181932 If h Then h = FindWindowEx(h, 0&, "TEdit", vbNullString) '<---------- h:= 3082460 Debug.Print h End Sub
なんとか出来ました。
まあ、VBA勉強中さんのローカルアプリが、同じように辿れるアプリだとは限りませんが、 事例のひとつとして掲載させて頂きます。
(白茶) 2023/06/08(木) 20:27:21
DialogHandle = FindWindow(vbNullString, "ファイルを開く")
Handle = FindWindowEx(DialogHnd, 0&, "ComboBoxEx32", vbNullString)
Handle = FindWindowEx(Handle, 0&, "ComboBox", vbNullString)
Handle = FindWindowEx(Handle, 0&, "Edit", vbNullString)
やはりSendMessageでフルパスが送れないです。
今回はフルパスで送りたいのですが、(1)で送れない原因を
ご存じであれば教えていただきたいです。
(VBA勉強中) 2023/06/12(月) 14:51:17
ByVal 途中パス & ファイル名 で送れるんなら、 ByVal CStr(フルパス) でも送れそうですね。
送れるんなら、変数[フルパス]の型宣言の問題かな? (ただ、それならエラーになってる気もするので... 正直よく分かんないス)
(白茶) 2023/06/12(月) 15:07:03
Private Declare PtrSafe Function SendMessage Lib "USER32" Alias "SendMessageA" (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr
(VBA勉強中) 2023/06/13(火) 12:06:12
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.