3

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");
        }
    }
}
SLI
  • 713
  • 11
  • 29
  • just to be sure: "close the program and rename it", you mean you're renaming the exe file, no rebuild involved, nothing registered in GAC or NGen etc.? – Cee McSharpface Jun 09 '16 at 17:43
  • yeap, just rename it without rebuild – SLI Jun 09 '16 at 17:44
  • can reproduce. Fails in source line 21, `DoCallBack`. interestingly, a Q that looks like a duplicate of this one, was deleted from SO: http://stackoverflow.com/questions/30512932/execute-code-in-appdomain-in-renamed-executables – Cee McSharpface Jun 09 '16 at 17:54
  • It fails because it try to load him self, but he can't. "appDomainProject, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null" appDomainProject is original exe name, i tryed much things, but they all won't help me – SLI Jun 09 '16 at 17:56
  • did you look into [this](http://stackoverflow.com/questions/6893137/load-current-assembly-into-different-appdomain?rq=1), it looks promising. but looking at its complexity, I would not recommend to take that path unless you absolutely have to... are there no alternatives to renaming physical files in your requirement? – Cee McSharpface Jun 09 '16 at 18:09
  • thanks i will check it, i need to rename it and there are no alternatives – SLI Jun 09 '16 at 18:29
  • if you can do the renaming at build time, use this: http://stackoverflow.com/questions/37685180/appdomain-docallback-filenotfound-exception?noredirect=1#comment62938423_37685180 otherwise, this might help as well: http://stackoverflow.com/questions/658498/how-to-load-an-assembly-to-appdomain-with-all-references-recursively – Cee McSharpface Jun 09 '16 at 18:50
  • What about creating symlink with old filename pointing to new filename (if you detected that your exe has been renamed)? – Evk Jun 09 '16 at 20:24
  • I tryed to work with Assembly.Load with new file name. Won't help – SLI Jun 09 '16 at 20:34
  • No I mean if you built A.exe and then renamed to B.exe and detected that at runtime - create symlink from A.exe to B.exe (or just copy B.exe to A.exe). – Evk Jun 09 '16 at 20:38

2 Answers2

2

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);
zastrowm
  • 8,017
  • 3
  • 43
  • 63
0

Sorry, this is by design...

as a workaround you can move the method to an external dll, so you can rename the .exe.

If you want your application to be only one file you can use ILmerge

giammin
  • 18,620
  • 8
  • 71
  • 89
  • Is there a source for "by design"? – zastrowm Jun 13 '16 at 22:58
  • @MackieChan no documentation but if you look at the .net code and if you think about what is Docallback for. It needs to store the name of the exe to do cross application call – giammin Jun 14 '16 at 08:00
  • Well I think what is happening is that it's storing the assembly name, which is the original exe name (probably because of the way that .NET exes are stored). Thus when it goes to load the assembly, it cannot resolve it properly. I don't know if I'd say that it's by design, but rather that it's a side-effect of the way .NET exes work (if my theory is correct). – zastrowm Jun 16 '16 at 03:09