Creating Addin with Multiple buttons, seperate dll's

Okay, I've done quite a bit of digging on this topic and not found a definitive answer.  I've managed to do this, creating code that load's dll's at runtime and adds them to the menu for the add-in. 

Some info to begin with:

I have a 'loader' assembly that is used by solidworks as the Addin.  This loader assembly includes the callback function for the addin

As solidworks is loading, my code looks through a folder to find dll files that represent buttons that I want to add to my menu

Every button dll file contains that button's callback sub as well as a link to a bmp file to use for the button image

Each button dll must be coded to meet a strict format that I rely on in my loader dll

I fire an event every time a button is pressed in solidworks that returns the button id of the pressed button

The name of the button dll and the name of the class inside the button dll must be exactly the same

Here's the code for the loader:

Imports System
Imports System.Collections
Imports System.Reflection
Imports System.Runtime.InteropServices
'Imports System.Runtime.InteropServices.DispatchWrapper
Imports System.Messaging

Imports SolidWorks.Interop.sldworks
Imports SolidWorks.Interop.swconst
Imports SolidWorks.Interop.swpublished
Imports SolidWorksTools
Imports SolidWorksTools.File

Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Data.SqlClient
Imports System.Collections.ObjectModel
Imports System.Drawing


_
_
            Description:="TDS Addin Manager", _
        Title:="TDS Addin Manager", _
        LoadAtStartup:=True _
        )> _
Public Class TDSTools
    Implements SolidWorks.Interop.swpublished.SwAddin

#Region "Local Variables"
    Dim WithEvents iSwApp As SldWorks
    Dim iCmdMgr As ICommandManager
    Dim addinID As Integer
    Dim dllLoc As String = My.Application.Info.DirectoryPath & "\Buttons"
    Dim objList(0) As Object
    Dim commandID As Integer
    Dim buttonNum As Integer
    Public Const mainCmdGroupID As Integer = 1
#End Region

#Region "Properties"

    ReadOnly Property SwApp() As SldWorks
        Get
            Return iSwApp
        End Get
    End Property

    ReadOnly Property CmdMgr() As ICommandManager
        Get
            Return iCmdMgr
        End Get
    End Property

#End Region

    Public Shared Sub RegisterFunction(ByVal t As Type)

        ' Get Custom Attribute: SwAddinAttribute
        Dim attributes() As Object
        Dim SWattr As SwAddinAttribute = Nothing

        attributes = System.Attribute.GetCustomAttributes(GetType(TDSTools), GetType(SwAddinAttribute))
        If attributes.Length > 0 Then
            SWattr = DirectCast(attributes(0), SwAddinAttribute)
        End If

        Try
            Dim hklm As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.LocalMachine
            Dim hkcu As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.CurrentUser

            Dim keyname As String = "SOFTWARE\SolidWorks\Addins\{" + t.GUID.ToString() + "}"
            Dim addinkey As Microsoft.Win32.RegistryKey = hklm.CreateSubKey(keyname)
            addinkey.SetValue(Nothing, 0)
            addinkey.SetValue("Description", SWattr.Description)
            addinkey.SetValue("Title", SWattr.Title)

            keyname = "Software\SolidWorks\AddInsStartup\{" + t.GUID.ToString() + "}"
            addinkey = hkcu.CreateSubKey(keyname)
            addinkey.SetValue(Nothing, SWattr.LoadAtStartup, Microsoft.Win32.RegistryValueKind.DWord)
        Catch nl As System.NullReferenceException
            Console.WriteLine("There was a problem registering this dll: SWattr is null.\n " & nl.Message)
            System.Windows.Forms.MessageBox.Show("There was a problem registering this dll: SWattr is null.\n" & nl.Message)
        Catch e As System.Exception
            Console.WriteLine("There was a problem registering this dll: " & e.Message)
            System.Windows.Forms.MessageBox.Show("There was a problem registering this dll: " & e.Message)
        End Try

    End Sub

    Public Shared Sub UnregisterFunction(ByVal t As Type)

        Try
            Dim hklm As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.LocalMachine
            Dim hkcu As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.CurrentUser

            Dim keyname As String = "SOFTWARE\SolidWorks\Addins\{" + t.GUID.ToString() + "}"
            hklm.DeleteSubKey(keyname)

            keyname = "Software\SolidWorks\AddInsStartup\{" + t.GUID.ToString() + "}"
            hkcu.DeleteSubKey(keyname)
        Catch nl As System.NullReferenceException
            Console.WriteLine("There was a problem unregistering this dll: SWattr is null.\n " & nl.Message)
            System.Windows.Forms.MessageBox.Show("There was a problem unregistering this dll: SWattr is null.\n" & nl.Message)
        Catch e As System.Exception
            Console.WriteLine("There was a problem unregistering this dll: " & e.Message)
            System.Windows.Forms.MessageBox.Show("There was a problem unregistering this dll: " & e.Message)
        End Try

    End Sub

#Region "ISwAddin Implementation"

    Function ConnectToSW(ByVal ThisSW As Object, ByVal Cookie As Integer) As Boolean Implements SolidWorks.Interop.swpublished.SwAddin.ConnectToSW
        iSwApp = ThisSW
        addinID = Cookie

        'loop through the dll files and add the ones that are named the same as the class inside to this assembly


        For Each name As String In My.Computer.FileSystem.GetFiles(dllLoc, FileIO.SearchOption.SearchTopLevelOnly, "*.dll")
            Dim assemblyType As System.Type
            Dim dllName As String = IO.Path.GetFileNameWithoutExtension(name)

            'loop through the types in the assembly to find the one with the right name, then add it


            For Each assemblyType In Assembly.LoadFrom(name).GetExportedTypes
                If dllName.ToUpper = assemblyType.Name.ToUpper Then


                    'add a slot to the array and create an object of the reference


                    If objList(objList.Length - 1) IsNot Nothing Then
                        Array.Resize(objList, objList.Length + 1)
                    End If
                    objList(objList.Length - 1) = DirectCast(Activator.CreateInstance(assemblyType), Object)
                    Exit For
                End If
            Next
        Next name

        'set the callback object to this assembly
        iSwApp.SetAddinCallbackInfo(0, Me, addinID)

        ' Setup the Command Manager
        iCmdMgr = iSwApp.GetCommandManager(Cookie)
        AddCommandMgr()
        AttachEventHandlers()

        ConnectToSW = True

    End Function

    Function DisconnectFromSW() As Boolean Implements SolidWorks.Interop.swpublished.SwAddin.DisconnectFromSW

        System.Runtime.InteropServices.Marshal.ReleaseComObject(iCmdMgr)
        iCmdMgr = Nothing
        System.Runtime.InteropServices.Marshal.ReleaseComObject(iSwApp)
        iSwApp = Nothing
        'The addin _must_ call GC.Collect() here in order to retrieve all managed code pointers
        GC.Collect()
        GC.WaitForPendingFinalizers()

        GC.Collect()
        GC.WaitForPendingFinalizers()

        DisconnectFromSW = True
    End Function

#End Region

#Region "UI Methods"
    Public Sub AddCommandMgr()
        Dim cmdGroup As CommandGroup = iCmdMgr.CreateCommandGroup(mainCmdGroupID, "TDS Tools", "Tools Developed by TDS", "", -1)
        Dim menuToolbarOption As Integer = swCommandItemType_e.swMenuItem Or swCommandItemType_e.swToolbarItem
        Dim id As Integer
        Dim ImageList As New Bitmap((objList.Length) * 16, 16)
        Dim g As Graphics = Graphics.FromImage(ImageList)
        Dim picLoc As String = My.Application.Info.DirectoryPath & "\ImageList.bmp"
        Dim buttonImage As Bitmap
        Dim buttonAdder As Integer = 0

        cmdGroup.HasToolbar = True
        cmdGroup.HasMenu = True

        'loop through the button objects, set the properties and run it's 'addCommand() sub to add the button to the command group
        For i = 0 To objList.Length - 1
            If i <> 0 Then
                buttonAdder = 1
            End If
            With objList(i)
                buttonImage = .ButtonPicture
                g.DrawImage(buttonImage, New Point(i * 16, 0))
                buttonImage = Nothing
                .ButtonPosition = -1
                .Commandgroup = cmdGroup
                .SwApp = iSwApp
                .ToolBarOption = menuToolbarOption
                .BtnUserID = i + 1000
                .ButtonImageListIndex = i
                .AddCommand()
            End With
        Next

        'save the new imagelist bitmap
        ImageList.Save(picLoc)

        'set the command group icon list to the new imageList bitmap
        cmdGroup.LargeIconList = picLoc
        cmdGroup.SmallIconList = picLoc
        cmdGroup.LargeMainIcon = picLoc
        cmdGroup.SmallMainIcon = picLoc

        'active the command group
        cmdGroup.Activate()


       'loop through the command group and get the commandID's assigned by solidworks.  add the id's to the button objects so we can reference them later
        For i = 0 To cmdGroup.NumberOfGroupItems - 1
            id = cmdGroup.CommandID(i)
            objList(i).commandID = id
        Next

    End Sub


    Public Sub RemoveCommandMgr()
        Try
            iCmdMgr.RemoveCommandGroup(mainCmdGroupID)
        Catch e As Exception
        End Try
    End Sub


    Function CompareIDs(ByVal storedIDs() As Integer, ByVal addinIDs() As Integer) As Boolean

        Dim storeList As New List(Of Integer)(storedIDs)
        Dim addinList As New List(Of Integer)(addinIDs)

        addinList.Sort()
        storeList.Sort()

        If Not addinList.Count = storeList.Count Then

            Return False
        Else

            For i As Integer = 0 To addinList.Count - 1
                If Not addinList(i) = storeList(i) Then

                    Return False
                End If
            Next
        End If

        Return True
    End Function
#End Region


#Region "UI Callbacks"

    'this is the sub that is run when any addin button is pressed


    Sub callback()


        'loop through the button objects and find the one that has a command id that matches the id of the button that was just pressed


        For i = 0 To objList.Length - 1
            If objList(i).commandID = commandID Then
                'once the object is found run it's buttonCallback() sub
                objList(i).ButtonCallBack()
                Exit For
            End If
        Next

    End Sub

#End Region

#Region "Event Methods"
    Sub AttachEventHandlers()
        AttachSWEvents()
    End Sub

    Sub AttachSWEvents()
        Try

          'This event will fire every time a solidworks button is pressed
            AddHandler iSwApp.CommandOpenPreNotify, AddressOf Me.SldWorks_CommandOpenPreNotify
        Catch e As Exception
            Console.WriteLine(e.Message)
        End Try
    End Sub

    Sub DetachSWEvents()
        Try
            RemoveHandler iSwApp.CommandOpenPreNotify, AddressOf Me.SldWorks_CommandOpenPreNotify
        Catch e As Exception
            Console.WriteLine(e.Message)
        End Try
    End Sub

    Function SldWorks_CommandOpenPreNotify(ByVal command As Integer, ByVal userCommand As Integer) As Integer

          'This is how I determine what button has been pressed
        commandID = userCommand
    End Function

#End Region

End Class

Here's the code for the button template:

'Created by JVC 9/12.
'Once this class is finished, compile it to the button folder located in the addIn manager folder
'current location is C:\SolidWorks Data\TDS\Add-Ins\TDS AddIn Manager\Buttons
Imports System
Imports System.Drawing

Imports SolidWorks.Interop.sldworks
Imports SolidWorksTools
Imports SolidWorks.Interop.swconst
Imports SolidWorks.Interop.swpublished


'Change the name of the class to something meaningful to your new button.  You must also change the Assembly Name in Project>Properties, the name of your project (in solution explorer),
'and the name of the .vb file to be EXACTLY the same as the name of your class.  Otherwise, this will not be loaded by the addin manager.  You must also rename the .bmp file
'in the solution explorer to the same name as the class
Public Class AddInButton


#Region "Required Variables"
   'all of these variables are required by the AddIn Manager, do not remove any of them
    Dim WithEvents iSwApp As SldWorks
    Dim cmdGroup As CommandGroup
    Dim menuToolbarOption As Integer
    Dim cmdIndex As Integer
    Dim userID As String
    Dim cmdID As Integer
    Dim imageIndex As Integer
    Dim callbackName As String = "callback"
#End Region

#Region "Required Properties"

    'All of these properties are required by the addin manager, do not remove any of them

    WriteOnly Property BtnUserID As String
        Set(value As String)
            userID = value
        End Set
    End Property

    WriteOnly Property ButtonPosition As Integer
        Set(value As Integer)
            cmdIndex = value
        End Set
    End Property

    WriteOnly Property ToolBarOption As Integer
        Set(value As Integer)
            menuToolbarOption = value
        End Set
    End Property

    WriteOnly Property Commandgroup() As CommandGroup
        Set(value As CommandGroup)
            cmdGroup = value
        End Set
    End Property

    Property SwApp() As SldWorks
        Get
            Return iSwApp
        End Get
        Set(value As SldWorks)
            iSwApp = value
        End Set
    End Property

    Property CommandID As Integer
        Get
            Return cmdID
        End Get
        Set(value As Integer)
            cmdID = value
        End Set
    End Property

    WriteOnly Property ButtonImageListIndex As Integer

        Set(value As Integer)
            imageIndex = value
        End Set
    End Property

#End Region

    'this property is also required but must be updated to locate your new button image bmp
    Public ReadOnly Property ButtonPicture As Bitmap
        Get
            'don't forget to change the name of the bmp here
            Dim picture As New Bitmap(My.Application.Info.DirectoryPath & "\AddInButton.bmp")
            Return picture
        End Get
    End Property

   'this is the sub that is called by the addin manager to add this button to the toolbar.  Update the text strings to be something meaningful
    'don't change anything else
   Public Sub AddCommand()

        cmdGroup.AddCommandItem2("Addin Button Template Name", cmdIndex, "Addin button template hint", "addin button template tooltip", imageIndex, callbackName, "", userID, menuToolbarOption)

    End Sub

#Region "Executed Callbacks"

    Public Sub ButtonCallBack()
        'this is where you put your code to make your button do something
    End Sub

#End Region

End Class

Enjoy!

Jason

SolidworksApi macros