为COM Interop注册的.NET程序集与一些将在程序集中加载类型的反射代码相结合会导致一些奇怪的行为。我已经分析了调试器中发生了什么,我搜索了网络以找到解决方案。 我找到了许多有帮助的文章,但没有任何东西能让我完全解决问题。
问题概述
有一个不是.NET的exe文件(在我的例子中是一个VB6应用程序)。它位于文件夹A.
我在文件夹B中有一些.NET dll。其中一个是COM dll
exe文件实例化COM .NET程序集的.NET对象的COM实例。
然后AppDomain的主路径是文件夹A,但我希望它是文件夹B.因为它是文件夹A,所以我的.NET代码中的一些反射类型加载失败。
以下是详细信息:
我有一个VB6应用程序。 exe文件位于文件夹A中。 在里面我有一个VB6声明
Set DotNetE2 = CreateObject("MyDotNet.E2")
这将创建一个我的.NET类的实例,它被注册为COM互操作。 .NET类的标题如下所示:
namespace MyDotNet.E2.COM
{
[ComVisible(true)]
[Guid("776FF4EA-2F40-4E61-8EF3-08250CB3712B")]
[ProgId("MyDotNet.E2")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class E2
{
我的.NET程序集“MyDotNet.E2.COM.dll”位于文件夹B中。 此程序集引用了另外两个名为E3和E4的.NET程序集,它们位于同一文件夹中。 E3没有E4的引用。 我能够按预期在这些程序集中执行代码,因此引用正常。 到现在为止还挺好。 现在,我在E3中有一些代码试图对E4中的类型进行一些反思。 这失败了。
以下是代码:
string dotnetPath = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location);
string mainDir = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
string otherDirs = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
Assembly assembly = Assembly.LoadFrom(Path.Combine(dotnetPath, "E4.dll"));
Type mytype = assembly.GetType("MyDotnet.E4.MyForm");
观察
dotnetPath与mainDir不同 mytype为null。预期结果是类型实例 如果我将exe文件与.NET程序集一起移动到文件夹B,它就可以工作。然后dotnetPath和mainDir是一样的 如果我在E2而不是E4中执行反射代码,它就可以工作,即使dotnetPath!= mainDir 但在完整的情况下,我概述了它不起作用。
我已经找到了一些提示,可以通过在配置文件中指定其他文件夹,将其他文件夹添加到PrivateBinPath中的AppDomain。但我没有成功。我试图将配置文件添加到我的COM .NET文件和我的VB6 exe文件中,但我的PrivateBinPath属性没有发生任何变化。这是我试图添加的配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="..\..\FolderB"/>
</assemblyBinding>
</runtime>
</configuration>
不要让我重组我的装配和类型。该项目相当复杂,这是最好的架构。
答案 0 :(得分:1)
我自己设法解决了这个问题。 关键是AssemblyResolve事件,如果系统无法解析程序集,则会触发该事件。 这里解释如下: http://msdn.microsoft.com/library/system.appdomain.assemblyresolve
似乎.NET框架中的一个错误我必须使用此事件,但这种解决方法结果相当不错。
string dotnetPath = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location);
string mainDir = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
if (!mainDir.Equals(dotnetPath, StringComparison.CurrentCultureIgnoreCase))
{
// This will happen if .NET process is fired
// from a COM call from another folder.
// Solution: an event is fired if assembly-resolving fails.
AppDomain.CurrentDomain.AssemblyResolve +=
new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
事件处理程序非常简单:
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.FullName == args.Name) return assembly;
}
return null;
}