【UIAutomation】エクスプローラーの検索ボックスに値をセット
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2023/7/10 10:43:31
> ■最終目的
> 「UIAutomationを使ってエクスプローラー右上の検索ボックスに値をセットして検索結果を取得する」
> ということが最終目的です。
UIAutomation 経由で操作することが最終目標ですか?
それとも UIAutomation 以外の方法でも良いが、右上の検索ボックスに
値を入れる操作を行いたいということですか?
それとも、検索ボックスへの入力は必ずしも必要ではなく、
Explorer による検索結果を得ることが最終目標なのですか?
もしも検索結果さえ得られれば良いのなら、WindowsAPICodePack の
Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser コントロールを
使うのが手っ取り早そうです。
> 「UIAutomationを使ってエクスプローラー右上の検索ボックスに値をセットして検索結果を取得する」
> ということが最終目的です。
UIAutomation 経由で操作することが最終目標ですか?
それとも UIAutomation 以外の方法でも良いが、右上の検索ボックスに
値を入れる操作を行いたいということですか?
それとも、検索ボックスへの入力は必ずしも必要ではなく、
Explorer による検索結果を得ることが最終目標なのですか?
もしも検索結果さえ得られれば良いのなら、WindowsAPICodePack の
Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser コントロールを
使うのが手っ取り早そうです。
' WindowsAPICodePack をNuGet しておく
Imports Microsoft.WindowsAPICodePack.Controls
Imports Microsoft.WindowsAPICodePack.Controls.WindowsForms
Imports Microsoft.WindowsAPICodePack.Shell
Imports Microsoft.WindowsAPICodePack.Shell.PropertySystem
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim pv = Me.ExplorerBrowser1.NavigationOptions.PaneVisibility
pv.Navigation = PaneVisibilityState.Hide
pv.Commands = PaneVisibilityState.Hide
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Dim searchString = Me.TextBox1.Text
'Dim sco = DirectCast(Me.ComboBox1.SelectedValue, SearchConditionOperation)
Dim searchString = "*.log"
Dim sco = SearchConditionOperation.DosWildcards
Dim sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.FileName, searchString, sco)
Dim dir = ShellObject.FromParsingName(Environment.GetFolderPath(Environment.SpecialFolder.Windows))
Dim ssf As New ShellSearchFolder(sc, dir)
Me.ExplorerBrowser1.Navigate(ssf)
End Sub
#If False Then
'検索条件を TextBox / ComboBox から指定できるようにする場合はこちら
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
SetEnumToComboBox(Me.ComboBox1, SearchConditionOperation.DosWildcards)
Me.TextBox1.Text = "*.log"
End Sub
''' <summary>
''' 列挙型をコンボボックスに設定する
''' </summary>
''' <typeparam name="TEnum">C# と違って列挙型の型制約を書けないので値型制約で凌ぐ</typeparam>
''' <param name="comboBox">設定対象のコンボボックス</param>
''' <param name="defaultValue">初期設定させたい既定値</param>
Private Sub SetEnumToComboBox(Of TEnum As Structure)(comboBox As ComboBox, Optional defaultValue As TEnum? = Nothing)
Dim options = [Enum].GetValues(GetType(TEnum)) _
.OfType(Of TEnum)() _
.ToDictionary(Function(k) k.ToString(), Function(v) v)
comboBox.DropDownStyle = ComboBoxStyle.DropDownList
comboBox.DisplayMember = "Key"
comboBox.ValueMember = "Value"
comboBox.DataSource = New BindingSource(options, Nothing)
If defaultValue IsNot Nothing Then
comboBox.SelectedValue = defaultValue
End If
End Sub
#End If
End Class
投稿者 豆腐  (社会人)
投稿日時
2023/7/10 13:45:58
魔界の仮面弁士様、ありがとうございます。
ネタ元は以下の掲示板での質問です。
https://www.excel.studio-kazu.jp/kw/20230708114319.html
質問の要旨は「フォルダ内のOffceファイルの中身を検索する」ということです。
「VBAで1つ1つファイルを開いては検索を繰り返す」ことで可能でしょうが、
2023/07/09(日) 11:22:14の (hatena)さんの回答内容が自分も真っ先に思いつきました。
VBAでもUIAutomationを使えば、可能と思い、.NETならUIAutomationを拡張メソッドとして登録済
なので、コーディングが楽なのでやってみると当初の問題にぶち当たりました。
ご提案の「Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser」も試してみました。
知らない知識だったので、勉強になりました。
「ファイル名の検索」は出来ましたが、こちらの方法で、「Offceファイルの中身を検索する」ことは可能でしょうか?
エクスプローラ側で、「ファイル名と内容を常に検索する」設定にしても中身の検索はNGでした。
できれば、VBAからも検索結果を取得したいので「UIAutomation」でやりたいですが、
「WindowsAPICodePack」での方法でも知識が増えるので、もし、ご存じなら教えてもらえるとうれしいです。
ネタ元は以下の掲示板での質問です。
https://www.excel.studio-kazu.jp/kw/20230708114319.html
質問の要旨は「フォルダ内のOffceファイルの中身を検索する」ということです。
「VBAで1つ1つファイルを開いては検索を繰り返す」ことで可能でしょうが、
2023/07/09(日) 11:22:14の (hatena)さんの回答内容が自分も真っ先に思いつきました。
VBAでもUIAutomationを使えば、可能と思い、.NETならUIAutomationを拡張メソッドとして登録済
なので、コーディングが楽なのでやってみると当初の問題にぶち当たりました。
ご提案の「Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser」も試してみました。
知らない知識だったので、勉強になりました。
「ファイル名の検索」は出来ましたが、こちらの方法で、「Offceファイルの中身を検索する」ことは可能でしょうか?
エクスプローラ側で、「ファイル名と内容を常に検索する」設定にしても中身の検索はNGでした。
できれば、VBAからも検索結果を取得したいので「UIAutomation」でやりたいですが、
「WindowsAPICodePack」での方法でも知識が増えるので、もし、ご存じなら教えてもらえるとうれしいです。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2023/7/10 16:49:35
> 知らない知識だったので、勉強になりました。
機能的には「Windows Search」API や「Desktop Search」API などの分野ですね。
https://learn.microsoft.com/ja-jp/windows/win32/search/windows-search
https://www.groovypost.com/howto/search-through-file-contents-windows-10/
SearchConditionFactory.CreateLeafCondition メソッド相当の COM API はこのあたり。
CreateAndOrCondition を用いて複合条件にもできます。
https://learn.microsoft.com/ja-jp/windows/win32/api/structuredquery/nf-structuredquery-iconditionfactory2-createleaf
別案として、Process.Start で "search:" プロトコルや "search-ms:" プロトコルを
単に呼び出すだけで済ませてしまうという手もあります。
https://stackoverflow.com/questions/34012842/open-explorer-process-with-a-search-pattern-on-file-name-vb-net
https://learn.microsoft.com/en-us/windows/win32/shell/search-protocol
https://learn.microsoft.com/en-us/windows/win32/search/-search-3x-wds-qryidx-searchms
> 「ファイル名の検索」は出来ましたが、こちらの方法で、「Offceファイルの中身を検索する」ことは可能でしょうか?
検索時に「ファイル コンテンツ」を有効にしてください。
https://did2memo.net/2016/04/24/windows-10-file-text-search/
機能的には「Windows Search」API や「Desktop Search」API などの分野ですね。
https://learn.microsoft.com/ja-jp/windows/win32/search/windows-search
https://www.groovypost.com/howto/search-through-file-contents-windows-10/
SearchConditionFactory.CreateLeafCondition メソッド相当の COM API はこのあたり。
CreateAndOrCondition を用いて複合条件にもできます。
https://learn.microsoft.com/ja-jp/windows/win32/api/structuredquery/nf-structuredquery-iconditionfactory2-createleaf
別案として、Process.Start で "search:" プロトコルや "search-ms:" プロトコルを
単に呼び出すだけで済ませてしまうという手もあります。
https://stackoverflow.com/questions/34012842/open-explorer-process-with-a-search-pattern-on-file-name-vb-net
https://learn.microsoft.com/en-us/windows/win32/shell/search-protocol
https://learn.microsoft.com/en-us/windows/win32/search/-search-3x-wds-qryidx-searchms
> 「ファイル名の検索」は出来ましたが、こちらの方法で、「Offceファイルの中身を検索する」ことは可能でしょうか?
検索時に「ファイル コンテンツ」を有効にしてください。
https://did2memo.net/2016/04/24/windows-10-file-text-search/
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2023/7/10 19:49:41
> Dim hwnd = FindWindow("CabinetWClass", "Windows")
Explorer の設定「タイトルバーに完全なパスを表示する」の設定によって、
タイトルが "C:\Windows" になることもあれば "Windows" になることもあります。
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\CabinetState\FullPath
タイトルからウィンドウを探る方法のほか、ShellWindows コレクションから
ウィンドウハンドルを拾う方法もありますね。
Explorer の設定「タイトルバーに完全なパスを表示する」の設定によって、
タイトルが "C:\Windows" になることもあれば "Windows" になることもあります。
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\CabinetState\FullPath
タイトルからウィンドウを探る方法のほか、ShellWindows コレクションから
ウィンドウハンドルを拾う方法もありますね。
'VBA
Dim oExp As Object
For Each oExp In GetObject("new:9BA05972-F6A8-11CF-A442-00A0C90A8F39")
Debug.Print "HWND: 0x"; Hex(oExp.Hwnd)
Debug.Print "Location:"; oExp.Top, oExp.Left, oExp.Width, oExp.Height
If TypeName(oExp.Document) Like "IShellFolderView*" Then
'Debug.Print oExp.LocationURL
Debug.Print "Path:"; oExp.Document.Folder.Items().Item.Path
Debug.Print "Name:"; oExp.Document.Folder.Items().Item.Name
End If
Debug.Print
Next
投稿者 豆腐  (社会人)
投稿日時
2023/7/10 21:11:37
魔界の仮面弁士様、ありがとうございます。
「Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser」を使った方法を試しているのですが、
当方ではコンテンツの検索結果がExplorerBrowserに表示されません。
>検索時に「ファイル コンテンツ」を有効にしてください。
は、Explorerで検索タブが表示されている場合の事だと思いますが、検索前はそもそも「検索タブ」が表示されないので、
前回書いたように、「表示」タブ=>「オプション」で開かれる「フォルダーオプション」の
「検索」タブ=>「ファイル名と内容を常に検索する」にチェックを入れています。
■検証方法
検索ワードは「みかん」と仮定して、「みかん」を「含む/含まない」、それぞれのxlsxファイルを
「C:\xxx\xxx\xlsxフォルダ」に用意して実験しています。
Explorerで「C:\xxx\xxx\xlsxフォルダ」を開き、直接、検索ボックスに「みかん」と入力すれば期待通り
中身に「みかん」が含まれるファイルのみ列挙されます。
しかし、以下のコードではExplorerBrowserに何も表示されません。ご指導、お願いします。
「プロトコル」と「ShellWindows コレクションからウィンドウハンドルを拾う方法」は、試したいのですが
現在、そこまで手が回りません。「COM API 」は敷居が高そうなのでパスです。
「Microsoft.WindowsAPICodePack.Controls.WindowsForms.ExplorerBrowser」を使った方法を試しているのですが、
当方ではコンテンツの検索結果がExplorerBrowserに表示されません。
>検索時に「ファイル コンテンツ」を有効にしてください。
は、Explorerで検索タブが表示されている場合の事だと思いますが、検索前はそもそも「検索タブ」が表示されないので、
前回書いたように、「表示」タブ=>「オプション」で開かれる「フォルダーオプション」の
「検索」タブ=>「ファイル名と内容を常に検索する」にチェックを入れています。
■検証方法
検索ワードは「みかん」と仮定して、「みかん」を「含む/含まない」、それぞれのxlsxファイルを
「C:\xxx\xxx\xlsxフォルダ」に用意して実験しています。
Explorerで「C:\xxx\xxx\xlsxフォルダ」を開き、直接、検索ボックスに「みかん」と入力すれば期待通り
中身に「みかん」が含まれるファイルのみ列挙されます。
しかし、以下のコードではExplorerBrowserに何も表示されません。ご指導、お願いします。
「プロトコル」と「ShellWindows コレクションからウィンドウハンドルを拾う方法」は、試したいのですが
現在、そこまで手が回りません。「COM API 」は敷居が高そうなのでパスです。
Imports Microsoft.WindowsAPICodePack.Controls
Imports Microsoft.WindowsAPICodePack.Shell
Imports Microsoft.WindowsAPICodePack.Shell.PropertySystem
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim pv = Me.ExplorerBrowser1.NavigationOptions.PaneVisibility
pv.Navigation = PaneVisibilityState.Hide
pv.Commands = PaneVisibilityState.Hide
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim searchString = "みかん"
Dim sco = SearchConditionOperation.WordEqual '★ここの設定がわからない
Dim sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.FileName, searchString, sco)
Dim dir = ShellObject.FromParsingName("C:\xxx\xxx\xlsxフォルダ")
Dim ssf As New ShellSearchFolder(sc, dir)
Me.ExplorerBrowser1.Navigate(ssf)
End Sub
End Class
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2023/7/11 12:08:45
> 「検索」タブ=>「ファイル名と内容を常に検索する」
それで OK です。[表示]タブの[オプション]における [検索]タブのチェックボックス設定は、
検索結果が表示された後でリボンに表示される[検索]タブのものと同じです。
設定結果は保持されるため、次回利用時にも同じ設定が引き継がれます。
コンテンツに対する全文検索を実施する場合は、インデックス作成の有無で
動作が変わる可能性がある点に注意してください。
> 検索前はそもそも「検索タブ」が表示されないので、
このタブは、エクスプローラーのナビゲーションが完了した後に操作するものですね。
[ピクチャ] フォルダーに移動すると、リボンに[ピクチャ ツール]タブが現れますし、
[ビデオ] フォルダーに移動すると、リボンに[ビデオ ツール]タブが現れるように、
検索結果が表示された後で、リボンに[検索]タブが現れます。
> 「プロトコル」と「ShellWindows コレクションからウィンドウハンドルを拾う方法」は、試したいのですが
> 現在、そこまで手が回りません。「COM API 」は敷居が高そうなのでパスです。
検索ボックスに対する操作なので、search-ms プロトコルに関する知識も事前に仕入れておいた方が良いかも。
https://learn.microsoft.com/ja-jp/windows/win32/search/-search-3x-wds-qryidx-searchms
[検索]リボンの[検索条件を保存]ボタンを押してみると分かるかと思いますが、
検索条件は *.search-ms ファイル(中身は XML ファイル)として保存されますね。
また、OS バージョンなどで使用可能なクエリーが異なってくることがあります。
> Dim ssf As New ShellSearchFolder(sc, dir)
ちなみに、Option Strict On の場合はキャストが必要です。
Dim ssf As New ShellSearchFolder(sc, DirectCast(dir, ShellContainer))
> Dim searchString = "みかん"
> Dim sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.FileName, searchString, sco)
System.FileName を指定しているので、これは「拡張子も含んだファイル名」に対する検索条件ですね。
https://learn.microsoft.com/ja-jp/windows/win32/properties/props-system-filename
sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.FullText, "AMD Ryzen", SearchConditionOperation.ValueContains)
などとすれば、AMD Ryzen について書かれた *.txt や *.docx などがヒットします。
ただし、内容検索が有効になっていないとヒットしません。
sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.Document.PageCount, "3", SearchConditionOperation.GreaterThanOrEqual)
などとすれば、3 ページ以上を持つ Word 文書がヒットします。
sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.Document.SlideCount, "1", SearchConditionOperation.Equal)
だと、1 ページだけで構成された PowerPoint 文書がヒットします。
> Dim sco = SearchConditionOperation.WordEqual '★ここの設定がわからない
ファイル名に対する検索だと、DosWildcards や ValueContains が良く利用されます。
それで OK です。[表示]タブの[オプション]における [検索]タブのチェックボックス設定は、
検索結果が表示された後でリボンに表示される[検索]タブのものと同じです。
設定結果は保持されるため、次回利用時にも同じ設定が引き継がれます。
コンテンツに対する全文検索を実施する場合は、インデックス作成の有無で
動作が変わる可能性がある点に注意してください。
> 検索前はそもそも「検索タブ」が表示されないので、
このタブは、エクスプローラーのナビゲーションが完了した後に操作するものですね。
[ピクチャ] フォルダーに移動すると、リボンに[ピクチャ ツール]タブが現れますし、
[ビデオ] フォルダーに移動すると、リボンに[ビデオ ツール]タブが現れるように、
検索結果が表示された後で、リボンに[検索]タブが現れます。
> 「プロトコル」と「ShellWindows コレクションからウィンドウハンドルを拾う方法」は、試したいのですが
> 現在、そこまで手が回りません。「COM API 」は敷居が高そうなのでパスです。
検索ボックスに対する操作なので、search-ms プロトコルに関する知識も事前に仕入れておいた方が良いかも。
https://learn.microsoft.com/ja-jp/windows/win32/search/-search-3x-wds-qryidx-searchms
[検索]リボンの[検索条件を保存]ボタンを押してみると分かるかと思いますが、
検索条件は *.search-ms ファイル(中身は XML ファイル)として保存されますね。
また、OS バージョンなどで使用可能なクエリーが異なってくることがあります。
> Dim ssf As New ShellSearchFolder(sc, dir)
ちなみに、Option Strict On の場合はキャストが必要です。
Dim ssf As New ShellSearchFolder(sc, DirectCast(dir, ShellContainer))
> Dim searchString = "みかん"
> Dim sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.FileName, searchString, sco)
System.FileName を指定しているので、これは「拡張子も含んだファイル名」に対する検索条件ですね。
https://learn.microsoft.com/ja-jp/windows/win32/properties/props-system-filename
sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.FullText, "AMD Ryzen", SearchConditionOperation.ValueContains)
などとすれば、AMD Ryzen について書かれた *.txt や *.docx などがヒットします。
ただし、内容検索が有効になっていないとヒットしません。
sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.Document.PageCount, "3", SearchConditionOperation.GreaterThanOrEqual)
などとすれば、3 ページ以上を持つ Word 文書がヒットします。
sc = SearchConditionFactory.CreateLeafCondition(SystemProperties.System.Document.SlideCount, "1", SearchConditionOperation.Equal)
だと、1 ページだけで構成された PowerPoint 文書がヒットします。
> Dim sco = SearchConditionOperation.WordEqual '★ここの設定がわからない
ファイル名に対する検索だと、DosWildcards や ValueContains が良く利用されます。
(FileName, "みかん", NotEqual) な条件を指定した場合
"notepad.exe" → ○
"みかん.txt" → ○
"みかん" → ×
"やかん" → ○
"あるみかん.txt" → ○
"アルミカン.txt" → ○
(FileName, "みかん", Equal) な条件を指定した場合
"notepad.exe" → ×
"みかん.txt" → ×
"みかん" → ○
"やかん" → ×
"あるみかん.txt" → ×
"アルミカン.txt" → ×
(FileName, "かん", ValueEndsWith) な条件を指定した場合
"notepad.exe" → ×
"みかん.txt" → ×
"みかん" → ○
"やかん" → ○
"あるみかん.txt" → ×
"アルミカン.txt" → ×
(FileName, "かん", ValueContains) な条件を指定した場合
"notepad.exe" → ×
"みかん.txt" → ○
"みかん" → ○
"やかん" → ○
"あるみかん.txt" → ○
"アルミカン.txt" → ○
"notepad.exe" → ○
"みかん.txt" → ○
"みかん" → ×
"やかん" → ○
"あるみかん.txt" → ○
"アルミカン.txt" → ○
(FileName, "みかん", Equal) な条件を指定した場合
"notepad.exe" → ×
"みかん.txt" → ×
"みかん" → ○
"やかん" → ×
"あるみかん.txt" → ×
"アルミカン.txt" → ×
(FileName, "かん", ValueEndsWith) な条件を指定した場合
"notepad.exe" → ×
"みかん.txt" → ×
"みかん" → ○
"やかん" → ○
"あるみかん.txt" → ×
"アルミカン.txt" → ×
(FileName, "かん", ValueContains) な条件を指定した場合
"notepad.exe" → ×
"みかん.txt" → ○
"みかん" → ○
"やかん" → ○
"あるみかん.txt" → ○
"アルミカン.txt" → ○
投稿者 豆腐  (社会人)
投稿日時
2023/7/11 23:15:48
魔界の仮面弁士様、ご教示頂いた内容を試したので、結果報告致します。
■「search-msプロトコル」を使って検索結果をExplorerで表示できました。(^^♪
こんな、簡単にできるなんて...という感想です。
「VB.NET/VBA]いずれからでも、コンテンツを含むファイルを表示できました。
検索結果の取得はUIAutomationを使って、自力で、できると思います。
■「ShellWindows コレクションからウィンドウハンドルを拾う方法」
Explorerの設定によってFindWindowの第2引数が変わってきて失敗する場合があるのですね。
ShellWindowsからならそれに左右されずにウィンドウハンドルが取れると....了解しました。
■「WindowsAPICodePackのExplorerBrowserを使った内容検索方法」
ご教示頂いたことにより一応、Excelファイルの「内容(コンテンツ)検索」が出来ました。
CreateLeafConditionの第1引数を「FullText」、第3引数を「WordEqual」で成功しました。
第3引数ばかりに気をとられていました。
当方の環境では、「ValueContains」では検索失敗、「WordEqual」で成功という結果となりました。
唯一、不明な点があります。甘えてばかりで申し訳ありませんが、もしご存じなら教えてください。
検索結果をExplorerBrowserにNavigateした後、別の検索条件でNavigateしても、ExplorerBrowserは画面更新されないのです。
ExplorerBrowserにそれっぽいRefreshメソッドがあったので試しましたがNGでした。
以下のコードで「みかん_Click」の後、「いちご_Click」を実行しても、画面更新されないのです。
では、何故、「期待通りの検索結果が返っているか」が分かったのは、
ExplorerBrowserの「右クリック、コンテキストメニュ」の「最新の情報に更新」を実行すれば画面更新されます。
このコンテキストメニュの「最新の情報に更新」同等のメソッドはないでしょうか?
フォルダを連続でNavigateした場合は、ちゃんと画面更新されます。
FormにExplorerBrowserとButton4つ(みかん、いちご、KnownFolderNavigate、FolderNavigate)、貼り付けてます。
■「search-msプロトコル」を使って検索結果をExplorerで表示できました。(^^♪
こんな、簡単にできるなんて...という感想です。
「VB.NET/VBA]いずれからでも、コンテンツを含むファイルを表示できました。
検索結果の取得はUIAutomationを使って、自力で、できると思います。
■「ShellWindows コレクションからウィンドウハンドルを拾う方法」
Explorerの設定によってFindWindowの第2引数が変わってきて失敗する場合があるのですね。
ShellWindowsからならそれに左右されずにウィンドウハンドルが取れると....了解しました。
■「WindowsAPICodePackのExplorerBrowserを使った内容検索方法」
ご教示頂いたことにより一応、Excelファイルの「内容(コンテンツ)検索」が出来ました。
CreateLeafConditionの第1引数を「FullText」、第3引数を「WordEqual」で成功しました。
第3引数ばかりに気をとられていました。
当方の環境では、「ValueContains」では検索失敗、「WordEqual」で成功という結果となりました。
唯一、不明な点があります。甘えてばかりで申し訳ありませんが、もしご存じなら教えてください。
検索結果をExplorerBrowserにNavigateした後、別の検索条件でNavigateしても、ExplorerBrowserは画面更新されないのです。
ExplorerBrowserにそれっぽいRefreshメソッドがあったので試しましたがNGでした。
以下のコードで「みかん_Click」の後、「いちご_Click」を実行しても、画面更新されないのです。
では、何故、「期待通りの検索結果が返っているか」が分かったのは、
ExplorerBrowserの「右クリック、コンテキストメニュ」の「最新の情報に更新」を実行すれば画面更新されます。
このコンテキストメニュの「最新の情報に更新」同等のメソッドはないでしょうか?
フォルダを連続でNavigateした場合は、ちゃんと画面更新されます。
FormにExplorerBrowserとButton4つ(みかん、いちご、KnownFolderNavigate、FolderNavigate)、貼り付けてます。
Option Strict On
Imports Microsoft.WindowsAPICodePack.Controls
Imports Microsoft.WindowsAPICodePack.Shell
Imports Microsoft.WindowsAPICodePack.Shell.PropertySystem
Public Class Form1
'検索フォルダ
Const searchDir As String = "C:\Users\xxxx\Desktop\検索test"
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim pv = Me.ExplorerBrowser1.NavigationOptions.PaneVisibility
pv.Navigation = PaneVisibilityState.Hide 'ナビゲーションエリアを非表示
pv.Commands = PaneVisibilityState.Hide 'ヘッダ上部のツールバーを非表示
End Sub
'検索結果をExplorerBrowserにNavigate
Private Sub SearchNavigate(searchString As String, searchFolder As String)
Dim pKey = SystemProperties.System.FullText
Dim sco = SearchConditionOperation.WordEqual
Dim sc = SearchConditionFactory.CreateLeafCondition(pKey, searchString, sco)
Dim dir = ShellObject.FromParsingName(searchFolder)
Dim ssf As New ShellSearchFolder(sc, DirectCast(dir, ShellContainer))
Me.ExplorerBrowser1.Navigate(ssf)
End Sub
'「*みかん*」を検索
Private Sub みかん_Click(sender As Object, e As EventArgs) Handles みかん.Click
SearchNavigate("みかん", searchDir)
End Sub
'「*いちご*」を検索
Private Sub いちご_Click(sender As Object, e As EventArgs) Handles いちご.Click
SearchNavigate("いちご", searchDir)
End Sub
'KnownFolderのNavigate(フォルダを連続でNavigateした場合は、ちゃんと画面更新されます。)
Private Sub KnownFolderNavigate_Click(sender As Object, e As EventArgs) Handles KnownFolderNavigate.Click
Me.ExplorerBrowser1.Navigate(CType(KnownFolders.Desktop, ShellObject))
End Sub
'一般FolderのNavigate(フォルダを連続でNavigateした場合は、ちゃんと画面更新されます。)
Private Sub FolderNavigate_Click(sender As Object, e As EventArgs) Handles FolderNavigate.Click
Me.ExplorerBrowser1.Navigate(ShellObject.FromParsingName("C:\windows"))
End Sub
Private Sub ExplorerBrowser1_NavigationComplete(sender As Object, e As NavigationCompleteEventArgs) Handles ExplorerBrowser1.NavigationComplete
MsgBox("NavigationComplete")
End Sub
End Class
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2023/7/12 11:56:43
> ■「ShellWindows コレクションからウィンドウハンドルを拾う方法」
ShellWindows による列挙処理は、検索結果の取得にも使うことができます。
COM なので VBA と VB.NET とで同じライブラリを利用できますし、API 宣言も不要です。
参照設定なし(レイトバインド)でも
参照設定あり(アーリーバインド)でも書けますが、
今回は参照設定ありで書いてみます。
参照設定に、COM の "Microsoft Shell Controls And Automation" を追加します。
エクスプローラーに表示されているアイテムが多いと、列挙に時間がかかるのでご注意を。
ShellWindows による列挙処理は、検索結果の取得にも使うことができます。
COM なので VBA と VB.NET とで同じライブラリを利用できますし、API 宣言も不要です。
参照設定なし(レイトバインド)でも
参照設定あり(アーリーバインド)でも書けますが、
今回は参照設定ありで書いてみます。
参照設定に、COM の "Microsoft Shell Controls And Automation" を追加します。
エクスプローラーに表示されているアイテムが多いと、列挙に時間がかかるのでご注意を。
Option Strict On
Public Class Form1
Private WithEvents Button1 As Button
Private WithEvents DataGrid1 As DataGrid
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
DataGrid1 = New DataGrid() With {.Name = "DataGrid1", .Dock = DockStyle.Fill}
Controls.Add(DataGrid1)
Button1 = New Button() With {.Name = "Button1", .Text = "エクスプローラーの列挙", .Dock = DockStyle.Top, .Height = 40}
Controls.Add(Button1)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
UseWaitCursor = True
DataGrid1.DataSource = Nothing
Dim ds As New DataSet("Explorer")
For Each sh In DirectCast(New Shell32.Shell().Windows(), IEnumerable)
Dim doc = TryCast(CallByName(sh, "Document", CallType.Get), Shell32.ShellFolderView)
If doc Is Nothing Then Continue For
Dim tbl = ds.Tables.Add()
Dim oFolder = doc.Folder
Try
tbl.TableName = oFolder.Title
tbl.TableName = $"(0x{Hex(CallByName(sh, "Hwnd", CallType.Get))})" & tbl.TableName
Catch
End Try
tbl.Columns.Add(oFolder.GetDetailsOf(Nothing, 0))
tbl.Columns.Add("区分")
tbl.Columns.Add("パス")
tbl.Columns.Add(oFolder.GetDetailsOf(Nothing, 1))
tbl.Columns.Add(oFolder.GetDetailsOf(Nothing, 2))
tbl.Columns.Add(oFolder.GetDetailsOf(Nothing, 3))
tbl.Columns.Add(oFolder.GetDetailsOf(Nothing, 4))
Dim oItems = oFolder.Items()
Dim values(6) As String
Dim count = oItems.Count
For n = 0 To count - 1
Dim oItem = oItems.Item(n)
Dim status As New List(Of String)()
If oItem.IsBrowsable Then status.Add("Browsable")
If oItem.IsFileSystem Then status.Add("FileSystem")
If oItem.IsFolder Then status.Add("Folder")
If oItem.IsLink Then status.Add("Link")
values(0) = oFolder.GetDetailsOf(oItem, 0)
values(1) = String.Join(", ", status)
values(2) = oItem.Path
values(3) = oFolder.GetDetailsOf(oItem, 1)
values(4) = oFolder.GetDetailsOf(oItem, 2)
values(5) = oFolder.GetDetailsOf(oItem, 3)
values(6) = oFolder.GetDetailsOf(oItem, 4)
tbl.Rows.Add(values)
Next
Next
ds.AcceptChanges()
DataGrid1.DataSource = ds
UseWaitCursor = False
Button1.Enabled = True
End Sub
End Class
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2023/7/12 16:02:51
> 唯一、不明な点があります。甘えてばかりで申し訳ありませんが、もしご存じなら教えてください。
私もさほど詳しくはありません。
ExplorerBrowser の存在も、豆腐さんの今回の質問を受けて初めて見つけたばかりの情報ですし。
ちなみに ExplorerBrowser コントロールというのは、内部的には
COM の IExplorerBrowser インターフェイスを実装しているようです。
https://learn.microsoft.com/ja-jp/windows/win32/api/shobjidl_core/nn-shobjidl_core-iexplorerbrowser
Navigate メソッドは COM の IExplorerBrowser::BrowseToObject メソッドを内部的に呼んでいますね。
https://learn.microsoft.com/ja-jp/windows/win32/api/shobjidl_core/nf-shobjidl_core-iexplorerbrowser-browsetoobject
> 別の検索条件でNavigateしても、ExplorerBrowserは画面更新されないのです。
ExplorerBrowser を介さず、検索した ShellSearchFolder オブジェクトを
直接 For Each 等でループした場合、みかんといちごが同じ結果になりますか?
私もさほど詳しくはありません。
ExplorerBrowser の存在も、豆腐さんの今回の質問を受けて初めて見つけたばかりの情報ですし。
ちなみに ExplorerBrowser コントロールというのは、内部的には
COM の IExplorerBrowser インターフェイスを実装しているようです。
https://learn.microsoft.com/ja-jp/windows/win32/api/shobjidl_core/nn-shobjidl_core-iexplorerbrowser
Navigate メソッドは COM の IExplorerBrowser::BrowseToObject メソッドを内部的に呼んでいますね。
https://learn.microsoft.com/ja-jp/windows/win32/api/shobjidl_core/nf-shobjidl_core-iexplorerbrowser-browsetoobject
> 別の検索条件でNavigateしても、ExplorerBrowserは画面更新されないのです。
ExplorerBrowser を介さず、検索した ShellSearchFolder オブジェクトを
直接 For Each 等でループした場合、みかんといちごが同じ結果になりますか?
Dim pKey = SystemProperties.System.FullText
Dim sco = SearchConditionOperation.WordEqual
Dim sc1 = SearchConditionFactory.CreateLeafCondition(pKey, "みかん", sco)
Dim dir = ShellObject.FromParsingName(searchDir)
Dim ssf As New ShellSearchFolder(sc1, DirectCast(dir, ShellContainer))
Dim sc2 = SearchConditionFactory.CreateLeafCondition(pKey, "いちご", sco)
Dim dir2 = ShellObject.FromParsingName(searchDir)
Dim ssf2 As New ShellSearchFolder(sc2, DirectCast(dir, ShellContainer))
ListBox1.DataSource = Nothing
ListBox2.DataSource = Nothing
ListBox1.DisplayMember = "ParsingName"
ListBox2.DisplayMember = "ParsingName"
ListBox1.DataSource = ssf.ToArray()
ListBox2.DataSource = ssf2.ToArray()
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2023/7/12 16:10:53
>> 別の検索条件でNavigateしても、ExplorerBrowserは画面更新されないのです。
類似情報がありましたので、一応紹介しておきます。
https://teratail.com/questions/7dnj7xj3xxesgf
***** 以下引用 *****
> 連続でTextBoxを使用し違うワードで検索を試したところ、検索結果が変わらないという症状が起きました。
> たまに正しい検索結果が表示されますが、基本的には最後に正しく検索できた結果がそのまま表示されます。
> 検索を行い画面の更新は行われているようなのですが検索結果が前回のまま、と言った感じです。
> 何度か試すとたまに正しく検索結果を表示してくれるということもあり、原因がよくわからず困っております。
-----
> しょーもない解決策として
> 1. Navigateしたいところで実際に検索したいところでなくKnownFolders.Computerにでも飛ばす。
> 2. NavigationCompleteで if (e.NewLocation == (ShellObject)KnownFolders.Computer) だったら実際に検索したいところへNavigate
>
> みたいにむりやり「ほかの場所を選んだ後」状態にすれば動くっちゃ動きます^^;
-----
> コントロールに表示されたファイルをダブルクリックで開く動作を挟んだ後などでも正しく検索してくれますね...謎です。
類似情報がありましたので、一応紹介しておきます。
https://teratail.com/questions/7dnj7xj3xxesgf
***** 以下引用 *****
> 連続でTextBoxを使用し違うワードで検索を試したところ、検索結果が変わらないという症状が起きました。
> たまに正しい検索結果が表示されますが、基本的には最後に正しく検索できた結果がそのまま表示されます。
> 検索を行い画面の更新は行われているようなのですが検索結果が前回のまま、と言った感じです。
> 何度か試すとたまに正しく検索結果を表示してくれるということもあり、原因がよくわからず困っております。
-----
> しょーもない解決策として
> 1. Navigateしたいところで実際に検索したいところでなくKnownFolders.Computerにでも飛ばす。
> 2. NavigationCompleteで if (e.NewLocation == (ShellObject)KnownFolders.Computer) だったら実際に検索したいところへNavigate
>
> みたいにむりやり「ほかの場所を選んだ後」状態にすれば動くっちゃ動きます^^;
-----
> コントロールに表示されたファイルをダブルクリックで開く動作を挟んだ後などでも正しく検索してくれますね...謎です。
投稿者 (削除されました)  ()
投稿日時
2023/7/12 22:38:41
(削除されました)
投稿者 豆腐  (社会人)
投稿日時
2023/7/13 00:17:27
■魔界の仮面弁士様、さすがです。
ご提示のコードで期待通りの結果がListBoxに表示されました。
結果さえ得られればいいので、これで十分です。
結果をファイル経由やクリップボード経由でVBAからも利用できると思います。
■ShellFolderViewを使ったサンプルも、ありがとうございます。
「search-msプロトコル」で検索して、こちらの方法でも検索結果を得られますね。
時間が取れたら、VBA用に移植してみます。
■その後、「WindowsAPICodePack.Shell」を使った「コンテンツ検索」で分かった事があるので、情報共有の為、記しておきます。
Excelファイル(.xlsx)、Wordファイル(.docx)、テキストファイル(.txt)(ANSI/UTF8)、PDFファイル(.pdf)の4形式で実験しました。
(1)「コンテンツ検索時」の「SearchCondition作成用」の「PropertyKey」は
「SystemProperties.System.FullText」か「SystemProperties.System.Search.Contents」が使える。
(2)「SystemProperties.System.FullText」と「SystemProperties.System.Search.Contents」の違いは、
ファイル名+コンテンツを検索対象とする場合=>「SystemProperties.System.FullText」
コンテンツのみを検索対象とする場合=>「SystemProperties.System.Search.Contents」
(3)テキストファイルについてはANSIのみ抽出されUTF8は検索できなかった。
=>何か設定があると思いますが現時点では不明。
(4)「SystemProperties.System.Search.Contents」だと「NotEqual」が使える。
(5)「Equal」「WordEqual」「WordStartsWith」は同じ結果となった。
(6)以下に使用可能な「SearchConditionOperation」との組み合わせを表にしました。
「●」が使用可能な組合せです。
■掲示板ネタで「なるほど、こういう需要もあるな」と思ったので、調査してみました。
元質問とは違う方向へ行きましたが、魔界の仮面弁士様のおかげで、十二分に満足いく成果を得られました。
もうお腹一杯なので、ここらで終わりにしたいと思います。
ご提示のコードで期待通りの結果がListBoxに表示されました。
結果さえ得られればいいので、これで十分です。
結果をファイル経由やクリップボード経由でVBAからも利用できると思います。
■ShellFolderViewを使ったサンプルも、ありがとうございます。
「search-msプロトコル」で検索して、こちらの方法でも検索結果を得られますね。
時間が取れたら、VBA用に移植してみます。
■その後、「WindowsAPICodePack.Shell」を使った「コンテンツ検索」で分かった事があるので、情報共有の為、記しておきます。
Excelファイル(.xlsx)、Wordファイル(.docx)、テキストファイル(.txt)(ANSI/UTF8)、PDFファイル(.pdf)の4形式で実験しました。
(1)「コンテンツ検索時」の「SearchCondition作成用」の「PropertyKey」は
「SystemProperties.System.FullText」か「SystemProperties.System.Search.Contents」が使える。
(2)「SystemProperties.System.FullText」と「SystemProperties.System.Search.Contents」の違いは、
ファイル名+コンテンツを検索対象とする場合=>「SystemProperties.System.FullText」
コンテンツのみを検索対象とする場合=>「SystemProperties.System.Search.Contents」
(3)テキストファイルについてはANSIのみ抽出されUTF8は検索できなかった。
=>何か設定があると思いますが現時点では不明。
(4)「SystemProperties.System.Search.Contents」だと「NotEqual」が使える。
(5)「Equal」「WordEqual」「WordStartsWith」は同じ結果となった。
(6)以下に使用可能な「SearchConditionOperation」との組み合わせを表にしました。
「●」が使用可能な組合せです。
┌──────────────┬────┬────────┐
│ SearchConditionOperation │FullText│Search.Contents │
├──┬───────────┼────┼────────┤
│ 0│Implicit │ │ │
├──┼───────────┼────┼────────┤
│ 1│Equal │ │ ● │
├──┼───────────┼────┼────────┤
│ 2│NotEqual │ │ ● │
├──┼───────────┼────┼────────┤
│ 3│LessThan │ │ │
├──┼───────────┼────┼────────┤
│ 4│GreaterThan │ │ │
├──┼───────────┼────┼────────┤
│ 5│LessThanOrEqual │ │ │
├──┼───────────┼────┼────────┤
│ 6│GreaterThanOrEqual │ │ │
├──┼───────────┼────┼────────┤
│ 7│ValueStartsWith │ │ │
├──┼───────────┼────┼────────┤
│ 8│ValueEndsWith │ │ │
├──┼───────────┼────┼────────┤
│ 9│ValueContains │ │ │
├──┼───────────┼────┼────────┤
│ 10│ValueNotContains │ │ │
├──┼───────────┼────┼────────┤
│ 11│DosWildcards │ │ │
├──┼───────────┼────┼────────┤
│ 12│WordEqual │ ● │ ● │
├──┼───────────┼────┼────────┤
│ 13│WordStartsWith │ ● │ ● │
├──┼───────────┼────┼────────┤
│ 14│ApplicationSpecific │ │ │
└──┴───────────┴────┴────────┘
■掲示板ネタで「なるほど、こういう需要もあるな」と思ったので、調査してみました。
元質問とは違う方向へ行きましたが、魔界の仮面弁士様のおかげで、十二分に満足いく成果を得られました。
もうお腹一杯なので、ここらで終わりにしたいと思います。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2023/7/13 12:34:38
> もうお腹一杯なので、ここらで終わりにしたいと思います。
解決済みということで何よりです。
> 情報共有の為、記しておきます。
Great Job! 👍
> (3)テキストファイルについてはANSIのみ抽出されUTF8は検索できなかった。
BOM なし UTF-8 で試しているのだと思いますが、
BOM あり UTF-8 だとどうでしょう。
あるいは拡張子が .txt ではなく、 .css や .htm や .html の場合はどうでしょうか。
> =>何か設定があると思いますが現時点では不明。
HKEY_CLASSES_ROOT\.txt\PersistentHandler を
{5e941d80-bf96-11cd-b579-08002b30bfeb} から
{eec97550-47a9-11cf-b952-00aa0051fe20} に変更してみるとか?(当方未確認)
前者は Plain Text persistent handler (BOM なし UTF-8 非対応)で、
後者は HTML File persistent handler (BOM なし UTF-8 対応)かと。
https://ascii.jp/elem/000/004/034/4034888/
https://answers.microsoft.com/ja-jp/windows/forum/all/%E3%83%A1%E3%83%A2%E5%B8%B3%E3%81%AEutf/a68fb4fc-390f-4962-ae79-2caddb27bccc
> ご提示のコードで期待通りの結果がListBoxに表示されました。
実際には ListBox に表示せず、For Each して取り出す感じ。
列挙結果は ShellObject 型として取り出されますので、そこから
ParsingName プロパティや Name プロパティでパスやファイル名を得られます。
https://learn.microsoft.com/ja-jp/windows/win32/search/-search-3x-wds-managed-code
解決済みということで何よりです。
> 情報共有の為、記しておきます。
Great Job! 👍
> (3)テキストファイルについてはANSIのみ抽出されUTF8は検索できなかった。
BOM なし UTF-8 で試しているのだと思いますが、
BOM あり UTF-8 だとどうでしょう。
あるいは拡張子が .txt ではなく、 .css や .htm や .html の場合はどうでしょうか。
> =>何か設定があると思いますが現時点では不明。
HKEY_CLASSES_ROOT\.txt\PersistentHandler を
{5e941d80-bf96-11cd-b579-08002b30bfeb} から
{eec97550-47a9-11cf-b952-00aa0051fe20} に変更してみるとか?(当方未確認)
前者は Plain Text persistent handler (BOM なし UTF-8 非対応)で、
後者は HTML File persistent handler (BOM なし UTF-8 対応)かと。
https://ascii.jp/elem/000/004/034/4034888/
https://answers.microsoft.com/ja-jp/windows/forum/all/%E3%83%A1%E3%83%A2%E5%B8%B3%E3%81%AEutf/a68fb4fc-390f-4962-ae79-2caddb27bccc
> ご提示のコードで期待通りの結果がListBoxに表示されました。
実際には ListBox に表示せず、For Each して取り出す感じ。
列挙結果は ShellObject 型として取り出されますので、そこから
ParsingName プロパティや Name プロパティでパスやファイル名を得られます。
https://learn.microsoft.com/ja-jp/windows/win32/search/-search-3x-wds-managed-code
投稿者 豆腐  (社会人)
投稿日時
2023/7/13 21:35:07
魔界の仮面弁士様、解決後にもかかわらず、追伸、ありがとうございます。
>BOM なし UTF-8 で試しているのだと思いますが、
>BOM あり UTF-8 だとどうでしょう。
>あるいは拡張子が .txt ではなく、 .css や .htm や .html の場合はどうでしょうか。
確かに、UTF-8(BOMなし)で実験していました。
UTF-8(BOMあり)で試したら、きちんと検索してくれました。
調べたら、これは一般に知られた不具合みたいですね。Windows11では改善されているのかな?
以下のページで、魔界の仮面弁士様も提示されたレジストリの設定も紹介されていました。
レジストリの変更だけでなく、「インデックスの再構築」も行う必要があるみたいです。
この方は24時間くらいかかったそうですが、自分は約70000個で2時間くらいで終わりました。
設定変更後は、見事「UTF-8(BOMなし)」も検索してくれました。\(^_^)/
又、拡張子を「.css、.htm 、.html」に変えるだけでもOKでした。
https://taka8aru.blogspot.com/2020/11/windows-10notepadutf-8-html.html
>BOM なし UTF-8 で試しているのだと思いますが、
>BOM あり UTF-8 だとどうでしょう。
>あるいは拡張子が .txt ではなく、 .css や .htm や .html の場合はどうでしょうか。
確かに、UTF-8(BOMなし)で実験していました。
UTF-8(BOMあり)で試したら、きちんと検索してくれました。
調べたら、これは一般に知られた不具合みたいですね。Windows11では改善されているのかな?
以下のページで、魔界の仮面弁士様も提示されたレジストリの設定も紹介されていました。
レジストリの変更だけでなく、「インデックスの再構築」も行う必要があるみたいです。
この方は24時間くらいかかったそうですが、自分は約70000個で2時間くらいで終わりました。
設定変更後は、見事「UTF-8(BOMなし)」も検索してくれました。\(^_^)/
又、拡張子を「.css、.htm 、.html」に変えるだけでもOKでした。
https://taka8aru.blogspot.com/2020/11/windows-10notepadutf-8-html.html
■最終目的
「UIAutomationを使ってエクスプローラー右上の検索ボックスに値をセットして検索結果を取得する」
ということが最終目的です。
■現状
現在は以下のコードで、「検索ボックス(SearchEditBox)」の取得まではできていると思っています。
(ValuePatternでSetValueした時、ドロプダウンされる為。失敗する場合もあり、確実性はありません。)
「Inspect」で確認すると、「Hostウインドウ」があり、「SearchTextBox」を取得すればいけるかな?
と思っているのですが、取得方法がわかりません。
■質問
最終目的を達成するには、どうすればいいですか?