0

I export email data from a folder that has subfolders, from a shared mailbox.
I am trying to loop through the existing values in column E, comparing the string value of the email.EntryID field to the string value of the cell, to skip the emails (email fields) that were already exported.

Option Explicit

Sub inbox_working()
   
Dim xlApp As Excel.Application
Dim xlWB As Excel.Workbook
Dim Sht As Excel.Worksheet

Dim olApp As Outlook.Application
Set olApp = New Outlook.Application
   
Dim olNs As Outlook.Namespace
Set olNs = olApp.GetNamespace("MAPI")
   
Dim olRecip As Outlook.Recipient
Set olRecip = olNs.CreateRecipient("exampleEmail@email.com") ' Update email
   
Dim Inbox As Outlook.MAPIFolder
Set Inbox = olNs.GetSharedDefaultFolder(olRecip, olFolderInbox)
       
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = True
Set xlWB = xlApp.Workbooks.Open("C:\Desktop\mails.xlsm")
Set Sht = xlWB.Sheets("inbox_email_data")
   
With Sht
    .Range("A3").Value = "Sender"
    .Range("B3").Value = "Received_Date_Time"
    .Range("C3").Value = "Converation Topic"
    .Range("D3").Value = "Category"
    .Range("E3").Value = "message_ID"
    .Range("F3").Value = "conversation_ID"
    .Range("G3").Value = "Folder Name"
End With

'   // Process Current Folder
LoopFolders Inbox, Sht
End Sub

Private Sub LoopFolders( _
  ByVal CurrentFolder As Outlook.MAPIFolder, _
  ByVal Sht As Worksheet _    )
   
Dim Items As Outlook.Items
Set Items = CurrentFolder.Items
   
Dim i As Long
Dim last_row As Long
Dim Item As Object ' Outlook.MailItem
Dim cell As Range
       
With Sht
    last_row = Sht.Range("A" & .Rows.Count).End(xlUp).Row + 1
   
    For i = Items.Count To 1 Step -1 ' run loop
               
        Set Item = Items(i)
        'DoEvents
               
        If TypeOf Item Is Outlook.MailItem Then
               
            For Each cell In .Range("ID_inbox").Cells  '----> the range here =$E$4:$Erowatendofworksheet
                   
                If CStr(Item.entryID) = CStr(cell.Value) Then  
                    ' do nothing
                Else
                    .Range("A" & last_row).Value = Item.SenderName '----> at this line it breaks with 1004 error
                    .Range("B" & last_row).Value = Item.ReceivedTime
                    .Range("C" & last_row).Value = Item.ConversationTopic
                    .Range("D" & last_row).Value = Item.categories
                    .Range("E" & last_row).Value = Item.entryID
                    .Range("F" & last_row).Value = Item.ConversationID
                    .Range("G" & last_row).Value = CurrentFolder.Name
                End If
            Next
        Else 
            ' do nothing
        End If
    Next
                    
    last_row = last_row + 1
    
    '   // Recurse through subfolders
    Dim folder As Outlook.MAPIFolder
    If CurrentFolder.Folders.Count > 0 Then
        For Each folder In CurrentFolder.Folders
            LoopFolders folder, Sht
        Next
    End If
   
End With

'   // Cleanup
Set folder = Nothing
Set Item = Nothing
Set Items = Nothing    
End Sub

The code writes the data (fields) of the first email and then gives the error at that line.
I gather, it fails to 'do nothing if the string values match.
The cells in the sheet are unlocked (Ctrl+A->Right click->Permissions->Locked field is unticked).

Later edit:

Using the below, exports every time, all email items from all folders and subfolders of the Inbox folder.

Using event the lightest filering (fx. Item.ReceivedTime > a specific date) or other (as the one mentioned in the answer), renders the Outlook frozen.

Option Explicit
Sub all_email()

    Dim xlApp As Excel.Application
    Dim xlWB As Excel.Workbook
    Dim Sht As Excel.Worksheet
    
    
    Dim olApp As Outlook.Application
    Set olApp = New Outlook.Application

    Dim olNs As Outlook.Namespace
    Set olNs = olApp.GetNamespace("MAPI")

    Dim olRecip As Outlook.Recipient
    Set olRecip = olNs.CreateRecipient("email@email.com") ' Update email

    Dim Inbox As Outlook.MAPIFolder
    Set Inbox = olNs.GetSharedDefaultFolder(olRecip, olFolderInbox)
    
    Set xlApp = CreateObject("Excel.Application")
    xlApp.Visible = True
    Set xlWB = xlApp.Workbooks.Open("C:\Desktop\mails.xlsm")
    Set Sht = Sheets("email_data")


    With Sht
        .Range("A3").Value = "Sender"
        .Range("B3").Value = "Date_Time"
        .Range("C3").Value = "Conversation_Topic"
        .Range("D3").Value = "Category"
        .Range("E3").Value = "Entry_ID"
        .Range("F3").Value = "Conversation_ID"
        .Range("G3").Value = "Mailbox"
        
    End With

'   // Process Current Folder
    LoopFolders Inbox, Sht

End Sub

Sub LoopFolders( _
    ByVal CurrentFolder As Outlook.MAPIFolder, _
    ByVal Sht As Worksheet _
)

    Dim Items As Outlook.Items
    Set Items = CurrentFolder.Items

    Dim i As Long
    Dim last_row As Long
    Dim Item As Object ' Outlook.MailItem

    With Sht
        last_row = Sht.Range("A" & .Rows.Count).End(xlUp).Row + 1

        For i = Items.Count To 1 Step -1 ' run loop
            Set Item = Items(i)
            DoEvents
            
            If TypeOf Item Is Outlook.MailItem Then

                 Debug.Print Item
                .Range("A" & last_row).Value = Item.SenderName
                .Range("B" & last_row).Value = Item.ReceivedTime
                .Range("C" & last_row).Value = Item.ConversationTopic
                .Range("D" & last_row).Value = Item.categories
                .Range("E" & last_row).Value = Item.entryID
                .Range("F" & last_row).Value = Item.ConversationID
                .Range("G" & last_row).Value = CurrentFolder.Name
                
            Else
            
            End If

            last_row = last_row + 1

        Next

    '   // Recurse through subfolders
        Dim folder As Outlook.MAPIFolder
        If CurrentFolder.Folders.Count > 0 Then
            For Each folder In CurrentFolder.Folders
                LoopFolders folder, Sht
            Next
        End If

    End With

'   // Cleanup
    Set folder = Nothing
    Set Item = Nothing
    Set Items = Nothing
End Sub

Later edit 2:

After using @niton's comments and partial solution, as it stands now, only the items from subfolders of the Inbox folder are extracted. When the script encounters a subfolder of a subfolder (i.e. Inbox->Subfolder->Subfolder) it stops.

How would I iterate through subfolders of each of the subfolder in the Inbox?

I tried:

// Recurse through subfolders
        Dim folder As Outlook.MAPIFolder
        Dim subfolder As Outlook.folder
        
        If CurrentFolder.Folders.Count > 0 Then
            For Each folder In CurrentFolder.Folders
                LoopFolders folder, Sht
                If folder.Folders.Count > 0 Then
                    For Each subfolder In folder.Folders
                    LoopFolders subfolder, Sht
                    Next
                End If
            Next
        End If

But with no awail.

Also, how would I be able to extract the mail items data from the Sent folder?

Thank you.

  • One thing that I noticed, and this might be a formatting issue, is the line `last_row = last_row + 1` outside of the `For` loop? If so, the `last_row` value will never update in the loop. As you go over the emails, each subsequent entry will just overwrite the last row. Try moving that line into the loop (just before the `Next` line) and see if that at least lists the emails. – basodre Sep 18 '20 at 14:37
  • Thank you for replying @basodre. Did as you mentioned, still not able to export more than a random email from all of them. –  Sep 18 '20 at 15:08
  • Please roll back the last edit. https://meta.stackoverflow.com/questions/332820/what-to-do-when-someone-answers-dont-be-a-chameleon-dont-be-a-vandal. Put as much code as necessary in the new question you have already created that links to this one. – niton Sep 22 '20 at 11:40
  • It's ok, I've re-accepted the answer as it's the solution for this post. –  Sep 22 '20 at 11:55

1 Answers1

0

Given the chosen method there has to be a lot more repetition. This could lead to a long run time.

If .Range("ID_inbox").Cells is the entire worksheet then limit the number of rows

    Dim bFound As Boolean
    Dim starting_last_row As Long

    With Sht
    
        last_row = Sht.Range("A" & .Rows.Count).End(xlUp).Row + 1
        starting_last_row = Sht.Range("A" & .Rows.Count).End(xlUp).Row
   
        For i = Items.Count To 1 Step -1 ' run loop
               
            Set Item = Items(i)
            
            bFound = False
            
            'DoEvents
               
            If TypeOf Item Is Outlook.MailItem Then
               
                'For Each cell In .Range("ID_inbox").Cells  '----> the range here =$E$4:$Erowatendofworksheet
                For Each cell In .Range("E1:E" & starting_last_row)
                    If CStr(Item.entryID) = CStr(cell.Value) Then
                       bFound = True
                       Exit For
                    End If
                Next
                   
                If bFound = False Then
                
                    .Range("A" & last_row).Value = Item.SenderName
                    .Range("B" & last_row).Value = Item.ReceivedTime
                    .Range("C" & last_row).Value = Item.ConversationTopic
                    .Range("D" & last_row).Value = Item.categories
                    .Range("E" & last_row).Value = Item.entryID
                    .Range("F" & last_row).Value = Item.ConversationID
                    .Range("G" & last_row).Value = CurrentFolder.Name
                    
                    last_row = last_row + 1
                End If
            End If
        Next

        '   // Recurse through subfolders

When code is in Excel some email properties may not be accessible if so move code to Outlook.
niton
  • 8,771
  • 21
  • 32
  • 52
  • Thank you for the reply, @niton. Will try it these days and will revert with an update. Your intuitive, as the code is already in Outlook VBA. –  Sep 19 '20 at 08:44
  • again thank you for the solution/approach. Time is not a problem in this case, but unfortunately, it doesn't work. When your solution is run, Outlook gets frozen and nothing gets added/inserted in the Excel sheet. I'll gladly take any other solution. I'm thinking it gets frozen because it tries to check each of the million empty rows in the sheet. –  Sep 21 '20 at 11:03
  • .Range("ID_inbox") represents $E$4:$E, a dynamic range. So the check should be only for the values that already are in that rows of that range, so at email no 1, there should be 1 row value in the named range to check against, at row 2 of the named range, 2 rows and so forth. I'm not looking to check only the last value. I know the execution time will be long to say the least, but even with the sheet empty, no email data is written. –  Sep 21 '20 at 13:21
  • 1
    Did you use the new lines of code with `starting_last_row`? – niton Sep 21 '20 at 13:24