Excel VBA - Copy selected cells including images - vba

I have a vba script that copies the selected range of cells and pastes it in the body of an email. Within that selected range of cells is an image of my company logo. Everything copies and pastes fine except the image.
Is there something I need to do to the image itself, to maybe "embed" it into the worksheet so it copies along with the cells?
Or is there something I need to do in the vba script to copy the image along with the cells?
Here is the full code:
Sub copyObjects()
Dim IsCreated As Boolean
Dim PdfFile As String, Title As String, signature As String
Dim OutlApp As Object
Dim RngCopied As Range
Set RngCopied = Selection
' Use already open Outlook if possible
On Error Resume Next
Set OutlApp = GetObject(, "Outlook.Application")
If Err Then
Set OutlApp = CreateObject("Outlook.Application")
IsCreated = True
End If
On Error GoTo 0
' Prepare e-mail with PDF attachment
With OutlApp.CreateItem(0)
.Display ' We need to display email first for signature to be added
.Subject = Title
.To = ActiveSheet.Range("E10").Value ' <-- Put email of the recipient here or use a cell value
.CC = "whoever#abc.com; copy#abc.com" ' <-- Put email of 'copy to' recipients here
.HTMLBody = "Thank you for the opportunity to bid on the painting for " & ActiveSheet.Range("B9").Value & ". " & " Please read our attached proposal in it's entirety to be sure of all inclusions, exclusions, and products proposed. Give us a call with any questions or concerns." & _
vbNewLine & vbNewLine & _
RangetoHTML(RngCopied) & _
"Thank you," & _
.HTMLBody ' Adds default outlook account signature
On Error Resume Next
' Return focus to Excel's window
Application.Visible = True
If Err Then
MsgBox "E-mail was not sent", vbExclamation
Else
' MsgBox "E-mail successfully sent", vbInformation
End If
On Error GoTo 0
End With
' Try to quit Outlook if it was not previously open
If IsCreated Then OutlApp.Quit
' Release the memory of object variable
' Note: sometimes Outlook object can't be released from the memory
Set OutlApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2010
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

Set
Application.CopyObjectsWithCells = True
Before copying

If it helps, I have it in a macro like this...
The macro copies the aforementioned tabs which each contain Charts and Data Cells and Macro Buttons, into a new workbook.
The CopyObjects line, ensures that ALL the data on each tab is included in the copy. Without it, you may find that the charts and other drawn objects are excluded.
Caveat, I'm just about to post a question about an issue I have regarding this command. For some strange reason, just one single chart label is not being copied across - very strange so, be warned - check it works correctly for you.
Nick

Related

Email Macro Pauses every 40 - 50 emails

I have a semi-working macro that
Loops through a list of Managers
Generates a email body for each manager
Filters a sheet of all data relevant for each manager
Converts the visible cells to a HTML table
Adds the table to email
Send
The issue is the macro stops generating emails every 50 iterations in and does not error out - it just appears to "run" without doing anything. I have manually stopped the macro and there is no consistent line that appears to be getting stuck. Cutting this down to bare bones as much as I can, but I have no clue where the issue is. When I step through, I can't recreate the issue. When I re-run, the first 50ish go fine and then it stops generating.
I have also tried adding Application.Wait call at the end of each loop iteration and get same issue
I end up having to CTRL + BREAK to stop the macro. When I restart its coded to pick up right where it left off and it sends the next batch just fine (meaning the line it gets paused on runs just fine when I start again). Issue is not every once in a while - it's gets stuck like clock work.
Start of macro (just generates a text body)
Sub Initiate()
Dim EmailBody As String
EmailBody = "HTML TEXT BODY HERE"
Builder EmailBody '<---- Call loop
End Sub
Performs the loop on managers and filters the other sheet for relevant data. Passes all ranges on to the macro to build email
Sub Builder(EmailBody As String)
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Distro List")
Dim Raw As Worksheet: Set Raw = ThisWorkbook.Sheets("Email Data")
Dim LR As Long, LR2 As Long
Dim EmailTable As Range, Target As Range, EmailRange As Range
LR = ws.Range("B" & ws.Rows.Count).End(xlUp).Row
Set EmailRange = ws.Range("C2:C" & LR)
LR2 = Raw.Range("A" & Raw.Rows.Count).End(xlUp).Row
Application.ScreenUpdating = False
For Each Target In EmailRange
If Target.Offset(, -2) = "y" Then
If Len(Target.Offset(, -1)) = 6 Then
If Right(Target.Offset(, 1), 7) = "#so.com" Or Right(Target.Offset(, 1), 11) = "#StackO.com" Then
Raw.Range("A1:H" & LR2).AutoFilter 1, Target.Offset(, -1), VisibleDropDown:=False
Raw.Range("A1:H" & LR2).SpecialCells(xlCellTypeVisible).Columns.AutoFit
Set EmailTable = Raw.Range("A1:H" & LR2).SpecialCells(xlCellTypeVisible)
Sender EmailBody, EmailTable, Target
Set EmailTable = Nothing
End If
End If
End If
Next Target
Application.ScreenUpdating = True
End Sub
Build email, call HTML Table generator macro, add HTML Table, SEND email
Sub Sender(EmailBody As String, EmailTable As Range, Target As Range)
Dim OutApp As Object
Dim OutMail As Object
On Error GoTo BNP:
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.SentOnBehalfOfName = "urdearboy#so.com"
.to = Target.Offset(, 1)
.Subject = "Your Employees....."
.HTMLBody = "<p style = 'font-family:arial' >" _
& EmailBody & "</p>" _
& RangetoHTML(EmailTable) _
& "<p style = 'font-family:arial' >"
.Send
Target.Offset(, -2) = "Sent"
End With
BNP:
Set OutApp = Nothing
Set OutMail = Nothing
End Sub
Macro I found online that converts a excel range to a HTML table that can be inserted into email.
Function RangetoHTML(EmailTable As Range)
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"
EmailTable.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
Extremely glad, yet also annoyed, to say that adding a Applitcation.Wait for 1 second to the function RangetoHTML fixed the issue.
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Application.Wait Now + #12:00:01 AM# '<------ Resolved Issue
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Still curious to know what the actual issue is since I suspect that this is a work around to the actual issue. Just glad I can finally use this macro to send large distro's without it pausing every 4 minutes!

Sending daily email from Excel to Outlook - include PivotTable

I'm trying to set-up a macro that simply sends an email containing two pivot tables that are located on the same sheet.
The current code I'm using doesn't include the table headers. How can I change this?
Side Note:
To send this daily, I figured I could extract the the VBA code to a .vbs file and have it run everytime the computer is turned on. Would this work if the excel file is located in a share drive?
Here's what is being sent to my email:
Here's what I want to be sent:
Here's my current code for sending the email.
'Expired & Warning Updates
'Functions:
' Send daily email with expired and almost-expired digsafes
'Sends email once called
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 = Sheets("EXPIRED_LIST").Range("A6:F300").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 = "address#test.com"
.CC = ""
.BCC = ""
.Subject = "Daily Dig-safe Update"
.HTMLBody = RangetoHTML(rng)
.Display 'or use .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)
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
I'm stupid and didn't realize my range was off by one row. Fixed the issue. Now it's time for me to figure out how to have it send an email daily.

Hide information in an Excel range which is to be pasted into email

I have a macro that pastes an Excel range into an email. The range has conditional formatting to turn the background color and font color black to hide information.
When pasted into Outlook the font changes from black to white on the areas with black backgrounds.
Sub OpenOutlookEmail()
Dim OutApp As Object
Dim outMail As Object
Dim rng As Range
Set OutApp = CreateObject("Outlook.Application")
OutApp.Session.Logon
Set outMail = OutApp.CreateItem(0)
Set rng = ActiveSheet.Range("A1:P35")
On Error Resume Next
With outMail
.To = "coworkers#me.com"
.CC =
.BCC = ""
.Subject = "Subject"
.HTMLBody = "Hello," & vbNewLine _
vbNewLine & "Body" & _
RangetoHTML(rng) & "Thanks!"
.Display
End With
On Error GoTo 0
Set outMail = Nothing
Set OutApp = Nothing
End Sub
I redacted some information.
Function RangetoHTML(rng As Range)
' Works in Excel 2000, Excel 2002, Excel 2003, Excel 2007, Excel 2010,
' Outlook 2000, Outlook 2002, Outlook 2003, Outlook 2007, and Outlook 2010.
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 workbook to receive the data.
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , True, 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 an .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 the RangetoHTML subroutine.
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.
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Is it possible to send as picture or to change a setting in Outlook so it doesn't reformat the text?
Changing to a black font on black background or white font on white background may be the most obvious method to 'hide' cell values but it certainly isn't the best method.
Change the cell number format to a custom number format of ;;; either manually or with condition formatting. This renders the cell display appear blank while retaining the underlying value.

Issue with format when VBA runs automatically

I have a vba code which runs automatically and after processing the data it sends the output table in outlook mail body.
The issue which i am facing is, the users who check this email in outlook are able to the table in the correct format but if the same mail is viewed on their gmail account they are not able to see the format and looks like plain text.
But this issue is not happening if i manually run macro. It only happens when macro runs automatically.
Dim OutApp As Object
Dim OutMail As Object
Dim mailid As String
Dim Excelsheet As String
Dim rng As Range
Dim StrBody As String
Dim excelfile As String
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
Set rng = Sheets("Report").Range("B4:W55").SpecialCells(xlCellTypeVisible)
On Error Resume Next
With OutMail
.To = xxxx
.CC = xxxx
.Subject = "xxxx"
.HTMLBody = StrBody & RangetoHTML(rng)
.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-2010
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
Dim StrBody As String
StrBody = "Dear Team" & "<br>" & "" & "<br>" & _
"Please find Group MTD Report" & "<br><br><br>"
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
Got a solution for my issue.
It is simple, in the mentioned VBA code i made first .Display and then .Send. By running both the codes my problem solved.
Thanks guys..

How to copy the format and values without copying the CF rules from a Conditional Formatted cell using vba

I try copying the data from excel and send via e-mail automatically. everything working fine except CF. '
I apply the below code but its coping the conditional format rules also, because of that the format getting changed at the time it pasted in outlook. Kindly help us to fix this issue.
Function RangetoHTML(rng As Range)
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 paste 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
In this small example, we start with a small block of cells having Conditional Formatting. The macro:
opens Word
creates a new doc
copies the table and pastes it into the doc
saves the doc
closes both the doc and Word
Put this in a standard module. The resulting Word table should be formatted like the Excel table but with no "conditions"
Sub UsingWord()
Dim wrdApp As Word.Application
Dim wrdDoc As Word.Document
Set wrdApp = CreateObject("Word.Application")
wrdApp.Visible = True
Set wrdDoc = wrdApp.Documents.Add
Range("A1:B2").Copy
With wrdDoc
wrdApp.Selection.PasteAndFormat (wdPasteDefault)
.SaveAs ("C:\TestFolder\tdoc.docx")
.Close
End With
wrdApp.Quit
Set wrdDoc = Nothing
Set wrdApp = Nothing
End Sub
Choose your own file and folder names.
You must include a reference to the Word Object Model in your VBA > Tools > References
Conditional Formatting is considered formatting, so the xlPasteFormats is why it is pasting the CF. I would try applying
.Cells(1).FormatConditions.Delete
after you have copied the value, and right before you start the paste option.