简体   繁体   中英

Embedding C# class libraries into an assembly and loading them dynamically

Good afternoon,

I've succesfully embedded a C# class dll file into my app, I can print out it being there via:

Console.WriteLine(Assembly.GetExecutingAssembly()
    .GetManifestResourceInfo("TestReferenceLib.dll").ResourceLocation);

But, even if I load it as so:

public static void Main(string[] args)
{
    Console.WriteLine(Assembly.GetExecutingAssembly().GetManifestResourceInfo("TestReferenceLib.dll").ResourceLocation);

    //This doesn't even fire
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler((o, e) =>
    {
        Console.WriteLine("Resolving?");

        var memory = new MemoryStream();
        Assembly.GetExecutingAssembly().GetManifestResourceStream(e.Name + ".dll").CopyTo(memory);


        if(memory.Length > 0)
            return Assembly.Load(memory.ToArray());

        return null;
    });

    Console.WriteLine("Test assembly compiled and ran succesfully!");

    //Pops errors
    TestReferenceLib.TestReferenceLibrary.PrintOut();
}

The line marked with comment 'Pops errors' says:

FileNotFoundException: Could not load file or assembly 'TestReferenceLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

I know that I could walk around it by taking the assembly returned by Assembly.Load(); and run GetType(), GetMethod() and Invoke() to make it work - or use tools like ILMerge or similar to make it work - but without going too much in depth, it's crucial for me not to use any external tools.

The reference via 'using TestReferenceLib' HAS to work.

Thank you in advance for any tips.

Update

With this little piece of code I manage to get it listed in loaded assemblies, but executing still doesn't work:

var memory = new MemoryStream();

Assembly.GetExecutingAssembly().GetManifestResourceStream("TestReferenceLib.dll").CopyTo(memory);

var assembly = AppDomain.CurrentDomain.Load(memory.ToArray());

foreach (var a in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
{
    Console.WriteLine(a.FullName);
}

foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
    Console.WriteLine(a.FullName);
}

How are you pushing that call to TestReferenceLib.TestReferenceLibrary through the compiler? Is there an interface somewhere you're not showing?

That aside, The MSDN documentation on Resolving Assembly Loads says that AssemblyResolve is only called when you try to load an assembly by name.

You're not doing that anywhere, so it sounds like you need an AppDomain.Load() , AppDomain.CreateInstance() or similar before your call to TestReferenceLib

I found a satisfying solution, so here it is if anybody else hits the wall.

If you are compiling code dynamically - code that requires referenced class libraries which in turn are supposed to be embedded as resources into your assembly, make sure to disable GenerateInMemory and to NOT execute the file immediately via Invoking.

If you want (like me) to compile an assembly then execute it immediately, generate it to a file, create an AppDomain and execute it inside of it as seen here:

var assembly = Compiler.Create(files, references, resources, "TestAssembly.TestEntryPoint");

var domain = AppDomain.CreateDomain("ScriptDOM");

domain.ExecuteAssembly(assembly.Location);

Here's the compiler params:

var parameters = new CompilerParameters();
parameters.ReferencedAssemblies.AddRange(references);
parameters.EmbeddedResources.AddRange(resources);
parameters.GenerateExecutable = true;
parameters.GenerateInMemory = false;
parameters.IncludeDebugInformation = true;
parameters.CompilerOptions = "/optimize";
parameters.MainClass = entryPoint;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM