1

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);
}
JTinkers
  • 1,631
  • 9
  • 18
  • As the error says is TestReferenceLibrary referencing other libraries which you need to embed as well? Or does it depend on unmanaged dlls? – Alois Kraus Dec 23 '18 at 22:26
  • 1
    It's a `FileNotFoundException` not Assembly resolution issue. As you already know, `AssemblyResolve ` `"Occurs when the resolution of an assembly fails."`. The event will be fire if you add the following line after the subscription `AppDomain.CurrentDomain.CreateInstance("UnknownAssembly, Version, Culture, PublicKeyToken", "WrongType");`. – adPartage Dec 23 '18 at 22:35
  • @AloisKraus It references System.dll only. – JTinkers Dec 23 '18 at 22:52
  • @adPartage That's not it sadly, the file is found succesfully. I'll post an update now. – JTinkers Dec 23 '18 at 22:52
  • What is `TestReferenceLib.TestReferenceLibrary`? It doesn't exist in your code. Oh it's the one loaded dynamically? Well it cannot work like that. See [this](https://stackoverflow.com/a/9180843/4228458) answer and it will guide you. – CodingYoshi Dec 23 '18 at 23:35
  • I found out that it's actually possible when generating an executable (and I've succesfully done so), the issue happens when there assembly is stored in the memory. – JTinkers Dec 24 '18 at 00:10
  • It is good idea to try to read code aloud... That's how you could try in this case: "I have a method that uses type from assembly that can't be loaded automatically. After method is JITed (obviously it fails to load assembly and hence fails to JIT) it would register to assembly resolution event and load that assembly needed for JIT to compile the method. I don't understand why if method fails to compile it does not run (did I just said that????)" – Alexei Levenkov Dec 24 '18 at 00:26
  • @AlexeiLevenkov I've already found a solution, I'm writting answer right now. – JTinkers Dec 24 '18 at 00:33

2 Answers2

1

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;
JTinkers
  • 1,631
  • 9
  • 18
  • 2
    Note that this does not really answer why code in question did not work... but rather some other question about compilation on the fly. Obviously no one could answer question that you did not ask. In any case would be nice if you address actual question too since you are trying to provide self-answer... – Alexei Levenkov Dec 24 '18 at 00:59
  • @AlexeiLevenkov I thought that it was obvious, afterall I've mentioned loading types and invoking methods from the said assembly. – JTinkers Dec 24 '18 at 01:05
0

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

MarcE
  • 3,586
  • 1
  • 23
  • 27
  • You are correct, I've managed to load it directly through the name as seen in the updated thread post. The issue I'm having now is, I can't execute it even though the assembly is loaded. – JTinkers Dec 23 '18 at 22:54