I am trying to loop through a set of worksheets, save each of them as a separate workbook, and then send them as attachment by mail.
However when running the below code, I end up with error 287 triggered by .Send. I have outlook open, so that is not the problem. If I change .Send to .Display, the mails are generated as drafts as displayed properly with the correct sheet attached.
Sub SendWorksheetsByMail()
Dim wb As Workbook
Dim destinationWb As Workbook
Dim OutApp As Outlook.Application
Dim OutMail As Outlook.MailItem
Set wb = Workbooks("Test.xlsm")
Application.EnableEvents = False
Application.ScreenUpdating = False
For Each ws In wb.Worksheets
'Ignore Summary and Config
If ws.Name <> "Summary" And ws.Name <> "Config" Then
'On Error Resume Next
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(olMailItem)
ws.Copy
Set destinationWb = ActiveWorkbook
destinationWb.SaveAs "C:\****************\" & ws.Name & ".xlsx", FileFormat:=51
With OutMail
.To = "*******************"
.Subject = "Test"
.Body = "Test"
.Attachments.Add destinationWb.FullName
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
End If
Next ws
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Edit: "It also fails even without an attachment. Essentially generating a message containing only the subject and text "test"."
Any suggestions for how to solve this? It would save a lot of time to not have to click Send for each individual mail, as the number of mails to send could potentially become quite large.
This is what I used to send a mail with attachment to multiple addresses, listed in column H while the name of the receiver is listed in another column
Sub Mail()
'####################################
'### Save the file as pdf ######
'####################################
Dim FSO As Object
Dim s(1) As String
Dim sNewFilePath As String
Set FSO = CreateObject("Scripting.FileSystemObject")
s(0) = ThisWorkbook.FullName
If FSO.FileExists(s(0)) Then
'//Change Excel Extension to PDF extension in FilePath
s(1) = FSO.GetExtensionName(s(0))
If s(1) <> "" Then
s(1) = "." & s(1)
sNewFilePath = Replace(s(0), s(1), ".pdf")
'//Export to PDF with new File Path
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:=sNewFilePath, Quality:=xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas:=False, OpenAfterPublish:=False
End If
Else
'//Error: file path not found
MsgBox "Error: this workbook may be unsaved. Please save and try again."
End If
Set FSO = Nothing
'##########################################
'### Attach the file and mail it ######
'##########################################
Dim OutApp As Object
Dim OutMail As Object
Dim sh As Worksheet
Dim cell As Range
Dim FileCell As Range
Dim rng As Range
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set sh = Sheets("sheet")
Set OutApp = CreateObject("Outlook.Application")
For Each cell In sh.Columns("H").Cells.SpecialCells(xlCellTypeConstants)
If cell.Value Like "?*#?*.?*" Then
Set OutMail = OutApp.CreateItem(0)
With OutMail
.to = cell.Value
.Subject = "file delivery "
.Body = "Hi " & cell.Offset(0, -3).Value & " here is my file"
.Attachments.Add sNewFilePath
.Send 'Or use .Display
End With
Set OutMail = Nothing
End If
Next cell
Set OutApp = Nothing
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
Try .GetInspector before .Send. It would be like .Display without displaying.
I found a two step soultion. By changing .Send to .Display in the code above, the messages will be created as drafts in outlook and Displayed. If you do not want an extra window per e-mail, changing .Display to .Save will just put them in the draft folder.
Then I can use a macro written in Outlook to send all drafts. Code based on solution found at the mrexcel forums.
I also discovered after reading this answer on SO that the drafts folder can not be selected when running the macro.
Hope this helps others running into the same problem.
Public Sub SendDrafts()
Dim lDraftItem As Long
Dim myOutlook As Outlook.Application
Dim myNameSpace As Outlook.NameSpace
Dim myFolders As Outlook.Folders
Dim myDraftsFolder As Outlook.MAPIFolder
'Send all items in the "Drafts" folder that have a "To" address filled in.
'Setup Outlook
Set myOutlook = Outlook.Application
Set myNameSpace = myOutlook.GetNamespace("MAPI")
Set myFolders = myNameSpace.Folders
'Set Draft Folder.
Set myDraftsFolder = myFolders("*******#****.com").Folders("Drafts")
'Loop through all Draft Items
For lDraftItem = myDraftsFolder.Items.Count To 1 Step -1
'Check for "To" address and only send if "To" is filled in.
If Len(Trim(myDraftsFolder.Items.Item(lDraftItem).To)) > 0 Then
'Send Item
myDraftsFolder.Items.Item(lDraftItem).Send
End If
Next lDraftItem
'Clean-up
Set myDraftsFolder = Nothing
Set myNameSpace = Nothing
Set myOutlook = Nothing
End Sub
Might be a good idea to add code that differntiates the messages you are trying to send from other drafts that may already be in the folder.
Would still prefere a one step solution, so I will wait with marking this as a solution.
I finally found the answer googling a lot.
The problem is not with the .send method, but rather the session object.
Replace Set myOutlook = Outlook.Application with
Set objOutlook = ThisOutlookSession
This ensures that your macro is using the same outlook session that is open. Atleast it did the trick for me
Related
I have a simple VBA script that attaches a range of my excel sheet as an attachment to an email.
Now I need to loop through a range of mail-adresses (let's say this is column A) and add them as recipients.
I have to the following code that automates the attachment but I don't know how to implement adding the mail-addresses to the email.
How do I implement this in below code?
Sub Mail_Range()
Dim Source As Range
Dim Dest As Workbook
Dim wb As Workbook
Dim TempFilePath As String
Dim TempFileName As String
Dim FileExtStr As String
Dim FileFormatNum As Long
Dim OutApp As Object
Dim OutMail As Object
Set Source = Nothing
On Error Resume Next
Set Source = Range("A1:K50").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Source Is Nothing Then
MsgBox "The source is not a range or the sheet is protected, please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Set wb = ActiveWorkbook
Set Dest = Workbooks.Add(xlWBATWorksheet)
Source.Copy
With Dest.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial Paste:=xlPasteValues
.Cells(1).PasteSpecial Paste:=xlPasteFormats
.Cells(1).Select
Application.CutCopyMode = False
End With
TempFilePath = Environ$("temp") & "\"
TempFileName = "Selection of " & wb.Name & " " & Format(Now, "dd-mmm-yy h-mm-ss")
If Val(Application.Version) < 12 Then
'You use Excel 97-2003
FileExtStr = ".xls": FileFormatNum = -4143
Else
'You use Excel 2007-2016
FileExtStr = ".xlsx": FileFormatNum = 51
End If
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With Dest
.SaveAs TempFilePath & TempFileName & FileExtStr, FileFormat:=FileFormatNum
On Error Resume Next
With OutMail
.to = "test#test.com"
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.Body = "Hi there"
.Attachments.Add Dest.FullName
'You can add other files also like this
'.Attachments.Add ("C:\test.txt")
.Display
End With
On Error GoTo 0
.Close savechanges:=False
End With
Kill TempFilePath & TempFileName & FileExtStr
Set OutMail = Nothing
Set OutApp = Nothing
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
-- EDIT
I used =TEXT.COMBINE(";";TRUE; AJ4:AJ15) to combine the mailadresses into one string (in cell AJ16).
Next I added the range to OutMail.to = Range("AJ16") but executing the macro doesn't show the recipients in the mail. How do I solve this?
With Dest
.SaveAs TempFilePath & TempFileName & FileExtStr, FileFormat:=FileFormatNum
On Error Resume Next
With OutMail
.to = Range("AJ16")
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.Body = "Hi there"
.Attachments.Add Dest.FullName
'You can add other files also like this
'.Attachments.Add ("C:\test.txt")
.Display
End With
On Error GoTo 0
.Close savechanges:=False
End With
Get the range values as text delimited by ; and set it as To value in your code.
'/Takes a vertical range and returns values as delimited text
Function GetAddressList(rng As Range)
Dim arrEmails
arrEmails = rng
arrEmails = Application.Transpose(Application.Index(arrEmails, , 1))
GetAddressList = Join(arrEmails, ",")
End Function
'/ This is how you use GetAddressList
Sub test()
MsgBox GetAddressList(Sheet1.Range("A1:A10"))
End Sub
so something like this OutMail.To = GetAddressList(Sheet1.Range("A1:A10"))
if you have newer Excel version then you can simply use TextJoin in one of the cells and then call that value directly
=TEXTJOIN(";",TRUE,A1:A10)
Another example would be
Option Explicit
Public Sub Example()
' add ref - tool -> references - > Microsoft Outlook XX.X Object Library
Dim olApp As Outlook.Application
Set olApp = New Outlook.Application
Dim Email As Outlook.MailItem
Set Email = olApp.CreateItem(0)
Dim Emails_Address_Sht As Excel.Worksheet
Set Emails_Address_Sht = ThisWorkbook.Worksheets("Emails_Address")
Dim Cell As Range
Dim Emails As String
For Each Cell In Emails_Address_Sht.Range("A1", _
Emails_Address_Sht.Range("A100").End(xlUp))
Emails = Emails & Cell & ";"
Next
With Email
.To = Emails
.Subject = "Hello"
.Display
End With
End Sub
There are two main ways for adding recipients to the email:
Modifying the To, Cc, Bcc properties that are represented by a string.
The Recipients.Add method creates a new recipient in the Recipients collection. For example:
Sub CreateStatusReportToBoss()
Dim myItem As Outlook.MailItem
Dim myRecipient As Outlook.Recipient
Set myItem = Application.CreateItem(olMailItem)
Set myRecipient = myItem.Recipients.Add("Eugene Astafiev")
myItem.Subject = "Hello world"
myItem.Display
End Sub
The Recipient.Type property depending on the type of recipient returns or sets an integer corresponding to the numeric equivalent of one of the following constants:
JournalItem recipient: the OlJournalRecipientType constant olAssociatedContact.
MailItem recipient: one of the following OlMailRecipientType constants: olBCC, olCC, olOriginator, or olTo.
MeetingItem recipient: one of the following OlMeetingRecipientType constants: olOptional, olOrganizer, olRequired, or olResource.
TaskItem recipient: either of the following OlTaskRecipientType constants: olFinalStatus, or olUpdate.
For example, to add a new recipient to the CC field:
Set myItem = Application.CreateItem(olMailItem)
Set myRecipient = myItem.Recipients.Add ("Eugene Astafiev")
myRecipient.Type = olCC
I am trying to paste multiple Excel ranges as images in Outlook mail using VBA. I am using the answer to this question (Pasting an Excel range into an email as a picture) to paste a range of excel as image in mail but as soon as I paste another range, it overwrites the previous image. Is there anyway to change the cursor position in Outlook mail using wordeditor. I tried using collapse before pasting the image but it did not help. Also how do I add the text to it as using Outmail.body to edit anything gets overwritten too by the image pasted afterwards.
This is the code I am using:
Sub Sendmail()
Dim r as range
Set r = Range("C2:O13)
r.copy
dim outlookapp as Outlook.Application
set outlookapp = CreateObject("Outlook.Application")
dim outMail As Outlook.Mailitem
Set outMail = outlookApp.CreateItem(olMailItem)
With outMail
.Display
.CC = "xyz#abc.com"
.Subject = "Test"
.Body = "Dear" & "Macro" & vbnewline
end with
outmail.Display
'Opening wordeditor
dim worddoc as Word.Document
Set worddoc = Outmail.GetInspector.WordEditor
worddoc.range.PasteandFormat wdChartPicture
'Adding new line after pasting image
worddoc.range.Insertafter vbNewline
' Adding second image
dim s as range
set s= Range(P2:Z30)
s.copy
worddoc.range.PasteandFormat wdChartPicture
You could refer to the below code:
Option Explicit
Public Sub Example()
Dim rng As Range
Dim olApp As Object
Dim Email As Object
Dim Sht As Excel.Worksheet
Dim wdDoc As Word.Document
Set Sht = ActiveWorkbook.Sheets("Dashboard")
Set rng = Sht.Range("B4:L17")
rng.CopyPicture Appearance:=xlScreen, Format:=xlPicture
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set olApp = CreateObject("Outlook.Application")
Set Email = olApp.CreateItem(0)
Set wdDoc = Email.GetInspector.WordEditor
With Email
.To = ""
.CC = ""
.BCC = ""
.Subject = ""
.Attachments.Add ActiveWorkbook.FullName
wdDoc.Range.PasteAndFormat Type:=wdChartPicture
' if need setup inlineshapes hight & width
With wdDoc
.InlineShapes(1).Height = 130
End With
.Display
End With
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set Email = Nothing
Set olApp = Nothing
End Sub
For more information, please refer to these links:
Copy Excel range as Picture to Outlook
Copy range of cells from Excel as picture and add text in the email body
My code is not finding an exported file to attach to an email.
I'm sure I'm doing something wrong in the myattachments.add line.
The file name and file directory will always be different based on each client, that's why I have specified a cell specifically for the filename in each quote.
I'm going to copy this Excel file to each client folder and run the code from there.
Sub sendremindermail()
ChDir ThisWorkbook.Path & "\"
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:=Range("'Costing'!C1"), Openafterpublish:=True
Dim outlookapp As Object
Dim outlookmailitem As Object
Dim myattachments As Object
Set outlookapp = CreateObject("outlook.application")
Set outlookmailitem = outlookapp.createitem(0)
Set myattachments = outlookmailitem.Attachments
With outlookmailitem
.To = Range("'Costing'!C8")
myattachments.Add ThisWorkbook.Path & Range("'Costing'!C1") & ".pdf" ' it cant find the pdf in the same directory
'.send
.Display
End With
Set outlookmailitem = Nothing
Set outlookapp = Nothing
End Sub
I'm new to VBA for Excel.
I suggest Outlook VBA does not know Excel VBA's Range.
You could pass the info from Range like this:
Sub sendremindermail()
Dim fName As String
Dim pathFileName As String
ChDir ThisWorkbook.Path & "\"
fName = Range("'Costing'!C8")
'ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:=Range("'Costing'!C1"), Openafterpublish:=True
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:=fName ', Openafterpublish:=True
Dim outlookapp As Object
Dim outlookmailitem As Object
Dim myattachments As Object
Set outlookapp = CreateObject("outlook.application")
Set outlookmailitem = outlookapp.createitem(0)
Set myattachments = outlookmailitem.Attachments
With outlookmailitem
.To = fName
pathFileName = ThisWorkbook.Path & "\" & fName & ".pdf"
Debug.Print "With fName not Range: " & pathFileName
myattachments.Add pathFileName
'.send
.Display
End With
Set outlookmailitem = Nothing
Set outlookapp = Nothing
End Sub
You could refer to the below code:
Sub Mail_Workbook_1()
' 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
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
' Change the mail address and subject in the macro before you run it.
With OutMail
.To = "ron#debruin.nl"
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.Body = "Hello World!"
.Attachments.Add ActiveWorkbook.FullName
' You can add other files by uncommenting the following line.
'.Attachments.Add ("C:\test.txt")
' In place of the following statement, you can use ".Display" to
' display the mail.
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
For more information, Please refer to this link:
OfficeTalk: Using the Excel Object Model to Send Workbooks and Ranges through E-Mail with Outlook (Part 1 of 2)
I am currently using a module on Microsoft Access to Open an Excel file and paste the results into an email. The module is working properly, but the Excel file is remaining open in the background. This is causing an issue when I try to run the same module using the same file.
The Excel file I am using also automatically updates a date field, so I also need the close call to save the file beforehand, or ignore the save changes pop-up.
Public Function emailPaste(exFile As String, exSheet As String, exRange As String, _
EmailSubject As String, To_Field As String, Optional CC_Field As String)
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Dim ApXL As Object
Set ApXL = CreateObject("Excel.Application")
ApXL.Workbooks.Open (exFile)
Set rng = Nothing
' Only send the visible cells in the selection.
Set rng = Sheets(exSheet).Range(exRange).SpecialCells(xlCellTypeVisible)
'If rng Is Nothing Then
'MsgBox "The selection is not a range or the sheet is protected. " & _
vbNewLine & "Please correct and try again.", vbOKOnly
'Exit Sub
'End If
With ApXL.Application
.EnableEvents = False
.ScreenUpdating = False
End With
Call OpenOutlook
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = To_Field
.CC = CC_Field
.Subject = EmailSubject
.HTMLBody = "<BODY style=font-size:12pt;font-family:Calibri> The report: " & EmailSubject & " " & _
"is pasted below. <br><br> Please review it and contact me if there are any issues.<br><br> " _
& RangetoHTML(rng) & ""
' In place of the following statement, you can use ".Display" to
' display the e-mail message.
.Display
End With
On Error GoTo 0
With ApXL.Application
.EnableEvents = True
.ScreenUpdating = True
End With
ApXL.Quit
Set ApXL = Nothing
Set OutMail = Nothing
Set OutApp = Nothing
End Function
How can I add at the end the code needed to save the excel file and close it without any user intervention?
You must be very strict in opening the Excel objects and closing them in reverse order - as done in this example:
Public Sub RenameWorkSheet()
Dim xls As Excel.Application
Dim wkb As Excel.Workbook
Dim wks As Excel.Worksheet
Set xls = New Excel.Application
Set wkb = xls.Workbooks.Open("c:\test\workbook1.xlsx")
Set wks = wkb.Worksheets(1)
wks.Name = "My New Name"
wkb.Close True
Set wks = Nothing
Set wkb = Nothing
xls.Quit
Set xls = Nothing
End Sub
You should try to tell the Application, that the Worksheet is saved
as it is.
Then Close the Worksheet
Then try to Close the Application.
something like this:
exFile.Sheets(exSheet).Saved = True
exFile.Sheets(exSheet).Close
ApXL.Quit
Or tell, that it doesn't have to save on closing...:
exFile.Sheets(exSheet).Close False
ApXL.Quit
I'd also propose, that you should store a direct reference to the Sheet and not implicitly calling the sheet via the active window...
Something like
dim wsh as Worksheet
set wsh = exFile.Sheets(exSheet)
then you can work with the variable wsh... more comfortable
I have a code that I've been using forever to automatically email a workbook via a commandbutton click. I tried to reformat this code to send 2 individual sheets (named: Pass, Pass Screenshot) from the workbook, but I can't get it to work. The sheets won't be active when the email is sent. This is the code I've been using, any help would be greatly appreciated:
Sub SendEmail()
ThisWorkbook.Save
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.to = "my email"
.Subject = "my subject" & Date
.Attachments.Add '???
.Display
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
The Attachments.Add method takes a file path argument, you can't reconfigure that to send a worksheet (or array of worksheet) object. What you can do is export those two sheets in to a new/temporary file, send as attachment, and then remove/kill the temporary file which is no longer needed.
Sub SendEmail()
Dim OutApp As Object
Dim OutMail As Object
Dim tempWB as Workbook
Dim tempFile as String
Dim wb as Workbook
tempFile = Environ("Temp") & "\sheets_copy.xlsx"
Set wb = ThisWorkbook
wb.Save
' The Sheets.Copy method will create a new workbook containing the copied sheets
wb.Sheets(Array("Pass", "Pass Screenshot")).Copy
Set tempWB = ActiveWorkbook
' ensure no temp wb already exists
' this can technically still fail if the file is open/locked
If Len(Dir(tempFile)) <> 0 Then
Kill tempFile
End If
' Save & close the tempFile
tempWB.SaveAs tempFile
tempWB.Close
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.to = "my email"
.Subject = "my subject" & Date
.Attachments.Add tempFile '## Add your attachment here
.Display
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub