Load Your Own External Command on the Fly

How to reload an external Revit command without having to restart the Revit session has been a subject of discussion many times in the past, e.g. in reloading an add-in for debug without restart, which lists four possibilities:

Revit 2014 adds some new twists to the scenario by the introduction of the MacroManager API, which enables programmatic listing, creating, removing, editing, debugging, and running of macros, and the option to load an add-in mid-session without restarting Revit. Unfortunately, there is no option to unload an add-in, once loaded.

The difficulty is that the AppDomain into which the add-in is loaded locks the add-in DLL, preventing any further changes to it.

One could create a new individual AppDomain for each add-in to work around that.

Another option is to not load the add-in from a DLL at all, but stream in the source code using reflection instead.

Here is an example of the latter approach in VB by David Rock and Yamin Tengono of BSE – Building Services Engineers. David says:

I am running my commands from the ribbon with the following function:

Public Shared Sub InvokeRevitCommand( _
  ByVal strCommandName As String,
  ByVal commandData As Object,
  ByRef message As String,
  ByVal elements As Object,
  ByVal fullPathDllName As String)
 
  ' Load the assembly into a byte array. 
  ' This way it WON'T lock the dll to disk.
 
  Dim assemblyBytes As Byte() = File.ReadAllBytes(
    fullPathDllName)
 
  Dim objAssembly As Assembly = Assembly.Load(
    assemblyBytes)
 
  ' Walk through each type in the assembly.
 
  For Each objType As Type In objAssembly.GetTypes()
    ' Pick up a class.
    If objType.IsClass Then
      If objType.Name.ToLower = strCommandName.ToLower Then
 
        Dim ibaseObject As Object = Activator.CreateInstance(objType)
 
        Dim arguments As Object() = New Object() {
          commandData, message, elements}
 
        Dim result As Object
 
        result = objType.InvokeMember(
          "Execute",
          BindingFlags.[Default] Or BindingFlags.InvokeMethod,
          Nothing, ibaseObject, arguments)
 
        Exit For
      End If
    End If
  Next
End Sub

This way I can re-build the DLL without exiting Revit and my sometimes quite large Revit projects.

I have over 100 Revit commands now and most are loaded via this dynamic method. This makes it super easy to update them on the fly.

Very many thanks to David and Yamin for developing and sharing this effective solution!