Count Outlook emails by Sender & date in Excel VBA - vba

The objective is to find the total/count of emails from Sender on Monthly basis.
The below code retrieves Date/Time for a count by month.
How to display by SenderName on the worksheet?
I'm not sure if I've to use two dictionaries? If yes no knowledge about how to work around it.
Sub ReferSpecificFolder()
'Declare Outlook application & folder object variables.
Dim objOutlook as Object, objnSpace as Object, objFolder As Outlook.MAPIFolder
Dim olItem As Variant 'Object
Dim dictDate as Object
Set objOutlook = CreateObject("Outlook.Application")
Set objnSpace=objOutlook.GetNamespace("MAPI")
Set objFolder = objOutlook.GetNamespace("MAPI").Folders("xyz#microsoft.com").Folders("Sales - 2020")
Set dictDate=CreateObject("Scripting.Dictionary")
Set myItems = objFolder.Items
On Error Resume Next
If Err.Number <> 0 Then
Err.Clear
MsgBox "No such folder!"
Exit Sub
End If
If fldr.Items.Count = 0 Then
MsgBox "There were no messages found in your folders"
Exit Sub
End If
'Select the sheet to enter the data
Dim wbData As Worksheet
Dim LastRow As Long
Set wbData = ThisWorkbook.Sheets("Rawdata - Time Period")
LastRow = wbData.Range("A" & wbData.Rows.Count).End(xlUp).Row + 1
myItems.SetColumns("SenderName, SentOn")
For Each i In myItems
dateStr=GetDate(i.SentOn)
strSender=i.SenderName
If Not dictDate.Exists(dateStr) Then
dictDate(dateStr)=0
End If
dictDate(dateStr)=CLng(dictDate(dateStr))+1
Next i
For Each o In dictDate.keys
LastRow = wbData.Range("A" & wbData.Rows.Count).End(xlUp).Row + 1
With wbData
.Cells(LastRow, 1) = o 'Received Date
.Cells(LastRow, 3) = dictDate(o) 'Count
End With
Next o
Set fldr = Nothing
Set olItem = Nothing
Set olApp = Nothing
MsgBox "DONE!"
End Sub
Function GetDate(dt as Date) as String
GetDate=Year(dt) & "-" & Month(dt) & "-" & Day(dt) & " " & Hour(dt) & ":" & Minute(dt)
End Function

This generates a single sender dictionary then a date dictionary corresponding to each entry in the sender dictionary.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
'
' If desperate declare as Variant
'
Sub ReferSpecificFolderSender()
' Early binding, must set a reference to
' Microsoft Outlook XX.X Object Library
Dim objOutlook As Outlook.Application
Dim objnSpace As Outlook.Namespace
Dim objFolder As Outlook.Folder
Dim olItemI As Object
Dim olItemJ As Object
Dim myItems As Outlook.Items
Dim myItemsDate As Outlook.Items
Dim strFilter As String
Dim dictDate As Object
Dim o As Variant
Dim dateStr As String
Dim dictSender As Object
Dim p As Variant
Dim strSender As String
Set objOutlook = CreateObject("Outlook.Application")
Set objnSpace = objOutlook.GetNamespace("MAPI")
' For the specific purpose of addressing an expected error
' due to not finding objFolder
On Error Resume Next
Set objFolder = objnSpace.Folders("xyz#microsoft.com").Folders("Sales - 2020")
If Err.Number <> 0 Then
Err.Clear
'MsgBox "No such folder!"
'Exit Sub
Set objFolder = objnSpace.PickFolder
If objFolder Is Nothing Then Exit Sub
End If
' Consider mandatory to closely follow On Error Resume Next
' Return to normal error handling
On Error GoTo 0
Set myItems = objFolder.Items
Debug.Print vbCr & "myItems.Count: " & myItems.Count
If objFolder.Items.Count = 0 Then
MsgBox "There were no messages found in " & objFolder.FolderPath
Exit Sub
End If
Set dictSender = CreateObject("Scripting.Dictionary")
' Restrict to mailitems
' 0x001A001F
' https://stackoverflow.com/questions/61793354/delete-items-in-outlook-by-type-or-message-class
'strFilter = "#SQL=""http://schemas.microsoft.com/mapi/proptag/0x001A001F"" LIKE 'IPM.Note'"
'Set myItems = myItems.Restrict(strFilter)
' 0x001A001E
' "PR_MESSAGE_CLASS" http://schemas.microsoft.com/mapi/proptag/0x001A001E
' https://www.slipstick.com/developer/read-mapi-properties-exposed-outlooks-object-model/
strFilter = "#SQL=""http://schemas.microsoft.com/mapi/proptag/0x001A001E"" LIKE 'IPM.Note'"
Set myItems = myItems.Restrict(strFilter)
Debug.Print vbCr & "Mailitems"
Debug.Print "myItems.Count: " & myItems.Count
myItems.Sort "[SenderName]", False
Set dictDate = CreateObject("Scripting.Dictionary")
Set myItemsDate = myItems
Debug.Print "myItemsDate.Count: " & myItemsDate.Count
'The sheet to enter the data
Dim wbData As Worksheet
Dim LastRow As Long
Set wbData = ThisWorkbook.Sheets("Rawdata - Time Period")
LastRow = wbData.Range("A" & wbData.Rows.Count).End(xlUp).Row + 1
' dictionary of sender names
Debug.Print "Sender dictionary"
' With the SetColumns method, Outlook only checks the properties that you have cached,
' and provides fast, read-only access to these properties.
' https://learn.microsoft.com/en-us/office/vba/api/outlook.items.setcolumns
myItems.SetColumns ("SenderName")
For Each olItemJ In myItems
strSender = olItemJ.SenderName
If Not dictSender.Exists(strSender) Then
Debug.Print " " & strSender
dictSender(strSender) = 0
End If
dictSender(strSender) = CLng(dictSender(strSender)) + 1
Next
' iterate unique sender names
For Each p In dictSender.keys
Debug.Print "Date dictionary for: " & p
myItems.Sort "[SentOn]", False
myItemsDate.SetColumns ("SenderName, SentOn")
' check item's date against the sender name dictionary
For Each olItemI In myItemsDate
strSender = olItemI.SenderName
If strSender = p Then
' unique dates for current SenderName
dateStr = GetDate(olItemI.SentOn)
If Not dictDate.Exists(dateStr) Then
dictDate(dateStr) = 0
End If
' count of ccurrences for each date
dictDate(dateStr) = CLng(dictDate(dateStr)) + 1
End If
Next
For Each o In dictDate.keys
LastRow = wbData.Range("A" & wbData.Rows.Count).End(xlUp).Row + 1
With wbData
.Cells(LastRow, 1) = p ' SenderName
.Cells(LastRow, 2) = o ' GetDate value
.Cells(LastRow, 3) = dictDate(o) ' Count
End With
Next
' delete date dictionary entries for current SenderName
dictDate.RemoveAll
Next p
ActiveSheet.Columns.AutoFit
Set objFolder = Nothing
Set olItemI = Nothing
Set olItemJ = Nothing
Set objOutlook = Nothing
Set objnSpace = Nothing
Set dictSender = Nothing
Set dictDate = Nothing
Set myItemsDate = Nothing
Debug.Print "DONE!"
'MsgBox "DONE!"
End Sub

Related

How to move email to folder based on the sender domain

The attached script on selected emails, create a folder on a non-default PST (OutlookEmail.PST) based on the sender name and move the email to the folder. For e.g MyTest#thisdomain.com, it creates a folder MyTest.
I need advise modifying the script that it creates a folder based on the sender domain for e.g thisdomain.com with subfolder MyTest and then move the email.
This macro is from https://www.slipstick.com/developer/file-messages-senders-name/
Public Sub MoveSelectedMessages()
Dim objOutlook As Outlook.Application
Dim objNamespace As Outlook.NameSpace
Dim objDestFolder As Outlook.MAPIFolder
Dim objSourceFolder As Outlook.Folder
Dim currentExplorer As Explorer
Dim Selection As Selection
Dim obj As Object
Dim objVariant As Variant
Dim lngMovedItems As Long
Dim intCount As Integer
Dim intDateDiff As Integer
Dim strDestFolder As String
Set objOutlook = Application
Set objNamespace = objOutlook.GetNamespace("MAPI")
Set currentExplorer = objOutlook.ActiveExplorer
Set Selection = currentExplorer.Selection
Set objSourceFolder = currentExplorer.CurrentFolder
For Each obj In Selection
Set objVariant = obj
If objVariant.Class = olMail Then
intDateDiff = DateDiff("d", objVariant.SentOn, Now)
' I'm using 40 days, adjust as needed.
If intDateDiff >= 0 Then
sSenderName = objVariant.SentOnBehalfOfName
If sSenderName = ";" Then
sSenderName = objVariant.senderName
End If
On Error Resume Next
' Use These lines if the destination folder is not a subfolder of the current folder
'Dim objInbox As Outlook.MAPIFolder
'Set objInbox = objNamespace.Folders(objDestFolder).Folders("OutlookEmail") ' or whereever the folder is
'Set objDestFolder = objInbox.Folders(sSenderName)
Set objDestFolder = objNamespace.Folders("OutlookEmail").Folders(sSenderName)
'Set objDestFolder = objDestFolder.Folders(sSenderName)
If objDestFolder Is Nothing Then
Set objDestFolder = objNamespace.Folders("OutlookEmail").Folders.Add(sSenderName)
End If
objVariant.Move objDestFolder
'count the # of items moved
lngMovedItems = lngMovedItems + 1
Set objDestFolder = Nothing
End If
End If
Err.Clear
Next
' Display the number of items that were moved.
' MsgBox "Moved " & lngMovedItems & " messages(s)."
Set currentExplorer = Nothing
Set obj = Nothing
Set Selection = Nothing
Set objOutlook = Nothing
Set objNamespace = Nothing
Set objSourceFolder = Nothing
End Sub
A modification that creates the domain but not the subfolder:
If intDateDiff >= 0 Then
sSenderName = Right(objVariant.SenderEmailAddress, Len(objVariant.SenderEmailAddress) - InStr(objVariant.SenderEmailAddress, "#"))
This second version takes into account exchange addresses. No applicable mail available for testing.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
Public Sub MoveSelectedMessages_ExchangeSMTP()
Dim objSenderDomainFolder As folder
Dim strSenderDomain As String
Dim strSenderEmailAddress As String
Dim objDestFolder As folder
Dim strDest As String
Dim Selection As Selection
Dim obj As Object
'Dim intDateDiff As Long
Set Selection = ActiveExplorer.Selection
For Each obj In Selection
If obj.Class = olmail Then
Debug.Print obj.Subject
'intDateDiff = dateDiff("d", obj.SentOn, Now)
'Debug.Print "intDateDiff: " & intDateDiff
'If intDateDiff >= 0 Then ' Not needed for 0
If obj.SenderEmailType = "EX" Then ' exchange
strSenderEmailAddress = obj.Sender.GetExchangeUser().PrimarySmtpAddress
Else ' smtp
strSenderEmailAddress = obj.SenderEmailAddress
End If
Debug.Print "SenderEmailAddress: " & strSenderEmailAddress
strSenderDomain = Right(strSenderEmailAddress, _
Len(strSenderEmailAddress) - InStr(strSenderEmailAddress, "#"))
Debug.Print "strSenderDomain: " & strSenderDomain
strDest = Left(strSenderEmailAddress, InStr(strSenderEmailAddress, "#") - 1)
Debug.Print "strDest: " & strDest
On Error Resume Next
' Bypass error if sSenderDomain folder does not exist, leaving objSenderDomainFolder as Nothing
Set objSenderDomainFolder = Session.folders("OutlookEmail").folders(strSenderDomain)
' Remove error bypass as soon as the purpose is served
On Error GoTo 0
If objSenderDomainFolder Is Nothing Then
Set objSenderDomainFolder = Session.folders("OutlookEmail").folders.Add(strSenderDomain)
End If
If Not objSenderDomainFolder Is Nothing Then
On Error Resume Next
' Bypass error if objDestFolder does not exist, leaving objDestFolder as Nothing
Set objDestFolder = objSenderDomainFolder.folders(strDest)
' Remove error bypass as soon as the purpose is served
On Error GoTo 0
If objDestFolder Is Nothing Then
Set objDestFolder = objSenderDomainFolder.folders.Add(strDest)
End If
obj.Move objDestFolder
End If
' Reset to Nothing for the next iteration of the selection
' Important step due to the use of On Error Resume Next
Set objSenderDomainFolder = Nothing
Set objDestFolder = Nothing
'End If
End If
Next
End Sub
First version. SMTP addresses only.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
Public Sub MoveSelectedMessages()
Dim objSenderDomainFolder As folder
Dim strSenderDomain As String
Dim objDestFolder As folder
Dim strDest As String
Dim Selection As Selection
Dim obj As Object
'Dim intDateDiff As Long
Set Selection = ActiveExplorer.Selection
For Each obj In Selection
If obj.Class = olMail Then
Debug.Print obj.Subject
'intDateDiff = dateDiff("d", obj.SentOn, Now)
'Debug.Print "intDateDiff: " & intDateDiff
'If intDateDiff >= 0 Then ' Not needed for 0
Debug.Print "SenderEmailAddress: " & obj.SenderEmailAddress
strSenderDomain = Right(obj.SenderEmailAddress, _
Len(obj.SenderEmailAddress) - InStr(obj.SenderEmailAddress, "#"))
Debug.Print "strSenderDomain: " & strSenderDomain
strDest = Left(obj.SenderEmailAddress, InStr(obj.SenderEmailAddress, "#") - 1)
Debug.Print "strDest: " & strDest
On Error Resume Next
' Bypass error if sSenderDomain folder does not exist,
' leaving objSenderDomainFolder as Nothing
Set objSenderDomainFolder = _
Session.folders("OutlookEmail").folders(strSenderDomain)
' Remove error bypass as soon as the purpose is served
On Error GoTo 0
If objSenderDomainFolder Is Nothing Then
Set objSenderDomainFolder = _
Session.folders("OutlookEmail").folders.Add(strSenderDomain)
End If
If Not objSenderDomainFolder Is Nothing Then
On Error Resume Next
' Bypass error if objDestFolder does not exist,
' leaving objDestFolder as Nothing
Set objDestFolder = objSenderDomainFolder.folders(strDest)
' Remove error bypass as soon as the purpose is served
On Error GoTo 0
If objDestFolder Is Nothing Then
Set objDestFolder = objSenderDomainFolder.folders.Add(strDest)
End If
obj.Move objDestFolder
End If
' Reset to Nothing for the next iteration of the selection
' Important step due to the use of On Error Resume Next
Set objSenderDomainFolder = Nothing
Set objDestFolder = Nothing
'End If
End If
Next
End Sub
To get the domain name try
DomainName = Mid$(EmailAddress, InStrRev(EmailAddress, "#") + 1, _
InStrRev(EmailAddress, ".") - _
InStrRev(EmailAddress, "#") - 1)
To get the sender name try
SenderName = Left(EmailAddress, InStr(EmailAddress, "#") - 1)

MS-Word 2010 - Macro to export table to Outlook Tasks

I'm trying to create a macro in MS-Word VBA to take the contents of a MS-Word table (with a bookmarked name), iterate through the rows of the table and create tasks in MS-Outlook (1 row=1 task).
I have Googled and think I will need to try and mix together the following two scripts I have found:
Script 1 - (For making calendar entries - not wanted, but iteration through rows - wanted)
Sub AddAppntmnt()
'Adds a list of events contained in a three column Word table
'with a header row, to Outlook Calendar
Dim olApp As Object
Dim olItem As Object
Dim oTable As Table
Dim i As Long
Dim bStarted As Boolean
Dim strStartDate As Range
Dim strEndDate As Range
Dim strSubject As Range
On Error Resume Next
Set olApp = GetObject(, "Outlook.Application")
If Err <> 0 Then
Set olApp = CreateObject("Outlook.Application")
bStarted = True
End If
Set oTable = ActiveDocument.Tables(1)
'Ignore the first (header) row of the table
For i = 2 To oTable.Rows.Count
Set strStartDate = oTable.Cell(i, 1).Range
strStartDate.End = strStartDate.End - 1
Set strEndDate = oTable.Cell(i, 2).Range
strEndDate.End = strEndDate.End - 1
Set strSubject = oTable.Cell(i, 3).Range
strSubject.End = strSubject.End - 1
Set olItem = olApp.CreateItem(1)
olItem.Start = strStartDate
olItem.End = strEndDate
olItem.ReminderSet = False
olItem.AllDayEvent = True
olItem.Subject = strSubject
olItem.Categories = "Events"
olItem.BusyStatus = 0
olItem.Save
Next i
If bStarted Then olApp.Quit
Set olApp = Nothing
Set olItem = Nothing
Set oTable = Nothing
End Sub
Script 2 - has the actual task creation bit I think I need although this one is about setting task to remind user to do something in 2 weeks or something:
Sub AddOutlookTask()
Dim olApp As Object
Dim olItem As Object
Dim bStarted As Boolean
Dim fName As String
Dim flName As String
On Error Resume Next
If ActiveDocument.Saved = False Then
ActiveDocument.Save
If Err.Number = 4198 Then
MsgBox "Process ending - document not saved!"
GoTo UserCancelled:
End If
End If
Set olApp = GetObject(, "Outlook.Application")
If Err <> 0 Then
'Outlook wasn't running, start it from code
Set olApp = CreateObject("Outlook.Application")
bStarted = True
End If
Set olItem = olApp.CreateItem(3) 'Task Item
fName = ActiveDocument.name
flName = ActiveDocument.FullName
olItem.Subject = "Follow up " & fName
olItem.Body = "If no reply to" & vbCr & _
flName & vbCr & "further action required"
olItem.StartDate = Date + 10 '10 days from today
olItem.DueDate = Date + 14 '14 days from today
olItem.Importance = 2 'High
olItem.Categories = InputBox("Category?", "Categories")
olItem.Save
UserCancelled:
If bStarted Then olApp.Quit
Set olApp = Nothing
Set olItem = Nothing
End Sub
How do I reference a particular table in MS-Word in code? I have bookmarked it so it has a "name" if that helps!
With Davids help (above) I have got the following solution to my problem. I post here for others if they come across a similar issue:
Sub CreateTasks()
'
' CreateTasks Macro
'
'
'
'Exports the contents of the ACtoins table to MS-Outlook Tasks
' Set Variables
Dim olApp As Object
Dim olItem As Object
Dim oTable As Table
Dim i As Long
Dim strSubject As Range
Dim strDueDate As Range
Dim strBody As Range
Dim strSummary As String
Dim bStarted As Boolean
'Dim strPupil As WdBookmark
Dim strPerson As Range
'Link to Outlook
On Error Resume Next
Set olApp = GetObject(, "Outlook.Application")
If Err <> 0 Then
Set olApp = CreateObject("Outlook.Application")
bStarted = True
End If
'Set table variable to the bookmarked table
Set oTable = ActiveDocument.Bookmarks("Actions").Range.Tables(1)
'Ignore the first (header) row of the table
For i = 3 To oTable.Rows.Count
Set strSubject = oTable.Cell(i, 3).Range
strSubject.End = strSubject.End - 1
Set strBody = oTable.Cell(i, 4).Range
strBody.End = strBody.End - 1
Set strDueDate = oTable.Cell(i, 5).Range
strDueDate.End = strDueDate.End - 1
'next line not working below
'Set strPupil = WdBookmark.Name
'Create the task
Set olItem = olApp.CreateItem(3) 'Task Item
strSummary = Left(strSubject, 30)
olItem.Subject = "CYPP Action for" & " " & strBody & "-" & strSummary & "..."
olItem.Body = strBody & vbNewLine & olItem.Body & vbNewLine & strSubject
olItem.DueDate = strDueDate & olItem.DueDate
olItem.Categories = "CYPP"
olItem.Save
Next i
If bStarted Then olApp.Quit
Set olApp = Nothing
Set olItem = Nothing
Set oTable = Nothing
End Sub
I will be adding to this to deal with empty rows but I am pleased with the functionality so far. The DateDue is not working yet but I think that is a formatting issue.
Thanks again David,
Richard.

Looping through every subfolder in inbox using vba

I have a problem looping through every sub-folder of an Outlook email using following code:
Sub HowManyEmails()
Dim objOutlook As Object, objnSpace As Object, objFolder As MAPIFolder
Dim EmailCount As Integer
Set objOutlook = CreateObject("Outlook.Application")
Set objnSpace = objOutlook.GetNamespace("MAPI")
On Error Resume Next
'Set objFolder = ActiveExplorer.CurrentFolder
Set objFolder = Session.GetFolderFromID (Application.ActiveExplorer.CurrentFolder.EntryID)
If Err.Number <> 0 Then
Err.Clear
MsgBox "No such folder."
Exit Sub
End If
EmailCount = objFolder.Items.Count
' MsgBox "Number of emails in the folder: " & EmailCount, , "email count"
Dim dateStr As String
Dim myItems As Outlook.Items
Dim dict As Object
Dim msg As String
Dim oStartDate As String
Dim oEndDate As String
Set dict = CreateObject("Scripting.Dictionary")
oStartDate = InputBox("Type the start date (format MM/DD/YYYY)")
oEndDate = InputBox("Type the end date (format MM/DD/YYYY)")
Set myItems = objFolder.Items.Restrict("[Received] >= '" & oStartDate & "' And [Received] <= '" & oEndDate & "'")
myItems.SetColumns ("Categories")
' date for mssg:
For Each myItem In myItems
dateStr = myItem.Categories
If Not dict.Exists(dateStr) Then
dict(dateStr) = 0
End If
dict(dateStr) = CLng(dict(dateStr)) + 1
Next myItem
' Output for days
msg = ""
For Each o In dict.Keys
msg = msg & o & ": " & dict(o) & vbCrLf
Next
MsgBox msg
Set objFolder = Nothing
Set objnSpace = Nothing
Set objOutlook = Nothing
End Sub
The code runs on the inbox itself, but it doesn't dig into sub-folders.
I have been trying to loop it correctly but I keep failing.
Thanks for help!
The code in ProcessFolder will call itself for each subfolder within the parent folder.
Option Explicit
Private MessageText As String
Public Sub ListAllFolders()
'Dim oOutlook As Object 'Outlook.Application
Dim nNameSpace As Object 'Outlook.Namespace
Dim mFolderSelected As Object 'Outlook.MAPIFolder
'''''''''''''''''''''''''''''''''''''''''
'No need to reference the Outlook application as the code
'is running from within the application itself.
''''''''''''''''''''''''''''''''''''''''
'Set oOutlook = GetObject(, "Outlook.Application")
'Set nNameSpace = oOutlook.GetNamespace("MAPI")
Set nNameSpace = GetNamespace("MAPI")
Set mFolderSelected = nNameSpace.PickFolder
ProcessFolder mFolderSelected
MsgBox MessageText
End Sub
Private Sub ProcessFolder(oParent As Object)
Dim oFolder As Object 'Outlook.MAPIFolder
Dim oMail As Object
Dim sName As String
'Get the folder name and count of items.
MessageText = MessageText & oParent.Name & ": " & oParent.Items.Count & vbCr
'If there are subfolders then process them as well.
If (oParent.Folders.Count > 0) Then
For Each oFolder In oParent.Folders
ProcessFolder oFolder
Next oFolder
End If
End Sub
Edit:
This is the code I use to count the different categories of emails in the selected folder & subfolders.
It splits the count by date and category:
Public Sub CreateReport()
Dim oOutlook As Object 'Outlook.Application
Dim nNameSpace As Object 'Outlook.Namespace
Dim mFolderSelected As Object 'Outlook.MAPIFolder
Dim oItem As Object
Dim rLastCell As Range
Dim x As Long
Set oOutlook = GetObject(, "Outlook.Application")
Set nNameSpace = oOutlook.GetNamespace("MAPI")
Set mFolderSelected = nNameSpace.PickFolder
''''''''''''''''''''''''''''''''
'Clear Sheet of existing data. '
''''''''''''''''''''''''''''''''
shtAnalysis.Cells.Delete Shift:=xlUp
ProcessFolder mFolderSelected
''''''''''''''''''''''''''
'Tidy up and add totals. '
''''''''''''''''''''''''''
Set rLastCell = LastCell(shtAnalysis)
ThisWorkbook.Activate
MsgBox "Complete", vbOKOnly
End Sub
Private Sub ProcessFolder(oParent As Object)
Dim oFolder As Object 'Outlook.MAPIFolder
Dim oMail As Object
Dim sName As String
Dim PropertyAccessor As Object
Dim v As Variant
On Error Resume Next
For Each oMail In oParent.Items
PlaceDetails oMail
Next oMail
If (oParent.Folders.Count > 0) Then
For Each oFolder In oParent.Folders
ProcessFolder oFolder
Next oFolder
End If
On Error GoTo 0
End Sub
Sub PlaceDetails(oMailItem As Object)
Dim rFoundCell As Range
Dim lColumn As Long
Dim lRow As Long
'''''''''''''''''''''''''''''''''''''''''''''
'Only process emails containing a category. '
'''''''''''''''''''''''''''''''''''''''''''''
If oMailItem.categories <> "" Then
With shtAnalysis
''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Does the category already exist on the spreadsheet? '
''''''''''''''''''''''''''''''''''''''''''''''''''''''
Set rFoundCell = .Rows("1:1").Cells.Find(What:=oMailItem.categories, After:=.Cells(1, 1), _
LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
If Not rFoundCell Is Nothing Then
lColumn = rFoundCell.Column
Else
lColumn = LastCell(shtAnalysis).Column + 1
End If
Set rFoundCell = Nothing
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Next find the row by looking for sent on date in column A. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Set rFoundCell = .Columns("A:A").Cells.Find(What:=Int(oMailItem.senton), After:=.Cells(2, 1), LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
If Not rFoundCell Is Nothing Then
lRow = rFoundCell.Row
Else
lRow = LastCell(shtAnalysis).Row + 1
End If
Set rFoundCell = Nothing
'''''''''''''''''''''''''''''''''''''''''''''''
'Place category, date and count on the sheet. '
'''''''''''''''''''''''''''''''''''''''''''''''
.Cells(lRow, 1).Value = Int(oMailItem.senton)
.Cells(1, lColumn).Value = oMailItem.categories
If .Cells(lRow, lColumn) = "" Then
.Cells(lRow, lColumn).NumberFormat = "General"
.Cells(lRow, lColumn) = 1
Else
.Cells(lRow, lColumn) = .Cells(lRow, lColumn) + 1
End If
End With
End If
End Sub
Public Function LastCell(wrkSht As Worksheet, Optional Col As Long = 0) As Range
Dim lLastCol As Long, lLastRow As Long
On Error Resume Next
With wrkSht
If Col = 0 Then
lLastCol = .Cells.Find("*", , , , xlByColumns, xlPrevious).Column
lLastRow = .Cells.Find("*", , , , xlByRows, xlPrevious).Row
Else
lLastCol = .Cells.Find("*", , , , xlByColumns, xlPrevious).Column
lLastRow = .Columns(Col).Find("*", , , , xlByColumns, xlPrevious).Row
End If
If lLastCol = 0 Then lLastCol = 1
If lLastRow = 0 Then lLastRow = 1
Set LastCell = wrkSht.Cells(lLastRow, lLastCol)
End With
On Error GoTo 0
End Function

Move Mail from Sender to Sender's Folder Name

I want to move messages from the sender to the folder I created for the sender.
The SenderName is displayed as "Doe, John (US)" and my folder would be "Doe, John".
What do I need to do to compare the SenderName to a subfolder name that is two levels below "Inbox". I.e. Inbox→Folder1→"Doe, John".
Public Sub MoveToFolder()
Dim objOutlook As Outlook.Application
Dim objNameSpace As Outlook.NameSpace
Dim objDestFolder As Outlook.MAPIFolder
Dim objSourceFolder As Outlook.Folder
Dim currentExplorer As Explorer
Dim Selection As Selection
Dim obj As Object
Dim objSubfolder As Outlook.Folder
Dim olsubFolder As Outlook.Folder
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Dim olFolder As Outlook.MAPIFolder
Dim Msg As Outlook.MailItem
Dim objVariant As Variant
Dim lngMovedItems As Long
Dim intCount As Integer
Set objOutlook = Application
Set objNameSpace = objOutlook.GetNamespace("MAPI")
Set currentExplorer = objOutlook.ActiveExplorer
Set Selection = currentExplorer.Selection
Set objSourceFolder = currentExplorer.CurrentFolder
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set olFolder = objNS.GetDefaultFolder(olFolderInbox)
Set olFolder = olFolder.Folders("Inbox")
Set colFolders = objParentFolder.Folders
For Each obj In Selection
Set objVariant = obj
Dim sfName As Object
Set sfName = Left(objVariant.senderName, Len(objVariant.senderName) - 5)
If objVariant.Class = olMail Then
On Error Resume Next
' Use These lines if the destination folder
' is not a subfolder of the current folder
For Each objSubfolder In colFolders
For Each olsubFolder In objSubfolder
If olsubFolder.Name = sfName Then
Set objDestFolder = objSubfolder
MsgBox "Ductus Exemplo"
'objVariant.Move objDestFolder
'count the # of items moved
lngMovedItems = lngMovedItems + 1
'Display the number of items that were moved.
MsgBox "Moved " & lngMovedItems & " messages(s) from " & _
sfName & "to " & objDestFolder
Else
If objDestFolder Is Nothing Then
MsgBox "No Folder Found for " & sfName
'Set objDestFolder = objSourceFolder.Folders.Add(sfName)
Exit Sub
End If
Next
Next
Next
End If
End Sub
Assumptions
The sender subfolders will be two levels below inbox but not under a single parent folder (i.e. "Doe, John" could appear under Folder1 and "Doe, Jane" under Folder2)
All emails that should be processed by the macro will be selected before executing it
The code should not create subfolders for missing senders - as there are multiple possible "parent" folders - but should output a message containing a list of missing sender folders
Conditions that trigger the end of a sender name:
A hyphen following or preceding a space (i.e. "Doe, John - US" = "Doe, John" and "Huntington-Whiteley, Rosie - CAN" = Huntington-Whiteley, Rosie")
The second instance of a comma (i.e. "Doe, John, CPA" = "Doe, John")
The second instance of a space (i.e. "Doe, John Q" = "Doe, John")
An apostrophe preceded or followed by a space (i.e. "O'Leary, John" = "O'Leary, John" but "Doe, John 'US'" = "Doe, John")
Any other non-alphabetical character (i.e. "Doe, John: US" = "Doe, John"
Proposed Solution
This code will satisfy all of the above conditions, and will output a single message at the end denoting any senders for whom folders could not be found (as opposed to a separate message for each email). It has been tested on Outlook 2013/Windows 10.
Public Sub MoveToFolder()
Dim objSelection As Selection
Set objSelection = Application.ActiveExplorer.Selection
Dim iSelected As Integer, iMoved As Integer
iSelected = objSelection.Count 'Get a total for output message
Dim StrOutput As String, StrUnmoved As String, StrName As String
StrUnmoved = "Unmoved Item Count by Sender" & vbNewLine & "============================"
Dim objNS As NameSpace
Dim objParentFolder As Folder, objSubFolder As Folder, objDestFolder As Folder
Dim BFound As Boolean, iLoc As Integer
Set objNS = Application.GetNamespace("MAPI")
Set objParentFolder = objNS.GetDefaultFolder(olFolderInbox)
'Only execute code if the parent folder has subfolders
If objParentFolder.Folders.Count > 0 Then
'Loop through all selected items
For Each Item In objSelection
If Item.Class = 43 Then
'This is an email.
BFound = False
StrName = GetSenderName(Item.SenderName)
For Each objSubFolder In objParentFolder.Folders
If objSubFolder.Folders.Count > 0 Then
On Error Resume Next
Set objDestFolder = Nothing
Set objDestFolder = objSubFolder.Folders(StrName)
On Error GoTo 0
If Not objDestFolder Is Nothing Then
'Folder found.
Item.Move objDestFolder
iMoved = iMoved + 1
BFound = True
Exit For
End If
End If
Next
If Not BFound Then
'Sender folder not found. Check if we have already logged this sender.
iLoc = 0
iLoc = InStr(1, StrUnmoved, StrName)
If iLoc > 0 Then
'Existing sender name. Increment current total.
StrUnmoved = Left(StrUnmoved, iLoc + Len(StrName) + 1) & _
Format(CInt(Mid(StrUnmoved, iLoc + Len(StrName) + 2, 5)) + 1, "00000") & Right(StrUnmoved, Len(StrUnmoved) - iLoc - Len(StrName) - 6)
Else
'New sender name.
StrUnmoved = StrUnmoved & vbNewLine & StrName & ": 00001"
End If
End If
End If
Next
If iMoved = iSelected Then
StrOutput = "All " & iSelected & " items moved to appropriate subfolders."
Else
'Remove extraneous zeroes
StrUnmoved = Replace(StrUnmoved, ": 000", ": ")
StrUnmoved = Replace(StrUnmoved, ": 00", ": ")
StrUnmoved = Replace(StrUnmoved, ": 0", ": ")
StrOutput = iMoved & "/" & iSelected & " items moved to appropriate subfolders; see below for unmoved details." & vbNewLine & vbNewLine & StrUnmoved
End If
MsgBox StrOutput
Else
MsgBox "There are no subfolders to the default inbox. Script will now exit."
End If
End Sub
Function GetSenderName(StrFullSender As String) As String
'Only take action if a non-null string is passed
If Len(StrFullSender) > 1 Then
StrFullSender = Trim(StrFullSender) 'Trim extraneous spaces
Dim StrOutput As String
'Find first case of the end of the name
Dim iChar As Integer
Dim iCommaCount As Integer
Dim iSpaceCount As Integer
For iChar = 1 To Len(StrFullSender)
Select Case Asc(Mid(StrFullSender, iChar, 1))
Case 65 To 90, 97 To 122 '192 to 246, 248 to 255 'Include 192-246 and 248-255 if you will receive emails from senders with accents or other symbols in their names
'No action necessary - this is a letter
Case 45, 151 'Hyphen or EM Dash - could be a hyphenated name
If Mid(StrFullSender, IIf(iChar = 1, 1, iChar - 1), IIf(Len(StrFullSender) - iChar > 1, 3, Len(StrFullSender) - iChar + 1)) <> _
Trim(Mid(StrFullSender, IIf(iChar = 1, 1, iChar - 1), IIf(Len(StrFullSender) - iChar > 1, 3, Len(StrFullSender) - iChar + 1))) Then Exit For
'There is a space on one or both sides of the hyphen. This is a valid stop.
Case 44
iCommaCount = iCommaCount + 1
If iCommaCount > 1 Then Exit For
Case 32
iSpaceCount = iSpaceCount + 1
If iSpaceCount > 1 Then Exit For
Case 39
If Mid(StrFullSender, IIf(iChar = 1, 1, iChar - 1), IIf(Len(StrFullSender) - iChar > 1, 3, Len(StrFullSender) - iChar + 1)) <> _
Trim(Mid(StrFullSender, IIf(iChar = 1, 1, iChar - 1), IIf(Len(StrFullSender) - iChar > 1, 3, Len(StrFullSender) - iChar + 1))) Then Exit For
'There is a space on one or both sides of the apostrophe. This is a valid stop.
Case Else
Exit For
End Select
Next
StrOutput = Trim(Left(StrFullSender, iChar - 1))
GetSenderName = StrOutput
End If
End Function
The goal appears to be organizing selected mail items based on the SenderName when invoked.
Have put a bit further as you can move items within a folder of choice when nothing in selection moved (ie. selected meeting items only).
When picking a folder to process items, the folder cannot be the main sub folder or it's child folders.
Option Explicit
Private Const SUB_FDR As String = "Folder1" ' The name of main sub-folder under Inbox to move mails to
Sub MoveSenderToFolder()
Dim oNS As NameSpace, oMainFDR As Folder, oSubFDR As Folder
Dim oItem As Variant, iMoved As Long
On Error Resume Next
Set oNS = Application.GetNamespace("MAPI")
On Error GoTo 0
If oNS Is Nothing Then
MsgBox "Cannot get MAPI namespace from Outlook! Abortting!", vbCritical + vbOKOnly, "MoveSenderToFolder()"
Else
' Proceed to Set Folders
Set oMainFDR = oNS.GetDefaultFolder(olFolderInbox)
' Get the sub folder "SUB_FDR" under Inbox
If Not oMainFDR Is Nothing Then Set oSubFDR = GetSubFolder(oMainFDR, SUB_FDR)
If oSubFDR Is Nothing Then
MsgBox "Cannot get the main sub folder """ & SUB_FDR & """ under """ & oMainFDR.Name & """"
Else
iMoved = 0
' [1] Process the Selected items
For Each oItem In ActiveExplorer.Selection
MoveItemToFolder oItem, oSubFDR, iMoved
Next
' [2] Ask to process a Folder if no MailItems are moved from Selection
If iMoved = 0 Then
If vbYes = MsgBox("Would you like to select a folder to move mail items?", vbQuestion + vbYesNo, "MoveSenderToFolder()") Then
Set oMainFDR = oNS.PickFolder ' Reuse oMainFDR object to the selected folder
' Only proceed if it's a folder not within Main Sub folder.
If Len(Replace(oMainFDR.FolderPath, oSubFDR.FolderPath, "")) = Len(oMainFDR.FolderPath) Then
For Each oItem In oMainFDR.Items
MoveItemToFolder oItem, oSubFDR, iMoved
Next
Else
MsgBox "Will not process folder/subfolders of the main folder """ & SUB_FDR & """", vbInformation + vbOKOnly, "MoveSenderToFolder()"
End If
End If
End If
Set oSubFDR = Nothing
Set oMainFDR = Nothing
End If
Set oNS = Nothing
MsgBox iMoved & " item(s) are moved.", vbInformation + vbOKOnly, "MoveSenderToFolder()"
End If
End Sub
' Move input item to a sub folder and increment counter
Private Sub MoveItemToFolder(ByRef oItem As Variant, ByRef oSubFDR As Folder, ByRef iMoved As Long)
Dim oMail As MailItem, sName As String, oTargetFDR As Folder
If TypeName(oItem) = "MailItem" Then
Set oMail = oItem
sName = GetSenderName(oMail)
Set oTargetFDR = GetSubFolder(oSubFDR, sName)
If oTargetFDR Is Nothing Then
MsgBox "Cannot get Target folder """ & oSubFDR.FolderPath & "\" & sName & """"
Else
oMail.Move oTargetFDR
iMoved = iMoved + 1
End If
Set oMail = Nothing
End If
End Sub
' Extract the Sender Name before any brackets
Private Function GetSenderName(ByRef oItem As MailItem) As String
Dim sName As String
sName = oItem.SenderName
If InStr(1, sName, "(", vbTextCompare) > 1 Then sName = Split(sName, "(")(0)
If InStr(1, sName, "<", vbTextCompare) > 1 Then sName = Split(sName, "<")(0)
If InStr(1, sName, "[", vbTextCompare) > 1 Then sName = Split(sName, "[")(0)
If InStr(1, sName, "{", vbTextCompare) > 1 Then sName = Split(sName, "{")(0)
GetSenderName = Trim(sName)
End Function
' Given a name, get the sub-folder object from a main folder (create if required)
Private Function GetSubFolder(ByRef oParentFDR As Folder, ByVal sName As String) As Folder
On Error Resume Next
Dim oFDR As Folder
Set oFDR = oParentFDR.Folders(sName)
If oFDR Is Nothing Then Set oFDR = oParentFDR.Folders.Add(sName)
Set GetSubFolder = oFDR
End Function
CODE UPDATE based on OP's comment
Searches all the sub folders within Inbox for the Sender's Name. If not found, prompts to create from Folder Picker.
Option Explicit
Private oNS As NameSpace
Sub MoveSenderToFolder()
Dim oMainFDR As Folder, oSubFDR As Folder
Dim oItem As Variant, iMoved As Long
On Error Resume Next
Set oNS = Application.GetNamespace("MAPI")
On Error GoTo 0
If oNS Is Nothing Then
MsgBox "Cannot get MAPI namespace from Outlook! Abortting!", vbCritical + vbOKOnly, "MoveSenderToFolder()"
Else
' Proceed to Set Folders
Set oMainFDR = oNS.GetDefaultFolder(olFolderInbox)
If Not oMainFDR Is Nothing Then
iMoved = 0
' [1] Process the Selected items
For Each oItem In ActiveExplorer.Selection
MoveItemToFolder oItem, oMainFDR, iMoved
Next
' [2] Ask to process a Folder if no MailItems are moved from Selection
If iMoved = 0 Then
If vbYes = MsgBox("Would you like to select a folder to move mail items?", vbQuestion + vbYesNo, "MoveSenderToFolder()") Then
Set oSubFDR = oNS.PickFolder ' Reuse oMainFDR object to the selected folder
For Each oItem In oSubFDR.Items
MoveItemToFolder oItem, oMainFDR, iMoved
Next
Set oSubFDR = Nothing
End If
End If
Set oSubFDR = Nothing
Set oMainFDR = Nothing
End If
Set oNS = Nothing
MsgBox iMoved & " item(s) are moved.", vbInformation + vbOKOnly, "MoveSenderToFolder()"
End If
End Sub
' Get Folder object based on a Name and a root folder
Private Function GetSenderFolder(ByRef oRootFDR As Folder, ByVal sName As String) As Folder
Dim oFDR As Folder, oFDR2 As Folder
For Each oFDR In oRootFDR.Folders
If oFDR.Name = sName Then
Set oFDR2 = oFDR
Exit For
End If
Next
If oFDR Is Nothing Then
For Each oFDR In oRootFDR.Folders
Set oFDR2 = GetSenderFolder(oFDR, sName)
If Not oFDR2 Is Nothing Then Exit For
Next
End If
Set GetSenderFolder = oFDR2
End Function
' Move input item (Mail Items only) to a sub folder and increment counter
Private Sub MoveItemToFolder(ByRef oItem As Variant, ByRef oRootFDR As Folder, ByRef iMoved As Long)
Dim oMail As MailItem, sName As String, oTargetFDR As Folder
If TypeName(oItem) = "MailItem" Then
Set oMail = oItem
sName = GetSenderName(oMail)
Set oTargetFDR = GetSenderFolder(oRootFDR, sName)
If oTargetFDR Is Nothing Then
If vbYes = MsgBox("Cannot get Target folder """ & oRootFDR.FolderPath & "\" & sName & """" & vbLf & _
"Would you like to create the folder from folder of your choice?", vbQuestion + vbYesNo) Then
Set oTargetFDR = CreateSubFolder(sName)
End If
End If
If Not oTargetFDR Is Nothing Then
oMail.Move oTargetFDR
iMoved = iMoved + 1
End If
Set oMail = Nothing
End If
End Sub
' Extract the Sender Name before any brackets
Private Function GetSenderName(ByRef oItem As MailItem) As String
Dim sName As String
sName = oItem.SenderName
If InStr(1, sName, "(", vbTextCompare) > 1 Then sName = Split(sName, "(")(0)
If InStr(1, sName, "<", vbTextCompare) > 1 Then sName = Split(sName, "<")(0)
If InStr(1, sName, "[", vbTextCompare) > 1 Then sName = Split(sName, "[")(0)
If InStr(1, sName, "{", vbTextCompare) > 1 Then sName = Split(sName, "{")(0)
GetSenderName = Trim(sName)
End Function
' Given a name, Create the sub-folder object from Folder Picker
Private Function CreateSubFolder(ByVal sName As String) As Folder
On Error Resume Next
Dim oFDR As Folder
Set oFDR = oNS.PickFolder
If Not oFDR Is Nothing Then Set oFDR = oFDR.Folders.Add(sName)
Set CreateSubFolder = oFDR
End Function
Update Folder Name Folders("Folder1")
Option Explicit
Sub File_olItems()
Dim olNameSpace As Outlook.NameSpace
Dim olSourceFolder As Outlook.Folder
Dim olDestFolder As Outlook.Folder
Dim currentExplorer As Explorer
Dim Selection As Selection
Dim olItem As MailItem
Dim vItem As Variant
Dim NameSender As String
Dim i As Long
Set olNameSpace = Application.GetNamespace("MAPI")
Set currentExplorer = Application.ActiveExplorer
Set Selection = currentExplorer.Selection
Set olSourceFolder = olNameSpace.GetDefaultFolder(olFolderInbox).Folders("Folder1")
For Each olItem In Selection
Set vItem = olItem
If vItem.Class = olMail Then
Debug.Print vItem.SentOnBehalfOfName
NameSender = vItem.SentOnBehalfOfName
If NameSender = ";" Then
NameSender = vItem.SenderName
End If
If InStr(1, NameSender, "(", vbTextCompare) > 1 Then
NameSender = Split(NameSender, "(")(0)
Debug.Print NameSender
End If
On Error Resume Next
Set olDestFolder = olSourceFolder.Folders(NameSender)
If olDestFolder Is Nothing Then
Set olDestFolder = olSourceFolder.Folders.Add(NameSender)
End If
vItem.Move olDestFolder
' // count items moved
i = i + 1
Set olDestFolder = Nothing
End If
Next olItem
' // Display the number of items that were moved.
MsgBox "Moved " & i & " Mail Items."
Set currentExplorer = Nothing
Set olItem = Nothing
Set Selection = Nothing
Set olNameSpace = Nothing
Set olSourceFolder = Nothing
End Sub

Runtime error looping through Outlook items

I am using VBA in Outlook to extract mail information from items in the mainfolder and subfolder. The mainfolder failed to set(capture) the subfolder properties into it and it causes the runtime error.
The runtime error differs whenever I run. For example, sometime I received -970718969 (c6240107) and another time I received -2044460793 (86240107).
When I clicked debug, it points to this line of code:
For Each itm In subFld.Items
Here is the screenshot:
Here is the full code:
Public monthValue As Integer
Public yearValue As String
'Ensure Microsoft Excel 11.0 Object Library is ticked in tools.
Sub ExportToExcel1()
Dim appExcel As Excel.Application
Dim wkb As Excel.Workbook
Dim wks As Excel.Worksheet
Dim rng As Excel.Range
Dim strSheet As String
Dim strPath As String
Dim intRowCounter As Integer
Dim msg As Outlook.MailItem
Dim nms As Outlook.NameSpace
Dim mainFld As Outlook.MAPIFolder
Dim subFld As Outlook.MAPIFolder
Dim itm As Object
Dim offsetRow As Long
Dim emailCount As Long
'Set the path of the excel file.
strSheet = "For fun.xlsx"
strPath = "C:\Users\xxxxxx\Desktop\xxxxx\"
strSheet = strPath & strSheet
Debug.Print strSheet
Set nms = Application.GetNamespace("MAPI")
Set mainFld = nms.PickFolder 'Open the box to select the file.
'Handle potential errors with Select Folder dialog box.
If mainFld Is Nothing Then
MsgBox "Thank you for using this service.", vbOKOnly, "Error"
Set nms = Nothing
Set mainFld = Nothing
Exit Sub
ElseIf mainFld.DefaultItemType <> olMailItem Then
MsgBox "Please select the correct folder.", vbOKOnly, "Error"
Set nms = Nothing
Set mainFld = Nothing
Exit Sub
ElseIf mainFld.Items.Count = 0 Then
MsgBox "There are no mail messages to export", vbOKOnly, "Error"
Set nms = Nothing
Set mainFld = Nothing
Exit Sub
End If
mainForm.Show
'If user clicks cancel, it will exit sub.
If yearValue = "" Then
Set nms = Nothing
Set mainFld = Nothing
Exit Sub
End If
'Open and activate Excel workbook.
Set appExcel = CreateObject("Excel.Application")
appExcel.Workbooks.Open (strSheet)
Set wkb = appExcel.ActiveWorkbook
Set wks = wkb.Sheets(1)
wks.Activate
appExcel.Application.Visible = True 'Show my workbook.
'Check if there are any subfolders.
If mainFld.Folders.Count = 0 Then '1
'No subfolder.
For Each itm In mainFld.Items
If itm.Class <> olMail Then '2
'do nothing
Else
Set msg = itm
'Validate the month and year for the email.
If Month(msg.ReceivedTime) = monthValue And Year(msg.ReceivedTime) = yearValue Then '3
With wks
offsetRow = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
intRowCounter = 1 + offsetRow
Set rng = wks.Cells(intRowCounter, 1)
rng.Value = msg.ReceivedTime
Set rng = wks.Cells(intRowCounter, 2)
rng.Value = msg.SentOn
Set rng = wks.Cells(intRowCounter, 3)
rng.Value = msg.Subject
emailCount = 1 + emailCount 'Track the number of email.
Else
'Do nothing
End If '3
End If '2
Next itm
Else
'With subfolder
For Each itm In mainFld.Items
If itm.Class <> olMail Then '4
'do nothing
Else
Set msg = itm
If Month(msg.ReceivedTime) = monthValue And Year(msg.ReceivedTime) = yearValue Then '5
With wks
offsetRow = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
intRowCounter = 1 + offsetRow
Set rng = wks.Cells(intRowCounter, 1)
rng.Value = msg.ReceivedTime
Set rng = wks.Cells(intRowCounter, 2)
rng.Value = msg.SentOn
Set rng = wks.Cells(intRowCounter, 3)
rng.Value = msg.Subject
emailCount = 1 + emailCount
Else
'Do nothing
End If '5
End If '4
Next itm
For Each subFld In mainFld.Folders
For Each itm In subFld.Items
If itm.Class <> olMail Then '6
'do nothing
Else
Set msg = itm
If Month(msg.ReceivedTime) = monthValue And Year(msg.ReceivedTime) = yearValue Then '7
With wks
offsetRow = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
intRowCounter = 1 + offsetRow
Set rng = wks.Cells(intRowCounter, 1)
rng.Value = msg.ReceivedTime
Set rng = wks.Cells(intRowCounter, 2)
rng.Value = msg.SentOn
Set rng = wks.Cells(intRowCounter, 3)
rng.Value = msg.Subject
emailCount = 1 + emailCount
Else
'Do nothing
End If '7
End If '6
Next itm
Next subFld
End If '1
Set appExcel = Nothing
Set wkb = Nothing
Set wks = Nothing
Set rng = Nothing
Set msg = Nothing
Set nms = Nothing
Set mainFld = Nothing
Set subFld = Nothing
Set itm = Nothing
'Inform the user that there are no email.
If emailCount = 0 Then
MsgBox "No emails associated with this date: " & MonthName(monthValue, True) & " " & yearValue, vbOKOnly, "No Emails"
End If
Exit Sub
Set appExcel = Nothing
Set wkb = Nothing
Set wks = Nothing
Set rng = Nothing
Set msg = Nothing
Set nms = Nothing
Set mainFld = Nothing
Set subFld = Nothing
Set itm = Nothing
End Sub
Do you get that error immediately or only after processing a large number of items? Most likely you are opening too many items and run out of RPC channels. Is this a cached or an online Exchange profile?
Instead of looping through all items, use the Table object (MAPITable.GetTable) - if nothing else, it will be a lot faster.
EDIT: If you are using Exchange, every store object (message, folder, store) opens an RPC channel. Exchange Server limits the number of RPC channels to 255 per client (can be changed on the server). Do not use "for each" loop (it keeps all items referenced until the loop ends) and avoid multiple dot notation (because you will have implicit variables that you cannot explicitly dereference). You will also need to release all Outlook objects as soon as you are done with them.
set fldItems = mainFld.Items
For i = 1 to fldItems.Count do
set itm = fldItems.Item(i)
'do stuff
set itm = Nothing
next
As for the Table object (introduced in Outlook 2007), see http://msdn.microsoft.com/en-us/library/office/ff860769.aspx. If you need to use this in an earlier version of Outlook, you can use the MAPITable object in Redemption (I am its author); it also has a MAPITable.ExecSQL method that takes a standard SQL query and returns the ADODB.Recordset object.