locked
Using quick search and user-defined properties RRS feed

  • Question

  • I have a C++ COM add-in that uses MAPI and OO to conditionally mark (tag) emails. The idea is to automatically tag emails and then use the tag value as a search key to find email.

    What I'm experiencing is the quick search does not find the email using tag values. Advance search doesn't either. In advanced search the custom field in "User Defined Fields in Folder" was selected and I built a query and there were no results. All of the email I'm searching is 'old' or server-side.

    In the Outlook view, I can see the tag names are getting set for the emails. What do I need to do in order to search on tag values?

    STDMETHODIMP CPropertyMarker::SetCustomProperty(LPMESSAGE lpMessage, wstring propVal) {
        HRESULT hRes = E_FAIL;
        try {
            LPSPropValue lpProp                     = {0};
    
            MAPIAllocateBuffer(sizeof(_SPropValue), (LPVOID*)&lpProp);
            if(NULL == lpProp) 
                return hRes;
                                
            MAPIAllocateMore(propVal.length() * sizeof(WCHAR) + 1, (LPVOID*)lpProp, (LPVOID*)&lpProp->Value.lpszW);
            if(NULL == lpProp->Value.lpszW) {
                MAPIFreeBuffer(&lpProp);
                return hRes;
            }
    
            StringCchCopy(lpProp->Value.lpszW, propVal.length() + 1, propVal.c_str());
    
            lpProp->ulPropTag   = m_ulPropTag;                            
    
            hRes = lpMessage->SetProps(1, lpProp, NULL);
    
            if(SUCCEEDED(hRes)) {
                hRes = lpMessage->SaveChanges(MAPI_DEFERRED_ERRORS);
                if(SUCCEEDED(hRes)) 
                    OutputDebugString(wstring(L"property changed to: " + propVal + L"\n").c_str());
            }
           
            MAPIFreeBuffer(&lpProp);
        }
        catch(exception){}
        return hRes;
    }

    Saturday, December 19, 2015 7:43 PM

Answers

  • Joel,

    The following code uses Advanced Search to find all messages in the Inbox that contain the text "Bozo" in the User defined property named "Clown".

    STDMETHODIMP CAddin::AdvSearch(IDispatch* pControl)
    {
    	HRESULT hr = E_FAIL;
    
    	CComBSTR bstrFilter(L"\"http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/Clown\" LIKE '%Bozo%'");
    
    	CComBSTR  bstrScope(L"'\\\\Optimum\\Inbox'");
    
    	CComPtr<Outlook::Search> pSearch;
    
    	hr = m_pApp->AdvancedSearch(bstrScope, CComVariant(bstrFilter), CComVariant(VARIANT_FALSE), CComVariant(L"AdvSearch"), &pSearch);
    
    	return S_OK;
    }
    

    The search is asynchronous and the results are delivered to the OnSearchComplete handler -

    STDMETHODIMP CAddin::OnSearchComplete( Outlook::Search *SearchObject ) { CComBSTR Tag; HRESULT hr; long lFound = 0; CComPtr<Outlook::_Results> pResults;

    hr = SearchObject->get_Tag( &Tag ); if (Tag == L"AdvSearch") { hr = SearchObject->get_Results(&pResults); if (pResults) { hr = pResults->get_Count(&lFound); ATLTRACE("Found %d Messages in Inbox", lFound); } } return S_OK; }



    • Edited by RLWA32 Saturday, December 19, 2015 9:22 PM
    • Marked as answer by Joel_Z Saturday, December 19, 2015 11:12 PM
    Saturday, December 19, 2015 9:20 PM
  • Joel,

    The code for the restriction needs to be changed.  Since the FromCloud property contains text and you are searching for the tag "Automotive" the Restriction needs to be RES_CONTENT, not RES_PROPERTY. I also think it would be more efficient if you applied the restriction to the contents table rather than using FindRows.  I'll post some sample code later.

    Update -

    Here's the sample.  As usual error checking is nonexistent.

    STDMETHODIMP CAddin::GetRestrictedContents(IDispatch* pDummy)
    {
    	HRESULT	hr = E_FAIL;
    	CComPtr<Outlook::_Explorer> pExp;
    	CComPtr<IUnknown> pUnk;
    	CComQIPtr<IMAPIFolder, &IID_IMAPIFolder> pFolder;
    	CComPtr<Outlook::MAPIFolder> polFolder;
    	CComPtr<IMAPITable> pContents;
    	SRestriction res = { 0 };
    	SPropValue sProp = { 0 };
    	LPSPropTagArray lpPropTag = NULL;
    	MAPINAMEID	mID = { 0 };
    	LPMAPINAMEID lpID = &mID;
    	ULONG cV = 0, nRows = 0;
    
    	mID.ulKind = MNID_STRING;
    	mID.lpguid = const_cast<LPGUID>(&PS_PUBLIC_STRINGS);
    	mID.Kind.lpwstrName = L"FromCloud";
    
    	hr = m_pApp->ActiveExplorer(&pExp);
    	hr = pExp->get_CurrentFolder(&polFolder);
    	hr = polFolder->get_MAPIOBJECT(&pUnk);
    	pFolder = pUnk;
    	hr = pFolder->GetContentsTable(MAPI_UNICODE, &pContents);
    	hr = pFolder->GetIDsFromNames(1, &lpID, 0, &lpPropTag);
    	res.rt = RES_CONTENT;
    	res.res.resContent.ulPropTag = CHANGE_PROP_TYPE(lpPropTag->aulPropTag[0], PT_UNICODE);
    	res.res.resContent.lpProp = &sProp;
    	res.res.resContent.ulFuzzyLevel = FL_FULLSTRING | FL_IGNORECASE;
    	sProp.ulPropTag =CHANGE_PROP_TYPE(lpPropTag->aulPropTag[0], PT_UNICODE);
    	sProp.Value.lpszW = L"Automotive";
    	hr = pContents->GetRowCount(0, &nRows);
    	ATLTRACE("Number of Rows in Contents table before restriction: %d\n", nRows);
    	hr = pContents->Restrict(&res, 0);
    	hr = pContents->GetRowCount(0, &nRows);
    	ATLTRACE("Number of Rows in Contents table after restriction: %d\n", nRows);
    	MAPIFREEBUFFER(lpPropTag);
    
    	return S_OK;
    }
    

    • Edited by RLWA32 Wednesday, December 23, 2015 11:35 AM
    • Marked as answer by Joel_Z Tuesday, December 29, 2015 12:36 AM
    Wednesday, December 23, 2015 10:49 AM

All replies

  • Joel,

    The following code uses Advanced Search to find all messages in the Inbox that contain the text "Bozo" in the User defined property named "Clown".

    STDMETHODIMP CAddin::AdvSearch(IDispatch* pControl)
    {
    	HRESULT hr = E_FAIL;
    
    	CComBSTR bstrFilter(L"\"http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/Clown\" LIKE '%Bozo%'");
    
    	CComBSTR  bstrScope(L"'\\\\Optimum\\Inbox'");
    
    	CComPtr<Outlook::Search> pSearch;
    
    	hr = m_pApp->AdvancedSearch(bstrScope, CComVariant(bstrFilter), CComVariant(VARIANT_FALSE), CComVariant(L"AdvSearch"), &pSearch);
    
    	return S_OK;
    }
    

    The search is asynchronous and the results are delivered to the OnSearchComplete handler -

    STDMETHODIMP CAddin::OnSearchComplete( Outlook::Search *SearchObject ) { CComBSTR Tag; HRESULT hr; long lFound = 0; CComPtr<Outlook::_Results> pResults;

    hr = SearchObject->get_Tag( &Tag ); if (Tag == L"AdvSearch") { hr = SearchObject->get_Results(&pResults); if (pResults) { hr = pResults->get_Count(&lFound); ATLTRACE("Found %d Messages in Inbox", lFound); } } return S_OK; }



    • Edited by RLWA32 Saturday, December 19, 2015 9:22 PM
    • Marked as answer by Joel_Z Saturday, December 19, 2015 11:12 PM
    Saturday, December 19, 2015 9:20 PM
  • Joel,

    The following code uses Advanced Search to find all messages in the Inbox that contain the text "Bozo" in the User defined property named "Clown".

    STDMETHODIMP CAddin::AdvSearch(IDispatch* pControl)
    {
    	HRESULT hr = E_FAIL;
    
    	CComBSTR bstrFilter(L"\"http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/Clown\" LIKE '%Bozo%'");
    
    	CComBSTR  bstrScope(L"'\\\\Optimum\\Inbox'");
    
    	CComPtr<Outlook::Search> pSearch;
    
    	hr = m_pApp->AdvancedSearch(bstrScope, CComVariant(bstrFilter), CComVariant(VARIANT_FALSE), CComVariant(L"AdvSearch"), &pSearch);
    
    	return S_OK;
    }
    

    The search is asynchronous and the results are delivered to the OnSearchComplete handler -

    STDMETHODIMP CAddin::OnSearchComplete( Outlook::Search *SearchObject ) { CComBSTR Tag; HRESULT hr; long lFound = 0; CComPtr<Outlook::_Results> pResults;

    hr = SearchObject->get_Tag( &Tag ); if (Tag == L"AdvSearch") { hr = SearchObject->get_Results(&pResults); if (pResults) { hr = pResults->get_Count(&lFound); ATLTRACE("Found %d Messages in Inbox", lFound); } } return S_OK; }




    It sounds like using the Outlook UI and the quick search textbox won't work to search on User-Defined properties by design and we need to implement our own search to specify the User-Defined property?

    Saturday, December 19, 2015 11:16 PM
  • When I read what you were trying to do I figured you were looking for a way to search using code in your add-in.  I never even tried to search for a user defined property in the UI.
    Saturday, December 19, 2015 11:53 PM
  • When I read what you were trying to do I figured you were looking for a way to search using code in your add-in.  I never even tried to search for a user defined property in the UI.
    I think searching using the Outlook UI should work, especially with the Advanced Search. With advanced search, we can specify the user defined property and operators for the value such as Contains, Exactly. But it is not working in that no results get returned. I'm at a loss trying to get it to work. I thought it did work at one point but so many changes have been made I may have been mistaking or regressed. 
    Sunday, December 20, 2015 1:19 AM
  • The Advanced Find dialog allows you to specify a user defined field and enter search criteria.  For example in Outlook 2013 --

    and

    Sunday, December 20, 2015 1:42 AM
  • Yes, exactly. That's what I'm referring to that isn't yielding results when I build criteria having the user defined field FromCloud. I thought I am setting the property wrong which is causing no results to be returned or some other thing I'm missing.

    Sorry I wasn't clear enough in my first post. Any ideas on how I could investigate this?

    Sunday, December 20, 2015 2:19 PM
  • What kind of property is your user defined field?  Text? numeric?  Are you using the right criteria in your search?  More information would be helpful.

    I also don't know why (in the initial posting) you are using MAPI code to deal with user defined properties.  They are exposed by the OOM and I think that's the best way to manipulate them.

    Sunday, December 20, 2015 2:32 PM
  • What kind of property is your user defined field?  Text? numeric?  Are you using the right criteria in your search?  More information would be helpful.

    I also don't know why (in the initial posting) you are using MAPI code to deal with user defined properties.  They are exposed by the OOM and I think that's the best way to manipulate them.

    The user defined field is text/unicode. How the property is created is shown in the snippet below. Does it look correct?

    In regards to search criteria, in the quick search I type in Automotive and think all email with the FromCloud user defined property set to Automotive would be returned. I tried specifying the field name and the search key like FromCloud:Automotive, but that didn't return email tagged automotive.

    The same expectation with advanced search, but the difference is going through more steps having to explicitly select the user defined property, choose an operator such as 'Contains' and then for the search key Automotive. Then add the criteria, select the inbox folder to search in and click Find Now. 

    In the Outlook view, the user defined property value of email is correctly displayed. There are emails visible with the Automotive value being displayed in the FromCloud column.

    MAPI code is being used because the requirement is to tag email that is old or server side while in cached mode. According to msdn docs, to access remote mail OpenMsgStore should have the MDB_ONLINE flag used.

    STDMETHODIMP CPropertyMarker::InitUserDefinedProperty() {
        HRESULT hRes  = E_FAIL;
        try {
            GUID pubGuid                = {0x00020329, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; // PS_PUBLIC_STRINGS
            MAPINAMEID nameId           = {0};
            nameId.Kind.lpwstrName      = L"FromCloud";
            nameId.lpguid               = &pubGuid;
            nameId.ulKind               = MNID_STRING;
            MAPINAMEID *rgpnameId[1]    = {&nameId};
            if(NULL == m_pMDB)
                return hRes;
            hRes = m_pMDB->GetIDsFromNames(1, rgpnameId, MAPI_CREATE, &m_lpSPropTags);
            if(SUCCEEDED(hRes)) {
                m_ulPropTag = m_lpSPropTags->aulPropTag[0];
                m_ulPropTag = PROP_TAG(PT_UNICODE, PROP_ID(m_ulPropTag)); 
            }
        }
        catch(exception){}
        return hRes;
    }



    The function below is called to set the user defined property

    STDMETHODIMP CPropertyMarker::SetCustomProperty(LPMESSAGE lpMessage, wstring propVal) {
        HRESULT hRes = E_FAIL;
        try {
            LPSPropValue lpProp                     = {0};
    
            MAPIAllocateBuffer(sizeof(_SPropValue), (LPVOID*)&lpProp);
            if(NULL == lpProp) 
                return hRes;
                                
            MAPIAllocateMore(propVal.length() * sizeof(WCHAR) + 1, (LPVOID*)lpProp, (LPVOID*)&lpProp->Value.lpszW);
            if(NULL == lpProp->Value.lpszW) {
                MAPIFreeBuffer(&lpProp);
                return hRes;
            }
    
            StringCchCopy(lpProp->Value.lpszW, propVal.length() + 1, propVal.c_str());
    
            lpProp->ulPropTag   = m_ulPropTag;                            
    
            hRes = lpMessage->SetProps(1, lpProp, NULL);
    
            if(SUCCEEDED(hRes)) {
                hRes = lpMessage->SaveChanges(MAPI_DEFERRED_ERRORS);
                if(SUCCEEDED(hRes)) 
                    OutputDebugString(wstring(L"property changed to: " + propVal + L"\n").c_str());
            }
           
            MAPIFreeBuffer(&lpProp);
        }
        catch(exception){}
        return hRes;
    }


    • Edited by Joel_Z Sunday, December 20, 2015 5:22 PM
    Sunday, December 20, 2015 5:16 PM
  • MAPIAllocateMore(propVal.length() * sizeof(WCHAR) + 1, (LPVOID*)lpProp, (LPVOID*)&lpProp->Value.lpszW);
    
    Just one quick observation.  I think that your allocation is one byte short.  It should be (propVal.length() +1)*sizeof(WCHAR).  Consequently, you're giving the wrong buffer size to the unicode version of StringCchCopy
    • Edited by RLWA32 Sunday, December 20, 2015 6:45 PM
    Sunday, December 20, 2015 6:45 PM
  • MAPIAllocateMore(propVal.length() * sizeof(WCHAR) + 1, (LPVOID*)lpProp, (LPVOID*)&lpProp->Value.lpszW);
    Just one quick observation.  I think that your allocation is one byte short.  It should be (propVal.length() +1)*sizeof(WCHAR).  Consequently, you're giving the wrong buffer size to the unicode version of StringCchCopy

    Thanks for spotting that, RLWA32. I'm going to fix that and re-tag to see if maybe that has an affect on the search results. I think that error may exclude the NULL terminator on the string. Certainly a problem!
    • Edited by Joel_Z Sunday, December 20, 2015 6:52 PM
    Sunday, December 20, 2015 6:50 PM
  • That off-by-one bug was a good spot and needed to be fixed. As you may have guessed, that didn't fix the search problem.

    A month ago I tested a prototype app that does this same user-defined column scenario with advanced find and it worked, so this problem where results weren't being returned was baffling. What I just discovered was quick search and advanced find don't search remote mail.  I must have had the Outlook profile to have cached mode off or to download all email local with the prototype app. I just turned off cached mode and now advanced find is working as expected returning expected results (FromCloud user defined column containing Automotive).

    I still need to get this to work somehow without touching the cached mode user settings leaving the setting default (cached mode on). Any ideas on how to get this to work? I was thinking to add another custom textbox on a ribbon tab and use that instead of quick search. Then do a MAPI query and display the results in the current view like quick search does. Could that work?


    • Edited by Joel_Z Sunday, December 20, 2015 8:20 PM
    Sunday, December 20, 2015 8:11 PM
  • According to Open a store on the remote server when outlook is in cached mode you will have to create a new MAPI Session to open a remote Exchange store if Outlook has already opened that store in cached mode.

    How do you propose to do a "MAPI query"?  Will you accomplish this by implementing a Restriction on a folder's contents table?  Or FindRow()?  What will be the performance impact on the server?  Will the results of your search of the remote store return read-only results to the user?  Even if you construct a UI to return your search results what will a user do with them?  The remote store is being handled in a separate MAPI session by your add-in, not by Outlook.  And I haven't got the foggiest notion of how Outlook would perform in this non-standard environment.  In a nutshell I don't have any experience navigating the waters you're about to jump into.

    Sunday, December 20, 2015 8:46 PM
  • According to Open a store on the remote server when outlook is in cached mode you will have to create a new MAPI Session to open a remote Exchange store if Outlook has already opened that store in cached mode.

    How do you propose to do a "MAPI query"?  Will you accomplish this by implementing a Restriction on a folder's contents table?  Or FindRow()?  What will be the performance impact on the server?  Will the results of your search of the remote store return read-only results to the user?  Even if you construct a UI to return your search results what will a user do with them?  The remote store is being handled in a separate MAPI session by your add-in, not by Outlook.  And I haven't got the foggiest notion of how Outlook would perform in this non-standard environment.  In a nutshell I don't have any experience navigating the waters you're about to jump into.

    I appreciate you helping me think this through, RLWA32. Implementing this sounds difficult and a lot more work if I could even do it.

    I'm already creating a new connection to update email in the exiting view. It works nicely. Outlook refreshes the email items dynamically/live right after setting the user-defined property and saving from a totally separate MAPI connection that was created in a Win32 thread.

    I'm pretty intimidated by this concept of creating a separate search (filter?). The results would need to be loaded into a new pane/Explorer to have separation between views. I may be in over my head in a few ways such as the amount of time I've spend on this and how long the client has waited.

    I think you have some great questions. Thank you!

    Sunday, December 20, 2015 10:44 PM
  • As I thought some more about the situation it occurred to me that you could pass the EntryId values for any search results to code in your addin that could use the Outlook namespace method GetitemFromID.  My thought is that would return an Outlook object that could be manipulated without causing a problem.  Of course I'm just speculating...it would need to be tested.
    Monday, December 21, 2015 1:05 AM
  • As I thought some more about the situation it occurred to me that you could pass the EntryId values for any search results to code in your addin that could use the Outlook namespace method GetitemFromID.  My thought is that would return an Outlook object that could be manipulated without causing a problem.  Of course I'm just speculating...it would need to be tested.

    Nice. Getting the email reference by way of the OO after identifying what email is needed via MAPI. That solves a big chunk of the problem. If during testing it doesn't work, I'm closer to finding what will work by knowing what doesn't work :).

    Monday, December 21, 2015 3:13 PM
  • I'm looking forward to reading the results of the experiment. :)
    Monday, December 21, 2015 3:56 PM
  • FindRow() and Restrict() return INVALIDARG. If I spend more time on it and get either call to work (return S_OK), I'll post back.
    Tuesday, December 22, 2015 11:36 PM
  • FindRow() and Restrict() return INVALIDARG. If I spend more time on it and get either call to work (return S_OK), I'll post back.

    There's not much to say to this beyond the usual suggestion that you post the code so we can take a look.
    Tuesday, December 22, 2015 11:44 PM
  • FindRow() and Restrict() return INVALIDARG. If I spend more time on it and get either call to work (return S_OK), I'll post back.


    There's not much to say to this beyond the usual suggestion that you post the code so we can take a look.

    I wanted to paste it but wasn't sure about the direction of the project. Regardless of that, it would still be fun to get it working. 

    MAPIInitialize() has been called from the thread that executes these functions.

    I also tried to use Restrict() and then QueryRows(), but Restrict() returns the same E_INVALIDARG error code. FindRow() I think is the better function for this.

    In regards to the return error code, I found a web page were a MS employee (Hui Chen) stated Restrict() won't work on user-defined properties and the MSDN documentation is wrong. However, I don't know if that comment is relevant today as the thread was dated 2007 and is the Pocket PC platform.

    void EmailSearch::Find(LPMAPITABLE lpContentsTable, ULONG ulPropTag, wstring searchKey) {
        try {
            HRESULT hRes            = E_FAIL;
            LPSRowSet pRows = NULL;
            ULONG lRowCount          = 0;
    
            CPropertyMarker propMkr;
            SRestriction* mailFilter             = new SRestriction;
            _SPropertyRestriction propFilter    = {0};
            propFilter.relop                    = RELOP_EQ;
            propFilter.lpProp                   = propMkr.GetMapiProperty(searchKey);
            propFilter.ulPropTag                = ulPropTag;
            mailFilter->res.resProperty         = propFilter;
            mailFilter->rt                      = RES_PROPERTY ;
            if(NULL == lpContentsTable || NULL == propFilter.lpProp) 
                return;
            hRes = lpContentsTable->FindRow(mailFilter, BOOKMARK_CURRENT, NULL);
    
    // remaining function code snipped. I see now delete should be called on mailFilter before returning
    }
    LPSPropValue CPropertyMarker::GetMapiProperty(wstring propVal) {
        LPSPropValue lpProp = {0};
        try {
            MAPIAllocateBuffer(sizeof(_SPropValue), (LPVOID*)&lpProp);
            if(NULL == lpProp)
                return NULL;
    
            MAPIAllocateMore((propVal.length() + 1) * sizeof(WCHAR), (LPVOID*)lpProp, (LPVOID*)&lpProp->Value.lpszW);
            if(NULL == lpProp->Value.lpszW) {
                MAPIFreeBuffer(&lpProp);
                return NULL;
            }
            StringCchCopy(lpProp->Value.lpszW, propVal.length() + 1, propVal.c_str());
            lpProp->ulPropTag   = m_ulPropTag;                            
        }
        catch(exception){}
        return lpProp;
    }

    STDMETHODIMP CPropertyMarker::InitUserDefinedProperty() {
        HRESULT hRes  = E_FAIL;
        try {
            GUID pubGuid                = {0x00020329, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; // PS_PUBLIC_STRINGS
            MAPINAMEID nameId           = {0};
            nameId.Kind.lpwstrName      = L"FromCloud";
            nameId.lpguid               = &pubGuid;
            nameId.ulKind               = MNID_STRING;
            MAPINAMEID *rgpnameId[1]    = {&nameId};
            if(NULL == m_pMDB)
                return hRes;
            hRes = m_pMDB->GetIDsFromNames(1, rgpnameId, MAPI_CREATE, &m_lpSPropTags);
            if(SUCCEEDED(hRes)) {
                m_ulPropTag = m_lpSPropTags->aulPropTag[0];
                m_ulPropTag = PROP_TAG(PT_UNICODE, PROP_ID(m_ulPropTag)); 
            }
        }
        catch(exception){}
        return hRes;
    }


    Wednesday, December 23, 2015 12:29 AM
  • Joel,

    The code for the restriction needs to be changed.  Since the FromCloud property contains text and you are searching for the tag "Automotive" the Restriction needs to be RES_CONTENT, not RES_PROPERTY. I also think it would be more efficient if you applied the restriction to the contents table rather than using FindRows.  I'll post some sample code later.

    Update -

    Here's the sample.  As usual error checking is nonexistent.

    STDMETHODIMP CAddin::GetRestrictedContents(IDispatch* pDummy)
    {
    	HRESULT	hr = E_FAIL;
    	CComPtr<Outlook::_Explorer> pExp;
    	CComPtr<IUnknown> pUnk;
    	CComQIPtr<IMAPIFolder, &IID_IMAPIFolder> pFolder;
    	CComPtr<Outlook::MAPIFolder> polFolder;
    	CComPtr<IMAPITable> pContents;
    	SRestriction res = { 0 };
    	SPropValue sProp = { 0 };
    	LPSPropTagArray lpPropTag = NULL;
    	MAPINAMEID	mID = { 0 };
    	LPMAPINAMEID lpID = &mID;
    	ULONG cV = 0, nRows = 0;
    
    	mID.ulKind = MNID_STRING;
    	mID.lpguid = const_cast<LPGUID>(&PS_PUBLIC_STRINGS);
    	mID.Kind.lpwstrName = L"FromCloud";
    
    	hr = m_pApp->ActiveExplorer(&pExp);
    	hr = pExp->get_CurrentFolder(&polFolder);
    	hr = polFolder->get_MAPIOBJECT(&pUnk);
    	pFolder = pUnk;
    	hr = pFolder->GetContentsTable(MAPI_UNICODE, &pContents);
    	hr = pFolder->GetIDsFromNames(1, &lpID, 0, &lpPropTag);
    	res.rt = RES_CONTENT;
    	res.res.resContent.ulPropTag = CHANGE_PROP_TYPE(lpPropTag->aulPropTag[0], PT_UNICODE);
    	res.res.resContent.lpProp = &sProp;
    	res.res.resContent.ulFuzzyLevel = FL_FULLSTRING | FL_IGNORECASE;
    	sProp.ulPropTag =CHANGE_PROP_TYPE(lpPropTag->aulPropTag[0], PT_UNICODE);
    	sProp.Value.lpszW = L"Automotive";
    	hr = pContents->GetRowCount(0, &nRows);
    	ATLTRACE("Number of Rows in Contents table before restriction: %d\n", nRows);
    	hr = pContents->Restrict(&res, 0);
    	hr = pContents->GetRowCount(0, &nRows);
    	ATLTRACE("Number of Rows in Contents table after restriction: %d\n", nRows);
    	MAPIFREEBUFFER(lpPropTag);
    
    	return S_OK;
    }
    

    • Edited by RLWA32 Wednesday, December 23, 2015 11:35 AM
    • Marked as answer by Joel_Z Tuesday, December 29, 2015 12:36 AM
    Wednesday, December 23, 2015 10:49 AM
  • Thanks for this, RLWA32!! I'm looking forward to trying it out.  I see what you mean with the Restriction mistake. I hope to get time to try this out tomorrow. I like how you assigned the PS_PUBLIC_STRINGS GUID. That is way more readable and simpler.


    • Edited by Joel_Z Thursday, December 24, 2015 1:21 AM
    Thursday, December 24, 2015 1:20 AM
  • RLWA32, I think the client is OK with the existing find/search features. I would try out the code, but its sounding like he doesn't need what I thought he needed.

    I am thankful for the time you spent creating/posting the GetRestrictedContents() function to solve a problem and contributing to the community. I think you make this forum a much better place!!

    Tuesday, December 29, 2015 12:31 AM
  • Perhaps you'll have a reason to use the code in the future.  You never know what will spark a client's appetite for a new feature.

    Thanks for the kind words.

    Tuesday, December 29, 2015 1:01 AM