Check Your Premises


August 26, 2008 – 12:47 pm by JP

    When you are using early bound code for your own personal applications, it's easy to ensure that the proper object libraries are referenced: Simply go to Tools > References and check off the libraries you need. Then you can code using fully qualified references like "Excel.Range" and "Outlook.Application".

    If you are writing code for others, however, there's no guarantee that the libraries will be referenced or even installed. For example, if your code uses ADO to utilize an Access database, your code needs to check if ADO is referenced and available before making any attempt to use ADO code.

    I've written previously about version checking (see Make your Office add-in version-independent); here are some sample subs that can make it happen. This first sub will list the references from the current workbook directly onto the worksheet, so you can see the names of each project. I saw this code somewhere else, I think it was Colo's Excel Junk Room, but I wasn't able to find a link, so I rolled my own. The Name Property is what we're most concerned with; it's how we are going to identify references later on in the "live" code.

VBA:
  1. Sub ListProjectReferences()
  2. Dim NextRow As Long
  3. Dim NextCol As Long
  4. Dim lCount As Long
  5. Dim objRef As VBIDE.Reference
  6. Dim rng As Excel.Range
  7.  
  8. Set rng = Range("A1")
  9.  
  10. NextRow = 0
  11. NextCol = 1
  12.  
  13. With Application.VBE.ActiveVBProject.References
  14.   For lCount = 1 To .Count
  15.     Set objRef = .item(lCount)
  16.  
  17.     Debug.Print objRef.Name & " " & objRef.FullPath
  18.     Cells(NextRow + 1, NextCol).Value = objRef.Name
  19.     Cells(NextRow + 1, NextCol + 1).Value = objRef.FullPath
  20.  
  21.     NextRow = NextRow + 1
  22.  
  23.   Next lCount
  24. End With
  25.  
  26. ExitProc:
  27. Set rng = Nothing
  28. Set objRef = Nothing
  29. End Sub

    The above code requires a reference to the Microsoft Visual Basic for Applications Extensibility library. As an alternative, you can change "Dim objRef As VBIDE.Reference" to "Dim objRef As Object" and it will work without the reference. The name and path of each currently referenced object library is written to the worksheet, starting in cell A1. Go ahead and test this out by setting a few references to other libraries and re-running the code to see the Name and the full path.

    Now that you have the names of each reference, you can loop through the collection quickly to see if the appropriate libraries needed for your application are already referenced. Then you can either move on to the purpose of the code, or create the object library references if they didn't already exist.

    For example, if we needed a reference to Outlook and DAO for our application, we run the code above and learn that, if referenced, the names are "Outlook" and "DAO" respectively, for each object library. Here's some startup code that would check if they are already referenced. We loop through the references and check the name, and if it matches, we use two Boolean variables to store the result.

VBA:
  1. Private Sub Workbook_Open()
  2. Dim lCount As Long
  3. Dim strRefName As String
  4. Dim bIsOutlookLibReferencedAtStartup As Boolean
  5. Dim bIsDAOLibReferencedAtStartup As Boolean
  6. ' check if each needed object library is already referenced
  7. With Application.VBE.ActiveVBProject.References
  8.   For lCount = 1 To .Count
  9.     strRefName = .item(lCount).Name
  10.     If strRefName = "Outlook" Then
  11.       bIsOutlookLibReferencedAtStartup = True
  12.     ElseIf strRefName = "DAO" Then
  13.       bIsDAOLibReferencedAtStartup = True
  14.     End If
  15.   Next lCount
  16. End With
  17. End Sub

    If our Boolean variable is False, then the needed reference doesn't already exist, so we need to add it. We'll continue with Outlook only. We'll use a separate single-purpose sub called AddVBRef (below) and pass the reference name we want to add as an argument. If successful, we use another boolean variable to remember that we referenced Outlook, so we can de-reference it later.

VBA:
  1. Dim bWeStartedOutlook As Boolean
  2. If Not bIsOutlookLibReferencedAtStartup Then
  3.   ' Outlook was not already referenced when we started
  4.   If Not AddVBRef("Outlook") Then
  5.     MsgBox "This add-in requires a reference to Outlook object library." & _
  6.          " Please make sure that Outlook is installed on your" & _
  7.     "computer and that you can set a reference to it manually.", vbCritical
  8.     ThisWorkbook.Close False
  9.     Exit Sub
  10.   Else
  11.     ' we were able to successfully reference Outlook
  12.     bWeStartedOutlook = True
  13.   End If
  14. End If

VBA:
  1. Private Function AddVBRef(strRefName As String) As Boolean
  2. ' short sub that takes argument and adds references whatever
  3. ' reference is passed to it, returns True/False to calling sub
  4. AddVBRef = False
  5. Dim OutlookStartFolder As String
  6.  
  7. With Application.VBE.ActiveVBProject.References
  8.   Select Case strRefName
  9.     Case "Outlook"
  10.       OutlookStartFolder = OFCStartFolder("Outlook")
  11.       Select Case Application.Version
  12.         Case Is <10 ' Office 2000
  13.           On Error Resume Next
  14.           .AddFromFile OutlookStartFolder & "\msoutl9.olb"
  15.           On Error GoTo 0
  16.           If Err = 0 Then AddVBRef = True
  17.           Exit Function
  18.         Case Is>= 10 ' Office 2002-2007
  19.           On Error Resume Next
  20.           .AddFromFile OutlookStartFolder & "\msoutl.olb"
  21.           On Error GoTo 0
  22.           If Err = 0 Then AddVBRef = True
  23.           Exit Function
  24.       End Select
  25.     End Select
  26. End With
  27. End Function

    Right now AddVBRef only adds a reference to Outlook; you would add additional Case statements, using the Name you want to add. Also keep in mind that it only works for Office programs; the function calls another function, OFCStartFolder which returns the install path for whatever Office program you pass to it:

VBA:
  1. Function OFCStartFolder(strProgram As String, Optional lVersion As Long) As String
  2. ' from Jon Peltier, slightly modded by Jimmy Pena 8-11-08
  3. '
  4. ' strProgram can be "Excel", "Word", "Access", "PowerPoint", "Outlook", "Publisher"
  5. ' lVersion can be 2007, 2003, 2003, 2000 - if blank, it looks for highest version available
  6. '
  7. Dim wsh As Object
  8. Dim i As Integer
  9. Dim sReg As String
  10. Dim lVerNum As Long
  11.  
  12. If lVersion <> 0 Then
  13. ' We are looking for a specific version's install folder, i.e. if someone has multiple versions
  14. ' and we want to hook a specific version.
  15. ' However, the point of an add-in is that we don't know what version the end user has, so
  16. ' this part of the IF loop should rarely be used.
  17.  
  18.   Select Case lVersion
  19.     Case 2007
  20.       lVerNum = 12
  21.     Case 2003
  22.       lVerNum = 11
  23.     Case 2002
  24.       lVerNum = 10
  25.     Case 2000
  26.       lVerNum = 9
  27.     Case Else
  28.       MsgBox "Please specify a valid version."
  29.       GoTo ExitProc
  30.   End Select
  31.  
  32.   sReg = "HKLM\SOFTWARE\Microsoft\Office\" & lVerNum & ".0\" & strProgram & "\InstallRoot\Path"
  33.   Set wsh = CreateObject("Wscript.Shell")
  34.  
  35.   On Error Resume Next
  36.   OFCStartFolder = wsh.RegRead(sReg)
  37.   On Error GoTo 0
  38. Else
  39. ' no version was specified, just find highest version
  40.   Set wsh = CreateObject("Wscript.Shell")
  41.  
  42.   For i = 12 To 9 Step -1   ' (12 = 2007, 11 = 2003, 10 = 2002, 9 = 2000)
  43.     sReg = "HKLM\SOFTWARE\Microsoft\Office\" & CStr(i) & ".0\" & strProgram & "\InstallRoot\Path"
  44.    
  45.     On Error Resume Next
  46.     OFCStartFolder = wsh.RegRead(sReg)
  47.     On Error GoTo 0
  48.  
  49.     If Len(OFCStartFolder)> 0 Then Exit For
  50.   Next i
  51. End If
  52. End Function

    And here is a companion sub that removes any reference passed to it. Again we are passing the Name Property here. We'll need this in case we referenced the object library, we must clean up after ourselves and remove that reference if our code is removed or uninstalled.

VBA:
  1. Private Function RemoveVBRef(strRefName As String)
  2. ' short sub that takes argument and de-references
  3. ' whatever reference is passed to it
  4. Dim lCount As Long
  5. With Application.VBE.ActiveVBProject.References
  6.   For lCount = 1 To .Count
  7.     If .item(lCount).Name = strRefName Then
  8.       .Remove .item(lCount)
  9.       Exit Function
  10.     End If
  11.   Next i
  12. End With
  13. End Function

    Which would be used as follows in our shutdown code:

VBA:
  1. If bWeStartedOutlook Then RemoveVBRef ("Outlook")

    Remember, bWeStartedOutlook would only be true if we manually set a reference to Outlook with the AddVBRef function, which itself would only be run if Outlook wasn't already referenced at startup.

    One thing I should also mention is that the purpose of much of the code above is to check if we can use early bound code. If you are going to use late-bound code, you won't need much of it (just a check of the install path to see if the program is installed before we can reference any of its objects).

Oh and if anyone was interested, here is the computer I ended up buying: Gateway

Enjoy,
JP


Share and Enjoy:
  • StumbleUpon
  • Technorati
  • Digg
  • Google
  • del.icio.us
  • MisterWong

Print This Post Print This Post  |  Email This Post Email This Post  |  Permalink  |  Subscribe to this feed Subscribe now!

Filed Under: AddIns, Excel, VBA
Tags: ,

This post has 75 views since August 26, 2008 – 12:47 pm.
  1. 2 Responses to “Check Your Premises”

  2. Re "OFCStartFolder", it's dangerous to ask a user which version of anything they are using. What I would do is accept their best guess for version number, check that the reg key exists, if so they got lucky and the code can proceed, if not do the registry search for the possible versions.

    Alternatively you could check the registry for available versions first, then let the user select, with the default being the most recent.

    By Jon Peltier on Aug 27, 2008

  3. The OFCStartFolder function was meant to be tweaked by the programmer, it wouldn't really be something the end user would access. I just added the Select Case in there to make it more flexible and to demonstrate the available options. I mentioned in the code comment that for the most part, you wouldn't know which version is highest version installed on someone else's computer, so that part of the code isn't something you would want to use often.

    Thx,
    JP

    By JP on Aug 27, 2008

Post a Comment

To post VBA code in your comment, use [VBA] tags, like this: [VBA]Code goes here[/VBA].





Subscribe without commenting

Keep Reading:

Browse Posts:


« Forget about it! || Conditional Import Text Files »