VBA Excel - How to reply all with email addresses, not just names - excel

I'm making a VBA userform that replies to the active Outlook email with a template (different templates based on listbox choices). The problem right now is that when I "reply all" it is grabbing just the first and last name of the sender and recipients.
The senders are primarily outside the company, so I need it to grab and populate the "To" field with the actual email addresses. If it were only in-company the users would be in the company directory and it wouldn't be an issue. The closest I've come to finding this is the answer to How do you extract email addresses from the 'To' field in outlook?. I feel like the information I need is available there (only explicitly deals with grabbing info for recipients but I figure the same principle will apply to the sender), but I can't make sense of how to insert it into my code for the desired result.
Here's what I am starting from:
Private Sub CommandButton1_Click()
Dim origEmail As MailItem
Dim replyEmail As MailItem
Set origEmail = ActiveExplorer.Selection(1)
Set replyEmail = CreateItemFromTemplate("C:\Download Tool\Need Stat Code X.oft")
replyEmail.To = origEmail.ReplyAll.To
replyEmail.HTMLBody = replyEmail.HTMLBody & origEmail.Reply.HTMLBody
replyEmail.SentOnBehalfOfName = "emailaddress#mycompany.com"
replyEmail.Display
Set origEmail = Nothing
Set replyEmail = Nothing
End Sub
The emails are populating and I'm getting nearly all the info I want, but I haven't found a clear explanation of how to grab & insert the email addresses.
Thanks for your time and advice!

If you put a stop after "Set origEmail = ..." and set a watch on origEmail, you will see the properties of the email. Included is the Recipients collection. There are two (that I can see) types, SMTP and EX. For me, EX means internal. in each item in the recipients items is a property called address and another called addressentry. The addressentry part contains the address type.
deep breath
OK, so you need to be able to convert the EX addresses into internal addresses by parsing the part at the end, and you can just put the SMTP ones in as is, I think. Build a string of the addresses in the recipients address list and put it in the To and/or CC fields and you should be good. The To or CC part is the recipients (n).type property...
I think.
Gosh, I hope someone posts an easier way to do it :)

Thanks to both #hrothgar and #Tony for the responses. I learned about some new tools and techniques from each. Ultimately, based on the info found in your responses, I ended up searching for "vba get recipients string" and finding Get item.recipient with Outlook VBA .
The code now works and looks like this:
Private Sub CommandButton1_Click()
Dim origEmail As MailItem
Dim replyEmail As MailItem
'new stuff
Dim recip As Recipient
Dim allRecips As String
'end new stuff
Set origEmail = ActiveExplorer.Selection(1)
Set replyEmail = CreateItemFromTemplate("C:\Template Placel\My Template.oft")
'more new stuff
For Each recip In origEmail.Recipients
If (Len(allRecips) > 0) Then allRecips = allRecips & "; "
allRecips = allRecips & recip.Address
Next
'end more new stuff
replyEmail.To = origEmail.SenderEmailAddress & "; " & allRecips 'updated to find sender email and
replyEmail.HTMLBody = replyEmail.HTMLBody & origEmail.Reply.HTMLBody
replyEmail.SentOnBehalfOfName = "inbox#company.com"
replyEmail.Display
Set origEmail = Nothing
Set replyEmail = Nothing
End Sub
The internal contacts look a bit ugly in the "To" line, but I've tested and it gets the job done.
Thanks for the help!

Related

Excel VBA - Outlook Email into Excel

I've done a fair bit of searching but everything I've come up with is doing the opposite of what I'm trying to do.
I have a whole bunch of automatically generated emails that I get, and I want to translate them down into excel. Everything works, except that it dumps it exclusively into one cell. I would like this to have multiple rows of the email come through as multiple lines in excel.
For example, email body is this. This will have a variable number of rows, so I can't really just use Mid functions.
Hello,
Job AAA completed successfully.
ThingA1 = good
ThingA2 = error code 5
This entire string shows up under cell A2 (which, is kinda what I told it to do...but I have no idea how to tell it to put it as multiple IDs). I want it to show up as different cells (covering cells A2:A6 in this instance).
Sub ParseAllEmails()
'loop through the outlook inbox, find stuff with errors, parse/paste it in
Dim OutApp As Outlook.Application, OLF As Outlook.MAPIFolder, OutMail As Outlook.MailItem
Dim myReport As Boolean, zeroErrors As Boolean
Dim parseSht As Worksheet
Dim i As Long
'establish connection
Set OutApp = CreateObject("Outlook.Application")
Set OLF = OutApp.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
Set parseSht = ThisWorkbook.Sheets("parse")
'go through inbox looking for scheduler emails
For i = OLF.Items.Count To 1 Step -1
If TypeOf OLF.Items(i) Is MailItem Then
Set OutMail = OLF.Items(i)
myReport = (LCase(Left(OutMail.Subject, 3)) = "job")
zeroErrors = (InStr(1, LCase(OutMail.Subject), "errors=0") > 0)
If myReport And Not zeroErrors Then
parseSht.Range("A2:A500").Value = Trim(OutMail.Body)
Exit Sub
End If
End If
Next
End Sub
First of all, I'd suggest replacing the following part where the code iterates over all items in the Inbox folder:
'go through inbox looking for scheduler emails
For i = OLF.Items.Count To 1 Step -1
If TypeOf OLF.Items(i) Is MailItem Then
Set OutMail = OLF.Items(i)
myReport = (LCase(Left(OutMail.Subject, 3)) = "job")
zeroErrors = (InStr(1, LCase(OutMail.Subject), "errors=0") > 0)
If myReport And Not zeroErrors Then
Use the Find/FindNext or Restrict methods of the Items class which allow getting items that correspond to your conditions only. All you need is to iterate over the result collection and process such items after. 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
To break the single message body string into separate lines you could use the Slit function available in VBA:
Dim strings() As String
strings = Split(mailItem.Body, vbNewLine)
So, you can detect the data which is required to be pasted and process these lines in the loop by adding each entry into a separate cell (if required).

Use VBA to #-Mentions a user in a Threaded Comment in excel

I added a UserForm into my file so comments can be added to a cell (that way I can update the data on other cells when a comment is added and someone is mentioned).
So far I can get the comment entered with no issues. But I canĀ“t find a way to #mention a user so the notification is sent. Does anyone know if this is manageable with VBA?
Range("A1").AddCommentThreaded ("Comment text")
Answer
By reading the documentation the method is not likely implemented in VBA and seems only a front end to Excel, but not visible to VBA itself. The only property that I found was "resolved" (which is not mentioned in the documentation of the object itself), but there is not a way to "resolve" it per say.
VBA does not resolve the user (even if it's correctly written) and most likely there is no native way to do so.
Workaroud
Your only solution would be to implement it by yourself: according to your question, since you are using an UserForm I would append something like this
Add the reference for outlook (you may use late binding, but I rather to add the references as it is better IMHO)
In a module, add the following:
Function Return_TxtFoundContact() As String
Dim ObjNamesDialog As Outlook.SelectNamesDialog
Set ObjNamesDialog = Outlook.Session.GetSelectNamesDialog
Dim ObjAddressEntry As Outlook.AddressEntry
With ObjNamesDialog ' 1. With ObjNamesDialog
.Caption = "Select contact to mention & notify": .ToLabel = "Mention:"
.NumberOfRecipientSelectors = olShowTo: .AllowMultipleSelection = False 'although with this setting it lets user to select more than one recipient
If .Display Then ' 1. If .Display
TxtEntryID = .Recipients(1).EntryID: Set ObjAddressEntry = Outlook.Application.Session.GetAddressEntryFromID(TxtEntryID)
Return_TxtFoundContact = ObjAddressEntry.GetExchangeUser.PrimarySmtpAddress
End If ' 1. If .Display
End With ' 1. With ObjNamesDialog
Set ObjAddressEntry = Nothing: Set ObjNamesDialog = Nothing
End Function
Sub Test()
Call Exec_SendNotificationMentionMail("sample#domain.com", Range("E4"))
End Sub
Sub Exec_SendNotificationMentionMail(TxtEmailToSendTo As String, RangeCommentIs As Range)
Dim AppOutlook As Outlook.Application
Set AppOutlook = New Outlook.Application
Dim ObjMailItem As Outlook.MailItem: Set ObjMailItem = AppOutlook.CreateItem(olMailItem)
With ObjMailItem
ObjMailItem.To = TxtEmailToSendTo
'since you may have many users under outlook, I rather to get the application username, however you may go to https://learn.microsoft.com/en-us/office/vba/api/outlook.namespace.currentuser
'to see how to get the username by outlook or use Environ("Username"), varies per needs/company to get the desired outcome
ObjMailItem.Subject = Application.UserName & " mentioned you in '" & ThisWorkbook.Name & "'"
'If you wish, format it as microsoft would do, just research on how to give format to the htmlbody on outlook, for simplicity I just add the basic
ObjMailItem.HTMLBody = Application.UserName & " mentioned you at: " & RangeCommentIs.Address(False, False) & Chr(10) & RangeCommentIs.CommentThreaded.Text
'for debug purposes, display it, once you have verified it works as you would like, comment the line
.Display
'Once you have verified it works as intended, uncomment this
'.Send
End With
'Once you have verified it works as intended, uncomment this
'Set ObjMailItem = Nothing: Set AppOutlook = Nothing
End Sub
In your userform, add a textbox where, upon double clicking, user agenda (per code above) would show up to select from the directory the person being mentioned
Private Sub TextBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
Dim TxtFoundContact As String
TxtFoundContact = Return_TxtFoundContact
TextBox1 = TxtFoundContact
End Sub
Finally, on your userform implementation when they hit "OK" or when your userform appends the comment to a mail and send it using the routine.
OT: This method may be more useful than the actual one, you may select users that the workbook has not been shared with, if they get mentioned, but they do not have access yet, they can request it (I think the communication process will be faster with this). I am not quite sure if the original implementation allows it, but if needed, multiple people can be notified under the same mail too, you just need to adjust the code above to do so.

To check if you have replied to client email via vba

Greetings for the day!
I have written a small VBA code to check if my team has responded to the client's email or not. on daily basis we get approx 500+ emails from the client, to track the same I have written the below code to check what all emails are being looked upon.
Dim O As Outlook.Application
Dim R As Long
Sub project2()
Set O = New Outlook.Application
Dim Omail As Outlook.MailItem
Set Omail = O.CreateItem(olMailItem)
Dim ONS As Outlook.Namespace
Set ONS = O.GetNamespace("MAPI")
Dim FOL As Outlook.Folder
Set FOL = ONS.GetDefaultFolder(olFolderInbox).Folders("MD-GPS")
R = 2
For Each Omail In FOL.Items
Cells(R, 1) = Omail.Subject
Cells(R, 2) = Omail.SenderEmailAddress
Call REPLY_STATUS(Omail.Subject, Omail.SenderEmailAddress)
R = R + 1
On Error Resume Next
Next Omail
End Sub
Sub REPLY_STATUS(MailSubject As String, MailSender As String)
Dim SentEmail As Outlook.MailItem
Set SentEmail = O.CreateItem(olMailItem)
Dim ONS2 As Outlook.Namespace
Set ONS2 = O.GetNamespace("MAPI")
Dim FOL2 As Outlook.Folder
Set FOL2 = ONS2.GetDefaultFolder(olFolderSentMail)
Dim check As String
check = "RE: " & MailSubject
For Each SentEmail In FOL2.Items
If check = SentEmail.Subject And MailSender = SentEmail.Recipients(1).Address Then
Cells(R, 3) = "Yes"
Exit For
Else
End If
On Error Resume Next
Next SentEmail
End Sub
But the ending is not that great as it looks, the code is working but
in most cases, the code captures something else rather than capturing the sender's email address in an excel sheet.
As we daily receive 500+ emails, the code becomes too slow as it checks the entire folder from the scratch, is there a possibility I can add a start date that I can mention in the excel sheet and the code will start from that date only.
Not sure why it is also not filling column 3 i.e. if replied however my team has actually replied to those emails.
it is not picking up the latest emails from the defined sub-folder ("MD-GPS"), why is that happening?
Any help on this would be greatly appreciated.
Note: To handle stmp exchange account error, I tried using the following MailItem.Sender.GetExchangeUser.PrimarySmtpAddress but the only drawback is if I change the sub-folder to something else, it doesn't work.
Firstly, you do not need to create SentEmail - get rid of the
Set SentEmail = O.CreateItem(olMailItem)
line.
Secondly, never loop through all items in a folder - use Items.Find/FindNext or Items.Restrict.
Thirdly, you are seeing an EX type address (as opposed to SMTP). Check MailItem.Sender.Type property - if it is "SMTP", use MailItem.Sender.Address. Otherwise (in case of "EX") use MailItem.Sender.GetExchangeUser().PrimarySmtpAddress.
That being said, you can check if anybody replied to the original message at all - check if PR_LAST_VERB_EXECUTED MAPI property (DASL name http://schemas.microsoft.com/mapi/proptag/0x10810003) is present - 103 is EXCHIVERB_REPLYTOALLand 102 is EXCHIVERB_REPLYTOSENDER. If the property is not set at all, there is no reason to search.
To search for a matching subject, use a query like
"[Subject] = ' & MailSubject & "'"
Note that Outlook Object Model will not let you search on the message recipients or attachments. If using Redemption (I am its author) is an option, you can use something like the following. You can specify Recipients as one of the search fields, and Redemption will create a restriction on recipient name / address / SMTP address
set session = CreateObject("Redemption.RDOSession")
session.MAPIOBJECT = O.Session.MAPIOBJECT
set SentEmail = FOL2.Items.Find("""http://schemas.microsoft.com/mapi/proptag/0x0E1D001F"" = '" & MailSubject & "' AND Recipients = '" & MailSender & "'")
Note that most MAPI stores won't search on PR_SUBJECT, only on PR_NORMALIZED_SUBJECT (which is the subject without the RE: or FW: prefix) - which is what the snippet above is using.

Drive Outlook Through Excel

I want to create a meeting invite only to book a room in outlook through Excel.
this is the code I am using,
Private Sub CommandButton2_Click()
Set myOutlook = CreateObject("Outlook.Application")
Set myApt = myOutlook.CreateItem(1)
myApt.Subject = "Training"
myApt.Start = Now
myApt.RequiredAttendees = "B 101-Training Room-24 <B101-TrainingRoom-24.IOC#cummins.com>"
myApt.End = Now + 30
myApt.MeetingStatus = olMeeting
myApt.send
MsgBox ("Meeting Invite Sent")
Set myOutlook = Nothing
Set myApt = Nothing
End Sub
this code doesnot create any meeting invite, but doesnot give error as well.
Can please tell what could be the missing thing?
Thanks.
I think you'd better use recipients, trying this
myApt.Recipients.Add("B 101-Training Room-24 <B101-TrainingRoom-24.IOC#cummins.com>")
The RequiredAttendees property only contains the display names for the required attendees. The attendee list should be set by using the Recipients collection.
Resources are usually added as BCC recipients:
set recip = myApt.RequiredAttendees("B 101-Training Room-24 <B101-TrainingRoom-24.IOC#cummins.com>")
recip.Type = 3 'olBCC
recip.Resolve
Do you see the meeting request in the Sent Items folder?
the code given adds all the fields except attendees. I want to have 'Invite attendees' option coded.
Set recip = myApt.RequiredAttendees("B 101-Training Room-24 <B101-TrainingRoom-24.IOC#cummins.com>")
is there any other way to add the invitees? FYI, I am using Outlook 2013.

How to avoid the Outlook security warning for email automation?

I'm trying to send an Outlook email from Excel 2010 using VBA.
Most answers on Stack Overflow don't seem to have a method of using VBA to avoid the Outlook security warning, nor for Outlook/Excel 2010.
Do any free methods exist? The Redemption method won't be a viable option, unless it is easy to install on 10 machines in a large company.
How I send emails:
Dim emailAddr As String
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = "xxxx#xxxx.edu"
.Subject = "Demande"
.HtmlBody = CombinedValueHtml
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
Exit Sub
This is a partial answer. I have made it a Community Wiki answer in the expectation that someone else can explain the final part which I cannot get to work.
This web page, http://msdn.microsoft.com/en-us/library/office/aa155754(v=office.10).aspx , explains the first three parts of the process. It was written in 1999 so cannot be followed exactly because it refers to old versions of Windows and Office.
The first step is to add Digital Signature for VBA Projects to your Office installation although I found it under Shared Tools rather than Office Tools. Don't make the mistake of just adding Digital Signature for VBA Projects to Outlook because, as I discovered, that means you uninstall Word, Excel, etc.
The second step is to run Selfcert.exe to create a digital certificate in your own name.
The third step is to open Outlook's VBA editor, select Tools then Digital Certificate then Choose to sign the project with your certificate.
With these steps you can suppress the warning that Outlook contains macros but this does not suppress that warning that a macro is accessing emails. To suppress that warning, you need a fourth step which is to place your certificate within the Trusted Root Certificate Authorities Store. This web page http://technet.microsoft.com/en-us/library/cc962065.aspx explains about the Certification Authority Trust Model but I cannot successfully use Microsoft Management Console to achieve the fourth step.
Instead .send use the following:
.Display 'displays outlook email
Application.SendKeys "%s" 'presses send as a send key
note: be careful when using display keys, if you move the mouse and click while the program is running it can change whats going on. also outlook will display on ur screen and send.. if you working on something else's and this bothers you, yea.. not the best idea
The Redemption method won't be a viable option, unless it is easy to
install on 10 machines inside of a large company.
You can use RedemptionLoader (I am its author) - it loads the dll directly and does no require the dll to be installed using the registry.
It is either Extended MAPI in C++ or Delphi, Redemption (I am its author - wraps Extended MAPI and can be used form any language) or a utility like ClickYes.
If you don't send the message immediately but just display it and let the user do modifications (if any) and let them press the send button theirselves, this would work:
i.e. use
.Display
instead of
.Send
I explained how you can use vba to send emails in this answer You will find a macro that I use extensively in my daily work.
Following recomendations from #Floern, here is the explanation:
In order to insert images (signature as images) you could use the following code:
Step 1. Copy this code an paste in class module and name that class module like "MailOptions"
Private Message As CDO.Message
Private Attachment, Expression, Matches, FilenameMatch, i
Public Sub PrepareMessageWithEmbeddedImages(ByVal FromAddress, ByVal ToAddress, ByVal Subject, ByVal HtmlContent)
Set Expression = CreateObject("VBScript.RegExp")
Expression.Pattern = "\<EMBEDDEDIMAGE\:(.+?)\>"
Expression.IgnoreCase = True
Expression.Global = False 'one match at a time
Set Message = New CDO.Message
Message.From = FromAddress
Message.To = ToAddress
Message.Subject = Subject
'Find matches in email body, incrementally increasing the auto-assigned attachment identifiers
i = 1
While Expression.Test(HtmlContent)
FilenameMatch = Expression.Execute(HtmlContent).Item(0).SubMatches(0)
Set Attachment = Message.AddAttachment(FilenameMatch)
Attachment.Fields.Item("urn:schemas:mailheader:Content-ID") = "<attachedimage" & i & ">" ' set an ID we can refer to in HTML
Attachment.Fields.Item("urn:schemas:mailheader:Content-Disposition") = "inline" ' "hide" the attachment
Attachment.Fields.Update
HtmlContent = Expression.Replace(HtmlContent, "cid:attachedimage" & i) ' update the HTML to refer to the actual attachment
i = i + 1
Wend
Message.HTMLBody = HtmlContent
End Sub
Public Sub SendMessageBySMTP(ByVal SmtpServer, ByVal SmtpUsername, ByVal SmtpPassword, ByVal UseSSL)
Dim Configuration
Set Configuration = CreateObject("CDO.Configuration")
Configuration.Load -1 ' CDO Source Defaults
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = SmtpServer
'Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = SmtpPort
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 30
If SmtpUsername <> "" Then
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = SmtpUsername
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = SmtpPassword
End If
Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = UseSSL
Configuration.Fields.Update
Set Message.Configuration = Configuration
Message.Send
End Sub
Step 2. In an standar module you will elaborate your .html content and instantiate a object from the class:
public sub send_mail()
Dim signature As String
dim mail_sender as new MailOptions 'here you are instantiating an object from the class module created previously
dim content as string
signature = "C:\Users\your_user\Documents\your_signature.png"
content = "<font face=""verdana"" color=""black"">This is some text!</font>"
content = content & "<img src=""<EMBEDDEDIMAGE:" & signature & " >"" />"
mail_sender.PrepareMessageWithEmbeddedImages _
FromAddress:="chrism_mail#blablabla.com", _
ToAddress:="addressee_mail#blablabla.com", _
Subject:="your_subject", _
HtmlContent:=content
'your_Smtp_Server, for example: RelayServer.Contoso.com
correos.SendMessageBySMTP "your_Smtp_Server", "your_network_user_account", "your_network_user_account_password", False
end sub

Resources