How to Search Phone number in Address book? - vba

There are options to get a contact detail from Mail id or Contact's Name using
Namespace.CreateRecipient / Recipient.Resolve
Is there any option to perform a direct search on Outlook Addressbook with Phone number or Company name etc.?
I am able to get many VBA coding options to loop through each contact in addressbook & find matching contact for phone number.

Please, try the next function. It should return the account name, searching by a phone number.
I played some minutes with my Outlook settings, because it used to not return any Address Book, even if it has a custom one from where it suggests the accounts address when start typing in the To box. If a similar problem in your case, I will try explaining what I did. But you maybe are luckier:
Function nameByPhoneNo(strPhNo As String) As String
Dim olApp As Outlook.Application, olNS As Outlook.NameSpace
Dim olGAL As Outlook.AddressList, olAdLs As Outlook.AddressLists
Dim olEntry As Outlook.AddressEntries, olMember As Outlook.AddressEntry
Set olApp = Outlook.Application
Set olNS = olApp.GetNamespace("MAPI")
Set olAdLs = olNS.AddressLists
Set olGAL = olAdLs.item(1) 'Your "Contacts" address book must be the first!
'the code can be adapted to search in all of them, if many...
If olGAL.AddressEntries.count < 1 Then
MsgBox "You must set ""Contacts"" folder as ""Outlook Address Book"", from its ""Properties""."
nameByPhoneNo = "No Address Book coould be found..."
Exit Function
End If
Set olEntry = olGAL.AddressEntries
For Each olMember In olEntry
If InStr(olMember.GetContact.BusinessTelephoneNumber, strPhNo) > 0 Or _
InStr(olMember.GetContact.HomeTelephoneNumber, strPhNo) > 0 Or _
InStr(olMember.GetContact.MobileTelephoneNumber, strPhNo) > 0 Then
nameByPhoneNo = olMember.GetContact.fullName: Exit Function
End If
Next
End Function
The function can be adapted to also search by "Company Name" etc.
It can be tested in this way:
Sub testNameByPhoneNo()
Debug.Print nameByPhoneNo("123252900") 'use it as string (between double quotes)
End Sub

Unlike message store providers, most address book providers do not support arbitrary searches (only because Outlook does not use them), so on the low level most of them support simple PR_ANR searches, which is what CreateRecipient / Resolve use AFAIK - it is essentially "here is a string, find the best match that makes sense to you".
Some address book providers (such as GAL) expose search templates (you can see its UI in the Outlook address book if you click "Advanced Search". That functionality is available either in Extended MAPI (C++ or Delphi only) or in Redemption (I am its author - any language - see RDOAddressListSearch object). Unfortunately, phone number is not one of the supported search fields for GAL - only fist/last names, department, company, city, etc. are.

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.

Looping through specific subfolders in outlook containing a specific string

I want to look for specific items inside specific subfolders in Outlook (macro VBA) that can be in first or second level subfolder, however I cannot make it to work. I have found other questions that loop through all the items in all folders, but not that go through all items in specific subfolders.
fldrname = "Clearing"
Set objNS = GetNamespace("MAPI")
Set ClearingFolders = Outlook.Folders("Clearing", objNS.Folders)
For Each ClearingFolders In objParentFolderCollection
For Each folder In ClearingFolders
If InStr(1, fldrname, folder.Name, vbTextCompare) > 0 Then
{findEmail}
End If
Next folder`
Thanks for your help!
The code below demonstrates how to access every mail item within every folder, and sub-folder to any depth, within a parent folder. It does this by outputting an indented list of items and sub-folders to the Immediate Window. The format of the output is:
ParentFolderName
Date Subject (of mail item within ParentFolder
Date Subject (of mail item within ParentFolder
Date Subject (of mail item within ParentFolder
ChildFolder1Name
Date Subject (of mail item within ChildFolder1Name
Date Subject (of mail item within ChildFolder1Name
GrandchildFolder1Name
Date Subject (of mail item within GrandchildFolder1Name
Date Subject (of mail item within GrandchildFolder1Name
ChildFolder2Name
Date Subject (of mail item within ChildFolder2Name
Date Subject (of mail item within ChildFolder2Name
GrandchildFolder2Name
Date Subject (of mail item within GrandchildFolder2Name
Date Subject (of mail item within GrandchildFolder2Name
GreatgrandchildFolder1Name
Date Subject (of mail item within GreatgrandchildFolder1Name
Date Subject (of mail item within GreatgrandchildFolder1Name
ChildFolder3Name
: : : : :
There are statements within your code I do not understand so I have ignored your code and created my own.
Consider first:
Set Fldr = Session.Folders("StoreName").Folders("TopLevelFolderName")
Your equivalent of this statement is:
Set objNS = GetNamespace("MAPI")
Set Fldr = objNS.Folders("StoreName").Folders("TopLevelFolderName")
With VBA there is often more than one way of achieving the same effect. I prefer Session to objNS. My code so my favourites. Change to your favourite if you wish.
A store is a file on disc that Outlook uses to hold mail items, tasks, appointment items and so on. I assume “Clearing” is the name of a folder and not the name of a store. Your folder pane will look something like this:
StoreName1
Clearing1
Deleted Items
Inbox
Sent Items
StoreName2
Inbox
Clearing2
Sent
Trash
You can have as many stores as you wish. There will be one per email address and perhaps one for archives. When I change computers, I add my old stores to my new Outlook installation, so I have access to all my old emails.
It seems there is always an “Inbox”. Other standard folders change their names from version to version so you might have “Deleted Items” or “Trash” or something else. You can add your own folders wherever you like.
If your “Clearing” is a store, you will need:
Set Fldr = Session.Folders("Clearing")
If your “Clearing” is at the same level as “Inbox” like my “Clearing1”, you will need:
Set Fldr = Session.Folders("StoreName1").Folders("Clearing1")
If your “Clearing” is under “Inbox” like my “Clearing2”, you will need:
Set Fldr = Session.Folders("StoreName2").Folders("Inbox").Folders("Clearing2")
Change my statement to match your system.
Notice that I write:
Dim Fldr As Outlook.Folder
but
Dim ItemCrnt As MailItem
This code runs under Outlook so I do not need to specific Outlook. I could have written Outlook.MailItem but it would not add value because VBA only has one data type named MailItem. However, Outlook as two data types Folder; one for disc folders and one for Outlook folders. Outlook VBA will assume you mean Outlook.Folder when you write Folder but I once got myself into a muddle when I did not specify which Folder I meant. Now, I am always careful to write Outlook.Folder or Scripting.Folder so I will not forget when it is important.
The sub ProcessChild is recursive. There are excellent explanations of recursion on the web so I will not attempt my own explanation now. However, if you are confused, I will add an explanation of my routine.
Now consider:
For InxI = 1 To FldrPrnt.Items.Count
: : :
For InxF = 1 To FldrPrnt.Folders.Count
You have used For Each. I sometimes use For Each but I find For Index more convenient most of the time.
FldrPrnt is the folder whose mail items and sub-folders I wish to access. FldrPrnt.Items gives me access to the items and FldrPrnt.Folders gives me access to the sub-folders.
When I write For InxI = 1 To FldrPrnt.Items.Count, I access the items oldest first. If I had written For InxI = FldrPrnt.Items.Count to 1 Step -1, I would have accessed the items newest first. “Oldest” and “Newest” here does not refer to the date of the item. It refers to the order in which items were added to FldrPrnt.Items. Normally mail items are added in date order so these two orders are the same. However, if you accidentally delete an old mail item then move it back from folder “Deleted Items”, it will become the newest item in the folder.
Often you can write either For InxI = 1 To FldrPrnt.Items.Count or For InxI = FldrPrnt.Items.Count to 1 Step -1. However, if your processing involves moving items to another folder, you must use FldrPrnt.Items.Count to 1 Step -1. With For Index, you are identifying items by their position within FldrPrnt.Items. If you move item 20 to another folder, item 21 becomes item 20, item 22 becomes item 21 and so on. For the next repeat of the loop, you will check the new item 21 not the old item 21. We sometimes get questions where someone is only checking half their items. This is the reason.
Notice If TypeName(FldrPrnt.Items(InxI)) = "MailItem" Then. Not every item is a MailItem. It is essential to check an item’s type before processing it since different items have different properties.
I hope the above, is enough for you to understand my code but ask question is necessary. All my code does is display the received time and subject of each mail item. You will have to replace my Debug.Print statement with whatever code you need to achieve your objectives.
Option Explicit
Sub Main()
Dim Fldr As Outlook.Folder
Set Fldr = Session.Folders("StoreName").Folders("TopLevelFolderName")
Call ProcessChild(Fldr, 0)
End Sub
Sub ProcessChild(ByRef FldrPrnt As Outlook.Folder, ByVal Indent As Long)
Dim InxF As Long
Dim InxI As Long
Dim ItemCrnt As MailItem
Debug.Print Space(Indent * 2) & FldrPrnt.Name
For InxI = 1 To FldrPrnt.Items.Count
If TypeName(FldrPrnt.Items(InxI)) = "MailItem" Then
Set ItemCrnt = FldrPrnt.Items(InxI)
With ItemCrnt
Debug.Print Space(Indent * 2 + 2) & .ReceivedTime & " " & .Subject
End With
End If
Next
For InxF = 1 To FldrPrnt.Folders.Count
If FldrPrnt.Folders(InxF).DefaultItemType = olMailItem Then
Call ProcessChild(FldrPrnt.Folders(InxF), Indent + 1)
End If
Next
End Sub
You need to iterate over all folders recursively:
Private Sub processFolder(ByVal oParent As Outlook.MAPIFolder)
Dim oFolder As Outlook.MAPIFolder
Dim oMail As Outlook.MailItem
For Each oMail In oParent.Items
'Get your data here ...
Next
If (oParent.Folders.Count > 0) Then
For Each oFolder In oParent.Folders
processFolder oFolder
Next
End If
End Sub
Also, you may consider using the AdvancedSearch method of the Application class which performs a search based on a specified DAV Searching and Locating (DASL) search string. 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.
Read more about the AdvancedSearch method in the Advanced search in Outlook programmatically: C#, VB.NET article.
So, maybe you don't need to iterate over all folders and search for specific items there any longer?

How determine in which language an email is written using VBA

In My project, I would like to automate The following task in my work place :
Mails Triaging
this task consist to read the email , detect the language of the email and send the email in particular folder in Outlook.
lets say the Inbox have two emails one English and another in French .
the program should send the email for example to Jack EN for English emails and Julie FR For french emails.
So far I have this what I have :
Sub TriageEmails()
Dim myolApp As Outlook.Application
Dim aItem As Object
Set myolApp = CreateObject ("Outlook.Application")
Set mail = myolApp.ActiveExplorer.CurrentFolder
For Each AItem In mail.Items
`i guess the code will be here
Next aItem
End Sub
You can't. You might be able to detect the code page (as specified in the email headers - see MailItem.InternetCodepage property), but that tells you nothing about the language. And what do you do if there are multiple languages or code pages in the same email?
You can try to use IMultiLanguage3::DetectOutboundCodePage, but I don't think you can access IMultiLanguage in VBA.

Automation error in global address list lookup

I'm trying to look up a person's email address using the Outlook global address list. Here is the VBA code I use to do it.
'Snip
If firstName <> "" Then
Dim o, AddressList, AddressEntry
Dim AddressName, Address, Address2 As String
Set o = CreateObject("Outlook.Application")
Set AddressList = o.Session.AddressLists("Global Address List")
AddressName = firstName
For Each AddressEntry In AddressList.AddressEntries
If AddressEntry.Name = AddressName Then
Address = AddressEntry.GetExchangeUser.PrimarySmtpAddress
Exit For
End If
Next AddressEntry
End If
'Snip
The variable "firstName" is in the format Last name, first name. This seemingly only works half of the time I try to use it. For example if I put my own name in it work perfectly fine every time but if I put in my co-worker's name I get a little pop up bubble from Outlook saying "outlook is trying to retrieve data from the Microsoft exchange server *.com" and then I get an automation error. I couldn't find anything in common between the names that don't work and the same for those that do. Any help would be very much appreciated.
EDIT: Another note I just thought of is that when I hit debug on the error window it highlights the "Next AddressEntry" line.
Do not loop through all items in GAL - some GALs contain tens of thousand of entries, and you will nto be able to loop through them.
Call Application.Session.CreateRecipient (returns Recipient object), call Recipient.Resolve, then use Recipient.AddressEntry.GetExchangeUser.
UPDATE:
if the name is ambiguous, you will get back an error from Recipient.Resolve - Outlook Object Model does not let you recover from that - there is no way to get the list of matches and select the one you want. If using Redemption (I am its author) is an option, you can use is RDOAddressBook.ResolveNameEx method - it returns a list of matches (of one entry if the name is not ambiguous). You can also use RDIAddressList.ResolveName / ResolveNameEx if you want to resolve against a particular container only (e.g. GAL).
Set o = CreateObject("Outlook.Application")
...
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = o.Session.MAPIOBJECT
set AdrrEntries = Session.AddressBook.ResolveNameEx("John")
MsgBox AdrrEntries.Count & " names were returned by ResolveNameEx:"
for each AE in AdrrEntries
MsgBox AE.Name
next

Move Outlook messages if content contains a number greater than threshold

I get thousands of Nagios alerts in my inbox daily, but many of them are actually trivial (even though Nagios reports them as critical). I want to check whether the text of these alerts contains numbers above a certain threshold; if the numbers are lower than that threshold, move the message to a junk folder. I should really work with my sysadmin to decrease the number of useless alerts Nagios sends in the first place, but humor me in my attempt at a creative workaround.
I'm using Outlook 2007 and have found several tutorials on writing Outlook macros in VB, including this one about programmatically creating a rule to move messages to different folders. That example uses a TextRuleCondition to check whether the subject contains any of the keywords in an array.
But I don't want to check for keywords, I want to check if a number in the message text is greater or less than a threshold value. For example, if the text of a message contains the following, it could be moved to a junk folder:
Nagios bad condition: foo = 3
But if a message contained this, I would want to keep it:
Nagios bad condition: foo = 157
This example seems a little more like what I want in terms of searching the content of the message for arbitrary text. But it requires the message to be open, so I'm not quite sure how to translate it into a rule. Any help would be appreciated.
The second example you link to will put you on the right track to write code that discriminates between good and junk e-mails.
Then you will want to put that code in the _ItemAdd event for the Inbox items, such that it runs every time something new pops up in your Inbox. Here's an example of what should go in your Outlook VBA module:
Public WithEvents myOlItems As Outlook.Items
Public Sub Application_Startup()
' Upon starting Outlook, set reference to the items in the Inbox.
Set myOlItems = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub myOlItems_ItemAdd(ByVal Item As Object)
' Because myOlItems is declared "WithEvents",
' the ItemAdd event will fire anytime something new pops up in the Inbox.
If TypeName(Item) = "MailItem" Then
' It's an e-mail.
' Here goes the code to test whether it should go to the junk folder.
Else
' It's something else than an e-mail.
' Do nothing.
End If
End Sub
JFC has already given you one way. Here is another using RULES to check messages as they arrive. Do this.
Open VBA Editor and paste this code in ThisOutlookSession
UNTESTED
Option Explicit
Sub Sample(MyMail As MailItem)
Dim strID As String, olNS As Outlook.NameSpace
Dim objInboxFolder As Outlook.MAPIFolder
Dim objDestinationFolder As Outlook.MAPIFolder
Dim olMail As Outlook.MailItem
Dim strFileName As String, strSubj As String
Dim Myarray() As String
Dim ThrsdVal As Long
strID = MyMail.EntryID
Set olNS = Application.GetNamespace("MAPI")
Set olMail = olNS.GetItemFromID(strID)
'~~> Email Subject
strSubj = olMail.Subject
'~~> Threshold value
ThrsdVal = 100
'Nagios bad condition: foo = 3
Myarray = Split(strSubj, "=")
Set objInboxFolder = olNS.GetDefaultFolder(olFolderInbox)
'~~> Destination folder
Set objDestinationFolder = objInboxFolder.Folders("Temp")
'~~> Check if less than threshold value
If Val(Trim(Myarray(1))) < ThrsdVal Then
olMail.Move objDestinationFolder
End If
Set olMail = Nothing
Set olNS = Nothing
End Sub
Now
1) Create a new Rule (Select "Check Messages When they Arrive")
2) In (Condition) select "From people or Distribution List"
3) Select the relevant email address from which you are getting the emails
4) In Actions, select "run a script" and then choose the above script.
5) Finally click on Finish and you are done :)
The best part about this is that you can run this rule for existing emails in your inbox folder as well :)
NOTE: Like I mentioned above, I have not tested the code so do let me know if you get any errors and I will rectify it. Also I am assuming that the message will have a subject with the format as "Nagios bad condition: foo = X". I have not included any error handling. I am sure you can take care of that :)
HTH
Sid