How to extract text from custom flags - vba

I handle a number of emails every day. I flag emails with custom text to track emails and correlate each with the other.
How do I extract emails that are flagged along with custom texts in each flagged email in a folder.
I could manage to follow VBA code available at VBA Express forum:
Sub CountItems()
Dim objMainFolder As Outlook.folder
Dim lItemsCount As Long
'Select a folder
Set objMainFolder = Outlook.Application.Session.PickFolder
If objMainFolder Is Nothing Then
MsgBox "You choose select a valid folder!", vbExclamation + vbOKOnly, "Warning for Pick Folder"
Else
'Initialize the total count
lItemsCount = 0
Call LoopFolders(objMainFolder, lItemsCount)
End If
'Display a message for the total count
MsgBox "There are " & lItemsCount & " items in the " & objMainFolder.Name & " folder Including its subfolders.", vbInformation, "Count Items"
End Sub
Sub LoopFolders(ByVal objCurrentFolder As Outlook.folder, lCurrentItemsCount As Long)
Dim objSubfolder As Outlook.folder
lCurrentItemsCount = lCurrentItemsCount + objCurrentFolder.Items.Count
'Process all folders and subfolders recursively
If objCurrentFolder.Folders.Count Then
For Each objSubfolder In objCurrentFolder.Folders
Call LoopFolders(objSubfolder, lCurrentItemsCount)
Next
End If
End Sub
It displays the count of emails in a folder and its subfolders.
I stumbled across "How do you access custom Follow-Up flag values (“Flag to…”) in Outlook 2016?" which partially solves my problem.
The solution, from what I could understand, pivots around the Search Folder in Outlook mail client and setting custom view by All Mail fields and Follow Up Flag and then setting the condition of the latter to "is not empty". Grouping by "Follow Up Flag" ascending then displays the custom flags in groups for easy reference.
However, that does not solve the problem of listing the custom flag values.

Iterating over all folder in Outlook and searching for specific items is not really a good idea as shown in your sample code:
'Process all folders and subfolders recursively
If objCurrentFolder.Folders.Count Then
For Each objSubfolder In objCurrentFolder.Folders
Call LoopFolders(objSubfolder, lCurrentItemsCount)
Next
End If
Instead, you may consider using the AdvancedSearch method of the Outlook Application class which performs a search based on a specified DAV Searching and Locating (DASL) search string. The AdvancedSearch method and related features in the Outlook object model do not create a Search Folder that will appear in the Outlook user interface. However, you can use the Save method of the Search object that is returned to create a Search Folder that will appear in the Search Folders list in the Outlook user interface.
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).
Finally, you can stop the search process at any moment using the Stop method of the Search class.
Read more about that in the Advanced search in Outlook programmatically: C#, VB.NET article.
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
Also you may consider using the Find/FindNext or Restrict methods that allow getting items that correspond to the search filter in a specific folder (per folders). 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

You can apply .Restrict on FlagRequest to count items with a custom text flag.
Option Explicit
Sub CountCustomFlagItems()
Dim objMainFolder As folder
Dim lItemsCount As Long
Dim customText As String
Dim resFilter As String
Dim uMsg As String
'Select a folder
Set objMainFolder = Session.PickFolder
If objMainFolder Is Nothing Then
' user cancelled
Exit Sub
Else
'Initialize the total count
lItemsCount = 0
customText = "Custom Text Here"
' https://learn.microsoft.com/en-us/office/vba/api/outlook.mailitem.flagrequest
resFilter = "[FlagRequest] = " & customText
Debug.Print "resFilter: " & resFilter
Call LoopFolders(objMainFolder, lItemsCount, resFilter)
End If
'Display a message for the total count
uMsg = "There are " & lItemsCount & " " & customText & " items in the " & objMainFolder.Name & _
" folder including subfolders."
Debug.Print uMsg & vbCr
'MsgBox uMsg, vbInformation, "Count Items"
End Sub
Sub LoopFolders(ByVal objCurrentFolder As folder, lCurrentItemsCount As Long, resFilter As String)
Dim objSubfolder As folder
Dim objCurrentFolderItems As Items
Dim resItms As Items
Set objCurrentFolderItems = objCurrentFolder.Items
If objCurrentFolderItems.Count Then
' https://learn.microsoft.com/en-us/office/vba/api/outlook.items.restrict
Set resItms = objCurrentFolderItems.Restrict(resFilter)
If resItms.Count Then
Debug.Print objCurrentFolder & " Items Count: " & objCurrentFolderItems.Count
Debug.Print " Filtered items in the folder " & objCurrentFolder & ": " & resItms.Count
Debug.Print
lCurrentItemsCount = lCurrentItemsCount + resItms.Count
End If
End If
'Process all folders and subfolders recursively
If objCurrentFolder.folders.Count Then
For Each objSubfolder In objCurrentFolder.folders
Call LoopFolders(objSubfolder, lCurrentItemsCount, resFilter)
Next
End If
End Sub

Related

Finding specific email Subject in Outlook sub folder and download attached files from the email

I have managed to make macro that downloads attachments files from my Inbox sub folder in Outlook, but it seems i can't make it works for specific combination of symbols in the email subject.
I need to download only the attachments from email that contains "906" in the Subject name. Can someone makes the modification i need for this task, please? I'm stuck already in my code :
Sub SaveMail()
SaveEmailAttachmentsToFolder "Meteologica SA Power Forecast", "csv", ""
End Sub
Sub SaveEmailAttachmentsToFolder(OutlookFolderInInbox As String, _
ExtString As String, DestFolder As String)
Dim ns As NameSpace
Dim Inbox As MAPIFolder
Dim SubFolder As MAPIFolder
Dim item As Object
Dim Att As Attachment
Dim FileName As String
Dim MyDocPath As String
Dim I As Integer
Dim wsh As Object
Dim fs As Object
Dim strAttachmentName As String
On Error GoTo ThisMacro_err
Set ns = GetNamespace("MAPI")
Set Inbox = ns.GetDefaultFolder(olFolderInbox)
Set SubFolder = Inbox.Folders(OutlookFolderInInbox)
I = 0
' Check subfolder for messages and exit of none found
If SubFolder.Items.Count = 0 Then
MsgBox "There are no messages in this folder : " & OutlookFolderInInbox, _
vbInformation, "Nothing Found"
Set SubFolder = Nothing
Set Inbox = Nothing
Set ns = Nothing
Exit Sub
End If
For Each item In SubFolder.Items
For Each Att In item.Attachments
If LCase(Right(Att.FileName, Len(ExtString))) = LCase(ExtString) And InStr(strAttachmentName, "906") > 0 Then
DestFolder = "C:\Users\Confi-005\OneDrive - confi.com\Desktop\Schedule\Mail_Temp\Download\"
FileName = DestFolder & item.SenderName & " " & Att.FileName
Att.SaveAsFile FileName
I = I + 1
End If
Next Att
Next item
If I > 0 Then
MsgBox "You can find the files here : " _
& DestFolder, vbInformation, "Finished!"
Else
MsgBox "No attached files in your mail.", vbInformation, "Finished!"
End If
ThisMacro_exit:
Set SubFolder = Nothing
Set Inbox = Nothing
Set ns = Nothing
Set fs = Nothing
Set wsh = Nothing
Exit Sub
ThisMacro_err:
MsgBox "An unexpected error has occurred." _
& vbCrLf & "Please note and report the following information." _
& vbCrLf & "Macro Name: SaveEmailAttachmentsToFolder" _
& vbCrLf & "Error Number: " & Err.Number _
& vbCrLf & "Error Description: " & Err.Description _
, vbCritical, "Error!"
Resume ThisMacro_exit
End Sub
Iterating over all items in the folder is not really a good idea:
For Each item In SubFolder.Items
For Each Att In item.Attachments
Instead, you need to use the Find/FindNext or Restrict methods of the Items class where you can deal only with items that correspond to the specified search criteria. You may find these methods described in depth in the articles that 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 may use the following search criteria to find items with a specific phrase in the Subject line:
criteria = "#SQL=" & Chr(34) _
& "urn:schemas:httpmail:subject" & Chr(34) _
& " ci_phrasematch 'question'"
Read more about that in the Filtering Items Using a String Comparison article.

Add email subject to file name when saving attachment

My goal is to extract the .png files of emails in the Outlook Inbox sub folder named Infuse Energy Daily Usage Reports.
The emails each contain six png files. The largest is the only one I need; it is exactly 37.6KB. The next largest file is 22.5KB. The third largest is 18.2KB.
The code mostly does what I need.
I want to add the full subject of the email to the beginning of the file name.
The file name should be:
"Email Subject, Creation Time ("yyyymmdd_hhnnss_"), Original File Name of PNG Image."
Sub SaveAttachmentsToFolder()
' This Outlook macro checks a named subfolder in the Outlook Inbox
' (here the "Infuse Eneregy Daily Usage Reports" folder) for messages with attached
' files of a specific type (here file with a "png" extension)
' and saves them to disk. Saved files are timestamped. The user
' can choose to view the saved files in Windows Explorer.
' NOTE: make sure the specified subfolder and save folder exist
' before running the macro.
On Error GoTo SaveAttachmentsToFolder_err
' Declare variables
Dim ns As NameSpace
Dim Inbox As MAPIFolder
Dim SubFolder As MAPIFolder
Dim Item As Object
Dim Atmt As Attachment
Dim FileName As String
Dim i As Integer
Dim varResponse As VbMsgBoxResult
Set ns = GetNamespace("MAPI")
Set Inbox = ns.GetDefaultFolder(olFolderInbox)
Set SubFolder = Inbox.Folders("Infuse Energy Daily Usage Reports") ' Enter correct subfolder name.
i = 0
' Check subfolder for messages and exit if none found
If SubFolder.Items.Count = 0 Then
MsgBox "There are no messages in the Infuse Energy Daily Usage folder.", vbInformation, _
"Nothing Found"
Exit Sub
End If
' Check each message for attachments
For Each Item In SubFolder.Items
For Each Atmt In Item.Attachments
' Check filename of each attachment and save if it has "png" extension
If Right(Atmt.FileName, 3) = "png" Then
' This path must exist! Change folder name as necessary.
FileName = "C:\Desktop\Energy Comparisons\Infuse Reports (from email)\" & _
Format(Item.CreationTime, "yyyymmdd_hhnnss_") & Atmt.FileName
Atmt.SaveAsFile FileName
i = i + 1
End If
Next Atmt
Next Item
' Show summary message
If i > 0 Then
varResponse = MsgBox("I found " & i & " attached files." _
& vbCrLf & "I have saved them into the Infuse Reports (from email)." _
& vbCrLf & vbCrLf & "Would you like to view the files now?" _
, vbQuestion + vbYesNo, "Finished!")
' Open Windows Explorer to display saved files if user chooses
If varResponse = vbYes Then
Shell "Explorer.exe /e,C:\Desktop\Energy Comparisons\Infuse Reports (from email)", vbNormalFocus
End If
Else
MsgBox "I didn't find any attached files in your mail.", vbInformation, "Finished!"
End If
' Clear memory
SaveAttachmentsToFolder_exit:
Set Atmt = Nothing
Set Item = Nothing
Set ns = Nothing
Exit Sub
' Handle Errors
SaveAttachmentsToFolder_err:
MsgBox "An unexpected error has occurred." _
& vbCrLf & "Please note and report the following information." _
& vbCrLf & "Macro Name: GetAttachments" _
& vbCrLf & "Error Number: " & Err.Number _
& vbCrLf & "Error Description: " & Err.Description _
, vbCritical, "Error!"
Resume SaveAttachmentsToFolder_exit
End Sub
First of all, there is no need to iterate over all items in a folder:
For Each Item In SubFolder.Items
For Each Atmt In Item.Attachments
This is not really a good idea because it can take a lot of time to iterate over all items in a folder. Instead, you need to use the Find/FindNext or Restrict methods of the Items class. Filter Example: [Attachment & Subject Like '%keyword%']
Filter = "#SQL=" & Chr(34) & "urn:schemas:httpmail:subject" & _
Chr(34) & " Like '%keyword%' AND " & _
Chr(34) & "urn:schemas:httpmail:hasattachment" & _
Chr(34) & "=1"
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
As for the filename of attachments saved to the disk, you need to make sure there are no forbidden symbols included to the filename before calling the SaveAsFile method.
If Right(Atmt.FileName, 3) = "png" Then
' This path must exist! Change folder name as necessary.
FileName = "C:\Desktop\Energy Comparisons\Infuse Reports (from email)\" & Item.Subject & _
Format(Item.CreationTime, "yyyymmdd_hhnnss_") & Atmt.FileName
Atmt.SaveAsFile FileName
i = i + 1
End If
Also be aware, an Outlook folder may contain different kind of items. I'd suggest checking the item's type at runtime to make sure you deal with mail items only. The Class property returns an OlObjectClass constant indicating the object's class. Or just use the following condition:
If TypeOf Item Is MailItem Then
' your code here
End If

How to search for appointments in Outlook via VBA/DASL?

I am trying to cycle through specific appointments of the current day and display their details in a msgbox.
I found out about DASL filter queries.
However, it only brings up the first appointment it finds. The FindNext method never steps to the next appointment, even though it came from an example I found on the web doing something very similar.
When I set the same DASL filter directly in Outlook, it shows the appointments as expected.
Here is my current sub:
Sub GetAppointments()
Dim sFilter As String
Dim oExplorer As Outlook.Explorer
Dim oFolder As Outlook.Folder
Dim oAppointment As Outlook.AppointmentItem
sFilter = "#SQL=" & _
"%today(""urn:schemas:calendar:dtstart"")% AND " & _
"%today(""urn:schemas:calendar:dtend"")% AND " & _
"""urn:schemas-microsoft-com:office:office#Keywords"" LIKE '%Meeting%'"
Set oExplorer = Application.ActiveExplorer
Set oFolder = oExplorer.CurrentFolder
Set oAppointment = oFolder.Items.Find(sFilter)
While TypeName(oAppointment) <> "Nothing"
MsgBox oAppointment.Subject & vbCr & _
oAppointment.Start & vbCr & _
oAppointment.End
Set oAppointment = oFolder.Items.FindNext
Wend
End Sub
You need to deal with the same Items collection if you want to get more results:
Dim appItems as Outlook.Items
Set appItems = oFolder.Items
Set oAppointment = appItems.Find(sFilter)
While TypeName(oAppointment) <> "Nothing"
MsgBox oAppointment.Subject & vbCr & _
oAppointment.Start & vbCr & _
oAppointment.End
Set oAppointment = appItems.FindNext
Wend
When you ask the Items property from a folder, a new Items instance is returned so further FindNext calls don't make any sense.
Read more about the Find/FindNext 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
Also you may want to include recurrence occurrences, in that case you need to set up a corresponding property on the Items collection:
Dim appItems as Outlook.Items
Set appItems = oFolder.Items
appItems.Sort "[Start]"
appItems.IncludeRecurrences = True
Set oAppointment = appItems.Find(sFilter)
While TypeName(oAppointment) <> "Nothing"
MsgBox oAppointment.Subject & vbCr & _
oAppointment.Start & vbCr & _
oAppointment.End
Set oAppointment = appItems.FindNext
Wend
The property returns a Boolean that indicates True if the Items collection should include recurrence patterns.
This property only has an effect if the Items collection contains appointments and is not sorted by any property other than Start in ascending order. The default value is False. Use this property when you want to retrieve all appointments for a given date, where recurring appointments would not normally appear because they are not associated with any specific date. If you need to sort and filter on appointment items that contain recurring appointments, you must do so in this order: sort the items in ascending order, set IncludeRecurrences to True, and then filter the items.

Copy Categories for Reply/Reply All and Forward

The common solution on the net is to make a registry edit for all Categories to be transferred from the incoming message to the Reply/Reply all and forward messages.
My office will not allow a registry change.
How can I accomplish the task to COPY and PASTE all of my assigned categories?
It is not usual to categorize outgoing mail. I believe most (maybe all) will find categories are removed upon sending.
Private Sub Categories_reply()
Dim currItem As mailItem
Dim repItem As mailItem
Set currItem = ActiveInspector.currentItem
Set repItem = currItem.reply
repItem.Categories = currItem.Categories
repItem.Display
repItem.Save
If repItem.Categories <> "" Then
MsgBox " Viewed in Drafts folder, with a categories column, you should see: " & _
vbCr & repItem.Categories & vbCr & vbCr & _
"The categories will likely be removed upon sending."
End If
End Sub

Why do I get Object doesn't support mailitem property SenderEmailAddress?

I want to save an attachment from a specific sender that has a specific file extension in the attachment. I'm having trouble with the If part of my loop. I receive Run-time error 438: Object doesn't support this property or method.
Sub GetAttachments()
Dim ns As NameSpace
Dim folder As Outlook.MAPIFolder
Dim Item As Object
Dim Atmt As Attachment
Dim FileName As String
Dim i As Integer
Set ns = GetNamespace("MAPI")
Set Inbox = ns.GetDefaultFolder(olFolderInbox)
i = 0
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
If Item.SenderEmailAddress = "email#domain.com" Then
For Each Atmt In Item.Attachments
' This path must exist! Change folder name as necessary.
If Right(Atmt.FileName, 3) = ".py" Then
FileName = "C:\Users\bill\Desktop\TEST\" & Atmt.FileName
Atmt.SaveAsFile FileName
i = i + 1
End If
Next Atmt
End If
Next Item
If i > 0 Then
MsgBox "I found " & i & " attached files." _
& vbCrLf & "I have saved them into the C:\Users\bill\Desktop\TEST folder." _
& vbCrLf & vbCrLf & "Have a nice day.", vbInformation, "Finished!"
Else
MsgBox "I didn't find any attached files in your mail." , vbInformation, "Finished!"
End If
GetAttachments_exit:
Set Atmt = Nothing
Set Item = Nothing
Set ns = Nothing
Exit Sub
GetAttachments_err:
MsgBox "An unexpected error has occurred." _
& vbCrLf & "Please note and report the following information." _
& vbCrLf & "Macro Name: GetAttachments" _
& vbCrLf & "Error Number: " & Err.Number _
& vbCrLf & "Error Description: " & Err.Description _
, vbCritical, "Error!"
Resume GetAttachments_exit
End Sub
The Folder can contain various types of items. Some of them don't provide the SenderEmailAddress property. Try to check out the item class first (or MessageCLass).
Also you may get a security issue if you automate Outlook from another application. See Outlook "Object Model Guard" Security Issues for Developers.
And don't interate over all items in the folder:
For Each Item In Inbox.Items
If Item.SenderEmailAddress = "email#domain.com" Then
You can use the Find/FindNext or Restrict methods of the Items class instead. 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 find the AdvancedSearch method of the Application class helpful. 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.
See this example
Filter = "[SenderEmailAddress] = 'email#domain.com'"
Set Items = Inbox.Items.Restrict(Filter)
ii = 0
For i = Items.Count To 1 Step -1
Set Item = Items.Item(i)
For Each Atmt In Item.Attachments
' This path must exist! Change folder name as necessary.
If Right(Atmt.FileName, 3) = ".py" Then
FilePath = "C:\Temp\"
FileName = Atmt.FileName
Atmt.SaveAsFile FilePath & FileName
ii = ii + 1
End If
Next Atmt
Next