VBA Outlook Email Attachment Save Permissions - excel

This script is suppose to loop through my outlook folder inbox-> Work Request
Then for each mail item in that folder download each attachment and save if to a file location.
Code seems to work fine in finding the folder and the correct emails however it is giving me an error message on the following line of code saying "Run-Time Error '-2147024891(80070005) Cannot save the attachment. You don't have the appropriate permissions to perform this operation."
I have tried multiple save location including our external cloud drive and my personal desktop. Currently the code is saving to my desktop and still says I do not have appropriate permissions to save. Any help would be greatly appreciated.
olAtt.SaveAsFile ("C:\Users\John Smith\Desktop\WOR Email Download")
The rest of the script is depicted below.
Option Explicit
Sub Download_Outlook_Attachemtns()
Dim olApp As Outlook.Application
Dim olNS As Outlook.Namespace
Dim olFolder As Outlook.MAPIFolder
Dim olItem As Object
Dim MailItem As Outlook.MailItem
Dim olAtt As Outlook.Attachment
Dim FileLocation As String
Set olApp = New Outlook.Application
Set olNS = olApp.GetNamespace("MAPI")
'single folder link to hidden sheet folders([admin].[Mailbox].text)
Set olFolder = olNS.Folders("JohnSmith#work.com")
Set olFolder = olFolder.Folders("Inbox")
Set olFolder = olFolder.Folders("Work Requests")
For Each olItem In olFolder.Items
If olItem.Class = olMail Then
Set MailItem = olItem
'Debug.Print MailItem.Subject
For Each olAtt In MailItem.Attachments
If MailItem.ReceivedTime > ThisWorkbook.Worksheets("Email_Info").Range("C6").Value Then
olAtt.SaveAsFile ("C:\Users\John Smith\Desktop\WOR Email Download")
'olAtt.SaveAs Filename:=Application.GetSaveAsFilename
End If
Next olAtt
End If
Next olItem
'Set olFolder = Nothing
'Set olNS = Nothing
End Sub

First of all, instead of iterating over all items in the folder:
For Each olItem In olFolder.Items
If olItem.Class = olMail Then
Set MailItem = olItem
You can find all items with attachments in the folder and iterate over them only. The Find/FindNext or Restrict methods of the Items class do the magic. Read more about these methods in the articles I wrote for the technical blog:
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
For example, you can use the following search criteria (VBA syntax):
Filter = "#SQL=" & Chr(34) & "urn:schemas:httpmail:hasattachment" & Chr(34) & "=1"
Also it makes sense to optimize the code by changing the order of conditions:
For Each olAtt In MailItem.Attachments
If MailItem.ReceivedTime > ThisWorkbook.Worksheets("Email_Info").Range("C6").Value Then
Instead of checking the received time of the email for each attachment you can check it once for the email before iterating over attached files or, better yet, you may include another condition to the search criteria by using the logical AND operator in the search string.
Finally, you may try to check the Attachment.Type property value before trying to save anything to the disk. The property returns an OlAttachmentType constant indicating the type of the specified object.

You must include the file name besides the folder name. Currently, you are telling Outlook to save to a file conflicting with an existing folder name ("WOR Email Download"), hence the "no access" error - the file cannot be created since its name conflicts with an existing folder name. Change your code to
if olAtt.Type = olByValue Then
olAtt.SaveAsFile "C:\Users\John Smith\Desktop\WOR Email Download\" & olAtt.FileName
End If

Related

Shared inbox subfolder object not found error

The purpose of this code is to go through a shared folder in outlook and tell me the email subject and the received time and enter that into a spreadsheet. I made a version of this code that worked before on my own inbox, so I know that all of the excel parts work just fine. Once I knew that that worked I shifted it to the shared inbox (which is what I actually need) so I could tell where the errors are.
I've been fiddling with this for ages and from what I can tell this is getting hung up on the subfolders in this shared inbox. I've gone into the properties of the folder in outlook itself and it insists that these are the only folders, nonetheless, I've tried also having the name of the shared inbox and even the inbox itself as other combinations but none of that works. I have the reference libraries for outlook active, and like I said the code worked on my personal email.
The error that I'm getting is '-2147221233 (8004010f)' Attempted operation failed. An object could not be found. This is happening on line 17 (Set olFolder = olFolder.Folders("MY TEAM'S FOLDER").Folders("THE FOLDER I WANT")) where the folder is set.
The structure that I want is 'my department's shared email'> that email's inbox > my team's folder > the subfolder I want As far as I can see this should do that, but it won't. Seeing as this is the file path as far as I can tell, and the email is correct, and the code worked on my inbox before... I'm at a loss.
Sub GetFromOutlook()
Worksheets("Sheet1").Activate
Dim OutlookApp As Outlook.Application
Dim olNs As Outlook.Namespace
Dim Folder As Outlook.Folder
Dim OutlookMail As Variant
Dim i As Integer
Dim olFolder As Folder
Dim olRecip As Outlook.Recipient
Set OutlookApp = New Outlook.Application
Set olNs = OutlookApp.GetNamespace("MAPI")
Set olRecip = olNs.CreateRecipient("SHARED EMAIL ADDRESS")
Set olFolder = olNs.GetSharedDefaultFolder(olRecip, olFolderInbox)
Set olFolder = olFolder.Folders("MY TEAM'S FOLDER").Folders("THE FOLDER I WANT")
i = 1
For Each OutlookMail In olFolder.Items
Cells(i + 1, 1) = OutlookMail.Subject
Cells(i + 1, 2) = OutlookMail.ReceivedTime
i = i + 1
Next OutlookMail
Set Folder = Nothing
Set olNs = Nothing
Set OutlookApp = Nothing
End Sub
The problematic line of code contains multiple property and method calls:
Set olFolder = olFolder.Folders("MY TEAM'S FOLDER").Folders("THE FOLDER I WANT")
I'd recommend breaking the chain of property and method calls by declaring each of them on a separate line of code. Thus, you will find the problematic call.
Anyway, to find whether such folder exists in a shared store I'd suggest iterating over all subfolders and getting their names, so you may find the required folder following that way.
For Each uFolder In olFolder.Folders
If uFolder.Name = "MY TEAM'S FOLDER" Then
MsgBox "MY TEAM'S FOLDER is found!"
End If
Next uFolder

Excel VBA Outlook16 cannot read MailItem .Body property, though Outlook14 can

I have taken on a spreadsheet that has a VBA routine to read outlook emails
It works fine for me on Excel2010 (using the Outlook Office14.0 Object library) but now doesnt work for my colleague who's on Excel2016 (he's referenced the Outlook Office16.0 Object library in the VBA references), here's the key bits of code:
Dim olItms As Outlook.Items, Dim olMail As Variant,
For Each olMail In olItms
mailContents() = Split(olMail.Body, Chr(13))
I can add a Watch and see all of the emails in the chosen folder are in the olItms array
I can view the properties for each olMail object, eg sender & time received, all look fine.
In my Excel2010 I can read the .Body property and write it to Excel etc
In his Excel2016 I can similarly add a Watch and see all of the emails
I can similarly view the properties for each olMail object
However I cannot read the .Body property, it shows as <> instead of the text and nothing is read
In his Excel2016 session I can use the VBA to open/activate the email
I can also write to the .Body property in the VBA, eg olMail.Body = "test text" works, replacing the body of text in the open/activate email with "test text"
However I still can't read the body text.
The other similar fields (HTMLBody, RTFBody) similarly show as <> with no text read
I can't see anything in his Outlook properties that could be restricting it
The emails definitely have body text in them, as they get read ok in my Excel2010
The Outlook16 object libary must be working ok as the other email properties are reading ok (unless it could be partly working ?)
Here's a copy of all the code up to the error point (with some names changed)
Sub GetIncomeUpdatesFromInbox()
Dim olApp As Outlook.Application
Dim olNs As Outlook.Namespace
Dim olFldr As Outlook.MAPIFolder
Dim olMailbox As Outlook.MAPIFolder
Dim olItms As Outlook.Items
Dim olMail As Variant, vRow As Variant
Dim i As Long
Dim FolderAddress As String, arrFolders() As String, mailContents() As String
Dim EarliestDate As Date
Set olApp = New Outlook.Application
Set olNs = olApp.GetNamespace("MAPI")
On Error Resume Next
Set olMailbox = olNs.Folders("mailbox#company.com").Folders("Inbox")
'Produces the relevant folder as a string
If Range("FolderAddress") = "Update" Or Range("FolderAddress") = "Create" Then
FolderAddress = "\\mailbox#company.com\*Folders\Data\xxx\"
Else
FolderAddress = "\\mailbox#company.com\*Folders\Data\xxx\Update\"
End If
FolderAddress = FolderAddress + Range("FolderAddress")
'changes Folder address into an array
arrFolders() = Split(FolderAddress, "\")
'Enters first part of fodler address
Set olFldr = olNs.Folders.Item(arrFolders(2))
'Navigates to relevant folder
If Not olFldr Is Nothing Then
For i = 3 To UBound(arrFolders)
Set colFolders = olFldr.Folders
Set olFldr = Nothing
Set olFldr = colFolders.Item(arrFolders(i))
If olFldr Is Nothing Then
Exit For
End If
Next
End If
Application.DisplayStatusBar = True
Set olItms = olFldr.Items
'Sorts emails by date received
olItms.Sort “Received”
i = 1
UserForm1.TextBox1 = Format(CDate(Evaluate("WORKDAY(TODAY(),-1)")), "dd/mm/yyyy")
UserForm1.TextBox2 = Format(CDate(Evaluate("WORKDAY(TODAY(),-0)")), "dd/mm/yyyy")
UserForm1.Show
EarliestDate = UserForm1.TextBox1
LatestDate = UserForm1.TextBox2
'moves through mails one by one for all emails received after specified earliest date"
iColumn = 3
For Each olMail In olItms
If LatestDate > CDate(olMail.ReceivedTime) Then
If CDate(olMail.ReceivedTime) > EarliestDate Then
'Splits content of the mail into an array with each element of the array one line in the original email
mailContents() = Split(olMail.Body, Chr(13))
Try to use the GetInspector or Display method before getting the message body.
Another point is a security trigger in the Outlook object model. Outlook may restrict access to secure properties when you automate the host from another process. You may try to run the same code from a COM add-in where you deal with a safe Application instance which doesn't trigger a security issue. There are several ways for suppressing such issues when dealing with OOM:
Use a third-party components for suppressing Outlook security warnings/issues. See Security Manager for Microsoft Outlook for more information.
Use a low-level API instead of OOM. Or any other third-party wrappers around that API, for example, Redemption.
Develop a COM add-in which has access to the trusted Application object. And then communicate from a standalone application with an add-in using standard .Net tools (Remoting).
Use group policy objects for setting up machines.
Install any AV software with the latest databases (up to date).
There are other aspects in the code listed above. Let's cover them in depth.
Instead of using the following code:
Set olMailbox = olNs.Folders("mailbox#company.com").Folders("Inbox")
You need to use the GetDefaultFolder method of the Namespace or Store class which is similar to the GetDefaultFolder method of the NameSpace object. The difference is that this method gets the default folder on the delivery store that is associated with the account, whereas NameSpace.GetDefaultFolder returns the default folder on the default store for the current profile.
Iterating over all items in the folder is not really a good idea:
For Each olMail In olItms
If LatestDate > CDate(olMail.ReceivedTime) Then
If CDate(olMail.ReceivedTime) > EarliestDate Then
Use the Find/FindNext or Restrict methods of the Items class instead. Read more about these methods in the following articles:
How To: Retrieve Outlook calendar items using Find and FindNext methods
How To: Use Restrict method in Outlook to get calendar items

Look through 2 outlook root folders in Excel with VBA

I have managed to get access to the items in 2 folders in Outlook from Excel by using VBA, but now I want to search for the email address x#gmail.com in both aI know how to search each one individually, but once, and sort for the most recent one. The piece I am stuck on is how to look through both folders at once.
I am using Microsoft Office 2016
Obviously, this dummy line does not do the trick: Set olJoinedFldr = olCleanUp + olFldr
Private Sub CommandButton2_Click()
Dim olApp As Outlook.Application 'set app
Dim olNs As Object 'get namespace
Dim olFldr As Outlook.Folder 'to be the inbox
Dim olArchive As Outlook.Folder 'the archive folder
Dim olCleanUp As Outlook.Folder ' the archive subfolder we need
Dim olJoinedFldr As Object 'the to be made joined object to filter....
Dim olItems As Object 'filtered items based on search criteria
Dim olItemReply As Object 'the reply mail
Dim i As Long
Dim emailStr As String
Dim filter As String
Set olApp = CreateObject("Outlook.Application")
Set olNs = olApp.GetNamespace("MAPI")
Set olFldr = olNs.GetDefaultFolder(6) ' olFolderInbox
Set olArchive = olNs.Folders(CStr(olNs.Accounts.Item(1))) 'find email of current user
Set olCleanUp = olArchive.Folders("Archive").Folders("Cleanup") ' get the archive sub folder
Set olJoinedFldr = olCleanUp + olFldr
Set emailStr = "somebody#gmail.com"
filter = "[SenderEmailAddress] = """ & emailStr & """" 'this is the email from person x we are searching for in the 2 folders
' from here on it is currently searching just 1 folder
Set olItems = olFldr.Items.Restrict(filter) 'filter the items
olItems.Sort "[ReceivedTime]", True 'sort by date
If olItems.Count > 0 Then
For i = 1 To olItems.Count
If olItems(i).Class = 43 Then
Set olItemReply = olItems(i).ReplyAll
With olItemReply
.HTMLBody = "<p Dear someone, <br><br></p>" & .HTMLBody
.Display
End With
Exit For
End If
Next
Else
' have code here to make a brand new email already
End If
Set olApp = Nothing
Set olNs = Nothing
Set olFldr = Nothing
Set olArchive = Nothing
Set olCleanUp = Nothing
Set olJoinedFldr = Nothing
Set olItems = Nothing
Set olItemReply = Nothing
Set i = Nothing
Set emailStr = Nothing
Set filter = Nothing
End Sub
You cannot search through two (or more) folders unless you create a Search object using Application.AdvancedSearch. Even then, it is a PITA to work with - the search is asynchronous, and you would need to use events to figure out when the search is completed.
You'd be better off searching one folder at a time and combining the results (if necessary) in your code.
You need to use 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.
Read more about this method in the Advanced search in Outlook programmatically: C#, VB.NET article.

Excel vba: Looping through all subfolders in Outlook email to find an email with certain subject

I have written the following code in Excel VBA that opens an email with the given subject if located in the default inbox folder in Outlook.
However, I would like to search for this email in all inbox subfolders.
Because the code will be used by several users, I do not know the number and the name of their outlook inbox subfolders. Any ideas on how I could search this email in all subfolders?
Sub GetEmail()
Dim OutApp as Object
Dim Namespace as Object
Dim Folder as Object
Dim myMail as Object
Set OutApp = CreateObject("Outlook.Application")
Set Namespace = OutApp.GetNamespace ("MAPI")
Set Folder = Namespace.GetDefaultFolder(6)
Set myMail = Folder.Items.Find ("[Subject] = ""Test""")
myMail.Display
End Sub
The below code cycles through all folders in Outlook, to the level one beneath the Inbox. You can just look at the inbox by specifying the initial folder to look at. Thus you can search the folder as you loop through. you can add further sub folders by looping deeper, or by saying folders.count > X.
I have always found Outlook from Excel frustrating so have made this Early Bound to make coding easier. This means that you will need to go to Tool/References and add Microsoft Outlook 16(x).0 Object Library
You can change it back to late bound after coding, as early binding will give you IntelliSense and make life a whole lot easier.
Sub GetEmail()
Dim OutApp As Outlook.Application
Dim Namespace As Outlook.Namespace
Dim Mfolder As Outlook.MAPIFolder
Dim myMail As Outlook.Items
Dim Folder As Outlook.MAPIFolder
Dim SubFolder As Outlook.MAPIFolder
Dim UserFolder As Outlook.MAPIFolder
Set OutApp = New Outlook.Application
Set Namespace = OutApp.GetNamespace("MAPI")
On Error Resume Next
For Each Folder In Namespace.Folders
For Each SubFolder In Folder.Folders
For Each UserFolder In SubFolder.Folders
Debug.Print Folder.Name, "|", SubFolder.Name, "|", UserFolder.Name
Next UserFolder
Next SubFolder
Next Folder
On Error GoTo 0
End Sub
The on error is to skip any issues with outlook mapping Archive pst files.

Excel vba attachment search

I am working on macro which would find string within outlook mails attachments.
I have working module searching through subject, body and attachments names on given mailbox and folder.
Problem is that my code don't want to emulate outlook search within attachment function.
Code searches for a word 'office' within mail subject field and displays found mails:
Sub t22()
Dim myolApp As Outlook.Application
Dim objNS As Outlook.Namespace
Dim objFolder As Outlook.MAPIFolder
Dim ProcessName As String
Dim EmailName As String
Set myolApp = CreateObject("Outlook.Application")
Set objNS = myolApp.GetNamespace("MAPI")
Set objFolder = objNS.Folders("my#mailbox.com")
Set TargetInbox = objFolder.Folders("Inbox")
Dim oItms As Outlook.Items
Dim oItm As Outlook.MailItem
Set oItms = TargetInbox.Items
Dim sFilter As String
Dim EmailTime As String
sFilter = "#SQL=""http://schemas.microsoft.com/mapi/proptag/0x0037001f"" = 'office'"
Set oItm = oItms.Find(sFilter)
'If Not IsEmpty(oltm) Then
oItm.Display
Debug.Print oItm.Body
'End If
End Sub
As far as I understand 'sFilter' should be referring to target search fields but when I use its value for attachments (0x0EA5001E) it fails.
I was also trying AdvancedSearch method but with same result - working for everything other than attachment.
The Outlook object model doesn't provide anything for searching a string in attachments. You need to find all items that have files attached to them and then iterate over all of them. While iterating you can open the attached file and search for a string inside. You can use the following search criteria to find all items that have attachments:
query ="#SQL=" & chr(34) & "urn:schemas:httpmail:hasattachment" & chr(34) & "=1"
You may also find the following articles helpful:
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

Resources