动态加载的程序集未加载到新的AppDomain中 [英] Dynamically loaded Assembly not loading in new AppDomain

查看:109
本文介绍了动态加载的程序集未加载到新的AppDomain中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This is not a duplicate - I have reviewed this related StackOverflow question with no luck: How to Load an Assembly to AppDomain with all references recursively?

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

I have two console applications. AssemblyLoaderTest.exe and testapp.exe


  1. 我正在尝试使用AssemblyLoaderTest.exe动态加载testapp.exe并从类中调用方法在testapp.exe中

  2. 到目前为止,代码仍然有效-testapp.exe中的 TestWrite()方法已正确执行(并写入了outputuccess.txt),但是 testapp.exe被加载到同一AppDomain 中,这被证明是因为 CallMethodFromDllInNewAppDomain始终返回false。我正在尝试在新AppDomain 中加载testapp.exe。

  1. I am trying to use AssemblyLoaderTest.exe to dynamically load testapp.exe and call a method from a class within testapp.exe
  2. So far the code works - the method "TestWrite()" in testapp.exe is executed correctly (and outputsuccess.txt is written), however, testapp.exe is loaded in the same AppDomain, which is proven because "CallMethodFromDllInNewAppDomain" always returns false. I am trying to load testapp.exe in a new AppDomain.

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

My question: how can I modify the below code so that testapp.exe is loaded in a new AppDomain, and as a result, "CallMethodFromDllInNewAppDomain" returns true? Thank you!

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

Code below. Both can be simply copied into new Console applications in VS and executed/compiled.

控制台应用程序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);
        }
    }

}


推荐答案

使用此类。以下是一些注意事项:

Use this class. Here are some notes:


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

  1. This class explicitly sets the current directory of the process and the app base path of the isolated app domain. This isn't entirely necessary, but it will make your life a whole lot easier.


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

  1. If you don't set the app base path to the directory containing the secondary assembly, then the runtime will attempt to resolve any dependencies of the secondary assembly against the same app base path as the primary assembly, which probably doesn't have the secondary assembly's dependencies. You could use the AssemblyResolve event to manually resolve the dependencies correctly, but settings the app base path is a much simpler and less error-prone way to do this.

如果未设置 Environment.CurrentDirectory ,则文件操作如 File。 WriteAllText( myfile.txt, blah)将解析当前目录的路径,这可能不是辅助程序集作者的意图。 (助理:出于这个原因,总是手动解析路径。)

If you don't set Environment.CurrentDirectory, then file operations such as File.WriteAllText("myfile.txt", "blah") will resolve paths against the current directory, which is probably not what the secondary assembly's author intended. (ASIDE: Always resolve paths manually for this reason.)


  • 我相信像<$这样的简单反射操作c $ c> GetMethod 不能在MarshalByRefObject代理上使用,例如 CreateInstanceFromAndUnwrap 返回的代理。因此,您需要做更多的调用。

  • I believe simple reflection operations like GetMethod won't work on a MarshalByRefObject proxy such as returned by CreateInstanceFromAndUnwrap. So you need to do a little more to invoke.


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

    1. If you are the owner of both the primary and secondary assemblies, you could create an interface for the invocation -- put the interface in a shared assembly, define the cross-domain call in the interface, implement the interface in the target class, do a domain.CreateInstanceFromAndUnwrap on the target type and cast the result as the interface, which you can then use to call across the domain boundary.

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

    The solution below provides an alternative means that is less invasive -- you don't have to own the secondary assembly for this technique to work. The idea is that the primary domain creates a well-known intermediary object (InvokerHelper) in the secondary domain, and that intermediary performs the necessary reflection from inside the secondary domain.


  • 这是一个完整的实现: / p>

    Here's a complete implementation:

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

    这篇关于动态加载的程序集未加载到新的AppDomain中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆