我可以使默认的AppDomain使用某些程序集的卷影副本吗?

时间:2009-09-03 12:43:30

标签: .net appdomain revit

我想要这样做的简短说明:

我正在为Autodesk Revit Architecture 2010编写插件。测试我的插件代码非常麻烦,因为我必须为每个调试会话重新启动Autodesk,手动加载Revit项目,单击Add-Ins选项卡然后启动我的插件。这只是花了太长时间。

我写了第二个插件来托管IronPython解释器。通过这种方式,我可以使用Revit提供的API。但最终,代码必须在C#中重写 - 并进行调试。

很简单,我想:只需从IronPython脚本加载插件DLL并进行练习即可。这确实有效,但一旦加载,我无法在Visual Studio中重新编译,因为DLL现在已加载到Revits AppDomain中。

很简单,我想(在StackOverflow的帮助下):只需在新的AppDomain中加载DLL即可。唉,RevitAPI对象无法封送到另一个AppDomain,因为它们不会扩展MarshalByRefObject

我想我可能会使用影子副本。 ASP.NET似乎正在这样做。但是阅读MSDN上的文档,我似乎只能在创建 AppDomain时指定它。

我可以为当前(默认)AppDomain更改此设置吗?我可以强制它使用来自特定目录的DLL的卷影副本吗?

3 个答案:

答案 0 :(得分:5)

我不知道你要做什么,但有一些不赞成使用的方法可以在当前的AppDomain上打开ShadowCopy。

AppDomain.CurrentDomain.SetCachePath(@"C:\Cache");
AppDomain.CurrentDomain.SetShadowCopyPath(AppDomain.CurrentDomain.BaseDirectory);
AppDomain.CurrentDomain.SetShadowCopyFiles();

答案 1 :(得分:2)

有时无法修改Main()方法代码,因为例如,您正在编写插件并由管理器实例化。

在这种情况下,我建议你将程序集和pdb(以及AssemblyResolve事件中的从属程序)复制到临时位置,然后使用Assembly.LoadFile()(而不是LoadFrom())从那里加载它们。

优点: - 没有DLL锁定。 - 每次重新编译目标程序集时,都可以访问新版本(这就是.LoadFile())。 - 整个程序集在AppDomain.CurrentDomain中完全可用。

缺点: - 文件复制是必要的。 - 装配不能卸载,这可能不方便,因为资源没有被释放。

此致

PD:此代码可以完成工作。

/// <summary>
/// Loads an assembly without locking the file
/// Note: the assemblys are loaded in current domain, so they are not unloaded by this class
/// </summary>
public class AssemblyLoader : IDisposable
{
    private string _assemblyLocation;
    private string _workingDirectory;
    private bool _resolveEventAssigned = false;

    /// <summary>
    /// Creates a copy in a new temp directory and loads the copied assembly and pdb (if existent) and the same for referenced ones. 
    /// Does not lock the given assembly nor pdb and always returns new assembly if recopiled.
    /// Note: uses Assembly.LoadFile()
    /// </summary>
    /// <param name="assemblyOriginalPath"></param>
    /// <returns></returns>
    public Assembly LoadFileCopy(string assemblyLocation)
    {
        lock (this)
        {
            _assemblyLocation = assemblyLocation;

            if (!_resolveEventAssigned)
            {
                _resolveEventAssigned = true;

                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyFileCopyResolveEvent);
            }

            //  Create new temp directory
            _workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
            Directory.CreateDirectory(_workingDirectory);

            //  Generate copy
            string assemblyCopyPath = Path.Combine(_workingDirectory, Path.GetFileName(_assemblyLocation));
            System.IO.File.Copy(_assemblyLocation, assemblyCopyPath, true);

            //  Generate copy of referenced assembly debug info (if existent)
            string assemblyPdbPath = _assemblyLocation.Replace(".dll", ".pdb");
            if (File.Exists(assemblyPdbPath))
            {
                string assemblyPdbCopyPath = Path.Combine(_workingDirectory, Path.GetFileName(assemblyPdbPath));
                System.IO.File.Copy(assemblyPdbPath, assemblyPdbCopyPath, true);
            }

            //  Use LoadFile and not LoadFrom. LoadFile allows to load multiple copies of the same assembly
            return Assembly.LoadFile(assemblyCopyPath);
        }
    }

    /// <summary>
    /// Creates a new copy of the assembly to resolve and loads it
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    private Assembly AssemblyFileCopyResolveEvent(object sender, ResolveEventArgs args)
    {
        string referencedAssemblyFileNameWithoutExtension = System.IO.Path.GetFileName(args.Name.Split(',')[0]);

        //  Generate copy of referenced assembly
        string referencedAssemblyPath = Path.Combine(Path.GetDirectoryName(_assemblyLocation), referencedAssemblyFileNameWithoutExtension + ".dll");
        string referencedAssemblyCopyPath = Path.Combine(Path.GetDirectoryName(args.RequestingAssembly.Location), referencedAssemblyFileNameWithoutExtension + ".dll");
        System.IO.File.Copy(referencedAssemblyPath, referencedAssemblyCopyPath, true);

        //  Generate copy of referenced assembly debug info (if existent)
        string referencedAssemblyPdbPath = Path.Combine(Path.GetDirectoryName(_assemblyLocation), referencedAssemblyFileNameWithoutExtension + ".pdb");
        if (File.Exists(referencedAssemblyPdbPath))
        {
            string referencedAssemblyPdbCopyPath = Path.Combine(Path.GetDirectoryName(args.RequestingAssembly.Location), referencedAssemblyFileNameWithoutExtension + ".pdb");
            System.IO.File.Copy(referencedAssemblyPath, referencedAssemblyCopyPath, true);
        }

        //  Use LoadFile and not LoadFrom. LoadFile allows to load multiple copies of the same assembly
        return Assembly.LoadFile(referencedAssemblyCopyPath);
    }


    public void Dispose()
    {
        Dispose(true);
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_resolveEventAssigned)
            {
                AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(AssemblyFileCopyResolveEvent);

                _resolveEventAssigned = false;
            }
        }
    }
}

答案 2 :(得分:1)

现在有一个Revit插件可以动态加载/卸载其他Revit插件,这样您就可以更改,重新编译和测试,而无需重新打开Revit项目。我在Building Coder blog找到了它。它随附Revit SDK。