Determining whether an existing Outlook instance is open - vba

After reading how to use automation to send a message, I'm unclear of whether it's possible to avoid opening a new instance of Outlook if I already have one opened. If so, I'm unsure of how to search for examples determining whether an existing Outlook instance is open.
-----Including the suggestion--------
I have the following snippet, but I found that I can't create the instance properly. I'm basically following this example. I'm either getting this screenshot, or the error of "User-defined type not defined." Any suggestions?
Sub Example()
'Dim w As Outlook.Application
Const ERR_APP_NOTRUNNING As Long = 429
On Error Resume Next
' Handle Microsoft outlook
Set w = GetObject(, "Outlook.Application")
If Err = ERR_APP_NOTRUNNING Then
'Set w = New Outlook.Application
Set w = CreateObject("Outlook.Application")
End If
End Sub

I know this question has been answered, but I thought I'd add that applications like Outlook (and I believe PowerPoint as well) are single-instance applications -- there is no need to determine if Outlook is already open because you can only have one copy of Outlook running.
http://msdn.microsoft.com/en-us/library/aa164542(v=office.10).aspx
If you need to instantiate Outlook, simply use CreateObject to create the instance; if Outlook is already running, your object reference will point to the existing instance. If not, you will create the class. Binding (late or early) is irrelevant.
For example, let's say Outlook isn't running. We can use this code to create the instance:
Sub testOutlook()
Dim olApp As Object ' Outlook.Application
Set olApp = CreateObject("Outlook.Application")
MsgBox (olApp2 Is Nothing)
End Sub
This will print "False" because we created the instance.
Let's say Outlook IS running. We can use this code to verify that using GetObject and CreateObject will refer to the existing instance:
Sub testOutlook()
Dim olApp As Object ' Outlook.Application
Dim olApp2 As Object ' Outlook.Application
Set olApp = GetObject(, "Outlook.Application")
MsgBox (olApp Is Nothing)
Set olApp2 = CreateObject("Outlook.Application")
MsgBox (olApp2 Is Nothing)
MsgBox "Same object? " & (olApp Is olApp2)
End Sub
This will print "False" (existing instance), "False" (our alleged "new instance"), but the last message box is "True" because the new instance is actually the same object as the existing instance.
So what do we do if we don't know if Outlook is running or not? As demonstrated above, CreateObject either created a new instance (if one didn't exist, as in the first example) or hooked the existing instance if Outlook was already open (as in the second example).

I see in your question that you commented out
'Dim w As Outlook.Application
presumably because this gives you the "User-defined type not defined" error.
This is likely because you have not set a reference to the Outlook library in your Excel-VBA project. This is done as follows: Tools > References > check "Microsoft Outlook xx.x Object Library". Then you can write this
Dim w As Outlook.Application
Set w = New Outlook.Application
' or,
'Set w = CreateObject("Outlook.Application")
which, by the way, results in compile-time (or "early") binding. And gives you the Outlook object intellisense.
Alternatively, you can omit setting the reference and declare w as a generic object and let it bind at run-time
Dim w As Object
Set w = CreateObject("Outlook.Application")
but runtime (or "late") binding is less efficient.
Do whatever feels best -- I'm going to go ahead and venture that chances are, you won't notice the difference in efficency. I'm a recent convert to the early-binding thing, really just because of the intellisense.
EDIT So you've created a new Outlook application, but you can't see it. If you look in the Windows task manager, you'll see that the process is there, running -- but it's just not showing on the screen. Unfortunately, some brilliant engineer at Microsoft decided that Outlook shouldn't have a Visible property like Word or Excel do, so we have to use an awkward workaround. Open one of the special folders e.g. the Inbox like this:
Dim w As Outlook.Application
Dim wInbox As Outlook.MAPIFolder
Set w = New Outlook.Application
Set wInbox = w.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
wInbox.Display 'This makes Outlook visible

Set w = GetObject(, "Outlook.Application")
this should get running instance, if none is running catch error and do CreateObject

If you like, use this.
This is not a perfect solution, but you can open Outlook App when it's not be opened.
Function OpenOutlookApp(isSend As Boolean) As Boolean
' If it has opened, return true.
' my office version is 2016.
Dim oApp As Object
On Error GoTo ErrorHandle
On Error Resume Next
Set oApp = GetObject(, "Outlook.Application")
On Error GoTo 0
If oApp Is Nothing Then
Set oApp = CreateObject("Outlook.Application")
oApp.GetNamespace("MAPI").GetDefaultFolder(6).Display
End If
If isSend Then Call SendAndReceiveOutlookMail(False)
OpenOutlookApp = True
GoTo NonErrorHandle
ErrorHandle:
NonErrorHandle:
On Error GoTo 0
End Function
Sub SendAndReceiveOutlookMail(isQuit As Boolean)
Dim oApp As New Outlook.Application
On Error Resume Next
Call oApp.Session.LogOn("Outlook", "")
Call oApp.Session.SendAndReceive(True)
If isQuit Then oApp.Quit
Set oApp = Nothing
On Error GoTo 0
End Sub

Related

How to access certain mailitem properties/methods in a Visual Studio Outlook Add-In?

I'm developing an Outlook (2010) add-in using Visual Studio 2013 (Targeting .NET 4).
Certain Outlook properties/methods seem to be unavailable.
The following code works from Outlook VBA.
Public Sub OutlookTest()
'Dim oApp As New Outlook.Application (NOT NEEDED FOR OUTLOOK VBA)
Dim oExp As Outlook.Explorer
Dim oSel As Outlook.Selection ' You need a selection object for getting the selection.
Dim oItem As Object ' You don't know the type yet.
Set oExp = Application.ActiveExplorer 'Get the ActiveExplorer.
Set oSel = oExp.Selection ' Get the selection.
For i = 1 To oSel.Count ' Loop through all the currently .selected items
Set oItem = oSel.Item(i) ' Get a selected item.
Call DisplayInfo(oItem) ' Display information about it.
Next i
End Sub
Private Sub DisplayInfo(oItem As Object)
Dim strMessageClass As String
Dim oMailItem As Outlook.MailItem
' You need the message class to determine the type.
strMessageClass = oItem.MessageClass
If (strMessageClass = "IPM.Note") Then ' Mail Entry.
Set oMailItem = oItem
MsgBox oMailItem.Subject
MsgBox oMailItem.EntryID
MsgBox oMailItem.HTMLBody
oMailItem.SaveAs "C:\Users\u001tb7\Desktop\New folder\testOL.msg", olMSG
Else
MsgBox "Pick something else"
End If
End Sub
When I try near identical code from Visual Studio in an add-in:
Private Sub butSettings_Click(sender As Object, e As RibbonControlEventArgs) Handles butSettings.Click
Dim oApp As New Outlook.Application
Dim oExp As Outlook.Explorer
Dim oSel As Outlook.Selection ' You need a selection object for getting the selection.
Dim oItem As Object ' You don't know the type yet.
oExp = oApp.ActiveExplorer ' Get the ActiveExplorer.
oSel = oExp.Selection ' Get the selection.
For i = 1 To oSel.Count ' Loop through all the currently .selected items
oItem = oSel.Item(i) ' Get a selected item.
DisplayInfo(oItem) ' Display information about it.
Next i
End Sub
Sub DisplayInfo(oItem As Object)
Dim strMessageClass As String
Dim oMailItem As Outlook.MailItem
' You need the message class to determine the type.
strMessageClass = oItem.MessageClass
If (strMessageClass = "IPM.Note") Then ' Mail Entry.
oMailItem = oItem
MsgBox(oMailItem.Subject)
MsgBox(oMailItem.EntryID)
MsgBox(oMailItem.HTMLBody) '<---FAILS
oMailItem.SaveAs("C:\Users\u001tb7\Desktop\New folder\testVS.msg", Outlook.OlSaveAsType.olMSG) '<---ALSO FAILS
Else
MsgBox("Pick something else")
End If
End Sub
I get an error on MailItem.HTMLBody and MailItem.SaveAs but NOT MailItem.Subject or .EntryID.
This makes me suspect it's something to do with the security, as I think properties like .HTMLBody are 'protected', but .EntryID and .Subject are not.
The error is the generic COM exception not giving me any detail:
An exception of type 'System.Runtime.InteropServices.COMException' occurred in MsgSave.dll but was not handled in user code
Additional information: Operation aborted (Exception from HRESULT: 0x80004004 (E_ABORT))
Is there any way to get Outlook to 'trust' my VS code (for eventual distribution)? Or is there something else amiss?
EDIT: Thanks to both below!
Instead of:
Dim oApp As New Outlook.Application
Use:
Dim oApp as Outlook.Application = Globals.ThisAddIn.Application
Do not use New Outlook.Application in an Outlook addin - you get Outlook.Application object for free when your addin starts up.
certain outlook properties/methods seem to be unavailable
What properties are you talking about? Could you be more specific?
As Dmitry suggested, you need to use the Application property provided by VSTO. In that case you will avoid security prompts or exceptions. Typically you get such issue when you try to automate Outlook from a standalone application. The Application object provided by VSTO is trusted and doesn't generate exceptions for secured properties or methods (for example, MailItem.Send). You can read more about that in the Outlook "Object Model Guard" Security Issues for Developers article.

Mail Item Send Method failing for Windows Authenticated Outlook Client when closed

I am currently having an issue around automatically sending emails via excel vba?
I have a simple example:
Sub sendOutlookEmail()
Dim oApp As Outlook.Application
Dim oMail As MailItem
Set oApp = CreateObject("Outlook.application")
Set oMail = oApp.CreateItem(olMailItem)
oMail.Body = "Body of the email"
oMail.Subject = "Test Subject"
oMail.To = "someone#somewhere.com"
oMail.Send
Set oMail = Nothing
Set oApp = Nothing
End Sub
This works fine when an outlook client is open. When outlook is closed I receive the following error:
Error 287: Application-defined or object-defined error
Now if I add a single line of inconsequential code above the oMail.Send Command:
Debug.Print oApp.Session.Accounts.Item(1).DisplayName
Now the code works fine regardless of Outlook open or closed.
I am aware that Windows Authentication is required to auto send an email via the OLE when Outlook is closed and is using LDAP, but as the extra code is unchanging of the sub routine. I believe this is some sort of bug around the initialisation of the Outlook.application.Session.Account default object.
Could you please help is this an LDAP issue or a Outlook.Application class issue, This is a replicated error from a VB app that I do not have access to the source code for, but is behaving in the same way and reporting the same error.
Thank You
The reason for this behaviour is stated here:
http://msdn.microsoft.com/en-us/library/office/ff861594%28v=office.15%29.aspx
Code example-
' Start Outlook.
Dim olApp As Outlook.Application
Set olApp = CreateObject("Outlook.Application")
' Get a session object.
Dim olNs As Outlook.NameSpace
Set olNs = olApp.GetNamespace("MAPI")
' Create an instance of the Inbox folder.
' If Outlook is not already running, this has the side
' effect of initializing MAPI.
Dim mailFolder As Outlook.Folder
Set mailFolder = olNs.GetDefaultFolder(olFolderInbox)
' Continue to use the object model to automate Outlook.
"Starting in Outlook 2010, if you have multiple profiles, you have configured Outlook to always use a default profile, and you use the Logon method to log on to the default profile without prompting the user, the user will receive a prompt to choose a profile anyway. To avoid this behavior, do not use the Logon method; use the workaround suggested in the preceding InitializeMAPI example instead."*
The 'workaround' as Microsoft calls it is to simply make a statement to the Namespace object. This initializes the MAPI. It appears that when communicating remotely through the OLE interface, the Outlook application object might not be visible and the prompt to choose a profile might not be displayed, giving the appearance that nothing is happening.
Thanks Mark for helping me to identify the problem.
Tom Feuerstake
Try to add a call to Namespace.Logon:
Set oApp = CreateObject("Outlook.application")
set oNS = oApp.GetNamespace("MAPI")
oNS.Logon

Call Outlook procedure using VBScript

I have a procedure in Outlook that sends all the saved messages in Drafts folder.
Below is the code:
Public Sub SendMail()
Dim olApp As Outlook.Application
Dim olNS As Outlook.NameSpace
Dim olFolder As Outlook.MAPIFolder
Dim olDraft As Outlook.MAPIFolder
Dim strfoldername As String
Dim i As Integer
Set olApp = Outlook.Application
Set olNS = olApp.GetNamespace("MAPI")
Set olFolder = olNS.GetDefaultFolder(olFolderInbox)
strfoldername = olFolder.Parent
Set olDraft = olNS.Folders(strfoldername).Folders("Drafts")
If olDraft.Items.Count <> 0 Then
For i = olDraft.Items.Count To 1 Step -1
olDraft.Items.Item(i).Send
Next
End If
End Sub
Above code works fine.
Question:
I want to use Task Scheduler to fire this procedure as a specified time.
1. Where will I put the procedure in Outlook, Module or ThisOutlookSession?
2. I am not good in vbscript so I also don't know how to code it to call the Outlook Procedure. I've done calling Excel Procedure but Outlook doesn't support .Run property.
So this doesn't work:
Dim olApp
Set olApp = CreateObject("Outlook.Application")
olApp.Run "ProcedureName"
Set olApp = Nothing
I've also read about the Session.Logon like this:
Dim olApp
Set olApp = CreateObject("Outlook.Application")
olApp.Session.Logon
olApp.ProcedureName
Set olApp = Nothing
But it throws up error saying object ProcedureName is not supported.
Hope somebody can shed some light.
SOLUTION:
Ok, I've figured out 2 work around to Avoid or get pass this pop-up.
1st one: is as KazJaw Pointed out.
Assuming you have another program (eg. Excel, VBScript) which includes sending of mail via Outlook in the procedure.
Instead of using .Send, just .Save the mail.
It will be saved in the Outlook's Draft folder.
Then using below code, send the draft which fires using Outlook Task Reminder.
Option Explicit
Private WithEvents my_reminder As Outlook.Reminders
Private Sub Application_Reminder(ByVal Item As Object)
Dim myitem As TaskItem
If Item.Class = olTask Then 'This works the same as the next line but i prefer it since it automatically provides you the different item classes.
'If TypeName(Item) = "TaskItem" Then
Set my_reminder = Outlook.Reminders
Set myitem = Item
If myitem.Subject = "Send Draft" Then
Call SendMail
End If
End If
End Sub
Private Sub my_reminder_BeforeReminderShow(Cancel As Boolean)
Cancel = True
Set my_reminder = Nothing
End Sub
Above code fires when Task Reminder shows with a subject "Send Draft".
But, we don't want it showing since the whole point is just to call the SendMail procedure.
So we added a procedure that Cancels the display of reminder which is of olTask class or TaskItem Type.
This requires that Outlook is running of course.
You can keep it running 24 hours as i did or, create a VBscript that opens it to be scheduled via Task Scheduler.
2nd one: is to use API to programatically click on Allow button when the security pop-up appears.
Credits to SiddarthRout for the help.
Here is the LINK which will help you programmatically click on the Allow button.
Of course you have to tweak it a bit.
Tried & Tested!
Assuming that you have Outlook Application always running (according to comment below your question) you can do what you need in the following steps:
add a new task in Outlook, set subject to: "run macro YourMacroName" and set time (plus cycles) when your macro should start.
go to VBA Editor, open ThisOutlookSession module and add the following code inside (plus see the comments inside the code):
Private Sub Application_Reminder(ByVal Item As Object)
If TypeName(Item) = "TaskItem" Then
Dim myItem As TaskItem
Set myItem = Item
If myItem.Subject = "run macro YourMacroName" Then
Call YourMacroName '...your macro name here
End If
End If
End Sub
Where will I put the procedure in Outlook, Module or ThisOutlookSession?
Neither. Paste the below code in a Text File and save it as a .VBS file. Then call this VBS file from the Task Scheduler as shown HERE
Dim olApp, olNS, olFolder, olDraft, strfoldername, i
Set olApp = GetObject(, "Outlook.Application")
Set olNS = olApp.GetNamespace("MAPI")
Set olFolder = olNS.GetDefaultFolder(6)
strfoldername = olFolder.Parent
Set olDraft = olNS.Folders(strfoldername).Folders("Drafts")
If olDraft.Items.Count <> 0 Then
For i = olDraft.Items.Count To 1 Step -1
olDraft.Items.Item(i).Send
Next
End If
If you are using Outlook 2007 or newer I have found you can easily eliminate the security pop up you mentioned above when running your script by doing the following:
In Outlook 2007 Trust Center, go to Macro Security - Select "No security Check for macros"
In Outlook 2007 Trust Center, go to Programatic Access - Select "Never warn me abous suspicious activity.
Of course that technically leaves you open to the remote possibility for someone to email you some malicious email script or something of that nature I assume. I trust my company has that managed though and this works for me. I can use VBS scripts in Outlook, Access, Excel to send emails with no security pop up.
Another Option:
If you don't want to do that, another option that has worked well for me prior to this is here:
http://www.dimastr.com/redemption/objects.htm
Basically a dll redirect that does not include the popup. It leaves your other default security in place and you write \ call your VBA for it and send mail without the secutity pop-ups.

Sending email through MS Access VBA / Outlook, choosing sending profile

I am looking at this snippet of code from another question here (MS Access VBA): https://stackoverflow.com/a/17975507/1085885
Right now this code only works when I run it while Outlook is open. Is there any way for this code to "open Outlook" and then run all the sending code?
Secondly, how can I choose which Outlook profile to send from? I have access to a couple different profiles and it's sending from my main top inbox but I want it to come from my second inbox.
You need to log to the specified profile (as shown in "Control Panel | Mail | Show Profiles", if that is what you mean by "profile"). After creating an instance of the Outlook application
Set oApp = CreateObject("Outlook.application")
add something like the following:
set oNS = oApp.GetNamespace.Logon
oNS.Logon("MyProfileName")
Note if Outlook is already running, Logon will do nothing. You will need to use Extended MAPI (C++ or Delphi or a MAPI wrapper like Redemption (I am its author, use RDOSession.Logon) to log to a specified profile.
If by "profile" you actually mean a different account in the same profile in Outlook, you can set the MailItem.SendUsingAccount property to specify a particular account.
If you are sending from an Exchange mailbox, and you need to set a different sender, set the MailItem.SentOnBehalfOfName property to the name of the user on whose behalf you are sending (assuming you have the right to send on behalf of that user).
Try it this way.
Private Sub Command1_Click()
Dim bStarted As Boolean
Dim oOutlookApp As Outlook.Application
Dim oItem As Outlook.MailItem
On Error Resume Next
'Get Outlook if it's running
Set oOutlookApp = GetObject(, "Outlook.Application")
If Err <> 0 Then
'Outlook wasn't running, start it from code
Set oOutlookApp = CreateObject("Outlook.Application")
bStarted = True
End If
'Create a new mailitem
Set oItem = oOutlookApp.CreateItem(olMailItem)
With oItem
'Set the recipient for the new email
.To = "receiver#gmail.com"
.Send
End With
If bStarted Then
' 'If we started Outlook from code, then close it
oOutlookApp.Quit
End If
'Clean up
Set oItem = Nothing
Set oOutlookApp = Nothing
End Sub

Send/Receive in Outlook via code

If I create an Outlook 2010 object In Excel VBA using
Sub CreateOL()
On Error Resume Next
Set myOlApp = GetObject(, "Outlook.Application")
If Err.Number = 429 Then
Set myOlApp = CreateObject("Outlook.Application")
End If
On Error GoTo 0
End Sub
Is it possible to force myOLAPP to send/receive. Please can somebody advise how it's done?
I've tried the following but it's not working for me.
Set nsp = myOlApp.GetNamespace("MAPI")
Set sycs = nsp.SyncObjects
For i = 1 To sycs.Count
Set syc = sycs.Item(i)
syc.Start
Next
Also, how do I make myOlApp visible? myOlApp.Visible = True and myOlApp.Application.Visible = True doesn't work
Thank you
Which Outlook version are you using? I tested this with Outlook 2010 and it works.
NOTE: I have not done any error handling. I am sure you can take care of that.
Public Sub Sample()
Dim oLook As Object
Dim nsp As Object, objSyncs As Object, objSync As Object
Dim i As Long
Set oLook = GetObject(, "Outlook.Application")
Set nsp = oLook.GetNamespace("MAPI")
Set objSyncs = nsp.SyncObjects
For i = 1 To objSyncs.Count
Set objSync = objSyncs.Item(i)
objSync.Start
Next
End Sub
MS Outlook has 2 types of windows
Explorer for Folders and
Inspector for individual items.
If you want to show a particular folder, you can start the Explorer for it then either use .Activate or .Display. An alternative would be to use MAPIFolder.Display method as well.