MEF - Get assembly from embedded DLL

2020-03-02 07:30发布

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.

标签: c# wpf mef fody
2条回答
\"骚年 ilove
2楼-- · 2020-03-02 08:18

I was able to get this to work in my project simply by loading the assemblies from the current AppDomain.

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    catalog.Catalogs.Add(new AssemblyCatalog(assembly));
}
查看更多
Melony?
3楼-- · 2020-03-02 08:21

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);
    }
}
查看更多
登录 后发表回答