検索ダイアログを制御するには?

解決


dlg  2006-01-03 13:45:00  No: 93435

Windows98+VB6で、[スタートメニュー] - [検索] - [ファイルやフォルダ]から表示される検索ダイアログの名前と場所タブ内のコントロールを制御し、検索結果をVB6に表示するものを作成中です。現状は、「名前」,「探す場所」,「サブフォルダも探す」の検索条件を入力し、「検索開始」ボタンを押す。まで、以下の通り作成したんですが、「探す場所」が設定した筈の値に変更されません。
対処法についてアドバイスお願いします。

'// Form1.frm
Option Explicit
Private Sub Command1_Click()
    Call OpenFindFiles95("*.ico", "c:\windows")
End Sub

'// Module1.bas
Option Explicit
Private msClassName As String
Private msWindowName As String
Private msChildClassName As String
Private mnCtrlID As Long

Private Declare Function EnumChildWindows Lib "user32" (ByVal hwndParent As Long, ByVal lpEnumFunc As Long, lParam As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function GetDlgCtrlID Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, lPalam As Any) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) 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 Const WM_SETTEXT As Long = &HC
Private Const BM_CLICK As Long = &HF5
Private Const BM_SETCHECK As Long = &HF1
Private Const BST_UNCHECKED As Long = 0

Public Sub OpenFindFiles95(ByVal sName As String, ByVal sFolder As String, _
                           Optional ByVal fCheckSubFolder As Boolean = True)
    Dim hDlg As Long
    Dim oShell As Object
    Dim hwndCtrl As Long
    Dim dwRetval As Long
    hDlg = FindTopWindow("#32770", "検索")
    If (hDlg = 0) Then
        Set oShell = CreateObject("Shell.Application")
        oShell.FindFiles
        Set oShell = Nothing
        Do
            hDlg = FindTopWindow("#32770", "検索")
        Loop Until (hDlg <> 0)
    End If
    If (hDlg = 0) Then Exit Sub
    '// [名前]の設定
    hwndCtrl = FindChildWindow(hDlg, "Edit", &H3E9)
    dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sName)
    '// [探す場所]の設定
    hwndCtrl = FindChildWindow(hDlg, "Edit", &H3002)
    dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sFolder)
    '// [サブフォルダも探す]チェックの設定
    If (fCheckSubFolder = False) Then
        hwndCtrl = FindChildWindow(hDlg, "Button", &H3711)
        Call SendMessage(hwndCtrl, BM_SETCHECK, BST_UNCHECKED, ByVal 0&)
    End If
    '// [検索開始]ボタンのクリック
    hwndCtrl = FindChildWindow(hDlg, "Button", &H3700)
    Call SendMessage(hwndCtrl, BM_CLICK, 0, ByVal 0&)
End Sub

Public Function FindTopWindow(ByVal sClassName As String, _
                              ByVal sWindowName As String) As Long
    msClassName = sClassName
    msWindowName = sWindowName
    Call EnumWindows(AddressOf EnumWindowsProc, FindTopWindow)
End Function

Private Function EnumWindowsProc(ByVal hwnd As Long, lParam As Long) As Long
    If (StrComp(GetWindowClass(hwnd), msClassName, vbTextCompare) = 0) And _
        (InStr(1, GetWindowTitle(hwnd), msWindowName, vbTextCompare)) Then
        lParam = hwnd
    Else
        EnumWindowsProc = 1
    End If
End Function

Public Function FindChildWindow(ByVal hwndParent As Long, _
                                ByVal sClassName As String, _
                                Optional ByVal nCtrlID As Long = 0) As Long
    msChildClassName = sClassName
    mnCtrlID = nCtrlID
    Call EnumChildWindows(hwndParent, AddressOf EnumChildProc, FindChildWindow)
End Function

Private Function EnumChildProc(ByVal hwnd As Long, lParam As Long) As Long
    If (StrComp(GetWindowClass(hwnd), msChildClassName, vbTextCompare) = 0) And _
        (mnCtrlID = 0 Or (mnCtrlID <> 0 And mnCtrlID = GetDlgCtrlID(hwnd))) Then
        lParam = hwnd
    Else
        EnumChildProc = 1
    End If
End Function

Private Function GetWindowClass(ByVal hwnd As Long) As String
    Dim sBuff As String * 256
    Dim dwRetval As Long
    dwRetval = GetClassName(hwnd, sBuff, Len(sBuff))
    If (dwRetval > 0) Then GetWindowClass = Left$(sBuff, dwRetval)
End Function

Private Function GetWindowTitle(ByVal hwnd As Long) As String
    Dim sBuff As String * 256
    Dim dwRetval As Long
    dwRetval = GetWindowText(hwnd, sBuff, Len(sBuff))
    If (dwRetval > 0) Then GetWindowTitle = Left$(sBuff, dwRetval)
End Function


我龍院忠太  2006-01-03 23:35:55  No: 93436

>「探す場所」が設定した筈の値に変更されません。
これは
>hwndCtrl = FindChildWindow(hDlg, "Edit", &H3002)
>dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sFolder)
これでテキストボックスにsFolderの値がセットされないと言うことですか?
ここまでは正常に処理が行われると解釈していいのですね。


dlg  2006-01-04 03:23:53  No: 93437

回答ありがとうございます。

>>「探す場所」が設定した筈の値に変更されません。
>これは
>>hwndCtrl = FindChildWindow(hDlg, "Edit", &H3002)
>>dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sFolder)
>これでテキストボックスにsFolderの値がセットされないと言うことですか?

その通りです。テキストボックスの表示も内容もデフォルト値のまんまです。
dwRetval = 1 を返しますので、メッセージの送信は成功している様です。

>ここまでは正常に処理が行われると解釈していいのですね。

その通りです。「探す場所」の処理だけがうまくいきません。


我龍院忠太  2006-01-04 07:24:05  No: 93438

>hwndCtrl = FindChildWindow(hDlg, "Edit", &H3002)
このハンドルはSpy++で見た時のハンドルと一致してますか?
私の環境はWin2kですが、Spy++で表示されたハンドルに
SendMessageで文字列を投げて見ましたが、問題なく
受け取りました。


dlg  2006-01-04 14:38:21  No: 93439

>>hwndCtrl = FindChildWindow(hDlg, "Edit", &H3002)
>このハンドルはSpy++で見た時のハンドルと一致してますか?

はい。Spy++で見た時のハンドルと一致します。

あと、以下(1),(3)の様にデフォルト値と変更値の設定を調べてみました。

hwndCtrl = FindChildWindow(hDlg, "Edit", &H3002)
'// (1)先ず、[探す場所]のデフォルト値を取得してみる。
Dim sBuff1 As String
sBuff1 = String$(255, 0)
dwRetval = SendMessage(hwndCtrl, WM_GETTEXT, 255, ByVal sBuff1)
sBuff1 = Left$(sBuff1, InStr(sBuff1, vbNullChar) - 1)
'// (2)[探す場所]の設定値を変更する。
dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sFolder)
'// (3)次に、[探す場所]の変更値を取得してみる。
Dim sBuff2 As String
sBuff2 = String$(255, 0)
dwRetval = SendMessage(hwndCtrl, WM_GETTEXT, 255, ByVal sBuff2)
sBuff2 = Left$(sBuff2, InStr(sBuff2, vbNullChar) - 1)
Debug.Print sBuff1, sBuff2

[結果]
sBuff1 = "" です。EditBoxに表示されいるデフォルト値が取得されません。
sBuff2 = 変更値です。しかし、EditBoxの表示はデフォルト値のまんまです。
これは、どういうことでしょうか


我龍院忠太  2006-01-04 22:55:31  No: 93440

症状から類推するに、違うハンドルをにメッセージを投げているとしか思えない。
Spy++で見ると、"[探す場所]の設定"のEditの親はComboBoxですよね。


我龍院忠太  2006-01-04 23:34:12  No: 93441

とりあえず、私の環境では
'// [探す場所]の設定
hwndCtrl = FindChildWindow(hDlg, "ComboBox", &H3EB)
hwndCtrl = FindChildWindow(hwndCtrl, "Edit", &H3EB)
dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sFolder)
これで設定が出来ました。


dlg  2006-01-05 03:11:10  No: 93442

> 違うハンドルをにメッセージを投げているとしか思えない。

間違い無いと思います。前にも書いた通り、Spy++で見た時のハンドルと一致します。

> Spy++で見ると、"[探す場所]の設定"のEditの親はComboBoxですよね。

ウィンドウの階層構造については把握しているつもりです。
Win2Kの環境が無いので、確認できませんが、以下にウィンドウの階層構造が載っていました。
http://homepage2.nifty.com/Mr_XRAY/Halbow/VCL08.html#VChap8-6

Win98の場合、[探す場所]の親子関係を調べた結果をクラス名で表わすと、以下の様になると思います。
#32770  
  +-  #32770
        +-  ComboBoxEx32
                +-  ComboBox 
                        +-  Edit

FindChildWindow関数は、子供のウィンドウハンドルだけではなく、その階層下にある全てのウィンドウハンドルを検索する様に作ったつもりです。なので、Win2Kの場合、
> hwndCtrl = FindChildWindow(hDlg, "ComboBox", &H3EB)
> hwndCtrl = FindChildWindow(hwndCtrl, "Edit", &H3EB)
のhwndCtrlは、
hwndCtrl = FindChildWindow(hDlg, "Edit", &H3EB)
のhwndCtrlと同じになりませんか?

どうやら、Win2Kでは現象が再現されない様ですね。
本件に関わる現象について、引き続きアドバイスお願いします。


我龍院忠太  2006-01-05 06:08:06  No: 93443

>どうやら、Win2Kでは現象が再現されない様ですね。
再現の意味が、ComboBoxのテキストに"c:\windows"が設定されないと言う
意味なら、再現してます。
>> hwndCtrl = FindChildWindow(hDlg, "ComboBox", &H3EB)
>> hwndCtrl = FindChildWindow(hwndCtrl, "Edit", &H3EB)
>のhwndCtrlは、
>hwndCtrl = FindChildWindow(hDlg, "Edit", &H3EB)
>のhwndCtrlと同じになりませんか?
ならないでしょ。
hwndCtrl = FindChildWindow(hDlg, "ComboBox", &H3EB)
でComboBoxのハンドルを取得して、今度はそれを親として
hwndCtrl = FindChildWindow(hwndCtrl, "Edit", &H3EB)
Editのハンドルを取得してます。

それと所々コードを修正してます、たとえば、
>Set oShell = CreateObject("Shell.Application")
>oShell.FindFiles
>Set oShell = Nothing
>Do
これはちょっとタイミング的に苦しいのでは、
Set oShell = Nothing
Sleep (500)
DoEvents
Do
このくらいの余裕は必要でしょう。
それと
dwRetval = GetClassName(hwnd, sBuff, Len(sBuff))
If (dwRetval > 0) Then GetWindowClass = Left$(sBuff, dwRetval)
これは変だよね、"検索"の場合dwRetvalに4が返って来ませんか。
もっとも私の場合は"検索"では無く"検索結果"に変えてますが。
それとも後から下の方式に変えたのですか?
>Left$(sBuff, InStr(sBuff, vbNullChar) - 1)
Windows98+VB6の環境は今となってはレアな環境なんで、その環境で
再現するのは難しいけれど、環境の為にうまくいかないわけでも
なさそうな気がするが、がんばってください。


dlg  2006-01-05 10:25:02  No: 93444

> 再現の意味が、ComboBoxのテキストに"c:\windows"が設定されないと言う
> 意味なら、再現してます。

ん?

>> とりあえず、私の環境では
>> '// [探す場所]の設定
>> hwndCtrl = FindChildWindow(hDlg, "ComboBox", &H3EB)
>> hwndCtrl = FindChildWindow(hwndCtrl, "Edit", &H3EB)
>> dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sFolder)
>>これで設定が出来ました。

「これで設定が出来ました。」の意味は何ですか?
ハンドルが正しければ、ComboBoxのテキストに"c:\windows"が設定されると言うことですか?

>>hwndCtrl = FindChildWindow(hDlg, "Edit", &H3EB)
>>のhwndCtrlと同じになりませんか?
>ならないでしょ。

ここに相違が生じると、議論が噛み合わないので、「ならない」理由を教えて下さい。当方では、実際に試しても同じになりました。理由は以下の通りです。
FindChildWindow関数は、EnumChildWindows APIを使用しています。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/jpwinui/html/_win32_enumchildwindows.asp
に、「子ウィンドウがさらに子ウィンドウを持つ場合、EnumChildWindows 関数はそれらのウィンドウも列挙します。」と解説されています。
Win2Kの場合、[探す場所]のウィンドウ階層は、以下の様になると思います。
CabinetWClass
    BaseBar
        ReBarWindow32
            ShellFileSearchControl
                #32770
                    ComboBoxEx32
                        ComboBox
                            Edit
hDlgがCabinetWClassで、hDlgを親とすれば、ComboBoxは6階層下の子ウィンドウで、Editは7階層下の子ウィンドウですよね。つまり、
hwndCtrl = FindChildWindow(hDlg, "ComboBox", &H3EB)
がOKで、
hwndCtrl = FindChildWindow(hDlg, "Edit", &H3EB)
がNGというのは理解できません。

>dwRetval = GetClassName(hwnd, sBuff, Len(sBuff))
>If (dwRetval > 0) Then GetWindowClass = Left$(sBuff, dwRetval)
>これは変だよね、"検索"の場合dwRetvalに4が返って来ませんか。
>それとも後から下の方式に変えたのですか?
>>Left$(sBuff, InStr(sBuff, vbNullChar) - 1)

GetWindowTextですね。既に修正しています。このままでも、
InStr(1, GetWindowTitle(hwnd), m_sWindowName, vbTextCompare)
で絞り込むので、影響ないとは思いますが、修正するのが筋ですね。


我龍院忠太  2006-01-05 18:13:51  No: 93445

とりあえずwin2kで動作したコードを載せて置きます。
面倒なので"[サブフォルダも探す]チェックの設定"は省略
もうこれ以上レス付け気は有りませんので、ご自分でデバッグして
下さい。

'// Form1.frm
Option Explicit
Private Sub Command1_Click()
    Call OpenFindFiles95("*.ico", "c:\winnt")
End Sub

'// Module1.bas
Public Sub OpenFindFiles95(ByVal sName As String, ByVal sFolder As String, _
                           Optional ByVal fCheckSubFolder As Boolean = True)
    Dim hDlg As Long
    Dim oShell As Object
    Dim hwndCtrl As Long
    Dim dwRetval As Long
    Dim strClassName As String
    Dim strText As String
    Dim intCount As Integer
    Const IdFileFolder = &H3E9
    Const IdComboBox = &H3EB
    Const IdButton = &H3EC
    strClassName = "CabinetWClass"
    strText = "検索結果"
    hDlg = FindTopWindow(strClassName, strText)
    If (hDlg = 0) Then
        Set oShell = CreateObject("Shell.Application")
        oShell.FindFiles
        Set oShell = Nothing
        Do
            hDlg = FindTopWindow(strClassName, strText)
            Sleep (1)
            DoEvents
            intCount = intCount + 1
            If intCount > 1000 Then
                MsgBox "探せません"
                Exit Sub
            End If
        Loop Until (hDlg <> 0)
    End If
    intCount = 0
    Do
        '// [名前]の設定
        hwndCtrl = FindChildWindow(hDlg, "Edit", IdFileFolder)
        dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sName)
        '// [探す場所]の設定
        hwndCtrl = FindChildWindow(hDlg, "ComboBox", IdComboBox)
        hwndCtrl = FindChildWindow(hwndCtrl, "Edit", IdComboBox)
        DoEvents
        Sleep (1)
        intCount = intCount + 1
        If intCount > 1000 Then
            MsgBox "探せません"
            Exit Sub
        End If
    Loop While (hwndCtrl = 0)
    dwRetval = SendMessage(hwndCtrl, WM_SETTEXT, 0, ByVal sFolder)
    '// [検索開始]ボタンのクリック
    hwndCtrl = FindChildWindow(hDlg, "Button", IdButton)
    Call SendMessage(hwndCtrl, BM_CLICK, 0, ByVal 0&)
End Sub


dlg  2006-01-05 19:40:51  No: 93446

サンプルありがとうございます。
参考に試行錯誤してみます。
#少しツッコミ過ぎたことを反省。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加