负载电流组装成不同的AppDomain [英] Load Current Assembly into different AppDomain

查看:210
本文介绍了负载电流组装成不同的AppDomain的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经创建了一个的AppDomain 具有不同的基本目录。不过,我似乎无法加载当前正在执行的组件插入到其他的AppDomain而无需在基本目录具有当前执行的程序集的副本。我甚至还尝试从字节加载它



我没有得到任何异常,当我尝试加载,但是当我尝试使用:

  domain.DoCallBack(新CrossAppDomainDelegate(... 

我得到:




无法加载文件或程序的........... 。系统找不到指定的文件




我的代码如下:

 私有静态无效SaveAssemblies(大会屁股,列表与LT;字节[]> assemblyByteList)
{
的AssemblyName [] assNames = ass.GetReferencedAssemblies();
的foreach(的AssemblyName assName在assNames)
{
大会referedAss =的Assembly.Load(assName);
如果(referedAss.GlobalAssemblyCache!)
{
SaveAssemblies(referedAss ,assemblyByteList);
}
}
字节[] = rawAssembly File.ReadAllBytes(ass.Location);
assemblyByteList.Add(rawAssembly);
}

公共静态的AppDomain CreateAppDomain(字符串目录,字符串名称)
{
AppDomainSetup domainSetup =新AppDomainSetup();
domainSetup.ApplicationBase = DIR;
domainSetup.ApplicationName = Path.GetFileName(DIR);
domainSetup.PrivateBinPath = Path.Combine(DIR利布斯);

AppDomain中域= AppDomain.CreateDomain(姓名,空,domainSetup);所需模块
名单,LT
//加载系统组件;字节[]> assemblyByteList =新的List<字节[]>();
SaveAssemblies(Assembly.GetExecutingAssembly(),assemblyByteList);

的foreach(字节[] rawAssembly在assemblyByteList)
domain.Load(rawAssembly);

domain.DoCallBack(新CrossAppDomainDelegate(设置→));
返回域;
}



更新:



看来组件加载,如果我在输出看,我看到这个



TaskExecuter.Terminal.vshost.exe(管理(v4.0.30319)):已加载 NLOG'
'TaskExecuter.Terminal.vshost.exe(管理(v4.0.30319)):已加载。TaskExecuter,符号加载



但我仍然得到的异常......我不明白这一点。




System.IO.FileNotFoundException了未处理消息=无法加载
文件或程序集TaskExecuter,版本= 1.0.4244.31921,
区域性=中性公钥= null或它的一个依赖。在
系统找不到指定的文件。来源= mscorlib程序结果
=文件名TaskExecuter,版本= 1.0.4244.31921文化=中立,
公钥=空FusionLog ====预绑定状态信息===
日志:用户=彼得PC\Peter日志:显示名称= TaskExecuter,
版本= 1.0.4244.31921文化=中性公钥=空
(完全指定的)日志:应用平台=
档: /// C:/ ProgramData / TaskExecuter / taskLib内/ uTorrentTasks LOG:初始
PrivatePath = C:\ProgramData\TaskExecuter\TaskLib\uTorrentTasks\Libs
调用汇编:(未知)。
===日志:此绑定的默认加载上下文开始。日志:正在使用
应用程序配置文件:D:\users\peter\documents\visual工作室
2010\Projects\TaskExecuter\TaskExecuter.Terminal\bin\Release\ TaskExecuter.Terminal.vshost.exe.Config
日志:使用主机配置文件:LOG:从
C使用的机器配置
档:\Windows\Microsoft.NET\Framework\ v4.0.30319\config\machine.config。
日志:没有被应用政策在这个时候(私人,
定制的,局部的,或基于位置的程序集绑定)来引用。日志:尝试新的URL
档的
下载:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.DLL。
日志:尝试新的URL
文件下载:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.DLL。
日志:尝试新的URL
文件下载:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter.DLL。
日志:尝试新的URL
文件下载:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter/TaskExecuter.DLL。
日志:尝试新的URL
文件下载:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.EXE。
日志:尝试新的URL
文件下载:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.EXE。
日志:尝试新的URL
文件下载:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter.EXE。
日志:尝试新的URL
文件下载:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter/TaskExecuter.EXE



堆栈跟踪:
在System.Reflection.RuntimeAssembly._nLoad(的AssemblyName
文件名,字符串的代码库,证据assemblySecurity,RuntimeAssembly
locationHint,StackCrawlMark&安培; stackMark,布尔throwOnFileNotFound,
布尔forIntrospection,布尔suppressSecurityChecks)
在System.Reflection.RuntimeAssembly.nLoad(的AssemblyName
文件名,字符串的代码库,证据assemblySecurity,RuntimeAssembly
locationHint,StackCrawlMark&安培; stackMark,布尔throwOnFileNotFound,
布尔forIntrospection,布尔suppressSecurityChecks)
。在
System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(的AssemblyName
assemblyRef,证据assemblySecurity,StackCrawlMark&安培; stackMark,
布尔forIntrospection,布尔suppressSecurityChecks)
。在System.Reflection.RuntimeAssembly.InternalLoad(字符串
assemblyString,证据assemblySecurity,StackCrawlMark&安培; stackMark,
布尔forIntrospection)
在System.Reflection.Assembly.Load(字符串assemblyString)
。在
System.Runtime.Serialization.FormatterServices.LoadAssemblyFromString(字符串
的AssemblyName )
。在
System.Reflection.MemberInfoSerializationHolder..ctor(SerializationInfo中
信息,的StreamingContext上下文)在System.AppDomain.DoCallBack
(CrossAppDomainDelegate
callBackDelegate)
。在TaskExecuter.AppDomainHelper.CreateAppDomain(字符串DIR,
字符串名称)在d:\users\peter\documents\visual工作室
2010\Projects\TaskExecuter\TaskExecuter\ AppDomainHelper.cs:在
D在TaskExecuter.TaskManagment.TaskFinder.Probe()线50
:\users\peter\documents\visual工作室
2010\Projects\TaskExecuter \TaskExecuter\TaskManagment\TaskFinder.cs:行
29
。在TaskExecuter.TaskManagment.TaskManager.LoadTasks()在
D:\users\peter\documents\visual工作室
2010\Projects\TaskExecuter\TaskExecuter\TaskManagment\TaskManager.cs:行
63
。在TaskExecuter.TaskManagment.TaskManager.Start()在
D: \users\peter\documents\visual工作室
2010\Projects\TaskExecuter\TaskExecuter\TaskManagment\TaskManager.cs:行
95
。在TaskExecuter.Terminal .Program.Main(字串[] args)在
D:\users\peter\documents\visual工作室
2010\Projects\TaskExecuter\TaskExecuter.Terminal\Program.cs :第16行
在System.AppDomain._nExecuteAssembly(RuntimeAssembly组装,
字串[] args)
在System.AppDomain.ExecuteAssembly(字符串assemblyFile,
证据assemblySecurity,字符串[]参数)在
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly
()
在System.Threading.ThreadHelper.ThreadStart_Context(对象
状态)
在的System.Threading .ExecutionContext.Run(ExecutionContext中
的ExecutionContext,ContextCallback回调,对象状态,布尔
ignoreSyncCtx)在System.Threading.ExecutionContext.Run
(ExecutionContext中
的ExecutionContext,ContextCallback回调,对象状态在System.Threading.ThreadHelper.ThreadStart)
()结果
的InnerException:



解决方案

我能够恢复使用挂钩,以archive.org博客发布,并拿出一个可行的解决方案。



我的目标是动态编译exe文件到一个临时位置,然后有一个EXE影子加载所有主要的dll在孩子的AppDomain使生成该exe文件的主要应用可以很容易地更新。其基本做法是使用childAppDomain.CreateInstanceFrom创建在构造函数中安装装配决心事件处理程序的类型。我的代码看起来像

  VAR exportAppDomain = AppDomain.CreateDomain(
runnerName,
空,
appDomainSetup,
新的PermissionSet(PermissionState.Unrestricted));

exportAppDomain.CreateInstanceFrom(
Assembly.GetExecutingAssembly()位置,
ExportLauncher.AppDomainResolver,
真,
BindingFlags.Public |。的BindingFlags。例如,
空,
新的对象[] {Assembly.GetExecutingAssembly()位置。},
空,
NULL);

和创建所需的AssemblyResolve处理器类型(下面的博客文章介绍了为什么需要另一种类型)

 类AppDomainResolver 
{
串_sourceExeLocation;

公共AppDomainResolver(字符串sourceExeLocation)
{
_sourceExeLocation = sourceExeLocation;
AppDomain.CurrentDomain.AssemblyResolve + = CurrentDomain_AssemblyResolve;
}

大会CurrentDomain_AssemblyResolve(对象发件人,ResolveEventArgs参数)
{
如果(args.Name.Contains(exporterLauncher))//它为何不已经知道它有这个总成装?在似乎是需要
返回的typeof(AppDomainResolver).Assembly;
,否则
返回NULL;
}
}

下面是原来的博客文章:



应用程序域是很难...



你有没有一直与应用程序域在.NET?在开始的时候它不 ŧ似乎所有的困难,但那些你了解他们,你开始认识到所有的小困难。



一切工作正常,只要你不外的移动主持人AppDomains.BaseDirectory,但在我们的例子中,我们希望有部署在说位置C:\My插件插件,而主机应用程序将在运行C:\Program Files\My应用程序,因为我们可能会从AppDomain中的一些主机大会问题碰上的依赖显然是不可避免的。



经典
下面是一些简单的代码,我们的第一次尝试

  1:字符串applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath); 
2:AppDomainSetup设置=新AppDomainSetup
3:{
4:应用程序名称=名称,
5:ApplicationBase = applicationBase,
6:PrivateBinPath = AppDomain.CurrentDomain。 BaseDirectory,
7:PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
8:=的ConfigurationFile AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
9:};
10:
11:证据证据=新证据(AppDomain.CurrentDomain.Evidence);
12:AppDomain中域= AppDomain.CreateDomain(姓名,证据,设置);



看起来很简单,但由于ApplicationBase,是从AppDomain.CurrentDomain.BaseDirectory不同,我们跑了。成什么似乎是一个很好的了解例外



System.IO.FileNotFoundException:未能加载文件或程序集Host.Services,版本= 1.0.0.0,文化=中性公钥= null或它的一个依赖。该系统找不到指定的文件。



如果您与任何类型的动态加载组件的工作,我相当肯定是你熟悉的。而问题是,Host.Services是主机应用程序域之内知道,因为它被保存在C:\Program Files\My应用程序,并且应用领域寻找它期待在C:\我的插件。



好了,我们认为我们的指示它也期待在AppDomain.CurrentDomain.BaseDirectory,这将是C:\Program Files\我的应用程序,但事实并非如此。



AppDomain.AssemblyResolve救援?
好了,所以我们一直在与这些怪癖之前,让我们知道我们如何使用AppDomain.AssemblyResolve手动解决在AppDomain它自身无法处理任何组件。

  1:字符串applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath); 
2:AppDomainSetup设置=新AppDomainSetup
3:{
4:应用程序名称=名称,
5:ApplicationBase = applicationBase,
6:PrivateBinPath = AppDomain.CurrentDomain。 BaseDirectory,
7:PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
8:=的ConfigurationFile AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
9:};
10:
11:证据证据=新证据(AppDomain.CurrentDomain.Evidence);
12:AppDomain中域= AppDomain.CreateDomain(姓名,证据,设置);
13:domain.AssemblyResolve + =解决;

这应该工作的权利,以及我们想象的那么和那些我们再次错了,现在会发生什么是而不是实际得到尽可能初始化应用程序域,并使用它,而不是它的权利失败,我们迷上了解决组件的事件处理程序。



同样异常看起来很就像前面提到的,这一次却无法找到包含有解决的处理程序,我们在上面的代码片段的最后一行建立类型的程序集。



AppDomain.Load呢!
好,所以很明显挂钩事件处理程序时,应用程序域需要知道对象的处理该事件的类型,实际上是相当可以理解的,当你想想看,如果应用程序域甚至不能发现之一,并加载我们确实不能处理任何事情。



那么,什么是下一个?我们的想法是手动指示应用程序域加载浅大会,没有什么可以在GAC中找到其他任何相关性,并且挂钩的事件处理程序了。

  1:字符串applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath); 
2:AppDomainSetup设置=新AppDomainSetup
3:{
4:应用程序名称=名称,
5:ApplicationBase = applicationBase,
6:PrivateBinPath = AppDomain.CurrentDomain。 BaseDirectory,
7:PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
8:=的ConfigurationFile AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
9:};
10:
11:证据证据=新证据(AppDomain.CurrentDomain.Evidence);
12:AppDomain中域= AppDomain.CreateDomain(姓名,证据,设置);
13分配:domain.Load(File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectoryHost.AssemblyLoader.dll)));
14:domain.AssemblyResolve + =新AssemblyLoader(AppDomain.CurrentDomain.BaseDirectory).Handle;



用一个非常简单的小类像下面,并且不介意奇解析行为。

  1:[Serializable接口] 
2:公共类AssemblyLoader
3:{
4:私人字符串ApplicationBase {搞定;组; }
5:
6:公共AssemblyLoader(字符串applicationBase)
7:{
8:ApplicationBase = applicationBase;
9:}
10:
11:公众集会解析(对象发件人,ResolveEventArgs参数)
12:{
13:的AssemblyName的AssemblyName =新的AssemblyName(参数。名称);
14:字符串文件名=的String.Format({0} .dll文件,assemblyName.Name);
15:返回Assembly.LoadFile(Path.Combine(ApplicationBase,文件名));
16:}
17:}



所以,是或否?NO? !...同样的问题依然。



事情更简单!
其实事情变得到底要简单得多,当我们设法使它工作。



我不能说.NET团队究竟是如何设想这应该工作,我们无法真正找出了PrivateBinPath和PrivateBinPathProbe被用于任何可用的东西。好了,我们现在使用他们,使他们的工作,因为我们预计他们会



所以我们改变了AssemblyLoader级看起来像这个代替:

  1:[Serializable接口] 
2:公共类AssemblyLoader:MarshalByRefObject的
3:{
4中:private string ApplicationBase {搞定;组; }
5:
6:公共AssemblyLoader()
7:{
8:ApplicationBase = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
9:AppDomain.CurrentDomain.AssemblyResolve + =解决;
10:}
11:
12:私人大会解析(对象发件人,ResolveEventArgs参数)
13:{
14:的AssemblyName的AssemblyName =新的AssemblyName(参数。名称);
15:字符串文件名=的String.Format({0} .dll文件,assemblyName.Name);
16:返回Assembly.LoadFile(Path.Combine(ApplicationBase,文件名));
17:}
18:}



因此,而不是连接事件在这里我们创建的应用领域,我们让类做用它的自我,并以CurrentDomain代替。



确定这样的等待,不会引起问题在出厂时创建它,因为它现在加载了错误的域名?嗯值得庆幸的是,你可以创建从外域之内的物体。



所以创建域现做如下:

  1:字符串applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath); 
2:AppDomainSetup设置=新AppDomainSetup
3:{
4:应用程序名称=名称,
5:ApplicationBase = applicationBase,
6:PrivateBinPath = AppDomain.CurrentDomain。 BaseDirectory,
7:PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
8:=的ConfigurationFile AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
9:};
10:
11:证据证据=新证据(AppDomain.CurrentDomain.Evidence);
12:AppDomain中域= AppDomain.CreateDomain(姓名,证据,设置);
13分配:domain.CreateInstanceFrom(Path.Combine(AppDomain.CurrentDomain.BaseDirectoryHost.AssemblyLoader.dll),Host.AssemblyLoader);

我们甚至不关心维持对AssemblyLoader,因为它应该几乎是一个参考通过挂钩它的自我到事件更让。



希望这可以帮助一些已经绊了同样的问题,我看到很多的解决方法,人们然后要么就让插件-INS被安装在同一主机目录,具有所有插件,即使它是不是插件知道它是依赖于等一起部署必要的依赖



以上,至少使我们能够安装插件从我们的主机应用程序的基础,我认为这是很好的了。



如果有人已经解决了这个不同,那么请作出回应,也许我们可以找到利弊无论哪种方式,或者只是发现了一个更好的解决方案。



如果您有任何疑问或不能得到。上述工作,那么随意问



作者:延斯·梅尔高|发布@周四,2010年下午3时08分07月01日,|反馈(0)


I have created an AppDomain with a different base directory. However, I cannot seem to load the currently executing assembly into the other AppDomain without having a copy of the current executing assembly in the base directory. I've even tried to load it from the bytes.

I get no exception when I try to load, but when I try to use:

domain.DoCallBack(new CrossAppDomainDelegate(... 

I get:

Could not load file or assembly ........... The system cannot find the file specified.

My code is as follows:

private static void SaveAssemblies(Assembly ass, List<byte[]> assemblyByteList)
{
    AssemblyName[] assNames = ass.GetReferencedAssemblies();
    foreach (AssemblyName assName in assNames)
    {
        Assembly referedAss = Assembly.Load(assName);
        if (!referedAss.GlobalAssemblyCache)
        {
            SaveAssemblies(referedAss, assemblyByteList);
        }
    }
    byte[] rawAssembly = File.ReadAllBytes(ass.Location);
    assemblyByteList.Add(rawAssembly);
}

public static AppDomain CreateAppDomain(string dir, string name)
{
    AppDomainSetup domainSetup = new AppDomainSetup();
    domainSetup.ApplicationBase = dir;
    domainSetup.ApplicationName = Path.GetFileName(dir);
    domainSetup.PrivateBinPath = Path.Combine(dir, "Libs");

    AppDomain domain = AppDomain.CreateDomain(name, null, domainSetup);
    //Load system assemblies needed for the module
    List<byte[]> assemblyByteList = new List<byte[]>();
    SaveAssemblies(Assembly.GetExecutingAssembly(), assemblyByteList);

    foreach (byte[] rawAssembly in assemblyByteList)
        domain.Load(rawAssembly);

    domain.DoCallBack(new CrossAppDomainDelegate(SetupLogging));
    return domain;
}

Update:

It seems the assembly is loaded if i look in output i see this

'TaskExecuter.Terminal.vshost.exe' (Managed (v4.0.30319)): Loaded 'NLog' 'TaskExecuter.Terminal.vshost.exe' (Managed (v4.0.30319)): Loaded 'TaskExecuter', Symbols loaded.

but i still get the exception... i don't understand this

System.IO.FileNotFoundException was unhandled Message=Could not load file or assembly 'TaskExecuter, Version=1.0.4244.31921, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. Source=mscorlib
FileName=TaskExecuter, Version=1.0.4244.31921, Culture=neutral, PublicKeyToken=null FusionLog==== Pre-bind state information === LOG: User = Peter-PC\Peter LOG: DisplayName = TaskExecuter, Version=1.0.4244.31921, Culture=neutral, PublicKeyToken=null (Fully-specified) LOG: Appbase = file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks LOG: Initial PrivatePath = C:\ProgramData\TaskExecuter\TaskLib\uTorrentTasks\Libs Calling assembly : (Unknown). === LOG: This bind starts in default load context. LOG: Using application configuration file: d:\users\peter\documents\visual studio 2010\Projects\TaskExecuter\TaskExecuter.Terminal\bin\Release\TaskExecuter.Terminal.vshost.exe.Config LOG: Using host configuration file: LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config. LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind). LOG: Attempting download of new URL file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.DLL. LOG: Attempting download of new URL file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.DLL. LOG: Attempting download of new URL file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter.DLL. LOG: Attempting download of new URL file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter/TaskExecuter.DLL. LOG: Attempting download of new URL file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.EXE. LOG: Attempting download of new URL file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.EXE. LOG: Attempting download of new URL file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter.EXE. LOG: Attempting download of new URL file:///C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter/TaskExecuter.EXE.

StackTrace: at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection, Boolean suppressSecurityChecks) at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) at System.Reflection.Assembly.Load(String assemblyString) at System.Runtime.Serialization.FormatterServices.LoadAssemblyFromString(String assemblyName) at System.Reflection.MemberInfoSerializationHolder..ctor(SerializationInfo info, StreamingContext context) at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate) at TaskExecuter.AppDomainHelper.CreateAppDomain(String dir, String name) in d:\users\peter\documents\visual studio 2010\Projects\TaskExecuter\TaskExecuter\AppDomainHelper.cs:line 50 at TaskExecuter.TaskManagment.TaskFinder.Probe() in d:\users\peter\documents\visual studio 2010\Projects\TaskExecuter\TaskExecuter\TaskManagment\TaskFinder.cs:line 29 at TaskExecuter.TaskManagment.TaskManager.LoadTasks() in d:\users\peter\documents\visual studio 2010\Projects\TaskExecuter\TaskExecuter\TaskManagment\TaskManager.cs:line 63 at TaskExecuter.TaskManagment.TaskManager.Start() in d:\users\peter\documents\visual studio 2010\Projects\TaskExecuter\TaskExecuter\TaskManagment\TaskManager.cs:line 95 at TaskExecuter.Terminal.Program.Main(String[] args) in d:\users\peter\documents\visual studio 2010\Projects\TaskExecuter\TaskExecuter.Terminal\Program.cs:line 16 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()
InnerException:

解决方案

I was able to recover the linked to blog post using archive.org and also come up with a working solution.

My goal was to dynamically compile an exe into a temporary location, and then have that exe shadow load all main dlls in a child appdomain so that the main application that spawned the exe can be updated easily. The basic approach is using childAppDomain.CreateInstanceFrom to create a type that in the constructor installs the assembly resolve event handler. My code looked like

var exportAppDomain = AppDomain.CreateDomain(
    runnerName,
    null,
    appDomainSetup,
    new PermissionSet(PermissionState.Unrestricted));

exportAppDomain.CreateInstanceFrom(
    Assembly.GetExecutingAssembly().Location,
    "ExportLauncher.AppDomainResolver",
    true,
    BindingFlags.Public | BindingFlags.Instance,
    null,
    new object[] { Assembly.GetExecutingAssembly().Location },
    null,
    null);

And the type that creates the needed AssemblyResolve handler (the blog post below describes why you need another type)

class AppDomainResolver
{
    string _sourceExeLocation;

    public AppDomainResolver(string sourceExeLocation)
    {
        _sourceExeLocation = sourceExeLocation;
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        if (args.Name.Contains("exporterLauncher")) // why does it not already know it has this assembly loaded? the seems to be required
            return typeof(AppDomainResolver).Assembly;
        else
            return null;
    }
}

Here is the original blog post:

Application Domains is hard…

Have you ever been working with Application Domain in .NET?, in the beginning it doesn’t seem all that difficult, but ones you get to know them you begin to realize all the little difficulties.

Everything works fine as long as you don’t move outside the Host AppDomains.BaseDirectory, but in our case we wanted to have Plug-ins deployed at say location "C:\My Plug-ins" while the host application would run at "C:\Program Files\My App", since we might run into dependencies from the AppDomain to some of the Host Assemblies problems was apparently inevitable.

The Classic Here is some simple code and our first attempt.

 1:  string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath);
   2:  AppDomainSetup setup = new AppDomainSetup
   3:  {
   4:      ApplicationName = name,
   5:      ApplicationBase = applicationBase,
   6:      PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
   7:      PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
   8:      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
   9:  };
  10:   
  11:  Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
  12:  AppDomain domain = AppDomain.CreateDomain(name, evidence, setup);

Seems very simple, but because "ApplicationBase" is different from "AppDomain.CurrentDomain.BaseDirectory" we ran into what seems to be a very well know exception.

System.IO.FileNotFoundException: Could not load file or assembly 'Host.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

If you have worked with any sort of dynamically loading assemblies I am fairly sure that is familiar to you. And the issue is that "Host.Services" was know within the Host Application Domain because it is stored in "C:\Program Files\My App", and the Application Domain looking for it is looking in "C:\My Plug-ins".

Well we Thought we instructed it to also look in "AppDomain.CurrentDomain.BaseDirectory" which would be "C:\Program Files\My App", but that was not the case.

AppDomain.AssemblyResolve to the rescue? Ok so we have been working with these quirks before, so we knew how we could use "AppDomain.AssemblyResolve" to manually resolve any assemblies that the AppDomain it self could not handle.

1:  string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath);
   2:  AppDomainSetup setup = new AppDomainSetup
   3:  {
   4:      ApplicationName = name,
   5:      ApplicationBase = applicationBase,
   6:      PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
   7:      PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
   8:      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
   9:  };
  10:   
  11:  Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
  12:  AppDomain domain = AppDomain.CreateDomain(name, evidence, setup);
  13:  domain.AssemblyResolve += Resolve;

That should work right, well we thought so and ones again we were wrong, what happens now is that instead of actually getting as far as initializing the Application Domain and using it, instead it fails right where we hooked up the event handler for resolving assemblies.

Again the exception looks very much like the previous mentioned, but this time it can’t find the Assembly that contains the Type that has the "Resolve" handler we set up in the very last line in the above snippet.

AppDomain.Load then! Ok, so obviously when hooking up the event handler, the Application Domain needs to know the Type of the object handling that event, that is actually fairly understandable when you think about it, so if the Application Domain can’t even find that one and load we can’t really handle anything.

So what is next? Our idea was to manually instruct the Application Domain to load a shallow assembly that didn’t have any other dependencies that what could be found in the GAC, and the hook an event handler up.

 1:  string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath);
   2:  AppDomainSetup setup = new AppDomainSetup
   3:  {
   4:      ApplicationName = name,
   5:      ApplicationBase = applicationBase,
   6:      PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
   7:      PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
   8:      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
   9:  };
  10:   
  11:  Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
  12:  AppDomain domain = AppDomain.CreateDomain(name, evidence, setup);
  13:  domain.Load(File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Host.AssemblyLoader.dll")));
  14:  domain.AssemblyResolve += new AssemblyLoader(AppDomain.CurrentDomain.BaseDirectory).Handle;

Using a very simple little class like the following, and don’t mind the odd Resolve behavior.

 1:  [Serializable]
   2:  public class AssemblyLoader
   3:  {
   4:      private string ApplicationBase { get; set; }
   5:   
   6:      public AssemblyLoader(string applicationBase)
   7:      {
   8:          ApplicationBase = applicationBase;
   9:      }
  10:   
  11:      public Assembly Resolve(object sender, ResolveEventArgs args)
  12:      {
  13:          AssemblyName assemblyName = new AssemblyName(args.Name);
  14:          string fileName = string.Format("{0}.dll", assemblyName.Name);
  15:          return Assembly.LoadFile(Path.Combine(ApplicationBase, fileName));
  16:      }
  17:  }

So yes or no?… NO!… same problem still.

Things are much more simple! Actually things became much more simple in the end when we managed to make it work.

I Can’t say how exactly the .NET team has envisioned that this should work, we couldn't really find out any useable things that the "PrivateBinPath" and "PrivateBinPathProbe" was used for. Well we use them now, and made them work as we expected they would!

So we changed the "AssemblyLoader" class to look like this instead:

   1:  [Serializable]
   2:  public class AssemblyLoader : MarshalByRefObject
   3:  {
   4:      private string ApplicationBase { get; set; }
   5:   
   6:      public AssemblyLoader()
   7:      {
   8:          ApplicationBase = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
   9:          AppDomain.CurrentDomain.AssemblyResolve += Resolve;
  10:      }
  11:   
  12:      private Assembly Resolve(object sender, ResolveEventArgs args)
  13:      {
  14:          AssemblyName assemblyName = new AssemblyName(args.Name);
  15:          string fileName = string.Format("{0}.dll", assemblyName.Name);
  16:          return Assembly.LoadFile(Path.Combine(ApplicationBase, fileName));
  17:      }
  18:  }

So rather than hooking up the event where we created the Application Domain, we let the class do it by it self, and to "CurrentDomain" instead.

Ok so wait, doesn’t that cause an issue when creating it in the factory since it is now loading for the wrong domain? Well thankfully you are able to create objects within domains from the outside.

So creating the domain is now done as follows:

1:  string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath);
   2:  AppDomainSetup setup = new AppDomainSetup
   3:  {
   4:      ApplicationName = name,
   5:      ApplicationBase = applicationBase,
   6:      PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
   7:      PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
   8:      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
   9:  };
  10:   
  11:  Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
  12:  AppDomain domain = AppDomain.CreateDomain(name, evidence, setup);
  13:  domain.CreateInstanceFrom(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Host.AssemblyLoader.dll"),"Host.AssemblyLoader");

We don’t even care for maintaining a reference to the "AssemblyLoader" since it should pretty much be kept alive by hooking it self up to the Event.

Hopefully this can help some that has stumbled over the same problem, I see many workarounds where people then either just let Plug-ins be installed in same host directory, having all the necessary dependencies deployed along with the plug-in even though it isn’t something the plug-in knows it is dependant on and so forth.

The above at least enables us to install plug-ins away from our host application base which I think is nice.

If anyone have solved this differently, then please make a response, maybe we can find pros and cons in either way, or just discover a better solution.

If you have any questions or can’t get the above to work, then feel free to ask.

author: Jens Melgaard | posted @ Thursday, July 01, 2010 3:08 PM | Feedback (0)

这篇关于负载电流组装成不同的AppDomain的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆