Open Outlook using VBA in MS Access - vba

I am trying to open Outlook when a button is clicked in MS Access, I have the following code which I have gathered online and after tinkering with it it is still not working. Here is my code:
Private Sub Command56_Click()
Dim obj
On Error Resume Next
Set obj = GetObject(, "Outlook.Application")
On Error GoTo 0
If obj Is Nothing Then Set obj = CreateObject("Outlook.Application")
End Sub
Does anyone have any suggestions?

No need to call GetObject. If Outlook is loaded, it will create a reference to it and if not, it will be loaded. It will not create a new instance though.
Private Sub Command56_Click()
Dim obj As Object
Set obj = CreateObject("Outlook.Application")
obj.Visible = True
'do work
obj.Quit '<-- This will close Outlook
Set obj = Nothing
End Sub

I have been using this procedure:
Private Sub OpenOutlook(emailAddress As String)
Dim objOutlook As Outlook.Application
Dim objOutlookMsg As Outlook.MailItem
Dim objOutlookRecip As Outlook.Recipient
Dim objOutlookAttach As Outlook.Attachment
' Create the Outlook session.
Set objOutlook = CreateObject("Outlook.Application")
' Create the message.
Set objOutlookMsg = objOutlook.CreateItem(olMailItem)
With objOutlookMsg
' Set the Subject, Body, and Importance of the message.
.Subject = "ISF"
.Body = "This is the body of the message." & vbCrLf & vbCrLf
.Recipients.Add (emailAddress)
' Add attachments to the message.
Set objOutlookAttach = .Attachments.Add("\\sql\images\" & Forms![WebQADocumentReview].FileName)
.Display
End With
Set objOutlook = Nothing
End Sub

But you can’t use similar code with Outlook due to how Outlook opens which is quite different from other Office products. Sometimes it is important to know that Outlook is open, for example to be sure that your mails created with VBA code are sent and not waiting in your outbox.
The code below is created by MVP Ben Clothier and can either retrieve an open instance of Outlook or open Outlook if it is closed. This uses a ‘self-healing object’ approach for returning an instance of Outlook.
https://www.rondebruin.nl/win/s1/outlook/openclose.htm

Add this to the Declarations
Dim g_olApp As Object
Create the below Subroutine
Private Sub fireOutlook()
Dim olShellVal As Long
On Error GoTo FIREOUTLOOK_ERR
Set g_olApp = GetObject(, "Outlook.Application") ' If outlook is open will create obj
' If closed this will goto the error handler and then resume
If g_olApp Is Nothing Then ' This checks if object was created
olShellVal = Shell("OUTLOOK", vbNormalNoFocus) ' Opens Outlook
Set g_olApp = CreateObject("Outlook.Application") ' Creates the Object
End If
FIREOUTLOOK_EXIT:
Exit Sub
FIREOUTLOOK_ERR:
If g_olApp Is Nothing Then
Err.Clear
Resume Next
Else
MsgBox Err.Description, , "Error Number: " & Err.Number
End If
GoTo FIREOUTLOOK_EXIT
End Sub
Once this is complete the global object can be used in any code involving outlook just make sure to call the fireOutlook subroutine first.

Related

Outlook has exhausted all shared resources. Why?

With this bit of VBA code in MS Access I'm getting an error if its executed too often. The only way I've found to clear it is reboot my computer. Any idea why and what can I do?
Public Function HasOutlookAcct(strEmail As String) As Boolean
Dim OutMail As Object
Dim OutApp As OutLook.Application
Dim objNs As OutLook.NameSpace
Dim objAcc As Object
'https://stackoverflow.com/questions/67284852/outlook-vba-select-sender-account-when-new-email-is-created
Set OutApp = CreateObject("Outlook.Application")
Set objNs = OutApp.GetNamespace("MAPI")
For Each objAcc In objNs.Accounts
If objAcc.SmtpAddress = strEmail Then
HasOutlookAcct = True
Exit For
End If
Next
OutApp.Quit
Set objAcc = Nothing
Set objNs = Nothing
End Function
The code looks good. The NameSpace.Accounts property returns an Accounts collection object that represents all the Account objects in the current profile. I don't see any extensive or heavy usage of the Outlook object model, but creating a new Outlook Application instance in the method for checking whether a particular account is configured in Outlook or not is not the best way of using Outlook. Instead, I'd recommend running Outlook once at some point and getting all the configured emails for saving for future usage where necessary.
Also it makes sense to disable all COM add-ins to see whether it helps or not. The problem may be related to any specific COM add-in.
Appears the error is addressed by considering the user.
The assumption, based on my results, is Outlook is not cleaned up completely when the user's instance is closed with outApp.Quit.
When Outlook is open, outApp.Quit is not applied and Outlook remains open at the end.
When Outlook is not open, it is opened in the background and later closed with outApp.Quit.
There is zero or one instance of Outlook at any time.
Option Explicit
Public Function HasOutlookAcct(strEmail As String) As Boolean
'Reference Outlook nn.n Object Library
' Consistent early binding
Dim outApp As Outlook.Application
Dim objNs As Outlook.Namespace
Dim objAcc As Outlook.Account
Dim bCreated As Boolean
On Error Resume Next
Set outApp = GetObject(, "Outlook.Application")
On Error GoTo 0
If outApp Is Nothing Then
bCreated = True
Set outApp = CreateObject("Outlook.Application")
End If
Set objNs = outApp.GetNamespace("MAPI")
For Each objAcc In objNs.Accounts
'Debug.Print objAcc.SmtpAddress
If objAcc.SmtpAddress = strEmail Then
HasOutlookAcct = True
Exit For
End If
'Set objAcc = Nothing ' Additional cleanup if needed
Next
If bCreated = True Then ' Outlook object had to be created
outApp.Quit
End If
'Set outApp = Nothing ' Additional cleanup if needed
Set objNs = Nothing
End Function
Private Sub HasOutlookAcct_Test()
Dim x As Boolean
Dim sEmail As String
sEmail = "someone#somewhere.com"
Dim i As Long
For i = 1 To 50
Debug.Print i & ": " & sEmail
x = HasOutlookAcct(sEmail)
Debug.Print " HasOutlookAcct: " & x
DoEvents
Next
Debug.Print "done"
End Sub

How to pass Outlook AppointmenItem data to Access form textbox

I have an Access form which will create an Outlook AppointmentItem for the current record.
The .Start and .Categories of the AppointmentItem are derived from user-input on the form.
I have a command button which will find and open the AppointmentItem so that the user may edit it.
After the user makes the edits I want to pass the edited information to the form controls so that the user can see the updated start time and category without having to open the AppointmentItem.
I am storing Public variables for the two bits of data. I cannot figure out the process by which the variables are updated with the data from the AppointmentItem.
Code for finding the existing AppointmentItem:
Option Compare Database
Public gdtStart As Date
Public gstrCat As String
Option Explicit
Function FindExistingAppt(strPath As String)
Dim OApp As Object
Dim OAppt As Object
Dim ONS As Object
Dim ORecipient As Outlook.Recipient
Dim OFolder As Object
Dim sFilter As String
Const olAppointmentItem = 1
Dim bAppOpened As Boolean
'Initiate our instance of the oApp object so we can interact with Outlook
On Error Resume Next
Set OApp = GetObject(, "Outlook.Application") 'Bind to existing instance of Outlook
If err.Number <> 0 Then 'Could not get instance of Outlook, so create a new one
err.Clear
Set OApp = CreateObject("Outlook.Application")
bAppOpened = False 'Outlook was not already running, we had to start it
Else
bAppOpened = True 'Outlook was already running
End If
On Error GoTo Error_Handler
Set OApp = GetObject(, "Outlook.Application")
Set ONS = OApp.GetNamespace("MAPI")
Set ORecipient = ONS.CreateRecipient("xxxxxxxxxxxxx")
'my example uses a shared folder but you can change it to your defaul
Set OFolder = ONS.GetSharedDefaultFolder(ORecipient, olFolderCalendar)
'use your ID here
sFilter = "[Mileage] = " & strPath & ""
If Not OFolder Is Nothing Then
Set OAppt = OFolder.Items.Find(sFilter)
If OAppt Is Nothing Then
MsgBox "Could not find appointment"
Else
With OAppt
.Display
End With
End If
End If
gdtStart = OAppt.Start
gstrCat = OAppt.Categories
Error_Handler_Exit:
On Error Resume Next
If Not OAppt Is Nothing Then Set OAppt = Nothing
If Not OApp Is Nothing Then Set OApp = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Number: " & err.Number & vbCrLf & _
"Error Source: FindExistingAppt" & vbCrLf & _
"Error Description: " & err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl), _
vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
Here is the code from the command button which opens the AppointmentItem.
Private Sub cmdFindAppt_Click()
'Goes to the OutlookApp module and uses the FindExistingAppt function to look for an appointment that has
'already been created to the Warrants Outlook calendar, and if it found, opens the appointment. After edits are
'made the Appointment Date and Category are updated on the form.
Call FindExistingAppt(Me.ID)
Me.ApptDate = gdtStart
Me.Category = gstrCat
End Sub
How do I update the form controls with the Public variables?
After the code runs the form controls do not reflect the values of the stored Public variables.
If I open the AppointmentItem one more time (using the FindExistingAppt code - not by opening the AppointmentItem in Outlook proper), and close either by saving or not, then the form controls update.
This probably doesn't work because the VBA code continues to run after OAppt.Display.
So any changes you make and save to OAppt won't be read to your variables, because the function is already finished.
Try using the Modal parameter, this may halt the code until OAppt is closed.
With OAppt
.Display True
https://learn.microsoft.com/en-us/office/vba/api/outlook.appointmentitem.display
Also add Debug.Print commands and/or breakpoints before and after .Display to see what's happening.
The way you update those form controls should be working fine.
Try to check if the FindExistingAppt function really does assign values into public variables by debugging the function.
Use locals window to watch the variables values (gdtStart and gstrCat) while debugging.
Just sharing common practice:
If your procedure/routine does not return the value then you can declare it with Sub keyword instead Function keyword.

How to send mail based on a draft then keep the draft?

We are updating mails from the drafts folder and sending them a few times a day.
I want to open a selected mail resend it save it so it goes back to drafts and then close it.
I tried below
Sub DRAFT()
Dim myItem As Outlook.MailItem
Dim objInsp As Outlook.Inspector
Dim objActionsMenu As Office.CommandBarControl
Dim olResendMsg As Outlook.MailItem
' get current item & open if needed
On Error Resume Next
Select Case TypeName(Application.ActiveWindow)
Case "Explorer"
Set myItem = Application.ActiveExplorer.Selection.Item(1)
myItem.Display
Case "Inspector"
Set myItem = Application.ActiveInspector.CurrentItem
Case Else
End Select
On Error GoTo 0
If myItem Is Nothing Then
MsgBox "Could not use current item. Please select or open a single email.", _
vbInformation
GoTo exitproc
End If
' run the resend command
Set objInsp = myItem.GetInspector
objInsp.CommandBars.ExecuteMso ("ResendThisMessage")
' save orig email
myItem.Save
' close orig email
myItem.Close
exitproc:
Set myItem = Nothing
Set objInsp = Nothing
Set objActionsMenu = Nothing
Set olResendMsg = Nothing
End Sub
You need to pass a OlInspectorClose enumeration value to the MailItem.Close method. It indicates the close behavior, i.e. the save mode. If the item displayed within the inspector has not been changed, this argument has no effect.
Name Value Description
olDiscard 1 Changes to the document are discarded.
olPromptForSave 2 User is prompted to save documents.
olSave 0 Documents are saved.
So, your code should like that:
' close orig email
myItem.Close olSave
Instead of executing the ribbon control programmatically using the CommandBars.ExecuteMso method you may try to create a cope of the source item and then send it.
The ExecuteMso method is useful in cases where there is no object model for a particular command. Works on controls that are built-in buttons, toggleButtons and splitButtons. On failure it returns E_InvalidArg for an invalid idMso, and E_Fail for controls that are not enabled or not visible.
Instead, you may use the MailItem.Copy method which creates another instance of an object.
Sub CopyItem()
Dim myNameSpace As Outlook.NameSpace
Dim myFolder As Outlook.Folder
Dim myNewFolder As Outlook.Folder
Dim myItem As Outlook.MailItem
Dim myCopiedItem As Outlook.MailItem
Set myNameSpace = Application.GetNamespace("MAPI")
Set myFolder = myNameSpace.GetDefaultFolder(olFolderInbox)
Set myNewFolder = myFolder.Folders.Add("Saved Mail", olFolderDrafts)
Set myItem = Application.CreateItem(olMailItem)
myItem.Subject = "Speeches"
Set myCopiedItem = myItem.Copy
myCopiedItem.To = "email#address.com"
myCopiedItem.Send()
End Sub
Although there is a mistake in myItem.Close, you cannot resend mail that has not been sent.
Option Explicit
Sub SendMailBasedOnPermanentDraft()
Dim myItem As MailItem
Dim objInsp As Inspector
Dim myCopyOfUnsentItemInDrafts As MailItem
' get current item & open if needed
On Error Resume Next
Select Case TypeName(ActiveWindow)
Case "Explorer"
Set myItem = ActiveExplorer.Selection.Item(1)
Case "Inspector"
Set myItem = ActiveInspector.CurrentItem
Case Else
End Select
On Error GoTo 0
If myItem Is Nothing Then
MsgBox "Could not use current item. Please select or open a single email.", vbInformation
GoTo exitProc
End If
If myItem.Sent = False Then
Set myCopyOfUnsentItemInDrafts = myItem.copy
With myCopyOfUnsentItemInDrafts
.Subject = "Copied " & Now & ": " & myItem.Subject
.Save
.Display ' change to .Send
End With
Else
MsgBox "Select or open a single unsent email.", vbInformation
End If
exitProc:
Set myItem = Nothing
Set objInsp = Nothing
Set myCopyOfUnsentItemInDrafts = Nothing
End Sub

VBA Outlook Run-time Error - Could not send the message

The VBA code shown below is for replying to an email with a specific template. The user selects the email that they want to reply to through a file dialog box, the code should then open a new email replying to the selected email. I am receiving the following error:
Run-time error '-2147352567 (80020009)'
Could not send the message.
When I click the "Help" button it takes me here: https://msdn.microsoft.com/en-us/VBA/Outlook-VBA/articles/olkcategory-backcolor-property-outlook
I can't imagine that link has anything to do with the error though. I have a feeling I'm doing something wrong with adding the reply email. Any help would be appreciated.
Here is my code:
Public Sub btnOK_Click()
Dim myOlApp As Outlook.Application
Dim myItem As Outlook.MailItem
Dim body As String
Set myOlApp = CreateObject("Outlook.Application")
Select Case templateUserForm.templateListBox.Text
Case "CWO"
ReplyTo
Set myItem = myOlApp.CreateItemFromTemplate("C:\example\temp\tempEmail.msg")
myItem.Reply = myOlApp.CreateItemFromTemplate("L:\example\CWO.oft") <--- Error occurs on this line
body = myItem.body
Unload templateUserForm
Unload inputUserForm
myItem.Display
End Select
End Sub
Public Sub ReplyTo()
Dim fso As Object
Dim xlApp As Object
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = False
Dim fd As Office.FileDialog
Set fd = xlApp.Application.FileDialog(msoFileDialogFilePicker)
fd.AllowMultiSelect = False
Dim selectedItem As Variant
If fd.Show = -1 Then
For Each selectedItem In fd.SelectedItems
Set fso = VBA.CreateObject("Scripting.FileSystemObject")
Call fso.CopyFile(fd.SelectedItems.Item(1), "C:\example\temp\tempEmail.msg")
Next
End If
End Sub
Typically you would create a reply object like this:
Set myReply = myItem.Reply
To start with an oft:
Dim myReply as Outlook.MailItem
Set myReply = myOlApp.CreateItemFromTemplate("L:\example\CWO.oft")
myReply.Display
Now that there is no error you have myReply to manipulate.
It appears you want this.
Set myItem = myOlApp.CreateItemFromTemplate("C:\example\temp\tempEmail.msg")
myReply = myOlApp.CreateItemFromTemplate("L:\example\CWO.oft")
myReply.body = myItem.body
Unload templateUserForm
Unload inputUserForm
myReply.Display
To make the result look more like a reply:
myReply.body = myReply.body & vbcr & myItem.reply.body
Depending on your templates you may want HTMLbody rather than body.

Delete automatic Signature from forwarded emails VBA macro

Newbie Outlook VBA. intermediate Excel VBA. Windows 7 Professional, Outlook 2010
I have a script running from a rule that autoforwards all incoming emails. I need it as a rule because otherwise it will not forward the mails in the queue when Outlook loads.
I would like to have the default signature deleted when the mails are forwarded. As the reply is "blank" it is unnecessary to have the sig appended. I have found some code that supposedly worked in Outlook 2007 from the MSDN site. It compiles no errors, executes no errors. I have referenced MS Word in VBA. But the forwarded emails all have the signature still attached.
I cannot just delete the signature because I need it to be there on replies. The switch for the signature is for both replies and forwarded mail.
Here is the code:
Option Explicit
Sub Incoming3(MyMail As MailItem)
Dim strID As String
Dim strSender As String
Dim StrSubject As String
Dim objItem As Outlook.MailItem
Dim myItem As Outlook.MailItem
strID = MyMail.entryID
Set objItem = Application.Session.GetItemFromID(strID)
strSender = objItem.SenderName
StrSubject = objItem.Subject
StrSubject = strSender + ": " + StrSubject
objItem.Subject = StrSubject
objItem.AutoForwarded = False
Set myItem = objItem.Forward
myItem.Recipients.Add "bcc.hwb#gmail.com"
myItem.DeleteAfterSubmit = True
Call DeleteSig(objItem)
myItem.Send
Set myItem = Nothing
Set objItem = Nothing
End Sub
Sub DeleteSig(msg As Outlook.MailItem)
Dim objDoc As Word.Document
Dim objBkm As Word.Bookmark
On Error Resume Next
Set objDoc = msg.GetInspector.WordEditor
Set objBkm = objDoc.Bookmarks("_MailAutoSig")
If Not objBkm Is Nothing Then
objBkm.Select
objDoc.Windows(1).Selection.Delete
End If
Set objDoc = Nothing
Set objBkm = Nothing
End Sub
Any help with Outlook or VBA code would be much appreciated.
Processing the wrong mail in DeleteSig.
myItem.DeleteAfterSubmit = True
Call DeleteSig(myItem)
myItem.Send
Edit 2015 02 26
Debugging VBA Code
Private Sub Incoming3_test()
' Open a mailitem then click F8 repeatedly from this code
Dim currItem As MailItem
Set currItem = ActiveInspector.currentItem
Incoming3 currItem
End Sub
Sub Incoming3(MyMail As MailItem)
Dim myItem As Outlook.MailItem
Set myItem = MyMail.Forward
myItem.Subject = MyMail.senderName & ": " & MyMail.Subject
myItem.Recipients.Add "bcc.hwb#gmail.com"
myItem.DeleteAfterSubmit = True
myItem.Display ' If you are using F8 you can
' view the action taken in DeleteSig.
' Delete the line later.
Call DeleteSig(myItem)
'myItem.Send
Set myItem = Nothing
End Sub
Sub DeleteSig(msg As Outlook.MailItem)
Dim objDoc As Word.Document
Dim objBkm As Word.Bookmark
On Error Resume Next '<--- Very bad without On Error GoTo 0
Set objDoc = msg.GetInspector.WordEditor
Set objBkm = objDoc.Bookmarks("_MailAutoSig")
On Error GoTo 0
If Not objBkm Is Nothing Then
objBkm.Select ' <--- This is where the action starts.
objDoc.Windows(1).Selection.Delete
End If
Set objDoc = Nothing
Set objBkm = Nothing
End Sub
Edit 2015 02 26 - End
When you assign a VBA macro sub to run by the rule you get an instance of the MailItem object. For example:
Sub Incoming3(MyMail As MailItem)
The MyMail object represents an incoming email message which you should use in the code. But I see that you get a new instance:
strID = MyMail.entryID
Set objItem = Application.Session.GetItemFromID(strID)
There is no need to do so. Use the MyMail object in the code.
Also I see the following code:
Set objBkm = objDoc.Bookmarks("_MailAutoSig")
Try to run the code under the debugger and see whether the bookmark can be found. If there is no such bookmark you need to search the body for the first entry From: in the text and delete all the content before that keyword.
Finally, you may find the Getting Started with VBA in Outlook 2010 article in MSDN helpful.