在Visual Studio扩展中创建域和加载程序集

本文关键字:加载 程序集 创建 Visual Studio 扩展 | 更新日期: 2023-09-27 18:00:03

这有点复杂,但我会尽量弄清楚。我正在创建一个visualstudio扩展,当你右键单击一个项目(并且该项目符合某些条件)时,该扩展会在上下文菜单中添加一个菜单项。菜单应该会触发一个过程:

1) 构建项目

2) 创建新的AppDomain

3) 将新程序集加载到新的AppDomain 中

4) 从新的AppDomain返回一些对象,该对象在第三个程序集中定义(即不是扩展程序集或构建的项目),但被引用。

我经常遇到的问题是数字4,并说服扩展第三个程序集的类型(我们称之为foo.dllFooAssembly)是同一类型。

在我的分机中(我们称之为extension.dll),我有这个:

// assemblyPath is the path to the dll I just build for the selected project
var setup = new AppDomainSetup()
{
    ApplicationBase = System.IO.Path.GetDirectoryName(assemblyPath)
};
domain = AppDomain.CreateDomain("Test_AppDomain", AppDomain.CurrentDomain.Evidence, setup);
// FooAssembly is defined in foo.dll and is reference by *both* the project.dll and
// but the extension.dll it exists both in the assemblyPath folder
// and typeof(FooAssembly).Assembly.Location which is NOT the same as
// the current AppDomain's CodeBase because the current CodeBase is the
// CodeBase of Visual Studio in this context
var obj = domain.CreateInstanceAndUnwrap(
    typeof(FooAssembly).Assembly.FullName,
    typeof(FooAssembly).FullName,
    false,
    BindingFlags.Default,
    null,
    new object[] { assemblyPath, typeof(FooAssembly).Assembly.Location }, null, null);
// This says the type is MarshalByRefObject
System.Diagnostics.Debug.WriteLine(obj.GetType().Name);
// this sets collection to null because it can't cast it
// which is essentially my problem
collection = obj as FooAssembly;

现在,我的FooAssembly,在foo.dll中定义,并由extension.dllproject.dll引用,看起来如下:

public class FooAssembly: MarshalByRefObject
{
    private Assembly _assembly;
    public FooAssembly(string assemblyPath, string parentPath)
    {
        AppDomain.CurrentDomain.AssemblyResolve += (o, e) =>
        {
            // this event never seems to get fired
            System.Diagnostics.Debug.WriteLine($"attempt to resolve {e.Name} for {e.RequestingAssembly.FullName}");
            return null;
        };
        // this didn't seem to help
        //Assembly.LoadFrom(parentPath);      // load spider assembly from same place (hopefully)...
        // I've tried LoadFrom as well, with the same result
        _assembly = Assembly.LoadFile(assemblyPath);
    }
    public override object InitializeLifetimeService()
    {
        return null;
    }
}

那么,这里到底发生了什么,我该如何让它表现出来呢?我需要我创建的AppDomain来加载我给它的程序集,然后从扩展获取它的同一位置加载它的依赖项(或者至少是foo.dll)。或者至少我需要它知道我的透明代理FooAssembly

EDIT:我尝试将project.dll复制到与extension.dll相同的文件夹中,这样它就必须从相同的位置加载。这没有任何区别,所以我尝试将这些行粘贴到我的FooAssembly构造函数中:

System.Diagnostics.Debug.WriteLine(AppDomain.CurrentDomain.FriendlyName);
System.Diagnostics.Debug.WriteLine(this.GetType().FullName);
System.Diagnostics.Debug.WriteLine(this.GetType().Assembly.CodeBase);

我可以看到a)我在创建的域中。b) 我有正确的类,c)我似乎有正确的路径?!?CodeBase返回为:

file:///C:/USERS/MATT.BURLAND/APPDATA/LOCAL/MICROSOFT/VISUALSTUDIO/14.0EXP/EXTENSIONS/SOMECO/FOOTOOLS/1.0/Foo.DLL

因此,在我的默认域中,在创建域之后和尝试创建对象之前,我会这样做:

System.Diagnostics.Debug.WriteLine(AppDomain.CurrentDomain.FriendlyName);
System.Diagnostics.Debug.WriteLine(typeof(SpiderAssembly).FullName);
System.Diagnostics.Debug.WriteLine(typeof(SpiderAssembly).Assembly.CodeBase);

我的代码库是:

file:///C:/USERS/MATT.BURLAND/APPDATA/LOCAL/MICROSOFT/VISUALSTUDIO/14.0EXP/EXTENSIONS/SOMECO/FOOTOOLS/1.0/foo.dll

这里唯一的区别是foo.dllFoo.DLL的外壳,我认为这应该不重要。

我尝试过这个(受此启发):

dynamic dobj = obj;
string l2 = dobj.GetType().Assembly.CodeBase;
System.Diagnostics.Debug.WriteLine(l2);

它显然认为这是从file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll加载的MarshalByRefObject,所以它似乎没有正确展开?

编辑:在阅读了更多关于这方面的内容后,我有了一个理论。为了在域之间共享foo.dll,它必须"与域无关"地加载,我目前的理论是,我的扩展可能被visual studio隔离在它自己的域中,因此foo.dll以一种而非与域无关的方式被更多的代码加载,这意味着我创建的应用程序域必须加载它自己的副本。这有道理吗?有办法绕过吗?

在Visual Studio扩展中创建域和加载程序集

经过大量搜索和多次尝试重新排列代码,当我找到ProviderBindingPathAttribute时,我最终破解了这个问题。

我把[ProvideBindingPath]添加到我的包类中,一直困扰我的问题就消失了。我不再需要为我的域处理ApplicationBase,只需要为我新的AppDomain传递AppDomainSetupAppDomain.CurrentDomain.SetupInformation