Run rules on the Sent Items folder - vba

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

Related

Outlook mail item multiple restrict methods

I am trying to apply a filter using the restrict method of mail items using Outlook VBA. Below code works fine if I use only one restrict method based on Category_Filter variable, but when I try to use two restrict methods,
Somehow my Flag_Filter fails.
I believe I am making some mistake in concatenation for Flag_Filter and need some clue here.
Sub ApplyFilters()
Dim ol As Outlook.Application
Dim ns As Outlook.NameSpace
Dim fol As Outlook.Folder
Dim i As Object
Dim mi As Outlook.MailItem
Dim OrderNumber, Category_Filter, Flag_Filter As String
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.GetDefaultFolder(olFolderInbox)
OrderNumber = "GCU5689"
Category_Filter = "[Categories] = 'Textile'"
Flag_Filter = "[FlagRequest] = " & OrderNumber
For Each i In fol.Items.Restrict(Category_Filter).Restrict(Flag_Filter)
' Some actions
Next i
End Sub
You cannot restrict a restricted collection. You need to combine the restrictions (using "and" ) into a single query and call Restrict only once.
like #Dmitry Streblechenko says use And Operator (Visual Basic) MSDN with your Outlook restrict method
Example
Option Explicit
Private Sub Examples()
Dim olNs As Outlook.NameSpace
Dim Inbox As Outlook.MAPIFolder
Dim Items As Outlook.Items
Dim Msg As String
Dim i As Long
Dim Filter As String
Set olNs = Application.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Filter = "[Categories] = 'Textile' And [FlagRequest] = 'Follow up'"
Set Items = Inbox.Items.Restrict(Filter)
Msg = Items.Count & " Items in " & Inbox.Name
If MsgBox(Msg, vbYesNo) = vbYes Then
For i = Items.Count To 1 Step -1
Debug.Print Items(i) 'Immediate Window
Next
End If
End Sub
More examples here
for variable you can use the chr(34) function to represent the double quote (whose ASCII character value is 34) that is used as an escape character or use double quote.
Example
Dim OrderNumber As String
OrderNumber = "GCU5689"
Filter = "[Categories] = 'Textile' And [FlagRequest] = '" & OrderNumber & "'"
In general you may apply multiple actions one at a time. Such an approach allows easier troubleshooting.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
Sub ApplyFilters()
Dim ns As NameSpace
Dim fol As folder
Dim i As Object
Dim mi As MailItem
Dim resItms As Items
Dim OrderNumber As String
Dim Category_Filter As String
Dim Flag_Filter As String
Set ns = GetNamespace("MAPI")
Set fol = ns.GetDefaultFolder(olFolderInbox)
OrderNumber = "GCU5689"
Category_Filter = "[Categories] = 'Textile'"
Flag_Filter = "[FlagRequest] = " & OrderNumber
Set resItms = fol.Items.Restrict(Category_Filter)
Debug.Print "Items after first restrict: " & resItms.Count
Set resItms = resItms.Restrict(Flag_Filter)
Debug.Print "Items after second restrict: " & resItms.Count
For Each i In resItms
' Some actions
' If, for instance, a property is only found on mailitems
If i.Class = olmail Then
Set mi = i
Debug.Print mi.Subject
End If
Next
End Sub

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).

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

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

Passing Outlook mail body to string

I'm trying to gather email addresses from bad responses to an email blast.
The code is split into two parts, the search part, which searches for a character in the email and returns the string before and after it, and the process part, which runs the search on every email in an Outlook folder.
I've tested the search on emails that I've copied into Excel and it works.
The issue I'm having is I can't pass the email body, which is an object, to a string variable.
Sub Extract()
On Error Resume Next
'specify the folder to pull emails from
Set myOlApp = Outlook.Application
Set mynamespace = myOlApp.GetNamespace("mapi")
Set myfolder = myOlApp.ActiveExplorer.CurrentFolder
Dim myitem As Outlook.MailItem
'start excel and open spreadsheet
Set xlobj = CreateObject("excel.application")
xlobj.Visible = True
xlobj.Workbooks.Add
'Set Heading on spreadsheet
xlobj.Range("a" & 1).Value = "Email"
'for loop passing email body to search code
For i = 1 To myfolder.Items.Count
Set myitem = myfolder.Items(i)
extractStr = myitem.Body
'search for specific text
CheckStr = "[A-Za-z0-9._-]"
OutStr = ""
Index = 1
Index1 = VBA.InStr(Index, extractStr, "#")
getStr = ""
If Index1 > 0 Then
For p = Index1 - 1 To 1 Step -1
If Mid(extractStr, p, 1) Like CheckStr Then
getStr = Mid(extractStr, p, 1) & getStr
Else
Exit For
End If
Next
getStr = getStr & "#"
For p = Index1 + 1 To Len(extractStr)
If Mid(extractStr, p, 1) Like CheckStr Then
getStr = getStr & Mid(extractStr, p, 1)
Else
Exit For
End If
Next
Index = Index1 + 1
If OutStr = "" Then
OutStr = getStr
Else
OutStr = OutStr & Chr(10) & getStr
End If
Else
GoTo 20
End If
'write to excel
20 xlobj.Range("a" & i + 1).Value = OutStr
Next
End Sub
Update: I think I've got it figured out. To test this script I place one or two of the emails to pull email addresses from into a test folder. The emails I selected were html formatted! I put the following line of code to convert the current email body (myitem) to plain text.
myitem.BodyFormat = olFormatPlain
I've declared the myitem variable as both an object and a mailitem. When I run the script with myitem as an object I get an "object doesn't support this property or method" error at the following line:
myitem.BodyFormat = olFormatPlain
However, when I run it as a mail item I get a type mismatch error at this line:
For Each myitem In myfolder
Here's how I'm declaring the myitem variable in the two different scenarios:
Dim myitem as MailItem
Dim myitem as Object
Here's my updated code.
Option Explicit
Sub Extract()
'On Error Resume Next
'Variable declaration
Dim myOlApp As Outlook.Application
Dim mynamespace As Outlook.NameSpace
Dim myfolder As Selection
Dim myitem As MailItem
Dim i As Integer
Dim extractStr As String
Dim CheckStr As String
Dim OutStr As String
Dim Index As Integer
Dim Index1 As Integer
Dim getStr As String
Dim p As Integer
'start excel and open spreadsheet
Dim xlobj As Object
Set xlobj = CreateObject("excel.application")
xlobj.Visible = True
xlobj.Workbooks.Add
'Set Heading on spreadsheet
xlobj.Range("a" & 1).Value = "Email"
'Set reference to the Selection.
Set myOlApp = Outlook.Application
Set mynamespace = myOlApp.GetNamespace("mapi")
Set myfolder = ActiveExplorer.Selection
'for loop passing email body to search code
For Each myitem In myfolder
myitem.BodyFormat = olFormatPlain
extractStr = myitem.Body
MsgBox (extractStr)
'search for specific text
CheckStr = "[A-Za-z0-9._-]"
OutStr = ""
Index = 1
Index1 = VBA.InStr(Index, extractStr, "#")
getStr = ""
If Index1 > 0 Then
For p = Index1 - 1 To 1 Step -1
If Mid(extractStr, p, 1) Like CheckStr Then
getStr = Mid(extractStr, p, 1) & getStr
Else
Exit For
End If
Next
getStr = getStr & "#"
For p = Index1 + 1 To Len(extractStr)
If Mid(extractStr, p, 1) Like CheckStr Then
getStr = getStr & Mid(extractStr, p, 1)
Else
Exit For
End If
Next
Index = Index1 + 1
If OutStr = "" Then
OutStr = getStr
Else
OutStr = OutStr & Chr(10) & getStr
End If
Else
GoTo 20
End If
'write to excel
20 xlobj.Range("a" & i + 1).Value = OutStr
Next
End Sub
I had two issues I needed to address. The first was selecting the correct folder for which to pull emails. Because I was using subfolders within the default folders I needed to declare each individually, similar to how you would move between folders in a Linux system.
'Set reference to the Selection.
Set myOlApp = Outlook.Application
Set mynamespace = myOlApp.GetNamespace("mapi")
Set myfolder = mynamespace.GetDefaultFolder(olFolderInbox)
Set myfolder = myfolder.Folders.Item("2/19 Training Email Blast")
Set myfolder = myfolder.Folders.Item("bad emails")
The second issue was passing my email body to a string variable. When I did it the body text would be converted to something I didn't recognize. I used the StrConv function to convert it back to unicode.
extractStr = StrConv(myitem.Body, vbUnicode)
The last thing I have to do is clean it up a bit. Thanks to #niton I'll be able to parse the emails that are actually reports and those that are mailitems to handle them differently.
Thanks to everyone who commented and provided answers!
Here's a copy of the entire code:
Option Explicit
Sub Extract()
On Error Resume Next
'Variable declaration
Dim myOlApp As Outlook.Application
Dim mynamespace As Outlook.NameSpace
Dim myfolder As Object
Dim myitem As Object
Dim i As Integer
Dim extractStr As String
Dim CheckStr As String
Dim OutStr As String
Dim Index As Integer
Dim Index1 As Integer
Dim getStr As String
Dim p As Integer
Dim xlobj As Object
'start excel and open spreadsheet
Set xlobj = CreateObject("excel.application")
xlobj.Visible = True
xlobj.Workbooks.Add
'Set Heading on spreadsheet
xlobj.Range("a" & 1).Value = "Email"
'Set reference to the Selection.
Set myOlApp = Outlook.Application
Set mynamespace = myOlApp.GetNamespace("mapi")
Set myfolder = mynamespace.GetDefaultFolder(olFolderInbox)
Set myfolder = myfolder.Folders.Item("2/19 Training Email Blast")
Set myfolder = myfolder.Folders.Item("bad emails")
'for loop passing email body to search code
For i = 1 To myfolder.Items.Count
Set myitem = myfolder.Items(i)
extractStr = StrConv(myitem.Body, vbUnicode)
myitem.Body = extractStr
'search for specific text
CheckStr = "[A-Za-z0-9._-]"
OutStr = ""
Index = 1
Index1 = InStr(Index, extractStr, "#")
getStr = ""
If Index1 > 0 Then
For p = Index1 - 1 To 1 Step -1
If Mid(extractStr, p, 1) Like CheckStr Then
getStr = Mid(extractStr, p, 1) & getStr
Else
Exit For
End If
Next
getStr = getStr & "#"
For p = Index1 + 1 To Len(extractStr)
If Mid(extractStr, p, 1) Like CheckStr Then
getStr = getStr & Mid(extractStr, p, 1)
Else
Exit For
End If
Next
Index = Index1 + 1
If OutStr = "" Then
OutStr = getStr
Else
OutStr = OutStr & Chr(10) & getStr
End If
Else
GoTo 20
End If
'write to excel
20 xlobj.Range("a" & i + 1).Value = OutStr
Next
End Sub
The standard method is to declare as Object, not a specific data type, then check that the item is that data type using Class or Typename.
When is a MailItem not a MailItem?
Option Explicit
Sub Extract()
'Variable declaration
Dim myOlApp As Outlook.Application
Dim mynamespace As Outlook.NameSpace
Dim myfolder As Selection
'Dim myitem As MailItem
Dim myitem As Object
Dim extractStr As String
'Set reference to the Selection.
Set myOlApp = Outlook.Application
Set mynamespace = myOlApp.GetNamespace("mapi")
Set myfolder = ActiveExplorer.Selection
'for loop passing email body to search code
For Each myitem In myfolder
if myitem.class = olmail then
myitem.BodyFormat = olFormatPlain
extractStr = myitem.Body
MsgBox extractStr
else
extractStr = myitem.Body
Msgbox "Not a mailitem " & extractStr
end if
Next
End Sub
The ReportItem object is similar to a MailItem object, and it contains a report (usually the non-delivery report).
Note a Reportitem has no BodyFormat property.

vba outlook: get emails and export to excel and save attachments

I have tried to put together a piece of vba code which does the following.
first it looks for all emails in my inbox folder for the account NewSuppliers#Hewden.co.uk where the subject contains certain key words.
Secondly it looks for all emails in my inbox folder CreditChecks#Hewden.co.uk where the subject contains certain keywords.
Then it exports certain data into excel row after row.
This works fine except with my emails which I export from the CreditChecks#Hewden.co.uk inbox, I want to export only the emails which contains a pdf attachment and save this attachment in a directory and place each seperate pdf document in a folder with the same name as the pdf file.
I've tested my save attachment and export emails scripts separate and they work fine but when I put them together I get an error saying
method or object not found
Set objAttachments = Outlook.Attachments
Can someone please help me get my code to do what I need it to do? Thanks in advance
Here is my code:
'On the next line edit the path to the spreadsheet you want to export to
Const WORKBOOK_PATH = "X:\New_Supplier_Set_Ups_&_Audits\NewSupplierSet-Up.xls"
'On the next line edit the name of the sheet you want to export to
Const SHEET_NAME = "Validations"
Const SHEET_NAME2 = "BankSetup"
Const SHEET_NAME3 = "CreditChecks"
Const MACRO_NAME = "Export Messages to Excel (Rev 7)"
Private Sub Application_Startup()
Dim olkMsg As Object, _
olkMsg2 As Object, _
excApp As Object, _
excWkb As Object, _
excWks As Object, _
excWks2 As Object, _
excWks3 As Object, _
intRow As Integer, _
intRow2 As Integer, _
intRow3 As Integer, _
intExp As Integer, _
intVersion As Integer
intVersion = GetOutlookVersion()
Set excApp = CreateObject("Excel.Application")
Set excWkb = excApp.Workbooks.Open(WORKBOOK_PATH)
Set excWks = excWkb.Worksheets(SHEET_NAME)
Set excWks2 = excWkb.Worksheets(SHEET_NAME2)
Set excWks3 = excWkb.Worksheets(SHEET_NAME3)
intRow = excWks.UsedRange.Rows.Count + 1
intRow2 = excWks2.UsedRange.Rows.Count + 1
intRow3 = excWks3.UsedRange.Rows.Count + 1
'Write messages to spreadsheet
Dim ns As Outlook.NameSpace
Dim Items As Outlook.Items
Dim Items2 As Outlook.Items
Dim objAttachments As Outlook.Attachments
Dim objMsg As Outlook.MailItem 'Object
Dim i As Long
Dim lngCount As Long
Dim strFile As String
Dim strFolderPath As String
Dim strDeletedFiles As String
Dim withParts As String
Dim withoutParts As String
' Get the MAPI Namespace
Set ns = Application.GetNamespace("MAPI")
' Get the Items for the Inbox in the specified account
Set Items = ns.Folders("New Suppliers").Folders("Inbox").Items
Set Items2 = ns.Folders("Credit Checks").Folders("Inbox").Items
Set objAttachments = Outlook.Attachments
' Start looping through the items
For Each olkMsg In Items
'Only export messages, not receipts or appointment requests, etc.
If olkMsg.class = olMail Then
If olkMsg.Subject Like "Accept: New Supplier Request*" Or olkMsg.Subject Like "Reject: New Supplier Request*" Then
'Add a row for each field in the message you want to export
excWks.Cells(intRow, 1) = olkMsg.ReceivedTime
Dim LResult As String
LResult = Replace(GetSMTPAddress(olkMsg, intVersion), ".", " ")
LResult = Left(LResult, InStrRev(LResult, "#") - 1)
excWks.Cells(intRow, 2) = LResult
excWks.Cells(intRow, 3) = olkMsg.VotingResponse
Dim s As String
s = olkMsg.Subject
Dim indexOfName As Integer
indexOfName = InStr(1, s, "Reference: ")
Dim finalString As String
finalString = Right(s, Len(s) - indexOfName - 10)
excWks.Cells(intRow, 4) = finalString
intRow = intRow + 1
End If
End If
If olkMsg.class = olMail Then
If olkMsg.Subject Like "Complete: Bank Details Set-Up for New Supplier*" Or olkMsg.Subject Like "Incomplete: Bank Details Set-Up for New Supplier*" Then
'Add a row for each field in the message you want to export
excWks2.Cells(intRow2, 1) = olkMsg.ReceivedTime
Dim LResult2 As String
LResult2 = Replace(GetSMTPAddress(olkMsg, intVersion), ".", " ")
LResult2 = Left(LResult2, InStrRev(LResult2, "#") - 1)
excWks2.Cells(intRow2, 2) = LResult2
excWks2.Cells(intRow2, 3) = olkMsg.VotingResponse
Dim s2 As String
s2 = olkMsg.Subject
Dim indexOfName2 As Integer
indexOfName2 = InStr(1, s2, "Reference: ")
Dim finalString2 As String
finalString2 = Right(s2, Len(s2) - indexOfName2 - 10)
excWks2.Cells(intRow2, 4) = finalString2
intRow2 = intRow2 + 1
End If
End If
Next
strFolderPath = "\\UKSH000-FILE06\Purchasing\New_Supplier_Set_Ups_&_Audits\ATTACHMENTS\"
Set objAttachments = objMsg.Attachments
lngCount = objAttachments.Count
For Each olkMsg2 In Items2
If olkMsg2.class = olMail Then
If olkMsg2.Subject Like "RE: New Supplier Credit*" Then
If lngCount > 0 Then
For i = lngCount To 1 Step -1
strFile = objAttachments.item(i).FileName
If Right(strFile, 3) = "pdf" Then
' Combine with the path to the Temp folder.
withParts = strFile
withoutParts = Replace(withParts, ".pdf", "")
strFile = strFolderPath & withoutParts & "\" & strFile
' Save the attachment as a file.
objAttachments.item(i).SaveAsFile strFile
'Add a row for each field in the message you want to export
excWks3.Cells(intRow3, 1) = olkMsg2.ReceivedTime
Dim LResult3 As String
LResult3 = Replace(GetSMTPAddress(olkMsg2, intVersion), ".", " ")
LResult3 = Left(LResult3, InStrRev(LResult3, "#") - 1)
excWks3.Cells(intRow3, 2) = LResult3
excWks3.Cells(intRow3, 3) = "Complete"
excWks3.Cells(intRow3, 4) = "File Attached"
Dim s3 As String
s3 = olkMsg2.Subject
Dim indexOfName3 As Integer
indexOfName3 = InStr(1, s3, "Reference: ")
Dim finalString3 As String
finalString3 = Right(s3, Len(s3) - indexOfName3 - 10)
excWks3.Cells(intRow3, 5) = finalString3
excWks3.Cells(intRow3, 6) = "File Path"
intRow3 = intRow3 + 1
End If
Next i
End If
End If
End If
Next
Set olkMsg = Nothing
Set olkMsg2 = Nothing
excWkb.Close True
Set excWks = Nothing
Set excWks2 = Nothing
Set excWks3 = Nothing
Set excWkb = Nothing
Set excApp = Nothing
On Error GoTo ErrHandle
ErrHandle:
Resume Next
End Sub
Private Function GetSMTPAddress(item As Outlook.MailItem, intOutlookVersion As Integer) As String
Dim olkSnd As Outlook.AddressEntry, olkEnt As Object
On Error Resume Next
Select Case intOutlookVersion
Case Is < 14
If item.SenderEmailType = "EX" Then
GetSMTPAddress = SMTP2007(item)
Else
GetSMTPAddress = item.SenderEmailAddress
End If
Case Else
Set olkSnd = item.Sender
If olkSnd.AddressEntryUserType = olExchangeUserAddressEntry Then
Set olkEnt = olkSnd.GetExchangeUser
GetSMTPAddress = olkEnt.PrimarySmtpAddress
Else
GetSMTPAddress = item.SenderEmailAddress
End If
End Select
On Error GoTo 0
Set olkPrp = Nothing
Set olkSnd = Nothing
Set olkEnt = Nothing
End Function
Function GetOutlookVersion() As Integer
Dim arrVer As Variant
arrVer = Split(Outlook.Version, ".")
GetOutlookVersion = arrVer(0)
End Function
Function SMTP2007(olkMsg As Outlook.MailItem) As String
Dim olkPA As Outlook.PropertyAccessor
On Error Resume Next
Set olkPA = olkMsg.PropertyAccessor
SMTP2007 = olkPA.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x5D01001E")
On Error GoTo 0
Set olkPA = Nothing
End Function
Set objAttachments = Outlook.Attachments is not the correct syntax.
Just remove the line as you have this later.
Set objAttachments = objMsg.Attachments