I have copy & pasted this code and modified it from a forum in Google to create a task in my Outlook based on what is on my form
With CreateObject("Outlook.Application").CreateItem(3)
.Subject = UserForm1.TextBox1.Value & " " & UserForm1.ComboBox1.Value
.StartDate = UserForm1.MonthView1.Value
.DueDate = UserForm1.MonthView1.Value
.ReminderTime = UserForm1.MonthView1.Value - 1
.Body = UserForm1.TextBox1.Value & " " & UserForm1.ComboBox1.Value
.Save
End With
The code doesn't crash when I debug it, my program goes through it fine. Is the method right?
Do I have to invoke CreateObject somewhere or can I just create the task from the With clause?
To start an Outlook automation session, you can use either early or late binding. Late binding uses either the GetObject or the CreateObject function to initialize Outlook. For example, the following code sets an object variable to the Outlook Application object, which is the highest-level object in the Outlook object model. All automation code must first define an Outlook Application object to be able to access any other Outlook objects.
Dim objOL as Object
Set objOL = CreateObject("Outlook.Application")
To use early binding, you first need to set a reference to the Outlook object library. You can then use the following syntax to start an Outlook session.
Dim objOL as Outlook.Application
Set objOL = New Outlook.Application
Most programming solutions interact with the data stored in Outlook. Outlook stores all of its information in Messaging Application Programming Interface (MAPI) folders. After you set an object variable to the Outlook Application object, you will commonly set a Namespace object to refer to MAPI, as shown in the following example.
Set objOL = New Outlook.Application
Set objNS = objOL.GetNameSpace("MAPI")
Set objFolder = objNS.GetDefaultFolder(olFolderContacts)
Once you have set an object variable to reference the folder that contains the items you wish to work with, you use appropriate code to accomplish your task, as shown in the following example.
Sub CreateNewDefaultOutlookTask()
Dim objOLApp As Outlook.Application
Dim NewTask As Outlook.TaskItem
' Set the Application object
Set objOLApp = New Outlook.Application
' You can only use CreateItem for default items
Set NewTask = objOLApp.CreateItem(olTaskItem)
' Display the new task form so the user can fill it out
NewTask.Display
' you can replace the .Display method with your own code for setting up the task item
End Sub
P.S. Don't forget to add an Outlook COM reference. On the Tools menu, click References. In the References box, click to select the Microsoft Outlook Object Library, and then click OK.
NOTE: If the Microsoft Outlook Object Library does not appear in the Available References box, browse your hard disk for the file, Msoutl*.olb. If you cannot locate this file, you must run the Microsoft Outlook Setup program to install it before you proceed further.
Related
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
I am trying to code a macro which checks the mails of the default outlook folder of the last seven days and extracts the body of the mail to an existing excel sheet if the mail contains a specific subject and sender name.
I already coded a macro, which checks every new mail as soon as received and extracts the content to excel if the specific subject & sender name is given. Although it worked, it was not a good solution for me to automatically check every new incoming mail. The code is:
Option Explicit
Private WithEvents olItems As Outlook.Items
Private Sub Application_Startup()
'Variablen dimensionieren
Dim olApp As Outlook.Application
Dim olNS As Outlook.Namespace
'Variabeln initialisieren
Set olApp = Outlook.Application
Set olNS = olApp.GetNamespace("MAPI")
Set olItems = olNS.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub olItems_ItemAdd(ByVal item As Object)
'Variablen dimensionieren
Dim olMail As Outlook.MailItem
Dim oxLApp As Object, oxLwb As Object, oxLws As Object
'Prüfen ob Item eine Mail ist
If TypeName(item) = "MailItem" Then
Set olMail = item
If InStr(olMail.Subject, "APPROVAL REQUIRED") And _
olMail.SenderName = "Test, Name" Then
Set oxLApp = GetObject(, "Excel.Application")
Set oxLwb = oxLApp.Workbooks.Open _
("C:\Users\A2000\Desktop")
Set oxLws = oxLwb.Sheets("Slide 3")
With oxLws
.Range("Q24") = olMail.VotingResponse
.Range("E41") = olMail.Body
End With
End If
End Sub
Any ideas how to check the mails of the last seven days?
You can use the Restrict or Find/FindNext methods of the Items class to get items that corresponds to the search criteria. Read more about them in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
The search criteria can be:
sFilter = "[RecievedTime] > '" & Format("1/15/22 3:30pm", "ddddd h:nn AMPM") & "'"
The MailItem.ReceivedTime property returns a Date indicating the date and time at which the item was received.
If you need to get such items from multiple folders at once you may consider using the AdvancedSearch method of the Application class. The key benefits of using the AdvancedSearch method in Outlook are:
The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
Full support for DASL queries (custom properties can be used for searching too). To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
You can stop the search process at any moment using the Stop method of the Search class.
Read more about that in the Advanced search in Outlook programmatically: C#, VB.NET article.
I wrote VBA code in Outlook to use AdvancedSearch. It worked.
When I moved it to Excel to be part of a larger routine, the event handlers stopped working.
The main code looks something like this.
Public gblnProcessAttachmentsDone As Boolean
Public gblnProcessAttachmentsStopped As Boolean
Sub ProcessAttachmentsSub()
' this routine performs the advanced search on a folder
...
gblnProcessAttachmentsDone = False
gblnProcessAttachmentsStopped = False
...
'perform search
Set objSearch = objOL.AdvancedSearch(strScope, strFilter, True, "ProcessAttachments")
Do Until gblnProcessAttachmentsDone
DoEvents
Loop
These are the event handlers.
Private Sub Application_AdvancedSearchComplete(ByVal SearchObject As Outlook.Search)
' this routine identifies the search that has just completed
If SearchObject.Tag = "ProcessAttachments" Then
Debug.Print "Search completed at " & Time
gblnProcessAttachmentsDone = True
End If
End Sub
Private Sub Application_AdvancedSearchStopped(ByVal SearchObject As Outlook.Search)
' this routine identifies the search that has just been stopped by the user
If SearchObject.Tag = "ProcessAttachments" Then
Debug.Print "Search stopped at " & Time
gblnProcessAttachmentsStopped = True
gblnProcessAttachmentsDone = True
End If
End Sub
I tried placing them in 'ThisWorkbook' and a Class module, but in both cases the events never get caught.
In Excel VBA, Application intrinsic variable points to Excel.Application, not Outlook.Application. Your event handler (Application_AdvancedSearchStopped) will not be automatically hooked up. Declare objOL with events and set up the event handler,.
To start an Outlook Automation session, you can use either early or late binding. Late binding uses either the Visual Basic GetObject function or the CreateObject function to initialize Outlook. For example, the following code sets an object variable to the Outlook Application object, which is the highest-level object in the Outlook object model. All Automation code must first define an Outlook Application object to be able to access any other Outlook objects.
Dim objOL as Object
Set objOL = CreateObject("Outlook.Application")
To use early binding, you first need to set a reference to the Outlook object library. Use the Reference command on the Visual Basic for Applications (VBA) Tools menu to set a reference to Microsoft Outlook xx.x Object Library, where xx.x represents the version of Outlook that you are working with. You can then use the following syntax to start an Outlook session.
Dim objOL as Outlook.Application
Set objOL = New Outlook.Application
To handle Outlook Application-level events in external applications:
First, you must declare a variable using the WithEvents keyword to identify the object whose event you want to handle.
Dim WithEvents objOL as Outlook.Application
Set objOL = New Outlook.Application
You can then select an Outlook application instance object in the Objects list of the module window and then select the event in the procedure list. The Visual Basic Editor will then add the template for the event procedure to the module window. You can then type the code you want to run when the event occurs.
Private Sub objOL_AdvancedSearchComplete(ByVal SearchObject As Outlook.Search)
' this routine identifies the search that has just completed
If SearchObject.Tag = "ProcessAttachments" Then
Debug.Print "Search completed at " & Time
gblnProcessAttachmentsDone = True
End If
End Sub
Read more about the AdvancedSearch method in the Advanced search in Outlook programmatically: C#, VB.NET article.
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
I have this code to create an Outlook appointment from an Excel sheet. It's working fine. But I am using 2 e-mail accounts on Outlook and I don't know how to alternate the meeting host between these accounts. What is the property of AppointmentItem Object, that changes the meeting host?
PS: Isn't "Organizer", I have already tried.
#EDIT:
I was trying to use .SendUsingAccountas suggested by Macro Man, but, still not changing the sender.
My code:
Set oApp = CreateObject("Outlook.Application")
Set ItemAppoint = oApp.CreateItem(1)
ItemAppoint.MeetingStatus = olMeeting
'===============Accounts===============
Dim Var As Object
Set Var = ItemAppoint.session.accounts
'======================================
With ItemAppoint
.SendUsingAccount = Var(2) 'The account that I want to use is the index "2"
.Subject = "Sub"
.Body = "text"
.Display
End With
The .Organizer property is read-only, you're after the .SendUsingAccount property which is read/write
AppointmentItem.SendUsingAccount
More information on the MSDN pages: AppointmentItem.SendUsingAccount Property (Outlook)
The AppointmentItem.SendUsingAccount property allows to specify an Account object that represents the account under which the AppointmentItem is to be sent.
What is the property of AppointmentItem Object, that changes the meeting host?
The easiest way is to create an appointment item in the calendar folder which belongs to a particular account. What code do you use for creating appointment items?
The How To: Create a new Outlook Appointment item article explains all possible ways for creating appointment items in Outlook. Try to get the right folder and use the Add method of the Items class. For example:
items.Add(Outlook.OlItemType.olAppointmentItem)
The GetDefaultFolder method of the Store class returns a Folder object that represents the default folder in the store and that is of the type specified by the FolderType argument. This method 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.
This works Well.
Sub Test()
Dim oNamespace As Outlook.Namespace
Dim oCalendarFolder As Outlook.MAPIFolder
Dim oItems As Outlook.items
Dim strEntryID As String
Set oOutlook = CreateObject("Outlook.Application")
Set oNamespace = oOutlook.GetNamespace("MAPI")
For Each i In oNamespace.Folders
If i.Name = "yourEmailIDhere" Then
For Each j In i.Folders
If j.Name = "Calendar" Then
strEntryID = j.EntryID
End If
Next j
End If
Next i
Set oCalendarFolder = oNamespace.GetFolderFromID(strEntryID)
oItems = oCalendarFolder.items
oMeeting = oItems.Add(Outlook.OlItemType.olAppointmentItem)
oMeeting.Save
oMeeting.Display`
End sub`
I just arrived to this question when searching for a solution to the same problem: in my case the appointment was being created for a different account than desired, and the .SendUsingAccount property was of no help.
I managed to solve this by directly creating the appointment inside the folder I wanted:
Set OutlookApp = CreateObject("Outlook.Application")
Set AppointItem = OutlookApp.GetNamespace("MAPI").Folders("secondary#hostname.com").Folders("Calendar").Items.Add(Outlook.OlItemType.olAppointmentItem)
Now the appointment is created for the secondary#hostname.com account, and not for the default#hostname.com account.