Save all Outlook mailitems to disk with VBA - vba

I have some experience with VBA in Excel, but taking my first steps in Outlook. I need to save all e-mail messages in a designated Outlook folder (Inbox\input) to disk (D:\myArchive\Email\) as .msg files and move mail item to archive folder in Outlook (Inbox\archive).
I have set up a mail rule in Outlook that moves mail to archive folder and runs a script below which actually does what I need. The problem is that I get mail rule error pop-ups occasionally and I struggle to track down the reason. Hence looking to turn away from Outlook mail rule and cycle through all folder contents "on-demand".
How could I convert it to cycle through Outlook folder as well as displace the mail item? Currently running Outlook 2019. Thanks!
edit: sorry, late clarification - target folder is in another mailbox (Office 365 shared mailbox). How to target a different account?
Public Sub saveEmailtoDisk(itm As Outlook.MailItem)
Dim saveFolder, msgName1, msgName2 As String
saveFolder = "D:\myArchive\Email\"
msgName1 = Replace(itm.Subject, ":", "")
msgName2 = Replace(msgName1, "/", "_")
itm.SaveAs saveFolder & msgName2 & ".msg", olMSG
End Sub

The following code assumes that both the input and archive folders are located within the default inbox.
Public Sub saveAndArchiveInputEmails()
Dim saveFolder As String
saveFolder = "D:\myArchive\Email\"
Dim sourceFolder As Folder
Dim destFolder As Folder
With Application.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
Set sourceFolder = .Folders("input")
Set destFolder = .Folders("archive")
End With
Dim itm As Object
Dim i As Long
With sourceFolder
For i = .Items.Count To 1 Step -1
Set itm = .Items(i)
If TypeName(itm) = "MailItem" Then
saveEmailtoDisk saveFolder, itm
itm.Move destFolder
End If
Next i
End With
End Sub
Public Sub saveEmailtoDisk(ByRef saveFolder As String, ByVal itm As Object)
Dim msgName1, msgName2 As String
msgName1 = Replace(itm.Subject, ":", "")
msgName2 = Replace(msgName1, "/", "_")
itm.SaveAs saveFolder & msgName2 & ".msg", olMSG
End Sub
EDIT
For a shared mailbox, try the following instead...
With Application.GetNamespace("MAPI")
Dim sharedEmail As Recipient
Set sharedEmail = .CreateRecipient("someone#abc.com")
Dim sourceFolder As Folder
Set sourceFolder = .GetSharedDefaultFolder(sharedEmail, olFolderInbox).Folders("input")
Dim destFolder As Folder
Set destFolder = .GetSharedDefaultFolder(sharedEmail, olFolderInbox).Folders("archive")
End With
For your default inbox...
Dim myInbox As Folder
Set myInbox = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)

Related

How to access shared subfolder in Outlook with VBA

I have some VBA which sometimes fails on the line Set Folder = Inbox.Folders("xxx") with the below error message.
Folder = Inbox.Folders is returning 0 folders even though I can see them in the the Outlook pane.
Run-time error '-2147221233 (8004010f)'
The attempted operation failed. An object could not be found.
Sub Export()
Select_Date.Show
'Dim OutlookApp As Outlook.Application
'Dim OutlookNamespace As NameSpace
'Dim Folder As MAPIFolder
'Dim OutlookMail As Variant
'Dim i As Integer
'Dim olNS As Outlook.NameSpace
'Dim Start_Date As Date
'Dim saveFolder As String
Start_Date = DateValue(Select_Date.ComboBox1.Value & " " & Select_Date.ComboBox2.Value & " " & Select_Date.ComboBox3.Value)
overwrite_flag = Select_Date.CheckBox1.Value
saveFolder = "K:\xxxx "
Set OutlookApp = New Outlook.Application
Set OutlookNamespace = OutlookApp.GetNamespace("MAPI")
Set objowner = OutlookNamespace.CreateRecipient("xx.xxx#xxx.com")
objowner.Resolve
Set Inbox = OutlookNamespace.GetSharedDefaultFolder(objowner, olFolderInbox)
'Set Folder_test = Inbox.Folders.GetLast
Set Folder = Inbox.Folders("xxx")
'Set Folder = Session.Folders("xxx").Folders("Inbox")
i = 1
For Each OutlookMail In Folder.Items
If TypeOf OutlookMail Is MailItem Then
If OutlookMail.ReceivedTime >= Start_Date And OutlookMail.Subject = "xxxx" Then
For Each attach In OutlookMail.Attachments
savename = saveFolder & Format(DateAdd("d", 35, OutlookMail.ReceivedTime), "yyyymmdd") & ".csv"
If (Dir$(savename) <> "") Then
If overwrite_flag = True Then
Kill savename ' delete if file exists
attach.SaveAsFile savename
End If
Else: attach.SaveAsFile savename
End If
attach.SaveAsFile savename
Next attach
End If
End If
i = i + 1
Next OutlookMail
Set Folder = Nothing
Set OutlookNamespace = Nothing
Set OutlookApp = Nothing
End Sub
You may try to iterate over all subfolders and find the required one by checking its name.
Also I've noticed that your code iterates over all items in the folder which is not really a good idea! Use the Find/FindNext or Restrict methods of the Items class. 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
The error is MAPI_E_NOT_FOUND, which means either the folder with that name does not exist, or that you have no access to its subfolders.
Also note that if shared folders are set to be cached, Outlook only caches the folder itself, but not its subfolders, hence you won't be able to access any subfolders.

Save email attachment based on email subject

Ever day at 12 am there is an automatic email with an excel attachment from a vendor service with a specific subject. I am using rules and code to attempt to save the attachment and insert the information into a database I have created upon being received in the inbox.
I have tried code that I have found online however I don't know if doesn't work because of some network/ security setting my company has or if its he code it self.
Rule:
CODE:
Public Sub CribMaster2Database(itm As Outlook.MailItem)
Dim objAtt As Outlook.Attachment
Dim saveFolder As String
saveFolder = "c:\temp\"
If olItem.Subject = "Test" Then
For Each objAtt In itm.Attachments
objAtt.SaveAsFile saveFolder & "\" & objAtt.DisplayName
Set objAtt = Nothing
Next
End If
End Sub
Add code to the ThisOutlookSession to watch your folder for arrivals.
CribMaster_ItemAdd fires whenever something arrives in your watched folder.
At the very top of the module:
Dim WithEvents CribMaster As Items
Const SAVE_PATH As String = "c:\temp\"
Private Sub Application_Startup()
Dim ns As Outlook.NameSpace
Set ns = GetNamespace("MAPI")
'Change `holi4683` to the name of your account
'(should be visible just above your inbox).
Set CribMaster = ns.Folders.Item("holi4683") _
.Folders.Item("Inbox").Items
End Sub
Sub CribMaster_ItemAdd(ByVal Item As Object)
Dim olAtt As Attachment
Dim i As Integer
With Item
For i = 1 To .Attachments.Count
Set olAtt = .Attachments(i)
olAtt.SaveAsFile SAVE_PATH & olAtt.DisplayName
.UnRead = False
DoEvents
Next i
End With
Set olAtt = Nothing
End Sub
I'd usually use a rule to move the emails to a subfolder and watch that folder - means I don't have to worry about meeting invites, etc.
To do this you'd change your watched folder like this:
Set CribMaster = ns.Folders.Item("holi4683") _
.Folders.Item("Inbox") _
.Folders.Item("SubFolder").Items
Restart Outlook for the code to work, or manually run the Application_Startup() procedure.

Automatically saving outlook attachments based on title

I am looking to set up a one drive folder that will hold reports for our companies various clients. Our reporting software only sends to email rather than saving to file so I've googled and found this piece of code to automatically download all attachments to a folder
Public Sub saveAttachtoDisk(itm As Outlook.MailItem)
Dim objAtt As Outlook.Attachment
Dim saveFolder As String
Dim dateFormat
dateFormat = Format(Now, "yyyy-mm-dd H-mm")
saveFolder = "C:\Report Attachments\"
For Each objAtt In itm.Attachments
objAtt.SaveAsFile saveFolder & "\" & dateFormat & objAtt.DisplayName
Set objAtt = Nothing
Next
End Sub
The issue is that I want to split the reports by company. for example, I want reports for Company A to go to
C:\Report Attachments\Company A
and reports for company B to go to
C:\Report Attachments\Company B
and so on. Each report should have the companies name in the title of attachment so I'm looking for a tweak to the code to change the save location based on the attachment title. Is this possible?
Set up a rule to move the emails to specific folders when they arrive (probably rule based on the email address domain).
In the ThisOutlookSession module in Outlook enter this code in the declarations section:
Dim WithEvents CompanyA As Items
Dim WithEvents CompanyB As Items
Const COMPA_PATH As String = "C:\Report Attachments\Company A\"
Const COMPB_PATH As String = "C:\Report Attachments\Company B\"
Private Sub Application_Startup()
Dim ns As Outlook.NameSpace
Set ns = Application.GetNamespace("MAPI")
Set CompanyA = ns.Folders.item("Mailbox - tomdemaine") _
.Folders.item("Inbox") _
.Folders.item("CompanyA").Items
Set CompanyB = ns.Folders.item("Mailbox - tomdemaine") _
.Folders.item("Inbox") _
.Folders.item("CompanyA").Items
End Sub
Sub CompanyA_ItemAdd(ByVal item As Object)
Dim oAtt As Attachment
If item.Attachments.Count > 0 Then
For Each oAtt In item.Attachments
item.UnRead = False
'Note DisplayName may contain illegal characters.
oAtt.SaveAsFile COMPA_PATH & oAtt.DisplayName
DoEvents
Next oAtt
End If
Set oAtt = Nothing
End Sub
Sub CompanyB_ItemAdd(ByVal item As Object)
Dim oAtt As Attachment
If item.Attachments.Count > 0 Then
For Each oAtt In item.Attachments
item.UnRead = False
'Note DisplayName may contain illegal characters.
oAtt.SaveAsFile COMPB_PATH & oAtt.DisplayName
DoEvents
Next oAtt
End If
Set oAtt = Nothing
End Sub
The code will start watching your CompanyA & CompanyB folders when you start Outlook. Any time something gets moved there that contains attachments it will save them to your file location and mark the email as read.
I haven't tested the code - and the Outlook folders and file locations will need updating to suit your needs.

Reference messages and access attachments

I am writing a program to track the current status of projects.
The users would like to save relevant documents to the current project. I can do this for files that are residing in a folder with FileSaveDialog. However, many times the file is an e-mail message or an attachment to a message. I would like to grab this directly from Outlook and either save the message as an MSG or save the attachment.
I have code like below to reference Outlook messages from VB.NET but I can't figure out how to reference an entire message to save as msg or attachment filename.
Dim objOutlook As Outlook._Application
objOutlook = New Outlook.Application()
Dim objSelection As Outlook.Selection = objOutlook.ActiveExplorer.Selection
Dim iCount As Int16 = objSelection.Count
For i = iCount To 1 Step -1
Console.WriteLine(objSelection.Item(i).Subject)
Console.WriteLine(objSelection.Item(i).Attachments)
Next
Use the Outlook Object Library for this.
An example on how to download an attachment from an unread mail:
Private Sub ThisAddIn_NewMail() Handles Application.NewMail
Dim inBox As Outlook.MAPIFolder = Me.Application.ActiveExplorer() _
.Session.GetDefaultFolder(Outlook. _
OlDefaultFolders.olFolderInbox)
Dim inBoxItems As Outlook.Items = inBox.Items
Dim newEmail As Outlook.MailItem
inBoxItems = inBoxItems.Restrict("[Unread] = true")
Try
For Each collectionItem As Object In inBoxItems
newEmail = TryCast(collectionItem, Outlook.MailItem)
If newEmail IsNot Nothing Then
If newEmail.Attachments.Count > 0 Then
For i As Integer = 1 To newEmail.Attachments.Count
Dim saveAttachment As Outlook.Attachment = _
newEmail.Attachments(i)
newEmail.Attachments(i).SaveAsFile _
("C:\TestFileSave\" & (newEmail _
.Attachments(i).FileName))
Next i
End If
End If
Next collectionItem
Catch ex As Exception
If Left(ex.Message, 11) = "Cannot save" Then
MsgBox("Create Folder C:\TestFileSave")
End If
End Try
End Sub
Good luck!
Source: msdn
Having the same problem as you on saving an e-mail message I ended up with the following solution:
Sub SaveEmail()
'Save e-mail from Outlook
Dim objOL As Outlook.Application
Dim objMsg As Outlook.MailItem 'Object
Dim objSelection As Outlook.Selection
Dim strFile As String
'Instantiate an Outlook Application object.
objOL = CreateObject("Outlook.Application")
'Get the collection of selected objects.
objSelection = objOL.ActiveExplorer.Selection
'Set the target folder
Dim FilePath1 as String
FilePath1 = "C:\tmp\"
'Save each selected e-mail to disk
For Each objMsg In objSelection
'Save attachment before deleting from item.
'Get the file name using "objMsg.Subject" and remove special characters.
strFile = Regex.Replace(objMsg.Subject, "[^a-zA-Z0-9_ -]", "-",_
RegexOptions.Compiled)
'Combine with the path to the Temp folder.
strFile = FilePath1 & strFile & ".msg"
'Save the attachment as a file.
objMsg.SaveAs(strFile, Outlook.OlSaveAsType.olMSG)
Next
End Sub
For a bit of input on the regex.replace function please see the following links:
https://www.regular-expressions.info/charclass.html
https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex.replace?view=netframework-4.7.2#System_Text_RegularExpressions_Regex_Replace_System_String_System_String_System_String_

Save outlook attachments and rename/append files with identifier from subject line

Im really new to VBA and need some help. I'm trying to write a VBA script (along with a Outlook rule) to automatically download attachments from daily emails and append the file names with the date that appears in the subject.
This is what the subject line looks like - "Email Alert for Department for 10/20/2014". I just need to isolate the rightmost 10 spaces that indicates the run date of the files.
So I found code online that works to automatically download the attachments and append by current date which does work. See below.
Public Sub saveAttachtoDisk(itm As Outlook.MailItem)
Dim objAtt As Outlook.Attachment
Dim saveFolder As String
Dim dateFormat
dateFormat = Format(Now, "yyyymmdd ")
saveFolder = "Z:\Daily Emails"
For Each objAtt In itm.Attachments
objAtt.SaveAsFile saveFolder & "\" & dateFormat & objAtt.DisplayName
Set objAtt = Nothing
Next
End Sub
I also found online that something like this should point to the date (formatted like XX/XX/XXXX and always at the end of the subject line.
Subject = Right(itm.Subject, 10) but im having trouble incorporating it into the code above.
Can anyone help me? It would mean a lot
Thanks!
-Christina
Using Rules to run a macro is good.
I used the same set up before. The problem is if you are to work on the newly received mail, the sub wouldn't trap it. If you need to save the attachment of an incoming email with Email Alert for Department for mm/dd/yyyy as subject, try using an event instead. By default, Outlook doesn't provide Items Event so you'll have to create it.
In your ThisOutlookSession (not in a module) try something like:
Option Explicit
Private WithEvents olIBoxItem As Outlook.Items
Private Sub Application_Startup()
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Dim olFolder As Outlook.MAPIFolder
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set olFolder = objNS.GetDefaultFolder(olFolderInbox)
'~~> change olFolder depending on what folder you're receiving the email
'~~> I assumed it is Outlook's default folder Inbox
Set olIBoxItem = olFolder.Items
End Sub
Private Sub olIBoxItem_ItemAdd(ByVal Item As Object)
Const strSub As String = "Email Alert for Department for "
If TypeOf Item Is Outlook.MailItem Then
Dim nMail As Outlook.MailItem
Set nMail = Item
If InStr(nMail.Subject, strSub) <> 0 Then
Const savefolder As String = "Z:\Details Mail\"
'~~> Extract your date
Dim dateSub As String: dateSub = Right(nMail.Subject, 10)
'~~> Make sure there is an attachment
If nMail.Attachments.Count > 0 Then
Dim olAtt As Outlook.Attachment
Set olAtt = nMail.Attachments.Item(1) '~~> if you only have 1
Dim attFName As String, addFExt As String
'~~> Get the filename and extension separately
attFName = Split(olAtt.Filename, ".")(0)
attFExt = Split(olAtt.Filename, ".")(1)
'~~> Reconstruct the filename
attFName = savefolder & attFName & " " & dateSub & attFExt
'~~> Save the attachment
olAtt.SaveAsFile attFName
End If
End If
End If
End Sub
So above routine automatically checks any received mail in the Inbox Folder. If the subject contains the specified string. If yes, it automatically saves the attachment.
If however you have more than one attachment, you'll have to look through it and then save each one. It may look confusing at first but you'll get the hang of it for sure. HTH.