C R*_*son 4 .net c# .net-assembly
我正在构建一个 WPF 工具,它将通过反射分析目标应用程序的程序集。
到目前为止,我一直在使用Assembly.Loadetc. 来加载目标程序集。这没问题,只是它有一些限制:我希望能够重建目标应用程序并“刷新”工具以重新分析新建的程序集。目前这不起作用,因为程序集在加载时被锁定,并且在工具退出之前不会释放。这也阻止了重新加载新建的程序集。
我相信我可以创建一个临时 AppDomain,将程序集加载到其中,执行我想做的反射,然后卸载域。
我遇到的问题是我无法让它工作。我尝试了多种变化并得到了结果,例如:
例如,遵循此处的建议:创建自定义 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");
}
}
Run Code Online (Sandbox Code Playgroud)
...并在调用方法中使用它:
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();
}
Run Code Online (Sandbox Code Playgroud)
...工具应用程序启动,但无法实例化 SimpleAssemblyLoader,报告与工具程序集关联的“文件未找到”异常 - 显然试图将工具的程序集加载到新域中(?)。
我做错了什么,我该如何解决?
这里有几个问题。首先,如文档中所述,PrivateBinPath应该相对于ApplicationBase- 否则它会被忽略。这就是你的情况发生的情况。由于它被忽略并ApplicationBase引用目标应用程序的目录而不是工具目录 - 应用程序域不知道从哪里加载您的SimpleAssemblyLoader. 要解决此问题,只需使用toolPathas ApplicationBase:
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;
}
Run Code Online (Sandbox Code Playgroud)
要不就
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;
}
Run Code Online (Sandbox Code Playgroud)
第二个问题
public Assembly LoadFrom(string path) {
ValidatePath(path);
return Assembly.LoadFrom(path);
}
Run Code Online (Sandbox Code Playgroud)
你打电话时
var assembly = assemblyLoader.LoadFrom(path);
Run Code Online (Sandbox Code Playgroud)
程序集在子应用程序域中加载,但是当您将此程序集作为结果返回(到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");
}
}
Run Code Online (Sandbox Code Playgroud)
然后
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);
Run Code Online (Sandbox Code Playgroud)