How to set focus to Inbox? - vba

When you open Outlook, Inbox has the focus. If you go to another folder then run VBA code the focus stays on that folder.
My macro deletes all files in my Deleted folder and Trash folder. I would like it to come back to the Inbox when complete.
Public Sub EmptyFolder()
Dim Items As Outlook.Items
Dim i As Long
Dim Count As Long
Dim Delete As Boolean
' Clear Junk Items
Set ns = Application.GetNamespace("MAPI")
Set Items = Session.GetDefaultFolder(olFolderCalendar).Parent.Folders("Junk").Items
Count = Items.Count
If Count = 0 Then
GoTo DeleteAll
End If
Delete = True
For i = Count To 1 Step -1
Items(i).Delete
Next
DeleteAll:
' Clear Trash Items
Set Items = Session.GetDefaultFolder(olFolderCalendar).Parent.Folders("Trash").Items
Count = Items.Count
If Count = 0 Then
GoTo EndAll
End If
Delete = True
For i = Count To 1 Step -1
Items(i).Delete
Next
EndAll:
End Sub

You could just change the CurrentFolder value to one of your Inbox folders in my example "family" as follow.
Sub ChangeCurrentFolder()
Dim myNamespace As Outlook.NameSpace
Set ns = Application.GetNamespace("MAPI")
Set Application.ActiveExplorer.CurrentFolder = ns.GetDefaultFolder(olFolderInbox)
End Sub

On a related note to what you are trying to achieve (but not directly related to the question), I use a generic function to delete older e-mails from folders and the code is below:
Call the function in Startup
In ThisOutlookSession
Private Sub Application_Startup()
On Error Resume Next
Call DeleteAgedJunkMail
End Sub
In a module
Function DeleteAgedJunkMail() As Boolean
Dim ThisNamespace As Outlook.NameSpace: Set ThisNamespace = Application.GetNamespace("MAPI")
Dim Inbox As Outlook.MAPIFolder: Set Inbox = ThisNamespace.GetDefaultFolder(olFolderInbox)
Dim Junk As Outlook.MAPIFolder: Set Junk = ThisNamespace.GetDefaultFolder(olFolderJunk)
On Error Resume Next
Call DeleteAgedMail(Junk, 7)
Call DeleteAgedMail(Inbox.Folders("Quotes"), 90)
Call DeleteAgedMail(Inbox.Folders("Orders"), 90)
DeleteAgedJunkMail = True
End Function
Function DeleteAgedMail(ByRef Folder As Outlook.MAPIFolder, Optional Days As Long = 7) As Boolean
On Error GoTo ExitFunction
Dim Var As Variant, i As Long, Difference As Long, Items As Long
For i = Folder.Items.Count To 1 Step -1
Set Var = Folder.Items.Item(i)
DoEvents
If Var.Class = olMail Then
Difference = DateDiff("d", Var.SentOn, Now)
If Difference > Days Then
Var.Delete
Items = Items + 1 'Count the # of items deleted
End If
End If
Next
' Record the number of items that were deleted.
Debug.Print "Deleted " & Items & " message(s) from '" & Folder.Name & "'."
DeleteAgedMail = True
ExitFunction:
End Function

Related

Outlook Inbox-to-Folder Sortation Macro for Non-Default Inbox

I am trying to, in VBA for Outlook 2013, sort any mail with a certain number format in the subject into corresponding folders. If the folder does not exist (if the strings in the subject and folder don't match), the folder is created. I need this macro to handle a non-default inbox. The following links are where I got the original code, which is spliced together at the bottom. I'm getting a run time error (-2147221233 (8004010f)) on line:
Set objProjectFolder = objDestinationFolder.Folders(folderName)
http://joelslowik.blogspot.com/2011/04/sort-emails-in-outlook-using-macro-and.html
Get email from non default inbox?
Dim WithEvents myitems As Outlook.Items
Dim objDestinationFolder As Outlook.MAPIFolder
Sub Application_Startup()
Dim myNameSpace As Outlook.NameSpace
Dim myInbox As Outlook.MAPIFolder
Dim myitems As Outlook.Items
Dim strFilter As String
' let the user choose which account to use
Set myAccounts = Application.GetNamespace("MAPI").Stores
For i = 1 To myAccounts.Count
res = MsgBox(myAccounts.Item(i).DisplayName & "?", vbYesNo)
If res = vbYes Then
Set myInbox = myAccounts.Item(i).GetDefaultFolder(olFolderInbox)
Exit For
End If
Next
If myInbox Is Nothing Then Exit Sub ' avoid error if no account is chosen
Set objDestinationFolder = myInbox.Parent.Folders("Inbox")
For Count = myInbox.Items.Count To 1 Step -1
Call myitems_ItemAdd(myInbox.Items.Item(Count))
Next Count
StopRule
End Sub
' Run this code to stop your rule.
Sub StopRule()
Set myitems = Nothing
End Sub
' This code is the actual rule.
Private Sub myitems_ItemAdd(ByVal Item As Object)
Dim objProjectFolder As Outlook.MAPIFolder
Dim folderName As String
' Search for email subjects that contain a case number
' Subject line must have the sequence of 4 numbers + - + 3 numbers (CPS case number syntax)
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Global = False
objRegEx.Pattern = "[0-9]{4,4}\-?[0-9]{0,3}"
Set colMatches = objRegEx.Execute(Item.Subject)
'For all matches, move those matches to respective folder (create folder if it does not exist)
If colMatches.Count > 0 Then
For Each myMatch In colMatches
folderName = "Docket # " & myMatch.Value
If FolderExists(objDestinationFolder, folderName) Then
Set objProjectFolder = objDestinationFolder.Folders(folderName)
Else
Set objProjectFolder = objDestinationFolder.Folders.Add(folderName)
End If
Item.Move objProjectFolder
Next
End If
Set objProjectFolder = Nothing
End Sub
Function FolderExists(parentFolder As MAPIFolder, folderName As String)
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Global = False
objRegEx.Pattern = folderName
For Each F In parentFolder.Folders
Set colMatches = objRegEx.Execute(F.Name)
If colMatches.Count > 0 Then
FolderExists = True
folderName = colMatches(0).Value
Exit Function
End If
Next
FolderExists = False
End Function
I recently upgraded to Outlook 2016 and had the same problem: the default Inbox was not where I expected it.
When I installed Outlook 2016, it created a default store “outlook data file”. As I added my email accounts, it created a separate store for each of them. It was not until later I realised the default Inbox was in the unused “outlook data file”.
For your interest, this macro will display the name of the store holding the default Inbox:
Sub DsplUsernameOfStoreForDefaultInbox()
Dim NS As Outlook.NameSpace
Dim DefaultInboxFldr As MAPIFolder
Set NS = CreateObject("Outlook.Application").GetNamespace("MAPI")
Set DefaultInboxFldr = NS.GetDefaultFolder(olFolderInbox)
Debug.Print DefaultInboxFldr.Parent.Name
End Sub
In your code replace
Set myInbox = myAccounts.Item(i).GetDefaultFolder(olFolderInbox)
by
Set myInbox = Session.Folders("outlook data file").Folders("Inbox")
after replacing "outlook data file" with the name of the store containing the Inbox you wish to access.
You can use this technique for referencing any folder at any depth within any store. For example:
Set FldrTgt = Session.Folders("zzzz").Folders("yyyy").Folders("xxxx").Folders("wwww")
Extra point
I do not see the point of:
Set objDestinationFolder = myInbox.Parent.Folders("Inbox")
This starts at myBox, uses property Parent to go up to the store then property Folders to go down to "Inbox" again. It is the same as:
Set objDestinationFolder = myInbox

Mark as Read loop errors on some mail items inconsistently

I receive 4000+ emails over a weekend and all throughout the week that are filtered into folders via rules. I created a macro to mark all these folders as read. However, on some mail items it errors with runtime error 91 object variable or with block variable not set.
If I skip the errors with On Error Resume Next it loops through everything but just doesn't set a bunch of the mail items as read. I can then rerurn the macro to get most of the remaining ones. If I run the macro 3-4 times it will eventually get them all.
How can I improve this macro to consistently mark ALL the items as read?
Public Function GetInboxFolderID(FolderName As String) As String
Dim nsp As Outlook.Folder
Dim mpfSubFolder As Outlook.Folder
Dim mpfSubFolder2 As Outlook.Folder
Dim flds As Outlook.Folders
Dim flds2 As Outlook.Folders
Set nsp = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
Set flds = nsp.Folders
Set mpfSubFolder = flds.GetFirst
Do While Not mpfSubFolder Is Nothing
If mpfSubFolder.Name = FolderName Then
GetInboxFolderID = mpfSubFolder.EntryID
Exit Function
End If
Set flds2 = mpfSubFolder.Folders
Set mpfSubFolder2 = flds2.GetFirst
Do While Not mpfSubFolder2 Is Nothing
If mpfSubFolder2.Name = FolderName Then
GetInboxFolderID = mpfSubFolder2.EntryID
Exit Function
End If
Set mpfSubFolder2 = flds2.GetNext
Loop
Set mpfSubFolder = flds.GetNext
Loop
End Function
Private Sub processFolder(ByVal oParent As Outlook.MAPIFolder)
Dim oFolder As Outlook.MAPIFolder
Dim oMail As Outlook.MailItem
Dim oFiltered As Outlook.Items
Set oFiltered = oParent.Items.Restrict("[unread] = true")
On Error Resume Next
For Each oMail In oFiltered
oMail.UnRead = False
Next
If (oParent.Folders.Count > 0) Then
For Each oFolder In oParent.Folders
processFolder oFolder
Next
End If
End Sub
Public Sub markNocAsRead()
Dim SubFolder As MAPIFolder
Set SubFolder = Session.GetDefaultFolder(olFolderCalendar).Parent.Folders("NOC Alerts")
'Application.Session.GetFolderFromID (GetInboxFolderID("NOC Alerts"))
Call processFolder(SubFolder)
End Sub
I was able to get this working consistently by taking the advice of Ryan Wildry in a comment above.
I replaced my loop:
Set oFiltered = oParent.Items.Restrict("[unread] = true")
On Error Resume Next
For Each oMail In oFiltered
oMail.UnRead = False
Next
with a loop that iterates from the end to beginning:
Set oFiltered = oParent.Items.Restrict("[unread] = true")
For I = oFiltered.Count To 1 Step -1
Set oMail = oFiltered(I)
oMail.UnRead = False
Next

Delete duplicate mails Outlook 2013

Im trying to create a VBA macro that checks if there is a duplicate mail (looks at subject) and then deletes the mail.
This code works but is deleting the oldest duplicates. It's counting in descending order and I can't seem to get the sorting of the items to work.
Basically I need help figuring out how to make sure the "newest" duplicate by received time gets deleted.
Sub RemoveDuplicates()
Dim oFolder As Folder
Dim oEmail As MailItem, oItems As ItemProperties, oItem As ItemProperty
Dim cMail As Collection
Dim i As Long
Set oFolder = Application.ActiveExplorer.CurrentFolder
Set cMail = New Collection
With oFolder
' .Items.Sort "[ReceivedTime]", True
If olMailItem <> .DefaultItemType Then Exit Sub
For i = .Items.Count To 1 Step -1
Set oItems = .Items(i).ItemProperties
Debug.Print oItems("ReceivedTime")
If Not oItems("ReceivedTime") Is Nothing Then
Set oItem = oItems("ReceivedTime")
'// Week old
If oItem >= Date - 7 Then
On Error GoTo ErrHandler
'// Delete Duplicate Subject
cMail.Add oItems("Subject"), oItems("Subject")
On Error GoTo 0
End If
End If
Next i
End With
Exit Sub
ErrHandler:
Debug.Print Err.Number, oItems("Subject"), oItems("ReceivedTime")
oFolder.Items(i).Delete
Resume Next
End Sub
Cache the Items collection before entering the loop (otherwise you get a brand new Items COM object each time), sort it on ReceivedTime (Items.Sort), then loop from Count down to 1.
Expanding on #DmitryStreblechenko's answer:
The following will keep the MailItem with the oldest date and delete more recent ones with the same subject.
For convenience TargetFolder and MinDate are configurable but optional. They default to the currently visible folder and seven days ago.
Sub RemoveDuplicates(Optional TargetFolder As Folder, Optional MinDate As Date)
Dim Items As Items, Email As MailItem
Dim i As Long, Dupes As Object
If MinDate = vbEmpty Then MinDate = Date - 7
If TargetFolder Is Nothing Then Set TargetFolder = ActiveExplorer.CurrentFolder
Set Dupes = CreateObject("Scripting.Dictionary")
Set Items = TargetFolder.Items
Items.Sort "[ReceivedTime]"
Debug.Print "Dedupe <" & TargetFolder.FolderPath & ">, " & Items.Count & " items"
For i = Items.Count To 1 Step -1
If TypeOf Items(i) Is MailItem Then
Set Email = Items(i)
If Email.ReceivedTime >= MinDate Then
If Dupes.Exists(Email.Subject) Then
Debug.Print "DELETE: " & Email.Subject
'Item.Delete
Else
Dupes.Add Email.Subject, 0
End If
End If
End If
Next i
End Sub
This makes use of a Scripting.Dictionary because unlike the Collection object it supports a handy Exists() method.

How do I create a macro to move the oldest 20 emails from the bottom of my inbox to another folder in outlook?

I'm trying to move the bottom 20 emails to another folder in Outlook to another folder where the macro runs. I'm able to move then when selected but I don't want to have to select 20 from the bottom (oldest) first. I'd like to automate this bit too.
Any help would be appreciated.
Here's what I have so far but it moves the most recent mail only, regardless of how the inbox is sorted:
Public Sub Move_Inbox_Emails()
Dim outApp As Object
Dim outNS As Object
Dim inboxFolder As Object
Dim destFolder As Object
Dim outEmail As Object
Dim inboxItems As Object
Dim i As Integer
Dim inputNumber As String
Dim numberToMove As Integer
inputNumber = InputBox("Enter number of emails to move")
On Error Resume Next
numberToMove = CInt(inputNumber)
On Error GoTo 0
If numberToMove < 1 Then Exit Sub
Set outApp = CreateObject("Outlook.Application")
Set outNS = outApp.GetNamespace("MAPI")
Set inboxFolder = outNS.GetDefaultFolder(olFolderInbox)
Set destFolder = inboxFolder.Parent.Folders("Mobus") 'Test folder at same level as Inbox
'Sort Inbox items by Received Time
Set itemsCol = inboxFolder.Items
itemsCol.Sort "[ReceivedTime]", False
'Loop through sorted items for the number entered by the user, up to the number of items in the Inbox
For i = inboxFolder.Items.Count To inboxFolder.Items.Count - numberToMove + 1 Step -1
If inboxFolder.Items(i).Class = OlObjectClass.olMail Then
Set outEmail = inboxFolder.Items(i)
'Debug.Print outEmail.ReceivedTime, outEmail.subject
outEmail.Move destFolder
End If
Next
End Sub
I've solved this now with some ideas from the commentors, thanks very much. This code now prompts for how many to move and takes them from the oldest first:
Public Sub Move_Inbox_Emails_From_Excel()
Dim outApp As Object
Dim outNS As Object
Dim inboxFolder As Object
Dim destFolder As Object
Dim outEmail As Object
Dim inboxItems As Object
Dim i As Integer
Dim inputNumber As String
Dim numberToMove As Integer
inputNumber = InputBox("Enter number of emails to move")
On Error Resume Next
numberToMove = CInt(inputNumber)
On Error GoTo 0
If numberToMove < 1 Then Exit Sub
Set outApp = CreateObject("Outlook.Application")
Set outNS = outApp.GetNamespace("MAPI")
Set inboxFolder = outNS.GetDefaultFolder(olFolderInbox)
Set destFolder = inboxFolder.Parent.Folders("Mobus") 'Test folder at same level as Inbox
'Sort Inbox items by Received Time
Set inboxItems = inboxFolder.Items
'inboxItems.Sort "[ReceivedTime]", False 'ascending order (oldest first)
inboxItems.Sort "[ReceivedTime]", True 'descending order (newest first)
'Loop through sorted items for the number entered by the user, up to the number of items in the Inbox
For i = inboxFolder.Items.Count To inboxFolder.Items.Count - numberToMove + 1 Step -1
Set outEmail = inboxItems(i)
'Debug.Print i, outEmail.Subject
outEmail.Move destFolder
Next
End Sub
Sort the Items collection by ReceivedTime property, loop though the last 20 items (use a down loop - step -1) and move the items.

outlook vba select messages in sub-folder

Outlook 2007 is configured with two email accounts:
Account#1: Hotmail
Account#2: Gmail
I would like to create a macro named simulating a user doing the following:
Left click on a within either the hotmail or gmail account.
Highlight all messages within the folder previously selected.
display a messageBox with the number of emails selected from this folder
I have tried several methods to define the folder, but its not working. My suspicion is it would work on the default PST, but that isn't what I'm using. Even tried using the method below to identify the specific folder I want to use. It does print out a path, but I am not able to use that as a variable value directly.
Any suggestions?
=== Information ===
The following macro was used to obtain information about the account & folder locations:
http://www.gregthatcher.com/Scripts/VBA/Outlook/GetFolderInfo.aspx
Hotmail
Name: aaaaa
FolderPath: \#hotmail.com\aaaaa
-
Gmail
Name: bbbbb
FolderPath: \#gmail.com\bbbbb
' please add your values for Const emailAccount and Const folderToSelect
' To begin, launch: start_macro
'
' the macro will loop all folders and will check two things , folder name and account name,
' when both are matched , will make that folder the active one , then will select all emails
' from it and at final will issue number of selected items no other References are required
' than default ones
Option Explicit
#If VBA7 Then
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) 'For 64 Bit Systems
#Else
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For 32 Bit Systems
#End If
' please provide proper values for email account and folder name
Const emailAccount = "username#hotmail.com"
Const folderToSelect = "folder"
' declare some public variables
Dim mySession As Outlook.NameSpace
Dim myExplorer As Outlook.Explorer
Dim mySelection As Outlook.Selection
Dim my_folder As Outlook.folder
Sub start_macro()
Dim some_folders As Outlook.Folders
Dim a_fld As Variant
Dim fld_10 As Outlook.folder
Set mySession = Application.Session
Set some_folders = mySession.Folders
For Each a_fld In some_folders
Set fld_10 = a_fld
Call loop_subfolders_2(fld_10)
Next a_fld
End Sub
Sub final_sub()
If Not (my_folder Is Nothing) Then
Set myExplorer = Application.ActiveExplorer
Set Application.ActiveExplorer.CurrentFolder = my_folder
Call select_all_items(my_folder)
Else
MsgBox "There is no folder available for specified account !!!"
End If
End 'end the macro now
End Sub
Sub loop_subfolders_2(a_folder As Outlook.folder)
Dim col_folders As Outlook.Folders
Dim fld_1 As Outlook.folder
Dim arr_1 As Variant
Set col_folders = a_folder.Folders
For Each fld_1 In col_folders
If Left(fld_1.FolderPath, 2) = "\\" Then
arr_1 = Split(fld_1.FolderPath, "\")
'Debug.Print fld_1.Name & vbTab & arr_1(2) & vbTab & fld_1.FolderPath
If InStr(LCase(emailAccount), "#gmail.com") > 0 Then
If LCase(folderToSelect) = LCase(fld_1.Name) Then
If LCase(emailAccount) = LCase(arr_1(2)) Or arr_1(2) = "Personal Folders" Then
Set my_folder = fld_1
Call final_sub
Else
Call loop_subfolders_2(fld_1)
End If
Else
Call loop_subfolders_2(fld_1)
End If
Else
If LCase(folderToSelect) = LCase(fld_1.Name) And LCase(emailAccount) = LCase(arr_1(2)) Then
Set my_folder = fld_1
Call final_sub
Else
Call loop_subfolders_2(fld_1)
End If
End If
End If
Next fld_1
End Sub
Sub select_all_items(my_folder As Outlook.folder)
Dim my_items As Outlook.Items
Dim an_item As MailItem
Dim a As Long, b As Long
Set my_items = my_folder.Items
b = my_items.Count
DoEvents
'sleep 2000
Set mySelection = myExplorer.Selection
If CLng(Left(Application.Version, 2)) >= 14 Then
On Error Resume Next ' there are other folders that do not contains mail items
For Each an_item In my_items
If myExplorer.IsItemSelectableInView(an_item) Then
myExplorer.AddToSelection an_item
Else
End If
Next an_item
On Error GoTo 0
Else
myExplorer.Activate
If b >= 2 Then
For a = 1 To b - 1
SendKeys "{DOWN}"
'Sleep 50
Next a
For a = 1 To b - 1
SendKeys "^+{UP}"
' 'Sleep 50
Next a
End If
DoEvents
'sleep 2000
End If
Set my_items = Nothing
Set mySelection = myExplorer.Selection
MsgBox mySelection.Count
End Sub
does this one work?
Function GetFolder(ByVal FolderPath As String) As Outlook.folder
Dim TestFolder As Outlook.folder
Dim FoldersArray As Variant
Dim i As Integer
On Error GoTo GetFolder_Error
If Left(FolderPath, 2) = "\\" Then
FolderPath = Right(FolderPath, Len(FolderPath) - 2)
End If
'Convert folderpath to array
FoldersArray = Split(FolderPath, "\")
Set TestFolder = Application.Session.Folders.item(FoldersArray(0))
If Not TestFolder Is Nothing Then
For i = 1 To UBound(FoldersArray, 1)
Dim SubFolders As Outlook.Folders
Set SubFolders = TestFolder.Folders
Set TestFolder = SubFolders.item(FoldersArray(i))
If TestFolder Is Nothing Then
Set GetFolder = Nothing
End If
Next
End If
'Return the TestFolder
Set GetFolder = TestFolder
Exit Function
GetFolder_Error:
'MsgBox ("Ordner für verschieben nicht gefunden")
Set GetFolder = Nothing
Exit Function
End Function
for me this works with all Folders, no matter if Primary or other box (but all of them being Exchange, but I do not think this maters)
e.g. These work:
Set mailitem.SaveSentMessageFolder = GetFolder(mailitem.SentOnBehalfOfName & "\inbox")
Dim Subfolder As Outlook.MAPIFolder
Set Subfolder = GetFolder(olfolder.FullFolderPath & "\erledigt")
Dim Subfolder As Outlook.MAPIFolder
Set Subfolder = GetFolder("someaccount\inbox")