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.
Related
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
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.
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
with the below VBA coding, I am able to send all the emails from outlook draft folder but the only problem is that I have to provide the parent folder name. Can we get this details through coding as this macro will be used by other user who is not familiar with VBA.
Dim lDraftItem As Long
Dim myOutlook As Outlook.Application
Dim myNameSpace As Outlook.NameSpace
Dim myFolders As Outlook.Folders
Dim myDraftsFolder As Outlook.MAPIFolder
Set myOutlook = Outlook.Application
Set myNameSpace = myOutlook.GetNamespace("MAPI")
Set myFolders = myNameSpace.Folders
Set myDraftsFolder = myFolders("Gaus_Shaikh2#syntelinc.com").Folders("Drafts")
For lDraftItem = myDraftsFolder.Items.Count To 1 Step -1
If Len(Trim(myDraftsFolder.Items.Item(lDraftItem).To)) > 0 Then
myDraftsFolder.Items.Item(lDraftItem).Send
End If
Next lDraftItem
This should work...
Set myDraftsFolder = myNamespace.GetDefaultFolder(olFolderDrafts)
* Edit *
The code below is probably a better function to use; it has error checking incorporated so any e-mails with invalid fields in the 'To' section shouldn't abort the function
Sub TestSendDrafts()
Call SendDraftMail
End Sub
Function SendDraftMail() As Boolean
On Error GoTo ExitFunction
Dim ThisNameSpace As Outlook.NameSpace: Set ThisNameSpace = Application.GetNamespace("MAPI")
Dim DraftFolder As Outlook.MAPIFolder: Set DraftFolder = ThisNameSpace.GetDefaultFolder(olFolderDrafts)
Dim Var As Variant, i As Long, Difference As Long, SentItems As Long
For i = DraftFolder.Items.Count To 1 Step -1
Set Var = DraftFolder.Items.Item(i)
DoEvents
If Var.Class = olMail Then
If Len(Trim(Var.To)) > 0 Then
On Error Resume Next
Var.Send
If Err.Number = 0 Then SentItems = SentItems + 1
On Error GoTo ExitFunction
End If
End If
Next i
Debug.Print "Sent " & SentItems & " message(s) from 'Draft E-mail'."
SendDraftMail = True
ExitFunction:
End Function
Replace this line:
Set myDraftsFolder = myFolders("Gaus_Shaikh2#syntelinc.com").Folders("Drafts")
With these three lines:
Dim sUser As String
sUser = myFolders.Item(2).Name
Set myDraftsFolder = myFolders(sUser).Folders("Drafts")
The 2nd folder name will be the user account name (e-mail address), which you can store as a string and pass into myFolders() to qualify their specific account.
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