How to find Calendar ID - vba

I need to find the ID of a Outlook Calendar. It is a public calendar with many contributors/users, but not listed as "shared".
I want to automatically export selected calendars to an *.ics.
Currently I only can export my default folder with:
Set oNamespace = Application.GetNamespace("MAPI")
Set oFolder = oNamespace.GetDefaultFolder(olFolderCalendar)
Set oCalendarSharing = oFolder.GetCalendarExporter
oCalendarSharing.SaveAsICal "C:\calendar.ics"
I need to add the "ThisOne" to the export.
The properties do not have a location:
How to I find the ID, so I can use it with "GetFolderFromID()"? Or are there better ways to include the Calendar in the export or export it on its own?
Edit:
Now I think I got the Calendar-ID by using
Set oNamespace = Application.GetNamespace("MAPI")
Set oFolderPicked = oNamespace.PickFolder
Set oFolder = oNamespace.GetFolderFromID(oFolderPicked.EntryID, oFolderPicked.StoreID)
Set oCalendarSharing = oFolder.GetCalendarExporter
but this throws a exception at the last line (GetCalendarExporter):
If I export in the GUI (File -> Save), it works without a problem...
(Later I dont want to use PickFolder and just hard-code the EntryID to the script)

Open Outlook -> Goto Calendar of choice -> Alt F11 -> Immediate Window -> Type the following and hit enter.
? ActiveExplorer.CurrentFolder.EntryID

This demonstrates how to get the EntryID of a folder.
Option Explicit
Sub entryIDFromActiveExplorer()
Dim entryIDStr As String
Dim uPrompt As String
Dim uTitle As String
Dim uDefault As String
Dim msg As String
' Select a folder in the folder view
' Do not use the calendar view
entryIDStr = ActiveExplorer.CurrentFolder.entryID
uPrompt = "To hardcode the entryID of the " & _
Session.GetFolderFromID(entryIDStr) & _
" folder, copy this ID"
' Copy from the immediate pane
Debug.Print uPrompt
Debug.Print entryIDStr
uTitle = Session.GetFolderFromID(entryIDStr)
uDefault = entryIDStr
msg = InputBox(Prompt:=uPrompt, Title:=uTitle, Default:=uDefault)
End Sub
Sub entryIDFromPickfolder()
' If you do not want to select a folder
' in the normal way, from the GUI
Dim oFolderPicked As folder
Dim entryIDStr As String
Dim uPrompt As String
Dim uTitle As String
Dim uDefault As String
Dim msg As String
Set oFolderPicked = Session.PickFolder
If Not oFolderPicked Is Nothing Then
entryIDStr = oFolderPicked.entryID
uPrompt = "To hardcode the entryID of the " & _
Session.GetFolderFromID(entryIDStr) & _
" folder, copy this ID"
' Copy from the immediate pane
Debug.Print uPrompt
Debug.Print entryIDStr
Set ActiveExplorer.CurrentFolder = Session.GetFolderFromID(entryIDStr)
DoEvents
uTitle = Session.GetFolderFromID(entryIDStr)
uDefault = entryIDStr
msg = InputBox(Prompt:=uPrompt, Title:=uTitle, Default:=uDefault)
End If
ExitRoutine:
Set oFolderPicked = Nothing
End Sub

Related

OutLook VBA Email or Notification Causes Out of Bounds Error

I have some outlook VBA code which works fine to save attachments, however every time I get an email or a meeting notification in Outlook it causes an instant Out of Bounds error If I don't get any emails or notifications the code will run fine through to completion.
Is there a way to ensure that these notifications will not stop the code from running?
Option Explicit
Sub SaveAttachmentsFromSelectedItemsPDF2_ForNext()
Dim currentItem As Object
Dim currentAttachment As Attachment
Dim saveToFolder As String
Dim savedFileCountPDF As Long
Dim i As Long
Dim j As Long
saveToFolder = "c:\dev\outlookexport" 'change the path accordingly
savedFileCountPDF = 0
For i = 1 To ActiveExplorer.Selection.Count
Set currentItem = ActiveExplorer.Selection(i)
For j = 1 To currentItem.Attachments.Count
Set currentAttachment = currentItem.Attachments(j)
If UCase(Right(currentAttachment.DisplayName, 5)) = UCase(".xlsx") Then
currentAttachment.SaveAsFile saveToFolder & "\" & _
Left(currentAttachment.DisplayName, Len(currentAttachment.DisplayName) - 5) & ".xlsx"
savedFileCountPDF = savedFileCountPDF + 1
End If
' If For Next does not release memory automatically then
' uncomment to see if this has an impact
'Set currentAttachment = Nothing
Next
' If For Next does not release memory automatically then
' uncomment to see if this has an impact
'Set currentItem = Nothing
Next
MsgBox "Number of PDF files saved: " & savedFileCountPDF, vbInformation
End Sub
This is what I tried to create from the answer below:
Option Explicit
Sub SaveAttachmentsFromSelectedItemsPDF2_ForNext()
Dim currentItem As Object
Dim currentAttachment As Attachment
Dim saveToFolder As String
Dim savedFileCountPDF As Long
Dim i As Long
Dim j As Long
Dim x As Long
Dim myOlExp As Object
Dim myOlSel As Object
' New
Set myOlExp = Application.ActiveExplorer
Set myOlSel = myOlExp.Selection
saveToFolder = "c:\dev\outlookexport" 'change the path accordingly
savedFileCountPDF = 0
For x = 1 To myOlSel.Count
If myOlSel.Item(x).Class = OlObjectClass.olMail Then
Set currentItem = ActiveExplorer.Selection(i)
For j = 1 To currentItem.Attachments.Count
Set currentAttachment = currentItem.Attachments(j)
If UCase(Right(currentAttachment.DisplayName, 5)) = UCase(".xlsx") Then
currentAttachment.SaveAsFile saveToFolder & "\" & _
Left(currentAttachment.DisplayName, Len(currentAttachment.DisplayName) - 5) & ".xlsx"
savedFileCountPDF = savedFileCountPDF + 1
End If
Next
End If
Next
MsgBox "Number of PDF files saved: " & savedFileCountPDF, vbInformation
End Sub
The Selection property of the Explorer class returns a Selection object that contains the item or items that are selected in the explorer window. In your code I've noticed the following lines of code:
For i = 1 To ActiveExplorer.Selection.Count
Set currentItem = ActiveExplorer.Selection(i)
So, if the selection is changed in Outlook between these two lines of code you may get out of range exception at runtime. Instead, I'd recommend caching the selection object and use it through the code to make sure it remains the same:
Set myOlExp = Application.ActiveExplorer
Set myOlSel = myOlExp.Selection
For x = 1 To myOlSel.Count
If myOlSel.Item(x).Class = OlObjectClass.olMail Then
' do something here
End If
Next
Another important thing is that a folder may contain different types of items. You'd need to check their message class to distinguish different kind of Outlook items.

Looping through large set of mailitems freezes Outlook

I am trying to loop through the mailItems of different subfolders looking for a comment (with PropertyAccessor) in different stores.
My code works when given 1-3 stores and around 2000 mailItems, however as the number increases it crashes Outlook.
I have implemented:
Date filtering
MailItem release
I am trying parallely the Application.AdvancedSearch method, however not managing yet.
Sub FindEmaibyComment()
Dim Fldr As Outlook.folder
Dim Str As Outlook.Store
Dim Strs As Outlook.Stores
Dim Pfldr As Outlook.folder
Dim oRoot As Outlook.folder
Dim clearingFolder As Outlook.folder
Dim mail As MailItem
Dim TaskID As String
Set Strs = Application.Session.Stores
TaskID = InputBox("Enter the MailID you want to look for." & vbNewLine & "(For example MAIL_20200525_1502769)", "Message input", "")
Set RegEx = CreateObject("vbscript.regexp")
With RegEx
.MultiLine = False
.Global = True
.IgnoreCase = True
.Pattern = "MAIL_" & "[0-9]{8}" & "_" & "[0-9]{6,100}" ' <-- Regex match for input string, example string: MAIL_20200513_1434402
End With
Set Match = RegEx.Execute(TaskID)
If Match.Count = 1 Then
'Select the stores that contain ICE, and loop through them
For Each Str In Strs
If InStr(Str.DisplayName, "Mailbox1") > 0 Then
On Error Resume Next '--> In case no permission for the store is given, go to the next store
Set oRoot = Str.GetRootFolder
Set clearingFolder = LoopFolders(oRoot, TaskID)
End If
Next Str
If MailFound = False Then
MsgBox ("Sorry, I could not find the Email")
End If
Else
MsgBox ("Please insert the correct ID with a format as follows: MAIL_12345678_1234567")
End If
End Sub
Function LoopFolders(ByVal oFolder As Outlook.folder, TaskID As String) As Outlook.folder
Dim folders As Outlook.folders
Dim Subfolders As Outlook.folders
Dim folder As Outlook.folder
Dim SubFolder As Outlook.folder
Dim foldercount As Integer
Dim clearingFolder As Outlook.folder
On Error Resume Next
Set folders = oFolder.folders
foldercount = folders.Count
'Count folders below oFolder. This is the store level
If foldercount Then
For Each folder In folders
'Count folders below Folder. This is 1. folder level "AE01"
If folder.folders.Count > 0 Then
Set Subfolders = folder.folders
For Each SubFolder In Subfolders
'Subfolders below Folder. This is 2. folder level "Clearing", "Destination"
If InStr(SubFolder, "Destination") > 0 Then
Set LoopFolders = SubFolder
FindID TaskID, SubFolder
End If
Next SubFolder
End If
Next folder
End If
End Function
Function FindID(TaskID As String, folderClearing As Outlook.folder)
Dim PropName, Comment, MessageID As String
Dim oMail As MailItem
Dim oPA As Outlook.PropertyAccessor
Dim olFolder As Outlook.MAPIFolder
Dim olNamespace As Outlook.NameSpace
Dim inputDate, inputDay, inputYear, inputMonth, sFilter, inputDateConverted, startDay, endDay As String
inputDate = Right(Left(TaskID, 13), 8) 'Example: 20200610
inputYear = Left(inputDate, 4)
inputDay = Right(inputDate, 2)
inputMonth = Right(Left(inputDate, 6), 2)
If Left(inputDay, 1) = "0" Then
inputDay = Right(inputDay, 1)
End If
If Left(inputMonth, 1) = "0" Then
inputMonth = Right(inputMonth, 1)
End If
inputDateConverted = inputMonth & "/" & inputDay & "/" & inputYear
startDay = Format(CDate(inputDateConverted & " 00:00 AM "), "\'m/d/yyyy hh:mm AM/PM\'")
endDay = Format(CDate(inputDateConverted & " 12:00 PM"), "\'m/d/yyyy hh:mm AM/PM\'")
Set myItems = folderClearing.Items
sFilter = startDay & " > [ReceivedTime] And" & endDay & " < [ReceivedTime]"
Set myRestrictedItems = myItems.Restrict(sFilter)
For Each oMail In myRestrictedItems
'PR_TRANSPORT_COMMENTS
PropName = "http://schemas.microsoft.com/mapi/proptag/0x3004001F"
Set oPA = oMail.PropertyAccessor
Comment = oPA.GetProperty(PropName)
If InStr(1, Comment, TaskID, vbTextCompare) > 0 Then
MailFound = True
MsgBox ("Mail was found in Company Code " & folderClearing.Parent & ", let me open it for you")
oMail.Display
End
End If
Set oMail = Nothing
Next oMail
End Function
Restrict and Find/FindNext methods are run on the main thread which means they block the UI and the overall user experience with Outlook. Moreover, iterating over all folder and subfolders is not really a good idea for searching items. That is for AdvancedSearch was introduced!
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 the AdvancedSearch method in the Advanced search in Outlook programmatically: C#, VB.NET article.
Using the Scope parameter, you can specify one or more folders in the same store, but you may not specify multiple folders in multiple stores. To specify multiple folders in the same store for the Scope parameter, use a comma character between each folder path and enclose each folder path in single quotes. For default folders such as Inbox or Sent Items, you can use the simple folder name instead of the full folder path.
You can run multiple searches simultaneously by calling the AdvancedSearch method in successive lines of code. However, you should be aware that programmatically creating a large number of search folders can result in significant simultaneous search activity that would affect the performance of Outlook, especially if Outlook conducts the search in online Exchange mode.
Public m_SearchComplete As Boolean
Private Sub Application_AdvancedSearchComplete(ByVal SearchObject As Search)
If SearchObject.Tag = "MySearch" Then
m_SearchComplete = True
End If
End Sub
Sub TestSearchForMultipleFolders()
Dim Scope As String
Dim Filter As String
Dim MySearch As Outlook.Search
Dim MyTable As Outlook.Table
Dim nextRow As Outlook.Row
m_SearchComplete = False
'Establish scope for multiple folders
Scope = "'" & Application.Session.GetDefaultFolder( _
olFolderInbox).FolderPath _
& "','" & Application.Session.GetDefaultFolder( _
olFolderSentMail).FolderPath & "'"
'Establish filter
If Application.Session.DefaultStore.IsInstantSearchEnabled Then
Filter = Chr(34) & "urn:schemas:httpmail:subject" _
& Chr(34) & " ci_phrasematch 'Office'"
Else
Filter = Chr(34) & "urn:schemas:httpmail:subject" _
& Chr(34) & " like '%Office%'"
End If
Set MySearch = Application.AdvancedSearch( _
Scope, Filter, True, "MySearch")
While m_SearchComplete <> True
DoEvents
Wend
Set MyTable = MySearch.GetTable
Do Until MyTable.EndOfTable
Set nextRow = MyTable.GetNextRow()
Debug.Print nextRow("Subject")
Loop
End Sub
A common cause of mysterious failure is hiding errors with On Error Resume Next.
For Each Str In Strs
Debug.Print "Str.DisplayName: " & Str.DisplayName
If InStr(Str.DisplayName, "Mailbox1") > 0 Then
Set oRoot = Nothing ' else bypssing expected error keeps previous value in oRoot
' bypass expected error
On Error Resume Next '--> In case no permission for the store is given, go to the next store
Set oRoot = Str.GetRootFolder
' remove error bypass
' to return to normal error handling
' to deal with unexpected errors
On Error GoTo 0 ' now you can see errors and can debug your code
' Handle the bypassed error
If Not oRoot Is Nothing Then
Set clearingFolder = LoopFolders(oRoot, TaskID)
End If
End If
Next Str

Outlook mail item multiple restrict methods

I am trying to apply a filter using the restrict method of mail items using Outlook VBA. Below code works fine if I use only one restrict method based on Category_Filter variable, but when I try to use two restrict methods,
Somehow my Flag_Filter fails.
I believe I am making some mistake in concatenation for Flag_Filter and need some clue here.
Sub ApplyFilters()
Dim ol As Outlook.Application
Dim ns As Outlook.NameSpace
Dim fol As Outlook.Folder
Dim i As Object
Dim mi As Outlook.MailItem
Dim OrderNumber, Category_Filter, Flag_Filter As String
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.GetDefaultFolder(olFolderInbox)
OrderNumber = "GCU5689"
Category_Filter = "[Categories] = 'Textile'"
Flag_Filter = "[FlagRequest] = " & OrderNumber
For Each i In fol.Items.Restrict(Category_Filter).Restrict(Flag_Filter)
' Some actions
Next i
End Sub
You cannot restrict a restricted collection. You need to combine the restrictions (using "and" ) into a single query and call Restrict only once.
like #Dmitry Streblechenko says use And Operator (Visual Basic) MSDN with your Outlook restrict method
Example
Option Explicit
Private Sub Examples()
Dim olNs As Outlook.NameSpace
Dim Inbox As Outlook.MAPIFolder
Dim Items As Outlook.Items
Dim Msg As String
Dim i As Long
Dim Filter As String
Set olNs = Application.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Filter = "[Categories] = 'Textile' And [FlagRequest] = 'Follow up'"
Set Items = Inbox.Items.Restrict(Filter)
Msg = Items.Count & " Items in " & Inbox.Name
If MsgBox(Msg, vbYesNo) = vbYes Then
For i = Items.Count To 1 Step -1
Debug.Print Items(i) 'Immediate Window
Next
End If
End Sub
More examples here
for variable you can use the chr(34) function to represent the double quote (whose ASCII character value is 34) that is used as an escape character or use double quote.
Example
Dim OrderNumber As String
OrderNumber = "GCU5689"
Filter = "[Categories] = 'Textile' And [FlagRequest] = '" & OrderNumber & "'"
In general you may apply multiple actions one at a time. Such an approach allows easier troubleshooting.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
Sub ApplyFilters()
Dim ns As NameSpace
Dim fol As folder
Dim i As Object
Dim mi As MailItem
Dim resItms As Items
Dim OrderNumber As String
Dim Category_Filter As String
Dim Flag_Filter As String
Set ns = GetNamespace("MAPI")
Set fol = ns.GetDefaultFolder(olFolderInbox)
OrderNumber = "GCU5689"
Category_Filter = "[Categories] = 'Textile'"
Flag_Filter = "[FlagRequest] = " & OrderNumber
Set resItms = fol.Items.Restrict(Category_Filter)
Debug.Print "Items after first restrict: " & resItms.Count
Set resItms = resItms.Restrict(Flag_Filter)
Debug.Print "Items after second restrict: " & resItms.Count
For Each i In resItms
' Some actions
' If, for instance, a property is only found on mailitems
If i.Class = olmail Then
Set mi = i
Debug.Print mi.Subject
End If
Next
End Sub

Server based rule to collate 500+ adresses into ~150 inbox folders

I have a Company Project where ~500 clients send Emails to the my project inbox. Those clients correspond to ~150 offices (I have an Excel-List of the email addresses & according offices).
Each office shall have one Outlook folder, so I can quickly check upon the past correspondence with a specific office.
The Project inbox is looked after and used by several co-workers, hence server- and not client based rules.
How do I set this up?
My simple idea in form of a pseudo code:
for each arriving email
if (from-adress is in "email & office-List")
move that email to outlook folder "according office name"
end if
end for
and the same for outgoing emails:
for each sent email
if (to-adress is in "email & office-List")
move that email to outlook folder "according office name"
end if
end for
Thanks for suggestions!
...and besides, can outlook folders be created programmatically from a list of names?
My solution is a skript i run daily on a manual basis since my employer doesnt allow scripts on arriving messages.
the logic in short is:
fetch list of emails & their corresponding offices (both string lists)
set up folder variables
loop through messages, and move them eventually
the code looks like
Option Compare Text ' makes string comparisons case insensitive
Sub sortEmails()
'sorts the emails into folders
Dim msg As Outlook.MailItem
Dim itm As Object
Dim adress As String
Dim pa As Outlook.PropertyAccessor
Const PR_SMTP_ADDRESS As String = _
"http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
'1) fetch emails
GetEMailsFolders locIDs, emails, n
'1.5) fetch folder objects
'Create an instance of Outlook & inbox reference
Dim Inbox As Outlook.MAPIFolder
Dim outbox As Outlook.MAPIFolder
Set outlookApp = New Outlook.Application
Set NS = outlookApp.GetNamespace("MAPI")
Set objOwner = NS.CreateRecipient("email#host.com")
objOwner.Resolve
'Set inbox = NS.GetSharedDefaultFolder(objOwner, olFolderInbox)
Set Inbox = NS.Folders("email#host.com").Folders("Inbox")
Set outbox = NS.Folders("email#host.com").Folders("Sent Items")
Dim basefolder As Outlook.MAPIFolder
Dim bfName As String
bfName = "Offices" 'name of the folder for the offices
Set basefolder = MkDirConditional(Inbox.Folders("Project folder"), bfName)
'2)loop through inbox & outbox emails
Dim destination As Outlook.MAPIFolder
Dim fold(1 To 2) As Outlook.MAPIFolder
Set fold(1) = Inbox
Set fold(2) = outbox
Dim LocID As String
For Each fol In fold 'loop through inbox & outbox
Debug.Print fol
'reverse fo loop because otherwise moved messages modify indices of following messages
For i = fol.Items.Count To 1 Step -1 'Each itm In fol.Items
Set itm = fol.Items(i)
If TypeName(itm) = "MailItem" Then ' others are AppointmentItem, MeetingItem, or TaskItem
Set msg = itm
'Debug.Print " " & msg.Subject
If fol = Inbox Then
' there are two formats of email adrersses.
If msg.SenderEmailType = "EX" Then 'check two kinds of email adress formats
adress = msg.Sender.GetExchangeUser().PrimarySmtpAddress
ElseIf msg.SenderEmailType = "SMTP" Then 'SMTP case
adress = msg.SenderEmailAddress
Else
Debug.Print " neither EX nor SMTP" & msg.Subject;
End If
pos = Findstring(adress, emails) ' position in the email / standort list
ElseIf fol = outbox Then
For Each rec In msg.Recipients
Set pa = rec.PropertyAccessor
adress = pa.GetProperty(PR_SMTP_ADDRESS)
pos = Findstring(adress, emails)
If pos > 0 Then
Exit For
End If
Next rec
End If
'4.5) if folder doesnt exist, create it
'5) move message
If pos > 0 Then
'Debug.Print " Its a Match!!"
LocID = locIDs(pos)
Set destination = MkDirConditional(basefolder, LocID)
Debug.Print " " & Left(msg.Subject, 20), adress, pos, destination
msg.Move destination
Else
'Debug.Print " not found!"
End If
Else
'Debug.Print " " & "non-mailitem", itm.Subject
End If
Next i
Next fol
End Sub
'// Function - Check folder Exist
Private Function FolderExists(Inbox As Outlook.MAPIFolder, FolderName As String) As Boolean
Dim Sub_Folder As MAPIFolder
On Error GoTo Exit_Err
Set Sub_Folder = Inbox.Folders(FolderName)
FolderExists = True
Exit Function
Exit_Err:
FolderExists = False
End Function
Function MkDirConditional(basefolder As Outlook.MAPIFolder, newfolder As String) As Outlook.MAPIFolder
Debug.Print newfolder & " ";
If FolderExists(basefolder, newfolder) Then
'folder exists, so just skip
Set MkDirConditional = basefolder.Folders(newfolder)
Debug.Print "exists already"
Else
'folder doesnt exist, make it
Set MkDirConditional = basefolder.Folders.Add(newfolder)
Debug.Print "created"
End If
End Function
'function to compare two strings, min the option compare text at the top line
Function Findstring(str As String, arr As Variant) As Integer
'returns -1 if a string is not found, otherwise its index
Findstring = -1
Dim i As Integer
i = 1
For Each Item In arr
'Debug.Print Item
If str = Item Then
Findstring = i
Exit For
End If
i = i + 1
Next
End Function
' function to fetch the lists of emails and offices
Sub GetEMailsFolders(ByRef rng1 As Variant, ByRef rng2 As Variant, ByRef n As Variant)
'declare variables
Dim xExcelFile As String
Dim xExcelApp As Excel.Application
Dim xWb As Excel.Workbook
Dim xWs As Excel.Worksheet
Dim xExcelRange As Excel.Range
Dim TotalRows As Long
'declare SPOC xls file
xExcelFile = "adresses.xlsx"
'open the file
Set xExcelApp = CreateObject("Excel.Application")
Set xWb = xExcelApp.Workbooks.Open(xExcelFile)
Set xWs = xWb.Sheets(1)
'extract LocIDs (column A), emails (column O) and thir number
n = xWs.Range(xWs.Range("A2"), xWs.Range("A2").End(xlDown)).Count ' works
ReDim rng1(1 To n) As Variant
ReDim rng2(1 To n) As Variant
For i = 1 To n
rng1(i) = xWs.Cells(i + 1, 1)
rng2(i) = xWs.Cells(i + 1, 15)
'Debug.Print rng1(i), rng2(i)
Next
Debug.Print "done reading LocIDs & emails"
End Sub

Download attachment (attachment not found)

I've got a code from here and I'm tweaking it for my need. My need is quite simple: I need it to download if it has the name of the Daily Tracker I'm keeping track of (as it changes daily with the Format(Now)). The problem is that it is not finding the attachment.
The code can find the email if I substitute the ElseIf to Next part for oOlItm.Display, but won't download the attachment.
Sub AttachmentDownload()
Const olFolderInbox As Integer = 6
'~~> Path for the attachment
Const AttachmentPath As String = "C:\TEMP\TestExcel"
Dim oOlAp As Object
Dim oOlns As Object
Dim oOlInb As Object
Dim oOlItm As Object
Dim oOlAtch As Object
Dim NewFileName As String
NewFileName = "Daily Tracker " & Format(Now, "dd/MM/yyyy")
Set oOlAp = GetObject(, "Outlook.application")
Set oOlns = oOlAp.GetNamespace("MAPI")
Set oOlInb = oOlns.GetDefaultFolder(olFolderInbox)
For Each oOlItm In oOlInb.Items
If InStr(oOlItm.Subject, NewFilename)) <> 0 Then
ElseIf oOlItm.Attachments.Count <> 0 Then
For Each oOlAtch In oOlItm.Attachments
oOlAtch.SaveAsFile (AttachmentPath)
Exit For
Next
Else
MsgBox "No attachments found"
End If
Exit For
Next
End Sub
The email:
This should work for you:
Sub AttachmentDownload()
Const olFolderInbox As Integer = 6
'~~> Path for the attachment
Const AttachmentPath As String = "C:\TEMP\TestExcel"
Dim oOlAp As Object
Dim oOlns As Object
Dim oOlInb As Object
Dim oOlItm As Object
Dim oOlAtch As Object
Dim oOlResults As Object
Dim x As Long
Dim NewFileName As String
NewFileName = "Daily Tracker " & Format(Now, "dd-MM-yyyy")
'You can only have a single instance of Outlook, so if it's already open
'this will be the same as GetObject, otherwise it will open Outlook.
Set oOlAp = CreateObject("Outlook.Application")
Set oOlns = oOlAp.GetNamespace("MAPI")
Set oOlInb = oOlns.GetDefaultFolder(olFolderInbox)
'No point searching the whole Inbox - just since yesterday.
Set oOlResults = oOlInb.Items.Restrict("[ReceivedTime]>'" & Format(Date - 1, "DDDDD HH:NN") & "'")
'If you have more than a single attachment they'll all overwrite each other.
'x will update the filename.
x = 1
For Each oOlItm In oOlResults
If oOlItm.attachments.Count > 0 Then
For Each oOlAtch In oOlItm.attachments
If GetExt(oOlAtch.FileName) = "xlsx" Then
oOlAtch.SaveAsFile AttachmentPath & "\" & NewFileName & "-" & x & ".xlsx"
End If
x = x + 1
Next oOlAtch
End If
Next oOlItm
End Sub
'----------------------------------------------------------------------
' GetExt
'
' Returns the extension of a file.
'----------------------------------------------------------------------
Public Function GetExt(FileName As String) As String
Dim mFSO As Object
Set mFSO = CreateObject("Scripting.FileSystemObject")
GetExt = mFSO.GetExtensionName(FileName)
End Function
Another way of doing it is from within Outlook:
Create a new folder in your Outlook Inbox and set a rule to move the email to this folder when it arrives. You can then write code to watch this folder and save the file as soon as it arrives.
Place this code within the ThisOutlookSession module in Outlook.
Dim WithEvents TargetFolderItems As Items
Const FILE_PATH As String = "C:\TEMP\TestExcel\"
Private Sub Application_Startup()
Dim ns As Outlook.NameSpace
Set ns = Application.GetNamespace("MAPI")
Set TargetFolderItems = ns.Folders.Item("Mailbox - Darren Bartrup-Cook") _
.Folders.Item("Inbox") _
.Folders.Item("My Email For Processing").Items
End Sub
Sub TargetFolderItems_ItemAdd(ByVal Item As Object)
'when a new item is added to our "watched folder" we can process it
Dim olAtt As Attachment
Dim i As Integer
Dim sTmpFileName As String
Dim objFSO As Object
Dim sExt As String
If Item.Attachments.Count > 0 Then
Set objFSO = CreateObject("Scripting.FileSystemObject")
For i = 1 To Item.Attachments.Count
Set olAtt = Item.Attachments(i)
sExt = objFSO.GetExtensionName(olAtt.FileName)
If sExt = "xlsx" Then
sTmpFileName = "Daily Tracker " & Format(Now, "dd-mm-yyyy") & ".xlsx"
End If
Item.UnRead = False
olAtt.SaveAsFile FILE_PATH & sTmpFileName
DoEvents
Next
End If
Set olAtt = Nothing
MsgPopup "A new attachment has been saved.", vbOKOnly, "New Daily Tracker"
End Sub
Private Sub Application_Quit()
Dim ns As Outlook.NameSpace
Set TargetFolderItems = Nothing
Set ns = Nothing
End Sub
Create a new module in Outlook and put this code in there. This will create a messagebox that won't stop whatever you're doing.
Public Function MsgPopup(Optional Prompt As String, _
Optional Buttons As VbMsgBoxStyle = vbOKOnly, _
Optional Title As String, _
Optional SecondsToWait As Long = 0) As VbMsgBoxResult
' Replicates the VBA MsgBox() function, with an added parameter
' to automatically dismiss the message box after n seconds
' If dismissed automatically, this will return -1: NOT ‘cancel’ or the default button choice.
' Nigel Heffernan, 2006. This code is in the public domain.
' Uses late-binding: bad for performance and stability, useful for code portability
' The correct declaration is: Dim objWshell As IWshRuntimeLibrary.WshShell
Dim objWshell As Object
Set objWshell = CreateObject("WScript.Shell")
MsgPopup = objWshell.Popup(Prompt, SecondsToWait, Title, Buttons)
Set objWshell = Nothing
End Function