I have a .dotm template file on a network share. There are macros with references to the Word, Office, and Outlook object libraries. We use two different platforms, Windows XP and Windows 7, along with Microsoft Office 2007 and Office 2010. When users open the template file the references for Word and Office adjust automatic and accordingly (that is, they’re set to Microsoft Word 12 Object Library or Microsoft Word 14 Object Library as needed), and the macros run without a problem.
Microsoft Outlook Object Library switches properly from version 12 to 14. It does not switch properly from version 14 to 12. In that case, it gives the error that the libary is not found. Is this a bug? Is there a workaround? Something I’m overlooking?
ForEachLoop,
It appears that your question has largely been answered. I will merely add a bit of information for clarity's sake, and to provide this question with an answer. A user on the Microsoft Forums, Ossiemac, noted that LateBinding was the way to go, as has been stated by Siddarth Rout. As implied by Siddarth, that means you do not have to worry about references.
Ossiemac provided some sample code for using the LateBinding in the sending of an email, which I have reformatted and placed here:
Private Sub btnLateBindMethod_Click()
' Variables used for LateBinding
Dim objOutlook As Object 'Outlook.Application
Dim objEmail As Object 'Outlook.MailItem
Dim objNameSpace As Object 'Outlook.NameSpace
Const OutLookMailItem As Long = 0 'For Late Binding
Const OutLookFolderInbox As Long = 6 'For Late Binding
Const OutLookFormatHTML As Long = 2 'For Late Binding
Dim strSubject As String
Dim strAddress As String
On Error Resume Next
Set objOutlook = GetObject(, "Outlook.Application")
On Error GoTo 0
If objOutlook Is Nothing Then
Set objOutlook = CreateObject("Outlook.Application")
Set objNameSpace = objOutlook.GetNamespace("MAPI")
objNameSpace.GetDefaultFolder(OutLookFolderInbox).Display
End If
Set objEmail = objOutlook.CreateItem(OutLookMailItem)
strSubject = "Hello World"
With objEmail
'.To = strToAddress 'Commented to prevent accidental send
.Subject = strSubject
.BodyFormat = OutLookFormatHTML
.Display
'Full Name of window can change depending on Tools -> Options -> Mail Format
'Changing this option for outgoing mail changes the window name.
'However, AppActivate appears not to require entire name but needs up to end
'of - Message which is included in heading following the Subject string
'irrespective of the Mail Format option chosen.
AppActivate (strSubject & " - Message")
End With
End Sub
Jimmy Pena has an article discussing the contrast of EarlyBinding and LateBinding -
~JOL
Related
A few years ago I had a developer write a macro for me to print the current email as two pages per page rather than on two separate pages.
It used to run successfully on my old computer Win 10/Outlook 2013.
I now have a new computer Win10/Outlook 2021
It now comes up with a compile error "User defined type not defined" for the line
Dim wdApp As Word.Application
I only have a rudimentary grasp of VBA so am unable to solve this one. Any help would be greatly appreciated.
Code is as follows:
Option Explicit
Public Sub print_mail()
Dim objOL As Outlook.Application
Dim objMsg As Outlook.MailItem
Dim objAttachments As Outlook.Attachments
Dim objSelection As Outlook.Selection
Dim i As Long
Dim lngCount As Long
Dim Response As Integer
Dim msg As String
Dim strSubject As String
Dim currentItem As Object
Set objOL = CreateObject("Outlook.Application")
Set objSelection = objOL.ActiveExplorer.Selection
For Each currentItem In objSelection
If currentItem.Class = olMail Then
Set objMsg = currentItem
PrintFirstPage objMsg
End If
Next
Set objAttachments = Nothing
Set objMsg = Nothing
Set objSelection = Nothing
Set objOL = Nothing
End Sub
Public Sub PrintFirstPage(Mail As Outlook.MailItem)
Dim wdApp As Word.Application
Dim wdDoc As Word.Document
Dim olDoc As Word.Document
Set wdApp = CreateObject("Word.Application")
Set wdDoc = wdApp.Documents.Add(Visible:=True)
Set olDoc = Mail.GetInspector.WordEditor
olDoc.Range.Copy
wdDoc.Range.Paste
' With wdDoc
' .PageSetup.Orientation = wdOrientLandscape
' End With
'wdDoc.PrintOut
wdApp.PrintOut FileName:="", Range:=wdPrintRangeOfPages, Item:= _
wdPrintDocumentWithMarkup, Copies:=1, Pages:="1-2", PageType:= _
wdPrintAllPages, Collate:=True, Background:=True, PrintToFile:=False, _
PrintZoomColumn:=2, PrintZoomRow:=1, PrintZoomPaperWidth:=0, _
PrintZoomPaperHeight:=0
wdDoc.Close False
wdApp.Quit
End Sub
Use an untyped variable:
Dim appWD as Object
appWD = CreateObject("Word.Application")
Or try to add the Word object library reference to the project.
Inside the Visual Basic Editor , select Tools then References and scroll down the list until you see Microsoft Word 12.0 Object Library. Check that box and hit Ok.
VBA macros are not designed for distributing on multiple machines. If you need to deploy your code on a wide range of machines you would better consider transforming your solution to Office add-ins - it can be a COM add-in or a web-based one. See Walkthrough: Create your first VSTO Add-in for Outlook for more information.
When you move your VBA code to another machine you need to make sure that all COM references are added as it was on your original machine.
In order to solve your problem, you have to add the Word object library reference to your project.
Inside the Visual Basic Editor, select Tools then References and scroll down the list until you see Microsoft Word XX.0 Object Library. Check that box and hit Ok.
From that moment, you should have the auto complete enabled when you type Word. to confirm the reference was properly set.
Note, you could also use the late binding technology which doesn't require adding COM references:
' No reference to a type library is needed to use late binding.
' As long as the object supports IDispatch, the method can
' be dynamically located and invoked at run-time.
' Declare the object as a late-bound object
Dim oWord As Object
Set oWord = CreateObject("Word.Application")
' The Visible property is called via IDispatch
oWord.Visible = True
So, to start an Word 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 Word. See Using early binding and late binding in Automation for more information.
I have a function that creates an email via VBA.
I made this through Excel 2016. When some of my colleagues try to use it there an error of missing references (Outlook Library 16.0).
I looked in the internet for solutions and found the best is Late Binding. I have read about it but I don't understand how to make it work in the following example code.
Sub EscalateCase(what_address As String, subject_line As String, email_body As String)
Dim olApp As Outlook.Application
Set olApp = CreateObject("Outlook.Application")
Dim olMail As Outlook.MailItem
Set olMail = olApp.CreateItem(olMailItem)
olMail.To = what_address
olMail.Subject = subject_line
olMail.BodyFormat = olFormatHTML
olMail.HTMLBody = email_body
olMail.Send
End Sub
This is early binding:
Dim olApp As Outlook.Application
Set olApp = New Outlook.Application
And this is late binding:
Dim olApp As Object
Set olApp = CreateObject("Outlook.Application")
Late binding does not require a reference to Outlook Library 16.0 whereas early binding does. However, note that late binding is a bit slower and you won't get intellisense for that object.
As Callum pointed out, late binding involves changing your application reference to an object and not setting a reference to the library.
Without a reference Excel doesn't know anything about Outlook until runtime. This also means that not only will intellisense not work, the constant names for values in Outlook won't work either.
e.g. In Outlooks Immediate window if you type Application.CreateItem( you'll get a whole load of item types pop up to choose from. olContactItem for instance.
Excel hasn't a clue what olContactItem means - it's an Outlook constant that only Outlook or an application with a reference to Outlook understands.
In Outlooks immediate window type ?olContactItem and it will return 2. That's the number you need to use instead of the constant name.
So your code changes from
Application.CreateItem(olContactItem) to olApp.CreateItem(2)
You need to do this throughout your code.
In that situation I generally define the constant globally in a module like this, that way you preserve the descriptive value of the variable.
Public Const olFormatHTML = 2
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.
Good morning,
Using Outlook 2010 I compiled code to send all emails that were saved in a drafts folder of a given account. Now I've upgraded to Office 2013 I am getting an error... It is the .Send bit where it falls over and presents the error message:
"This method can't be used with an inline response mail item."
I am certain that there is a v simple method for sending drafts, but I have scoured the web and can't figure it as yet.
Public Sub SendDrafts()
Dim lDraftItem As Long
Dim myOutlook As Outlook.Application
Dim myNameSpace As Outlook.NameSpace
Dim myFolders As Outlook.Folders
Dim myDraftsFolder As Outlook.MAPIFolder
'Send all items in the "Drafts" folder that have a "To" address filled
'Setup Outlook
Set myOutlook = Outlook.Application
Set myNameSpace = myOutlook.GetNamespace("MAPI")
Set myFolders = myNameSpace.Folders
'Set Draft Folder. This will need modification based on where it's
Set myDraftsFolder = myFolders("accounts#credec.co.uk").Folders("Drafts")
'Loop through all Draft Items
For lDraftItem = myDraftsFolder.Items.count To 1 Step -1
'Check for "To" address and only send if "To" is filled in.
If Len(Trim(myDraftsFolder.Items.Item(lDraftItem).To)) ] 0 Then
'Send Item
myDraftsFolder.Items.Item(lDraftItem).Send
End If
Next lDraftItem
'Clean-up
Set myDraftsFolder = Nothing
Set myNameSpace = Nothing
Set myOutlook = Nothing
End Sub
I know this is old, but in case someone elses is looking for an answer:
"Active Inline Response" refers to a draft that is open in Outlook. So, when you are debugging, close the draft and flip back to a different message. Then see if your code will run.
I found that if you have clicked on the draft folder so that the Draft folder is active, then you get that error message, usually on the email in the folder that is highlighted.
SOLUTION: Click on any other folder, then run the code, should work, mine did!
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