如何加载与所有引用recursion的程序集到AppDomain?
我想加载到一个新的AppDomain
一些程序集,其中有一个复杂的引用树(MyDll.dll – > Microsoft.Office.Interop.Excel.dll – > Microsoft.Vbe.Interop.dll – > Office.dll – > stdole.dll )
据我所知,当一个程序集加载到AppDomain
,它的引用不会自动加载,我必须手动加载它们。 所以当我这样做时:
string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory string path = System.IO.Path.Combine(dir, "MyDll.dll"); AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation; setup.ApplicationBase = dir; AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup); domain.Load(AssemblyName.GetAssemblyName(path));
并得到FileNotFoundException
:
无法加载文件或程序集'MyDll,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'或其依赖项之一。 该系统找不到指定的文件。
我认为关键是它的一个依赖 。
好吧,我在下一步之前做domain.Load(AssemblyName.GetAssemblyName(path));
foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies()) { domain.Load(refAsmName); }
但在另一个(引用的)程序集上再次得到FileNotFoundException
。
如何recursion加载所有引用?
在加载根组件之前,我必须创build引用树吗? 如何获得程序集的引用而不加载它?
在代理对象在外部应用程序域中执行之前,您需要调用CreateInstanceAndUnwrap
。
class Program { static void Main(string[] args) { AppDomainSetup domaininfo = new AppDomainSetup(); domaininfo.ApplicationBase = System.Environment.CurrentDirectory; Evidence adevidence = AppDomain.CurrentDomain.Evidence; AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo); Type type = typeof(Proxy); var value = (Proxy)domain.CreateInstanceAndUnwrap( type.Assembly.FullName, type.FullName); var assembly = value.GetAssembly(args[0]); // AppDomain.Unload(domain); } } public class Proxy : MarshalByRefObject { public Assembly GetAssembly(string assemblyPath) { try { return Assembly.LoadFile(assemblyPath); } catch (Exception) { return null; // throw new InvalidOperationException(ex); } } }
另外,请注意,如果使用LoadFrom
,则可能会得到FileNotFound
exception,因为程序集parsing器将尝试查找要在GAC或当前应用程序的bin文件夹中加载的程序集。 使用LoadFile
来加载一个任意的程序集文件 – 但是请注意,如果你这样做,你将需要自己加载任何依赖关系。
http://support.microsoft.com/kb/837908/en-us
C#版本:
创build一个主持人类并从MarshalByRefObject
inheritance它:
class ProxyDomain : MarshalByRefObject { public Assembly GetAssembly(string assemblyPath) { try { return Assembly.LoadFrom(assemblyPath); } catch (Exception ex) { throw new InvalidOperationException(ex.Message); } } }
来自客户站点的呼叫
ProxyDomain pd = new ProxyDomain(); Assembly assembly = pd.GetAssembly(assemblyFilePath);
在新的AppDomain上,尝试设置一个AssemblyResolve事件处理程序。 当一个依赖项丢失时,这个事件被调用。
一旦你将程序集实例传递给调用者域,调用者域将尝试加载它! 这就是为什么你会得到这个例外。 这发生在您的最后一行代码中:
domain.Load(AssemblyName.GetAssemblyName(path));
因此,无论你想对程序集做什么,都应该在一个代理类中完成 – 一个inheritanceMarshalByRefObject的类。
请注意,调用者域和新创build的域都应该可以访问代理类程序集。 如果您的问题不是太复杂,请考虑保持ApplicationBase文件夹不变,因此它将与调用者域文件夹相同(新域将只加载它需要的组件)。
在简单的代码中:
public void DoStuffInOtherDomain() { const string assemblyPath = @"[AsmPath]"; var newDomain = AppDomain.CreateDomain("newDomain"); var asmLoaderProxy = (ProxyDomain)newDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(ProxyDomain).FullName); asmLoaderProxy.GetAssembly(assemblyPath); } class ProxyDomain : MarshalByRefObject { public void GetAssembly(string AssemblyPath) { try { Assembly.LoadFrom(AssemblyPath); //If you want to do anything further to that assembly, you need to do it here. } catch (Exception ex) { throw new InvalidOperationException(ex.Message, ex); } } }
如果您确实需要从不同于当前应用程序域文件夹的文件夹加载程序集,请使用特定的dllsearchpath文件夹创build新的应用程序域。
例如,上述代码中的应用程序域创build行应replace为:
var dllsSearchPath = @"[dlls search path for new app domain]"; AppDomain newDomain = AppDomain.CreateDomain("newDomain", new Evidence(), dllsSearchPath, "", true);
这样,所有的DLL将自动从dllsSearchPathparsing。
如果引用程序集不在GAC或CLR的探测path中,则需要处理AppDomain.AssemblyResolve或AppDomain.ReflectionOnlyAssemblyResolve事件(取决于您正在执行哪个负载)。
AppDomain.AssemblyResolve
AppDomain.ReflectionOnlyAssemblyResolve
我花了一段时间了解@ user1996230的答案,所以我决定提供一个更明确的例子。 在下面的示例中,我为在另一个AppDomain中加载的对象进行代理,并从另一个域调用该对象的方法。
class ProxyObject : MarshalByRefObject { private Type _type; private Object _object; public void InstantiateObject(string AssemblyPath, string typeName, object[] args) { assembly = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + AssemblyPath); //LoadFrom loads dependent DLLs (assuming they are in the app domain's base directory _type = assembly.GetType(typeName); _object = Activator.CreateInstance(_type, args); ; } public void InvokeMethod(string methodName, object[] args) { var methodinfo = _type.GetMethod(methodName); methodinfo.Invoke(_object, args); } } static void Main(string[] args) { AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = @"SomePathWithDLLs"; AppDomain domain = AppDomain.CreateDomain("MyDomain", null, setup); ProxyObject proxyObject = (ProxyObject)domain.CreateInstanceFromAndUnwrap(typeof(ProxyObject).Assembly.Location,"ProxyObject"); proxyObject.InstantiateObject("SomeDLL","SomeType", new object[] { "someArgs}); proxyObject.InvokeMethod("foo",new object[] { "bar"}); }
Key是AppDomain引发的AssemblyResolve事件。
[STAThread] static void Main(string[] args) { fileDialog.ShowDialog(); string fileName = fileDialog.FileName; if (string.IsNullOrEmpty(fileName) == false) { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; if (Directory.Exists(@"c:\Provisioning\") == false) Directory.CreateDirectory(@"c:\Provisioning\"); assemblyDirectory = Path.GetDirectoryName(fileName); Assembly loadedAssembly = Assembly.LoadFile(fileName); List<Type> assemblyTypes = loadedAssembly.GetTypes().ToList<Type>(); foreach (var type in assemblyTypes) { if (type.IsInterface == false) { StreamWriter jsonFile = File.CreateText(string.Format(@"c:\Provisioning\{0}.json", type.Name)); JavaScriptSerializer serializer = new JavaScriptSerializer(); jsonFile.WriteLine(serializer.Serialize(Activator.CreateInstance(type))); jsonFile.Close(); } } } } static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { string[] tokens = args.Name.Split(",".ToCharArray()); System.Diagnostics.Debug.WriteLine("Resolving : " + args.Name); return Assembly.LoadFile(Path.Combine(new string[]{assemblyDirectory,tokens[0]+ ".dll"})); }