Search code examples
c#dllreflection.net-assemblyautocad-plugin

Loading assembly at runtime, Autocad plugin example


After hours spent on this subject (and also dozens of pages visited) i am forced to ask for a help. I have seen a lot of posts on this subject, but i couldn't fix problem i get.

Basically, i want to do very simple thing: load assembly from my folder to application.


Here is question in short (all other details are explained in rest of text) I want to use Assembly.LoadFrom method to load my assembly (same assembly file is referenced with project, but with CopyLocal: false), but although assembly is loaded when method using it is invoked, program tries to load assembly from default locations. If it is found, then 2 same assemblies are loaded, and program uses last one, if it is not found then FileNotFoundException is raised. But, when i use same idea in Autocad plugin, everything works and exception is not raised, although files are not found.


I have test solution with 2 simple projects: TestExe (console application) and TestDll(dll library). I want to use types from TestDll in TestExe so i've added reference to TestDll(i am not referencing TestDll project, but file on specified location), but i want to load TestDll manually in the runtime. Why this is needed is explained at the end of text, with Autocad example.

As far as i understand, Assembly.LoadFrom method can be used for this purpose. So basic idea is: Load TestDll before method in TestExe is using it, so when method is called we have already loaded assembly. This way, whether referenced dll exists in default directories or not, we already have assembly loaded and it is going to be used.

From MSDN:

If an assembly with the same identity is already loaded, LoadFrom returns the loaded assembly even if a different path was specified.

So i understand that if i load dll once, every next load of same assembly (also from other location) will know that it is already added, so first one will be used.


Project: TestExe

//File: Program.cs
using System;
namespace TestExe
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(new ClassExe().ExeMethod());
        }
    }    
}
//File: ClassExe.cs
namespace TestExe
{
    class ClassExe
    {
        public string ExeMethod()
        {
            return new TestDll.ClassDll().DllMethod();
        }
    }
}

Project TestDll

using System.Reflection;

namespace TestDll
{
    public class ClassDll
    {
        public string DllMethod()
        {
            return Assembly.GetExecutingAssembly().Location;
        }
    }
}

As you can see, task is simple: display location of called assembly.

Lets say TestDll.dll is copied to application folder Extern\TestDll.dll.

If i set property CopyLocal: false of reference to TestDll in TestExe project, program fails with FileNotFoundException. (1) Is it because assembly is searched only in default directories (application dir and TestDll\TestDll.dll)?

Then i tried to load assembly manually, before using it:

static void Main(string[] args)
    {
        Assembly.LoadFrom(@"Extern\TestDll.dll");
        Console.WriteLine(new ClassExe().ExeMethod());
    }

But i get same FileNotFoundException, although TestDll has been loaded enter image description here

When i set attribute CopyLocal: true, then program works. But, it loads TestDll.dll again, from default location, and ignores assembly which is already loaded. enter image description here

The only way i managed program to work how i wanted (to be used assembly Extern\TestDll.dll) is with using AppDomain.AssemblyResolve event.


I have Autocad plugin which uses 10 different dlls, from different solutions. So i have plugin.dll which references 10 dll files across Documents folder, and set CopyLocal: true. But, when deployed to customer, i keep only plugin.dll in application folder, and all other dlls are in Libraries subdirectory. In static constructor of plugin class, i put Assembly.LoadFrom to be loaded from Libraries subdirectory, and everything works fine.

Sorry for long post but i wanted to explain it with details.

And thanks for any feedback :)


Solution

  • You can try by checking the load context of an assembly. Here is an example and here is an article.

    The basics are that AutoCad loads its assemblies and plugin assemblies in the same AppDomain where the process of acad.exe is running therefore they are visible to all other assemblies that are loaded because they are references of a plug-in dll. But when you load a dll in acad process it will not be visible for the .exe and vice versa - they both run in different app domains and do not see each other.