在Visual Studio扩展中创建域和加载程序集
本文关键字:加载 程序集 创建 Visual Studio 扩展 | 更新日期: 2023-09-27 18:00:03
这有点复杂,但我会尽量弄清楚。我正在创建一个visualstudio扩展,当你右键单击一个项目(并且该项目符合某些条件)时,该扩展会在上下文菜单中添加一个菜单项。菜单应该会触发一个过程:
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.dll
中定义,并由extension.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
。
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.dll
和Foo.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
以一种而非与域无关的方式被更多的代码加载,这意味着我创建的应用程序域必须加载它自己的副本。这有道理吗?有办法绕过吗?
经过大量搜索和多次尝试重新排列代码,当我找到ProviderBindingPathAttribute时,我最终破解了这个问题。
我把[ProvideBindingPath]
添加到我的包类中,一直困扰我的问题就消失了。我不再需要为我的域处理ApplicationBase
,只需要为我新的AppDomain
传递AppDomainSetup
的AppDomain.CurrentDomain.SetupInformation
。