2

I've been asked to troubleshoot a VBA script that sends an email from an Access database. The DB and VBA was developed on Access 2010 (Data stored in a SQL db), and likely targets Outlook 2010.

Currently, we use Outlook 2013 and 2016.

When my script (noted below) runs, an error is generated on the Outlook.Application declaration: User Defined Data Type Not Defined.

Here's be beginning of the script, where we define data types.

Option Compare Database
Option Explicit
 
' InitOutlook sets up outlookApp and outlookNamespace.
Private outlookApp As Outlook.Application
Private outlookNamespace As Outlook.NameSpace

The rest of the script is below. It is being triggered by a form button directly calling SendEmail() and passing an email address as a variable.

Private Sub InitOutlook()
    ' Initialize a session in Outlook
    Set outlookApp = New Outlook.Application
    
    'Return a reference to the MAPI layer
    Set outlookNamespace = outlookApp.GetNamespace("MAPI")
    
    'Let the user logon to Outlook with the
    'Outlook Profile dialog box
    'and then create a new session
    outlookNamespace.Logon , , True, False
End Sub

Public Sub SendEmail(varTo As Variant)
    Dim mailItem As Outlook.mailItem
        
    InitOutlook
    Set mailItem = outlookApp.CreateItem(olMailItem)
    mailItem.To = varTo & ""
    mailItem.Subject = "subject text"
    mailItem.Body = "Body text"
    mailItem.Display
    
    Set mailItem = Nothing
    CleanUp
End Sub

I'm not super familiar with VBA / Access, but I've worked with VB.NET and I've been fairly certain since the beginning that this was a simple matter of "it's missing an imports statement or a reference."

After digging into it a bit, I've found online here that in order to use this functionality, you must add a reference to the Microsoft Outlook XX.X Object Library.

I haven't seen the references that are on this database yet (Working on getting there now), as I have limited access to this access DB, as it contains a lot of sensitive information and I have to be supervised when viewing it.

However, given that we used Outlook 2010 when we wrote this script, and are now split between Outlook 2013 and 2016, I believe we need to update this reference to a more recent one.

I was discussing this with a coworker here, and he asked me an important question:

Can we reference more than one version of the Microsoft Outlook Object Library?

If we target the most recent version of the library, for say, Outlook 2016, will the script not function for Outlook 2013 users?

Update: I found out through testing that if we used the MS Outlook 16.0 object library, Outlook 2013 would not recognize the reference, and would throw errors that the reference was missing.

If we used the MS Outlook 15.0 library, the script worked on machines with either version of outlook.

Community
  • 1
  • 1
schizoid04
  • 888
  • 1
  • 11
  • 27
  • You can use late binding instead of early binding and it will figure out on its own which version to use. – braX May 01 '18 at 15:36
  • 1
    As I mentioned [here](https://stackoverflow.com/questions/24630378/i-need-to-code-for-the-correct-reference-to-ms-outlook-from-excel/24634115) you can avoid it all if you just use late-binding instead of early binding. If you insist on early binding, and need to be Version-agnostic, you probably need to go the route of using [Conditional Compilation](https://msdn.microsoft.com/en-us/vba/language-reference-vba/articles/understanding-conditional-compilation). Early binding will be much easier :) – David Zemens May 04 '18 at 12:41
  • 1
    @DavidZemens I attempted to switch to late binding and your post was very helpful with that, but realized while testing it that there were many unrelated sections of our code that would need to be changed as well, that also used early binding. I'm not familiar with the other areas that need to be updated, and therefore can't reliably test the changes on the other pages to ensure they'll work for all users. I ended up referencing MS Outlook 15.0 Object Library, and this corrected the issue for our outlook 2013 and 2016 users. – schizoid04 May 04 '18 at 15:34
  • The reason I was asking about the references was in case late binding did not work, as it is not something I've practiced before and I did not have the resources to troubleshoot it if i ran into issues with that, as it's not my main job function – schizoid04 May 04 '18 at 15:35
  • 1
    that'll work too. just include the earliest supported reference and it should take care of itself. very few things are not backwards compatible. cheers – David Zemens May 04 '18 at 15:37
  • Not directly related to your question but be sure you have rights to distribute the Outlook libraries if you plan to bundle them with your app. – nicomp May 04 '18 at 16:49
  • No, if you use a previous reference, WHEN you make that reference you note that it is a hard coded path name. BEYOND SILLY to suggest that a earlier binding will by magic work with later versions. If you are writing in VB6, FoxPro, c++, .net etc., and you reference a specific library, then such code will FOR SURE BUST! Such referances are a hard coded path name. The ONLY reason why access "can" work is on startup it attempts to change the references, but in effect you already busted the code. – Albert D. Kallal May 04 '18 at 19:16
  • The change to late binding is VERY minor. If done right only the create object need be changed - rest of code usually works as "is", but some constants will have to be set since the compiler will not have use of constants provided by the referenced library that you not referencing anymore. – Albert D. Kallal May 04 '18 at 19:18

2 Answers2

4

If you need to use early-binding, add ONLY the earliest supported reference (i.e., there's no need to add reference to 2003 Outlook if you only intend to support 2013+) and it should take care of itself. Very few things are not backwards compatible

Otherwise, use late-binding. This requires using the CreateObject function instead of the New keyword for any Outlook.__object__.

Note that you'll need to explicitly declare the Outlook constants like olMailItem otherwise they will raise compile errors (assuming you're using Option Explicit):

Option Compare Database
Option Explicit

' InitOutlook sets up outlookApp and outlookNamespace.
Private outlookApp As Object ' Outlook.Application
Private outlookNamespace As  Object ' Outlook.NameSpace

And then small tweaks to the body of your code:

Const olMailItem As Long = 0 '## You need to add this enumeration!

Private Sub InitOutlook()
    ' Initialize a session in Outlook
    Set outlookApp = CreateObject("Outlook.Application")

    'Return a reference to the MAPI layer
    Set outlookNamespace = outlookApp.GetNamespace("MAPI")

    'Let the user logon to Outlook with the
    'Outlook Profile dialog box
    'and then create a new session
    outlookNamespace.Logon , , True, False
End Sub

Public Sub SendEmail(varTo As Variant)
    Dim mailItem As Object ' Outlook.mailItem

    InitOutlook
    Set mailItem = outlookApp.CreateItem(olMailItem)
    mailItem.To = varTo & ""
    mailItem.Subject = "subject text"
    mailItem.Body = "Body text"
    mailItem.Display

    Set mailItem = Nothing
    CleanUp
End Sub
David Zemens
  • 53,033
  • 11
  • 81
  • 130
0

Short(-ish) Answer: Yes, you can.

And for your second question; the library reference should not have any adverse effect like that described. I'd say the best way to check for certain though is to test the issue.

Long Answer: Why would you need to? IIRC; the newer reference should contain everything the older one did, with additional library information. However, if that is somehow not the case and the newer reference does not cover the problem, adding the new reference would likely solve the issue, but adding library references willy nilly is not always the best practice.

My best guess here is that a single Outlook library reference will solve your problem anyway.

ccarpenter32
  • 1,058
  • 2
  • 9
  • 17
  • So should I just target the latest outlook library? Or should I try to convert to late binding instead? – schizoid04 May 01 '18 at 15:04
  • 1
    Just go for late bindings. It solves these kind of problems. Converting to late bindings should be very easy. – Erik A May 01 '18 at 16:03
  • 1
    The newer library should contain everything the old one did (usually). But the newer library will not be available on the older version of the application, which is the OP problem. IF you must attach a reference (rather than just using late-bound object references), I think you need to attach the *earliest* supported version. – David Zemens May 04 '18 at 12:43
  • @DavidZemens was correct on this note; I experienced this yesterday through some testing. Have updated the op. – schizoid04 May 04 '18 at 15:41