我正在编写一个可视化工作室扩展,我完全不知道它是如何以及何时何地加载程序集。我有这个:
我的Visual Studio扩展项目(我们称之为MyExtension
)引用了几个程序集,包括一个名为Foo.dll
的程序集。
MyExtension
包含一个名为FooManager
的类,它将被实例化以响应单击的菜单项。
当FooManager
被实例化时,它会在当前解决方案中传递项目的输出路径,并创建一个应加载该程序集的AppDomain,如下所示:
public FooManager(string assemblyPath)
{
// The actual ApplicationBase of the current domain will be the one of VS and
// not of my plugin
// We need our new AppDomain to be able to find our assemblies
// without this even the CreateInstanceAndUnwrap will fail
var p = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var cachePath = Path.Combine(p, "Cache");
var pluginPath = Path.Combine(p, "Test");
if (Directory.Exists(cachePath))
{
Directory.Delete(cachePath, true);
}
if (Directory.Exists(pluginPath))
{
Directory.Delete(pluginPath, true);
}
Directory.CreateDirectory(cachePath);
Directory.CreateDirectory(pluginPath);
var newPath = Path.Combine(pluginPath, Path.GetFileName(assemblyPath));
File.Copy(assemblyPath, newPath, true);
var setup = new AppDomainSetup()
{
ApplicationBase = p,
ShadowCopyFiles = "true",
ShadowCopyDirectories = pluginPath,
CachePath = cachePath
};
domain = AppDomain.CreateDomain("MyTest_AppDomain", AppDomain.CurrentDomain.Evidence, setup);
// FooCollection is defined in MyExtension. but has several references
// to things defined in Foo.dll - it used MEF to load the assembly
// referenced by pluginPath
collection = domain.CreateInstanceAndUnwrap(
typeof(FooCollection).Assembly.FullName,
typeof(FooCollection).FullName,
false,
BindingFlags.Default,
null,
new object[] { pluginPath }, null, null) as FooCollection;
}
现在FooManager
的一个属性看起来像这样(FooInfo
定义在
Foo.dll
):
public IEnumerable<FooInfo> Spiders
{
get
{
return collection.Foos.Select(s => s.Metadata);
}
}
但是当我尝试访问时,我得到System.ArgumentException
的消息Object type cannot be converted to target type.
我知道如果同一个程序集的两个副本从不同的位置加载,我想,最终,这是这里发生了什么,但我无法弄清楚如何从同一个地方加载它。
所以经过这么多的努力(以上只是我最近的尝试),我想也许我可以序列化到byte[]
,然后再次反序列化,以避免类型的问题,所以我试过像这样的东西:
var msgBytes = collection.SerializeFooInfo();
var msg = FooInfo.DeserializeMessage(msgBytes);
我的序列化和反序列化只使用BinaryFormatter
(类标记为Serializable
)。序列化似乎有效,但在反序列化时,当我到达这里时:
public static List<FooInfo> DeserializeMessage(byte[] source)
{
using (var stream = new MemoryStream(source))
{
BinaryFormatter formatter = new BinaryFormatter();
var msg = formatter.Deserialize(stream);
return msg as List<FooInfo>;
}
}
msg
返回null。如果我尝试使用即时窗口在调试器中运行它,我看到Deserialize
向FileNotFoundException
投了一条消息:
无法加载程序集'C:\ Users \ matt.burland \ AppData \ Local \ Microsoft \ VisualStudio \ 14.0Exp \ ProjectAssemblies \ qesxy6ms01 \ Foo.dll'
但我不明白那条道路来自哪里。这不是我的扩展程序的安装位置,C:\Users\matt.burland\AppData\Local\Microsoft\VisualStudio\14.0Exp\Extensions\MyCompany\FooTools\1.0
并且已设置为ApplicationBase
的{{1}}并包含文件AppDomain
。那么为什么它试图从另一个神秘的位置加载呢?另一个位置似乎是动态创建的,只包含foo.dll
程序集。
我在Windows服务中做了一些非常类似的东西(并且使用了很多相同的类)并且它工作得很好,所以这似乎是Visual Studio扩展的特殊方式。任何人都可以在这里发光吗?
所以我认为这可能会有所帮助:http://geekswithblogs.net/onlyutkarsh/archive/2013/06/02/loading-custom-assemblies-in-visual-studio-extensions-again.aspx
但是如果我尝试在包类中附加一个AssemblyResolve处理程序,就像建议的那样,它不会被调用任何有趣的东西(这并不奇怪,它不是我试图从中加载的域),但如果我尝试如果我尝试这样的话,附加到我创建的新域名:
foo.dll
然后它失败,因为我的domain.AssemblyResolve += OnAssemblyResolve;
未标记为可序列化。所以我创建了一个仅用于绑定FooManager
的代理,但AssemblyResolve
永远不会触发。所以我在创建域时尝试不设置AssemblyResolve
,认为这会强制它必须尝试解析,但后来我无法在创建的域中创建我的代理类,因为它没有知道从哪里加载程序集!。