繁体   English   中英

动态加载的程序集未加载到新的AppDomain中

[英]Dynamically loaded Assembly not loading in new AppDomain

这不是重复的-我没有碰到运气就审查了这个与StackOverflow相关的问题: 如何将具有所有引用的程序集递归加载到AppDomain?

我有两个控制台应用程序。 AssemblyLoaderTest.exe和testapp.exe

  1. 我正在尝试使用AssemblyLoaderTest.exe动态加载testapp.exe并从testapp.exe中的类调用方法
  2. 到目前为止,代码可以正常工作-testapp.exe中的方法“ TestWrite()”已正确执行(并写入了outputuccess.txt), 但是testapp.exe已加载到同一AppDomain中 ,这被证明是因为“ CallMethodFromDllInNewAppDomain”始终返回假。 我正在尝试在新的AppDomain中加载testapp.exe。

我的问题:如何修改以下代码,以便将testapp.exe加载到新的AppDomain中,结果“ CallMethodFromDllInNewAppDomain”返回true? 谢谢!

下面的代码。 两者都可以简单地复制到VS中的新控制台应用程序中并执行/编译。

控制台应用程序1:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Policy;

namespace AssemblyLoaderTest
{
    class Program
    {
        static void Main(string[] args)
        {
            List<object> parameters = new List<object>();
            parameters.Add("Test from console app");
            bool loadedInNewAppDomain = DynamicAssemblyLoader.CallMethodFromDllInNewAppDomain(@"c:\temp\testapp.exe", "testapp.TestClass", "TestWrite", parameters);
        }
    }
    public static class DynamicAssemblyLoader
    {
        public static string ExeLoc = "";
        public static bool CallMethodFromDllInNewAppDomain(string exePath, string fullyQualifiedClassName, string methodName, List<object> parameters)
        {
            ExeLoc = exePath;
            List<Assembly> assembliesLoadedBefore = AppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>();
            int assemblyCountBefore = assembliesLoadedBefore.Count;
            AppDomainSetup domaininfo = new AppDomainSetup();
            Evidence adevidence = AppDomain.CurrentDomain.Evidence;
            AppDomain domain = AppDomain.CreateDomain("testDomain", adevidence, domaininfo);
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
            domain.CreateInstanceFromAndUnwrap(exePath, fullyQualifiedClassName);
            List<Assembly> assemblies = domain.GetAssemblies().ToList<Assembly>();
            string mainExeName = System.IO.Path.GetFileNameWithoutExtension(exePath);
            Assembly assembly = assemblies.FirstOrDefault(c => c.FullName.StartsWith(mainExeName));
            Type type2 = assembly.GetType(fullyQualifiedClassName);
            List<Type> parameterTypes = new List<Type>();
            foreach (var parameter in parameters)
            {
                parameterTypes.Add(parameter.GetType());
            }
            var methodInfo = type2.GetMethod(methodName, parameterTypes.ToArray());
            var testClass = Activator.CreateInstance(type2);
            object returnValue = methodInfo.Invoke(testClass, parameters.ToArray());
            List<Assembly> assembliesLoadedAfter = AppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>();
            int assemblyCountAfter = assembliesLoadedAfter.Count;
            if (assemblyCountAfter > assemblyCountBefore)
            {
                //  Code always comes here
                return false;
            }
            else
            {
                // This would prove the assembly was loaded in a NEW domain.  Never gets here.
                return true;
            }
        }
        public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            // This is required I've found
            return System.Reflection.Assembly.LoadFrom(ExeLoc);
        }
    }
}

控制台应用程序2:

using System;
namespace testapp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello from console");
        }
    }
    [Serializable]
    public class TestClass : MarshalByRefObject
    {
        public void TestWrite(string message)
        {
            System.IO.File.WriteAllText(@"outputsuccess.txt", message);
        }
    }

}

使用此类。 这里有一些注意事项:

  1. 此类显式设置进程的当前目录以及隔离的应用程序域的应用程序基本路径。 这不是完全必要的,但是它将使您的生活更加轻松。

    1. 如果未将应用程序基本路径设置为包含辅助程序集的目录,则运行时将尝试针对与主程序集相同的应用程序基本路径来解析辅助程序集的任何依赖关系,而该主程序集可能没有辅助程序集。程序集的依赖项。 您可以使用AssemblyResolve事件手动正确地解决依赖关系,但是设置应用程序基本路径是一种更简单且更不易出错的方式。

    2. 如果未设置Environment.CurrentDirectory ,则诸如File.WriteAllText("myfile.txt", "blah")类的文件操作将解析当前目录的路径,这可能不是辅助程序集的作者想要的。 (补充:因此,请始终手动解析路径。)

  2. 我相信像GetMethod这样的简单反射操作将不适用于MarshalByRefObject代理,例如CreateInstanceFromAndUnwrap返回的代理。 因此,您需要做更多的调用。

    1. 如果您同时是主程序集和辅助程序集的所有者,则可以为调用创建接口-将接口放入共享程序集中,在接口中定义跨域调用,在目标类中实现接口,目标类型上的domain.CreateInstanceFromAndUnwrap并将结果domain.CreateInstanceFromAndUnwrap为接口,然后可以使用该接口跨越域边界进行调用。

    2. 以下解决方案提供了一种侵入性较小的替代方法-您无需拥有辅助组件即可使用此技术。 这个想法是,主域在辅助域中创建了一个众所周知的中介对象( InvokerHelper ),并且中介从辅助域内部执行了必要的反射。

这是一个完整的实现:

// Provides a means of invoking an assembly in an isolated appdomain
public static class IsolatedInvoker
{
    // main Invoke method
    public static void Invoke(string assemblyFile, string typeName, string methodName, object[] parameters)
    {
        // resolve path
        assemblyFile = Path.Combine(Environment.CurrentDirectory, assemblyFile);
        Debug.Assert(assemblyFile != null);

        // get base path
        var appBasePath = Path.GetDirectoryName(assemblyFile);
        Debug.Assert(appBasePath != null);

        // change current directory
        var oldDirectory = Environment.CurrentDirectory;
        Environment.CurrentDirectory = appBasePath;
        try
        {
            // create new app domain
            var domain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, appBasePath, null, false);
            try
            {
                // create instance
                var invoker = (InvokerHelper) domain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, typeof(InvokerHelper).FullName);

                // invoke method
                var result = invoker.InvokeHelper(assemblyFile, typeName, methodName, parameters);

                // process result
                Debug.WriteLine(result);
            }
            finally
            {
                // unload app domain
                AppDomain.Unload(domain);
            }
        }
        finally
        {
            // revert current directory
            Environment.CurrentDirectory = oldDirectory;
        }
    }

    // This helper class is instantiated in an isolated app domain
    private class InvokerHelper : MarshalByRefObject
    {
        // This helper function is executed in an isolated app domain
        public object InvokeHelper(string assemblyFile, string typeName, string methodName, object[] parameters)
        {
            // create an instance of the target object
            var handle = Activator.CreateInstanceFrom(assemblyFile, typeName);

            // get the instance of the target object
            var instance = handle.Unwrap();

            // get the type of the target object
            var type = instance.GetType();

            // invoke the method
            var result = type.InvokeMember(methodName, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, instance, parameters);

            // success
            return result;
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM