How can I launch an Access add-in (not COM add-in) from VBA code?
Asked Answered
H

2

7

In Microsoft Access I have an add-in (*.accda) function that I would like to call from VBA just as if I clicked the item from the Database Tools -> Add-ins menu.

enter image description here

In the registry, there is a key that lists the library path and expression, but I haven't found an place where the ability to invoke this is exposed to VBA.

enter image description here

The closest I have come is to add a reference to the library, then immediately remove the reference. This loads the VBE project and allows me to call the function without adding a persistent reference to the project.

With References
    .AddFromFile Environ$("AppData") & "\Microsoft\AddIns\Version Control.accda"
    .Remove references("MSAccessVCS")
End With
Run "MSAccessVCS.AddInMenuItemLaunch"

The problem is that if I close the CurrentDB, the library is no longer loaded. On the other hand, if I invoke the add-in through the add-ins menu, it is persisted even when the current database is closed.

Since the add-in needs to perform functions that include the closing of the current database, it only works if I launch the add-in through the menu.

Has anyone found a system command, WizHook reference, API call, or other function that can launch an add-in in the same way as clicking on the link in the add-ins menu? The end goal here is to create an automated DevOps process where the database opens and invokes the add-in function without any user intervention (i.e. menu clicks) required.

Working Solution

A huge thank you to Victor K for pointing me in the right direction! It turns out that you can actually include a file path in the Application.Run function to load an add-in at the application level, which is what happens when you launch the add-in from a menu command.

The syntax is similar to how you would load an add-in in Microsoft Excel, but as of yet I have not figured out how to successfully add the procedure name to the file path to call the add-in function in a single call. Instead, I take a two-step approach where the first call loads the library, and the second call runs my add-in function. I have implemented this as a function in my VBA code.

Hopefully no one is actually using 30 named arguments in their add-in function, :-) but I went ahead and added all of them in the second example to fully mirror the functionality of Application.Run.

' Example of calling an add-in function.
Public Sub ShowVersionControl()
    RunAddin Environ$("AppData") & "\Microsoft\AddIns\Version Control.accda", "MSAccessVCS.AddInMenuItemLaunch"
End Sub


' Simple function to demonstrate concept:
Public Sub RunAddin(strPath As String, strFunction As String)
    ' The following lines will load the add-in at the application level,
    ' but will not actually call the function. Ignore the error of function not found.
    On Error Resume Next
    Application.Run strPath & "!DummyFunction"
    Application.Run strFunction
End Sub


' More robust function with full parameter support for .Run()
Public Sub RunAddin2(strPath As String, strFunction As String, _
    Optional Arg1, Optional Arg2, Optional Arg3, Optional Arg4, Optional Arg5, _
    Optional Arg6, Optional Arg7, Optional Arg8, Optional Arg9, Optional Arg10, _
    Optional Arg11, Optional Arg12, Optional Arg13, Optional Arg14, Optional Arg15, _
    Optional Arg16, Optional Arg17, Optional Arg18, Optional Arg19, Optional Arg20, _
    Optional Arg21, Optional Arg22, Optional Arg23, Optional Arg24, Optional Arg25, _
    Optional Arg26, Optional Arg27, Optional Arg28, Optional Arg29, Optional Arg30)
    
    Dim lngError As Long
    
    ' Trap and ignore expected error.
    On Error Resume Next
    
    ' The following lines will load the add-in at the application level,
    ' but will not actually call the function. Ignore the error of function not found.
    Application.Run strPath & "!DummyFunction"
    
    ' Check returned error just in case it was something else.
    lngError = Err.Number
    If Err Then Err.Clear
    On Error GoTo 0
    If lngError <> 2517 Then Err.Raise lngError
    
    ' Now that the library is loaded, we can call the function.
    Application.Run strFunction, _
        Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10, _
        Arg11, Arg12, Arg13, Arg14, Arg15, Arg16, Arg17, Arg18, Arg19, Arg20, _
        Arg21, Arg22, Arg23, Arg24, Arg25, Arg26, Arg27, Arg28, Arg29, Arg30

End Sub

Now I can successfully call a function in my add-in in such a way that it is loaded at the application level. This allows the add-in to close the current database and rebuild it from source code in a fully automated process.

Note: When using this approach, you may need to add a trusted location to allow the add-in to run without a security warning.

Hittel answered 8/6, 2020 at 19:48 Comment(8)
Would it work to just call an empty function in that add-in using Application.Run to load it? Also why does it not work to permanently add this add-in to references?Sellars
@VictorK - From my testing, calling the function doesn't work till the library is loaded. I would rather not add this as a permanent reference because it is strictly used by the developers, not the end users. (See more details on the add-in here.)Hittel
I preload add-ins for some users with Application.Run "'X:\Filepath\MyAddIn.xlam'!Ping" where Ping is an empty sub in MyAddIn. This loads MyAddIn. It did not test it for Access, so maybe it does not work for it at all. There may also be some security restrictions on files that are not loaded via references. Maybe I am also misinterpreting what you want to achieve.Sellars
@VictorK - Nice!! I didn't know you could pass a full file path to the Application.Run function. We are really close! It loads the add-in file, but I think the syntax might be different in Microsoft Access for the part where it specifies the function name...Hittel
What type of function are you running after loading the add-in? And what is the problem after you load the add-in?Sellars
@VictorK - The problem that I was that if I load the library as a reference to the current database, the library is unloaded when the database is closed. However, if I run the add-in using the menu, the library is connected to the active instance of Access, not the open database. This allows the add-in to close the current database and rebuild it from source files as described here. Your idea of using the full path in Run is fantastic! If you can post it as an answer, I would be happy to award you the bounty.Hittel
@AdamsTips: Whatfor do you call the DummyFunction at all? Simply calling Application.Run strPath instead will work fine too here.Vane
@UnhandledException - In my testing Access still throws the same error whether or not you include the function name. Personally, I like to keep the function name there to mirror the Excel VBA syntax, but you are exactly right that it isn't required to load the add-in library. If Microsoft ever fixes this functionality to work like Excel, we wouldn't see any error at all and could potentially call the desired function at the same time.Hittel
S
6

In order to load an add-in or a file that contains macros you can use Application.Run with a full path to the file like this:

In the add-in create an empty subroutine, for example:

Public Sub Ping()
End Sub

Call this subroutine from the project that needs to load the add-in:

Application.Run "'X:\ExamplePath\Addins\MyAddIn.accda'!Ping"

In fact you can normally use it to call whatever subroutine you want right away:

Application.Run "'X:\ExamplePath\AddIns\MyAddIn.accda'!SubIWnatToRunInTheAddIn"

You can also use this approach in a similar way to late binding (plus you can pass arguments as well):

In add-in:

Public Function NewAddInClass() as AddInClass
Set NewAddInClass = New AddInClass
End Function

In calling code:

Public Sub CallAddInClass()
Dim TestClass as Object
Set TestClass = Applcation.Run ("'X:\AddinPath\MyAddIn.accda'!NewAddInClass")
TestClass.ExampleMethod
End Sub

I don't work as much with Access, but it appears that Access has CurrentProject object that you can perhaps return a reference to from the add-in in a similar way that could give you access to AllMacros, AllModules, etc.

Sellars answered 30/6, 2020 at 15:49 Comment(1)
Thanks, Victor! The key piece here was using the full path in Application.Run. This loads the add-in at the application level, which is what I was trying to achieve. I will edit my question to include a working example using this approach.Hittel
K
-1

Why not just record a macro of loading it manually that runs when the app opens, and run another macro that unloads it when closing the app?

Kassel answered 29/6, 2020 at 23:26 Comment(1)
Eh, you might want to elaborate on how to do that. Access doesn't have a macro recorder.Breault

© 2022 - 2024 — McMap. All rights reserved.