我正在编写Visual Studio扩展程序,我对如何以及在何时何地加载程序集感到困惑。我有这个:
我的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
返回为空。如果尝试使用即时窗口在调试器中运行它,则会看到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
的AppDomain
,并包含文件foo.dll
。那么,为什么要尝试从其他神秘位置加载呢?另一个位置似乎是动态创建的,并且仅包含foo.dll
程序集。
我已经在Windows服务中(与此同时使用了许多相同的类)做了非常类似的事情,并且工作得很好,因此,这似乎是Visual Studio扩展方式的特殊之处。有人可以在这里发光吗?
但是如果我尝试按照建议的那样在程序包类中附加AssemblyResolve处理程序,则它不会被调用任何有趣的东西(这不足为奇,这不是我尝试加载的域),但是如果我尝试附加到我创建的新域,然后尝试以下操作:
domain.AssemblyResolve += OnAssemblyResolve;
然后失败,因为我的FooManager
未标记为可序列化。因此,我创建了一个仅用于绑定AssemblyResolve
的代理,但AssemblyResolve
从未触发。因此,我尝试在创建域时不设置ApplicationBase
,以为这将迫使它不得不尝试解析,但是后来我无法在创建的域中创建代理类,因为它不知道在哪里从中加载程序集!。
@@ Matt Burland-您是否能够解决此问题?因为,目前我也面临着同样的问题。如果您可以共享解决方案,则IT确实会有所帮助。
预先感谢。