Running an Excel macro from Outlook - excel

I am trying to set up an automated database in Excel which sends an email when a user has equipment signed out past the expected due date.
I also want to implement a feature where the user can reply to the email with a keyword 'extend' to extend their sign out date by 7 days.
I have the email sending correctly. I want to create a script in Outlook that links their reply to the Excel worksheet that is open. Both Outlook and Excel will be open at the same time on a dedicated PC. I don't want to open a new Excel file every time the Outlook macro runs.
Here is the Outlook macro:
Sub Exc_macro(Item As Outlook.MailItem)
Dim ExApp As Workbook
Dim gageID As String
Dim cap As String
If Left(Item.Body, 6) = "extend" Then 'Check for keyword in body
gageID = Mid(Item.Subject, 23) 'Get equipment ID number
Set ExApp = Excel.ActiveWorkbook
Call ExApp.Application.Run("Module2.increase", gageID)
End If
End Sub
I want to pass the gageID argument to the Excel macro here:
Sub increase(gageID As String)
Set Rng = Range(Range("A2"), Range("A" & Rows.Count).End(xlUp))
For Each cell In Rng
If cell.Value = gageID Then
cell.Offset(0, 9).Value = cell.Offset(0, 9).Value + 7
End If
Next
End Sub
How do I reference the open Workbook in Outlook and subsequently run the Excel macro?

Like this:
Sub Exc_macro(Item As Outlook.MailItem)
Dim ExApp As Object
Dim gageID As String
Dim cap As String
If Left(Item.Body, 6) = "extend" Then 'Check for keyword in body
gageID = Mid(Item.Subject, 23) 'Get equipment ID number
Set ExApp = GetObject(,"Excel.Application")
ExApp.Run "'my ExcelWB.xlsm'!increase", gageID
End If
End Sub
Make your increase Sub is in a regular code module
http://www.rondebruin.nl/win/s9/win001.htm

Related

How to call MailItem.Display method when clicking on a table cell?

I need to call the Outlook MailItem.Display method when clicking on a specific cell in a table column in Excel.
Below is my module for filling out table.
' This module performs email retrieval and viewing. Dynamically adds email information to a table and creates
' links that open Outlook mailitems in a modal window.
Option Explicit
'Initialize Outlook objects
Dim appOL, appNS, appFolder, email As Object
'initialize ListObject
Dim tbl As ListObject
'Add email information to tbl_email_data
Public Sub addDataToEmailTable()
'GetDefaultFolder(6) is "Inbox" of whoever is signed into Outlook desktop version.
'Does not account for subfolders in Inbox and does not work with Web Outlook version.
Set appOL = CreateObject("Outlook.Application")
Set appNS = appOL.GetNamespace("MAPI")
Set appFolder = appNS.GetDefaultFolder(6)
'initialize table
Set tbl = ThisWorkbook.Worksheets("Email").ListObjects("tbl_email_data")
Dim rowCount As Long
rowCount = 1
If tbl.DataBodyRange Is Nothing Then
tbl.ListRows.Add
End If
'loop through emails and put information into tbl_email_data
For Each email In appFolder.Items
If email.Unread = True Then
tbl.DataBodyRange.Cells(rowCount, 1).Value2 = "Unread"
Else
tbl.DataBodyRange.Cells(rowCount, 1).Value2 = "Read"
End If
tbl.DataBodyRange.Cells(rowCount, 2).Value2 = email.SenderName
tbl.DataBodyRange.Cells(rowCount, 3).Value2 = email.SentOn
tbl.DataBodyRange.Cells(rowCount, 4).Value2 = email.Subject
rowCount = rowCount + 1
Next email
End Sub
I was going to create a userform with a comboBox so when selected a textbox fills with item.body.
This does not account for embedded images, and HTML formatted messages.
I saw Outlook has a method for mailitem that opens the email directly without exiting Excel.
So I figured out how to call the MailItem.Display method based on a table that represents email information in the Inbox folder of Outlook. Lots of trial and error but I got it to work. Below is the full code for the module that will handle all of this.
Option Explicit
Public excelInbox As Collection
Dim appOL, appNS, appInbox, appItem As Object
Public isOnline As Boolean
Public Function checkConnection(status As Boolean)
Set appOL = CreateObject("Outlook.Application")
Set appNS = appOL.GetNameSpace("MAPI")
If appNS.Offline = True Then
MsgBox "Outlook account is not connected to Exchange server. Please verify network connection to get updated Inbox preview"
status = False
Set appNS = Nothing
Set appOL = Nothing
Else
MsgBox "Outlook account is online"
status = True
End If
Set appInbox = appNS.GetDefaultFolder(6)
Set excelInbox = New Collection
End Function
Public Sub makeExcelInbox()
Call checkConnection(isOnline)
If isOnline <> True Then Exit Sub
Set appInbox = appNS.GetDefaultFolder(6) '6 is the enumeration for Inbox root folder in Outlook.
'loop and place only emails into excel Inbox.
For Each appItem In appInbox.Items
If appItem.Class = 43 Then excelInbox.Add appItem '43 represents a mail item in Outlook.
Next appItem
End Sub
Public Sub makeEmailPreviewTable()
Call makeExcelInbox
If excelInbox.Count <> 0 Then
Dim tbl As ListObject
Dim rowCount As Integer
Set tbl = ws_email.ListObjects("tbl_emailData")
rowCount = 1
For Each appItem In excelInbox
If appItem.Unread = True Then
tbl.DataBodyRange.Cells(rowCount, 1).Value2 = "Unread"
Else
tbl.DataBodyRange.Cells(rowCount, 1).Value2 = "Read"
End If
tbl.DataBodyRange.Cells(rowCount, 2).Value2 = appItem.SenderName
tbl.DataBodyRange.Cells(rowCount, 3).Value2 = appItem.SentOn
tbl.DataBodyRange.Cells(rowCount, 4).Value2 = appItem.Subject
rowCount = rowCount + 1
Next appItem
Set tbl = Nothing
ElseIf excelInbox.Count = 0 Then MsgBox "No messages to show in Inbox Preview."
End If
End Sub
Public Function getEmailForDisplay(Target As Range)
'Call makeExcelInbox
For Each appItem In excelInbox
If Target.Value = appItem.Subject Then appItem.Display
Next appItem
End Function
I used the selection change event in the worksheet that has the table to pass the target range value to a function that checks if that value is the same as the subject property of an email in the inbox. It is not the prettiest code, but for any others that come across this with a similar problem this should at least get you on the right path. Here is the worksheet code for event below.
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Check for selection of a cell in tbl_emailData column Subject, then pass the value to a function.
Dim rng As Range
Dim tableRange As Range
Set tableRange = ListObjects("tbl_emailData").DataBodyRange
Dim rowCount As Long
rowCount = 1
If Intersect(Target, tableRange) Is Nothing Then Exit Sub
'check for valid target location
For Each rng In tableRange
On Error GoTo ErrorHandler
If Target = tableRange(rowCount, 4) Then
Call getEmailForDisplay(Target)
Else
rowCount = rowCount + 1
End If
Next rng
ErrorHandler:
Exit Sub
End Sub
Just an important note, I am still designing this program so if you sample the code you have to make sure you have a table called "tbl_emailData" and a worksheet called "ws_email". Then when you want to run the code, make sure to run the sub "makeEmailPreviewTable" first. In my design the worksheets and cells will all be locked so only the subject column cells will be selectable, this prevents run-time errors in case the user selects more than one cell.
Update: Added errorhandling to the selection event to ignore multi-selection errors. This will ignore, and then when a proper cell is selected then display the email in a Outlook modal.

Convert Outlook Contact Group early binding Excel VBA to late binding

I am trying to insert a list of email addresses from Excel into a contact group in Outlook.
I found Excel VBA code online. It uses early binding. It is not an option to force the user to go into Tools-> References -> Outlook, when they open the file.
I need to transform the code from early to late binding.
Questions:
I understand that I need to change Outlook.Application to
CreateObject('Outlook.Application') and that I can access
olFolderContacts with the number 10 instead. See code below.
I can't figure out how to access the remaining items such as
CreateItem(olDistributionListItem).
Sub CreateContactGroupfromExcel()
Dim objContactsFolder As Outlook.Folder
Dim objContact As Outlook.ContactItem
Dim objContactGroup As Outlook.DistListItem
Dim objNameCell As Excel.Range
Dim objEmailCell As Excel.Range
Dim strName As String
Dim strEmail As String
Dim objTempMail As Outlook.MailItem
Dim objRecipients As Outlook.Recipients
Set objContactsFolder = Outlook.Application.Session.GetDefaultFolder(olFolderContacts)
Set objContactGroup = Outlook.Application.CreateItem(olDistributionListItem)
'You can change the contact group name
objContactGroup.DLName = "PlaceHolder_VBA"
i = 0
Do While Range("vba_email_outlook").Offset(i, 0).Value <> "":
strName = Range("vba_name_outlook").Offset(i, 0).Value
strEmail = Range("vba_email_outlook").Offset(i, 0).Value
Set objContact = objContactsFolder.Items.Find("[FullName] = '" & strName & "'")
'If there is no such a contact, create it.
If objContact Is Nothing Then
Set objContact = Outlook.Application.CreateItem(olContactItem)
With objContact
.FullName = strName
.Email1Address = strEmail
.Save
End With
End If
'Add the contacts to the new contact group
Set objTempMail = Outlook.CreateItem(olMailItem)
objTempMail.Recipients.Add (strName)
Set objRecipients = objTempMail.Recipients
objContactGroup.AddMembers objRecipients
i = i + 1
Loop
'Use "objContactGroup.Save" to straightly save it
objContactGroup.Display
objTempMail.Close olDiscard
End Sub
Declare object variables as generic Object
Dim objContactsFolder As Object
Determine number values of constants. With early binding, these values can be seen when hovering over constant or in VBA immediate window: ?olMailItem. Then reference number in place of constant or leave constants referenced as they are and declare them as constants with Const statements. Const olMailItem = 0
olFolderContacts = 10
olMailItem = 0
olDistributionListItem = 7
I am not an expert but this code allows you to add the reference when you run the VBA script, but it will mean that if it errors out the code quits running you will not be able to debug.
On Error Resume Next ''' If reference already exist this would cause an error
Application.VBE.ActiveVBProject.References.AddFromFile ("C:\Program Files (x86)\Microsoft Office\Office16\MSOUTL.OLB") ''' Might have to change file path
On Error GoTo 0

VBA Loop to go through table and export chart to each email

I need an excel file to email an exported chart to a varying number of contacts on open. For every email, the chart needs to be refiltered. I figured out how to do this by creating a dynamic chart with a scrollbar and on each iteration of the loop I will at 13 to its position (p).
How do I get my VBA code to send an email with the exported chart to whatever is in column 2? It also is currently only sending one email, rather than however many are in the column. Any help would be awesome.
Private Sub Workbook_Open()
Dim b1 As Workbook, b2 As Workbook
Dim sh As Worksheet
Set b1 = ThisWorkbook
Dim olApp As Object
Dim olMail As Object
Dim i As Long
Dim p As Integer
Dim email As Range
Dim book As Range
Set olApp = CreateObject("Outlook.application")
Set olMail = olApp.createitem(i)
Set book = Range("A1:B9")
p = 1
'START LOOP
For Each email In book.Rows
Sheets("nothing").Range("B1").Select
ActiveCell.FormulaR1C1 = p
Worksheets(1).ChartObjects(1).Activate
ActiveChart.Export "testchartlocation.png"
With olMail
.To = "test#email.com"
.Subject = "Emailer Testing..."
.HTMLbody = "<html><p>Testing...</p><img src='testchartlocation.png'>"
.display
End With
p = p + 13
Application.Wait (Now + TimeValue("0:00:01"))
Next
'END LOOP
'ThisWorkbook.Close False
End Sub
If by
How do I get my VBA code to send an email with the exported chart to
whatever is in column 2?
You mean you have email addresses stored in column 2 that you need to access with each iteration to send the exported chart to, you could change this line
.To = "test#email.com"
To
.To = Cells(email.Row, 2) '<-Make sure to qualify this range with whatever worksheet you're pulling from
Concerning your issue with your email only being generated once, you need to move
Set olMail = olApp.createitem(i) '<- you can change `i` to `0`
Into the beginning of your For-Next loop and set it = Nothing at the end like
For Each email In book.Rows
Set olMail = olApp.createitem(0)
'Do Stuff
Set olMail = Nothing
Next email
That way a new email is generated every iteration.
EDIT:
You can probably get rid of this line
Sheets("nothing").Range("B1").Select
And replace
ActiveCell.FormulaR1C1 = p
With
Sheets("nothing").Range("B1").FormulaR1C1 = p
Since you're working with multiple sheets and .Activate functions, I would recommend qualifying all of your ranges.

How to insert a hyperlink and table into a cell in the Excel sheet?

This is the code in outlook VBA
Sub Sendmail()
Dim olItem As Outlook.MailItem
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSht As Excel.Worksheet
Dim sPath As String
Dim iRow As Long
Dim strRFIitems As String
Dim Signature As String
sPath = "**"
' // Excel
Set xlApp = CreateObject("Excel.Application")
' // Workbook
Set xlBook = xlApp.Workbooks.Open(sPath)
' // Sheet
Set xlSht = xlBook.Sheets("Sheet1")
' // Create e-mail Item
Set olItem = Application.CreateItem(olMailItem)
strRFIitems = xlSht.Range("E2")
Signature = xlSht.Range("F2")
With olItem
.To = Join(xlApp.Transpose(xlSht.Range("A2", xlSht.Range("A9999").End(xlUp))), ";")
.CC = Join(xlApp.Transpose(xlSht.Range("B2", xlSht.Range("B9999").End(xlUp))), ";")
.Subject = xlSht.Range("C2")
.Body = xlSht.Range("D2") & Signature
.Attachments.Add (strRFIitems)
.Display
End With
' // Close
xlBook.Close SaveChanges:=True
' // Quit
xlApp.Quit
Set xlApp = Nothing
Set xlBook = Nothing
Set xlSht = Nothing
Set olItem = Nothing
End Sub
The code retrieves the data from the linked Excel sheet. Now the
problem is with .Body = xlSht.Range("D2") & Signature this line
of code, where the body of the mail is retrieved from the D2 cell of
Excel sheet.
And as per my requirement, the body of the mail should contain a hyper
link and table along with the text.
Example:
Hello All,
Please update the details in the portal
portal link :http://google.com.
Please contact me for any clarifications.
Below mentioned details needs to be updated:
table has to be inserted here
Suppose above mentioned text is inserted into a cell of Excel.
List item
This portal link: http://google.com. becomes a plain text not a hyper link.
If I try to make it a hyper link the entire cell becomes hyper link. i.e even the text.
List item
How to insert table into a cell of Excel sheet and call it using Outlook VBA
Query:
How to insert a hyperlink and table along with the test
message into a cell of Excel and retrieve it as it is using the above
mentioned code and send a mail containing hyperlink and table.
For hyperlink you can use the following code:
Range("K6").Select
ActiveCell.FormulaR1C1 = "test"
Range("K6").Select
ActiveSheet.Hyperlinks.Add Anchor:=Selection, Address:= _
"http://www.google.com", TextToDisplay:="test"
Range("K6").Select
Selection.Hyperlinks(1).Follow NewWindow:=False, AddHistory:=True

Excel Automation Error Run-time Error 440- Autocorrecting Outlook Appointment .Start

I have been searching how to fix this problem for sometime. It's hard to find specific answers without having to dive deep into VBA for outlook, which I would do if I have to.
I have a calendar update macro that should be adding appointments to our outlook calendar. I inherited the code, and just copied it and pasted it over, making some minor tweaks as far as cell references. The codes is below:
Sub CreateNewItems()
Dim dimnum As Integer
Dim num As Integer
Dim objOL 'As Outlook.Application
Dim objApt 'As Outlook.AppointmentItem
Dim objNamespace
Dim strFolderName
Dim objCalendar
Dim objInbox
Dim pctCompl As Single
Const olMeeting = 1
Const olFolderInbox = 6
Const olAppointmentItem = 1 '1 = Appointment
'do not display alerts
Application.DisplayAlerts = False
'do not update screen
Application.ScreenUpdating = False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Create new Undergrad info session events on Outlook Calendar
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Windows("WebScraper.xlsm").Activate
Sheets("Calendar Info Sess. Bridge").Activate
Set ws = ActiveSheet
ws.Range("B2").Select
ws.Range(Selection, Selection.End(xlDown)).Select
dimnum = Selection.count
ws.Range("B2").Select
num = 0
Do Until num = dimnum
Set objOL = CreateObject("Outlook.Application")
Set objNamespace = objOL.GetNamespace("MAPI")
'Finds your Inbox
Set objInbox = objNamespace.GetDefaultFolder(olFolderInbox)
'Gets the parent of your Inbox which gives the Users email
strFolderName = objInbox.Parent
Set objCalendar = objNamespace.Folders(strFolderName).Folders("Calendar").Folders("Undergrad TNRB")
Set objApt = objCalendar.Items.Add(olAppointmentItem)
With objApt
.Subject = ActiveCell.Offset(0, 9).Value
.Location = ActiveCell.Offset(0, 4).Value
.start = ActiveCell.Offset(0, 1).Value & ActiveCell.Value
.End = ActiveCell.Offset(0, 3).Text & ActiveCell.Offset(0, 2).Value
.Save
End With
ActiveCell.Offset(1, 0).Select
num = num + 1
Loop
End Sub
From what I can tell, the problem is that in the With block, it should be .Start instead of .start. Unfortunately, every time I try to capitalize, VBA autocorrects back to .start. I do have some other subs that run before this code, but I removed any instance where I used the word "start", whether it was capitalized or not, commented, or executable code, so I'm not creating any variable unknowingly that this line would be trying to reference (as far as I can tell). I thought it might be that .Start is an VBA defined function, but I don't know enough to know if that's true, or what the needed convention would be to get this to save the appointment to outlook.
You can see that I am getting the number of rows in the sheet in the first couple of lines, and then I repeat the Do loop for every row of the sheet. Just as is intuitive in the With block, 9 columns to the right is a subject line for the appointment, 4 columns to the right is the location of the appointment, the active column is the date of the appointment starting, the next column over is the date the appointment ends (all events start and end on the same date), the column 2 to the right is the start time, and the column 3 to the right is the end time.
When I was running through the code, the time columns (columns 1 to the right and 3 to the right) were narrow, and visually these columns showed #s (just like excel does when the column isn't fully expanded). For some reason, the code was pulling the #s, rather than the actual value. It may have something to do with the .Text, but when I used .Value, it still wasn't working. So, if anyone else has this problem, try Columns.EntireColumn.Autofit on the columns that are too narrow that you're trying to pull values from.

Resources