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

时间:2016-08-02 15:14:31

标签: c# visual-studio-2015 vsix

这有点复杂,但我会尽量让它变得清晰。我正在创建一个visual studio扩展,当您右键单击项目时(项目符合某些条件),该菜单项会添加到上下文菜单中。该菜单应该触发一个过程:

1)构建项目

2)创建一个新的AppDomain

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

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

我一直遇到的问题是使用数字4并且说服扩展,第三个程序集中的类型(我们称之为foo.dll和类型FooAssembly)是相同的类型。

在我的扩展程序中(让我们称之为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.dllextension.dll引用的project.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

编辑:我尝试将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的套管,我认为这不应该是重要的。

我试过这个(灵感来自this):

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

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

编辑:在阅读了更多内容之后,我有一个理论。对于域之间共享的foo.dll,它必须被加载为“域中立”,而我目前的理论是我的扩展可能被visual studio隔离在它自己的域中,因此foo.dll被加载以域中立的方式处理更多代码,这意味着我创建的应用程序域必须加载它自己的副本。这有任何意义吗?有办法吗?

1 个答案:

答案 0 :(得分:1)

经过大量搜索并多次尝试重新排列代码后,当我找到ProvideBindingPathAttribute时,我最终破解了这一点。

我将[ProvideBindingPath]添加到我的包类中,一直困扰着我的问题就消失了。我不再需要为我的域ApplicationBase弄乱,并且可以为我的新AppDomain.CurrentDomain.SetupInformation传递AppDomainSetup {。}}。