[英]Dynamically loaded Assembly not loading in new AppDomain
这不是重复的-我没有碰到运气就审查了这个与StackOverflow相关的问题: 如何将具有所有引用的程序集递归加载到AppDomain?
我有两个控制台应用程序。 AssemblyLoaderTest.exe和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);
}
}
}
使用此类。 这里有一些注意事项:
此类显式设置进程的当前目录以及隔离的应用程序域的应用程序基本路径。 这不是完全必要的,但是它将使您的生活更加轻松。
如果未将应用程序基本路径设置为包含辅助程序集的目录,则运行时将尝试针对与主程序集相同的应用程序基本路径来解析辅助程序集的任何依赖关系,而该主程序集可能没有辅助程序集。程序集的依赖项。 您可以使用AssemblyResolve
事件手动正确地解决依赖关系,但是设置应用程序基本路径是一种更简单且更不易出错的方式。
如果未设置Environment.CurrentDirectory
,则诸如File.WriteAllText("myfile.txt", "blah")
类的文件操作将解析当前目录的路径,这可能不是辅助程序集的作者想要的。 (补充:因此,请始终手动解析路径。)
我相信像GetMethod
这样的简单反射操作将不适用于MarshalByRefObject代理,例如CreateInstanceFromAndUnwrap
返回的代理。 因此,您需要做更多的调用。
如果您同时是主程序集和辅助程序集的所有者,则可以为调用创建接口-将接口放入共享程序集中,在接口中定义跨域调用,在目标类中实现接口,目标类型上的domain.CreateInstanceFromAndUnwrap
并将结果domain.CreateInstanceFromAndUnwrap
为接口,然后可以使用该接口跨越域边界进行调用。
以下解决方案提供了一种侵入性较小的替代方法-您无需拥有辅助组件即可使用此技术。 这个想法是,主域在辅助域中创建了一个众所周知的中介对象( 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.