Retroactive link between outlook and vba - excel

I’m currently working on an access Vba program in order to automatically write mails to people. However we chose to still press ‘Send’ in Outlook manually (in case there are possible issues, so we can control the mail beforehand).
Is there a way to have a link in the other direction, as in, when pressing the Send button in Outlook, getting the email address of the person back in excel? (The goal would be to make a ‘history’ sheet in order to keep track of which mails were actually sent and to whom)
Thank you!

Yes. A simple case is shown below. This is bare bones demonstrating the actions you requested.
Public variable, addressSent, holds the To address. A boolean test on mail sent (by #Rory) tests for the mail item having been sent and calls a function, by #Dwipayan Das, that opens a specified Excel file, and writes the addressSent to cell A1 in sheet1.
You can tinker with this to fit your purposes. E.g. Adapt the function to accept a file name as parameter.....
Taking a note from #ashleedawg's book: remember to include a xlApp.Quit line so Excel is not left hanging.
I believe your question wanted to go from Outlook to Excel so this is the application that you will have created that needs closing.
So in Outlook goes the following code:
Put this in a standard module:
Option Explicit
Public addressSent As String
Dim itmevt As New CMailItemEvents
Public Sub CreateNewMessage()
Dim objMsg As MailItem
Set objMsg = Application.CreateItem(olMailItem)
Set itmevt.itm = objMsg
With objMsg
.Display
.To = "somebody#mail.com"
.Subject = "Blah"
addressSent = .To
.Send
End With
End Sub
Public Function openExcel() As Boolean 'Adapted from #Dwipayan Das
Dim xlApp As Object
Dim sourceWB As Object
Dim sourceWS As Object
Set xlApp = CreateObject("Excel.Application")
With xlApp
.Visible = True
.EnableEvents = False
End With
Dim strFile As String
strFile = "C:\Users\User\Desktop\Delete.xlsb" 'Put your file path.
Set sourceWB = xlApp.Workbooks.Open(strFile, , False, , , , , , , True)
sourceWB.Activate
sourceWB.Worksheets(1).Range("A1") = addressSent
End Function
Then in a class module called CMailItemEvents, code from #Rory, put the following:
Option Explicit
Public WithEvents itm As Outlook.MailItem
Private Sub itm_Close(Cancel As Boolean)
Dim blnSent As Boolean
On Error Resume Next
blnSent = itm.Sent
If Err.Number = 0 Then
Debug.Print "not sent"
Else
openExcel
End If
End Sub
References:
Check to see if an Outlook Email was sent from Excel VBA
How can I use Outlook to send email to multiple recipients in Excel VBA
How to open an excel file in Outlook vba code
Create a new Outlook message using VBA
Run code after item sent

Just a quick 'n dirty function that will run in Excel/Access/Word and returns the email address from the most recent item in the Sent Items folder (no error handling, etc):
Function LastSentEmailAddress() As String
'Requires reference: "Microsoft Outlook xx.x Object Library"
Dim olApp As Outlook.Application, olMail As Object
Set olApp = New Outlook.Application 'create Outlook object
Set olMail = olApp.GetNamespace("MAPI").GetDefaultFolder(olFolderSentMail).Items.GetLast
LastSentEmailAddress = olMail.Recipients(1).PropertyAccessor.GetProperty( _
"http://schemas.microsoft.com/mapi/proptag/0x39FE001E") 'get email addy
olApp.Quit 'close Outlook
End Function
A Note about working with Outlook objects from Excel:
When working with applications such as Excel it's important to make sure the application object is properly .Quit / .Close'd when finished with them, (and to Set all objects to Nothing), otherwise there's a risk of inadvertently having multiple instances running, which can lead to memory leaks, which leads to crashes and potential data loss.
To check if there is an existing instance of Outlook, use this function:
Function IsOutlookOpen()
'returns TRUE if Outlook is running
Dim olApp As Outlook.Application
On Error Resume Next
Set olApp = GetObject(, "Outlook.Application")
On Error GoTo 0
If olApp Is Nothing Then
IsOutlookOpen= False
Else
IsOutlookOpen= True
End If
End Function
(Source: Rob de Bruin)
More Information:
MSDN : Items.GetLast Method (Outlook)
MSDN : Items Object (Outlook)
MSDN : Obtain the E-mail Address of a Recipient
Office.com : How to disable warnings about programmatic access to Outlook
MSDN : Chapter 17: Working with Item Bodies (Book Excerpt)
MSDN : Check or Add an Object Library Reference
Stack Overflow : VBA to search an Outlook 2010 mail in Sent Items from Excel

Related

Excel VBA Outlook16 cannot read MailItem .Body property, though Outlook14 can

I have taken on a spreadsheet that has a VBA routine to read outlook emails
It works fine for me on Excel2010 (using the Outlook Office14.0 Object library) but now doesnt work for my colleague who's on Excel2016 (he's referenced the Outlook Office16.0 Object library in the VBA references), here's the key bits of code:
Dim olItms As Outlook.Items, Dim olMail As Variant,
For Each olMail In olItms
mailContents() = Split(olMail.Body, Chr(13))
I can add a Watch and see all of the emails in the chosen folder are in the olItms array
I can view the properties for each olMail object, eg sender & time received, all look fine.
In my Excel2010 I can read the .Body property and write it to Excel etc
In his Excel2016 I can similarly add a Watch and see all of the emails
I can similarly view the properties for each olMail object
However I cannot read the .Body property, it shows as <> instead of the text and nothing is read
In his Excel2016 session I can use the VBA to open/activate the email
I can also write to the .Body property in the VBA, eg olMail.Body = "test text" works, replacing the body of text in the open/activate email with "test text"
However I still can't read the body text.
The other similar fields (HTMLBody, RTFBody) similarly show as <> with no text read
I can't see anything in his Outlook properties that could be restricting it
The emails definitely have body text in them, as they get read ok in my Excel2010
The Outlook16 object libary must be working ok as the other email properties are reading ok (unless it could be partly working ?)
Here's a copy of all the code up to the error point (with some names changed)
Sub GetIncomeUpdatesFromInbox()
Dim olApp As Outlook.Application
Dim olNs As Outlook.Namespace
Dim olFldr As Outlook.MAPIFolder
Dim olMailbox As Outlook.MAPIFolder
Dim olItms As Outlook.Items
Dim olMail As Variant, vRow As Variant
Dim i As Long
Dim FolderAddress As String, arrFolders() As String, mailContents() As String
Dim EarliestDate As Date
Set olApp = New Outlook.Application
Set olNs = olApp.GetNamespace("MAPI")
On Error Resume Next
Set olMailbox = olNs.Folders("mailbox#company.com").Folders("Inbox")
'Produces the relevant folder as a string
If Range("FolderAddress") = "Update" Or Range("FolderAddress") = "Create" Then
FolderAddress = "\\mailbox#company.com\*Folders\Data\xxx\"
Else
FolderAddress = "\\mailbox#company.com\*Folders\Data\xxx\Update\"
End If
FolderAddress = FolderAddress + Range("FolderAddress")
'changes Folder address into an array
arrFolders() = Split(FolderAddress, "\")
'Enters first part of fodler address
Set olFldr = olNs.Folders.Item(arrFolders(2))
'Navigates to relevant folder
If Not olFldr Is Nothing Then
For i = 3 To UBound(arrFolders)
Set colFolders = olFldr.Folders
Set olFldr = Nothing
Set olFldr = colFolders.Item(arrFolders(i))
If olFldr Is Nothing Then
Exit For
End If
Next
End If
Application.DisplayStatusBar = True
Set olItms = olFldr.Items
'Sorts emails by date received
olItms.Sort “Received”
i = 1
UserForm1.TextBox1 = Format(CDate(Evaluate("WORKDAY(TODAY(),-1)")), "dd/mm/yyyy")
UserForm1.TextBox2 = Format(CDate(Evaluate("WORKDAY(TODAY(),-0)")), "dd/mm/yyyy")
UserForm1.Show
EarliestDate = UserForm1.TextBox1
LatestDate = UserForm1.TextBox2
'moves through mails one by one for all emails received after specified earliest date"
iColumn = 3
For Each olMail In olItms
If LatestDate > CDate(olMail.ReceivedTime) Then
If CDate(olMail.ReceivedTime) > EarliestDate Then
'Splits content of the mail into an array with each element of the array one line in the original email
mailContents() = Split(olMail.Body, Chr(13))
Try to use the GetInspector or Display method before getting the message body.
Another point is a security trigger in the Outlook object model. Outlook may restrict access to secure properties when you automate the host from another process. You may try to run the same code from a COM add-in where you deal with a safe Application instance which doesn't trigger a security issue. There are several ways for suppressing such issues when dealing with OOM:
Use a third-party components for suppressing Outlook security warnings/issues. See Security Manager for Microsoft Outlook for more information.
Use a low-level API instead of OOM. Or any other third-party wrappers around that API, for example, Redemption.
Develop a COM add-in which has access to the trusted Application object. And then communicate from a standalone application with an add-in using standard .Net tools (Remoting).
Use group policy objects for setting up machines.
Install any AV software with the latest databases (up to date).
There are other aspects in the code listed above. Let's cover them in depth.
Instead of using the following code:
Set olMailbox = olNs.Folders("mailbox#company.com").Folders("Inbox")
You need to use the GetDefaultFolder method of the Namespace or Store class which is similar to the GetDefaultFolder method of the NameSpace object. The difference is that this method gets the default folder on the delivery store that is associated with the account, whereas NameSpace.GetDefaultFolder returns the default folder on the default store for the current profile.
Iterating over all items in the folder is not really a good idea:
For Each olMail In olItms
If LatestDate > CDate(olMail.ReceivedTime) Then
If CDate(olMail.ReceivedTime) > EarliestDate Then
Use the Find/FindNext or Restrict methods of the Items class instead. Read more about these methods in the following articles:
How To: Retrieve Outlook calendar items using Find and FindNext methods
How To: Use Restrict method in Outlook to get calendar items

"Method 'body' of object'_mailItem' failed" error in Excel and Outlook 2016

I wrote a program in Excel 2010 to sift through emails in a selected Outlook 2010 folder, and pull in information from the email (html) body.
I updated to Office 2016. Since then, I get an error when using certain properties of the MailItem object. I can pull the subject of the email into Excel, but certain properties cause a "method 'body' of object'_mailItem" failed error (including the .Body and .To properties).
Below is a simplified version of the code:
Sub GatherInfo()
Dim ObjOutlook As Object
Dim MyNamespace As Object
Dim FormFolder As Object
Set ObjOutlook = GetObject(, "Outlook Application")
Set MyNamespace = ObjOutlook.GetNamespace("MAPI")
Set FormFolder = MyNamespace.PickFolder
For i = 1 To FormFolder.Items.Count
Range("A2").Select
ActiveCell.Value = FormFolder.Items(i).Subject
ActiveCell.Offset(0, 1).Value = FormFolder.Items(i).To
End Sub
This results in:
Run-time error '-2147467259(80004005)':
Method 'To' of object'_MailItem' failed
I've done some research, and wondering if Outlook 2016 security settings could be to blame.
This is a corporate email account, running on an exchange server. Do you think that could be preventing me from accessing the body/sender of the email?
It's strange that the subject property of the email works, but not the body/to properties.
Things I've ruled out:
1) I've sent both plain text and html based emails with the same result.
2) I've tried binding the Outlook objects early (Dim ObjOutlook as Outlook.Application, etc.)
I ensured there were only mail items and no calendar items, etc.
It'll trip out the first time it hits the item.To assignment. If I insert a line to resume next then it'll go through all the emails, but will only record the subject and not the .To property.
Avoid using multiple dot notation and check if you really have a MailItem object (you can also have ReportItem or MeetingItem):
set items = FormFolder.Items
For i = 1 To items.Count
set item = items.Item(i)
if item.Class = 43 Then
Range("A2").Select
ActiveCell.Value = item.Subject
ActiveCell.Offset(0, 1).Value = item.To
End If
set item = Nothing
next
set items = Nothing

Detect when a new e-mail has been created (WithEvents)

I am trying to call a procedure stored in Outlooks' 'ThisOutlookSession', from an Excel workbook. Unfortunately the Newer Outlook 2010 app does not have compatibility with the application.run *SubName* between MS office products.
It is not an option to complete an Excel script that sends the email on Outlooks behalf due to security messages on '.send' which requires a manned station. (& unable to change security settings from company policy)
Current workflow...
-User sends me an e-mail with 'command' in subject & attachments
-Event listener finds and successfully runs an Excel routine on attachments with the below headers for listening in Outlook
Private WithEvents Items As Outlook.Items
&
Private Sub Items_ItemAdd(ByVal Item As Object)
-Once processed in Excel, I am trying to get this data automatically returned to sender. (This is where the problem is)
I am using late binding in Excel to create and ready the return e-mail. It is one step before '.send'. I would ideally like to avoid a SendKeys statements because it is not fully reliable if working on other workbooks at the same time.
In Excel...
Sub test()
Dim aOutlook As Object
Dim aEmail As Object
Set aOutlook = CreateObject("Outlook.Application")
Set aEmail = aOutlook.CreateItem(0)
aEmail.To = "My email address"
aEmail.Subject = "Testing"
aEmail.Body = "Testing"
aEmail.display
End Sub
I have been endlessly trying to get Outlook to recognise Excel creating this new email with events listed in the MSDN pages. I think something like the below code is what is needed to identify a new mailitem, but no success using most of the preset declarations under the Outlook.mailItem.
Private WithEvents NewItem As Outlook.mailItem
Please let me know if you have solution or an alternative idea for me to pursue
Solved, if anyone needs this in the future. It bypasses security warnings and does not rely on send-keys.
Excel prepares an email and displays it - when prepared, the 'On event' from Outlook recognises the 'to' address when the mailitem is loaded, and can then take over from there: finishing with a .send statement.
Note, more parameters will need to be added such as a specific code in the item.subject to ensure that its the correct e-mail that's being sent.
In Excel:
Sub test()
Dim aOutlook As Object
Dim aEmail As Object
Set aOutlook = CreateObject("Outlook.Application")
Set aEmail = aOutlook.CreateItem(0)
aEmail.To = "abs#123.com"
aEmail.Subject = "Testing"
aEmail.Body = "testing"
aEmail.Display
End Sub
In Outlook:
Public WithEvents myItem As Outlook.mailItem
Private Sub Application_ItemLoad(ByVal Item As Object)
If (TypeOf Item Is mailItem) Then
Set myItem = Item
End If
End Sub
Private Sub myItem_Open(Cancel As Boolean)
If myItem.To = "abs#123.com" Then
MsgBox "Detected"
myItem.Send
End If
End Sub

Excel VBA, return GAL value

i have a very simple small app in mind which will help me save time from alt tabbing from excel to outlook. i want to create a small userform that will have a textbox for a exchange user alias and return the exchange user's full name. now the issue i have here is that the guide in msdn is a little vague for a userform: https://msdn.microsoft.com/en-us/library/office/ff869721.aspx and i'm getting some error messages, some got fixed by activating some references. and the code is quite complicated.
so basically i have 2 textboxes and a button. textbox1 will accept the alias, textbox2 will return the username after clicking the button.
there are several examples but most of them will result in dumping the GAL to an excel file which i don't need.
thanks in advanced
This will give you what you want.
Private Function GetFullName(inAlias As String) As String
Dim olApp As Outlook.Application
Dim olNS As Outlook.Namespace
Dim olAdd As Outlook.AddressEntries
Dim olMem As Outlook.AddressEntry
Dim olLst As Outlook.AddressList
Dim olAlias As String
On Error Resume Next
Set olApp = New Outlook.Application
On Error GoTo 0
If olApp Is Nothing Then
GetFullName = "Source not available"
Exit Function
End If
Set olNS = olApp.GetNamespace("MAPI")
Set olLst = olNS.GetGlobalAddressList
Set olAdd = olLst.AddressEntries
For Each olMem In olAdd
On Error Resume Next
olAlias = olMem.GetExchangeUser.Alias
On Error GoTo 0
If olAlias = inAlias Then
GetFullName = olMem.GetExchangeUser.Name
Exit For
Else
GetFullName = "Invalid Alias"
End If
Next
Set olApp = Nothing: Set olNS = Nothing: Set olAdd = Nothing
End Function
The draw back is this may take a while if your GAL is quite large.
I'll check if I can dump the list to an array first and then manipulate from there.
Or if there is another way to get to the name via alias using other method.
But for now, try learning from this first.
This is a function, so to get it to your textbox, you can simply:
TextBox2 = GetFullname(TextBox1)
Note:
I intentionally declared all the objects, you need to know what type of object you're working on. Also I used On Error Resume Next because there are AddressEntry without Alias and that gives error. That's the easiest work around I can think of for now.
Requirement:
You need to reference Microsoft Outlook xx.x Object Library.
xx.x vary depending on the Outlook version installed in your machine.

Extract Outlook body to Excel VBA

after searching multiple things, and getting errors
How do I upon pressing "f5" in a vba script copy the body of an email into an excel sheet /csv
where every line = a new cell below.
Thanks
Sorry, this is causing me nothing but trouble.
What I have tried so far
http://smallbusiness.chron.com/export-outlook-emails-excel-spreadsheets-41441.html
How to copy Outlook mail message into excel using VBA or Macros
http://www.vbforums.com/showthread.php?415518-RESOLVED-outlook-the-macros-in-this-project-are-disabled
http://www.ozgrid.com/forum/showthread.php?t=181512
and a few more, last year.
This will work for you. we are basically splitting the email body into an array based on a new line. Notice that this will yield blank cells if you had a blank line in the email body.
Public Sub SplitEmail() ' Ensure reference to Word and Excel Object model is set
Dim rpl As Outlook.MailItem
Dim itm As Object
Set itm = GetCurrentItem()
If Not itm Is Nothing Then
Set rpl = itm.Reply
rpl.BodyFormat = olFormatHTML
'rpl.Display
End If
Dim objDoc As Word.Document
Set objDoc = rpl.GetInspector.WordEditor
Dim txt As String
txt = objDoc.Content.text
Dim xlApp As Excel.Application
Set xlApp = CreateObject("Excel.application")
xlApp.Visible = True
Dim wb As Excel.Workbook
Set wb = xlApp.Workbooks.Add
Dim i As Long
For i = LBound(Split(txt, Chr(13)), 1) To UBound(Split(txt, Chr(13)), 1)
wb.Worksheets(1).Range("A" & i + 1).Value = Split(txt, Chr(13))(i)
Next i
End Sub
Function GetCurrentItem() As Object
Dim objApp As Outlook.Application
Set objApp = Application
On Error Resume Next
Select Case TypeName(objApp.ActiveWindow)
Case "Explorer"
Set GetCurrentItem = objApp.ActiveExplorer.Selection.Item(1)
Case "Inspector"
Set GetCurrentItem = objApp.ActiveInspector.CurrentItem
End Select
GetCurrentItem.UnRead = False
Set objApp = Nothing
End Function
The Outlook object model doesn't recognize lines in the body. You can try to resize any inspector window in Outlook and see how the body lines are changed.
Anyway, you may try to use the Word object model to get the exact lines. Outlook uses Word as an email editor. The WordEditor property of the Inspector class returns an instance of the Document class which represents the message body. You can read more about all possible ways in the Chapter 17: Working with Item Bodies article.
The How to automate Microsoft Excel from Visual Basic article explains how to automate Excel from any external application.

Resources