'Loop through three specific Outlook folders

I have Excel VBA code that imports Outlook mail data from one folder into Excel.
I change this for three folders all at the same level (not subfolders).

Is there a way I can specify and run the code on all three folders at once?

Dim mailFolderItemsB As Object
objOwner.Resolve
If objOwner.Resolved Then
    Set mailFolderItemsB = objNamespace.GetSharedDefaultFolder(objOwner, olFolderInbox) _
      .Parent.folders("Folder A") 'change this to B and then C once code has run.
      '.Parent.folders("Folder B")
      '.Parent.folders("Folder C")
    Set mailFolderItems = mailFolderItemsB.Items

This might be a second question if so ignore and apologies.
Is it possible to track changes?
Users often open mail, change the subject header and then close. Thinking of a old.value event.

My code.

Sub GetEmail()
    On Error GoTo ErrorHandler
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    
    Dim results() As String
    
    ' call function
    results = ExportEmails(True)
    
    ' paste onto worksheet
    Range(Cells(1, 1), Cells(UBound(results), UBound(results, 2))).Value = results
    Exit Sub

ErrorHandler:
    MsgBox Err.Description & " " & Err.Number, vbOKOnly + vbCritical, "Database Error"
End Sub


Function ExportEmails(Optional headerRow As Boolean = False) As String()
    Dim objOutlook As Object ' Outlook.Application
    Dim objNamespace As Object ' Outlook.Namespace
    Dim strFolderName As Object
    Dim objMailbox As Object
    Dim objFolder As Object
    'Dim mailFolderItems As Object ' Outlook.items
    Dim folderItem As Object
    Dim msg As Object ' Outlook.MailItem
    Dim tempString() As String
    Dim i As Long
    Dim numRows As Long
    Dim startRow As Long
    Dim jAttach As Long ' counter for attachments
    Dim debugMsg As Integer
    
    ' select output results worksheet and clear previous results
    Sheets("Outlook Results").Select
    Sheets("Outlook Results").Cells.ClearContents
    Range("A1").Select
    Set objOutlook = CreateObject("Outlook.Application")
    Set objNamespace = objOutlook.GetNamespace("MAPI")
    
    Dim OutApp As outlook.Application
    Dim objOwner As outlook.Recipient
    
    Set OutApp = New outlook.Application
    Set objOwner = objNamespace.CreateRecipient("[email protected]")
    
    Dim mailFolderItemsB As Object, f, mailFolderItems
    
    objOwner.Resolve
    If objOwner.Resolved Then
    
        For Each f In Array("Folder A", "Folder B", "Folder C")
            Set mailFolderItemsB = objNamespace.GetSharedDefaultFolder(objOwner, olFolderInbox) _
              .Parent.folders(f) 'change this to B and then C once code has run.
            Set mailFolderItems = mailFolderItemsB.Items
    
            ' if calling procedure wants header row
            If headerRow Then
                startRow = 1
            Else
                startRow = 0
            End If
    
            numRows = mailFolderItems.Count
    
            ReDim tempString(1 To (numRows + startRow), 1 To 100)
    
            ' loop through folder items
            For i = 1 To numRows
    
                Set folderItem = mailFolderItems.Item(i)
    
                If IsMail(folderItem) Then
                    Set msg = folderItem
                End If
                
                With msg
                    tempString(i + startRow, 1) = .subject
                    tempString(i + startRow, 2) = Replace(.body, vbLf, "")
                    tempString(i + startRow, 3) = .Categories
                    tempString(i + startRow, 4) = .cc
                    tempString(i + startRow, 5) = .entryid
                    tempString(i + startRow, 6) = .ConversationID ' .ConversationTopic 'conversationID  or conversationindex.propertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E") '.ConversationIndex
                    tempString(i + startRow, 7) = .LastModificationTime 'sent
                    tempString(i + startRow, 8) = .ReceivedByName
                    tempString(i + startRow, 9) = .ReceivedOnBehalfOfName
                    tempString(i + startRow, 10) = .ReceivedTime
                    tempString(i + startRow, 11) = .SenderEmailAddress
                    tempString(i + startRow, 12) = .SenderName
                    tempString(i + startRow, 13) = .SentOn
                    tempString(i + startRow, 14) = .To
                    tempString(i + startRow, 15) = .propertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x1035001E")
                End With
                
                ' adding file attachment names where they exist
                If msg.Attachments.Count > 0 Then
                    For jAttach = 1 To msg.Attachments.Count
                        'get pdf and xlsx files
                        Dim sAttachment As String
                        sAttachment = msg.Attachments.Item(jAttach).DisplayName
                        If Right(sAttachment, 4) = ".pdf" Or Right(sAttachment, 5) = ".xlsx" Then
                            tempString(i + startRow, 21 + jAttach) = msg.Attachments.Item(jAttach).DisplayName
                        End If
                    Next jAttach
                End If
            
            Next i
    
            ' first row of array should be header values
            If headerRow Then
                tempString(1, 1) = "Subject"
                tempString(1, 2) = "Body"
                tempString(1, 3) = "Categories"
                tempString(1, 4) = "CC"
                tempString(1, 5) = "CreationTime"
                tempString(1, 6) = "ConversationID"
                tempString(1, 7) = "LastModificationTime"
                tempString(1, 8) = "ReceivedByName"
                tempString(1, 9) = "ReceivedOnBehalfOfName"
                tempString(1, 10) = "ReceivedTime"
                tempString(1, 11) = "SenderEmailAddress"
                tempString(1, 12) = "SenderName"
                tempString(1, 13) = "SentOn"
                tempString(1, 14) = "To"
                tempString(1, 15) = "ID"
                tempString(1, 16) = "Number of Attachments"
                tempString(1, 17) = "Attachment 1 Filename"
                tempString(1, 18) = "Attachment 2 Filename"
                tempString(1, 19) = "Attachment 3 Filename"
                tempString(1, 20) = "Attachment 4 Filename"
                tempString(1, 21) = "Attachment 5 Filename"
                tempString(1, 22) = "Attachment 6 Filename"
                tempString(1, 23) = "Attachment 7 Filename"
                tempString(1, 24) = "Attachment 8 Filename"
                tempString(1, 25) = "Attachment 9 Filename"
                tempString(1, 26) = "Attachment 10 Filename"
                tempString(1, 27) = "Attachment 11 Filename"
                tempString(1, 28) = "Attachment 12 Filename"
                tempString(1, 29) = "Attachment 13 Filename"
                tempString(1, 30) = "Attachment 14 Filename"
                tempString(1, 31) = "Attachment 15 Filename"
                tempString(1, 32) = "Attachment 16 Filename"
                tempString(1, 33) = "Attachment 17 Filename"
                tempString(1, 34) = "Attachment 18 Filename"
                tempString(1, 35) = "Attachment 19 Filename"
                tempString(1, 36) = "Attachment 20 Filename"
            End If
        Next f
    End If
    
    ExportEmails = tempString
    
    Range("A2").Select
    ActiveWindow.FreezePanes = True
    Rows("1:1").Select
    
End Function


Solution 1:[1]

First, you need to retrieve the shared default folder for the user - this is a common operation which should be repeated three times in a row:

Dim mailFolderItemsB As Object
objOwner.Resolve
If objOwner.Resolved Then
    Set mailSharedInboxFolder = objNamespace.GetSharedDefaultFolder(objOwner, olFolderInbox) 

Then you can grab the parent folder for your three folders:

Set mailSharedParentInboxFolder = mailSharedInboxFolder.Parent

And finally you can get three folders:

Set mailFolderItemsA = mailSharedParentInboxFolder.folders("Folder A")
Set mailFolderItemsB = mailSharedParentInboxFolder.folders("Folder B")
Set mailFolderItemsC = mailSharedParentInboxFolder.folders("Folder C")
'
Set mailFolderItems = mailFolderItemsB.Items

I'd recommend avoiding multiple dots in the single line of code. So, you will be able to troubleshoot easily and optimize the overall performance.


For tracking item changes I'd suggest creating an inspector wrapper, see Implement a wrapper for inspectors and track item-level events in each inspector for more information.

Solution 2:[2]

Something like this:

Dim mailFolderItemsB As Object, f, mailFolderItems 

'...
objOwner.Resolve
If objOwner.Resolved Then

    For Each f in Array("Folder A","Folder B","Folder C")
        Set mailFolderItemsB = objNamespace.GetSharedDefaultFolder(objOwner, olFolderInbox) _
                 .Parent.folders(f) 'change this to B and then C once code has run.
        Set mailFolderItems = mailFolderItemsB.Items

        'something happens here...

    Next f
End If

EDIT: you have one huge function which does too much. Try this re-worked example

Sub ProcessEmails()

    Dim OutApp As Outlook.Application
    Dim objOwner As Outlook.Recipient
    Dim objOutlook As Object ' Outlook.Application
    Dim objNamespace As Object ' Outlook.Namespace
    Dim strFolderName As Object
    Dim objMailbox As Object
    Dim objFolder As Object, data
    Dim wsResults As Worksheet
    Dim mailFolderInbox As Object, f, dataRow As Long
    
    ' select output results worksheet and clear previous results
    Set wsResults = ThisWorkbook.Sheets("Outlook Results")
    wsResults.Cells.ClearContents
    
    Set objOutlook = CreateObject("Outlook.Application")
    Set objNamespace = objOutlook.GetNamespace("MAPI")
    
    Set OutApp = New Outlook.Application
    Set objOwner = objNamespace.CreateRecipient("[email protected]")
    
    objOwner.Resolve
    If objOwner.Resolved Then
    
        Set mailFolderInbox = objNamespace.GetSharedDefaultFolder(objOwner, olFolderInbox)
        dataRow = 1 'start putting data on this row
        For Each f In Array("Folder A", "Folder B", "Folder C")
            data = MailData(mailFolderInbox.Parent.Folders(f), True)
            If Not IsEmpty(data) Then
                wsResults.Cells(dataRow, 1).Resize(UBound(data, 1), UBound(data, 2)).Value = data
                dataRow = dataRow + UBound(data, 1) 'next data dump position
            End If
        Next f 'next folder
    End If
End Sub

'Given an Outlook folder object, return an array of data about the contained
'  mail items.  Optionally add a header row to the array.
Function MailData(objFolder As Object, Optional headerRow As Boolean = False)
    
    Dim colMail As New Collection, itm As Object, data(), dataRow As Long, i As Long
    Dim arrHdr, dn, attNum As Long
    
    'only want mail items, so collect them in a Collection
    For Each itm In objFolder.Items
        If TypeName(itm) = "MailItem" Then colMail.Add itm
    Next itm
    If colMail.Count = 0 Then
        MailData = Empty 'nothing to report
        Exit Function
    End If
    
    dataRow = IIf(headerRow, 2, 1) 'data "row" for mail#1
    ReDim data(1 To colMail.Count + (dataRow - 1), 1 To 36)
    
    If headerRow Then 'adding a header "row"?
        arrHdr = Array("Subject", "Body", "Categories", "CC", "EntryId", _
                   "ConversationID", "LastModificationTime", "ReceivedByName", _
                   "ReceivedOnBehalfOfName", "ReceivedTime", "SenderEmailAddress", _
                   "SenderName", "SentOn", "To", "ID", "Number of Attachments")
        For i = 0 To UBound(arrHdr)
            data(1, i + 1) = arrHdr(i)
        Next i
        For i = 1 To 20
            data(1, 16 + i) = "Attachment " & i & " Filename"
        Next i
    End If
    
    For Each itm In colMail 'process all the mailitems found
        With itm
            data(dataRow, 1) = .Subject
            data(dataRow, 2) = Replace(.body, vbLf, "")
            data(dataRow, 3) = .Categories
            data(dataRow, 4) = .cc
            data(dataRow, 5) = .entryid
            data(dataRow, 6) = .ConversationID ' .ConversationTopic 'conversationID  or conversationindex.propertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E") '.ConversationIndex
            data(dataRow, 7) = .LastModificationTime 'sent
            data(dataRow, 8) = .ReceivedByName
            data(dataRow, 9) = .ReceivedOnBehalfOfName
            data(dataRow, 10) = .ReceivedTime
            data(dataRow, 11) = .SenderEmailAddress
            data(dataRow, 12) = .SenderName
            data(dataRow, 13) = .SentOn
            data(dataRow, 14) = .to
            data(dataRow, 15) = .propertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x1035001E")
            data(dataRow, 16) = .attachments.Count
            attNum = 0
            For i = 1 To .attachments.Count
                dn = .attachments(i).DisplayName
                If dn Like "*.xlsx" Or dn Like "*.pdf" Then
                    attNum = attNum + 1
                    If attNum > 20 Then Exit For 'too many attachments...
                    data(dataRow, 16 + attNum) = dn
                End If
            Next i
            dataRow = dataRow + 1
        End With
    Next itm
    MailData = data
End Function

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Eugene Astafiev
Solution 2