[英]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.