Search code examples
c#wpfmeffody

MEF - Get assembly from embedded DLL


I am using MEF to create "plugins" for my WPF application. Some of these plugins I want to embed directly into the EXE file as the EXE needs to be standalone. I am using Costura by Fody to embed the resource along with all my other references. As the exe file needs to be standalone I am unable to create a directory for these plugins and use the DirectoyCatalog

Is there anyway I can either load the assembly from the embedded resource, or simply specify the assembly name such as:

catalog.Catalogs.Add(new AssemblyCatalog("My.Assembly.Name));

I have tried looping through the Manifest resources but these appear to be zipped by Fody:

var resourceNames = GetType().Assembly.GetManifestResourceNames();
            foreach (var resourceName in resourceNames)

Any help/suggestions appreciated.


Solution

  • Ok so got this to work for me, using the class below (found this code at https://github.com/Sebazzz/EntityProfiler/blob/master/src/UI/EntityProfiler.Viewer/AppBootstrapper.cs and tweaked to suit my needs):

    To use it you simply call the extract function which will find any Costura Zip files in resource manifest and decompresses it and registers it.

    The function returns a dictionary of all assemblies that match the string passed in the function. I then iterate over them and add to catalog to be used by composition container:

    var assemblies = CosturaAssemblyExtractor.Extract(AppDomain.CurrentDomain, Assembly.GetExecutingAssembly(), "My.AssemblyName");
    foreach (var assembly in assemblies)
    {
        catalog.Catalogs.Add(new AssemblyCatalog(assembly.Value));
    }
    container = new CompositionContainer(catalog);
    

    Class:

    public static class CosturaAssemblyExtractor
    {
        public static Dictionary<string, Assembly> Extract(AppDomain OrigDomain, Assembly ExecutingAssembly, string AssemblyStartsWith)
        {
            //var currentDomain = origDomain;
            var assemblies = OrigDomain.GetAssemblies();
    
            var references = new Dictionary<string, Assembly>();
    
            var manifestResourceNames = ExecutingAssembly.GetManifestResourceNames().Where(x => {
                return x.ToUpper().StartsWith(("costura." + AssemblyStartsWith).ToUpper()) && x.ToUpper().EndsWith(".dll.zip".ToUpper());
            });
    
            foreach (var resourceName in manifestResourceNames)
            {
                var solved = false;
                foreach (var assembly in assemblies)
                {
                    var refName = string.Format("costura.{0}.dll.zip", GetDllName(assembly, true));
                    if (resourceName.Equals(refName, StringComparison.OrdinalIgnoreCase))
                    {
                        references[assembly.FullName] = assembly;
                        solved = true;
                        break;
                    }
                }
    
                if (solved)
                    continue;
    
                using (var resourceStream = ExecutingAssembly.GetManifestResourceStream(resourceName))
                {
                    if (resourceStream == null) continue;
    
                    if (resourceName.EndsWith(".dll.zip"))
                    {
                        using (var compressStream = new DeflateStream(resourceStream, CompressionMode.Decompress))
                        {
                            var memStream = new MemoryStream();
                            CopyTo(compressStream, memStream);
                            memStream.Position = 0;
    
                            var rawAssembly = new byte[memStream.Length];
                            memStream.Read(rawAssembly, 0, rawAssembly.Length);
                            var reference = Assembly.Load(rawAssembly);
                            references[reference.FullName] = reference;
                        }
                    }
                    else
                    {
                        var rawAssembly = new byte[resourceStream.Length];
                        resourceStream.Read(rawAssembly, 0, rawAssembly.Length);
                        var reference = Assembly.Load(rawAssembly);
                        references[reference.FullName] = reference;
                    }
                }
            }
            return references;
        }
    
        private static void CopyTo(Stream source, Stream destination)
        {
            var array = new byte[81920];
            int count;
            while ((count = source.Read(array, 0, array.Length)) != 0)
            {
                destination.Write(array, 0, count);
            }
        }
    
        private static string GetDllName(Assembly assembly, bool withoutExtension = false)
        {
            var assemblyPath = assembly.CodeBase;
            return withoutExtension ? Path.GetFileNameWithoutExtension(assemblyPath) : Path.GetFileName(assemblyPath);
        }
    }