Convert from sending email immediately from Excel to displaying in Outlook - excel

In this code an Excel file table from active sheet is sent directly via email.
I need to change it to same result only difference is I need it open in Outlook as draft and not send it (there will be added more of text etc.).
I tried .Display but it won't open Outlook new email, it is displayed in Excel.
Sub Send()
Dim AWorksheet As Worksheet
Dim Sendrng As Range
Dim rng As Range
On Error GoTo StopMacro
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
'Fill in the Worksheet/range you want to mail
'Note: if you use one cell it will send the whole worksheet
Set Sendrng = Range("B1:M44")
'Remember the activesheet
Set AWorksheet = ActiveSheet
With Sendrng
' Select the worksheet with the range you want to send
.Parent.Select
'Remember the ActiveCell on that worksheet
Set rng = ActiveCell
'Select the range you want to mail
.Select
' Create the mail and send it
ActiveWorkbook.EnvelopeVisible = True
With .Parent.MailEnvelope
' Set the optional introduction field thats adds
' some header text to the email body.
.Introduction = "Dear All," & vbNewLine & vbNewLine & "Please find XXX."
With .Item
.To = "XXXX"
.Subject = "XXX"
.Send
End With
End With
'select the original ActiveCell
rng.Select
End With
'Activate the sheet that was active before you run the macro
AWorksheet.Select
StopMacro:
With Application

Replace .Send with .Display. If you want to wait for the user to either click on the Send button or dismiss the message, use .Display(true) to show it modally.

You can automate Outlook directly from an Excel macro where you could display a new mail item to a user. For example, the following code creates a new mail items, set up properties on the item and then display it for a user:
variables declared as a specific object type ie. specific to the application which is being automated:
Dim applOL As Outlook.Application
Dim miOL As Outlook.MailItem
'Create a new instance of the Outlook application. Set the Application object as follows:
Set applOL = New Outlook.Application
'create mail item:
Set miOL = applOL.CreateItem(olMailItem)
With miOL
.To = "info#test.com"
.CC = ""
.Importance = olImportanceLow
.Subject = "Mail Automation"
.Body = "Sending the Active Excel Workbook as attachment!"
'add host workbook as an attachment to the mail:
.Attachments.Add ActiveWorkbook.FullName
.ReadReceiptRequested = True
.Display
End With
'clear the object variables:
Set applOL = Nothing
Set miOL = Nothing
Don't forget to add an Outlook COM reference to your VBA project in Excel. See Automating Outlook from a Visual Basic Application for more information.

Related

Use VBA to send mass email

I'm taking over a workbook created from the last employee. There's a specific coding that I cannot figure out where it states to grab the email list from to plug into the BCC line.
I'm not finding anywhere in the code that states that but the macro somehow works. It is able to extract the email addresses and plug them into the bcc line on the email template. I see that they specified bc_r but I don't see any worksheets name RecipientEmails. The only worksheet with a list of email addresses in this workbook that I see is in the same tab as the command button to pull this email template. The email address is listed on this sheet under column T. So, I'm unsure why the code says c1. Please review the codes below and if you can guide me, I'd appreciate it.
Sub esendemail()
Dim outlook As Object
Dim newEmail As Object
Dim xInspect As Object
Dim pageEditor As Object
Set outlook = CreateObject("Outlook.Application")
Set newEmail = outlook.CreateItem(0)
bc_r = ""
For Each c1 In Range("RecipientEmails")
bc_r = bc_r & ";" & c1.Value
Next c1
With newEmail
.To = ""
.BCC = bc_r
.Subject = "Welcome to the team!"
.Body = "[Greetings]" & vbCrLf & " " & vbCrLf & "Practice Name:"
.Display
Set xInspect = newEmail.GetInspector
Set pageEditor = xInspect.WordEditor
Set pageEditor = Nothing
End With
Set newEmail = Nothing
Set outlook = Nothing
End Sub
The email addresses are retrieved in the named range:
bc_r = ""
For Each c1 In Range("RecipientEmails")
bc_r = bc_r & ";" & c1.Value
Next c1
Check out the RecipientEmails named range in your worksheet. See Create a named range from selected cells in a worksheet for more information about named ranges.
Also I'd recommend using the Recipients property for setting up recipients on the Outlook item. You can read more about that approach in the article I wrote for the technical blog, see How To: Fill TO,CC and BCC fields in Outlook programmatically for more information.

Copy PowerPoint native table to the body of an Outlook Email

I cannot provide all the code. It is from an internal project of my company.
I created VBA code to take elements from an Excel list and save it in a PowerPoint native table (dimensions: 7 rows, 6 columns, name: Table1), which is already created inside of the PowerPoint template file. The code only fills it with the correct data in the correct cells.
'Example of how I access the native table in PowerPoint
Set oPPTShape = oPPTFile.Slides(SlideNum).Shapes("Table1")
'I can get data from a cell by using, for example:
oPPTShape.Table.Cell(2, 1).Shape.TextFrame.TextRange.Text
'But I cannot select a range from this PowerPoint table
I would like to extract this native table from PowerPoint and paste it in an Outlook email's body. I read that maybe I can do this by using .HTMLBody = StrBody & RangetoHTML(rng), inside of the OutMail as described below:
With OutMail
.To = name_email
'Add file
.Attachments.Add ("C:... .pptx")
.Subject = "Data"
.Body = StrBody
.HTMLBody = StrBody & RangetoHTML(rng)
.SaveAs "C:... .msg", 5
.Display 'Or use .Send
End With
Where rng is the Range that will be copied from the Table1 inside of the email's body.
Until now I can use the data from PowerPoint Table1 with the code below and I was trying to use the same method to insert the Table1 in the email's body.
Dim strNewPresPath As String
strNewPresPath = "C:\... .pptx"
Set oPPTApp = CreateObject("PowerPoint.Application")
oPPTApp.Visible = msoTrue
Set oPPTFile = oPPTApp.Presentations.Open(strNewPresPath)
SlideNum = 1
Sheets("Open Tasks").Activate
Dim myStr As String
myStr = "Open"
Do
oPPTFile.Slides(SlideNum).Select
'Select PowerPoint shape with the name Table1
Set oPPTShape = oPPTFile.Slides(SlideNum).Shapes("Table1")
.
.
.
My question is:
Is there another way to copy and paste this Table1 from PowerPoint to the email's body with VBA code?
It can be as an Image/Picture from the Table or even not in the same exact format that it is in PowerPoint, because until now I am sending it as an attachment file and I believe it is easier to read when the Table is visible under the text written in the email.
Here is a basic example that will take a PowerPoint Table and copy it over to an outlook email using early binding.
Keep in mind this can be volatile at times, in other words, the information doesn't actually make it to the clipboard but this can be dealt with by pausing the application for a few seconds. Also, this will work if Outlook is ALREADY OPEN.
Sub ExportToOutlook()
'Declare PowerPoint Variables
Dim PPTShape As PowerPoint.Shape
'Declare Outlook Variables
Dim oLookApp As Outlook.Application
Dim oLookItm As Outlook.MailItem
'Create a reference to the table you want to copy, & select it.
Set PPTShape = ActivePresentation.Slides(1).Shapes("Table 1")
PPTShape.Select
On Error Resume Next
'Test if Outlook is Open
Set oLookApp = GetObject(, "Outlook.Application")
'If the Application isn't open it will return a 429 error
If Err.Number = 429 Then
'If it is not open then clear the error and create a new instance of Outlook
Err.Clear
Set oLookApp = New Outlook.Application
End If
'Create a mail item in outlook.
Set oLookItm = oLookApp.CreateItem(olMailItem)
'Copy the table
PPTShape.Copy
'Create the Outlook item
With oLookItm
'Pass through the necessary info
.To = "Someone"
.Subject = "Test"
.Display
'Get the word editor
Set oLookInsp = .GetInspector
Set oWdEditor = oLookInsp.WordEditor
'Define the content area
Set oWdContent = oWdEditor.Content
oWdContent.InsertParagraphBefore
'Define the range where we want to paste.
Set oWdRng = oWdEditor.Paragraphs(1).Range
'Paste the object.
oWdRng.Paste
End With
End Sub

Sending multiple worksheets in same workbook via VBA macro through Outlook Email?

I would like to copy multiple worksheets (for example, Sheet71, Sheet76, Sheet60, and Sheet77) that are located within one workbook into another workbook to send in an email to a recipient that is outlined within my email key sheet on Sheet 71.
These emails will be sent to individuals to outline their bonus pay.
Therefore, it is critical that the recipients only receive their own or who they are responsible for.
I have figured out how to send one single worksheet to one recipient, but cannot figure out how to accomplish this with multiple worksheets without using the name on the worksheet (Pierce Group Matrix, Shuff Matrix, Gamble Matrix, and Reed Matrix) versus Sheet71, Sheet76, Sheet60, and Sheet77 in VBA.
I need to be able to reference within the macro to the sheet number rather than the name, because turnover does happen.
Below is the code that I wrote to send an email to one individual in my email key sheet (Sheet81) with one worksheet but it only sends Sheet 71.
I have tried the Array keyword and multiple other keywords but can't seem to get it to work.
I need to reference to the Sheet number rather than the Sheet name because the names are changed when people are replaced.
I would prefer to make a copy like the below code does, but I am open to try a Select command if that will work.
Sub Mail()
Dim OutlookApp As Object
Dim Mess As Object, Recip
Recip = Sheet81.[C35].Value
newDate = MonthName(Month(DateAdd("m", -1, Date)), False)
' Make a copy of the active worksheet
' and save it to a temporary file
Sheet71.Copy
Set WB = ActiveWorkbook
Filename = WB.Worksheets(1).Name
On Error Resume Next
Kill "C:\" & Filename
On Error GoTo 0
WB.SaveAs Filename:="C:\" & Filename
Set OutlookApp = CreateObject("Outlook.Application")
Set Mess = OutlookApp.CreateItem(olMailItem)
With Mess
.Subject = (newDate + " Matrix")
.Body = ("Attached is your " + newDate + " bonus matrix. Thanks! Neil")
.to = Recip
.Attachments.Add WB.FullName
.Display
.Send
End With
ActiveWorkbook.Close
Set OutlookApp = Nothing
Set Mess = Nothing
End Sub
In this method, I elected to create a new sub routine called sendMultMails. This will create a collection of worksheets that you choose to add. Since you do not want to use the sheet name as the reference, I used the sheet's CodeName.
So, add your sheets to the collection and loop that collection. Within the loop, you will call your other routine Mail, passing the sheet as a parameter.
Sub sendMultMails()
Dim wsColl As New Collection, ws As Worksheet
Rem: Add your worksheets to the collection via the worksheet's CodeName
With wsColl
.Add Sheet71
.Add Sheet76
.Add Sheet60
.Add Sheet77
End With
Rem: loop through each collection item, calling the Mail Routine
For Each ws In wsColl
Mail ws
Next
End Sub
Rem: Added an argument for you to pass the ws obj to this routine
Sub Mail(ws As Worksheet)
Dim OutlookApp As Object
Dim Mess As Object, Recip
Recip = ws.Range("C35").Value
newDate = MonthName(Month(DateAdd("m", -1, Date)), False)
' Make a copy of the active worksheet
' and save it to a temporary file
ws.Copy
Set WB = ActiveWorkbook
Filename = WB.Worksheets(1).Name
On Error Resume Next
Kill "C:\" & Filename
On Error GoTo 0
WB.SaveAs Filename:="C:\" & Filename
Set OutlookApp = CreateObject("Outlook.Application")
Set Mess = OutlookApp.CreateItem(olMailItem)
With Mess
.Subject = (newDate + " Matrix")
.Body = ("Attached is your " + newDate + " bonus matrix. Thanks! Neil")
.to = Recip
.Attachments.Add WB.FullName
.Display
.Send
End With
ActiveWorkbook.Close
Set OutlookApp = Nothing
Set Mess = Nothing
End Sub
You could use the WB.Worksheets(1).CodeName to reference the Sheet number.
the CodeName property is read-only
You can reference a particular sheet as Worksheets("Fred").Range("A1") where Fred is the .Name property or as Sheet1.Range("A1") where Sheet1 is the codename of the worksheet.
For more information, you could refer to this link:
Excel tab sheet names vs. Visual Basic sheet names

MailItem.GetInspector.WordEditor in Office 2016 generates Application-defined or object defined error

I wrote an Excel macro to send email from a spreadsheet. It works on Office 2013, but not Office 2016.
I looked at the VBA differences between Office 2013 and 2016, but couldn't see anything about changes to the inspector or word editor for message objects.
Once it gets to .GetInspector.WordEditor it throws:
Run-time error '287':
Application-defined or object defined error
Here is the relevant part of the macro:
Sub SendEmail()
Dim actSheet As Worksheet
Set actSheet = ActiveSheet
'directories of attachment and email template
Dim dirEmail as String, dirAttach As String
' Directory of email template as word document
dirEmail = _
"Path_To_Word_Doc_Email_Body"
' Directories of attachments
dirAttach = _
"Path_To_Attachment"
' Email Subject line
Dim subjEmail As String
subjEmail = "Email Subject"
Dim wordApp As Word.Application
Dim docEmail As Word.Document
' Opens email template and copies it
Set wordApp = New Word.Application
Set docEmail = wordApp.Documents.Open(dirEmail, ReadOnly:=True)
docEmail.Content.Copy
Dim OutApp As Outlook.Application
Set OutApp = New Outlook.Application
Dim OutMail As MailItem
Dim outEdit As Word.Document
' The names/emails to send to
Dim docName As String, sendEmail As String, ccEmail As String, siteName As String
Dim corName As String
Dim row As Integer
For row = 2 To 20
sendName = actSheet.Cells(row, 1)
sendEmail = actSheet.Cells(row, 2)
ccEmail = actSheet.Cells(row, 3)
siteName = actSheet.Cells(row, 4)
Set OutMail = OutApp.CreateItem(olMailItem)
With OutMail
.SendUsingAccount = OutApp.Session.Accounts.Item(1)
.To = sendEmail
.CC = ccEmail
.Subject = subjEmail & " (Site: " & siteName & ")"
Set outEdit = .GetInspector.WordEditor
outEdit.Content.Paste
outEdit.Range(0).InsertBefore ("Dear " & sendName & "," & vbNewLine)
.Attachments.Add dirAttach
.Display
'.Send
End With
Debug.Print row
Set OutMail = Nothing
Set outEdit = Nothing
Next row
docEmail.Close False
wordApp.Quit
End Sub
Things I've tried based on suggestions:
Checked Outlook settings - default is HTML text
Moved .display over .GetInspector.WordEditor
Ensure Word is the default email editor. From the Inspector.WordEditor dox:
The WordEditor property is only valid if the IsWordMail method returns True and the EditorType property is olEditorWord . The returned WordDocument object provides access to most of the Word object model...
Further, ensure that Outlook is configured to send Rich Text or HTML emails, not plain text.
I am not entirely sure if I had the same issue as you, but the call to GetInspector started failing for me after upgrading Office 2016. So to be clear it worked with Office 2016 and then stopped working after the latest update.
The following workaround worked for me
dim item : set item = Addin.Outlook.CreateItemFromTemplate(Filename)
Outlook.Inspectors.Add(item) ' Outlook is the application object
it only appears to work if I add the item straight after creating it, setting properties on it and then adding it did not work.
Note: I have not tested with CreateItem instead of CreateItemFromTemplate. The second line was added and unnecessary prior to the Office update.
Problem:
For security purposes, the HTMLBody, HTMLEditor, Body and WordEditor properties all are subject to address-information security prompts because the body of a message often contains the sender's or other people's e-mail addresses. And, if Group Policy does not permit then these prompts do not come on-screen. In simple words, as a developer, you are bound to change your code, because neither registry changes can be made nor group policy can be modified.
Hence, if your code suddenly stopped working after migrating to Office 365 or for any other reasons, please refer to the solutions below. Comments have been added for easy understanding and implementation.
Solution 1:
If you have administrative rights then try the registry changes given at below link:
https://support.microsoft.com/en-au/help/926512/information-for-administrators-about-e-mail-security-settings-in-outlo
However, as developer, I recommend a code that's rather compatible with all versions of Excel instead of making system changes because system changes will be required on each end user's machine as well.
Solution 2: VBA Code
Code Compatible: Excel 2003, Excel 2007, Excel 2010, Excel 2013, Excel 2016, Office 365
Option Explicit
Sub Create_Email(ByVal strTo As String, ByVal strSubject As String)
Dim rngToPicture As Range
Dim outlookApp As Object
Dim Outmail As Object
Dim strTempFilePath As String
Dim strTempFileName As String
'Name it anything, doesn't matter
strTempFileName = "RangeAsPNG"
'rngToPicture is defined as NAMED RANGE in the workbook, do modify this name before use
Set rngToPicture = Range("rngToPicture")
Set outlookApp = CreateObject("Outlook.Application")
Set Outmail = outlookApp.CreateItem(olMailItem)
'Create an email
With Outmail
.To = strTo
.Subject = strSubject
'Create the range as a PNG file and store it in temp folder
Call createPNG(rngToPicture, strTempFileName)
'Embed the image in Outlook
strTempFilePath = Environ$("temp") & "\" & strTempFileName & ".png"
.Attachments.Add strTempFilePath, olByValue, 0
'Change the HTML below to add Header (Dear John) or signature (Kind Regards) using newline tag (<br />)
.HTMLBody = "<img src='cid:DashboardFile.png' style='border:0'>"
.Display
End With
Set Outmail = Nothing
Set outlookApp = Nothing
Set rngToPicture = Nothing
End Sub
Sub createPNG(ByRef rngToPicture As Range, nameFile As String)
Dim wksName As String
wksName = rngToPicture.Parent.Name
'Delete the existing PNG file of same name, if exists
On Error Resume Next
Kill Environ$("temp") & "\" & nameFile & ".png"
On Error GoTo 0
'Copy the range as picture
rngToPicture.CopyPicture
'Paste the picture in Chart area of same dimensions
With ThisWorkbook.Worksheets(wksName).ChartObjects.Add(rngToPicture.Left, rngToPicture.Top, rngToPicture.Width, rngToPicture.Height)
.Activate
.Chart.Paste
'Export the chart as PNG File to Temp folder
.Chart.Export Environ$("temp") & "\" & nameFile & ".png", "PNG"
End With
Worksheets(wksName).ChartObjects(Worksheets(wksName).ChartObjects.Count).Delete
End Sub
Try moving the editor to the first action...
...
With OutMail
Set outEdit = .GetInspector.WordEditor
outEdit.Content.Paste
.SendUsingAccount = OutApp.Session.Accounts.Item(1)
.To = sendEmail
.CC = ccEmail
.Subject = subjEmail & " (Site: " & siteName & ")"
...

Email sent with VBA using Task Scheduler gets stuck in Outbox

I have some macros and Task Scheduler to launch Excel at a specified time, update some tables, create PDF documents from those tables and then email those PDF documents to select individuals.
Sometimes the email gets stuck in the Outbox and does not send until I open up Outlook.
Here is the code for sending the email:
Option Explicit
Public strFileName As String
Sub EmailPDFAsAttachment()
'This macro grabs the file path and stores as a concatenation/variable. Then it emails the file to whomever you specify.
' Works in Excel 2000, Excel 2002, Excel 2003, Excel 2007, Excel 2010, Outlook 2000, Outlook 2002, Outlook 2003, Outlook 2007, Outlook 2010.
' This example sends the last saved version of the Activeworkbook object .
Dim OutApp As Object
Dim OutMail As Object
Dim FilePath As String
'This part is setting the strings and objects to be files to grab with their associated filepath. (e.g. FilePath is setting itself equal to the text where we plan to set up each report)
FilePath = "\\"ServerNameHere"\UserFolders\_AutoRep\DA\PDFs\SealantsVS1SurfaceRestore\" _
& strFileName & ".pdf"
With Application
.EnableEvents = True
.ScreenUpdating = True
' End With
'Below is where it creats the actual email and opens up outlook.
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
' ******Make sure to set the .To to only recipients that are required to view it. Separate email addresses with a semicolon (;).
' Current distribution list:
'
With OutMail
.To = "example#Example.com"
.CC = ""
.BCC = ""
.Subject = strFileName
.HTMLBody = "Hello all!" & "<br>" & _
"Here is this month's report for the Sealants vs Surface Restore. It goes as granular as to by show results by provider." & "<br>" & _
"Let me know what you think or any comments or questions you have!" & "<br>" & _
vbNewLine & .HTMLBody
'Here it attached the file, saves the email as a draft, and then sends the file if everything checks out.
.Attachments.Add FilePath
.Send
End With
On Error GoTo 0
' With Application
' .EnableEvents = True
' .ScreenUpdating = True
End With
'This closes out the Outlook application.
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
After this completes, the Private sub jumps back to the macros in this workbook and quits MS Excel with the CloseWorkbook Application.
My tools reference library in Outlook's VBA settings:
My Trust Settings:
Macro Settings:
"Enable all macros" selected
"Apply macro security settings to installed add-ins" selected
The idea is to have this program run in the early morning and have these emails in the inbox of select individuals by the time they come in to work.
If anyone is still looking for an answer; this allows to actually send an email without opening outlook app.
Dim mySyncObjects As Outlook.SyncObjects
Dim syc As Outlook.SyncObject
Set mySyncObjects = Outlook.Application.GetNamespace("MAPI").SyncObjects
Set syc = mySyncObjects(1)
syc.start
Outlook, just like any other Office app, cannot run in a service(such as the Scheduler).
That being said, you need to force Outlook to perform SendReceive and wait for it to complete. Call Namespace.SendAndReceive or retrieve the first SyncObject object from the Namespace.SyncObjects collection, call SyncObject.Start and wait fro the SyncObject.SyncEnd event to fire.

Resources