(Less Than) Portable Search Folders


Let’s have fun with the object model and search folders:

  1. Outlook running a profile in online mode.
  2. Create this macro:

    Sub TestAdvancedSearchComplete()

        Dim sch As Outlook.Search

        set sch = Application.AdvancedSearch(“Inbox”, “urn:schemas-microsoft-com:office:office#Keywords like ‘Test'”, True, “MySearchFolder”)

        sch.Save (“MySearchFolder”)

    End Sub

  3. Run the macro, observe the folder.
  4. On the same machine, create a new profile in cached mode and start Outlook
  5. Run macro, get “Run-time error ‘-2147219964 (80040604)’: Cannot create folder.”

If we want to have more fun, instead of running the macro the second time, we can locate the folder “MySearchFolder” under the Search Folder node in Outlook. If we click on it, Outlook creates a search folder, but it doesn’t populate with the items we expect to find.

What’s going on here? To answer that, we have to know a bit about how Outlook’s persisted search folder feature works. When you create a search folder, either using the Object Model, or directly in Outlook, the folder isn’t the first thing created. The first thing created is the search folder definition message. This is a message with the message class “IPM.Microsoft.WunderBar.SFInfo” which lives in the associated contents of the Common Views folder. We can use MFCMAPI to take a look at this message:

Our search folder definition message from Common Views

And we can use MFCMAPI’s Smart View feature to parse the search folder definition stored in PR_WB_SF_DEFINITION:

PR_WB_SF_DEFINITION, with parsing courtesy Smart View

From this, we find that the search folder definition contains the restriction that will form the basis of the search folder:

Restriction:
lpRes->rt = RES_CONTENT
lpRes->res.resContent.ulFuzzyLevel = FL_IGNORECASE | FL_FULLSTRING = 0x00010000
lpRes->res.resContent.ulPropTag = 0x8010101F (PT_MV_UNICODE)
lpRes->res.resContent.lpProp->ulPropTag = 0x8010001F (PT_UNICODE)
lpRes->res.resContent.lpProp->Value = Test
  Alt: cb: 8 lpb: 5400650073007400

Note that this restriction includes a property in the 0x8000 range, meaning it’s a named property. In fact, it’s the named property “Keywords” in the PS_PUBLIC_STRINGS namespace. Note also that there’s nothing in the restriction which tells me this. I only know it because I created the search folder and I know what the search folder is supposed to be looking for.

Now – what happens when we go to a different profile looking at the same mailbox? The search folder itself doesn’t get synched back to the server. Only this search folder definition message gets synced. When Outlook encounters this message, it places a dummy node under Search Folders and waits for the user to click on it. If the user does click on it, it builds the search folder using the information from PR_WB_SF_DEFINITION.

And that’s where the problem comes in: Named property mappings are store specific. A named property mapping that’s valid for one store will most likely be invalid for another store. When we switched to cached mode, we’re now working with the OST, not Exchange. Even if there happens to be an 0x8010101F property in this store, it’s not going to be the “Keywords” property. So when we clicked on the folder in Outlook, it created a search folder that searched for a bogus property.

What happened when we ran the macro is even more interesting: Outlook looked at our search string and built a search folder definition message with the appropriate PR_WB_SF_DEFINITION. However, it couldn’t save the message because it already existed, with a different definition!

Why don’t we have a problem when we create search folders directly in Outlook? The Outlook user interface severely restricts the kinds of restrictions you can set up. Because of that, Outlook is able to store the PR_WB_SF_DEFINITION in a different format (using SFST_FILTERSTREAM instead of SFST_MRES). That format is more flexible with named properties, but isn’t capable of storing the wide variety of restrictions that could be specified through the Outlook Object Model.

Workaround

We did look at taking a fix for this, but the limitations of SFST_FILTERSTREAM eliminated it as a possibility for a fix. And the fact that this problem only happens if the restriction contains a named property (IE it won’t repro on a search for subject or recipients) limited the scope of the problem.

However, for the case where the search folder is being created by a macro and we’re getting the “Cannot create folder” error, we found a neat workaround. We can use Outlook’s PropertyAccessor to delete the search folder definition message so it can be recreated. Here’s a sketch of the code:

Sub DeleteSFItem()
    Dim CommonViewsEIDBin As String
    Dim CommonViewsEIDString As String
    Dim CommonViewsFolder As Folder
    Dim ACTable As Table
    Dim oRow As Row
    Dim SFDefinitionEID As String
    Dim SFDefinitionItem As StorageItem
        
    CommonViewsEID = Session.DefaultStore.PropertyAccessor.GetProperty( _
        "http://schemas.microsoft.com/mapi/proptag/0x35E60102")
    CommonViewsEIDString = Session.DefaultStore.PropertyAccessor.BinaryToString( _
        CommonViewsEID)
    Set CommonViewsFolder = Session.GetFolderFromID(CommonViewsEIDString)
    Set ACTable = CommonViewsFolder.GetTable( _
        "[Subject] = 'MySearchFolder'", olHiddenItems)
    
    Set oRow = ACTable.GetNextRow()
    If (Not (oRow Is Nothing)) Then
        SFDefinitionEID = oRow("EntryID")
        Set SFDefinitionItem = Session.GetItemFromID(SFDefinitionEID)
        SFDefinitionItem.Delete
    End If
End Sub

For further reading on the inner workings of search folder definition messages, check out the Exchange Protocol Doc [MS-OSOSRCH].

Comments (5)

  1. So is there a chance of having the SFST_FILTERSTREAM format documented?

    The very real problem I have is that firstly I cannot re-created a search folder with the search definition stored in that format and, secondly, whatever custom search folder I create in my code, teh restriction will not be displayed by OUtlook if the user were to modify teh search folder definition.

    Thanks!

  2. Stephen:

    All for my own personal use, I am an inveterate Word and Outlook VBA amateur programmer, and reasonably accomplished, particularly on the Word side.

    I am stumped, however, by how to delete an existing Outlook search folder programatically (or, in fact, to open it as a folder in an explorer programatically) in Outlook 2003.

    I can enumerate all the search folders in my mailbox’s store (and see that there are numerous folders of the tag form "Search Folder[x]" where [x] is either null or 1 to 24 in addition to the built-in search folders and those I have created, whether manually or through VBA) using CDO and MAPI stores, but for the life of me, I can’t programatically delete the folder or otherwise deal with it.

    Any help would be much appreciated.

    Thanks,

    Nick De Lancie

  3. Nick,

    I haven’t tried to delete the actual search folders, but in this scenario, deleting the folder itself doesn’t help. It’s the folder definition, which is an item, that has to be wiped out.

    Steve

  4. Nick De Lancie says:

    Steve:

    Thank you for your reply.

    I probably should have been more specific before, but I think you almost certainly already know that executing, for a search folder "sfldr" identified by running through the InfoStore of the MAPI Session with the Finder EntryID to enumerate the search folders, the method sfldr.Delete only changes the text of the name of the search folder in the search folders display in the Outlook folder view list to italics, but the search folder is still "there" so that a new programmatic search with the same tag name given fails with "unable to create".

    Since I need to delete the search folder definition item as you note above, do you know how that can programmatically be accessed in Outlook 2003, which does not have the PropertyAccessor and Table capabilities you utilized in your code example?

    Also, what is the programmatic methodology for displaying the found sfldr in a new explorer (so at least I could trap the cannot create folder error and display the existing one rather than deleting and recreating it, although this doesn’t get to the clean-up problem of programmatically deleting multiple created search folders now no longer needed)?

    Again, any help would be much appreciated.

    Thanks,

    Nick

  5. I don’t think there’s a way to delete the item with the Outlook 2003 object model. Of course, if you use MAPI, all things are possible.