Advanced Search of Messages for Multiple Words in Outlook 2016 - vba

I would like to do an Advanced Search of messages for multiple words without having to do each one manually/separately.
If I could record a macro in Outlook 2016, which as I understand it I cannot do, these are the commands I would do:
Record Macro
Click on Search Current Mailbox.
Click on Search Tools.
Click Advanced Search.
For the drop-down after “In:” choose “frequently-used text fields”.
Type into “Search for the word(s):” the text to find.
(Entering multiple words there means to find one string of all the words, not to find each word individually.)
Click Find Now.
Stop recording macro.
Edit the VBA code produced to:
Specify the rest of the words to find
Specify case-insensitive
Then do the Find Now.
Then I would like to display them all.
Preferably sorted, but not necessary if will make it more complicated code.
From piecing together code found online, I have the code below.
Subject is the only value I can use debug.print on. The others give errors:
"Run-time error '5': Invalid procedure call or argument".
I only know how to search on Subject.
I do not know how to display the list of what I have found (not in Immediate window).
' Test VBA for Multiple-Word Search in Outlook - 3
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 folder
Scope = "'" & Application.Session.GetDefaultFolder(olFolderInbox).FolderPath & "'"
Debug.Print Scope
'Establish filter
If Application.Session.DefaultStore.IsInstantSearchEnabled Then
Filter = Chr(34) & "urn:schemas:httpmail:subject" & Chr(34) & " ci_phrasematch 'Office'"
Debug.Print Filter
Filter = Chr(34) & "urn:schemas:httpmail:subject" & Chr(34) & " ci_phrasematch 'rent'" _
& " OR ""urn:schemas:httpmail:subject" & Chr(34) & " ci_phrasematch 'breaking'"
'(subject:invoice OR body:invoice) AND hasattachments:yes NOT from:Amazon
Else
Filter = Chr(34) & "urn:schemas:httpmail:subject" & Chr(34) & " like '%Office%'"
End If
Debug.Print Filter
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("SentOnBehalfOf")
' Debug.Print nextRow("From")
' Debug.Print nextRow("ReceivedTime")
Debug.Print nextRow("Subject")
Loop
End Sub

Outlook doesn't provide a macro recorder like Word or Excel. You need to create a VBA macro manually in Outlook.
The Advanced search in Outlook programmatically: C#, VB.NET article explains how to use the AdvancedSearch method in Outlook.
The Outlook Object Model also provides the AdvancedSearchStopped event. The signatures of the AdvancedSearchStopped and AdvanvedSearchComplete event handlers are the same. The AdvancedSearchStopped event is triggered when the Stop method is called on the Search object to cancel searching. However, the AdvancedSearchComplete is called afterwards anyway.
Admittedly, the Search class allows you to save the results of searching in a search folder (actually, it doesn’t contain any items, only references to items from the scope folders). You just need to call the Save method on the Search object in the AdvanvedSearchComplete event handler.
For example, in VBA you can define the AdvanvedSearchComplete event handler in the following way:
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

Related

VSTO Outlook: AddToSelection(mailItem) does not work

I have written an Outlook-VSTO-Add-In in VB to solve the issue that in Quick search results, Outlook does not tell you in which folder a found item lives.
My Add-In adds two items to the explorer's context menu:
Name of containing folder to display the name of the parent folder of the selected item
Goto containing folder to change the current folder to the selected item's parent folder
This works all as desired.
Now, I'd like to extend the functionality of 2. so that when Outlook changes to the item's parent folder the item is automatically selected.
myOutlook.ActiveExplorer.AddToSelection(mailItem) should do the thing, but unfortunately the adding operation fails; the selection count myOutlook.ActiveExplorer.Selection.Count is not changed. Any help would be much appreciated.
My environment:
Windows 10
Outlook für Office 365 MSO (16.0.12527.21296) 64 Bit
Microsoft Visual Studio Community 2019, Version 16.8.3
Full project (as *.sln) is available on GitHub here
Code extract:
Public Sub GetButtonID(control As Office.IRibbonControl)
Dim myOutlook As Outlook.Application = Globals.ThisAddIn.Application
System.Diagnostics.Debug.WriteLine("Count: " & CStr(myOutlook.ActiveExplorer.Selection.Count))
If myOutlook.ActiveExplorer.Selection.Count > 0 Then
Dim mailItem As Outlook.MailItem
mailItem = DirectCast(myOutlook.ActiveExplorer.Selection(1), Outlook.MailItem)
Select Case control.Id
Case "FolderDisplay"
Dim messageBoxText As String = "The item is in folder:" & vbLf & vbLf _
& DirectCast(mailItem.Parent, Outlook.Folder).FolderPath
MsgBox(messageBoxText, 0, "In which folder?")
Case "FolderGoto"
System.Diagnostics.Debug.WriteLine("Current: " & TryCast(myOutlook.ActiveExplorer.CurrentFolder.Name, String) _
& myOutlook.ActiveExplorer.Selection.Count) ' yields <1>
' Change the current folder:
myOutlook.ActiveExplorer.CurrentFolder = DirectCast(mailItem.Parent, Outlook.Folder)
System.Diagnostics.Debug.WriteLine("Current: " & TryCast(myOutlook.ActiveExplorer.CurrentFolder.Name, String) _
& myOutlook.ActiveExplorer.Selection.Count) ' yields <0>
System.Windows.Forms.Application.DoEvents() ' needed, otherwise the mailItem will not be selectable
' Additionally, set the initial mailItem as selected:
' But this doesn't work for unknown reasons.
' .AddToSelection() does not work for unknown reasons.
If myOutlook.ActiveExplorer.IsItemSelectableInView(mailItem) Then
' https://social.msdn.microsoft.com/Forums/en-US/aedcbda9-5304-4969-82ac-dbd41e0879b0/select-item-in-activeexplorer?forum=outlookdev
System.Diagnostics.Debug.WriteLine("Selectable!")
System.Diagnostics.Debug.WriteLine("Adding0: " & myOutlook.ActiveExplorer.Selection.Count) ' <0> is OK
System.Diagnostics.Debug.WriteLine(CStr("Item: " & mailItem.Subject)) ' verify, that the element is the desired one
myOutlook.ActiveExplorer.AddToSelection(mailItem) ' This FAILS !
System.Diagnostics.Debug.WriteLine("Adding1: " & myOutlook.ActiveExplorer.Selection.Count) ' should be <1> but is still <0> :-(
System.Windows.Forms.Application.DoEvents() ' thought in vain that this would help
System.Diagnostics.Debug.WriteLine("Adding2: " & myOutlook.ActiveExplorer.Selection.Count) ' should be <1> but is still <0> :-(
System.Diagnostics.Debug.WriteLine("Which Folder? = " & myOutlook.ActiveExplorer.CurrentFolder.Name)
Else
System.Diagnostics.Debug.WriteLine("Not selectable!")
End If
End Select
End If
End Sub

How do I create a script to remove the body of incoming emails?

I am trying to create a script that I can use with a message rule, to remove the body of incoming emails. Ideally I would like to leave the first 20 characters intact and delete the rest of the email, but I would settle for deleting the entire contents.
I thought this would be simple macro but I have found it impossible to achieve exactly what you requested; however, I have achieved something close. I have not deleted my diagnostic code so you can experiment yourself and perhaps discover a sequence of statements that I have not tried.
This is the macro that makes the changes:
Public Sub ReduceBody(ItemCrnt As Outlook.MailItem)
Dim ReducedBody As String
With ItemCrnt
' Not all items in Inbox are mail items. It should not be possible for
‘ a non-mail-item to reach this macro but check just in case.
If .Class = olMail Then
' I test for a particular subject and a particular sender
' Many properties of a mail item can be checked in this way. Adjust
' the If statement as necessary
If LCase(.Subject) = "attachments" And _
LCase(.SenderEmailAddress) = "xxxxx.com" Then
Debug.Print "Html: [" & Replace(Replace(.HtmlBody, vbLf, "{l}"), vbCr, "{r}") & "]"
Debug.Print "Text: [" & Replace(Replace(.Body, vbLf, "{l}"), vbCr, "{r}") & "]"
Debug.Print "Format: " & .BodyFormat
Debug.Assert False ' Have a look at the initial values of the properties
' Save reduced body because clearing the Html body also clears the text body
ReducedBody = Left$(.Body, 20)
.BodyFormat = olFormatPlain ' Set body format to plain text
.HtmlBody = "<BODY>" & ReducedBody & "</BODY>"
Debug.Print "Html: [" & .HtmlBody & "]"
Debug.Print "Text: [" & .Body & "]"
Debug.Print "Format: " & .BodyFormat
Debug.Assert False ' Have a look at the new values of the properties
.Close (olDiscard) ' Delete when the new
Exit Sub ‘ values are as you require
.Save ' Save amended mail item
End If
End If
End With
End Sub
I believe my comments explain the structure of the macro adequately.
Once the macro has confirmed that the item it has been passed is one it should process, it outputs the current values of the Html body, the text body and the body format to the Immediate Window and uses Debug.Assert to stop processing. Click F5 when you are ready to continue.
The code modifies these three properties, displays their new values and stops again.
I have known for a long time that Outlook will build a text body from an Html body but I had not realised how linked the Html body, the text body and the body format are. Changing any of them changes the others. The modification code I have provided, is the best I have been able to create which is:
Text body = first 20 characters of original text body
Html body = “” & first 20 characters of original text body & “”
Body format = Html
When you restart the macro with F5, the changes will be discarded. Unless the changes are discarded, they will be saved even if you do not execute the save command. Keep the discard statements until the values displayed are acceptable.
To test the above macro, I used:
Sub TestReduceBody()
Dim Exp As Explorer
Dim ItemCrnt As MailItem
Set Exp = Outlook.Application.ActiveExplorer
If Exp.Selection.Count = 0 Then
Call MsgBox("Please select one or more emails then try again", vbOKOnly)
Exit Sub
Else
For Each ItemCrnt In Exp.Selection
Call ReduceBody(ItemCrnt)
Next
End If
End Sub
I use a macro like this to test all my new mail-item, processing macros. Select one or more mail items and then start this macro. This macro allows me to start with a simple email and, only when that is processed correctly, do I try more complicated emails. I have several email addresses and I sent suitable test emails from a secondary account to my main account. You will have genuine emails ready to test. I highly recommend using macros like this.
Once you have amended the first macro to your requirements, set up a rule and link the rule to this macro. I assume you know how to create a rule, but I can provide instructions if necessary.

Insert String into Body of received email

I wish to take the body of the selected received email (in folder view or as a selected email) and add an action stub to the beginning e.g.…
.Body = StubString & 'Body
I am uncertain how to use ActiveInspector and mailitem etc.
I have looked through the list of answered questions but I can't find one that will help me out.
Warnings:
Few of the emails I receive have a text body (property .Body) that I would wish to view or amend for someone else to view. Most have an html body (property .HTMLBody). If there is a text body, it is a crude simplification of the html body.
The email packages I use only show the text body if there is no html body. Amending the text body would have no effect on the display unless you delete the html body.
Between them, I believe the two answers and the macro below will give all the background you need to create your macro.
This answer of mine is a tutorial taking the reader through the Outlook Object model with the example macros you find most helpful. You should probably skip this now and come back later because I believe the second answer is closer to your requirement. Update excel sheet based on outlook mail
This second answer demonstrates how to create an Excel worksheet and copy selected properties of a mail item to it so the user can see what a mail item looks like to a VBA program. As written, the macro outputs details of every mail item in the Inbox. Comments within it, show how to limit the macro's output to selected emails so you can examine the content of the mail items you wish to amend. How to copy Outlook mail message into excel using VBA or Macros
Both the above answers examine all mail items in a selected folder. If you select a few mail items then run the macro below, you will get selected properties of those mail items.
Public Sub DemoExplorer()
Dim Exp As Outlook.Explorer
Dim ItemCrnt As MailItem
Dim NumSelected As Long
Set Exp = Outlook.Application.ActiveExplorer
NumSelected = Exp.Selection.Count
If NumSelected = 0 Then
Debug.Print "No emails selected"
Else
For Each ItemCrnt In Exp.Selection
With ItemCrnt
Debug.Print "--------------------------"
Debug.Print "From: " & .SenderName
Debug.Print "Subject: " & .Subject
Debug.Print "Received: " & Format(.ReceivedTime, "dMMMyy h:mm:ss")
'Debug.Print "Text " & Replace(Replace(Replace(.Body, vbLf, "{lf}"), vbCr, "{cr}"), vbTab, "{tb}")
'Debug.Print "Html " & Replace(Replace(Replace(.HTMLBody, vbLf, "{lf}"), vbCr, "{cr}"), vbTab, "{tb}")
End With
Next
End If
End Sub

How to list DataMacro objects in an Access database?

Is it possible to programmatically enumerate the Data Macros in an Access 2010+ database? If so, how?
Note: Data Macros are trigger-like procedures that are created in the context of the table designer UI. They were new in Acces 2010. They are NOT the same thing as normal macros, which are easy to enumerate.
They have their own new AcObjectType enumeration value : acTableDataMacro, but I can find no other aspect of the Access or DAO object model that refers to them. They do not even appear in the MSysObjects table.
This code will export DataMacro metadata to an XML Document (Source):
Sub DocumentDataMacros()
'loop through all tables with data macros
'write data macros to external files
'open folder with files when done
' click HERE
' press F5 to Run!
' Crystal
' April 2010
On Error GoTo Proc_Err
' declare variables
Dim db As DAO.Database _
, r As DAO.Recordset
Dim sPath As String _
, sPathFile As String _
, s As String
' assign variables
Set db = CurrentDb
sPath = CurrentProject.Path & "\"
s = "SELECT [Name] FROM MSysObjects WHERE Not IsNull(LvExtra) and Type =1"
Set r = db.OpenRecordset(s, dbOpenSnapshot)
' loop through all records until the end
Do While Not r.EOF
sPathFile = sPath & r!Name & "_DataMacros.xml"
'Big thanks to Wayne Phillips for figuring out how to do this!
SaveAsText acTableDataMacro, r!Name, sPathFile
'have not tested SaveAsAXL -- please share information if you do
r.MoveNext
Loop
' give user a message
MsgBox "Done documenting data macros for " & r.RecordCount & " tables ", , "Done"
Application.FollowHyperlink CurrentProject.Path
Proc_Exit:
' close and release object variables
If Not r Is Nothing Then
r.Close
Set r = Nothing
End If
Set db = Nothing
Exit Sub
Proc_Err:
MsgBox Err.Description, , _
"ERROR " & Err.Number _
& " DocumentDataMacros"
Resume Proc_Exit
Resume
End Sub
EDIT: Gord pointed out that you wanted the DataMacros opposed to standard macros. I found some code and tested it (it works) here
I tested the top function when you follow that link and it saves information regarding your table macros for each table in an XML document. It works nicely, props to whoever wrote it.

FileDialog in Word 2011 VBA

I'm hoping for a bit of a sanity check. I'm adapting a Word add-in (written in VBA for Word 2010) for Mac, specifically, at this point, Word 2011. I'm aware of many of the differences, but one that I haven't been able to find much documentation on is the apparent lack of FileDialog. The closest I've come to an answer is here: http://www.rondebruin.nl/mac.htm where the author uses Application.GetOpenFilename. That method doesn't seem to exist for Word, though (the focus of that site is Excel).
Does anyone know how to use the file and folder picker dialogs that FileDialog makes available? I'm not familiar with Applescript, really, but I've had to learn a little in order to get around Word 2011's funky file management issues (Dir, FileCopy, etc.). So, if that's the answer, any sense of what the code might look like in Applescript would be greatly appreciated. (I more or less know how to translate that into VBA).
I believe you have to use Apple Script in order to do this a bit better on the Mac. The following code allows the user to select text files which is returned as an array from the function. You would simply be able to modify the Apple Script to return other file types and select directories, I'll leave that to you.
The code that calls the function and displays a message box with all the files:
Sub GetTextFilesOnMac()
Dim vFileName As Variant
'Call the function to return the files
vFileName = Select_File_Or_Files_Mac
'If it's empty then the user cancelled
If IsEmpty(vFileName) Then Exit Sub
'Loop through all the files specified
For ii = LBound(vFileName) To UBound(vFileName)
MsgBox vFileName(ii)
Next ii
End Sub
And the function that does the Apple Script work:
Function Select_File_Or_Files_Mac() As Variant
'Uses AppleScript to select files on a Mac
Dim MyPath As String, MyScript As String, MyFiles As String, MySplit As Variant
'Get the documents folder as a default
On Error Resume Next
MyPath = MacScript("return (path to documents folder) as String")
'Set up the Apple Script to look for text files
MyScript = "set applescript's text item delimiters to "","" " & vbNewLine & _
"set theFiles to (choose file of type " & " {""public.TEXT""} " & _
"with prompt ""Please select a file or files"" default location alias """ & _
MyPath & """ multiple selections allowed true) as string" & vbNewLine & _
"set applescript's text item delimiters to """" " & vbNewLine & _
"return theFiles"
'Run the Apple Script
MyFiles = MacScript(MyScript)
On Error GoTo 0
'If there are multiple files, split it into an array and return the results
If MyFiles <> "" Then
MySplit = Split(MyFiles, ",")
Select_File_Or_Files_Mac = MySplit
End If
End Function
Finally, it can be a bit of a pain specifying different file types, if you want to specify only Word documents, then replace public.TEXT with com.microsoft.word.doc, however this won't allow .docx or .docm files. You need to use org.openxmlformats.wordprocessingml.document and org.openxmlformats.wordprocessingml.document.macroenabled respectively for these. For more info on these see: https://developer.apple.com/library/mac/#documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html