Server based rule to collate 500+ adresses into ~150 inbox folders - vba

I have a Company Project where ~500 clients send Emails to the my project inbox. Those clients correspond to ~150 offices (I have an Excel-List of the email addresses & according offices).
Each office shall have one Outlook folder, so I can quickly check upon the past correspondence with a specific office.
The Project inbox is looked after and used by several co-workers, hence server- and not client based rules.
How do I set this up?
My simple idea in form of a pseudo code:
for each arriving email
if (from-adress is in "email & office-List")
move that email to outlook folder "according office name"
end if
end for
and the same for outgoing emails:
for each sent email
if (to-adress is in "email & office-List")
move that email to outlook folder "according office name"
end if
end for
Thanks for suggestions!
...and besides, can outlook folders be created programmatically from a list of names?

My solution is a skript i run daily on a manual basis since my employer doesnt allow scripts on arriving messages.
the logic in short is:
fetch list of emails & their corresponding offices (both string lists)
set up folder variables
loop through messages, and move them eventually
the code looks like
Option Compare Text ' makes string comparisons case insensitive
Sub sortEmails()
'sorts the emails into folders
Dim msg As Outlook.MailItem
Dim itm As Object
Dim adress As String
Dim pa As Outlook.PropertyAccessor
Const PR_SMTP_ADDRESS As String = _
"http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
'1) fetch emails
GetEMailsFolders locIDs, emails, n
'1.5) fetch folder objects
'Create an instance of Outlook & inbox reference
Dim Inbox As Outlook.MAPIFolder
Dim outbox As Outlook.MAPIFolder
Set outlookApp = New Outlook.Application
Set NS = outlookApp.GetNamespace("MAPI")
Set objOwner = NS.CreateRecipient("email#host.com")
objOwner.Resolve
'Set inbox = NS.GetSharedDefaultFolder(objOwner, olFolderInbox)
Set Inbox = NS.Folders("email#host.com").Folders("Inbox")
Set outbox = NS.Folders("email#host.com").Folders("Sent Items")
Dim basefolder As Outlook.MAPIFolder
Dim bfName As String
bfName = "Offices" 'name of the folder for the offices
Set basefolder = MkDirConditional(Inbox.Folders("Project folder"), bfName)
'2)loop through inbox & outbox emails
Dim destination As Outlook.MAPIFolder
Dim fold(1 To 2) As Outlook.MAPIFolder
Set fold(1) = Inbox
Set fold(2) = outbox
Dim LocID As String
For Each fol In fold 'loop through inbox & outbox
Debug.Print fol
'reverse fo loop because otherwise moved messages modify indices of following messages
For i = fol.Items.Count To 1 Step -1 'Each itm In fol.Items
Set itm = fol.Items(i)
If TypeName(itm) = "MailItem" Then ' others are AppointmentItem, MeetingItem, or TaskItem
Set msg = itm
'Debug.Print " " & msg.Subject
If fol = Inbox Then
' there are two formats of email adrersses.
If msg.SenderEmailType = "EX" Then 'check two kinds of email adress formats
adress = msg.Sender.GetExchangeUser().PrimarySmtpAddress
ElseIf msg.SenderEmailType = "SMTP" Then 'SMTP case
adress = msg.SenderEmailAddress
Else
Debug.Print " neither EX nor SMTP" & msg.Subject;
End If
pos = Findstring(adress, emails) ' position in the email / standort list
ElseIf fol = outbox Then
For Each rec In msg.Recipients
Set pa = rec.PropertyAccessor
adress = pa.GetProperty(PR_SMTP_ADDRESS)
pos = Findstring(adress, emails)
If pos > 0 Then
Exit For
End If
Next rec
End If
'4.5) if folder doesnt exist, create it
'5) move message
If pos > 0 Then
'Debug.Print " Its a Match!!"
LocID = locIDs(pos)
Set destination = MkDirConditional(basefolder, LocID)
Debug.Print " " & Left(msg.Subject, 20), adress, pos, destination
msg.Move destination
Else
'Debug.Print " not found!"
End If
Else
'Debug.Print " " & "non-mailitem", itm.Subject
End If
Next i
Next fol
End Sub
'// Function - Check folder Exist
Private Function FolderExists(Inbox As Outlook.MAPIFolder, FolderName As String) As Boolean
Dim Sub_Folder As MAPIFolder
On Error GoTo Exit_Err
Set Sub_Folder = Inbox.Folders(FolderName)
FolderExists = True
Exit Function
Exit_Err:
FolderExists = False
End Function
Function MkDirConditional(basefolder As Outlook.MAPIFolder, newfolder As String) As Outlook.MAPIFolder
Debug.Print newfolder & " ";
If FolderExists(basefolder, newfolder) Then
'folder exists, so just skip
Set MkDirConditional = basefolder.Folders(newfolder)
Debug.Print "exists already"
Else
'folder doesnt exist, make it
Set MkDirConditional = basefolder.Folders.Add(newfolder)
Debug.Print "created"
End If
End Function
'function to compare two strings, min the option compare text at the top line
Function Findstring(str As String, arr As Variant) As Integer
'returns -1 if a string is not found, otherwise its index
Findstring = -1
Dim i As Integer
i = 1
For Each Item In arr
'Debug.Print Item
If str = Item Then
Findstring = i
Exit For
End If
i = i + 1
Next
End Function
' function to fetch the lists of emails and offices
Sub GetEMailsFolders(ByRef rng1 As Variant, ByRef rng2 As Variant, ByRef n As Variant)
'declare variables
Dim xExcelFile As String
Dim xExcelApp As Excel.Application
Dim xWb As Excel.Workbook
Dim xWs As Excel.Worksheet
Dim xExcelRange As Excel.Range
Dim TotalRows As Long
'declare SPOC xls file
xExcelFile = "adresses.xlsx"
'open the file
Set xExcelApp = CreateObject("Excel.Application")
Set xWb = xExcelApp.Workbooks.Open(xExcelFile)
Set xWs = xWb.Sheets(1)
'extract LocIDs (column A), emails (column O) and thir number
n = xWs.Range(xWs.Range("A2"), xWs.Range("A2").End(xlDown)).Count ' works
ReDim rng1(1 To n) As Variant
ReDim rng2(1 To n) As Variant
For i = 1 To n
rng1(i) = xWs.Cells(i + 1, 1)
rng2(i) = xWs.Cells(i + 1, 15)
'Debug.Print rng1(i), rng2(i)
Next
Debug.Print "done reading LocIDs & emails"
End Sub

Related

How to identify emails where sender is also a recipient?

I'm trying to export sender email address and recipient email addresses (to and cc) to Excel. I adapted code I found online. It does most of what I need but there are two problems:
It only works with a single recipient. If there are two or more recipients, it provides names (e.g. Jo Bloggs) instead of email addresses.
It only includes people in the 'To' field, not those in the 'CC' field.
I think the bit that needs fixing is:
'trying to get recipient email address
Dim olEU2 As Outlook.ExchangeUser
Dim oEDL2 As Outlook.ExchangeDistributionList
Dim recip2 As Outlook.Recipient
Set recip2 = Application.Session.CreateRecipient(strColE)
Select Case recip2.AddressEntry.AddressEntryUserType
Case OlAddressEntryUserType.olExchangeUserAddressEntry
Set olEU2 = recip.AddressEntry.GetExchangeUser
If Not (olEU2 Is Nothing) Then
strColE = olEU2.PrimarySmtpAddress
End If
Case OlAddressEntryUserType.olOutlookContactAddressEntry
Set olEU2 = recip.AddressEntry.GetExchangeUser
If Not (olEU2 Is Nothing) Then
strColE = olEU2.PrimarySmtpAddress
End If
Case OlAddressEntryUserType.olExchangeDistributionListAddressEntry
Set oEDL2 = recip.AddressEntry.GetExchangeDistributionList
If Not (oEDL2 Is Nothing) Then
strColE = olEU2.PrimarySmtpAddress
End If
End Select
Full code:
Option Explicit
Sub CopyToExcel()
Dim xlApp As Object
Dim xlWB As Object
Dim xlSheet As Object
Dim rCount As Long
Dim bXStarted As Boolean
Dim enviro As String
Dim strPath As String
Dim objOL As Outlook.Application
Dim objFolder As Outlook.MAPIFolder
Dim objItems As Outlook.Items
Dim obj As Object
Dim olItem 'As Outlook.MailItem
Dim strColA, strColB, strColC, strColD, strColE, strColF As String
' Get Excel set up
enviro = CStr(Environ("USERPROFILE"))
'the path of the workbook
strPath = enviro & "\Documents\Book1.xlsx"
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")
If Err <> 0 Then
Application.StatusBar = "Please wait while Excel source is opened ... "
Set xlApp = CreateObject("Excel.Application")
bXStarted = True
End If
On Error GoTo 0
On Error Resume Next
' Open the workbook to input the data
' Create workbook if doesn't exist
Set xlWB = xlApp.Workbooks.Open(strPath)
If Err <> 0 Then
Set xlWB = xlApp.Workbooks.Add
xlWB.SaveAs FileName:=strPath
End If
On Error GoTo 0
Set xlSheet = xlWB.Sheets("Sheet1")
On Error Resume Next
' add the headers if not present
If xlSheet.Range("A1") = "" Then
xlSheet.Range("A1") = "Sender Name"
xlSheet.Range("B1") = "Sender Email"
xlSheet.Range("C1") = "Subject"
xlSheet.Range("D1") = "Body"
xlSheet.Range("E1") = "Sent To"
xlSheet.Range("F1") = "Date"
End If
'Find the next empty line of the worksheet
rCount = xlSheet.Range("B" & xlSheet.Rows.Count).End(-4162).Row
'needed for Exchange 2016. Remove if causing blank lines.
rCount = rCount + 1
' get the values from outlook
Set objOL = Outlook.Application
Set objFolder = objOL.ActiveExplorer.CurrentFolder
Set objItems = objFolder.Items
For Each obj In objItems
Set olItem = obj
'collect the fields
strColA = olItem.SenderName
strColB = olItem.SenderEmailAddress
strColC = olItem.Subject
strColD = olItem.Body
strColE = olItem.To
strColF = olItem.ReceivedTime
' Get the Exchange address
' if not using Exchange, this block can be removed
Dim olEU As Outlook.ExchangeUser
Dim oEDL As Outlook.ExchangeDistributionList
Dim recip As Outlook.Recipient
Set recip = Application.Session.CreateRecipient(strColB)
If InStr(1, strColB, "/") > 0 Then
' if exchange, get smtp address
Select Case recip.AddressEntry.AddressEntryUserType
Case OlAddressEntryUserType.olExchangeUserAddressEntry
Set olEU = recip.AddressEntry.GetExchangeUser
If Not (olEU Is Nothing) Then
strColB = olEU.PrimarySmtpAddress
End If
Case OlAddressEntryUserType.olOutlookContactAddressEntry
Set olEU = recip.AddressEntry.GetExchangeUser
If Not (olEU Is Nothing) Then
strColB = olEU.PrimarySmtpAddress
End If
Case OlAddressEntryUserType.olExchangeDistributionListAddressEntry
Set oEDL = recip.AddressEntry.GetExchangeDistributionList
If Not (oEDL Is Nothing) Then
strColB = olEU.PrimarySmtpAddress
End If
End Select
End If
' End Exchange section
'trying to get recipient email address
Dim olEU2 As Outlook.ExchangeUser
Dim oEDL2 As Outlook.ExchangeDistributionList
Dim recip2 As Outlook.Recipient
Set recip2 = Application.Session.CreateRecipient(strColE)
Select Case recip2.AddressEntry.AddressEntryUserType
Case OlAddressEntryUserType.olExchangeUserAddressEntry
Set olEU2 = recip.AddressEntry.GetExchangeUser
If Not (olEU2 Is Nothing) Then
strColE = olEU2.PrimarySmtpAddress
End If
Case OlAddressEntryUserType.olOutlookContactAddressEntry
Set olEU2 = recip.AddressEntry.GetExchangeUser
If Not (olEU2 Is Nothing) Then
strColE = olEU2.PrimarySmtpAddress
End If
Case OlAddressEntryUserType.olExchangeDistributionListAddressEntry
Set oEDL2 = recip.AddressEntry.GetExchangeDistributionList
If Not (oEDL2 Is Nothing) Then
strColE = olEU2.PrimarySmtpAddress
End If
End Select
'write them in the excel sheet
xlSheet.Range("A" & rCount) = strColA
xlSheet.Range("B" & rCount) = strColB
xlSheet.Range("c" & rCount) = strColC
xlSheet.Range("d" & rCount) = strColD
xlSheet.Range("e" & rCount) = strColE
xlSheet.Range("f" & rCount) = strColF
'Next row
rCount = rCount + 1
xlWB.Save
Next
' don't wrap lines
xlSheet.Rows.WrapText = False
xlWB.Save
xlWB.Close 1
If bXStarted Then
xlApp.Quit
End If
Set olItem = Nothing
Set obj = Nothing
Set xlApp = Nothing
Set xlWB = Nothing
Set xlSheet = Nothing
End Sub
Iterating through all items in the folder is not really a good idea. I'd recommend starting from the Find/FindNext or Restrict methods instead. Please note that there are some item properties that you can’t use for the filter. You can read more about the properties not allowed in the filter string and string formats used for the search criterion on MSDN.
The following example uses the Restrict method to get all Inbox items of Business category and moves them to the Business folder. To run this example, create or make sure a subfolder called 'Business' exists under Inbox:
Sub MoveItems()
Dim myNamespace As Outlook.NameSpace
Dim myFolder As Outlook.Folder
Dim myItems As Outlook.Items
Dim myRestrictItems As Outlook.Items
Dim myItem As Outlook.MailItem
Set myNamespace = Application.GetNamespace("MAPI")
Set myFolder = _
myNamespace.GetDefaultFolder(olFolderInbox)
Set myItems = myFolder.Items
Set myRestrictItems = myItems.Restrict("[Categories] = 'Business'")
For i = myRestrictItems.Count To 1 Step -1
myRestrictItems(i).Move myFolder.Folders("Business")
Next
End Sub
Also, you may find 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.
Please remember that you can set a suitable filter (View | View Settings |filter) on a folder and study the filter string on the SQL tab of the Filter dialog. Then you can construct any required filter string in the code.
If woudl be nice to use Items.Find/FindNext or Items.Restrict, but I cannot think of a query that would let you do what you want. If it is a one time thing, you have no choice but to loop through all items in a folder and for each item loop through all recipients and compare each recipient's entry id (Recipient.EntryID) with the sender entry id (MailItem.Sender.EntryId).

Targeting specific Outlook Mail folder

I am trying to create an Outlook Macro that will analyze the subject of an Inbox folder and decide where to move them to a subfolder or delete them based on a list of keywords for four different categories.
The problem is that the Inbox I am using is not the regular Inbox (I have two different Inbox folders, and this one is not the default one). So I need to target it in a way similar to writing the full path (Example: "\\xxx#xxx.net\Inbox\"). I tried to find an answer to it but all the info I found here relates to the assumption that we are working from the default Inbox.
Sub CountAttachmentsMulti2()
Dim oItem As Object
Dim iAttachments As Integer
For Each oItem In ActiveExplorer.Selection
iAttachments = oItem.Attachments.Count + iAttachments
If oItem.Attachments.Count <> 0 Then 'Si el mensaje contiene adjuntos
NumofItems = oItem.Attachments.Count + NumofItems
For j = 1 To oItem.Attachments.Count
MsgBox oItem.Attachments.Item(j).DisplayName
Value = oItem.Attachments.Item(j).DisplayName
If InStr(LCase(Value), "su") > 0 Then
MsgBox "Clap"
End If
Next j
Else
MsgBox oItem.Subject 'Get Subject Title
NumofItems = NumofItems + 1
End If
Next
MsgBox "Selected " & ActiveExplorer.Selection.Count & " messages with " & iAttachments & " attachements"
MsgBox "# of items = " & NumofItems
End Sub
This is the code I have tried initially, because before they have already separated by categories. So all that required is to count the total e-mails either by subject or number of attachments.
My issue right now is that I do not know how to target this e-mail account by using a full path.
If I know how to target that folder I think I can solve the rest of the problem myself.
After following the "possible-duplicate" link I was able to complete my code. I apologize because I did not know it was called a reference. Here is my complete solution to the issue:
Sub Test()
Dim objNS As Outlook.NameSpace
Dim objFolder As Outlook.MAPIFolder
Dim Target_Folder As Outlook.MAPIFolder
Dim oItem As Object
Dim iAttachments As Integer
Set objNS = GetNamespace("MAPI")
Set objFolder_root = objNS.Folders("Testing") 'Getting Outlook Container
Set objFolder = objFolder_root.Folders("Inbox") 'Target Inbox of the other container
For Loops = objFolder.Items.Count To 1 Step -1
Set oItem = objFolder.Items(Loops)
If Category1(oItem.Subject) Then
'MsgBox "Clap1"
Set Target_Folder = objFolder_root.Folders("Category 1")
oItem.Move Target_Folder
ElseIf Category2(oItem.Subject) Then
'MsgBox "Clap2"
Set Target_Folder = objFolder_root.Folders("Category 2")
oItem.Move Target_Folder
ElseIf Category3(oItem.Subject) Then
'MsgBox "Clap3"
Set Target_Folder = objFolder_root.Folders("Category 3")
oItem.Move Target_Folder
ElseIf Category4(oItem.Subject) Then
'MsgBox "Clap4"
Set Target_Folder = objFolder_root.Folders("Category 4")
oItem.Move Target_Folder
Else
MsgBox oItem.Subject & " does not belong to any of the 4 categories"
End If
Next
End Sub
Function Category1(value)
Category_1_Keywords = Array("a")
For i = 0 To UBound(Category_1_Keywords)
If InStr(LCase(value), Category_1_Keywords(i)) > 0 Then
Category1 = True
Exit Function
Else
Category1 = False
End If
Next
End Function
There are, of course, more functions, I just posted the Category1 as a reference

SenderEmailAddress in vba code giving path in excel

I have designed a VBA code to retrieve the list of mails from the inbox of your outlook using the link Retrieve maillist from outlook
Here there is a line of code
ThisWorkbook.Sheets(1).Cells(oRow, 5) = Folder.Items.Item(iRow).SenderEmailAddress
which specifies to get senders email Address but when it is stored in excel it shows as below
/O=EXCHANGELABS/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=WIPRO365.ONMICROSOFT.COM-52823-C1374FA5
I would like to see it as knowledge#wipro.com mean to say in the proper email format. How to avail this option? Should I do changes at VBA code or excel.
I have tried this in many blogs still vain. Any suggestions will be helpful.
Firstly, this is multiple dot notation take to its extreme - Folder.Items.Item(iRow). This is a really bad idea, especially in a loop - each "." forces Outlook to create and return a brand new COM object. Cache Folder.Items before entering the loop, and retrieve MailItem using Items.Item(I) only once at the beginning of the loop.
That being said, what you get is a perfectly valid EX type address. Check the MailItem.SenderEmailType property first. If it is "EX", use MailItem.Sender.GetExchangeUser.PrimarySmtpAddress (be prepared to handle nulls). Otherwise just use MailItem.SenderEmailAddress property.
Have a look here for how to look at the Global Address Book
Outlook 2010 GAL with Excel VBA
Here is a very simple implementation that converts to the smtp address for Exchange accounts.
Option Explicit
Dim appOL As Object
Dim oGAL As Object
Dim i
Dim oContact
Dim oUser
Dim UserIndex
Dim arrUsers(1 To 65000, 2) As String
Sub test()
End Sub
Sub Download_Outlook_Mail_To_Excel()
'Add Tools->References->"Microsoft Outlook nn.n Object Library"
'nn.n varies as per our Outlook Installation
Dim folders As Outlook.folders
Dim folder As Outlook.MAPIFolder
Dim iRow As Integer
Dim Pst_Folder_Name
Dim MailboxName
Set appOL = CreateObject("Outlook.Application")
'Mailbox or PST Main Folder Name (As how it is displayed in your Outlook Session)
MailboxName = "your email address"
'Mailbox Folder or PST Folder Name (As how it is displayed in your Outlook Session)
Pst_Folder_Name = "Inbox"
Set folder = Outlook.Session.folders(MailboxName).folders(Pst_Folder_Name)
If folder = "" Then
MsgBox "Invalid Data in Input"
GoTo end_lbl1:
End If
'Rad Through each Mail and export the details to Excel for Email Archival
Sheets(1).Activate
Dim mail As Outlook.MailItem
Dim oAccount As Outlook.Account
Dim stringAddress
FillAddress
For iRow = 1 To folder.Items.Count
If folder.Items.Item(iRow).Class = olMail Then
Set mail = folder.Items.Item(iRow)
Sheets(1).Cells(iRow, 1).Select
Sheets(1).Cells(iRow, 1) = mail.SenderName
Sheets(1).Cells(iRow, 2) = mail.Subject
Sheets(1).Cells(iRow, 3) = mail.ReceivedTime
Sheets(1).Cells(iRow, 4) = mail.Size
Select Case mail.SenderEmailType
Case "SMTP"
Sheets(1).Cells(iRow, 5) = mail.SenderEmailAddress
Case "EX"
'Set oAccount = Outlook.
stringAddress = FindAddress(mail.SenderEmailAddress)
Sheets(1).Cells(iRow, 5) = stringAddress
End Select
End If
'Set oAccount = mail.SenderEmailAddress
'Sheets(1).Cells(iRow, 6) = Folder.Items.Item(iRow).Body
Next iRow
MsgBox "Outlook Mails Extracted to Excel"
end_lbl1:
End Sub
Function FindAddress(strAddress)
Dim address As String
For i = 1 To 65000
If UCase(arrUsers(i, 0)) = strAddress Then
address = arrUsers(i, 2)
Exit For
End If
Next
FindAddress = address
End Function
Sub FillAddress()
Set oGAL = appOL.GetNamespace("MAPI").AddressLists("Global Address List").AddressEntries
For i = 1 To oGAL.Count
Set oContact = oGAL.Item(i)
If oContact.AddressEntryUserType = 0 Then
Set oUser = oContact.GetExchangeUser
If Len(oUser.LastName) > 0 Then
UserIndex = UserIndex + 1
arrUsers(UserIndex, 0) = oUser.address
arrUsers(UserIndex, 1) = oUser.Name
arrUsers(UserIndex, 2) = oUser.PrimarySmtpAddress
End If
End If
Next i
End Sub

How do you extract email addresses from the 'To' field in outlook?

I have been using VBA to some degree, using this code:
Sub ExtractEmail()
Dim OlApp As Outlook.Application
Dim Mailobject As Object
Dim Email As String
Dim NS As NameSpace
Dim Folder As MAPIFolder
Set OlApp = CreateObject("Outlook.Application")
' Setup Namespace
Set NS = ThisOutlookSession.Session
' Display select folder dialog
Set Folder = NS.PickFolder
' Create Text File
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("c:\mydocuments\emailss.txt", True)
' loop to read email address from mail items.
For Each Mailobject In Folder.Items
Email = Mailobject.To
a.WriteLine (Email)
Next
Set OlApp = Nothing
Set Mailobject = Nothing
a.Close
End Sub
However this gives output as the names of the email addresses and not the actual email address with the "something#this.domain".
Is there an attributte of the mailobject that will allow the email addresses and not the names to be written from the 'To' Textbox.
Thanks
Check out the Recipients collection object for your mail item, which should allow you to get the address: http://msdn.microsoft.com/en-us/library/office/ff868695.aspx
Update 8/10/2017
Looking back on this answer, I realized I did a bad thing by only linking somewhere and not providing a bit more info.
Here's a code snippet from that MSDN link above, showing how the Recipients object can be used to get an email address (snippet is in VBA):
Sub GetSMTPAddressForRecipients(mail As Outlook.MailItem)
Dim recips As Outlook.Recipients
Dim recip As Outlook.Recipient
Dim pa As Outlook.PropertyAccessor
Const PR_SMTP_ADDRESS As String = _
"http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
Set recips = mail.Recipients
For Each recip In recips
Set pa = recip.PropertyAccessor
Debug.Print recip.name &; " SMTP=" _
&; pa.GetProperty(PR_SMTP_ADDRESS)
Next
End Sub
It looks like, for email addresses outside of your organization, the SMTP address is hidden in emailObject.Recipients(i).Address, though it doesn't seem to allow you to distinguish To/CC/BCC.
The Microsoft code was giving me an error, and some investigating reveals that the schema page is no longer available. I wanted a semicolon-delaminated list of email addresses that were either in my Exchange organization or outside of it. Combining it with another S/O answer to convert inner-company email display names to SMTP names, this does the trick.
Function getRecepientEmailAddress(eml As Variant)
Set out = CreateObject("System.Collections.Arraylist") ' a JavaScript-y array
For Each emlAddr In eml.Recipients
If Left(emlAddr.Address, 1) = "/" Then
' it's an Exchange email address... resolve it to an SMTP email address
out.Add ResolveDisplayNameToSMTP(emlAddr)
Else
out.Add emlAddr.Address
End If
Next
getRecepientEmailAddres = Join(out.ToArray(), ";")
End Function
If the email is inside your organization, you need to convert it to an SMTP email address. I found this function from another StackOverflow answer helpful:
Function ResolveDisplayNameToSMTP(sFromName) As String
' takes a Display Name (i.e. "James Smith") and turns it into an email address (james.smith#myco.com)
' necessary because the Outlook address is a long, convoluted string when the email is going to someone in the organization.
' source: https://stackoverflow.com/questions/31161726/creating-a-check-names-button-in-excel
Dim OLApp As Object 'Outlook.Application
Dim oRecip As Object 'Outlook.Recipient
Dim oEU As Object 'Outlook.ExchangeUser
Dim oEDL As Object 'Outlook.ExchangeDistributionList
Set OLApp = CreateObject("Outlook.Application")
Set oRecip = OLApp.Session.CreateRecipient(sFromName)
oRecip.Resolve
If oRecip.Resolved Then
Select Case oRecip.AddressEntry.AddressEntryUserType
Case 0, 5 'olExchangeUserAddressEntry & olExchangeRemoteUserAddressEntry
Set oEU = oRecip.AddressEntry.GetExchangeUser
If Not (oEU Is Nothing) Then
ResolveDisplayNameToSMTP = oEU.PrimarySmtpAddress
End If
Case 10, 30 'olOutlookContactAddressEntry & 'olSmtpAddressEntry
ResolveDisplayNameToSMTP = oRecip.AddressEntry.Address
End Select
End If
End Function
The answers above did not work for me. I think they only work when the recipient is in the address book. The following code is also to able to lookup email addresses from outside the organisation. Additionally it makes a distinction between to/cc/bcc
Dim olRecipient As Outlook.Recipient
Dim strToEmails, strCcEmails, strBCcEmails As String
For Each olRecipient In item.Recipients
Dim mail As String
If olRecipient.AddressEntry Is Nothing Then
mail = olRecipient.Address
ElseIf olRecipient.AddressEntry.GetExchangeUser Is Nothing Then
mail = olRecipient.Address
Else
mail = olRecipient.AddressEntry.GetExchangeUser.PrimarySmtpAddress
End If
Debug.Print "resolved", olRecipient.Name, mail
If olRecipient.Type = Outlook.OlMailRecipientType.olTo Then
strToEmails = strToEmails + mail & ";"
ElseIf olRecipient.Type = Outlook.OlMailRecipientType.olCC Then
strCcEmails = strCcEmails + mail & ";"
ElseIf olRecipient.Type = Outlook.OlMailRecipientType.olBCC Then
strBCcEmails = strBCcEmails + mail & ";"
End If
Next
Debug.Print strToEmails
Debug.Print strCcEmails
Debug.Print strBCcEmails
Another code alternative (based initially on the answer by #andreasDL) which should be able to be used...
Pass in a MailItem to the EmailAddressInfo function to get an array of the Sender, To and CC fields from the message
Private Const olOriginator As Long = 0, olTo As Long = 1, olCC As Long = 2, olBCC As Long = 3
'BCC addresses are not included within received messages
Function PrintEmailAddresses(olItem As MailItem)
If olItem.Class <> olMail Then Exit Function
Dim Arr As Variant: Arr = EmailAddressInfo(olItem)
Debug.Print "Sender: " & Arr(olOriginator)
Debug.Print "To Address: " & Arr(olTo)
Debug.Print "CC Address: " & Arr(olCC)
End Function
Private Function EmailAddressInfo(olItem As MailItem) As Variant
If olItem.Class <> olMail Then Exit Function
On Error GoTo ExitFunction
Dim olRecipient As Outlook.Recipient
Dim olEU As Outlook.ExchangeUser
Dim olEDL As Outlook.ExchangeDistributionList
Dim ToAddress, CCAddress, Originator, email As String
With olItem
Select Case UCase(.SenderEmailType)
Case "SMTP": Originator = .SenderEmailAddress
Case Else
Set olEU = .Sender.GetExchangeUser
If Not olEU Is Nothing Then Originator = olEU.PrimarySmtpAddress
End Select
End With
For Each olRecipient In olItem.Recipients
With olRecipient
Select Case .AddressEntry.AddressEntryUserType
Case olSmtpAddressEntry 'OlAddressEntryUserType.
email = .Address
Case olExchangeDistributionListAddressEntry, olOutlookDistributionListAddressEntry
Set olEDL = .AddressEntry.GetExchangeDistributionList
email = IIf(Not olEDL Is Nothing, olEDL.PrimarySmtpAddress, "")
Case Else
Set olEU = .AddressEntry.GetExchangeUser
email = IIf(Not olEU Is Nothing, olEU.PrimarySmtpAddress, "")
End Select
If email <> "" Then
Select Case .Type
Case olTo: ToAddress = ToAddress & email & ";"
Case olCC: CCAddress = CCAddress & email & ";"
End Select
End If
End With
Next
EmailAddressInfo = Array(Originator, ToAddress, CCAddress)
ExitFunction:
End Function
This is what worked for me with Outlook 2019. Use your internal domain name(s). Might need some tweaking yet - not heavily tested. Place code in the ThisOutlookSession module. (Updated to handle Exchange distribution lists 7/31/20.)
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim xMailItem As Outlook.MailItem
Dim xRecipients As Outlook.Recipients
Dim OutRec As Outlook.Recipient
Dim OutTI As Outlook.TaskItem
Dim i As Long
Dim j As Long
Dim xOKCancel As Integer
Dim sMsg As String
Dim oMembers As AddressEntries
Dim oMember As AddressEntry
Dim sDomains As String
Dim sTemp As String
On Error Resume Next
If Item.Class <> olMail Then GoTo ExitCode
sDomains = "#test1.com #test2.com"
Set xMailItem = Item
Set xRecipients = xMailItem.Recipients
'Loop through email recipients to get email addresses
For i = xRecipients.Count To 1 Step -1
'If we have a text address entry in the email
If InStr(xRecipients.Item(i).AddressEntry, "#") > 0 Then
sTemp = xRecipients.Item(i).AddressEntry
If InStrRev(sDomains, LCase(Mid(sTemp, InStr(sTemp, "#"), 254))) <= 0 Then
sMsg = sMsg & sTemp & vbCrLf
End If
Else
Select Case xRecipients.Item(i).AddressEntry.DisplayType
Case Is = olDistList
Set oMembers = xRecipients.Item(i).AddressEntry.Members
For j = oMembers.Count To 1 Step -1
Set oMember = oMembers.Item(j)
sTemp = oMember.GetExchangeUser.PrimarySmtpAddress
If InStrRev(sDomains, LCase(Mid(sTemp, InStr(sTemp, "#"), 254))) <= 0 Then
sMsg = sMsg & sTemp & vbCrLf
End If
Set oMember = Nothing
Next j
Set oMembers = Nothing
Case Is = olUser
Set OutTI = Application.CreateItem(3)
OutTI.Assign
Set OutRec = OutTI.Recipients.Add(xRecipients.Item(i).AddressEntry)
OutRec.Resolve
If OutRec.Resolved Then
sTemp = OutRec.AddressEntry.GetExchangeUser.PrimarySmtpAddress
If InStrRev(sDomains, LCase(Mid(sTemp, InStr(sTemp, "#"), 254))) <= 0 Then
sMsg = sMsg & sTemp & vbCrLf
End If
End If
Set OutTI = Nothing
Set OutRec = Nothing
Case Else
MsgBox "Unaccomodated AddressEntry.DisplayType."
GoTo ExitCode
End Select
End If
Next i
'Display user message
If Len(sMsg) > 0 Then
sMsg = "This email is addressed to the following external Recipients:" & vbCrLf & vbCrLf & sMsg
xOKCancel = MsgBox(sMsg, vbOKCancel + vbQuestion, "Warning")
If xOKCancel = vbCancel Then Cancel = True
End If
End Sub

Run rules on the Sent Items folder

I have rules in Outlook for incoming and posted emails.
I found in the web the following routine which works for the emails which are in the 'INBOX'.
I am not able to use the GetRootFolder to select the 'Sent Items' folder.
The routine is the following:
Sub RunRules()
Dim st As Outlook.Store
Dim myRules As Outlook.rules
Dim rl As Outlook.Rule
Dim count As Integer
Dim k As Long
Dim fname As String
Dim currentcount As Integer
Dim prova As String
Dim numero As Integer
Dim prova1 As String
Dim Nrules As Integer
Dim objFolder, objNamespace, objOutlook, objFile
Set objOutlook = CreateObject("Outlook.Application")
Set objNamespace = objOutlook.GetNamespace("MAPI")
objNamespace.Logon "Default Outlook Profile", , False, False
numero = 1
' this is for the SENT Items
fname = "I"
count = 1
k = 1
Set rl = Nothing
Set st = Nothing
Set myRules = Nothing
'On Error Resume Next
' get default store (where rules live)
Set st = Application.Session.DefaultStore
Application.Session.DefaultStore.GetRootFolder (olFolderSentMail)
' get rules
Set myRules = st.GetRules
For k = 1 To myRules.count ' might be 0-based, didnt check
On Error Resume Next
Set rl = Nothing
Set rl = myRules(k)
If rl.RuleType = olRuleReceive Then 'determine if it’s an Inbox rule, if so, run it
' I selecto just the rules that are for the sent ITEMS
prova = rl.Name
prova1 = Left(prova, 1)
If prova1 = fname Then
rl.Execute ShowProgress:=True
objFile.WriteLine rl.Name
count = count + 1
prova = ""
prova1 = ""
End If
End If
Next
Set rl(count) = Nothing
Set st = Nothing
Set myRules = Nothing
Set objFolder = Nothing
End Sub
Sorry, I did not notice your question before.
I have not tried your code. Instead I show an extract from one of my routines which moves selected properties to variables from every mail item in the Sent Items folders.
Hope this helps.
Dim FolderTgt As MAPIFolder
Dim HtmlBody As String
Dim InxItemCrnt As Long
Dim ReceivedTime As Date
Dim SenderEmailAddress As String
Dim SenderName As String
Dim Subject As String
Dim TextBody As String
Dim FolderTgt As MAPIFolder
Set FolderTgt = CreateObject("Outlook.Application"). _
GetNamespace("MAPI").GetDefaultFolder(olFolderSentMail)
For InxItemCrnt = FolderTgt.Items.Count To 1 Step -1
With FolderTgt.Items.Item(InxItemCrnt)
If .Class = olMail Then
' Save selected data to variables
ReceivedTime = .ReceivedTime
Subject = .Subject
SenderName = .SenderName
SenderEmailAddress = .SenderEmailAddress
TextBody = .Body
HtmlBody = .HtmlBody
End If
End With
Next
Unless specified, rules run on the Inbox of the default store, regardless of the store with the rules.
Option Explicit
Sub RunRules()
Dim st As Store
Dim myRules As rules
Dim rl As Rule
Dim count As Long
Dim k As Long
Dim fname As String
Dim prova As String
Dim prova1 As String
Dim objFolder As Folder
' this is for the SENT Items
fname = "I"
' get store (can be any store)
Set st = Session.defaultStore
' get rules from specified store
Set myRules = st.GetRules()
' The default folder is the Inbox of the default store,
' regardless of the store with the rules.
Set objFolder = st.GetDefaultFolder(olFolderSentMail)
Debug.Print "objFolder: " & objFolder
For k = 1 To myRules.count
Set rl = myRules(k)
Debug.Print rl.RuleType ' 0 = olRuleReceive, 1 = olRuleSend
If rl.RuleType = olRuleSend Then
' rule name starting with "I"
prova = rl.name
prova1 = Left(prova, 1)
If prova1 = fname Then
' Designate a folder if not the Inbox of the default store
rl.Execute ShowProgress:=True, Folder:=objFolder
count = count + 1
End If
End If
Next
Debug.Print count & " rules processed"
End Sub