Sending a chart in mail body - vba

I'm trying to modify Ron de Bruin's code to send a chart in mail body.
I export the chart and save it as an PNG image, then I modify HTML code to add it to the message.
The code should run on a server and send mails to people in my workplace.
When using MailItem.Display and manually clicking "send" when my message appears, everything works.
When I try to use MailItem.Send I get an icon in the mail body like it tried to attach an image which it couldn't find.
When I send that mail from a server, on a server account, the chart is displayed correctly.
It doesn't work when I try to send it on "local" computers.
Sub wyslij()
NameOfThisFile = ActiveWorkbook.Name
Dim rng As Range
Dim dataminus1, dataminus2 As Date
Dim olApp As Outlook.Application
Set olApp = CreateObject("Outlook.Application")
Dim olMail As Outlook.MailItem
Set olMail = olApp.CreateItem(olMailItem)
Set rng = Nothing
Set rng = Sheets(2).Range("E1:P13")
olMail.To = "xxx#xxx"
olMail.CC = "xxxx#xxx"
olMail.Subject = "xxxx"
olMail.HTMLBody = RangetoHTML(rng)
olMail.Display
'olMail.Send
'Delete file after sending a mail
'Call DeleteFile(Path)
End Sub
Sub Save_ChartAsImage()
ChartEx = False
Dim cht As ChartObject
For Each cht In ActiveSheet.ChartObjects
If cht.TopLeftCell.Column = ChartCol And cht.TopLeftCell.Row = ChartRow Then
ChartEx = True
On erRROR GoTo Err_Chart
cht.Chart.Export Filename:=ActiveWorkbook.Path & "\Chart.png", Filtername:="PNG"
End If
Next cht
Err_Chart:
If Err <> 0 Then
Debug.Print Err.Description
Err.Clear
End If
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2013
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
'.Cells(1).PasteSpecial xlPasteAll
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'kopiujemy wykres z poprzedniego dziaƂu
'Workbooks("WplatyFinal.xlsm").Activate
Workbooks(NameOfThisFile).Activate
Call Save_ChartAsImage
TempWB.Activate
TempWB.Sheets(1).Select
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
If ChartEx Then
RangetoHTML = RangetoHTML & "<img src ='" & ActiveWorkbook.Path & "\Chart.png" & "'>"
End If
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
I tried to use the Wait function directly after the Send method.

Getting the images to appear as inline is certainly possible. The img src in the HTML must refer to the cid with an identifier for the image. The code below sets up the email and adds all of the chart objects as inline images to an email.
Option Explicit
Sub CreateEmail()
Const PR_ATTACH_MIME_TAG = "http://schemas.microsoft.com/mapi/proptag/0x370E001E"
Const PR_ATTACH_CONTENT_ID = "http://schemas.microsoft.com/mapi/proptag/0x3712001E"
Dim wb As Workbook
Dim ws As Worksheet
Dim olApp As Object
Dim olMail As Object
Dim msg As String
Dim msgGreeting As String
Dim msgPara1 As String
Dim msgEnding As String
Dim chrt As ChartObject
Dim fname As String
Dim ident As String
Dim tempFiles As Collection
Dim imgIdents As Collection
Dim imgFile As Variant
Dim attchmt As Object
Dim oPa As Object
Dim i As Integer
'--- create the email body with HTML-formatted content
msgGreeting = "<bold>Dear Sirs</bold>,<br><br>"
msgPara1 = "<div>Here is the data you requested:</div>"
msgEnding = "<br><br>Sincerely,<br>JimBob<br>"
'--- build the other email body content
Set wb = ActiveWorkbook
Set ws = ActiveSheet
msg = msgGreeting & msgPara1
'--- loops and adds all charts found on the worksheet
If ws.ChartObjects.Count > 0 Then
Set tempFiles = New Collection
Set imgIdents = New Collection
For Each chrt In ws.ChartObjects
fname = ""
msg = msg & ChartToEmbeddedHTML(chrt, fname, ident) & "<br><br>"
tempFiles.Add fname
imgIdents.Add ident
Next chrt
End If
msg = msg & msgEnding
'--- create the mail item
Set olApp = CreateObject("Outlook.Application")
Set olMail = olApp.CreateItem(0) 'olMailItem=0
With olMail
.To = "yyy#zzzz.com"
'.CC = "xxxx#xxx"
.Subject = "xxxx"
.bodyformat = 2 'olFormatHTML=2
'--- each of the images is referenced as a filename, but each one must be
' individually added as an attachment, then the attachment properties
' set to show the attachment as "inline". Because the image will be
' inlined, we'll use the "ident" as the reference (internal to the
' message body HTML)
If (Not tempFiles Is Nothing) Then
For i = 1 To tempFiles.Count
Set attchmt = .attachments.Add(tempFiles.Item(i))
Set oPa = attchmt.PropertyAccessor
oPa.SetProperty PR_ATTACH_MIME_TAG, "image/png"
oPa.SetProperty PR_ATTACH_CONTENT_ID, imgIdents.Item(i)
Next i
End If
'--- the email item needs to be saved first
.Save
'--- now add the message contents
.htmlbody = msg
.display
End With
'--- delete the temp files now
For Each imgFile In tempFiles
Kill imgFile
Next imgFile
'--- clean up and get out
Set tempFiles = Nothing
Set imgIdents = Nothing
Set attchmt = Nothing
Set oPa = Nothing
Set olMail = Nothing
Set olApp = Nothing
Set ws = Nothing
Set wb = Nothing
End Sub
Private Function ChartToEmbeddedHTML(thisChart As ChartObject, _
ByRef tmpFile As String, _
ByRef ident As String) As String
Dim html As String
ident = RandomString(8)
tmpFile = thisChart.Parent.Parent.Path & "\" & ident & ".png"
thisChart.Activate
thisChart.Chart.Export Filename:=tmpFile, Filtername:="png"
html = "<img alt='Excel Chart' src='cid:" & ident & "'></img>"
ChartToEmbeddedHTML = html
End Function
Private Function RandomString(strlen As Integer) As String
Dim i As Integer, iTemp As Integer, bOK As Boolean, strTemp As String
'48-57 = 0 To 9, 65-90 = A To Z, 97-122 = a To z
'amend For other characters If required
For i = 1 To strlen
Do
iTemp = Int((122 - 48 + 1) * Rnd + 48)
Select Case iTemp
Case 48 To 57, 65 To 90, 97 To 122: bOK = True
Case Else: bOK = False
End Select
Loop Until bOK = True
bOK = False
strTemp = strTemp & Chr(iTemp)
Next i
RandomString = strTemp
End Function

Excellent! I couldn't manage to attach the active workbook into the mail.
I tried to add the code .Attachments.Add (ActiveWorkbook.FullName) but didn't work, I received a message saying that the file is in use, and sometimes Runtime error 424 - Object required
With olMail
.To = "yyy#zzzz.com"
'.CC = "xxxx#xxx"
.Subject = "xxxx"
.Attachments.Add (ActiveWorkbook.FullName) ' this i added

Related

VBA Excel code will not embed image in signature when directly sent from Excel

I have correctly created the HTMLBody and Signature scrub code from previous code discussions. I have the macro creating the direct binding to the image file and using .send, but when I do this nothing is displayed on the receivers end.
Although, when I use .display and then using send off of the display the image correctly is attached. The file I am using is not in the /Signatures path so Outlook would not have access to the file to 'attache' correctly.
The original code had the signature in reference.. that of course didnt work.
The second instance I used the GetSignature function that is generally the fix.
I then added a direct reference to the image as an additional line in the email construct just to make sure I wasnt doing something boneheaded.
I have also pulled the return HTML fro GetSignature and the altered HTML from the VBA.Replace .. everything points back to a valid file on the workstation.
Dim OutApp, OutMail As Object
Dim ws, wsTemp, wsEmail As Worksheet
Dim tempLO As Range
Dim TagName, NameValue, LangValue, DocLoc, compFilename, NameLine As String
Dim emSalutation, emBody, emClose As String
Dim StrSignature, LangCert, LangSubject, LangSig, LangSheet, tempLoc, compLoc As String
Dim sPath As String
Dim signImageFolderName, signImageOutlookFolder As String
Dim completeFolderPath, completeTempPath, completeCompPath As String
Dim lastRow As Long
Dim mailSTR As String
Dim runDate, SkipValue, errorTxt As String
Dim answer, emailCnt, certCnt As Integer
Dim testin As Boolean
Dim sTxtFilePath As String
Dim txtFileNumber As Integer
Set ws = Sheets("Certificates")
Set wsTemp = Sheets("Templates")
Set tempLO = wsTemp.Range("Template_Table")
Set OutApp = CreateObject("Outlook.Application")
DocLoc = wsTemp.Range("B2").Value
'Get the row data from Certificates to work with, name and lang
NameValue = .Cells(i, 1).Value
LangValue = .Cells(i, 3).Value
SkipValue = .Cells(i, 4).Value
'Get the Certificate, Email Subject, Email Tab and Email Signature for the correct set language
LangCert = Application.WorksheetFunction.VLookup(LangValue, tempLO, 2, False)
LangSubject = Application.WorksheetFunction.VLookup(LangValue, tempLO, 3, False)
LangSig = Application.WorksheetFunction.VLookup(LangValue, tempLO, 4, False)
LangSheet = "Email_" & LangValue
Set wsEmail = Sheets("Email_" & LangValue)
sPath = DocLoc & LangSig & ".htm"
signImageFolderName = LangSig & "_files"
signImageOutlookFolder = signImageFolderName & "/"
completeFolderPath = DocLoc & signImageFolderName & "\"
errorTxt = OpenFile(DocLoc & LangSig & ".htm")
errorTxt = Folder_Exist_With_Dir(DocLoc & signImageFolderName, 3)
StrSignature = GetSignature(sPath)
StrSignature = VBA.Replace(StrSignature, signImageOutlookFolder, completeFolderPath)
Print #txtFileNumber, StrSignature
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
If Not IsEmpty(wsTemp.Range("B7").Value) And (wsTemp.Range("B7").Value Like "?*#?*.?*") Then
.SentOnBehalfOfName = wsTemp.Range("B7").Value
End If
.Subject = LangSubject
emSalutation = "<font style=""font-family: Calibri; Color: #1F497D; font-size: 14pt;""/font>" & _
ws.Range("A" & i).Value & ";<br>"
emBody = RangetoHTML(wsEmail.UsedRange)
emClose = "<br>" & StrSignature _
& "<br>" _
& "<img src='C:\<directory path to OneDrive Folder>\BTSL_SecAware_files\image001.png'>"
.HTMLBody = emSalutation & emBody & emClose
If IsEmpty(ws.Range("B" & i).Value) Or Not (ws.Range("B" & i).Value Like "?*#?*.?*") Then
.To = "Display Only"
.Display 'or use
Else
.To = ws.Range("B" & i).Value
.Send
emailCnt = emailCnt + 1
End If
End With
Set OutMail = Nothing
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2016
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "/" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
fileName:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.ReadAll
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close SaveChanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Function GetSignature(ByVal fPath As String) As String
Dim fso As Object
Dim TSet As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set TSet = fso.GetFile(fPath).OpenAsTextStream(1, -2)
GetSignature = TSet.ReadAll
TSet.Close
End Function
The code basically boils down to pull the signature file from a directory read in the text file of the .htm and update it with something different.. In this instance a qualified directory path.
When this is run and this file is added to the Htmlbody and .sent it does not embed the image, but when it is .displayed and then sent it embeds.
<link rel=File-List href="C:\<directory path to OneDrive Folder>\BTSL_SecAware2_files\filelist.xml">
<link rel=Edit-Time-Data href="C:\<directory path to OneDrive Folder>\BTSL_SecAware2_files\editdata.mso">
<!--[if !mso]>
<style>
v\:* {behavior:url(#default#VML);}
o\:* {behavior:url(#default#VML);}
w\:* {behavior:url(#default#VML);}
.shape {behavior:url(#default#VML);}
</style>
<![endif]--><!--[if gte mso 9]><xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
</o:OfficeDocumentSettings>
</xml><![endif]-->
<link rel=themeData href="C:\<directory path to OneDrive Folder>\BTSL_SecAware2_files\themedata.thmx">
<link rel=colorSchemeMapping href="C:\<directory path to OneDrive Folder>\BTSL_SecAware2_files\colorschememapping.xml">
<!--[if gte mso 9]><xml>

Copying range including formatting when pasting in Outlook email body

I had search all over of this question and still not get the exact codes for it.
I need to copy the color of the pivot table from excel to outlook body. When running the code i got the format but the only problem is the color of the table is turning into black and grey.
Please help me to figure it out how to put the exact color that i need.
This is my codes:
Sub AUTO_MAIL()
Dim rng As Range, rng2 As Range, rng3 As Range, rng4 As Range, sub1 As Range, sub2 As Range, sub3 As Range, sub4 As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Nothing
On Error Resume Next
' Only send the visible cells in the selection.
Set rng = Sheets("Data Entry").PivotTables(1).TableRange1
Set rng2 = Sheets("ACN Workflow").PivotTables(1).TableRange1
Set rng3 = Sheets("L'Oreal Workflow").PivotTables(1).TableRange1
Set rng4 = Sheets("MTD Volume").PivotTables(1).TableRange1
Set sub1 = Sheets("Data Entry").Range("A1:E1").SpecialCells(xlCellTypeVisible)
Set sub2 = Sheets("ACN Workflow").Range("A1:G1").SpecialCells(xlCellTypeVisible)
Set sub3 = Sheets("L'Oreal Workflow").Range("A1:G1").SpecialCells(xlCellTypeVisible)
Set sub4 = Sheets("MTD Volume").Range("A1:B1").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 Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = ""
.CC = ""
.BCC = ""
.Subject = "Step+ Volume Tracker, Data Entry/Workflow Ageing Report and Rejection Report |"
.HTMLBody = "<b>Dear All,</b><br><br>" & "Please see below summary of invoices and links to the <b>Volume Tracker</b> and <b>Ageing Report</b> (Data Entry and Workflow).<br>" & RangetoHTML(sub4) & vbCrLf & RangetoHTML(rng4) & vbCrLf & RangetoHTML(sub3) & vbCrLf & RangetoHTML(rng3) & vbCrLf & RangetoHTML(sub2) & vbCrLf & RangetoHTML(rng2) & vbCrLf & RangetoHTML(sub1) & vbCrLf & 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 Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2013
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.SpecialCells(xlCellTypeVisible).Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
You will have to tweak the code a bit which should look something like this:
Sub due()
Dim ol As Object 'Outlook.Application
Dim olEmail As Object 'Outlook.MailItem
Dim olInsp As Object 'Outlook.Inspector
Dim wd As Object 'Word.Document
Dim rCol As Collection, r As Range, i As Integer
'/* if outlook is running use GO, create otherwise */
Set ol = GetObject(Class:="Outlook.Application")
Set olEmail = ol.CreateItem(0) 'olMailItem
Set rCol = New Collection
With rCol
.Add Sheet1.Range("A1:B6") '/* add your ranges the same sequence */
.Add Sheet2.Range("A1:B6") '/* as you want them added in the body */
End With
With olEmail
.To = ""
'/* bonus basic html */
.HTMLBody = "<html><body style=""font-family:calibri"">" & _
"<p><b>Dear Deer,</b><br><br> She see seas." & _
"</p></body></html>"
Set olInsp = .GetInspector
If olInsp.EditorType = 4 Then 'olEditorWord
Set wd = olInsp.WordEditor
For i = 1 To rCol.Count '/* iterate all ranges */
Set r = rCol.Item(i): r.Copy
wd.Range.InsertParagraphAfter
wd.Paragraphs(wd.Paragraphs.Count).Range.PasteAndFormat 16
'16 - wdFormatOriginalFormatting
Next
End If
wd.Range.InsertParagraphAfter
wd.Paragraphs(wd.Paragraphs.Count).Range.Text = "Regards, Patricia"
wd.Paragraphs.Last.Range.Sentences.Last.Font.Bold = True
.Display
End With
End Sub
In case you want to do more, you will have to read more about Word VBA. This is just a sample on what you can do with Outlook's Word Editor.

Using RangetoHtml Function and Excel Won't Close after functions are done running

Basically I created a report and then send it via an email. I use Ron B's function to paste the excel sheets into the body of the email. What happens is excel doesn't destroy or close after the send email function is done. When I end task on it and run it again it says that the remote machine or server doesn't exits. This is probably because I am not explicitly defining the objects but I don't know how to between the two procedures. I tried making the xlApp Public but that didn't work. I even tried adding it to the rangetohtml function but at the point of where it says rng.copy it says there is an object that is required. I tried adding xlApp.rng.copy or wb.rng.copy or ws.rng.copy. So i have the below where the range to html copies the rng. That is added to a temp workbook and copied into the email. It call from the one function over to the other and I can't figure out how to destroy the excel session when it is done.
Set rng = xlApp.Selection.SpecialCells(xlCellTypeVisible)
Set rng = wb.Sheets(2).Range("A:U").SpecialCells(xlCellTypeVisible)
Set rng2 = xlApp.Selection.SpecialCells(xlCellTypeVisible)
Set rng2 = wb.Sheets(1).Range("A:U").SpecialCells(xlCellTypeVisible)
Now the range to html says RangetoHtml(rng as Range) then at the bottom it says
rng.copy
Below that when you debug it after end tasking on excel it stops on this line:
Set TempWB = Workbooks.Add(1)
I know you are supposed to put either XlApp.Workbooks.Add(1) but in the rangeto Html function it isn't declared as an object but it is in the function that it is calling from. I don't know what to do next and how to fix the code. I am posting both functions so you can see the code. When the send email happens it brings up the email and pastes the excel sheet into the email but excel doesn't closed.
Public Function sendEmailorbetechprealert()
Dim appOutLook As Outlook.Application
Dim Items As Outlook.Items
Dim Item As Object
Dim strPath As String
Dim strFilter As String
Dim strFile As String
Dim rng As Range
Dim rng2 As Range
Dim xlApp As Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
Dim MyFileName As String
Dim bfile As String
Dim Cell As Range
bfile = "S:\_Reports\Orbotech\Orbotech - Open Deliveries Pre-Alert\Orbotech - Open Deliveries Pre-Alert - "
MyFileName = bfile & Format(Date, "mm-dd-yyyy") & ".xls"
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")
If Err.Number > 0 Then Set xlApp = CreateObject("Excel.Application")
On Error GoTo 0
Set wb = xlApp.Workbooks.Open(MyFileName)
Set ws = wb.Sheets(1)
ws.Activate
Set rng = Nothing
Set rng2 = Nothing
On Error Resume Next
Set rng = xlApp.Selection.SpecialCells(xlCellTypeVisible)
Set rng = wb.Sheets(2).Range("A:U").SpecialCells(xlCellTypeVisible)
Set rng2 = xlApp.Selection.SpecialCells(xlCellTypeVisible)
Set rng2 = wb.Sheets(1).Range("A:U").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rng Is Nothing Then
Set appOutLook = Nothing
Set Items = Nothing
End If
If rng2 Is Nothing Then
Set appOutLook = Nothing
Set Items = Nothing
Exit Function
End If
strPath = "S:\_Reports\Orbotech\Orbotech - Open Deliveries Pre-Alert\" 'Edit to your path
strFilter = "*.xls"
strFile = Dir(strPath & strFilter)
'For Each Cell In Columns("E").Cells.SpecialCells(xlCellTypeConstants)
If strFile <> "" Then
Set appOutLook = CreateObject("Outlook.Application")
Set Items = Outlook.Application.ActiveExplorer.CurrentFolder.Items
Set Item = Items.Add("IPM.Note.iCracked")
With Item
.To = ""
'.CC =
''.bcc = ""
.Subject = "Orbotech Open Deliveries Report Pre-Alert"
.htmlBody = "This is the Open Deliveries Report. Please open the attachment. These lines are what have been inbound." & RangetoHTML(rng) & "This is what is still due" & RangetoHTML(rng2)
.Attachments.Add (strPath & strFile)
'.Send
Item.Display 'Used during testing without sending (Comment out
.Send if using this line)
wb.CheckCompatibility = False
wb.Save
wb.CheckCompatibility = True
DoEvents
End With
Else
MsgBox "No file matching please re run Orbotech Report"
Exit Function 'This line only required if more code past End If
End If
'Next Cell
DoEvents
On Error GoTo 0
wb.CheckCompatibility = False
wb.Save
wb.CheckCompatibility = True
xlApp.Quit
Set rng = Nothing
Set rng2 = Nothing
Set wb = Nothing
Set ws = Nothing
Set xlApp = Nothing
Exit Function
End Function
Now in the htmlbody it calls the RangetoHtml(rng) to paste it in the email. He is the rangetohtml function:
Public Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2016
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
'xlApp.Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
fileName:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.CheckCompatibility = False
TempWB.Save
TempWB.CheckCompatibility = True
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Any help that anyone can provide it would certainly be appreciated.

VB scripts hangs on more rows in excel although I want single row data from excel

I have a scrip to sent mail from excel via outlook .
Here i select a cell and copy it as html and sent to outlook .
but in hangs in method Function RangetoHTML(rng As Range) at line RangetoHTML = ts.readall when no. of row are upto 70 .
Can someone help.
Sub Mail_Selection_Range_Outlook_Body()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Nothing
On Error Resume Next
'Only the visible cells in the selection
Set rng = Selection.SpecialCells(xlCellTypeVisible)
'You can also use a fixed range if you want
'Set rng = Sheets("YourSheet").Range("D4:D12").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
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 Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = "xxxxx#xxxxx.com"
.BCC = ""
.Subject = "Report" & Format(Now, "dd-MM-yyyy")
.HTMLBody = RangetoHTML(rng)
.Display 'or use .Display .Send
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2016
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
Dim SignatureFilePath As String
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
SignatureFilePath = "Office.htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall // hangs here
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
RangetoHTML = "<BODY style= color:black;font-size:11.0pt;font-weight:400;font-family:Consolas,monospace>Dear Chetan<p>Please find details<p> </BODY>" & RangetoHTML & "<br><br>" & GetSignature(SignatureFilePath)
'Close TempWB
TempWB.Close savechanges:=False
Debug.Print
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Function GetSignature(fPath As String) As String
Dim fso As Object
Dim TSet As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set TSet = fso.GetFile(fPath).OpenAsTextStream(1, -2)
GetSignature = TSet.readall
TSet.Close
End Function
This will copy & paste the selection from Excel to the Outlook email body, preserving your default signature (if there is one). If you need to add some other signature from file location, you'd need some additional magic.
With OutMail
.To = "xxxxx#xxxxx.com"
.BCC = ""
.Subject = "Report" & Format(Now, "dd-MM-yyyy")
.Display 'or use .Display .Send
Dim wdDoc As Object '## Word.Document
Dim wdRange As Object '## Word.Range
Set wdDoc = OutMail.GetInspector.WordEditor
Set wdRange = wdDoc.Range(0, 0)
wdRange.InsertAfter vbCrLf & vbCrLf
'Copy the range in-place
rng.Copy
wdRange.Paste
End With
Alternatively, copy the data to a new worksheet and send as attachment:
Sub SendAsAttachment()
Dim rng As Range
Dim newWB As Workbook
Dim newWS As Worksheet
On Error Resume Next
Set rng = Selection.SpecialCells(xlCellTypeVisible)
If Err.Number <> 0 Then Exit Sub
On Error GoTo 0
Set newWB = Workbooks.Add
With newWB
Set newWS = .Sheets(1)
newWS.Name = rng.Parent.Name
rng.Copy newWS.Range("A1")
Do While .Worksheets.Count > 1
.Worksheets(.Count).Delete
Loop
.SendMail "david.zemens#jdpa.com", "subject", False
.Close False
End With
End Sub
The latter method does not include signature line in the email, though.

VBA '1004' error at ActiveWorkbook.Save

I have created a VBA module that :
searches for a specific email in outlook
grabs the excel file attachment from the email it finds
formats the excel file attachment (adds colors and grid to make it look more presentable)
saves the formatted excel file to my desktop
sends email(s) to our client with the formatted excel file as an attachment (and pastes the excel file into the body of the email)
** I use multiple arrays to send to individual clients
My code works pretty well and has worked without issues many times. However, every now and again it will have a '1004 run time error' pop up randomly while processing. When I debug, it takes me to 'ActiveWorkbook.Save'. Usually if I run it again it works just fine, but I need it to be more user friendly for others to use. Code is as follows.
Public f As Integer 'format integer
Sub Clients()
'Array([file destination to be saved], [subject of file being searched in outlook], [file name given when saved], [emails the report is going to])
f = 0
email_1 = Array("C:\User\Desktop\", "FL Test Results", "FL_Reports", "client1#email.com")
Call Reports(email_1)
f = 1
email_2 = Array("C:\User\Desktop\", "CA Test Results", "CA_Reports", "client2#email.com")
Call Reports(email_2)
f = 2
email_3 = Array("C:\User\Desktop\", "NY Test Results", "NY_Reports", "client3#email.com")
Call Reports(email_3)
email_4 = Array("C:\User\Desktop\", "TX Test Results", "TX_Reports", "client4#email.com")
Call Reports(email_4)
End Sub
Function Reports(a As Variant)
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Dim olApp As Outlook.Application
Dim olNs As Namespace
Dim olFldr As MAPIFolder
Dim olItms As Items
Dim olMi As MailItem
Dim olEmail As Outlook.MailItem
Dim olAtt As Attachment
Dim subj As String
Dim saveAs As String
Dim emails As String
Dim FilePath As String
FilePath = a(0)
subj = a(1)
saveAs = a(2)
emails = a(3)
Set olApp = GetObject(, "Outlook.Application")
Set olNs = olApp.GetNamespace("MAPI")
Set olFldr = olNs.GetDefaultFolder(olFolderInbox)
Set olItms = olFldr.Items
Set olEmail = olApp.CreateItem(olMailItem)
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set rng = Nothing
Set rng = ActiveSheet.UsedRange
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
Set olMi = olItms.Find("[Subject] = " & Chr(34) & subj & Chr(34))
If Not (olMi Is Nothing) Then
For Each olAtt In olMi.Attachments
olAtt.SaveAsFile FilePath & saveAs & ".xls"
Workbooks.Open (FilePath & saveAs & ".xls")
Call format.Run 'Seperate file that formats the raw excel sheet to look more pretty
If f = 0 Then
Call format.DeleteOldClasses 'different ways clients want there excel file info sorted
ElseIf f = 1 Then
Call format.sortByDate
Else
End If
ActiveWorkbook.Save '#######This is where the error pops up
Set rng = Worksheets(saveAs).UsedRange
Next olAtt
End If
On Error Resume Next
With OutMail
.Attachments.Add FilePath & saveAs & ".xls"
.To = emails
.CC = ""
.BCC = ""
.subject = subj
.HTMLBody = RangetoHTML(rng)
.send
End With
On Error GoTo 0
ActiveWorkbook.Close
Kill (FilePath & saveAs & ".xls")
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
Set olAtt = Nothing
Set olMi = Nothing
Set olFldr = Nothing
Set olNs = Nothing
Set olApp = Nothing
End Function
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2013
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Thank you for your time and help.
So I found a solution that works for me but maybe not others with the same issue. I Set my workbooks as #findwidow and #R3uk suggested. I simply put "On Error Resume Next" and save an extra copy in a new place that I pull the attachment from to place in the email.
On Error Resume Next
wB.Save
wB.SaveCopyAs ("C:\Users\Ken\Desktop\" & saveAs & ".xls")
Set rng = Worksheets(saveAs).UsedRange
Next olAtt
End If
It wont save the formatted excel file at times during the error, however this rarely happens now and it is only for our own documentation. It now continues through the cycle of client arrays with ease (and actually seems faster). Thank you for the help.