Opening Outlook address book from Excel - excel

I'm using VBA in Excel 2010, with Outlook 2010 (already open).
How could I write a sub such that:
1 Outlook address book opens;
2 The user selects a contact and clicks ok;
3 The contact's first name, last name and email address are stored in cells of the active worksheet?
I tried with this method without success: SelectNamesDialog Object
Also I'm not sure if I need to use: Application.GetNamespace("MAPI")

You are on the right avenue, the SelectNamesDialog is exactly what you are looking for. The GetNamepsace method equals to the Session property used in the sample code:
Sub ShowContactsInDialog()
Dim oDialog As SelectNamesDialog
Dim oAL As AddressList
Dim oContacts As Folder
Set oDialog = Application.Session.GetSelectNamesDialog
Set oContacts = _
Application.Session.GetDefaultFolder(olFolderContacts)
'Look for the address list that corresponds with the Contacts folder
For Each oAL In Application.Session.AddressLists
If oAL.GetContactsFolder = oContacts Then
Exit For
End If
Next
With oDialog
'Initialize the dialog box with the address list representing the Contacts folder
.InitialAddressList = oAL
.ShowOnlyInitialAddressList = True
If .Display Then
'Recipients Resolved
'Access Recipients using oDialog.Recipients
End If
End With
End Sub
You may find the following articles helpful:
How to automate Outlook from another program
Automating Outlook from a Visual Basic Application

Here is how to get all the details from a selected contact in the GAL:
You need to open the Global Address List and not the contacts from the contact folder, and use an Outlook.ExchangeUser object as explained on this page: see last answer from David Zemens.
Private Sub cmdSetProjectMember1_Click()
Dim olApp As Outlook.Application
Dim oDialog As SelectNamesDialog
Dim oGAL As AddressList
Dim myAddrEntry As AddressEntry
Dim exchUser As Outlook.ExchangeUser
Dim AliasName As String
Dim FirstName As String
Dim LastName As String
Dim EmailAddress As String
Set olApp = GetObject(, "Outlook.Application")
Set oDialog = olApp.Session.GetSelectNamesDialog
Set oGAL = olApp.GetNamespace("MAPI").AddressLists("Global Address List")
With oDialog
.AllowMultipleSelection = False
.InitialAddressList = oGAL
.ShowOnlyInitialAddressList = True
If .Display Then
AliasName = oDialog.Recipients.Item(1).Name
Set myAddrEntry = oGAL.AddressEntries(AliasName)
Set exchUser = myAddrEntry.GetExchangeUser
If Not exchUser Is Nothing Then
FirstName = exchUser.FirstName
LastName = exchUser.LastName
EmailAddress = exchUser.PrimarySmtpAddress
'...
MsgBox "You selected contact: " & vbNewLine & _
"FirstName: " & FirstName & vbNewLine & _
"LastName:" & LastName & vbNewLine & _
"EmailAddress: " & EmailAddress
End If
End If
End With
Set olApp = Nothing
Set oDialog = Nothing
Set oGAL = Nothing
Set myAddrEntry = Nothing
Set exchUser = Nothing
End Sub

Related

VBA to search and reply in Outlook with specific criteria

I am Using windows 10, Excel 2013 and Outlook 2013
I am new to Macro. I need macro to perform below Task:
1) From Excel I want to open Outlook if Outlook is closed and move Point.2, If outlook is already open then move to Point.2
2) Search for a specific email in outlook in all folders and sub folders with criteria “A” and “B”
a) Latest dated received or sent email.
b) With specific Subject contains “Approved”, this to be taken from active cell.
3) Open the found latest mail as per above criteria do “Reply all”.
4) Write a comment and display the mail or send.
Below code was my start, but it has the following issues:
The code search for the exact name, while i need to search for any email contain the word which in active cell.
The code search only in sent emails, while i need to search in both inbox and sent.
The code just open the email, i need to write template comment as well.
Many thanks in advance.
Sub ReplyMail_No_Movements()
' Outlook's constant
Const olFolderSentMail = 5
' Variables
Dim OutlookApp As Object
Dim IsOutlookCreated As Boolean
Dim sFilter As String, sSubject As String
' Get/create outlook object
On Error Resume Next
Set OutlookApp = GetObject(, "Outlook.Application")
If Err Then
Set OutlookApp = CreateObject("Outlook.Application")
IsOutlookCreated = True
End If
On Error GoTo 0
' Restrict items
sSubject = ActiveCell.Value
sFilter = "[Subject] = '" & sSubject & "'"
' Main
With OutlookApp.Session.GetDefaultFolder(olFolderSentMail).Items.Restrict(sFilter)
If .Count > 0 Then
.Sort "ReceivedTime", True
With .Item(1).replyall
.Display
'.Send
End With
Else
MsgBox "No emails found with Subject:" & vbLf & "'" & sSubject & "'"
End If
End With
' Quit Outlook instance if it was created by this code
If IsOutlookCreated Then
OutlookApp.Quit
Set OutlookApp = Nothing
End If
End Sub
It seems work now:
Sub ReplyAllLastEmailFromInboxAndSent()
Dim olApp As Outlook.Application
Dim olNs As Namespace
Dim Fldr As MAPIFolder
Dim objMail As Object
Dim objReplyToThisMail As MailItem
Dim lngCount As Long
Dim objConversation As Conversation
Dim objTable As Table
Dim objVar As Variant
Dim strBody As String
Dim searchFolderName As String
Set olApp = Session.Application
Set olNs = olApp.GetNamespace("MAPI")
Set Fldr = olNs.GetDefaultFolder(olFolderSentMail)
searchFolderName = "'" & Outlook.Session.GetDefaultFolder(olFolderInbox).FolderPath & "','" & Outlook.Session.GetDefaultFolder(olFolderSentMail).FolderPath & "'"
lngCount = 1
For Each objMail In Fldr.Items
If TypeName(objMail) = "MailItem" Then
If InStr(objMail.Subject, ActiveCell.Value) <> 0 Then
Set objConversation = objMail.GetConversation
Set objTable = objConversation.GetTable
objVar = objTable.GetArray(objTable.GetRowCount)
Set objReplyToThisMail = olApp.Session.GetItemFromID(objVar(UBound(objVar), 0))
With objReplyToThisMail.replyall
strBody = "Dear " & "<br>" & _
"<p>Following up with the below. May you please advise?" & _
"<p>Thank you," & vbCrLf
.HTMLBody = strBody & .HTMLBody
.Display
End With
Exit For
End If
End If
Next objMail
Set olApp = Nothing
Set olNs = Nothing
Set Fldr = Nothing
Set objMail = Nothing
Set objReplyToThisMail = Nothing
lngCount = Empty
Set objConversation = Nothing
Set objTable = Nothing
If IsArray(objVar) Then Erase objVar
End Sub

Referencing a shared inbox, ERROR: assignment to constant not permitted

I have a code that will:
Go to a specific folder ("Company A status report") which is below the shared mailbox (Inquiry#company.com).
Search for unread emails + a subject phrase: "Company A status report"
Take emails that match the criteria, find the last email then check if an attachment exists.
If attachments exist then download the file.
The code has previously worked, but now I get an error at this line:
Set olFolder = oOlns.GetSharedDefaultFolder(olShareName, olFolderInbox) '// Inbox
The error is:
"assignment to constant not permitted"
Library references
Option Explicit
Const olFolderInbox As Integer = 6
'~~> Path for the attachment
Const AttachmentPath As String = "C:\Projects\Attachments"
Sub DownloadAttachmentFirstUnreadEmail()
Dim oOlInbFiltered As Variant
Dim oOlAp As Object, oOlns As Object, oOlInb As Object
Dim oOlItm As Object, oOlItmF As Object, oOlAtch As Object
'~~> New File Name for the attachment
Dim NewFileName As String
NewFileName = AttachmentPath & Format(Date, "DD-MM-YYYY") & " - "
'~~> Get Outlook instance
Set oOlAp = GetObject(, "Outlook.application")
Set oOlns = oOlAp.GetNamespace("MAPI")
'Set oOlInb = oOlns.GetDefaultFolder(olFolderInbox).Folders("Company A status report") 'If outlook only contain the following:
'Looks in Inbox
'-Personal Inbox
'-Company A status report
Dim olShareName As Object
'https://superuser.com/questions/1035062/how-to-run-a-macro-on-a-shared-mailbox-in-outlook-2013
Set olShareName = oOlns.CreateRecipient("Inquiry#company.com") '// Owner's email address
Set olFolder = oOlns.GetSharedDefaultFolder(olShareName, olFolderInbox) '// Inbox
Set oOlInb = olFolder.Folders("Company A status report")
'Looks in Shared Inbox
'-Personal Inbox
'-Inquiry Inbox (Shared)
'-Company A status report
'~~> Check if there are any actual unread emails
If oOlInb.Items.Restrict("[UnRead] = True").Count = 0 Then
MsgBox "NO Unread Email In Inbox"
Exit Sub
End If
'https://stackoverflow.com/questions/30464271/find-an-email-starting-with-specific-subject-using-vba
'~~> Filter all unread mails with the subject: Company A status report
Dim Findvariable As String
Findvariable = "Company A status report"
Dim filterStr As String
filterStr = "#SQL=" & "urn:schemas:httpmail:subject like '%" & Findvariable & "%'"
Set oOlInbFiltered = oOlInb.Items.Restrict(filterStr)
Set oOlInbFiltered = oOlInb.Items.Restrict("[UnRead] = True")
'Set oOlInbFiltered = oOlInb.Items.Restrict("[UnRead] = True AND [Subject] = 'Company A status report'") - works
'Test how many mails that are found and populated in the variable: oOlInbFiltered
MsgBox ("Hello Test")
Dim testp As Object
For Each testp In oOlInbFiltered
Debug.Print testp.Subject
Next testp
'Sort all the mails by ReceivedTime so the loop will start with the latest mail
oOlInbFiltered.Sort "ReceivedTime", True 'True for Ascending. Take the last mail to the oldest. We only want the last and therefore exit the loop after we find it.
For Each oOlItm In oOlInbFiltered
'Debug.Print oOlItm
'~~> Check if the email actually has an attachment
If oOlItm.Attachments.Count <> 0 Then
For Each oOlAtch In oOlItm.Attachments
Debug.Print oOlAtch
'~~> Download the attachment
oOlAtch.SaveAsFile NewFileName & oOlAtch.FileName
'Mark the found mail as read
oOlItm.UnRead = False
DoEvents
oOlItm.Save
Exit For
Next
Else
MsgBox "The Email doesn't have an attachment"
End If
Exit For
Next oOlItm
'Open the downloaded file
Dim wb As Workbook
Dim FilePath As String
FilePath = NewFileName & oOlAtch.FileName
Set wb = Workbooks.Open(FilePath)
'Set DataPage = wb1.Sheets("DATA")
End Sub
Sorry but can't comment yet.
Error might be caused by:
Const olFolderInbox As Integer = 6
If you change it to normal olFolderInbox = 6 it might fix your issue.
I've got similar vba, that opens inbox and then check's e-mail details and iterate through them.
On mine I've set different Dim's
Dim myOlApp As New Outlook.Application
Dim filteredItems As Outlook.Items
Dim Ns As Outlook.Namespace
Dim Folder As Outlook.Folder
Dim olSharedName As Outlook.Recipient
Where
Set Ns = myOlApp.GetNamespace("MAPI")
Set olSharedName = Ns.CreateRecipient("e'mail#domain.com")
Set Folder = Ns.GetSharedDefaultFolder(olSharedName, olFolderInbox)
My references are:
Hope I've helped.

Check if AddMember method has failed

I am writing code in VBA (in Excel) that puts Outlook contacts into a distribution list.
It works so far, but error handling in case the contact does not exist is not working. MS says
If the specified recipient is not valid, the AddMember method will
fail.
So how do you notice if the method fails?
Public Function olAddContactToList(ByVal sLastName As String, _
Optional ByVal sFirstName As String, _
Optional ByVal sGroup As String) As Boolean
Dim oOutlook As Object ' Outlook.Application
Dim oNameSpace As Object ' Outlook.NameSpace
Dim oMAPIFolder As Object ' Outlook.MAPIFolder
Dim oContact As Object ' Outlook.ContactItem
Dim oList As Object ' Outlook.DistListItem
Dim oMail As Object ' Outlook.MailItem
Dim oRecip As Object ' Outlook.Recipient
Const olFolderContacts = 10
On Error GoTo ErrHandler
Set oOutlook = CreateObject("Outlook.Application")
Set oNameSpace = oOutlook.GetNamespace("MAPI")
Set oMAPIFolder = oNameSpace.GetDefaultFolder(olFolderContacts)
Set oList = oNameSpace.GetDefaultFolder(olFolderContacts).Items(sGroup)
'Adds a member to a new distribution list
Set oMail = oOutlook.CreateItem(olMailItem)
'Create recipient for distlist
Set oRecip = oOutlook.Session.CreateRecipient(sFirstName & " " & sLastName)
oRecip.Resolve
oList.AddMember oRecip
oList.Save
olAddContactToList = True
ErrHandler:
If Err.Number <> 0 Then
MsgBox "Fehler beim Hinzufügen des Outlook-Kontakts zu einer Liste." & vbCrLf & _
CStr(Err.Number) & " " & Err.Description, vbExclamation + vbOKOnly
olAddContactToList = False
End If
Set oContact = Nothing
Set oMAPIFolder = Nothing
Set oNameSpace = Nothing
Set oOutlook = Nothing
Set oList = Nothing
Set oMail = Nothing
Set oRecip = Nothing
End Function
I have tried
Dim AddCheck As Long
AddCheck = oList.AddMember(oRecip)
but AddCheck stays 0 irrespective of the contact existing and being successfully added to the list or not existing and failing to be added.
As the result of Recipient.Resolve is either True or False (says Microsoft), you should check that.
Otherwise you only have a firstname and lastname, but not a full email address:
If Not oRecip.Resolve Then
' no resulting email address
Else
' email address found, go further
End If
As DistListItem.AddMember even raises no error, if you try to add a simple phone number string (just doesn't add it), I suggest to compare DistListItem.MemberCount (oList.MemberCount in your code) additionally before and after trying to add a new member.
By that you`ll either get an error or an unchanged member count, if something went wrong, and can do further checks.

Search by subject for latest email in all folders and reply all

The code below doesn't execute reply all property, hence, I am not able to edit the body of the email and keep the conversation of the email chain.
I think the best option is to use Application.advancesearch as it gives you latest email by searching through all folders. But I do not know how to run it through Excel.
Objective:
1) Search the inbox and subfolders (multiple) and Sent items folder for the latest email for selected "Subject"
2) select the latest email and reply to all
Sub ReplyMail()
' Variables
Dim OutlookApp As Object
Dim IsOutlookCreated As Boolean
Dim sFilter As String, sSubject As String
Dim SentTime As Long
Dim IndoxTime As Long
Dim olEmailIndox As Outlook.MailItem
Dim olEmailSent As Outlook.MailItem
' Get/create outlook object
On Error Resume Next
Set OutlookApp = GetObject(, "Outlook.Application")
If Err Then
Set OutlookApp = CreateObject("Outlook.Application")
IsOutlookCreated = True
End If
On Error GoTo 0
Set olEmailIndox = OutlookApp.CreateItem(olMailItem)
Set olEmailSent = OutlookApp.CreateItem(olMailItem)
' Restrict items
sSubject = "Subject 1"
sFilter = "[Subject] = '" & sSubject & "'"
' Main
With OutlookApp.Session.GetDefaultFolder(olFolderSentMail).Items.Restrict(sFilter)
If .Count > 0 Then
.Sort "ReceivedTime", True
Set olEmailSent = .Item(1)
SentTime = olEmailSent.SentOn
End If
End With
With OutlookApp.Session.GetDefaultFolder(olFolderInbox).Items.Restrict(sFilter)
If .Count > 0 Then
.Sort "ReceivedTime", True
Set olEmailInbox = .Item(1)
InboxTime = olEmailInbox.ReceivedTime
End If
End With
If SentTime > InboxTime Then
With olEmailSent
.ReplyAll
.Display
'.body
'.Send
End With
Else
With olEmailInbox
.ReplyAll
.Display
'.body
'.Send
End With
End If
' Quit Outlook instance if it was created by this code
If IsOutlookCreated Then
OutlookApp.Quit
Set OutlookApp = Nothing
End If
End Sub
I have tested the code below and even though you can polish it, should get you started.
Let me know and mark the answer if it helps.
Add in a vba module this code:
Public Sub ProcessEmails()
Dim testOutlook As Object
Dim oOutlook As clsOutlook
Dim searchRange As Range
Dim subjectCell As Range
Dim searchFolderName As String
' Start outlook if it isn't opened (credits: https://stackoverflow.com/questions/33328314/how-to-open-outlook-with-vba)
On Error Resume Next
Set testOutlook = GetObject(, "Outlook.Application")
On Error GoTo 0
If testOutlook Is Nothing Then
Shell ("OUTLOOK")
End If
' Initialize Outlook class
Set oOutlook = New clsOutlook
' Get the outlook inbox and sent items folders path (check the scope specification here: https://learn.microsoft.com/en-us/office/vba/api/outlook.application.advancedsearch)
searchFolderName = "'" & Outlook.Session.GetDefaultFolder(olFolderInbox).FolderPath & "','" & Outlook.Session.GetDefaultFolder(olFolderSentMail).FolderPath & "'"
' Loop through excel cells with subjects
Set searchRange = ThisWorkbook.Worksheets("Sheet1").Range("A2:A4")
For Each subjectCell In searchRange
' Only to cells with actual subjects
If subjectCell.Value <> vbNullString Then
Call oOutlook.SearchAndReply(subjectCell.Value, searchFolderName, False)
End If
Next subjectCell
MsgBox "Search and reply completed"
' Clean object
Set testOutlook = Nothing
End Sub
Then add a class module and name it: clsOutlook
To the class module add the following code:
Option Explicit
' Credits: Based on this answer: https://stackoverflow.com/questions/31909315/advanced-search-complete-event-not-firing-in-vba
' Event handler for outlook
Dim WithEvents OutlookApp As Outlook.Application
Dim outlookSearch As Outlook.Search
Dim outlookResults As Outlook.Results
Dim searchComplete As Boolean
' Handler for Advanced search complete
Private Sub outlookApp_AdvancedSearchComplete(ByVal SearchObject As Search)
'MsgBox "The AdvancedSearchComplete Event fired."
searchComplete = True
End Sub
Sub SearchAndReply(emailSubject As String, searchFolderName As String, searchSubFolders As Boolean)
' Declare objects variables
Dim customMailItem As Outlook.MailItem
Dim searchString As String
Dim resultItem As Integer
' Variable defined at the class level
Set OutlookApp = New Outlook.Application
' Variable defined at the class level (modified by outlookApp_AdvancedSearchComplete when search is completed)
searchComplete = False
' You can look up on the internet for urn:schemas strings to make custom searches
searchString = "urn:schemas:httpmail:subject like '" & emailSubject & "'" ' Use: subject like '%" & emailSubject & "%'" if you want to include words see %
' Perform advanced search
Set outlookSearch = OutlookApp.AdvancedSearch(searchFolderName, searchString, searchSubFolders, "SearchTag")
' Wait until search is complete based on outlookApp_AdvancedSearchComplete event
While searchComplete = False
DoEvents
Wend
' Get the results
Set outlookResults = outlookSearch.Results
If outlookResults.Count = 0 Then Exit Sub
' Sort descending so you get the latest
outlookResults.Sort "[SentOn]", True
' Reply only to the latest one
resultItem = 1
' Some properties you can check from the email item for debugging purposes
On Error Resume Next
Debug.Print outlookResults.Item(resultItem).SentOn, outlookResults.Item(resultItem).ReceivedTime, outlookResults.Item(resultItem).SenderName, outlookResults.Item(resultItem).Subject
On Error GoTo 0
Set customMailItem = outlookResults.Item(resultItem).ReplyAll
' At least one reply setting is required in order to replyall to fire
customMailItem.Body = "Just a reply text " & customMailItem.Body
customMailItem.Display
End Sub

Search Excel Worksheet for text highlighted in Outlook email

Everyday I get emails with project numbers on them. I usually highlight and copy the project number from the email, click on my excel spreadsheet (WI_Design_Tracker), ctrl + F (to find) and paste the project number into the find field, then Find Next. I'm trying to create a macro that will shorten the process since I do it a hundred times a day. I found a macro that does the opposite (finds a highlighted number in Excel and searches through Outlook to find the email. I tried to amend it to fit my purposes but it's above my skill level. Any help would be greatly appreciated. Here's the code I'm trying to convert to search my Excel worksheet for the project number I have highlighted on an Outlook email.
'Code:
Option Explicit
Public Sub Search_Outlook_Emails()
Dim outApp As Outlook.Application
Dim outNs As Outlook.Namespace
Dim outStartFolder As Outlook.MAPIFolder
Dim foundEmail As Outlook.MailItem
Set outApp = New Outlook.Application
Set outNs = outApp.GetNamespace("MAPI")
'Start at Inbox's parent folder
Set outStartFolder = outNs.GetDefaultFolder(Outlook.olFolderInbox).Parent
'Or start at folder selected by user
'Set outStartFolder = outNs.PickFolder
If Not outStartFolder Is Nothing Then
Set foundEmail = Find_Email_In_Folder(outStartFolder, ActiveCell.Value)
If Not foundEmail Is Nothing Then
If MsgBox("Email subject: " & foundEmail.Subject & vbNewLine & vbNewLine & _
"Folder: " & foundEmail.Parent.FolderPath & vbNewLine & vbNewLine & _
"Open the email?", vbYesNo, "'" & ActiveCell.Value & "' found") = vbYes Then
foundEmail.Display
End If
Else
MsgBox "", vbOKOnly, "'" & ActiveCell.Value & "' not found"
End If
End If
End Sub
Private Function Find_Email_In_Folder(outFolder As Outlook.MAPIFolder, findText As String) As Outlook.MailItem
Dim outItem As Object
Dim outMail As Outlook.MailItem
Dim outSubFolder As Outlook.MAPIFolder
Dim i As Long
Debug.Print outFolder.FolderPath
Set Find_Email_In_Folder = Nothing
'Search emails in this folder
i = 1
While i <= outFolder.Items.Count And Find_Email_In_Folder Is Nothing
Set outItem = outFolder.Items(i)
If outItem.Class = Outlook.OlObjectClass.olMail Then
'Does the findText occur in this email's body text?
Set outMail = outItem
If InStr(1, outMail.Body, findText, vbTextCompare) > 0 Then Set Find_Email_In_Folder = outMail
End If
i = i + 1
Wend
DoEvents
'If not found, search emails in subfolders
i = 1
While i <= outFolder.Folders.Count And Find_Email_In_Folder Is Nothing
Set outSubFolder = outFolder.Folders(i)
'Only check mail item folders
If outSubFolder.DefaultItemType = Outlook.olMailItem Then Set Find_Email_In_Folder = Find_Email_In_Folder(outSubFolder, findText)
i = i + 1
Wend
End Function
Thanks for the link Tim. That was much simpler. I'd had seen that code before but wasn't able to make it work so I tried again. Here's what I ended up with. It could still use some tweaking and error handling, but this is what's working for now:
Sub FindOutlookValue()
Dim OutApp As Object
Dim OutMail As Object
Dim olInsp As Object
Dim WdDoc As Object
Dim strText As String
On Error Resume Next
'Get Outlook if it's running
Set OutApp = GetObject(, "Outlook.Application")
Set OutMail = OutApp.ActiveExplorer.Selection.Item(1)
With OutMail
Set olInsp = .GetInspector
Set wdDoc = olInsp.WordEditor
strText = WdDoc.Application.Selection.Range.Text
End With
'Find strText in Excel
Dim cl As Range
With Worksheets("MyWorksheet").Cells
Set cl = .Find(strText, After:=.Range(A1), LookIn:=xlValues)
If Not cl Is Nothing Then
cl.Select
End If
End With
End Sub

Resources