Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/gwt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用AppDomain临时加载程序集_C#_.net_.net Assembly - Fatal编程技术网

C# 使用AppDomain临时加载程序集

C# 使用AppDomain临时加载程序集,c#,.net,.net-assembly,C#,.net,.net Assembly,我正在构建一个WPF工具,它将通过反射分析目标应用程序的程序集 到目前为止,我一直在使用Assembly.Load等加载目标程序集。这是可以的,只是它有几个限制:我希望能够重建目标应用程序并“刷新”工具以重新分析新构建的程序集。这目前不起作用,因为装配在加载时被锁定,直到工具退出后才释放。这也避免了重新加载新构建的部件 我相信我可以创建一个临时AppDomain,将程序集加载到其中,执行我想要执行的反射,然后卸载该域 我遇到的问题是我无法让它工作。我尝试了许多变化,得到了如下结果: 加载到当前

我正在构建一个WPF工具,它将通过反射分析目标应用程序的程序集

到目前为止,我一直在使用
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);