簡體   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