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