我想加载到一个新的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
又一次,在另一个(参考)集会上.
如何递归加载所有引用?
在加载根程序集之前是否必须创建引用树?如何在不加载程序集的情况下获取程序集的引用?
您需要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
异常,因为程序集解析程序将尝试查找您在GAC或当前应用程序的bin文件夹中加载的程序集.使用LoadFile
加载任意组件文件,而不是-但请注意,如果你这样做,你需要自己加载任何依赖关系.
http://support.microsoft.com/kb/837908/en-us
C#版本:
创建一个主持人类并从MarshalByRefObject
以下位置继承它:
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));
因此,无论你想对程序集做什么,都应该在代理类中完成 - 一个继承MarshalByRefObject的类.
记住调用者域和新创建的域应该都可以访问代理类程序集.如果您的问题不是太复杂,请考虑保持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); } } }
如果确实需要从与当前应用程序域文件夹不同的文件夹加载程序集,请使用特定的dll搜索路径文件夹创建新的应用程序域.
例如,上面代码中的app域创建行应替换为:
var dllsSearchPath = @"[dlls search path for new app domain]"; AppDomain newDomain = AppDomain.CreateDomain("newDomain", new Evidence(), dllsSearchPath, "", true);
这样,所有dll都将自动从dllsSearchPath中解析出来.
如果引用的程序集不在GAC中或在CLR的探测路径上,则需要处理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"}); }