gpt4 book ai didi

c# - 将当前程序集加载到不同的AppDomain中

转载 作者:行者123 更新时间:2023-12-03 00:37:14 24 4
gpt4 key购买 nike

我创建了具有不同基本目录的AppDomain。但是,如果在基本目录中没有当前执行程序集的副本,我似乎无法将当前执行程序集加载到另一个AppDomain中。我什至尝试从字节加载它。

当我尝试加载时,我没有例外,但是当我尝试使用时:

domain.DoCallBack(new CrossAppDomainDelegate(... 


我得到:


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


我的代码如下:

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;
}


更新:

如果我在输出中看到这个,似乎程序集已加载

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

但我仍然有例外...我不明白


未处理System.IO.FileNotFoundException消息=无法加载
文件或程序集“ TaskExecuter,版本= 1.0.4244.31921,
文化=中性,PublicKeyToken =空”或其依赖项之一。的
系统找不到指定的文件。来源= mscorlib
FileName = TaskExecuter,Version = 1.0.4244.31921,Culture = neutral,
PublicKeyToken =空FusionLog ====预绑定状态信息===
日志:用户= Peter-PC \ Peter日志:DisplayName = TaskExecuter,
版本= 1.0.4244.31921,文化=中性,PublicKeyToken =空
(完全指定)日志:Appbase =
file:/// C:/ ProgramData / TaskExecuter / TaskLib / uTorrentTasks日志:初始
PrivatePath = C:\ ProgramData \ TaskExecuter \ TaskLib \ uTorrentTasks \ Libs
调用程序集:(未知)。
=== LOG:此绑定在默认的加载上下文中启动。日志:使用
应用程序配置文件:d:\ users \ peter \ documents \ visual studio
2010 \ Projects \ TaskExecuter \ TaskExecuter.Terminal \ bin \ Release \ TaskExecuter.Terminal.vshost.exe.Config
日志:使用主机配置文件:日志:使用机器配置
来自的文件
C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ config \ machine.config。
日志:目前未将政策应用于引用(私有,
自定义,部分或基于位置的程序集绑定)。日志:尝试
下载新网址
文件:/// 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
fileName,字符串codeBase,证据程序集安全性,RuntimeAssembly
locationHint,StackCrawlMark&stackMark,布尔throwOnFileNotFound,
布尔值用于自省,布尔值抑制安全性检查)
在System.Reflection.RuntimeAssembly.nLoad(AssemblyName
fileName,字符串codeBase,证据程序集安全性,RuntimeAssembly
locationHint,StackCrawlMark&stackMark,布尔throwOnFileNotFound,
布尔值用于自省,布尔值抑制安全性检查)

System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName
assemblyRef,证据assemblySecurity,StackCrawlMark和stackMark,
布尔值用于自省,布尔值抑制安全性检查)
在System.Reflection.RuntimeAssembly.InternalLoad(String
assemblyString,证据assemblySecurity,StackCrawlMark&stackMark,
自省的布尔值)
在System.Reflection.Assembly.Load(String assemblyString)

System.Runtime.Serialization.FormatterServices.LoadAssemblyFromString(String
assemblyName)

System.Reflection.MemberInfoSerializationHolder..ctor(SerializationInfo
信息,StreamingContext上下文)
在System.AppDomain.DoCallBack(CrossAppDomainDelegate
callBackDelegate)
在TaskExecuter.AppDomainHelper.CreateAppDomain(String dir,
字符串名称)在d:\ users \ peter \ documents \ visual studio中
2010 \ Projects \ TaskExecuter \ TaskExecuter \ AppDomainHelper.cs:第50行
在位于的TaskExecuter.TaskManagment.TaskFinder.Probe()中
d:\ users \ peter \ documents \ visual studio
2010 \ Projects \ TaskExecuter \ TaskExecuter \ TaskManagment \ TaskFinder.cs:line
29
在位于的TaskExecuter.TaskManagment.TaskManager.LoadTasks()中
d:\ users \ peter \ documents \ visual studio
2010 \ Projects \ TaskExecuter \ TaskExecuter \ TaskManagment \ TaskManager.cs:line
63
在TaskExecuter.TaskManagment.TaskManager.Start()中
d:\ users \ peter \ documents \ visual studio
2010 \ Projects \ TaskExecuter \ TaskExecuter \ TaskManagment \ TaskManager.cs:line
95
在TaskExecuter.Terminal.Program.Main(String [] args)中
d:\ users \ peter \ documents \ visual studio
2010 \ Projects \ TaskExecuter \ TaskExecuter.Terminal \ Program.cs:第16行
在System.AppDomain._nExecuteAssembly(RuntimeAssembly程序集,
字符串[]参数)
在System.AppDomain.ExecuteAssembly(String assemblyFile,
证据AssemblySecurity,String []参数)

Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
在System.Threading.ThreadHelper.ThreadStart_Context(Object
州)
在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,以便可以轻松更新生成该exe的主要应用程序。基本方法是使用childAppDomain.CreateInstanceFrom创建一个类型,该类型在构造函数中安装程序集解析事件处理程序。我的代码看起来像

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);


以及创建所需的AssemblyResolve处理程序的类型(下面的博客文章描述了为什么需要另一种类型)

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;
}
}


这是原始博客文章:

应用程序域很难...

您曾经在.NET中使用Application Domain吗?一开始似乎并不那么困难,但是一旦您了解了它们,便开始意识到所有的小困难。

只要您不移出Host AppDomains.BaseDirectory,一切都会正常运行,但是在我们的案例中,我们希望将插件部署在“ C:\ My Plug-ins”位置,而主机应用程序将运行在“ C:\ Program Files \ My App”,因为我们可能会遇到从AppDomain到某些主机程序集的依赖性,这显然是不可避免的。

经典
这是一些简单的代码,也是我们的首次尝试。

 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);


看起来非常简单,但是由于“ ApplicationBase”与“ AppDomain.CurrentDomain.BaseDirectory”不同,因此我们遇到了一个众所周知的例外。

System.IO.FileNotFoundException:无法加载文件或程序集“ Host.Services,版本= 1.0.0.0,区域性=中性,PublicKeyToken =空”或其依赖项之一。该系统找不到指定的文件。

如果您使用过任何类型的动态加载程序集,我都可以肯定您很熟悉。问题是主机应用程序域中知道“ Host.Services”,因为它存储在“ C:\ Program Files \ My App”中,正在寻找它的应用程序域在“ C:\ My Plug- ins”。

好吧,我们认为我们指示它也要查看“ AppDomain.CurrentDomain.BaseDirectory”,该目录将为“ C:\ Program Files \ My App”,但事实并非如此。

AppDomain.Assembly决心解救吗?
好的,所以我们之前一直在处理这些怪癖,因此我们知道如何使用“ AppDomain.AssemblyResolve”手动解析其自身无法处理的AppDomain的任何程序集。

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;


那应该行得通,好吧,我们认为是这样,再一次我们错了,现在发生的是,实际上并没有深入到初始化应用程序域并使用它,而是在我们挂接事件处理程序以解决程序集的地方失败了。

同样,该异常看起来非常类似于前面提到的异常,但是这次它找不到包含具有我们在上面代码片段的最后一行中设置的“解析”处理程序的Type的Assembly。

AppDomain.Load然后!
好的,因此很明显,在挂接事件处理程序时,应用程序域需要知道处理该事件的对象的类型,当您考虑该事件时,这实际上是可以理解的,因此,如果应用程序域甚至找不到那个和负载,我们真的无法处理任何事情。

那么下一步是什么?我们的想法是手动指示“应用程序域”加载一个浅层程序集,该浅层程序集没有在GAC中可以找到的任何其他依赖项,并连接一个事件处理程序。

 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;


使用一个非常简单的小类,如下所示,不要介意奇怪的解决行为。

 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: }


所以,是或否?……不!……同样的问题。

事情要简单得多!
实际上,当我们设法使它工作时,事情变得简单得多。

我不能说.NET团队对它应该工作的设想是多么准确,我们无法真正找到使用“ PrivateBinPath”和“ PrivateBinPathProbe”的任何有用的东西。好吧,我们现在就使用它们,并使它们按我们预期的那样工作!

因此,我们将“ AssemblyLoader”类更改为如下所示:

   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: }


因此,与其在创建应用程序域的事件上进行连接,不如让类由它自己完成,并改为“ CurrentDomain”。

好的,等等,因为它现在正在为错误的域加载,所以在工厂创建它时不会引起问题吗?好吧,幸运的是,您能够从外部在域内创建对象。

因此,现在按照以下步骤创建域:

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");


我们甚至不需要维护对“ AssemblyLoader”的引用,因为应该通过将其自身挂接到事件上来使其几乎保持活动状态。

希望这可以对遇到相同问题的人有所帮助,我看到了许多变通办法,然后人们要么只是将插件安装在同一主机目录中,就将所有必要的依赖项与该插件一起部署,即使它不是。插件知道它所依赖的东西等等。

上面的内容至少使我们能够从主机应用程序库之外安装插件,我认为这很好。

如果有人以不同的方式解决了这个问题,请做出回应,也许我们可以以任何一种方式找到利弊,或者只是找到更好的解决方案。

如果您有任何疑问或无法解决上述问题,请随时提问。

作者:Jens Melgaard |发表@ 2010年7月1日,星期四,下午3:08 |反馈(0)

关于c# - 将当前程序集加载到不同的AppDomain中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6893137/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com