10

我为什么要这样做的简短解释:

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

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

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

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

我想我可能会对影子副本有所了解。ASP.NET 似乎正在这样做。但是阅读 MSDN 上的文档,似乎我只能在创建AppDomain 时指定这一点。

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

4

3 回答 3

6

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

AppDomain.CurrentDomain.SetCachePath(@"C:\Cache");
AppDomain.CurrentDomain.SetShadowCopyPath(AppDomain.CurrentDomain.BaseDirectory);
AppDomain.CurrentDomain.SetShadowCopyFiles();
于 2009-09-03T13:09:14.980 回答
3

有时无法修改 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;
            }
        }
    }
}
于 2010-08-21T22:18:52.863 回答
1

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

于 2012-12-07T18:51:11.933 回答