none
AppDomain.BaseDirectory being ignored when loading assemblies ? RRS feed

  • Question

  • I have a problem getting my newly created AppDomains to load the assemblies from the directory specified as the BaseDirectory.

    My setup looks like this:
    1) An .exe assembly has a form that contains a component that I made. The assembly is run from the directory "C:\MyExeFile".
    2) A .dll component assembly that provides a component that can be inserted into a form. This assembly is found in the same directory as the exe file ("C:\MyExeFile").
    3) A .dll plugin assemlby that contains plugin functionality that I need to load into the component assemlby. This assemlby is found in the directory "C:\MyPlugin".

    The .exe assembly has a simple form that contains the component .dll assemlby. This component assembly creates a new AppDomain in order to load the plugin assembly. This is where it goes wrong. The AppDomain is created by using an AppDomainSetup that specifies the plugin assembly's path as it's ApplicationBase ("C:\MyPlugin"):

    AppDomainSetup appDomainSetup  = new AppDomainSetup();
    
    appDomainSetup.DisallowCodeDownload = true;
    
    appDomainSetup.ApplicationBase  = @"C:\MyPlugin";
    
    AppDomain appDomain   = AppDomain.CreateDomain(name, null, appDomainSetup);

    When the AppDomain tries to load a plugin, that exists in the plugin directory ("C:\MyPlugin"), a "System.IO.FileNotFoundException: Could not load file or assembly" exception is thrown. I can see from the FusionLog that the assembly is attempted loaded from the directory that contains the .exe file ("C:\MyExeFile") instead of the ApplicationBase that was specified when creating the AppDomain ("C:\MyPlugin").

    I tried setting all of the other directory properties on the AppDomainSetup object, but it hasn't changed anything.

    Can anyone here tell me what I need to do, to get my AppDomain to load the assemblies from the plugin directory instead of trying to load them from the directory containing the executable ?

    • Edited by Mr. Andersen Sunday, August 23, 2009 4:07 PM Typing errors
    Sunday, August 23, 2009 11:30 AM

Answers

  • I don't see an obvious failure mode in your snippet.  What is missing is the code that actually causes the assembly to be loaded, the appDomain.CreateInstance() call.  A classic failure mode there is that it wants to load that assembly in the primary domain as well.  Which will indeed fail since c:\MyPlugin is not in its probing path.

    You'll need to carefully avoid referencing any types from the plugin in your main code.  To do anything useful with the plugin, that requires an interface that the plugin type implements.  That interface type must be declared in an assembly that's stored in the probing path for both appdomains.  Given the directory names you are using, that will require implementing the AppDomain.AssemblyResolve event or storing the assembly in the GAC.

    Hans Passant.
    • Marked as answer by Mr. Andersen Tuesday, August 25, 2009 3:35 PM
    Sunday, August 23, 2009 1:20 PM
    Moderator
  • Hi Andersen,

    The method AppDomain.Load will load the assembly in both of domains. It is because Assembly does not derive from MarshalByRefObject, and therefore the return value of the Load method cannot be marshaled. Instead, the common language runtime tries to load the assembly into the calling application domain. The assemblies that are loaded into the two application domains might be different if the path settings for the two application domains are different. That causes the FileNotFound exception occured.
    Please refer to: http://msdn.microsoft.com/en-us/library/36az8x58.aspx

    In order to avoid the error, I agree with nobugz to use a shared assembly to keep the interface of the plugins. 

    Hongye Sun [MSFT]

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by Mr. Andersen Tuesday, August 25, 2009 3:35 PM
    Monday, August 24, 2009 12:28 PM

All replies

  • I don't see an obvious failure mode in your snippet.  What is missing is the code that actually causes the assembly to be loaded, the appDomain.CreateInstance() call.  A classic failure mode there is that it wants to load that assembly in the primary domain as well.  Which will indeed fail since c:\MyPlugin is not in its probing path.

    You'll need to carefully avoid referencing any types from the plugin in your main code.  To do anything useful with the plugin, that requires an interface that the plugin type implements.  That interface type must be declared in an assembly that's stored in the probing path for both appdomains.  Given the directory names you are using, that will require implementing the AppDomain.AssemblyResolve event or storing the assembly in the GAC.

    Hans Passant.
    • Marked as answer by Mr. Andersen Tuesday, August 25, 2009 3:35 PM
    Sunday, August 23, 2009 1:20 PM
    Moderator

  • The assembly is being loaded using the following call:

    appDomain.Load("MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
    I tried using AssemblyResolve, but this event doesn't get fired, when the Load call is executed.
    Sunday, August 23, 2009 4:06 PM
  • What's next?  You'll have to create a type as well.

    Hans Passant.
    Sunday, August 23, 2009 4:23 PM
    Moderator
  • I don't get any further, the call to appDomain.Load is the call that fails with the FileNotFoundException.
    Sunday, August 23, 2009 8:04 PM
  • You're not answering my question.  To get further help with this, you'll need to create a solution that reproduces this problem and post it to a file sharing service, like skydrive.live.com

    Hans Passant.
    Sunday, August 23, 2009 8:49 PM
    Moderator
  • Hi Andersen,

    The method AppDomain.Load will load the assembly in both of domains. It is because Assembly does not derive from MarshalByRefObject, and therefore the return value of the Load method cannot be marshaled. Instead, the common language runtime tries to load the assembly into the calling application domain. The assemblies that are loaded into the two application domains might be different if the path settings for the two application domains are different. That causes the FileNotFound exception occured.
    Please refer to: http://msdn.microsoft.com/en-us/library/36az8x58.aspx

    In order to avoid the error, I agree with nobugz to use a shared assembly to keep the interface of the plugins. 

    Hongye Sun [MSFT]

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by Mr. Andersen Tuesday, August 25, 2009 3:35 PM
    Monday, August 24, 2009 12:28 PM
  • Thank you Hongye Sun for the clarification. I read nobugz post, but misunderstood it. Your post made it clear to me what nobugz was saying.

    The problem was indeed that AppDomain.Load() tried to load the assembly in both application domains. The problem occurred when trying to load the plugin in the .exe assembly's application domain. The plugin did (intentionally) not exist in the BaseDirectory of the .exe assembly and because of this the exception occurred.

    I have instead created yet another assembly, which contains the interface definition of the plugin and added this assembly to both the .exe assembly's executing directory and the plugin assemlby's executing directory. This assembly has then been instantiated with appDomain.CreateInstanceAndUnwrap() in order to load it into the plugin's application domain.
    Tuesday, August 25, 2009 3:35 PM