另一个域中的CreateInstanceAndUnwrap?

Loc*_*cke 8 c# reflection appdomain

由于某种原因,我现在遇到了CreateInstanceAndUnwrap的问题(之前正在工作).

我的过程是这样的:

我动态生成一些代码,它通过MEF从子目录加载DLL.然后,这些应用程序从这些DLL加载不同的部分(按需).我不得不更新我的代码,现在包含一个包含调用程序集路径的AppDomainSetup.

我正确地创建了新的AppDomain - 没有问题.当我尝试运行此代码时:

object runtime = domain.CreateInstanceAndUnwrap(
                typeof(CrossDomainApplication).Assembly.FullName,
                typeof(CrossDomainApplication).FullName);
Run Code Online (Sandbox Code Playgroud)

我遇到了大量问题 - 运行时(上面的变量)不再可以转换为CrossDomainApplication或ICrossDomainApplication.

实际对象如下:

public class CrossDomainApplication : MarshalByRefObject, ICrossDomainApplication
Run Code Online (Sandbox Code Playgroud)

接口看起来像:

public interface ICrossDomainApplication
{
    void Run(CrossDomainApplicationParameters parameters);
}
Run Code Online (Sandbox Code Playgroud)

参数看起来像:

[Serializable]
public class CrossDomainApplicationParameters : MarshalByRefObject
{
    public object FactoryType { get; set; }
    public Type ApplicationType { get; set; }
    public string ModuleName { get; set; }
    public object[] Parameters { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

本机类型的运行时似乎是MarshalByRefObject - 它不喜欢转换为其他任何东西.

什么可能出错?

编辑:这是我运行时出现的错误,如下所示:

            ICrossDomainApplication runtime = (ICrossDomainApplication)domain.CreateInstanceAndUnwrap(
                     typeof(CrossDomainApplication).Assembly.FullName,
                     typeof(CrossDomainApplication).FullName);

            //Exception before reaching here
            runtime.Run(parameters);
Run Code Online (Sandbox Code Playgroud)

System.InvalidCastException:无法转换透明代理以键入"Infrastructure.ICrossDomainApplication".

以下是我创建域时的域名:

        AppDomain domain = AppDomain.CreateDomain(
                   Guid.NewGuid().ToString(),
                   null,
                   new AppDomainSetup()
                   {
                       ApplicationBase = GetPath(),
                       ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
                       ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
                       LoaderOptimization = LoaderOptimization.MultiDomainHost
                   });               
Run Code Online (Sandbox Code Playgroud)

和GetPath()看起来像这样:

    private string GetPath()
    {
        Uri path = new Uri(Assembly.GetCallingAssembly().CodeBase);

        if (path.IsFile)
        {
            path = new Uri(path, Path.GetDirectoryName(path.AbsolutePath));
        }

        return path.LocalPath.Replace("%20", " ");
    }
Run Code Online (Sandbox Code Playgroud)

Loc*_*cke 16

为了帮助其他穷人,穷人出去,我终于在尝试了很多其他组合之后想出来了.

我不得不改变一些事情......第一个:

            AppDomain domain = AppDomain.CreateDomain(
                    Guid.NewGuid().ToString(),
                    AppDomain.CurrentDomain.Evidence,
                    new AppDomainSetup()
                    {
                        ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
                        ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
                        LoaderOptimization = LoaderOptimization.MultiDomainHost,
                        PrivateBinPath = GetPrivateBin(AppDomain.CurrentDomain.SetupInformation.ApplicationBase)
                    });
Run Code Online (Sandbox Code Playgroud)

PrivateBinPath是真正的技巧,它使其他所有东西(最终)开始工作.PrivateBinPath(阅读文档)ABSOLUTELY需要是一个相对路径.如果你有一个名为"assemblies"的子文件夹,那么PrivateBinPath应该是"程序集".如果在它前面加上\或绝对路径,它将无效.

通过这样做,它导致AssemblyResolve事件最终触发.我使用以下内容(在创建新的子AppDomain之前):

            AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
Run Code Online (Sandbox Code Playgroud)

并且ResolveAssembly方法如下所示:

    private Assembly ResolveAssembly(object sender, ResolveEventArgs args)
    {
        var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

        foreach (var assembly in loadedAssemblies)
        {
            if (assembly.FullName == args.Name)
            {
                return assembly;
            }
        }

        return null;
    }
Run Code Online (Sandbox Code Playgroud)

使用Linq可以更轻松地编写该方法,但我保持这种方式,尝试使自己有点明显的是为了调试目的而解析和加载的内容.

并且,始终确保断开事件(如果您一次加载AppDomain):

            AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;
Run Code Online (Sandbox Code Playgroud)

这解决了一切.感谢所有帮助过的人!

  • 什么是GetPrivateBin方法,它有什么作用? (3认同)