C# 使用AppDomain临时加载程序集
我正在构建一个WPF工具,它将通过反射分析目标应用程序的程序集 到目前为止,我一直在使用C# 使用AppDomain临时加载程序集,c#,.net,.net-assembly,C#,.net,.net Assembly,我正在构建一个WPF工具,它将通过反射分析目标应用程序的程序集 到目前为止,我一直在使用Assembly.Load等加载目标程序集。这是可以的,只是它有几个限制:我希望能够重建目标应用程序并“刷新”工具以重新分析新构建的程序集。这目前不起作用,因为装配在加载时被锁定,直到工具退出后才释放。这也避免了重新加载新构建的部件 我相信我可以创建一个临时AppDomain,将程序集加载到其中,执行我想要执行的反射,然后卸载该域 我遇到的问题是我无法让它工作。我尝试了许多变化,得到了如下结果: 加载到当前
Assembly.Load
等加载目标程序集。这是可以的,只是它有几个限制:我希望能够重建目标应用程序并“刷新”工具以重新分析新构建的程序集。这目前不起作用,因为装配在加载时被锁定,直到工具退出后才释放。这也避免了重新加载新构建的部件
我相信我可以创建一个临时AppDomain,将程序集加载到其中,执行我想要执行的反射,然后卸载该域
我遇到的问题是我无法让它工作。我尝试了许多变化,得到了如下结果:
- 加载到当前应用程序域,而不是我显式创建的应用程序域
- 加载请求的程序集时出错
- 加载刀具组件时出错(?)
SimpleAssemblyLoader
,因此:
public class SimpleAssemblyLoader : MarshalByRefObject
{
public Assembly Load(string path)
{
ValidatePath(path);
return Assembly.Load(path);
}
public Assembly LoadFrom(string path)
{
ValidatePath(path);
return Assembly.LoadFrom(path);
}
public Assembly UnsafeLoadFrom(string path)
{
ValidatePath(path);
return Assembly.UnsafeLoadFrom(path);
}
private void ValidatePath(string path)
{
if (path == null) throw new ArgumentNullException(nameof(path));
if (!System.IO.File.Exists(path))
throw new ArgumentException($"path \"{path}\" does not exist");
}
}
。。。并在调用方法中使用它,因此:
private static AppDomain MakeDomain(string name, string targetPath, string toolPath)
{
var appDomain =
AppDomain.CreateDomain(name, AppDomain.CurrentDomain.Evidence, new AppDomainSetup
{
ApplicationBase = targetPath,
PrivateBinPath = toolPath,
LoaderOptimization = LoaderOptimization.MultiDomainHost
},
new PermissionSet(PermissionState.Unrestricted));
return appDomain;
}
/// <summary>
///
/// </summary>
/// <param name="targetPath">Location of assemblies to analyze</param>
/// <param name="toolPath">Location of this tool</param>
/// <param name="file">Filename of assembly to analyze</param>
/// <returns></returns>
public string[] Test(string targetPath, string toolPath, string file)
{
var dom = MakeDomain("TestDomain", targetPath, toolPath);
var assemblyLoader = (SimpleAssemblyLoader)dom.CreateInstanceAndUnwrap(typeof(SimpleAssemblyLoader).Assembly.FullName, typeof(SimpleAssemblyLoader).FullName);
var path = Path.Combine(targetPath, file);
var assembly = assemblyLoader.LoadFrom(path);
var types = assembly.GetTypes();
List<string> methods = new List<string>();
foreach (var type in types)
{
foreach (var method in type.GetMethods(BindingFlags.Instance|BindingFlags.Public))
{
methods.Add(method.Name);
}
}
AppDomain.Unload(dom);
return methods.ToArray();
}
私有静态AppDomain MakedDomain(字符串名称、字符串目标路径、字符串工具路径)
{
var appDomain=
AppDomain.CreateDomain(名称,AppDomain.CurrentDomain.Evidence,新AppDomainSetup
{
ApplicationBase=targetPath,
PrivateBinPath=刀具路径,
LoaderOptimization=LoaderOptimization.MultiDomainHost
},
新的PermissionSet(PermissionState.Unrestricted));
返回appDomain;
}
///
///
///
///要分析的部件的位置
///此工具的位置
///要分析的程序集的文件名
///
公共字符串[]测试(字符串目标路径、字符串工具路径、字符串文件)
{
var dom=MakeDomain(“TestDomain”、targetPath、toolPath);
var assemblyLoader=(SimpleAssemblyLoader)dom.CreateInstanceAndUnwrap(typeof(SimpleAssemblyLoader.Assembly.FullName),typeof(SimpleAssemblyLoader.FullName);
var path=path.Combine(targetPath,文件);
var assembly=assemblyLoader.LoadFrom(路径);
var types=assembly.GetTypes();
列表方法=新列表();
foreach(类型中的变量类型)
{
foreach(type.GetMethods(BindingFlags.Instance | BindingFlags.Public)中的var方法)
{
方法.添加(方法.名称);
}
}
卸载(dom);
返回方法。ToArray();
}
。。。工具应用程序启动,但无法实例化SimpleAssemblyLoader,报告与工具程序集关联的“未找到文件”异常-显然是试图将工具程序集加载到新域(?)
我做错了什么,如何修复它?这里有几个问题。首先,如文档中所述,
PrivateBinPath
应该与ApplicationBase
相对,否则将忽略它。这就是你的情况。由于它被忽略,并且ApplicationBase
指的是带有目标应用程序的目录,而不是工具目录-应用程序域不知道从何处加载您的SimpleAssemblyLoader
。要解决此问题,只需使用刀轨
作为应用程序库
:
private static AppDomain MakeDomain(string name, string toolPath)
{
var appDomain =
AppDomain.CreateDomain(name, AppDomain.CurrentDomain.Evidence, new AppDomainSetup
{
ApplicationBase = toolPath,
LoaderOptimization = LoaderOptimization.MultiDomainHost
},
new PermissionSet(PermissionState.Unrestricted));
return appDomain;
}
或者只是
private static AppDomain MakeDomain(string name) {
var appDomain =
AppDomain.CreateDomain(name, AppDomain.CurrentDomain.Evidence, new AppDomainSetup {
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
LoaderOptimization = LoaderOptimization.MultiDomainHost
},
new PermissionSet(PermissionState.Unrestricted));
return appDomain;
}
第二个问题
public Assembly LoadFrom(string path) {
ValidatePath(path);
return Assembly.LoadFrom(path);
}
当你打电话的时候
var assembly = assemblyLoader.LoadFrom(path);
程序集已加载到子应用程序域中,但当您返回此程序集时(返回到var Assembly
)-当前应用程序域也将加载它(很好-将尝试加载并失败,因为它不知道此程序集位于何处)。您不应该像这样返回程序集,因为它要么在当前域和子域中都被加载(坏),要么在当前域中加载失败。相反-将整个方法放入MarshalByRefObject
中,以在子域中完全执行它并返回结果:
public class WpfInspector : MarshalByRefObject {
public string[] Inspect(string path) {
ValidatePath(path);
var assembly = Assembly.LoadFrom(path);
var types = assembly.GetTypes();
List<string> methods = new List<string>();
foreach (var type in types) {
foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Public)) {
methods.Add(method.Name);
}
}
return methods.ToArray();
}
private void ValidatePath(string path) {
if (path == null) throw new ArgumentNullException(nameof(path));
if (!System.IO.File.Exists(path))
throw new ArgumentException($"path \"{path}\" does not exist");
}
}
什么是刀具路径?使用工具的目录路径?@Evk是。我不清楚我是否需要使用PrivateBinPath或者我是否误用了它。为了回答您的问题,我编辑了示例代码。谢谢,您提供了我解决问题所需的内容。对您的答案只有一个更正:
Assembly.LoadFrom
仍在加载到父域。我必须使用AppDomain.Load
方法,这需要一些其他工作,但我已经在另一个SO线程中找到了如何做到这一点Assembly.LoadFrom
从我尝试的所有不同排列中潜入到我的问题中。@CRobinson如果行得通,它就行了。但请注意,AppDomain.Load
不建议用于将任何内容加载到其他应用程序域-仅加载到主应用程序域。不确定你是如何使用它来实现你的目标的。和Assembly.LoadFrom
不应加载到父应用程序域,除非您从中返回该程序集或任何类型。例如,在测试期间,我能够在卸载应用程序域后自由更改目标文件-目标程序集未加载到父应用程序域中,因此未锁定。当然我指的是在子域中执行的Assembly.LoadFrom
(从marshallbyrefobject
)。这应该是我接受的答案。谢谢,您解决了我的问题!
var dom = MakeDomain("TestDomain", toolPath);
var inspector = (WpfInspector)dom.CreateInstanceAndUnwrap(typeof(WpfInspector).Assembly.FullName, typeof(WpfInspector).FullName);
var path = Path.Combine(targetPath, file);
var methods = inspector.Inspect(path);
AppDomain.Unload(dom);