Move outlook mail from one mailbox inbox to different folder in same mailbox - vba

I have several mailboxes which I can see in my Outlook profile. One of the mailboxes, let's call it "Mailbox - HUR" receives messages constantly. presently one of my team goes into the inbox of this mailbox every day and moves (drag and drop) the messages into a subfolder of the inbox called Archive (we're an imaginative lot!) if the messages are greater than 24 hours old.
Is there any way that a macro can be set up to do this task? I know my simple way around VBA but have never used it with Outlook and can't figure out the namespace details to point me to the correct mailbox instead of my mailbox.
Unfortunately I do not have access to Exchange server, only using outlook client.
Any help anyone could give would be great.

You might like to try:
Sub MoveOldEmail()
Dim oItem As MailItem
Dim objMoveFolder As MAPIFolder
Dim objInboxFolder As MAPIFolder
Dim i As Integer
Set objMoveFolder = GetFolder("Personal Folders\Inbox\Archive")
Set objInboxFolder = GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
For i = objInboxFolder.Items.Count - 1 To 0 Step -1
With objInboxFolder.Items(i)
''Error 438 is returned when .receivedtime is not supported
On Error Resume Next
If .ReceivedTime < DateAdd("h", -24, Now) Then
If Err.Number = 0 Then
.Move objMoveFolder
Else
Err.Clear
End If
End If
End With
Next
Set objMoveFolder = Nothing
Set objInboxFolder = Nothing
End Sub
Public Function GetFolder(strFolderPath As String) As MAPIFolder
'' strFolderPath needs to be something like
'' "Public Folders\All Public Folders\Company\Sales" or
'' "Personal Folders\Inbox\My Folder"
Dim objNS As NameSpace
Dim colFolders As Folders
Dim objFolder As MAPIFolder
Dim arrFolders() As String
Dim i As Long
On Error GoTo TrapError
strFolderPath = Replace(strFolderPath, "/", "\")
arrFolders() = Split(strFolderPath, "\")
Set objNS = GetNamespace("MAPI")
On Error Resume Next
Set objFolder = objNS.Folders.Item(arrFolders(0))
If Not objFolder Is Nothing Then
For i = 1 To UBound(arrFolders)
Set colFolders = objFolder.Folders
Set objFolder = Nothing
Set objFolder = colFolders.Item(arrFolders(i))
If objFolder Is Nothing Then
Exit For
End If
Next
End If
On Error GoTo TrapError
Set GetFolder = objFolder
Set colFolders = Nothing
Set objNS = Nothing
Exit_Proc:
Exit Function
TrapError:
MsgBox Err.Number & " " & Err.Description
End Function

You should setup a mailbox rule. Tools | Rules Wizard
If you are using Exchange server have an Outlook rule to move the emails to the specific folder, then use the Mailbox Manager in Exchange to delete messages from that folder after a specific period of time. See this article for more information.

Fionnuala you rock!
I've been looking for a solution to a similar issue for months. With my corporate restrictions, I wasn't able to use the UDF (worked just fine on my personal); Within the sub MoveOldEmail, I instead used:
Set objMoveFolder = GetNamespace("MAPI").PickFolder
Cool thing is that this seems to let me move between email accounts that I have associated with my Outlook (until corp figures out at least).

Related

Moving over 20,000 emails, based on email address, freezes Outlook

I am trying to move over 20,000 emails, based on email address, into desired folders.
The code I found freezes Outlook. The code does work before the freeze.
Using first code from the answer to this post
Option Explicit
Public Sub Move_Items()
' // Declare your Variables
Dim Inbox As Outlook.MAPIFolder
Dim SubFolder As Outlook.MAPIFolder
Dim olNs As Outlook.NameSpace
Dim Item As Object
Dim Items As Outlook.Items
Dim lngCount As Long
On Error GoTo MsgErr
' Set Inbox Reference
Set olNs = Application.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Set Items = Inbox.Items
' // Loop through the Items in the folder backwards
For lngCount = Items.Count To 1 Step -1
Set Item = Items(lngCount)
If Item.Class = olMail Then
Select Case Item.SenderEmailAddress
' // Email_One
Case "Email_One#email.com"
' // Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder One")
Set Item = Items.Find("[SenderEmailAddress] = 'Email_One#email.com'")
If TypeName(Item) <> "Nothing" Then
' // Mark As Read
Item.UnRead = False
' // Move Mail Item to sub Folder
Item.Move SubFolder
End If
' // Email_Two
Case "Email_Two#email.com"
' // Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder Two")
Set Item = Items.Find("[SenderEmailAddress] = 'Email_Two#email.com'")
If TypeName(Item) <> "Nothing" Then
' // Mark As Read
Item.UnRead = False
' // Move Mail Item to sub Folder
Item.Move SubFolder
End If
End Select
End If
Next lngCount
MsgErr_Exit:
Set Inbox = Nothing
Set SubFolder = Nothing
Set olNs = Nothing
Set Item = Nothing
Set Items = Nothing
Exit Sub
'// Error information
MsgErr:
MsgBox "An unexpected Error has occurred." _
& vbCrLf & "Error Number: " & Err.Number _
& vbCrLf & "Error Description: " & Err.Description _
, vbCritical, "Error!"
Resume MsgErr_Exit
End Sub
Also is it possible to filter not a specific email address e.g. dave#test.com but *#test.com?
I think at least your first problem might be the line 'Set Inbox = olNs.GetDefaultFolder(olFolderInbox)'
I have the similar line 'Set Items = objNS.GetDefaultFolder(olFolderInbox).Items' in my start-up routine Private Sub Application_Startup() . This worked fine ever since we switched to 365, but then circa February 2021 it started to crash on start-up. I got here by searching on this problem. Presumably they have changed something about the object model.
I also suppose it could be where olNs is set in the first place ' Set objNS = olApp.GetNamespace("MAPI"), if you mail doesn't use MAPI?
I've chucked the problem at out IT support, and I'll let you know if they come back with anything other than a mildly panicked 'what the hell you doing using VBA?'
The delay is caused by running a time-consuming task/code in Outlook. So, you need to optimize what and how is run in Outlook.
The problem is in the source code. I've noticed that you are iterating over all items in the folder:
// Loop through the Items in the folder backwards
For lngCount = Items.Count To 1 Step -1
That is completely a bad idea!
Instead, you need to use the Find/FindNext or Restrict methods to process all items that correspond to the specified search criteria. The Find method returns a single and first entry from the list of items. To get the second (if any) you need to use the FindNext method in the loop.
Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Also you may consider using the AdvancedSearch method of the Application class. 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.
See Advanced search in Outlook programmatically: C#, VB.NET for more information.
If processing every item there is no need for a Find. Find replaces the For loop item. It is more likely to run to completion when there are fewer items.
The simplest change is to remove the Find. This should fix any array out of bounds errors. Still it is inefficient.
// Email_One
Case "Email_One#email.com"
'// Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder One")
'// Mark As Read
Item.UnRead = False
'// Move Mail Item to sub Folder
Item.Move SubFolder
One way to limit processing to the applicable items.
Option Explicit
Public Sub Move_Items_Restrict()
'// Declare your Variables
Dim myInbox As Folder
Dim subFolder As Folder
Dim myItem As Object
Dim myItems As Items
Dim resItems As Items
Dim strfilter As String
Dim i As Long
' Not while developing
'On Error GoTo MsgErr
' Set Inbox Reference
Set myInbox = Session.GetDefaultFolder(olFolderInbox)
'// Email_One
Set myItems = myInbox.Items
strfilter = "[SenderEmailAddress] = 'Email_One#email.com'"
Debug.Print strfilter
' some of these work, fromemail does
' https://learn.microsoft.com/en-us/previous-versions/office/developer/exchange-server-2007/aa579702(v=exchg.80)
'strfilter = "#SQL=urn:schemas:httpmail:fromemail LIKE '%#test.com'"
'Debug.Print strfilter
Set resItems = myItems.Restrict(strfilter)
Debug.Print resItems.count
If resItems.count > 0 Then
'// Set SubFolder of Inbox
Set subFolder = myInbox.folders("Folder One")
For i = resItems.count To 1 Step -1
Set myItem = resItems(i)
With myItem
'// Mark As Read
.UnRead = False
'// Move Mail Item to sub Folder
.Move subFolder
End With
' If there is a memory error,
' release item when no longer necessary,
'Set myItem = Nothing
Next
End If
'// Email_Two
Set myItems = myInbox.Items
strfilter = "[SenderEmailAddress] = 'Email_Two#email.com'"
Debug.Print strfilter
Set resItems = myItems.Restrict(strfilter)
Debug.Print resItems.count
If resItems.count > 0 Then
'// Set SubFolder of Inbox
Set subFolder = myInbox.folders("Folder Two")
For i = resItems.count To 1 Step -1
Set myItem = resItems(i)
With myItem
' // Mark As Read
.UnRead = False
' // Move Mail Item to sub Folder
.Move subFolder
End With
' If there is a memory error,
' release item when no longer necessary,
'Set myItem = Nothing
Next
End If
MsgErr_Exit:
Exit Sub
'// Error information for users to advise the developer
MsgErr:
MsgBox "An unexpected Error has occurred." _
& vbCrLf & "Error Number: " & err.Number _
& vbCrLf & "Error Description: " & err.description _
, vbCritical, "Error!"
Resume MsgErr_Exit
End Sub

How to move messages from a specific account?

I have multiple accounts attached to Outlook 2010.
I want to move messages from a specific account, older than X days, to a .pst file for local storage.
I found scripts to move messages from the default inbox, but nothing on specifying an account.
I know you can specify an account when sending email using
Set OutMail.SendUsingAccount = Outlook.Application.Session.Accounts.Item(2)
but I can't find anything for looking into another account.
I've found the stores references for the folders (\Inbox and \Sent) and I know how to specify the days old. I have a script that works, but only in my primary account.
After some more searching and testing I came up with the following solution. This was actually from a 2009 post on stackoverflow here: Original VBA
It uses a public function to build the folder locations and a Subroutine to look for received dates older than 60 days and move those files to the specified locations.
The public function is:
Public Function GetFolder(strFolderPath As String) As MAPIFolder
Dim objNS As NameSpace
Dim colFolders As folders
Dim objFolder As MAPIFolder
Dim arrFolders() As String
Dim i As Long
On Error GoTo TrapError
strFolderPath = Replace(strFolderPath, "/", "\")
arrFolders() = Split(strFolderPath, "\")
Set objNS = GetNamespace("MAPI")
On Error Resume Next
Set objFolder = objNS.folders.Item(arrFolders(0))
If Not objFolder Is Nothing Then
For i = 1 To UBound(arrFolders)
Set colFolders = objFolder.folders
Set objFolder = Nothing
Set objFolder = colFolders.Item(arrFolders(i))
If objFolder Is Nothing Then
Exit For
End If
Next
End If
On Error GoTo TrapError
Set GetFolder = objFolder
Set colFolders = Nothing
Set objNS = Nothing
Exit_Proc:
Exit Function
TrapError:
MsgBox Err.Number & " " & Err.Description
End Function
The subroutine that does the actual work is below.
I added the Pass as Integer to allow the routine to work through two different source and destination folders. If I change the Sub name to Application_Startup it will run whenever outlook is started.
PST Folder Name\Archive-Inbox - PST folder name in Outlook with sub-folder
Email Account Name\Inbox - Account name in Outlook with sub-folder
Sub MoveOldEmail()
Dim oItem As MailItem
Dim objMoveFolder As MAPIFolder
Dim objInboxFolder As MAPIFolder
Dim i As Integer
Dim Pass As Integer
For Pass = 1 To 2
If Pass = 1 Then
Set objMoveFolder = GetFolder("PST Folder Name\Archive-Inbox")
Set objInboxFolder = GetFolder("Email Account Name\Inbox")
ElseIf Pass = 2 Then
Set objMoveFolder = GetFolder("PST Folder Name\Archive-Sent Items")
Set objInboxFolder = GetFolder("Email Account Name\Sent Items")
End If
For i = objInboxFolder.Items.Count - 1 To 0 Step -1
With objInboxFolder.Items(i)
''Error 438 is returned when .receivedtime is not supported
On Error Resume Next
If .ReceivedTime < DateAdd("d", -60, Now) Then
If Err.Number = 0 Then
.Move objMoveFolder
Else
Err.Clear
End If
End If
End With
Next
Next Pass
Set objMoveFolder = Nothing
Set objInboxFolder = Nothing
End Sub
Hope this helps someone else.

Using Microsoft Access, need to pull attachments from Outlook emails of a different account

I have working code below to extract attachments from my own Outlook email account, however I need to do it for a different account that is setup as a default reply email box for an automated process.
I'm not entirely sure how to tell the code below to check for that mailbox instead of my own. I've tried different variations of setting the Inbox variable, but none of them have worked thus far. This is done within Access 2013.
Private Sub GetAttachments()
Dim ns As Namespace
Dim Inbox As Outlook.MAPIFolder
Dim Item As Object
Dim Atmt As Outlook.Attachment
Dim FileName As String
Set ns = GetNamespace("MAPI")
Set Inbox = ns.GetDefaultFolder(olFolderInbox)
If Inbox.Items.Count = 0 Then
MsgBox "There are no messages in the Inbox.", vbInformation, _
"Nothing Found"
Exit Sub
End If
For Each Item In Inbox.Items
For Each Atmt In Item.Attachments
If Atmt.Type = 1 And InStr(Atmt, "xlsx") > 0 Then
FileName = "C:\attachments\" & Atmt.FileName
Atmt.SaveAsFile FileName
End If
Next Atmt
Next Item
End Sub
Try this:
Set Inbox = ns.Folders("MailboxName").Folders("Inbox")
Use Namespace.GetSharedDefaultFolder
Set recip = ns.CreateRecipient("other mailbox owner")
recip.Resolve
Set Inbox = ns.GetSharedDefaultFolder(recip, olFolderInbox)

Run-time error 13 randomly while iterating emails in a public folder

I receive a random run-time error 13 (type mismatch). This routine works most of the time. The folder passed as an argument is legitimate at the time of the failure.
From what I can see in the debugger, objitem is missing some of the fields during runtime. After it break-points in the debugger, I can immediately single-step (re-executing the offending line) and there is no error.
I attempted using on error goto to sleep then retry various lines, and the error persists until it stops in the debugger.
I also attempted changing between the For ii and For Each forms of the loop commands.
I also temporarily disabled by anti-virus.
I iterate over a large number of public folders. My Outlook client is 2003 running under XP, and I am attached to Exchange Server version 7654.
Sub SearchFolders(objFolder As Outlook.MAPIFolder)
Dim objFolders As Outlook.Folders
Dim subFolder As Outlook.MAPIFolder
Dim objitem As MailItem
Dim ii As Integer
' Recurse through all subfolders
Set objFolders = objFolder.Folders
For Each subFolder In objFolders
Call SearchFolders(subFolder)
Next subFolder
' Search the emails
For ii = 1 To objFolder.Items.Count
If objFolder.Items(ii).Class = olMail Then
If TypeName(objFolder.Items(ii)) <> "MailItem" Then
MsgBox ("Type mismatch: object s/b MailItem and is " & TypeName(objFolder.Items(ii)))
GoTo NextdblLoop
End If
Set objitem = objFolder.Items(ii)
CheckEmailForErrorReports (objFolder.Items(ii))
End If
NextdblLoop:
Next ii
End Sub
Code below is modified per #dmitry suggestions and now works.
Sub SearchFolders(objFolder As Outlook.MAPIFolder)
Dim objFolders As Outlook.Folders
Dim subFolder As Outlook.MAPIFolder
Dim Objitem As Outlook.MailItem
Dim ii As Integer
Dim ThisItem As Object
Dim Items As Outlook.Items
' Recurse through all subfolders
Set objFolders = objFolder.Folders
For Each subFolder In objFolders
Call SearchFolders(subFolder)
Next subFolder
' Search the emails
Set Items = objFolder.Items
For ii = 1 To Items.Count
Set ThisItem = Items.item(ii)
If ThisItem.Class = olMail Then
If VarType(ThisItem) = 9 Then GoTo NextdblLoop
Set Objitem = ThisItem
CheckEmailForErrorReports (objFolder.Items(ii))
Set Objitem = Nothing
End If
Set ThisItem = Nothing
NextdblLoop:
Next ii
Set Items = Nothing
End Sub
Firstly, do not use multiple dot notation; cache the Items collection before entering the loop.
Secondly, release the variables as soon as you are done with them
dim item As Object
dim Items as Outlook.Items
set Items = objFolder.Items
For ii = 1 To Items.Count
set item = Items.Item(ii)
If item.Class = olMail Then
If TypeName(item) <> "MailItem" Then
'THIS CAN NEVER HAPPEN. The check above is sufficient
MsgBox ("Type mismatch: object s/b MailItem and is " & TypeName(item))
GoTo NextdblLoop
End If
Set objitem = item
CheckEmailForErrorReports (objitem)
Set objitem = Nothing
End If
Set item = Nothing
NextdblLoop:
Next ii
End Sub

How can I create a script to move the currently active email in the Inbox to another folder in Outlook 2007

I sometimes get emails that I want to keep but to move them into the appropriate folder can be a pain. How can I execute a script that will move (like using C-S-v) the email I'm looking at into a certain folder called "buffer", for instance?
I'm using Outlook 2007.
thanks.
EDIT:
there isn't any criteria that can be created to automate this process like through a rule. it is merely a judgment call I make as i'm staring at it.
This code may work better.
In your code, objFolder may be equal to Nothing, yet you continue the procedure. Also, the For Each loop assumes that each item is a mail item.
Sub MoveSelectedMessagesToFolder()
Dim objNS As Outlook.NameSpace
Dim objFolder As Outlook.MAPIFolder
Dim obj As Object
Dim msg As Outlook.mailItem
Set objNS = Application.GetNamespace("MAPI")
On Error Resume Next
Set objFolder = objNS.Folders.item("Personal Folders").Folders.item("Buffer")
On Error GoTo 0
If objFolder Is Nothing Then
MsgBox "This folder doesn't exist!", vbOKOnly + vbExclamation, "INVALID FOLDER"
Exit Sub
End If
For Each obj In ActiveExplorer.Selection
If TypeName(obj) = "MailItem" Then
Set msg = obj
msg.Move objFolder
End If
Next obj
End Sub
Here's the code I'm using.
Sub MoveSelectedMessagesToFolder()
'Originally written by Chewy Chong
'Taken from http://verychewy.com/archive/2006/04/12/outlook-macro-to-move-an-email-to-folder.aspx
'Thanks Chewy!
'Ken
On Error Resume Next
Dim objFolder As Outlook.MAPIFolder, objInbox As Outlook.MAPIFolder
Dim objNS As Outlook.NameSpace, objItem As Outlook.MailItem
Set objNS = Application.GetNamespace("MAPI")
Set objInbox = objNS.GetDefaultFolder(olFolderInbox)
'For the "Item" portion, I used the name of the folder exactly as it appear in the ToolTip when I hover over it.
Set objFolder = objNS.Folders.Item("Personal Folders").Folders.Item("Buffer")
'Assume this is a mail folder
If objFolder Is Nothing Then
MsgBox "This folder doesn't exist!", vbOKOnly + vbExclamation, "INVALID FOLDER"
End If
If Application.ActiveExplorer.Selection.Count = 0 Then
'Require that this procedure be called only when a message is selected
Exit Sub
End If
For Each objItem In Application.ActiveExplorer.Selection
If objFolder.DefaultItemType = olMailItem Then
If objItem.Class = olMail Then
objItem.Move objFolder
End If
End If
Next
Tools -> Rules & Alerts
Then Create a new rule telling all mail that fit whatever criteria to be deleted/marked as read/moved to a folder/any combination of those.
Edit:
If you don't want a rule/can't make a rule that fits, you can create a Macro (Tools -> Macro) to move it to a folder, then bind it to a shortcut.