How to get a search folder criteria in Outlook - vba

I am trying to edit a search folder in Outlook. But a search folder is treated as a regular folder:
in this MSDN reference, MS informs that "GetSearchFolders returns a Folders collection". I believe that, once I have the search folder filter, I will have to delete the current, edit the filter and create a new one - but that's the easy part. I have found e.g. here how to create and delete search folders. Everywhere I find create and delete, but nobody seems to know how to edit it or get the filter that applies to it...

Outlook Object Model will not let you edit the search criteria of a Search folder.
You can either use Extended MAPI (C++ or Delphi, you can see the search criteria in OutlookSpy (I am its author) if you select the search folder and click the IMAPIFolder button and go to the GetSearchCriteria tab) or Redemption (any language - I am also its author): it exposes the RDOSearchFolder object (lets you create and manage MAPI search folders) and RDOStore2.Searches collection - it exposes saved searches (backed up by MAPI search folders) visible under the Search Folders node in Outlook.
UPDATE: the following script will print the search criteria of all searches in the default profile:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set Searches = Session.Stores.DefaultStore.Searches
for each Search in Searches
Debug.Print "-------------"
Debug.Print Search.Name & ": "
Debug.Print Search.SearchCriteria.AsSQL
next

Related

Is it possible to find related emails and loop the results in the background?

I'd like to find related emails to the email I have currently selected. Then I want to loop the results.
Using the ActiveExplorer.Search takes a moment, and at the same time the code keeps running. So it doesn't return any results, because of loading still happening in the background, I guess.
So my questions are:
How do I find related emails?
How do I loop the search results (in the background)?
To find related emails, maybe something like this:
Sub FindRelatedEmails()
Dim ns As Outlook.NameSpace
Set ns = myOlApp.GetNamespace("MAPI")
Dim oMail As Outlook.MailItem
Set oMail = ActiveExplorer.Selection.Item(1)
Dim strFrom As String
strFrom = oMail.SenderName
Dim strSubject As String
strSubject = oMail.ConversationTopic
Dim myOlApp As New Outlook.Application
Set myOlApp.ActiveExplorer.CurrentFolder = ns.GetDefaultFolder(olFolderInbox)
Dim txtSearch As String
txtSearch = "[Konversation]:=""" & strSubject & """"
myOlApp.ActiveExplorer.Search txtSearch, olSearchScopeAllFolders
' Problem occurs below, since the code keeps running but the search results haven't loaded yet.
myOlApp.ActiveExplorer.SelectAllItems
Dim i As Long
For i = ActiveExplorer.Selection.Count To 1 Step -1
Dim Item As MailItem
Set Item = ActiveExplorer.Selection.Item(i)
Debug.Print Item.Subject, Item.Sender, Item.Parent.FolderPath
Next
Set ns = Nothing
Set oMail = Nothing
Set myOlApp = Nothing
Set Item = Nothing
End Sub
Try to use Application.AdvancedSearch instead - it exposes Application.AdvancedSearchComplete event.
The Explorer.Search method is used to perform a Microsoft Instant Search on the current folder displayed in the Explorer using the given Query. Basically, it will use Outlook UI for searching items and the result is visible in Outlook. The functionality of Explorer.Search is analogous to the Search button in Instant Search. It behaves as if the user has typed the query string in the Instant Search user interface and then clicked Search. When calling Search, the query is run in the user interface, and there is no programmatic mechanism to obtain the search results. The Search method does not provide a callback to enable the developer to determine when the search is complete.
Instead, you may find the Find/FindNext or Restrict methods of the Items class helpful. Read more about them in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Also you may consider using the AdvancedSearch method of the Application class helpful. The key benefits of using the AdvancedSearch method in Outlook are:
The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
Full support for DASL queries (custom properties can be used for searching too). You can read more about this in the Filtering article in MSDN. To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
You can stop the search process at any moment using the Stop method of the Search class.
The Outlook Object Model provides the AdvanvedSearchComplete event of the Application class. An instance of the Search class containing the search results is passed to the event handler (see the Results property).
See Advanced search in Outlook programmatically: C#, VB.NET for more information.

Search Outlook Global Address List Asynchronously

I want to;
Search the Global Address List of MS Outlook
Using an input, filterStr
To retrieve all items asynchronously that match a criteria satisfying
Contact first name starts with filterStr or contact last name starts with filterStr
Display the contacts as they become available in some sort of list
I am developing a MS Outlook add-in with Visual Studio 2017 using VB.net. (c# code examples are wellcome). I need this add-in to be able to search contacts like MS Skype for Business does, through its "Find Someone" field of "RICHEDIT60W"
I have tried the following so far;
Use "RICHEDIT60W" of MS Skype for Business (the search field) in my form
Could not find documentation
Use the "RichEdit20WPT" of MS Outlook (the "TO:" field in new e-mail compose)
Could not find documentation
Get the "Global Address List" through Session.GetGlobalAddressList
Succeeded, but looping through more than 50k items is too slow
Use the System.DirectorySearcher
This does not always work. When I'm connected to my corporate network through cable, I can create the object. When I'm connected through my home wireless, I get the error: "specified domain does not exist or could not be contacted"
I did try using a DirectoryEntry object initialized with;
name.surname.corporation.com where my e-mail address is name.surname#corporation.com, this time I get an "unspecified error"
I tried using ADODB to connect as given in https://learn.microsoft.com/en-us/windows/desktop/ad/example-code-for-searching-for-users#visual-basic-example
This also gives me an error on line 42 (root = GetObject("LDAP://rootDSE")
QUESTION: How can I do any of the following;
Get DirectorySearcher to work
Get ADODB method to work
Use the existing RichEdit controls of either Outlook or Skype for Business
?
I solved it like this;
Getting the GAL itself and the AddressEntries is fast enough, no problem there
I'm looping through the AddressEntries only once, during initialization and getting only the Nameproperties of each to a List(Of String), which takes around 3 seconds
When I need to search for an entry, I'm using Linq to query the list with IndexOf function, which takes about 150ms max to get all matches, and the items of the list are immediately accessible
This suited my needs because
- It works through my home network and even when I don't have a connection (I assume Outlook is cacheing the GAL)
- Querying using Linq is fast enough to facilitate searching as the user types
On the low (Extended MAPI - C++ or Delphi only) level, you would need to apply PR_ANR MAPI restriction: that is what Outlook uses when it resolves a name and displays the list of ambiguous matches. You can run that code on a separate thread, but it won't return matches one at a time - you will get the whole set back.
If Extended MAPI is not an option, you can use Redemption (I am its author) and its RDOSession.AddressBook.GAL.ResolveNamesEx method:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set AdrrEntries = Session.AddressBook.GAL.ResolveNameEx("John")
Debug.Print AdrrEntries.Count & " names were returned by ResolveNameEx:"
Debug.Print "------------"
for each AE in AdrrEntries
Debug.Print AE.Name
next
Debug.Print "------------"

How to bulk export Attachments from emails (which are emails) to another folder within Outlook

I need to extract .msg attachments from emails in a range and save these into another outlook sub-folder. This works currently by dragging the attachment into a sub-folder of 'inbox' but is there a quicker way?
I have searched around a bit and found ways to extract them to a local folder but i need them to be contained within outlook.
I appreciate any help and suggestions.
Thanks.
There are two problems here - first is accessing embedded message attachments without saving them first as MSG file. Second is importing the MSG files back - you can use Application.CreateItemFromTemplate, but the item will be unsent. You can use Namespace.OpenSharedItem, and then use MailItem.Move, but it is still a kludge.
There issn't much much you can do in OOM alone. Extended MAPI would work, but it is C++ or Delphi only. If using Redemption is an option (I am its author), you can use EmbeddeedMsg property exposed by the Redemption RDOAttachment object. You can also use RDOMail.CopyTo and pass a folder as a parameter to copy an embedded message attachment to a folder:
Set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set redItem = Session.GetMessageFromId(OutlookMessage.EntryID)
set redFolder = Session.GetFolderFromId(OutlookFolder.EntryID)
for each attach in redItem.Attachments
if attach.Type = olEmbeddeditem Then
attach.EmbeddedMsg.CopyTo OutlookFolder
End If
next

What is the VBA property for: Server folder contains x items?

I like to compare the amount of items in each folder on the Exchange server with the amount of items in my local Outlook file.
In Outlook I can use the following to get the item count per folder
Dim Folder As Outlook.MAPIFolder
Set Folder = objNS.Folders("xxx").Folders("Inbox")
Debug.Print Folder.Items.Count
How do I get the item count on the server like in the following screenshot?
To find out the number of items in a server (online) folder, you will need to open that folder in the online mode.
In Extended MAPI (C++ or Delphi) you would need to use the MAPI_NO_CACHE bit when calling IMAPISession::OpenEntry - you can play with that bit in OutlookSpy (I am its author): click IMAPIFolder button, select PR_ENTRYID property, right click, select IMAPISession::OpenEntry, make sure MAPI_NO_CACHE is checked. One you do that, you can retrieve the count either from the IMAPITable contents table or by reading the PR_CONTENT_COUNT (0x36020003) MAPI property from the folder itself.
Outlook Object Model will not let you override the caching mode on the per folder/message level - the whole profile must have the right connection mode.
In case of languages other than C++ or Delphi, you can use Redemption (I am also its author) - its versions of GetFolderFromID, GetMessageFromID, etc. allow to pass flags to be used by IMAPISession::OpenEntry.
MAPI_NO_CACHE = &H0200
MAPI_BEST_ACCESS = &H0010
set OomFolder = Application.ActiveExplorer.CurrentFolder
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set RdoFolder = Session.GetFolderFromID(OomFolder.EntryID, , MAPI_NO_CACHE Or MAPI_BEST_ACCESS)
MsgBox "Number of items in the online folder: " & RdoFolder.Items.Count

VBA: Function for returning specific Outlook folder?

I want to search for specific emails in specific folders, but these folders have different sub-levels and not all the same parent folder.
So I like to have a function that will simply give me the Outlook folder object for a given folder name.
Does something like this exists?
FindFolder(str_FolderName As String) As Outlook.Folder
So that I can just write
objFolder = FindFolder("MyFolder")
All the solutions I´ve found or come up with weren´t as flexible as needed.
There is no such function due to to the fact that folders with the same name can exists at different levels in the tree. For example, a folder may contain a child folder with the same name. What folder should be returned then? A parent or child folder?
You may find the Getting Started with VBA in Outlook 2010 article helpful.
Are you looking for a counterpart of MAPIFolder.FullFolderPath property (returns something like \\user#domain.name\RSS Feeds\Stack Overflow) to return a folder given its path?
You can write a function that converts the path to an array/list and recursively processes the list starting with Namespace.Folders collection. You will need to take care of special character encoding (e.g. %20 for " ").
If using Redemption is an option (I am its author), it exposes RDOSession.GetFolderFromPath function.