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
Related
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.
I have a workbook which has individual sheets that house office data based on the office location (example; sheet named Chicago would have data for our Chicago office and so on and so forth). Currently on every sheet I have a button with an assigned macro that exports the sheet as a pdf and attaches it to a prefilled email that I can then send to that office's designated contact based on some code I found online that works perfectly. The email of the contact is located in the same cell in every sheet. (See code below). I only need to send this email to locations that haven't reached a certain benchmark.
Sub SendEmailBulk()
'Update 20131209
Dim Wb As Workbook
Dim ws As Worksheet
Dim FileName As String
Dim OutlookApp As Object
Dim OutlookMail As Object
On Error Resume Next
Set Wb = ThisWorkbook
Set ws = Active.Sheet
FileName = Wb.FullName
xIndex = VBA.InStrRev(FileName, ".")
With ActiveSheet.PageSetup
.Orientation = xlLandscape
.FitToPagesTall = 1
.FitToPagesWide = 1
End With
If xIndex > 1 Then FileName = VBA.Left(FileName, xIndex - 1)
FileName = FileName & "_" + ActiveSheet.Name & ".pdf"
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, FileName:=FileName
Set OutlookApp = CreateObject("Outlook.Application")
Set OutlookMail = OutlookApp.CreateItem(0)
With OutlookMail
.SentOnBehalfOfName = "abc#xyz.com"
.To = ActiveSheet.Range("J10")
.CC = ""
.BCC = ""
.Subject = Range("C1") & " Data"
.Body = "abcxyz"
.Attachments.Add FileName
.Display
End With
Kill FileName
Set OutlookMail = Nothing
Set OutlookApp = Nothing
End Sub
What I would like to do is instead of having to check every sheet and send the emails individually to instead have some code that checks and emails only the required ones for me.
In another sheet I have a sheet called SUMMARY which contains a summary of all of our office locations in column B and a true or false counter setup that records whether they have achieved the benchmark in column C. I only need to send the above email to those locations who have not reached that benchmark (a FALSE value in column C).
I understand that I would need some code that checks the first row of the SUMMARY sheet for the first office location in Column B if it contains the FALSE value in Column C then, if so, assigns the value in Column B to a variable (lets say SheetName). Then the ActiveSheet reference would need to be replaced with WorkSheets("SheetName") to have the email code run before looping back around to check the second office location and so on until the end.
I have an idea of how this would work in theory I just don't have the exact VBA knowledge on how to write something to this effect. Any help would be greatly appreciated.
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
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 & ")"
...
I have write a macro in outlook and excel vba, the description are:
1. Code in Outlook for Open Excel file if email subject line match :
Private Sub Items_ItemAdd(ByVal Item As Object)
If TypeOf Item Is Outlook.MailItem Then
'// Subject line here
If InStr(Item.Subject, "Run Dashboard") Then
Call openExcel
End If
End If
End Sub
Once Excel is open and dashboard has run then email has to be sent via excel.
vba and code:
Dim outapp As Object
Dim nmail As Object
Set outapp = CreateObject("Outlook.Application")
Set nmail = outapp.CreateItem(0)
With nmail
.To = "xxxxxx#xxxx.com"
.cc = ""
.bcc = ""
.Subject = flname
.htmlbody = RangetoHTML(Range("A1:" & Split(Cells(, lastcol1).Address, "$")(1) & lastrow1))
.attachments.Add ActiveWorkbook.FullName
.display
End With
On Error GoTo 0
Set nmail = Nothing
Set outapp = Nothing
Now I am facing the error on Set outapp = CreateObject("Outlook.Application")
This error is only showing if i open excel file through outlook email as mentioned in point 1, if i open file in normal way i.e. without outlook help, then code is running successfully.
Please help in the same.
Thanks in advance
Why do you need to automate Excel from Outlook and then Outlook from Excel?
Set outapp = CreateObject("Outlook.Application")
Instead you may get the running Outlook instance (if any) because only one instance of Outlook can be run simultaneously. See How to automate Outlook from another program and GetObject and CreateObject behavior of Office automation servers for more information.
Try to use the following line of code instead:
Set nmail = Application.CreateItem(olMailItem)
If you have multiple profiles in Outlook configured most probably you will need to use the Logon method of the Namespace class.