Results 1 to 20 of 20

Thread: [VB6] Using Structured Queries to conduct a Windows Search by any property

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    [VB6] Using Structured Queries to conduct a Windows Search by any property

    Structured Queries (ICondition) and the SearchFolderItemFactory


    IMPORTANT: You need an updated version of oleexp.tlb, v4.61 (released on October 3rd, 2019) or higher to run this project.


    So I was looking through some old SDK examples and found a whole new way of automatically conducting very powerful searches through Structured Query objects. This creates a temporary folder of search results you can view in a file browser (like my ucShellBrowse, I'm upgrading that and spun this off as a separate demo) or, as in this demo project, show in an Open File Dialog.

    This search is done by Windows, so if you have search indexing on it will be faster, but I don't and it seems very fast anyway. The primary advantage of this search method is the structured query system allows search by various criteria among any PROPERTYKEY. The Demo shows that basics, name (PKEY_ItemNameDisplay), size (PKEY_Size), date modified (PKEY_DateModified), kind (PKEY_Kind), and attributes (PKEY_FileAttributes).
    Comparing dates with PKEY_DateModified necessitates using the VT_FILETIME type, so this demo shows how to create a PROPVARIANT from a VB User Type. You can use similar techniques for other PROPVARIANT types not well supported in VB.

    One other neat thing in the demo; the file types listed as possible values for 'Kind' vary among Windows versions, so I've created a function to enumerate the ones on the current platform, using IPropertyEnumTypeList.

    Requirements
    Windows 7 or newer
    oleexp.tlb 4.61 or higher (Released 03 Oct 2019).
    oleexp addons mIID.bas and mPKEY.bas: These modules are included in the main oleexp download; add them to your project (and the demo project, they're referenced but not included).

    Code Overview
    Here's the main function:
    Code:
    Private Function ExecFileSearchEx(pLocation As IShellItem, pConditions As ICondition) As IShellItem
    Dim pObjects As IObjectCollection
    Dim ppia As IShellItemArray
    Dim psiSearch As IShellItem
    
    Dim pSearchFact As ISearchFolderItemFactory
    Set pSearchFact = New SearchFolderItemFactory
    
    If CreateSearchLibrary(pObjects) = S_OK Then
        pObjects.AddObject ByVal ObjPtr(pLocation)
        Set ppia = pObjects
        pSearchFact.SetScope ppia
        pSearchFact.SetDisplayName StrPtr("Search Results")
        Dim pCond As ICondition
        If (pConditions Is Nothing) = False Then
            pSearchFact.SetCondition pConditions
            pSearchFact.GetShellItem IID_IShellItem, psiSearch
            If (psiSearch Is Nothing) = False Then
                Set ExecFileSearchEx = psiSearch
            Else
                Debug.Print "ExecFileSearchEx->Failed to get IShellItem from search factory"
            End If
            Set pCond = Nothing
        Else
            Debug.Print "ExecFileSearchEx->Failed to get ICondition"
        End If
        Set ppia = Nothing
    End If
    End Function
    The first thing created is the SearchFolderItemFactory object, which creates the results folder for us.
    The search scope (starting location) is defined by an IShellItemArray, and getting one of those from a single location is a bit tricky... we create an empty IShellLibrary object (like the Documents, Video libraries you see in Explorer), ask for an object collection from that, and add our item; here's the CreateSearchLibrary function referenced above:
    Code:
    Private Function CreateSearchLibrary(pObC As IObjectCollection) As Long
    Set pObC = Nothing
    Dim pLib As IShellLibrary
    Set pLib = New ShellLibrary
    If (pLib Is Nothing) = False Then
        CreateSearchLibrary = pLib.GetFolders(LFF_ALLITEMS, IID_IObjectCollection, pObC)
    Else
        Debug.Print "CreateSearchLibrary->Failed to create ShellLibrary"
    End If
    End Function
    The most difficult part is creating that ICondition object that's passed to ExecFileSearchEx. You create an ICondition object for each search condition (file name contains, size is less than, etc), then merge them into one ICondition object. Here's what that looks like:
    Code:
    Private Function SetConditions(ppCondition As ICondition) As Long
    'Translates the Search settings into an ICondition object
    Set ppCondition = Nothing
    SetConditions = -1
    Dim pFact As IConditionFactory2
    Set pFact = New ConditionFactory
    
    'Conditions demonstrated here:
    Dim pKind As ICondition
    Dim pSize As ICondition
    Dim pName As ICondition
    Dim pDate1 As ICondition
    Dim pAttrib As ICondition
    
    Dim aCds() As ICondition
    ReDim aCds(0)
    Dim nCds As Long
    
    If (pFact Is Nothing) = False Then
        Dim nCOP As CONDITION_OPERATION
        
        'First, set Name
        If Check2.Value = vbChecked Then
            Dim sText As String
            sText = Text1.Text
            If (sText <> "") And (sText <> "*.*") And (sText <> "*") Then 'Only create a condition if it's not 'All Files'
                If InStr(sText, "*") Or InStr(sText, "?") Then
                    nCOP = COP_DOSWILDCARDS
                Else
                    nCOP = COP_VALUE_CONTAINS
                End If
                pFact.CreateStringLeaf PKEY_ItemNameDisplay, nCOP, StrPtr(sText), 0&, CONDITION_CREATION_DEFAULT, IID_ICondition, pName
                Set aCds(nCds) = pName
                nCds = nCds + 1
            End If
        End If
    
        'Exclude folders by looking for the 'D' attribute
        If Check4.Value = vbUnchecked Then
            pFact.CreateStringLeaf PKEY_FileAttributes, COP_VALUE_NOTCONTAINS, StrPtr("D"), 0&, CONDITION_CREATION_DEFAULT, IID_ICondition, pAttrib
            ReDim Preserve aCds(nCds)
            Set aCds(nCds) = pAttrib
            nCds = nCds + 1
        End If
        
        'Set size
        If Check1.Value = vbChecked Then
            Select Case Combo1.ListIndex
                Case 0: nCOP = COP_GREATERTHAN
                Case 1: nCOP = COP_EQUAL
                Case 2: nCOP = COP_LESSTHAN
            End Select
            pFact.CreateIntegerLeaf PKEY_Size, nCOP, CLng(Text3.Text) * 1024, CONDITION_CREATION_DEFAULT, IID_ICondition, pSize
            ReDim Preserve aCds(nCds)
            Set aCds(nCds) = pSize
            nCds = nCds + 1
        End If
        
        'Kind
        If List1.ListIndex > 0 Then
            Dim nKind As Long
            nKind = List1.ListIndex - 1 'ListIndex starts at 0 but we inserted (any) at the start
            pFact.CreateStringLeaf PKEY_Kind, COP_EQUAL, StrPtr(sKindVals(nKind)), 0&, CONDITION_CREATION_DEFAULT, IID_ICondition, pKind
            ReDim Preserve aCds(nCds)
            Set aCds(nCds) = pKind
            nCds = nCds + 1
        End If
    
        'DateTime
        If Check3.Value = vbChecked Then
            Dim stVal As SYSTEMTIME
            Dim dtNew As Date
            Dim ftVal As FILETIME, ftUtc As FILETIME
            Dim vr As Variant
            Dim pkDate As oleexp.PROPERTYKEY
            dtNew = DTPicker1.Value
            Debug.Print "SearchDateTime " & CStr(dtNew)
            stVal = DateToSystemTime(dtNew)
            SystemTimeToFileTime stVal, ftVal
            LocalFileTimeToFileTime ftVal, ftUtc
            InitPropVariantFromFileTime ftUtc, vr
            
            If Option1(0).Value = True Then
                nCOP = COP_LESSTHAN
            Else
                nCOP = COP_GREATERTHAN
            End If
            pFact.CreateLeaf PKEY_DateModified, nCOP, vr, 0&, StrPtr(LOCALE_NAME_USER_DEFAULT), Nothing, Nothing, Nothing, CONDITION_CREATION_DEFAULT, IID_ICondition, pDate1
            If (pDate1 Is Nothing) = False Then
                Debug.Print "SetConditions->Created DT leaf"
                ReDim Preserve aCds(nCds)
                Set aCds(nCds) = pDate1
                nCds = nCds + 1
            Else
                Debug.Print "SetConditions->Failed to create DateTime condition"
            End If
                    
        End If
        ''You can continue to create as many condition leafs with as many Property Keys as you want.
        
        If UBound(aCds) = 0 Then
            'Only one condition, don't need an array
            Set ppCondition = aCds(0)
        Else
            pFact.CreateCompoundFromArray CT_AND_CONDITION, aCds(0), nCds, CONDITION_CREATION_DEFAULT, IID_ICondition, ppCondition
        End If
        If (ppCondition Is Nothing) = False Then SetConditions = S_OK
    
        Set pFact = Nothing
    Else
        Debug.Print "SetConditions->Failed to create factory."
    End If
    End Function
    And that's it, all that's left is calling it and displaying the results:
    Code:
    Private Sub Command1_Click()
    Dim psiLoc As IShellItem
    Dim pCond As ICondition
    oleexp.SHCreateItemFromParsingName StrPtr(Text2.Text), Nothing, IID_IShellItem, psiLoc
    If (psiLoc Is Nothing) = False Then
        If SetConditions(pCond) = S_OK Then
            Set siResultsItem = ExecFileSearchEx(psiLoc, pCond)
            If (siResultsItem Is Nothing) = False Then
                Dim fod As FileOpenDialog
                Set fod = New FileOpenDialog
                fod.SetFolder siResultsItem
                fod.Show Me.hWnd
                'Process a file(s) selected from the results here, or even skip it and enumerate the contents of siResultsItem for the full list
            End If
        End If
    End If
    End Sub
    That displays them in an Open File dialog, but there's any number of possibilities since it creates an actual location. In the upcoming version of my Shell Browser, it's just handed off to the normal load folder routine, the 'Results Folder' gets added to the directory tree under Desktop.
    Attached Files Attached Files
    Last edited by fafalone; Oct 22nd, 2019 at 03:06 AM. Reason: Emphasized need to update oleexp.tlb... there have been a lot more downloads of this project than of the new version, UPDATE!

  2. #2

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    It's not in the Demo project but if you wanted to just get a list of all the results, you'd pass siResultsItem to:
    Code:
    Private Function PrintAllResults(psi As IShellItem)
    Dim lpFile As Long
    Dim pEnum As IEnumShellItems
    Dim siChild As IShellItem
    Dim pc As Long
    
    psi.BindToHandler 0&, BHID_EnumItems, IID_IEnumShellItems, pEnum
    If (pEnum Is Nothing) = False Then
        Do While (pEnum.Next(1&, siChild, pc) = S_OK)
            siChild.GetDisplayName SIGDN_FILESYSPATH, lpFile
            Debug.Print LPWSTRtoStr(lpFile)
        Loop
    End If
    
    End Function

  3. #3
    PowerPoster
    Join Date
    Jan 2020
    Posts
    3,749

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    A window will pop up showing the search results:
    search-ms:displayname=Search%20Results&crumb=名称:~"ole*.tlb"&crumb=location:C%3A%5C

    Sometimes files will be duplicated
    It is also useful to enter the Explorer, which will display the results of files in other non-search directories

  4. #4
    Fanatic Member
    Join Date
    Jan 2015
    Posts
    596

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    Very instructing

    I haven't seen this post in 2019.

    I tested it and it seems faster to search files on a network folder, instead of searching through all folders myself.
    I will replace my search function in my apps using this way.

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    @xiaoyao, if a file exists in more than 1 directory that's search, there's one entry for each.

    C:\dir1\a.txt
    C:\dir1\dir2\a.txt

    if you search dir1 for *.txt, you'll get
    a.txt (Folder=dir1)
    a.txt (Folder=dir2)

    The default dialog includes that 'Folder' column that tells you which folder that match is in.



    @Thierry69, yeah it's good for Networks and other locations besides local, regular file system locations, but what I like most is the ability to search any property.

  6. #6
    Fanatic Member
    Join Date
    Jan 2015
    Posts
    596

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    I just tested locally on my local folder, but it doesn't return all the files to be searched.
    I tried several way, reducing criterias, but no way.

    Any idea?

    NB : No indexing too

  7. #7

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    Could it be shortcut files? I noticed whether it includes the .lnk or not varied against some other search methods I used, returning a different number of results.

  8. #8
    Fanatic Member
    Join Date
    Jan 2015
    Posts
    596

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    They are not shortcut.
    On several thausands of files, it has only found arround 5/6 files, strange

  9. #9
    PowerPoster
    Join Date
    Jan 2020
    Posts
    3,749

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    maybe will also can search by Everything sdk,or search by windows api with createthread
    if we have six driver,use createthread 6 times

  10. #10

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    Quote Originally Posted by Thierry69 View Post
    They are not shortcut.
    On several thausands of files, it has only found arround 5/6 files, strange
    Just running the Demo program? What settings are you using (which params are checked, what values they're using)

    I've never encountered such problem... even use this method for my ShellBrowser control.

  11. #11
    Fanatic Member
    Join Date
    Jan 2015
    Posts
    596

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    Just tried it again.
    Even the windows serach on my computer doesn't return *.pdf files
    (only 1 PDF found)

    so the problem should be on my computer

  12. #12

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    Yeah that's weird, just checked and no trouble on mine. Could they be hidden? I'm not sure it always returns hidden files or looks in hidden folders.

  13. #13
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    832

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    I whatched your sample and built my own out of it with a little different GUI. But that's not the problem.
    The problem is the condition leafes.
    For example I always got ALLFILES and ALLFOLDERS search in the displayname leaf.

    Code:
      If hr = S_OK Then
        ReDim pCondArray(nConds)
        If (sSearchPattern = "*.*") Or (sSearchPattern = "*.") Or (sSearchPattern = " ") Then
          If InStr(1, sSearchPattern, ";", vbTextCompare) Or InStr(1, sSearchPattern, "?", vbTextCompare) Then
            lCondOpFlag = COP_DOSWILDCARDS
          End If
        Else
          lCondOpFlag = COP_VALUE_CONTAINS
        End If
        
        pICondFactory.CreateStringLeaf PKEY_ItemNameDisplay, lCondOpFlag, StrPtr(sSearchPattern), StrPtr(vbNullString), CONDITION_CREATION_DEFAULT, IID__ICondition, pCondString
        Set pCondArray(nConds) = pCondString
        nConds = nConds + 1
    Attached Images Attached Images  

  14. #14

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    Your If blocks make no sense.

  15. #15
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    832

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    I have tried them verus as well…

  16. #16

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    These are mutually exclusive:

    Code:
     If (sSearchPattern = "*.*") Or (sSearchPattern = "*.") Or (sSearchPattern = " ") Then
          If InStr(1, sSearchPattern, ";", vbTextCompare) Or InStr(1, sSearchPattern, "?", vbTextCompare) Then
    If the search is exactly equal to one of the three in the first line, it *can't* match any on the second line. So you wind up with COP_IMPLICIT (0), which obviously doesn't give you what you want.

  17. #17
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    832

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    It doesn't matter...I have tried this leaf both with leaf and out learf and strait with text and I got full search after all.

  18. #18
    PowerPoster
    Join Date
    Jan 2020
    Posts
    3,749

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    Windows Search sometimes fails to handle string case issues. I don't know if you've ever been in this situation.
    The folder is in lowercase, and you can't find the file if you use uppercase to search.

  19. #19
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    832

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    The string leaf is totally useless!!

  20. #20

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,708

    Re: [VB6] Using Structured Queries to conduct a Windows Search by any property

    I'm looking at a bug with certain special folders in certain versions of Windows 10/11, but other than that it works fine for me. (The bug for me is causing match none instead of match all, because it's replacing a disk path with a special folder GUID but not searching it).

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width