アドバイスをお願いします。
Excel-VBAで起動しているエクスプローラに対してハンドルを取得してクリックしたり、テキストボックスに文字をセットするプログラムを作って動かしています。
WindowsXPのときはできていたのですが、Windows7になったらエクスプローラが変わったため正しく動作しなくなりました。
やりたいのはエクスプローラに検索文字列(添付ファイル参照)をセットして検索をさせたいのです。
「windows7のエクスプローラをVBAで操作-1」「windows7のエクスプローラをVBAで操作-2」のタイトルで質問させていただき、下のコードを考えましたが、★のところでハンドルが返らず0でした。InspectObjectsで見ると「読み込み専用」と。これが原因ですか?どうしたらいいのだろう・・・。
またこのハンドルを取得して、AccessibleObjectFromWindowでIAccessible を取り出して、accDoDefaultActionしたいのですが、UUIDの値が分かりません。OLEViewで見たのですがExplorer関連がいくつかあり(添付ファイル参照)どれを使っていいものやら。ボタンクリックの後に文字列セットがあるのですが、まだたどり着けず。
IAccessible初心者です。
質問ばかりで済みませんがコーディングのアドバイスお願いします。
Option Explicit
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" ( _
ByVal hwndParent As Long, _
ByVal hwndChildAfter As Long, _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
(ByVal hWnd As Long, _
ByVal dwObjectID As Long, _
ByRef riid As Any, _
ByRef ppvObject As Any) As Long
Const WM_SETTEXT = &HC '文字列送信
Const BM_CLICK = &HF5 'クリック
Const OBJID_CLIENT As Integer = &HFFFFFFFC
Private Type UUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Private IID_IAccessible As UUID
Private oShell
Private ie
Private hWnd As Long
Private SearchBoxハンドル As Long
Private Search_Folder As String
Private RC As Long
'Private objAcc As IAccessible
Private objAcc As UUID
Private varChild As Variant
Private cnt
Private fullnames As String
Sub 検索指定のインプット()
Search_Folder = "c:\xxx"
Set oShell = CreateObject("Shell.Application")
For cnt = oShell.Windows().Count - 1 To 0 Step -1
' On Error Resume Next
Set ie = oShell.Windows().Item(cnt)
fullnames = ie.FullName
' On Error GoTo 0
If fullnames = "C:\Windows\Explorer.EXE" Then
ie.Navigate (Search_Folder)
hWnd = FindWindow("CabinetWClass", vbNullString)
hWnd = FindWindowEx(hWnd, 0, "WorkerW", vbNullString)
hWnd = FindWindowEx(hWnd, 0, "ReBarWindow32", vbNullString)
hWnd = FindWindowEx(hWnd, 0, "UniversalSearchBand", vbNullString)
hWnd = FindWindowEx(hWnd, 0, "Search Box", vbNullString)
hWnd = FindWindowEx(hWnd, 0, "SearchEditBoxWrapperClass", vbNullString)
hWnd = FindWindowEx(hWnd, 0, "SearchBox", vbNullString) ' ★--> 0 return
SearchBoxハンドル = hWnd
hWnd = FindWindowEx(SearchBoxハンドル, 0, "Button", "検索")
hWnd = FindWindowEx(SearchBoxハンドル, 0, "SearchEditBox", vbNullString)
' "SearchEditBox" に検索文字列入力の予定だった
With IID_IAccessible
.Data1 = &H68284FAA
.Data2 = &H6A48
.Data3 = &H11D0
.Data4(0) = &H8C
.Data4(1) = &H78
.Data4(2) = &H0
.Data4(3) = &HC0
.Data4(4) = &H4F
.Data4(5) = &HD9
.Data4(6) = &H18
.Data4(7) = &HB4
End With
RC = AccessibleObjectFromWindow(hWnd, OBJID_CLIENT, IID_IAccessible, objAcc)
' 80004005が返る
varChild = 2 ' Class="Button", Name="検索"のコントロールをクリックしたいが・・・
' RC = objAcc.accDoDefaultAction(varChild) ' 書き方が違うみたい・・・
' Class="SearchEditBox" に検索する文字列を入れたいが・・・
Exit Sub
End If
Set ie = Nothing
Next
End Sub
No.11ベストアンサー
- 回答日時:
ご報告ありがとうございました。
>2時間かかる検索もあります。
MsgWaitForMultipleObjectsを出した意図はDoEventsだけのループだと負荷が高くなるからなのですが、
使われないなら、Sleep APIぐらいは挟んでおいた方がいいですよ。
>検索したい設計書にはExcel、Word、PDF等があり、
了解です。
>WindowObject
不必要にVariant型を使うと遅くなるので固有型の方がいいのでは。
kumatti1さん
コメントありがとうございます。
> 使われないなら、Sleep APIぐらいは挟んでおいた方がいいですよ。
アドバイスありがとうございます、入れときました。
>>WindowObject 不必要にVariant型を使うと遅くなるので固有型の方がいいのでは。
as Object で定義しました。As InternetExplorerは自分的になじみが少ないので。
これでクローズしたいと思います。
いろいろとありがとうございました。感謝!
No.10
- 回答日時:
>ネットワーク越しの検索をやっていて、1つの検索はMax2時間くらいで、
>トータルで1昼夜かかっています。
いや、違くてトータルの時間でなくて、一回の検索に時間が掛かるならと言う意味で書きました。
(今更ですが)そもそも、エクスプローラで検索しなくてはいけない状況は不思議な気がします。
FileSystemObjectで遅ければ、Dir関数とかコマンドプロンプトのDirとかAPIのFindFirstFileとか使えばいいのではと。
kumatti1さん
いろいろとありがとうございました。
ご提供のコードにNavigate処理を入れて、標準モジュールからCallして一応完了する
ことができました。
最後にUserformモジュールに書いたコードを載せています。
一応と言うのは、Navigateしたときに★(2)のie_DocumentCompleteのイベントは2回
発生しますが、SUB検索処理が★(1)のステップまで到達しているときに2回目の
ie_DocumentCompleteのイベントを受けることがあり、誤動作してしまうことがあります。
この誤動作を検知したときはNavigateからリトライすることで回避しています。
とは言え、ここまで来れたのはkumatti1さんのおかげです。
ありがとうございました。
>いや、違くてトータルの時間でなくて、一回の検索に時間が掛かるならと言う意味
で書きました。
10秒で終わる検索もありますが、2時間かかる検索もあります。
>(今更ですが)そもそも、エクスプローラで検索しなくてはいけない状況は不思議な
気がします。
仕事で納品されたシステムの設計書から文字列を探したいときに使います。
検索したい設計書にはExcel、Word、PDF等があり、これらは検索できなければなりません。
Excelで言えば、セルの中の文字だけではなく、吹き出しの図形の中の文字列も検索
させたくあります。
エクスプローラの検索ではこれらができますので使用しています。(フリーソフトは使用禁止です)
あっ、もしかしてFileSystemObjectとかFindFirstFileでも同様のことができるのかな?
Option Explicit
Private WithEvents EventHandler As Shell32.ShellFolderViewOC
Private WithEvents ie As SHDocVw.InternetExplorer
Private Filter_Done_flg As Boolean
Private Navigate_Done_flg As Boolean
Private Sub EventHandler_EnumDone()
Filter_Done_flg = True
EventHandler.SetFolderView Nothing
End Sub
Private Sub ie_DocumentComplete(ByVal pDisp As Object, URL As Variant) ★ (2)
If Not EventHandler Is Nothing Then
EventHandler.SetFolderView pDisp.Document
End If
End Sub
Private Sub ie_NavigateComplete2(ByVal pDisp As Object, URL As Variant)
Navigate_Done_flg = True
End Sub
Public Sub 検索処理(WindowObject, 検索フォルダ As String, 検索文字列 As
String)
Dim doc As IShellFolderViewDual3
Set EventHandler = Nothing
Set ie = WindowObject
Navigate_Done_flg = False
ie.Navigate (検索フォルダ)
While ie.Busy Or ie.ReadyState <> 4
DoEvents
Wend
Do
DoEvents
Loop Until Navigate_Done_flg = True
Filter_Done_flg = False
Set EventHandler = New Shell32.ShellFolderViewOC ★(1)
Set doc = ie.Document
doc.FilterView 検索文字列
While ie.Busy Or ie.ReadyState <> 4
DoEvents
Wend
Do
DoEvents
Loop Until Filter_Done_flg = True
Set EventHandler = Nothing
Set ie = Nothing
End Sub
No.9
- 回答日時:
No.8
- 回答日時:
Microsoft Internet Controlsにも参照設定を行って、UserFormモジュールに貼り付けて、
リンク先の回答通りに直せば期待した動作をします。
フィルタリングに時間が掛かる状況は想像出来ませんが、もしそうならMsgWaitForMultipleObjectsを使うのもありかもしれません。
(再利用性を考慮すると)クラスモジュールもありかもしれませんが、その辺は適宜直してください。
kumatti1さん
回答ありがとうございました。
UserFormモジュールに貼り付けでできました。感謝!
kumatti1さんには大変お世話になりました。
> フィルタリングに時間が掛かる状況は想像出来ませんが、
ネットワーク越しの検索をやっていて、1つの検索はMax2時間くらいで、
トータルで1昼夜かかっています。
殆どクローズできる状況ですが、あと1~2日だけ開けさせて置いてください。
No.7
- 回答日時:
kumatti1 さん、コードの提供ありがとうございました。
余り確認する時間がなかったのですが、コードのすべてを標準モジュールに
貼り付けたところ、以下の2行が赤く反転しました。
こちらExcel2010です。
Private WithEvents d As Shell32.ShellFolderViewOC
Private WithEvents ie As SHDocVw.InternetExplorer
一部をクラスモジュール、一部を標準モジュールに置くのでしょうか。
以下の部分をクラスモジュールに置き、Private Sub UserForm_Initialize()以下を
標準モジュールに置くと、dとかieの変数の宣言が足りません。
Option Explicit
Public WithEvents d As Shell32.ShellFolderViewOC
Public WithEvents ie As SHDocVw.InternetExplorer
Private flg As Boolean
Private Sub d_EnumDone()
flg = True
Debug.Print "d_EnumDone"
d.SetFolderView Nothing
End Sub
Private Sub ie_DocumentComplete(ByVal pDisp As Object, URL As Variant)
d.SetFolderView pDisp.Document
End Sub
クラスモジュールを使ったことがなく、基本的なところの質問で申し訳ありませんが
よろしくお願いします。
No.6
- 回答日時:
ShellFolderViewOC.EnumDoneイベントですが、タイミングがシビアみたいでこちらではまだ、上手く行ってません。
http://msdn.microsoft.com/en-us/library/windows/ …
# 参照設定は「Microsoft Shell Controls And Automation」
kumatti1 さん、アドバイスありがとうございます。
> ShellFolderViewOC.EnumDoneイベントですが、タイミングがシビアみたいでこち
らではまだ、上手く行ってません。
ExcelVBA側でエクスプローラのイベントをハンドルすること自体から難しそうです
ね。
ShellFolderViewOC.EnumDoneno のネット上の例題もあまりありませんでした。
クラスモジュールに書かないといけない・・・?
エクスプローラを起動しないといけない・・・?
Windows7でテストしたいと思いますので(もしかして問題なく動くかも)コードを
お教え戴けないでしょうか。
以上よろしくお願いします。
No.5
- 回答日時:
Win.document.FilterView "hoge"
の一文で済む様です。
IShellFolderViewDual3::FilterView method
http://msdn.microsoft.com/en-us/library/windows/ …
kumatti1さん、驚きの回答ありがとうございます。
>Win.document.FilterView "hoge"
>の一文で済む様です。
と、あったので「何が一文で済むのか」分からないまま動かしてみると
検索文字列として hoge が入るじゃないですか。
びっくりしました。
ありがとうございました。
もう自分のスキルが届くところではないですね。
最後にもうひとつアドバイス戴けたらと思います。
検索させる部分と検索結果を取り出す部分の骨格は以下のようになりましたが、
検索が終わったことをどのようにして判断するかが分かっておりません。
(Sleep 10000 の部分)
定期的に検索結果を取得するとしても、終了を表す文字列も無さそうですし・・・。
何かのプロパティがあれば嬉しいのですが。
長い間お付き合いして戴き申し訳ありませんがよろしくお願いします。
Option Explicit
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" ( _
ByVal hwndParent As Long, _
ByVal hwndChildAfter As Long, _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Sub 検索()
Dim Win As Object
Dim Search_Folder As String
Dim Oshell As Object
Dim Gyo As Long
Dim Col As Long
Dim hWnd As Long
Dim Works As String
Dim Folder As Object
Dim Folderitem As Object
Dim Folder_title As String
Set Oshell = CreateObject("Shell.Application")
Set Win = Nothing
For Each Win In Oshell.Windows()
If Win.FullName = "C:\Windows\Explorer.EXE" Then
Exit For
End If
Set Win = Nothing
Next
If Win Is Nothing Then Exit Sub
ThisWorkbook.Sheets("sheet1").Cells.ClearContents
Win.Navigate ("c:\xxx")
Sleep 500
Win.document.FilterView "検索文字列"
Sleep 10000 ' ここで検索終了を待ちたい
ThisWorkbook.Sheets("sheet1").Cells(1, 1).Value = "結果なし"
Set Folder = Win.document.Folder
On Error Resume Next
Folder_title = Folder.Items().Item().Path
On Error GoTo 0
If Left(Folder_title, 4) = "検索場所" Then
If Folder.Items().Count <> 0 Then
Gyo = 1
For Each Folderitem In Folder.Items()
For Col = 0 To 7
ThisWorkbook.Sheets("sheet1").Cells(Gyo, Col + 1).Value = Folder.GetDetailsOf(Folderitem, Col)
Next
Gyo = Gyo + 1
Next
End If
End If
End Sub
No.4
- 回答日時:
こちらはWin8.1ですが、ここは
>hWnd = FindWindowEx(hWnd, 0, "SearchBox", vbNullString) ' ★--> 0 return
DirectUIHWND
になってますね。
ありがとうございます。
Windows7ですが、hWnd = FindWindowEx(hWnd, 0, "DirectUIHWND", vbNullString)
でハンドルが確かに返ってきました。
InspectObjectsで見るとClass="SearchBox"なのですが、Classが"DirectUIHWND"みたいな文言もあります。
よく分かりません。
No.3
- 回答日時:
こちらでも確認したところ、Application.SendKeysでなくて、SendKeysなら 漢字文字列 もセット出来ました。
https://gist.github.com/kumatti1/9b6eac43e294db9 …
>UUID(IAccessible?)の値
継承関係のあるインターフェースIDならOKです。
IAccessible以外には IDispatch とか IUnknown とか。
---
Visual Studio評価版は3ヶ月利用できるようなので、C++を入れればSpy++も入るのでそちらからも確認されるとか。
IAccessibleの他にUI Automationてのもありますよ。
No.2
- 回答日時:
アドレスバーと同じ理屈でクリックさせてからでないと無理なのかも。
(調査用と言うわけでもないが)AccessibleObjectFromPointとかもありますよ。
hwndプロパティでハンドルが得られるので、「Ctrl + F」相当のキー送信をWM_KEYDOWN辺りで送れば、検索ボックスにフォーカスされるので、
(他プロセスであっても)AttachThreadInputでGetFocus(APIの方)でハンドルが得られるのでWM_SETTEXTやらWM_CHARを送信するとか。で確定はWM_KEYDOWNでVK_RETURNとか。
↓コメントアウトしてる方が正しいです。
'Private objAcc As IAccessible
Private objAcc As UUID
>Const OBJID_CLIENT As Integer = &HFFFFFFFC
Long型
---
ただ、この辺を見るとIUnknown_QueryService(提示のコードで言えば引数にieを指定)で
IFolderView2インターフェースを得て制御するのが本来の実装なんでしょうね。
まあでもVBA向けのタイプライブラリがありませんので、C++でDLLを作ってVBAから呼び出すとかになるのかなと。
http://detail.chiebukuro.yahoo.co.jp/qa/question …
http://www.activebasic.com/forum/viewtopic.php?t …
この回答への補足
kumatti1さん
引き続きアドバイスありがとうございます。
>アドレスバーと同じ理屈でクリックさせてからでないと無理なのかも。
上位の"Search Box"のハンドルと "SearchEditBoxWrapperClass"のハンドルに対して
SendMessage(hWnd, BM_CLICK, 0, 0)を実行してみましたが、やはり結果は同じで "SearchBox",のハンドルは取得できませんでした。
>(調査用と言うわけでもないが)AccessibleObjectFromPointとかもありますよ。
あるポイントのAccessibleObjectを取得できるみたいですね。まだ試しておりません。
>「Ctrl + F」相当のキー送信をWM_KEYDOWN辺りで送れば、検索ボックスにフォーカスされるので、
Navigateしたあとに、以下のコードを実行したら検索文字列"ABC"は入りました。
AppActivate ("xxx") ・・・検索フォルダの最下位のフォルダ名
Sleep 1000
Application.SendKeys "^f"
Sleep 1000
Application.SendKeys "abc"
しかし、漢字文字列を検索したいので、やはり"SearchEditBox"に対して文字を入れないとダメかなと思っています。またSendKeysは動作時点にフォーカスされているところにSendkeyされるので操作の制約ができてしまうかなと思っています。
と言うことで、 IAccessibleの攻略が必須のようです。
まだ分かっていないのですが、今回場合、UUID(IAccessible?)の値には何を与えるのでしょうか?
OLEViewで見たのですがExplorer関連がいくつかありました(添付ファイル参照)。
Explorer Browser、Explorer Browser Results Folder、Explorer Navigation Bar、
Explorer Search Band、Explorer Travel Band、ExplorerCommandEnumeratorのどれ
かとは思うのですが・・・。DLL名から推測するとか・・・?
試しにExplorerBrowserを展開するとIAccssibleObjectと言う如何にもそれっぽいものもあります。表示されるInterface=に続く数字がUUIDなのかな?どなたか教えて戴ければ有難いです。
また、よろしくお願いします。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・チョコミントアイス
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・あなたの習慣について教えてください!!
- ・ハマっている「お菓子」を教えて!
- ・高校三年生の合唱祭で何を歌いましたか?
- ・【大喜利】【投稿~11/1】 存在しそうで存在しないモノマネ芸人の名前を教えてください
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・家の中でのこだわりスペースはどこですか?
- ・つい集めてしまうものはなんですか?
- ・自分のセンスや笑いの好みに影響を受けた作品を教えて
- ・【お題】引っかけ問題(締め切り10月27日(日)23時)
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・10秒目をつむったら…
- ・人生のプチ美学を教えてください!!
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
EXCELで=より左の文字を一括で...
-
エクセルでアルファベットか数...
-
MS SQLServer のSQLで文字列の...
-
Excelで3E8を3.00E+8にしない方...
-
エクセルで文字列の最大値を抽...
-
aaa.bbb.ccc という、「ドット...
-
エクセル 数値データを桁をそ...
-
VBAでの Replace関数で、ワイル...
-
文字列からタブコードを取り除...
-
Excelで指数表現しないようにす...
-
MessageBoxが表示されないのは何で
-
アクセスで特定の数字以外(複...
-
Left関数とRight関数を合わせた...
-
Pro c/c++ でホスト変数の後に....
-
アドレスでの参照の仕方
-
C#で年月を比較する
-
“丸(〇/○/◯)”に似た文字…
-
エクセルで文字列をtxtファイル...
-
windows7のエクスプローラをVBA...
-
同一セル内に関数と文字列を同...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
エクセルでアルファベットか数...
-
EXCELで=より左の文字を一括で...
-
文字列からタブコードを取り除...
-
VBAでの Replace関数で、ワイル...
-
Excelで3E8を3.00E+8にしない方...
-
エクセルで文字列をtxtファイル...
-
Excelで指数表現しないようにす...
-
【Excel VBA】複数ある特定の文...
-
Left関数とRight関数を合わせた...
-
エクセルで文字列の最大値を抽...
-
同一セル内に関数と文字列を同...
-
VBA2005 16進を2桁で表示したい。
-
VBの「As String * 128」とは?
-
MS SQLServer のSQLで文字列の...
-
エクセル 数値データを桁をそ...
-
C#で年月を比較する
-
16進数を10進数に簡単に変換す...
-
“丸(〇/○/◯)”に似た文字…
-
アクセスで特定の数字以外(複...
-
Msgboxの×が押されたとき
おすすめ情報