Communication Between Multiple Add-ins

Started by perry59, March 28, 2024, 06:13:10 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

perry59

Not Visio related but...

...the folks here are more helpful :)

does anyone know of any articles giving instructions on how to call code in one addin from another addin?
I have read these two articles msn , code project and implemented my solution accordingly but still get at best a message box from the addin I am calling into before getting null reference errors.
any tips appreciated!
what, me worry?

Nikolay

#1
Dear Perry, the approach from those two articles you listed should have worked. Maybe some minor issue with implementation.
I mean, you do it as following (minimal changes IMHO):

VisioAddin1:
===========

- Create some class where you define the methods to call from outside. Mark it as com visible
<ComVisible(True)>
Public Class ApiAddin1

    Public Sub SayHello(name As String)
        ' you can access Addin1 using Globals.ThisAddIn for example
        MessageBox.Show($"Hello {name}", "Addin1")
    End Sub

End Class

Return it from your addin when asked by overwriting RequestComAddInAutomationService
Public Class ThisAddIn

    Protected Overrides Function RequestComAddInAutomationService() As Object
        Return New ApiAddin1()
    End Function

    ....
End Class

That should be it. You should be already able to call it from VBA for example (from the "immediate" window).
To call it from another add-in, you use the ComAddins() and then just invoke the method like this:

VisioAddin2
===========

Public Class Ribbon1

    Private Sub Button1_Click(....)

        Globals.ThisAddIn.Application.COMAddIns("VisioAddin1").Object.SayHello("World")

    End Sub

End Class

perry59

#2
I was hoping you would chime in Nikolay :)
So the addin I want to call into is "visioTables". In it's properties (My project) I have checked "register for COM interop"
The addin I am calling from is called "visioElectrical", it has a reference set to visioTables.

inside visioTables:
partial listing of thisaddin.vb
Protected Overrides Function RequestComAddInAutomationService() As Object
        If accessVBA Is Nothing Then
            accessVBA = New ClassVBA()
        End If
        Return accessVBA
    End Function
   
   
partial listing of ClassVBA.vb
<ComVisible(True)> _
Public Interface IClassVBA
 Sub test()
End Interface


<ComVisible(True)> _
<ClassInterface(ClassInterfaceType.None)> _
Public Class ClassVBA
    Implements IClassVBA
   
    Public Sub test() Implements IClassVBA.test
        'when stepping into code from VE the messagebox executes
        MsgBox("classVBA")

        'Call noBOM()
        'Call BOM()

        'but execution stops when declaring variable
        Dim NewTable As New VisioTable
        NewTable.CreatTable("BOM", 1, 6, 4, 1, 0.5, 1, 1, True, True)
        NewTable = Nothing

    End Sub
End class

From visioElectrical I call into visioTables like this:

Public Sub debugMe()

        Dim junk As visioTables.ClassVBA
        junk = New visioTables.ClassVBA

        If Not junk Is Nothing Then
            junk.test()
        End If
        junk = Nothing
     
    End Sub
   
when I learned to turn off "my code only" in the debugger it would then step out of visioElectrical and into visioTables where it started to execute the "test" sub.
it would display the messagebox but when it hit the line where it tried to create a new table execution just stopped and the debugger appeared to lock up.
The call that you described,  Globals.ThisAddIn.Application.COMAddIns("VisioTables").Object.test(), does not work for me at all.
Once I add the dot after ("VisioTables"), "object" is not listed as a parameter in intellisense.

So I think I'm doing it right, but it is still not working as it should, I don't know what else to do!
what, me worry?

Nikolay

I've tested the code above (with 'Object'), seems to work. Maybe just intellisense issue?
Here I've shared a project with 2 add-ins, the second one is calling the first:

https://github.com/nbelyh/VisioSamples

BTW, you don't need to set "register for COM Interop" checkbox, it's of no use in this case, the only thing that is actually required is to mark the class to call with <ComVisible(true)> attribute.

perry59

#4
Ok, I got rid of the "register for COM Interop", had no ill effect. I noticed in your example the second addin does not have a reference to the first, I guess that is not necessary either, I also removed that from my project with no ill effect.
So my only issue now is HOW I call into the second addin.
this method seems much more intuitive, I see it often and it is implemented in the code project example:
Dim junk As visioTables.ClassVBA
junk = New visioTables.ClassVBA
If Not junk Is Nothing Then
    junk.test()
End If
junk = Nothing
It also has the benefits that after declaring the object intellisense shows me all the methods/properties etc.
And it allows me to step into the code of the second addin. Unfortunately if fails to execute the code in the other addin.

This method of calling into the second addin is unfamiliar to me:
Globals.ThisAddIn.Application.COMAddIns("VisioTables").Object.test()although it could be written as:
Dim addIn As Microsoft.Office.Core.COMAddIn
Dim automationObject As Object
addIn = Globals.ThisAddIn.Application.COMAddIns("visioTables")
automationObject = addIn.Object
automationObject.test
this method does execute the code (mostly) in the second addin but does not allow stepping into that code which is a real bummer because now I'll have to add a lot of debugging messages to find out why one function is failing when it works fine when called from it's parent addin.
Also, this method does not expose any methods/properties via intellisense. And since intellisense doesn't work you better make sure you spell everything right! If I could just get the first method to work, apparently it does for other folks but I must be doing something wrong.
So one method executes code but won't let me debug, the other lets me debug but doesn't execute code!
EDIT:
Actually setting a reference to the second addin within the first IS necessary when using method 1 of calling into the second addin
More info:
In the first method of calling the other addon "junk" in this case, in the watch window of the debugger it is shown as type "visioTables.ClassVBA" so it looks legit, however all the properties that are part of it are "red X'd" with a "typeinitializationException" that is probably to be expected since those properties won't have a value yet as there is no "new" function in the addin and no initialization code.

what, me worry?

Nikolay

Hm, for me it does step in into the first Add-In..
You can set a breakpoing on the "button click" event in the second one, and then if you execute "step" command you'll get into the first one.

perry59

stepping into the first addin is no problem, I can set a breakpoint at the button click as you said. But when I get to this call
Globals.ThisAddIn.Application.COMAddIns("VisioAddin1").Object.SayHello("World")
it stops there, using the other method of invocation

Dim junk As visioTables.ClassVBA
 junk = New visioTables.ClassVBA

 If Not junk Is Nothing Then
   junk.test()
End If
I CAN step into the second addin but something is not right as it stops executing once inside the second addin
what, me worry?

Nikolay

#7
I think here is the confusion.

If you are using the "second method", you are not actually calling the second Add-In, instead you are adding the CODE of the second add-in into your first Add-in (as a library reference) and calling it "locally".

I.e. in this case, you don't really need the "second" add-in, you can just uninstall it.

For me, when I set a breakpoing on this line:
Globals.ThisAddIn.Application.COMAddIns("VisioAddin1").Object.SayHello("World")And then use "Step Into" (F10 or F11), the debugger steps into the second add-in.
I've tried with the sample solution on github.

BTW, in your case maybe it could be more natural to simply copy the content (files) from the second add-in to your first add-in, and make it just one add-in instead? Or make sort of a "library" out of the second add-in you call, and then reference that library.

perry59

Quote from: Nikolay on April 08, 2024, 08:36:06 PMI think here is the confusion.

If you are using the "second method", you are not actually calling the second Add-In, instead you are adding the CODE of the second add-in into your first Add-in (as a library reference) and calling it "locally".

I.e. in this case, you don't really need the "second" add-in, you can just uninstall it.
True, I uninstalled the second addin and the "first method" still worked, but I still can't step into the second addin with the "second method".
I will probably just add the code from the second addin to the first and avoid this alltogether.
Thanks for all  your help Nikolay!
what, me worry?