另一个域中的CreateInstanceAndUnwrap?

时间:2014-07-15 14:13:26

标签: c# reflection appdomain

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

我的过程就是这样:

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

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

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

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

实际对象如下:

public class CrossDomainApplication : MarshalByRefObject, ICrossDomainApplication

界面如下:

public interface ICrossDomainApplication
{
    void Run(CrossDomainApplicationParameters parameters);
}

参数如下:

[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; }
}

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

有什么可能出错的想法?​​

编辑:这是我运行时遇到的错误:

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

            //Exception before reaching here
            runtime.Run(parameters);

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

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

2 个答案:

答案 0 :(得分:15)

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

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

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

PrivateBinPath是真正的技巧,它使其他一切(最终)开始工作。 PrivateBinPath(阅读文档)ABSOLUTELY需要是一个相对路径。如果你有一个名为“assemblies”的子文件夹,那么PrivateBinPath应该是“程序集”。如果在它前面加上\或绝对路径,它就不起作用。

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

            AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;

,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;
    }

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

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

            AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;

修复了一切。感谢所有帮助过的人!

答案 1 :(得分:0)

这不是一个答案,只是示例代码共享

嗯,这可能是别的吗?这个样本,与你描述的不完全相同,是有效的。

#region

using System;
using System.Reflection;
using System.Threading;

#endregion

internal class Program
{
    #region Methods

    private static void Main(string[] args)
    {
        // Get and display the friendly name of the default AppDomain. 
        string callingDomainName = Thread.GetDomain().FriendlyName;
        Console.WriteLine(callingDomainName);

        // Get and display the full name of the EXE assembly. 
        string exeAssembly = Assembly.GetEntryAssembly().FullName;
        Console.WriteLine(exeAssembly);

        // Construct and initialize settings for a second AppDomain.
        var ads = new AppDomainSetup
                  {
                      ApplicationBase = Environment.CurrentDirectory,
                      DisallowBindingRedirects = false,
                      DisallowCodeDownload = true,
                      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
                  };

        // Create the second AppDomain.
        AppDomain ad2 = AppDomain.CreateDomain("AD #2", null, ads);

        // Create an instance of MarshalbyRefType in the second AppDomain.  
        // A proxy to the object is returned.
        var mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, typeof(MarshalByRefType).FullName);

        // Call a method on the object via the proxy, passing the  
        // default AppDomain's friendly name in as a parameter.
        mbrt.SomeMethod(new MarshalByRefParameter{ModuleName =  callingDomainName});

        // Unload the second AppDomain. This deletes its object and  
        // invalidates the proxy object.
        AppDomain.Unload(ad2);
        try
        {
            // Call the method again. Note that this time it fails  
            // because the second AppDomain was unloaded.
            mbrt.SomeMethod(new MarshalByRefParameter { ModuleName = callingDomainName });
            Console.WriteLine("Successful call.");
        }
        catch (AppDomainUnloadedException)
        {
            Console.WriteLine("Failed call; this is expected.");
        }
    }


    #endregion
}

public interface IMarshalByRefTypeInterface
{
    void SomeMethod(MarshalByRefParameter parameter);
}

public class MarshalByRefType : MarshalByRefObject, IMarshalByRefTypeInterface
{
    //  Call this method via a proxy. 

    #region Public Methods and Operators

    public void SomeMethod(MarshalByRefParameter parameter)
    {
        // Get this AppDomain's settings and display some of them.
        AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation;
        Console.WriteLine("AppName={0}, AppBase={1}, ConfigFile={2}", ads.ApplicationName, ads.ApplicationBase, ads.ConfigurationFile);

        // Display the name of the calling AppDomain and the name  
        // of the second domain. 
        // NOTE: The application's thread has transitioned between  
        // AppDomains.
        Console.WriteLine("Calling from '{0}' to '{1}'.", parameter.ModuleName, Thread.GetDomain().FriendlyName);
    }

    #endregion
}

[Serializable]
public class MarshalByRefParameter : MarshalByRefObject
{
    public string ModuleName { get; set; }
}

然而,我只是抓住入口集合,而你有一个动态编译。什么错误,你到底得到了什么?