Chapter Six: Loading VSTA Addins with MEF

My integration journey has come to an end. For my last post, I am going to write about a new way of loading VSTA AddIns: MEF. I have talked about MEF some in my previous posts, and have successfully used both VSTA and MEF together before. This time, I have successfully used MEF to load VSTA AddIns. To do this, I had to edit part of the MEF library to prevent it from locking the .dll and .pdb files, so the debugging and rebuilding of AddIns would still be possible.

Instead of having NEF load the assembly itself, which it usually does, I had it load a byte array of the .dll and .pdb files. All that I needed to edit was part of AssemblyCatalog.cs in the MEF Preview 6 source. I changed the LoadAssembly method from this:

 

 

private static Assembly LoadAssembly(string codeBase)

        {

            Requires.NotNullOrEmpty(codeBase, "codeBase");

 

            AssemblyName assemblyName;

 

            try

            {

                assemblyName = AssemblyName.GetAssemblyName(codeBase);

            }

            catch (ArgumentException)

            {

                assemblyName = new AssemblyName();

                assemblyName.CodeBase = codeBase;

            }

 

            return Assembly.Load(assemblyName);           

        }

 

to this:

 

 

private static Assembly LoadAssembly(string codeBase)

        {

            byte[] assemStream = null, pdbStream = null;

            using (FileStream fs = new FileStream(codeBase, FileMode.Open))

            {

                assemStream = new byte[fs.Length];

                fs.Read(assemStream, 0, (int)fs.Length);

            }

            string pdbPath = Path.Combine(Path.GetDirectoryName(codeBase), Path.GetFileNameWithoutExtension(codeBase) + ".pdb");

            bool hasPdb = false;

            if (File.Exists(pdbPath))

            {

                using (FileStream fs = new FileStream(pdbPath, FileMode.Open))

                {

                    pdbStream = new byte[fs.Length];

                    fs.Read(pdbStream, 0, (int)fs.Length);

                    hasPdb = true;

                }

            }

            else

            {

                Debug.WriteLine("Cannot find the pdb file: " + pdbPath);

            }

            if (!hasPdb)

            {

                return Assembly.Load(assemStream);

            }

            else

            {

                return Assembly.Load(assemStream, pdbStream);

            }

        }

Now we have the ability to load files without locking them, and also have the discoverability benefits of using MEF.

            Here is part of the code from the application that allows it to load AddIns with MEF:

 

    Public Sub Compose()

        Dim Dir As [String] = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments), "ContactManager\AppAddins")

        Dim Dir2 As [String] = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments), "ContactManager\MacroAddIns")

        If Directory.Exists(Dir) And Directory.Exists(Dir2) Then

            Dim Catalog As New AggregateCatalog(New DirectoryCatalog(Dir), New DirectoryCatalog(Dir2))

            Dim Container As New CompositionContainer(Catalog)

            Container.SatisfyImportsOnce(Me)

            macroBool = True

            appLevelBool = True

        ElseIf Directory.Exists(Dir) Then

            Dim Catalog As New DirectoryCatalog(Dir)

            Dim Container As New CompositionContainer(Catalog)

            Container.SatisfyImportsOnce(Me)

            macroBool = False

            appLevelBool = True

        ElseIf Directory.Exists(Dir2) Then

            Dim Catalog As New DirectoryCatalog(Dir2)

            Dim Container As New CompositionContainer(Catalog)

            Container.SatisfyImportsOnce(Me)

            macroBool = True

            appLevelBool = False

        End If

    End Sub

 

    <Import(("MacroAddIn"), GetType(IAddIn))> Public myMacroAddIn As IAddIn

 

    Private _AppAddIns As IEnumerable(Of IAddIn)

    <ImportMany(("AppAddIn"), GetType(IAddIn))> _

    Private Property AppAddIns() As IEnumerable(Of IAddIn)

        Get

            Return _AppAddIns

        End Get

        Set(ByVal value As IEnumerable(Of IAddIn))

            _AppAddIns = value

        End Set

    End Property

 

    Private Sub LoadAppLevelAddIns()

        Try

            Compose()

            If appLevelBool = True Then

                For Each appaddin As IAddIn In AppAddIns

                    StartAddIn(appaddin)

                    Me.appLevelAddIns.Add(appaddin)

                Next

            End If

        Catch ex As Exception

            System.Diagnostics.Trace.WriteLine(ex.ToString())

        End Try

    End Sub

 

Private Function StartAddIn(ByVal myAddIn As IAddIn) As IAddIn

        Try

            myAddIn.Startup(Me.application)

            Return myAddIn

        Catch ex As Exception

            System.Diagnostics.Trace.WriteLine(ex.ToString())

            Return Nothing

        End Try

    End Function

 

    Private Function CurrentDomain_AssemblyResolve(ByVal sender As Object, ByVal args As ResolveEventArgs) As Assembly

        Return Nothing

    End Function

 

    Friend Sub LoadMacrosInProcess()

        LoadMacros()

 

        Me.m_macroAddInLoadedInProc = True

    End Sub

 

    Private Sub LoadMacros()

        Try

            Compose()

            If macroBool = True Then

                StartAddIn(myMacroAddIn)

                m_macroAddIn = myMacroAddIn

            End If

        Catch ex As Exception

            System.Diagnostics.Trace.WriteLine(ex.ToString())

        End Try

        If Me.application.MacroManager IsNot Nothing Then

            Me.application.MacroManager.CloseThreadSafe()

        End If

    End Sub

 

This allows the user to load multiple AppAddIns to be run at startup, and one Macro AddIn to be debugged and run, etc. (Of course, you can always make your Macro AddIn extensible as well, which will still work even if you load the AddIns themselves with MEF.)

            I hope you have found this blog helpful in your own integration journey.

Please feel free to email me with any questions.

 


Posted Aug 13 2009, 12:53 PM by BillL
Filed under: , ,

Comments

Jamie Lynn wrote re: Chapter Six: Loading VSTA Addins with MEF
on 01-29-2010 1:18 PM

Very helpful. Thanks for the in-depth help!

- Jamie (http://www.sprainedankle.me/)

Copyright Summit Software Company, 2008. All rights reserved.