Click here to Skip to main content
11,717,570 members (81,154 online)
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C#
I'm trying to create a small plugin framework, and have been going around and around the AppDomain code with not much luck.

The most important thing for me is that I want to have no conflicts between different versions of the same class (for example a plugin may want to use its own Common.dll v 1.0 library whereas I may have 2.0), similar to other issues.

So I wrote the small app below.

static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
 
            Console.WriteLine(Utility.GetVersion());
 
            var ads = new AppDomainSetup();
            string appBase = @"../../ExtensionLib/ExtensionLib/bin/Debug";
            ads.ApplicationBase = appBase;
            //ads.PrivateBinPath = appBase;
            //ads.ConfigurationFile = "ExtensionLib.dll.config";
            //ads.PrivateBinPathProbe = ".";
            var ad = AppDomain.CreateDomain("Extension App", null, ads);
//          var ah = new AssemblyHelper();
//            ad.AssemblyResolve += ah.MyResolveEventHandler;
//            ad.AssemblyLoad += ah.AssemblyLoadedHandler;

            //MessageBox.Show(ad.BaseDirectory);
            Assembly a = ad.Load("ExtensionLib");
            ILib l = (ILib) a.CreateInstance("LibImpl");
            l.DoSomething();
 
            Console.WriteLine(Utility.GetVersion());
        }
    }

Common.dll contains the Utility class, but we have different versions of that class. Both the plugin and the host application use the same ILib (defined on the same dll).

Host Application Setup
c:\....\123
   Common.dll (v 2.0)
   IntegInterface.dll (where ILib is defined, v1.0)
   LoadCommon.exe (the host)

Plugin Application Setup
c:\....\456
   Common.dll (v 1.0)
   IntegInterface.dll (where ILib is defined, v1.0)
   ExtensionLib.dll (the plugin)

When the app runs, I get an exception at the moment of ad.Load()

System.IO.FileNotFoundException was unhandled
  Message="Could not load file or assembly 'ExtensionLib' or one of its dependencies. The system cannot find the file specified."
  Source="mscorlib"
  FileName="ExtensionLib"
  FusionLog="Assembly manager loaded from:  C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\mscorwks.dll
  Running under executable  C:\\Users\\Roberto\\Documents\\Visual Studio 2008\\Projects\\LoadCommon\\LoadCommon\\bin\\Debug\\LoadCommon.vshost.exe
  --- A detailed error log follows. \n
  === Pre-bind state information ===
  LOG: User = Roberto-PC\\Roberto
  LOG: DisplayName = ExtensionLib\n (Partial)
  LOG: Appbase = file:///C:/Users/Roberto/Documents/Visual Studio 2008/Projects/LoadCommon/LoadCommon/ExtensionLib/ExtensionLib/bin/Debug
  LOG: Initial PrivatePath = NULL
  Calling assembly : (Unknown).
  ===
  LOG: This bind starts in default load context.
  LOG: Using application configuration file: C:\\Users\\Roberto\\Documents\\Visual Studio 2008\\Projects\\LoadCommon\\LoadCommon\\ExtensionLib\\ExtensionLib\\bin\\Debug\\ExtensionLib.dll.config
  LOG: Using machine configuration file from C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\config\\machine.config.
  LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
  "
  StackTrace:
       at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
       at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
       at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
       at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
       at System.AppDomain.Load(String assemblyString)
       at System.AppDomain.Load(String assemblyString)
       at LoadCommon.Program.Main() in C:\Users\Roberto\Documents\Visual Studio 2008\Projects\LoadCommon\LoadCommon\Program.cs:line 36
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

I also tried to setup an the assemblyresolve delegate to see if I could help, no luck however, I ended up with Serialization errors even though I did tag AssemblyHelper as serializable.

At some point I will also have the IntegInterface have different versions, and am sure I will end up with a whole slew of issues.

Can someone shed some light here? Very much appreciated.

Thanks
Posted 13-Apr-11 11:42am
rld19714.3K
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 2

This is the usual confusion about loading assembly. Do you want to load Assembly by the file name of its main executable module?

Use System.Reflection.Assembly.LoadFrom(string fileName). See all other System.Reflection.Assembly.Load... methods and help pages on their differences. This call should be done in the newly created Application Domain context, not in your default (initial) Application Domain of your process. If my assumption about your plug-in assembly was wrong, please free to correct me, I'll give you a bit different approach.

System.AppDomain ad = AppDomain.CreateDomain("Extension App", null, ads);
ad.DoCallBack(() => {
   //...
   System.Reflection.Assembly.LoadFrom(myPluginFileName);
   //...
});

If your plug-in Assembly has an entry point (EXE file) you can use System.AppDomain.ExecuteAssembly. I doubt you design you plug-ins this way (you mentioned ILib interface; if this is the interface supported by your plug-in — congratulations! This is the best approach. See be reference below on using a special assembly attribute to select a class implementing plug-in interface &mdas; very good for robustness and performance).
So, even though I don't think your design is not bases on EXE entry point, let me show this just in case:

System.AppDomain ad = AppDomain.CreateDomain("Extension App", null, ads);
ad.ExecuteAssembly(myExeFileName);


This is great that you understand importance of loading plug-ins dynamically in the separate Application Domain, but this is only needed if you want to unload them later. In you need to load all plug-ins only once, work in one domain, your default one, do not create an extra Application Domain.

You can real all my Answers about plug-in Assemblies and using Application Domain, a variety of designs:
Create WPF Application that uses Reloadable Plugins...[^]
See also:
code generating using CodeDom[^].
Important example of use with CodeDOM compiler for use in .NET calculator:
Create WPF Application that uses Reloadable Plugins...[^].

—SA
  Permalink  
v3
Comments
Manfred R. Bihy at 13-Apr-11 18:17pm
   
Nice! My 5+
Cheers!
SAKryukov at 13-Apr-11 18:18pm
   
Thank you, Manfred. Just updated with important detail.
--SA
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

Try to load the assembly with the version, culture and PublicKeyToken

Assembly.Load
            ("SampleAssembly, Version=2.0.0000.0, Culture=neutral, PublicKeyToken=12312320f8da049e3");
  Permalink  
Comments
SAKryukov at 13-Apr-11 18:02pm
   
That would work, but I think not in the OP's case. If this is a plug-in Assembly, LoadFrom should be used.
Please see my Answer.
--SA
luisnike19 at 13-Apr-11 18:29pm
   
Thanks for your comment SA.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 3

All I needed to do was make a call, so although SAK's solution makes sense (thanks!), I decided to just use
ILib l = (ILib)ad.CreateInstanceAndUnwrap("ExtensionLib", "ExtensionLib.LibImpl");
which does the trick just fine. Furthermore, I can change the interface on the different domains and they are still somehow compatible. The transparent proxy does some funky dancing i'm sure.

Thanks all!
  Permalink  

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
0 Sergey Alexandrovich Kryukov 719
1 OriginalGriff 215
2 F-ES Sitecore 210
3 Maciej Los 140
4 Jochen Arndt 120
0 Sergey Alexandrovich Kryukov 1,494
1 OriginalGriff 535
2 Maciej Los 529
3 Richard MacCutchan 440
4 CHill60 415


Advertise | Privacy | Mobile
Web03 | 2.8.150901.1 | Last Updated 14 Apr 2011
Copyright © CodeProject, 1999-2015
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100