Search code examples
c#appdomainfilenotfoundexception

AppDomain DoCallBack FileNotFound exception


I load a function to new created AppDomain and all work's fine, but if i close program and then rename it and start it again, than i will have FileNotFound exception. I just don't get it, how can that be?

Error (appDomainProject original program name)

System.IO.FileNotFoundException: Could not load file or assembly "appDomainProject, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null" or one of its dependencies. Can not find the file specified.

Source

using System;
using System.IO;
using System.Windows.Forms;

namespace appDomainProject
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            try
            {
                AppDomain authDomain = AppDomain.CreateDomain(DateTime.Now.ToString());
                authDomain.DoCallBack(load_Auth);
            }
            catch (Exception ex)
            {
                File.WriteAllText("e.txt", ex.ToString());;
                MessageBox.Show(ex.ToString());
            }
        }

        private static void load_Auth()
        {
            MessageBox.Show("AUTH");
        }
    }
}

Solution

  • I'm guessing that exes have some sort of hardcoded names about them. In fact, if you check typeof(Program).AssemblyName it's the original name which is why I expect we get the error.

    However, you can always manually load the dll into the AppDomain and then have some class in there that intercepts the request for the "orginal" name and replaces it with the correct assembly.

    This code does that:

    /// <summary>
    ///  It seems that if you rename an exe and then try to load said 
    ///  assembly into a separate app-domain, .NET looks for the original
    ///  name.  This class loads the current assembly into the app
    ///  domain manually and then detects request for the original name 
    ///  and redirects it to the correct assembly.
    /// 
    ///  http://stackoverflow.com/questions/37685180
    /// </summary>
    public class RenamedExeFixer : MarshalByRefObject
    {
      /// <summary> Load the assembly that this type is in into the app-domain. </summary>
      public static void LoadOriginatingAssemblyIntoDomain(AppDomain appDomain)
      {
        var pathToSelf = new Uri(typeof(RenamedExeFixer).Assembly.CodeBase).LocalPath;
        appDomain.Load(File.ReadAllBytes(pathToSelf));
    
        // create an instance of the class inside of the domain
        // so that it can hook into the AssemblyResolve event
        appDomain.CreateInstanceFrom(pathToSelf, typeof(RenamedExeFixer).FullName);
      }
    
      private readonly string _myAssemblyName;
    
      public RenamedExeFixer()
      {
        // cached for efficiency (probably not needed)
        _myAssemblyName = typeof(RenamedExeFixer).Assembly.FullName;
    
        AppDomain.CurrentDomain.AssemblyResolve += HandleAssemblyResolve;
      }
    
      private Assembly HandleAssemblyResolve(object sender, ResolveEventArgs args)
      {
        if (args.Name == _myAssemblyName)
        {
          return typeof(RenamedExeFixer).Assembly;
        }
    
        return null;
      }
    }
    

    And then all you need to do is call the helper method after creating your app-domain:

    AppDomain authDomain = AppDomain.CreateDomain(DateTime.Now.ToString());
    RenamedExeFixer.LoadOriginatingAssemblyIntoDomain(authDomain);
    authDomain.DoCallBack(load_Auth);