AppDomain DoCallBack 文件未找到 异常

3
我将一个函数加载到新创建的AppDomain中,所有工作都正常,但如果我关闭程序,然后将其重命名并再次启动它,那么我将遇到FileNotFound异常。我不明白,这可能是怎么回事? 错误(appDomainProject原始程序名称)
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.

源代码

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");
        }
    }
}

你的意思是“关闭程序并重命名它”,你是指重命名exe文件,没有重新构建,也没有在GAC或NGen中注册等操作,对吗? - Cee McSharpface
可以复现。在源代码的第21行DoCallBack出现错误。有趣的是,一个看起来和这个问题重复的Q被从SO中删除了:http://stackoverflow.com/questions/30512932/execute-code-in-appdomain-in-renamed-executables - Cee McSharpface
谢谢,我会检查的。我需要重命名它,但没有其他选择。 - SLI
我尝试使用新的文件名与Assembly.Load一起工作,但没有帮助。 - SLI
不,我的意思是如果你构建了A.exe,然后将其重命名为B.exe并在运行时检测到-请从A.exe创建符号链接到B.exe(或仅将B.exe复制到A.exe)。 - Evk
显示剩余5条评论
2个回答

2

我猜测exe文件有一些硬编码的名称。事实上,如果您检查typeof(Program).AssemblyName,它就是原始名称,这就是为什么我们会遇到错误的原因。

但是,您可以手动将dll加载到AppDomain中,然后在其中有一个类拦截对“原始”名称的请求,并将其替换为正确的程序集。

下面的代码可以实现这一点:

/// <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.
/// 
///  https://dev59.com/4Jfga4cB1Zd3GeqPBM2u
/// </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;
  }
}

在创建应用程序域后,您只需要调用帮助方法即可:

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

0

抱歉,这是按设计要求的...

作为一种解决方法,您可以将该方法移动到外部dll中,以便您可以重命名.exe文件。

如果您希望您的应用程序只有一个文件,您可以使用ILmerge


“by design” 有来源吗? - zastrowm
@MackieChan 没有文档,但是如果你查看 .net 代码并思考 Docallback 的作用,它需要存储 exe 的名称以进行跨应用程序调用。 - giammin
我认为发生的情况是它正在存储程序集名称,这是原始exe名称(可能是因为.NET exes的存储方式)。因此,当它尝试加载程序集时,无法正确解析它。我不知道是否应该说这是设计上的问题,而更应该说这是.NET exes工作方式的副作用(如果我的理论是正确的话)。 - zastrowm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接