There are two ways you can use a VBA macro to save all messages to a hard drive: use an ItemAdd macro to watch for new messages in your Inbox or use a Run a Script rule.
If you want to save every message, it's generally better to use the ItemAdd macro since it can handle a larger volume of messages. The Run a Script rule is great for low volume accounts or when you only need to save some messages.
If you need to save messages already downloaded, see Save selected email message as .msg file. It's essentially the same macro, but works with selected messages.
Using ItemAdd
This ItemAdd macro is a simpler version of the macro at E-Mail: Save new items immediately as files. My version saves all messages in the user's profile path, in the native Outlook .msg format.
If you need to watch a folder other than your Inbox, see Working with VBA and non-default Outlook Folders.
To use this code, paste it in the ThisOutlookSession module. To test this code sample without restarting Outlook, click in the Application_Startup procedure then click Run.
Option Explicit
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Dim Ns As Outlook.NameSpace
Set Ns = Application.GetNamespace("MAPI")
Set Items = Ns.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub Items_ItemAdd(ByVal Item As Object)
If TypeOf Item Is Outlook.MailItem Then
Dim sPath As String
Dim dtDate As Date
Dim sName As String
Dim enviro As String
enviro = CStr(Environ("USERPROFILE"))
sName = Item.Subject
ReplaceCharsForFileName sName, "_"
dtDate = Item.ReceivedTime
sName = Format(dtDate, "yyyymmdd", vbUseSystemDayOfWeek, _
vbUseSystem) & Format(dtDate, "-hhnnss", _
vbUseSystemDayOfWeek, vbUseSystem) & "-" & sName & ".msg"
' use My Documents for older Windows.
sPath = enviro & "\Documents\"
Debug.Print sPath & sName
Item.SaveAs sPath & sName, olMSG
End If
End Sub
Private Sub ReplaceCharsForFileName(sName As String, _
sChr As String _
)
sName = Replace(sName, "/", sChr)
sName = Replace(sName, "\", sChr)
sName = Replace(sName, ":", sChr)
sName = Replace(sName, "?", sChr)
sName = Replace(sName, Chr(34), sChr)
sName = Replace(sName, "<", sChr)
sName = Replace(sName, ">", sChr)
sName = Replace(sName, "|", sChr)
End Sub
Run a Script Rule
This version of the macro is a "Run a Script" macro and used in a Rule. When a message arrives meeting the conditions in the rule, the script runs and saves the message. You can create a rule containing no conditions, if you want it to use the script on all messages.
For best results, all Rule Actions need to be in the script. The rule should contain only conditions.
Public Sub SaveMsg(Item As Outlook.MailItem)
Dim sPath As String
Dim dtDate As Date
Dim sName As String
Dim enviro As String
enviro = CStr(Environ("USERPROFILE"))
sName = Item.Subject
ReplaceCharsForFileName sName, "_"
dtDate = Item.ReceivedTime
sName = Format(dtDate, "yyyymmdd", vbUseSystemDayOfWeek, _
vbUseSystem) & Format(dtDate, "-hhnnss", _
vbUseSystemDayOfWeek, vbUseSystem) & "-" & sName & ".msg"
' use My Documents in older Windows.
sPath = enviro & "\Documents\"
Debug.Print sPath & sName
Item.SaveAs sPath & sName, olMsg
End Sub
Private Sub ReplaceCharsForFileName(sName As String, _
sChr As String _
)
sName = Replace(sName, "/", sChr)
sName = Replace(sName, "\", sChr)
sName = Replace(sName, ":", sChr)
sName = Replace(sName, "?", sChr)
sName = Replace(sName, Chr(34), sChr)
sName = Replace(sName, "<", sChr)
sName = Replace(sName, ">", sChr)
sName = Replace(sName, "|", sChr)
End Sub
How to use macros
First: You will need macro security set to low during testing.
To check your macro security in Outlook 2010 or 2013, go to File, Options, Trust Center and open Trust Center Settings, and change the Macro Settings. In Outlook 2007 and older, it’s at Tools, Macro Security.
After you test the macro and see that it works, you can either leave macro security set to low or sign the macro.
Open the VBA Editor by pressing Alt+F11 on your keyboard.
To put the code in a module:
- Right click on Project1 and choose Insert > Module
- Copy and paste the macro into the new module.
More information as well as screenshots are at How to use the VBA Editor
Aumor says
LOVE IT. Thank you!
After saving the msg file, before closing the macro, i would further like to use "Microsoft
Print to PDF" to print the created msg file as .pdf with the same 'sname' & 'spath'.
As i understand outlook does not have a macro recorder.
All my efforts did not get any result
I would greatly appreciate any guidance you would be willing to provide.
Anticipated THANKS
Michael says
Diane,
I have added several different email addresses to Outlook. How could I modify this code to so that instead of saving messages arriving to the default inbox, it saves messages arriving to a different email address that is not the default?
ahmed says
thank you a lot you saved my day, can i ask for one more thing i need to move the mail item after saving to archive folder but i failed to do so if you can help that will be fantastic.
Brian says
I have attempted to use the ItemAdd in ThisSessionOutlook, but I keep getting an this error message:
"Run-time Error '-2147287037 (80030003)':
The operation failed."
When I select to debug, it highlights this line:
Item.SaveAs sPath & sName, olMsg
Any ideas why I am getting this message?
Thanks,
Brian
vojtech says
Hi, Thank you for your macro. How to modify the script if I want to have the sender name in the Exported msg ?
Date-Time-Sender-Subject.msg ?
Thank you
Evan Garcia says
Love this!! Think its almost exactly what I need! Thank you!
However, I'm getting a bug when I run it.
Zachary says
I am trying to put the vba as suggested above but save the message in a newly created folder which also has the same name:
strNewFolderName = Format(dtDate - 1, "yyyymmdd", vbUseSystemDayOfWeek, _
vbUseSystem)
I use Mkdir command to create a folder.
When I specify the path with this variable where I want to save the message the script is not working:
sPath = enviro & "\Documents\" & strNewFolderName
Debug.Print sPath & sName
Item.SaveAs sPath & sName, olMSG
Can you please help why it is not saving in a folder just created?
Thanks
Diane Poremsky says
Does the debug.print show the correct path? (Yo might need a & "\" at the end of sPath.
James says
How do I modify the code so it limits the number of characters in the email subject? I've been successful in moving emails to a SharePoint document library but get a runtime error when the subject is too long, which I think is due to the library column's character limit.
Diane Poremsky says
When you create the name use sname = left(sname, 10) where 10 is the number of letters.
James says
Prior to posting my question, I tried this on the different instances sName occurred throughout the code, but it didn't work. I guess I just missed the line where I should have defined it. It works now. Thanks for the prompt reply.
Diane Poremsky says
You aren't alone - a lot of people miss/forget to enter valid paths...
Brady says
hi Diane
I have added to the code and it works perfectly when i manually drag the email with attachment to the specified folder but the itemAdd fails to be triggered when I use a rule to move the email into the specified folder.
Our group policy prevents us from using run a script in the rule.
Can you please help??
Many thanks
Diane Poremsky says
>> Our group policy prevents us from using run a script in the rule.
They let you use macros but not script rules? that doesn't make a lot of sense. :) If you use 2013 or 2016, it's possible run a script was disabled by an update - but gpo is probably blocking you from restoring that https://www.slipstick.com/outlook/rules/outlook-2016-run-a-script-rules/
itemadd should definitely be triggered when messages move - and as long the itemadd doesn't use an if statement to filter the mail, it should work from the rule.
Hmmm. is the rule a server side rule? it shouldn't matter, but i wonder if moving the message on the server is causing outlook/the macro to not deleted the added item.
Thomass says
Hello Diane,
I've implemented the "Using ItemAdd"-code to save new messages on disk, and it worked quite fine since last changes recommended below.
But now, I get runtime errors from time to time without any possibility to check why.
When I execute the procedure manually (I've implemented a button for that) on the mails causing the errors, it works fine.
Is there any possibility to find out what causes the runtime errors ?
Kind regards,
Mynz
Diane Poremsky says
What is the error code? Any idea which message is triggering the error?
Thomass says
Hi,
there is no errorcode, but I can determine which message it causes.
But as I said, when executing the same procedure on the same message manually direct after the error, it works.
Kind Regards,
Mynz
Thomass says
This is the "errormeassage"...
Diane Poremsky says
Typical useless outlook error message. :) Are you using ItemAdd macro or a rule? ItemAdd watches the folder and runs when a message arrives rather than having a rule tell it when to run.
Thomass says
I'm using a rule
With this error, the rule gets deactivated.
Diane Poremsky says
The macro with this name: Private Sub Items_ItemAdd(ByVal Item As Object) is not designed to work with rules.
This macro: Public Sub SaveMsg(Item As Outlook.MailItem) will work with rules.
the contents might be the same (or very similar) but you need to have the name formatted correctly.
Mynz says
Hello Diane,
I've implemented the "Using ItemAdd"-code to save new messages on disk.
But since I have several rules filtering my messages, no all are stored to disk. Those, moved by a rule, are not dected by the "Using ItemAdd"-solution.
Is there any possibility to run the script before the rules are executed ?
Kind regards,
Mynz
Diane Poremsky says
You'd need to use a rule to save them first, before the next rule runs (all saved messages would be in one folder, although you could file by email address or display name). The other option is to move them using the macro either before or after saving - this would work if everything gets moved to a folder that fits a pattern (such as their email address).
It's possible to watch more than one folder for new items (use stub macros to watch and send everything to one main macro to save), but it's not practical if you have more than maybe 5 folders to watch.
madhuri says
Hi,
I am unable to use the option run a script while editing the rule.
going to mange rules and alerts->run a script.. when I click on the script to select the script i am getting a blank window. Please help
Diane Poremsky says
Did you put the macro in a new module or in thisoutlooksession? it usually doesn't matter, but it should be in a module and sometimes if its in thisoutlooksession, it won't show up in the list of scripts.
Does the first line/macro name include (Item As Outlook.MailItem) ? While the word 'item' can be anything (as long as it matches what is used to reference the item in the rest of the code), you need to use that format.
Public Sub SaveMsg(Item As Outlook.MailItem)
Billy says
Diane,
I'm trying to use "Rules" to transfer my incoming "Inbox" to sent it to my hard drive "Inbox." I've tried several times and sought the help of our techs to no avail. Any help is appreciated.
Diane Poremsky says
you need to use a run a script rule (the second macro on this page) - standard rules won't work. if you want to save all messages, the itemadd macro will be more efficient.
Rob says
Hi Diane,
Great script! I have the script running monitoring a subfolder within my inbox. When I drag messages in there it automatically saves, assigns a category, and moves it back to the inbox. What I would like to do is have the script also monitor a second folder (this one a subfolder of my sent mail). I can get either the inbox script working, or the sent mail script working, but I am not sure how to expand the script to monitor two locations at once. I would greatly appreciate any guidance you would be willing to provide.
Thanks!
Rob
Diane Poremsky says
The Exact same script? The quick and dirty method:
You'll need to Add these lines:
Top:
Private WithEvents sItems As Outlook.Items
In App startup:
Set sItems = Ns.GetDefaultFolder(olFolderSentItems).Items
Copy the itemadd macro and rename it
Private Sub sItems_ItemAdd(ByVal Item As Object)
Alternately you could rename the itemadd macro to 'SaveandCategorize' and share it between the two:
Private Sub Items_ItemAdd(ByVal Item As Object)
If TypeOf Item Is Outlook.MailItem Then
SaveandCategorize item
End If
End Sub
Private Sub sItems_ItemAdd(ByVal Item As Object)
If TypeOf Item Is Outlook.MailItem Then
SaveandCategorize item
End If
End Sub
Private Sub SaveandCategorize (ByVal Item As Object)
' all the itemadd code but the if/end if
End Sub
Even if you are need to use a different category or a different folder, you assign the folder to a variable and pass it from the 'stub macro' so both can share the code.
Inderjeet says
I have list of reports in a excel files. Now, I want to add the sending time of these reports when send through outlook. I am trying but no luck. please help
Diane Poremsky says
Do you want to change the name of the file after you send it (attachment name doesn't change, but file name on hard drive is changed) or at the time you send it (so the attachment name has the sent time)?
Mark says
Hi Diane,
Is there a way to save to desktop/hard drive non-delivery reports? I tried the macro above and it works perfectly for email that are not NDR, however, I will also need to save those NDR on my desktop. Is this possible?
Thanks,
Mark
Diane Poremsky says
with the right coding, yes. if you don't care what is saved (meeting requests etc) you can remove the if /end if lines: If TypeOf Item Is Outlook.MailItem Then
or if you only want reports, try if typeof item is outlook.ReportItem then
NDRs are olReport class, so you could use if item.class = olreport then
karen says
Hello Diane and thank you for posting this code! Trying to send my incoming emails to a shared drive G:SharedOpen
I am getting an error I am unable to resolve:
Debug line: Item.SaveAs sPath & sName, olMSG
I am unsure if it is having trouble saving the file to the remote drive or if the file type is not saving. Any input is greatly appreciated!
Cheers!
Jonathan says
Hello Diane, just wanted to say we really appreciate all your time.
Question:
- Do you know how to prevent the macro from crashing when the file name is too long?
I'm ok if the macro skips it but i dont want it to stop and having to restart outlook.
Thank you.
Diane Poremsky says
try adding on error resume next to the macro. or trim the files names to the max length allowed.
Darren says
Diane,
Many thanks for this script and for all of the help you have given - I am really new to VBA, just learning by copying/pasting and trial and error. The rule works wonderfully and saves the messages in the folder I wanted them to go to.
In Windows Explorer it is possible to display other columns, such as "From", "From Address" and "Sender Name". Is it possible to populate one of these columns with the sender of the email when saving the email as a .msg file so I could then sort the .msg files by sender?
I would be most grateful for any help you can give with this.
Diane Poremsky says
Sorry it has taken me so long to get to this, I'm trying to get through a backlog of comments.
Those fields are picked up from the windows mail app - so I can't promise anything, but will look into the possibility of writing to windows properties when saving.
ashley says
I want to save these emails to a particular folder on my server. How do I edit the script to allow that?
Diane Poremsky says
use \\servername\folder as the path in the code.
ryan says
I wanted to save the emails to my computer, rather than on outlook. Based off of your code, when I try this code, I receive a run time error on the Item.SaveAs line.
Public Sub SaveMsg(Item As Outlook.MailItem)
Dim sPath As String
Dim dtDate As Date
Dim sName As String
sName = Item.Subject
ReplaceCharsForFileName sName, "_"
dtDate = Item.ReceivedTime
sName = Format(dtDate, "mmddyyyy", vbUseSystemDayOfWeek, vbUseSystem) & Format(dtDate, "-hhmmss", vbUseSystemDayOfWeek, vbUseSystem) & "-" & sName & ".msg"
sPath = "C:\IT Documents\" & Format(Date, "mmddyyyy") & "\"
Debug.Print sPath & sName
Item.SaveAs sPath & sName, olMSG
End Sub
Maybe I missed something. Thank you for your help and quick responsed
Diane Poremsky says
I'm guessing the problem is with this - does the date folder exist? If not, you need to use code to create it.
sPath = "C:\IT Documents\" & Format(Date, "mmddyyyy") & "\"
There is a code sample here - https://www.slipstick.com/developer/saving-messages-to-the-hard-drive-using-vba/ - update the variables to match your code.
dim fso as object
StrSaveFolder = Left(StrFolderPath, Len(StrFolderPath) - 1) & "\"
If Not FSO.FolderExists(StrFolderPath) Then
FSO.CreateFolder (StrFolderPath)
End If
ryan says
Hi, I am trying to create a macro that will save emails to a daily folder. Right now, I'm not sure if this is the ideal way of doing so, but I have a batch file that is run daily which will create a folder every day in the form (mm-dd-yyyy). My goal is to have all emails (including attachments) be saved to the daily folder. I came across your script and i believe that this helps pointing me in the right direction. I am unsure of how to run the script so that it scans the date of the email and save it to the folder that is created daily.
Thank you for your help
Diane Poremsky says
You want to save the messages to a folder in the windows file system or file them in Outlook?
If in the windows file system, you need to set the file path using something like this:
StrFolderPath = "C:\Email\" & Format(date,"yyyymmdd") & "\"
If Not FSO.FolderExists(StrFolderPath) Then
FSO.CreateFolder (StrFolderPath)
End If
then you can use the strfolderpath to direct the messages.
Mark says
Diane, Thank you so much for sharing this with us. I would be interested in removing all of the email formatting, keeping the email body as text only. I can save it this way manually, is there a way to do it in this script?
Thanks again.
Diane Poremsky says
Try the macro here - https://www.slipstick.com/developer/code-samples/save-email-message-text-file/
Brian says
Hello Diane,
Thanks for the great website! I'd like to make a modification to your code that will delete the message from the Outlook Inbox - only after it is saved to the hard drive successfully. Would there be an easy way to do that after the End If statement?
Thank you,
Brian
Diane Poremsky says
Try adding item.delete after the End if
Sumit j says
Hi, I want to save particular mails not all, what changes needs to be done here?
Diane Poremsky says
As they arrive or do you want to select the messages and run the macro? To do it as the messages arrive, you need to set up a rule and change the macro - this first line: Private Sub Items_ItemAdd(ByVal Item As Object) needs be changed to
Public Sub SaveMessages(Item As MailItem). You don't need the application_start macro.
James Shanks says
I am trying to use this code with a rule in outlook. I am trying to save email from a specific person and have it sent to P:\example. Where do I place this in the code???
Diane Poremsky says
Replace this: sPath = enviro & "\Documents\"
with sPath = "P:\Example\"
The person will be the condition in the rule.
santhosh says
Hi,
I believe the problem is in ItemAdd.When I use the below code to monitor only one Inbox it works fine.All the new incoming mails are saved in the shared drive.
Set Items = ns.Folders("Mailbox - ABC").Folders("Inbox").Items
When we need to monitor two different Group Mailboxes we create a Item2 in application startup and point item2 to a Group Mailbox.However the ItemAdd doesn't pickup the mails from Items2.Please advise how to create a ItemAdd for the Item2.
santhosh says
Hi,
I made the below changes in the code.
1) Added WithEvents for Items2 below Option Explicit
Option Explicit
Private WithEvents Items2 As Outlook.Items
2)Added the folder for Items2 in Private Sub Application_Startup()
Set Items2 = ns.Folders("Mailbox - ABC").Folders("Inbox").Items
3)Copied the itemadd macro and renamed it.
Private Sub Items_ItemAdd2(ByVal Items As Object)
But it didn't work.Please help
Diane Poremsky says
Oh, sorry about that. You need to use thegetfolderpath function instead.
Santhosh says
Hi,
How can we use the Itemadd code to save the incoming mails for two different Groupmailboxes?
Diane Poremsky says
In application startup macro, you need to set items2 and point to the other folder you are watching. Copy the itemadd macro, using items2. Withevents needs copied too.
santhosh says
Hi,
Thank you very much.It worked when I used ThisOutlookSession.I have a question, how to automatically save the new mails when the outlook is closed.
Diane Poremsky says
Sorry, outlook needs to be running for the macro to work.
santhosh says
Hi,
I opened the VBA editor and right clicked the project1 and inserted module and pasted the code.
Diane Poremsky says
Yeah, for the first sample, you need to use ThisOutlookSession. Application Start macros need to be there.
santhosh says
Hi,
Thanks for sharing the code.While using the Itemadd code the below line item is higlighted in red.Please advise.
Private WithEvents Items As Outlook.Items
Diane Poremsky says
Where did you put the code? Private WithEvents Items As Outlook.Items needs to be at the top of the ThisOutlookSession module.