从importlibed ed未注册的类型库访问引用ITypeInfo的ITypeInfo会导致TYPE_E_CANTLOADLIBRARY错误

时间:2016-11-22 19:19:50

标签: c# .net ole midl ole-automation

我正在使用.NET中的Automation API(System.Runtime.InteropServices.ComTypes)来检查我自己生成的类型库Bar.tlb(见下文)。此类型库声明了一个接口IBar,它继承自接口IFoo,该接口在导入的类型库Foo.tlb中定义。检查代表ITypeInfo的{​​{1}}会导致异常。 (代码如下。)

在我开始使用代码之前,我的方法是如何生成IBar类型的库。

Bar.idl:

Bar.tlb

Foo.idl:

[uuid(32E81FDD-BCB0-481B-AD3C-3ED04BFA7D1F)]
library Bar
{
    importlib("Foo.tlb");

    [uuid(CF062BE8-86D2-4D9B-8D1D-D889A77DA876)]
    interface IBar : IFoo { };
}

我使用以下命令编译了两个IDL文件,这些命令成功,没有任何错误或警告:

[uuid(22E81FDD-BCB0-481B-AD3C-3ED04BFA7D1E)]
library Foo
{
    importlib("stdole32.tlb");

    [uuid(BF062BE8-86D2-4D9B-8D1D-D889A77DA875)]
    interface IFoo : IUnknown { };
}

现在我要做的是:

midl.exe /mktyplib203 /env win32 /i … /tlb Foo.tlb Foo.idl
midl.exe /mktyplib203 /env win32 /i … /tlb Bar.tlb Bar.idl

在标有using System.Runtime.InteropServices; using ITypeLib = System.Runtime.InteropServices.ComTypes.ITypeLib; using ITypeInfo = System.Runtime.InteropServices.ComTypes.ITypeInfo; static class Program { [DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)] static extern ITypeLib LoadTypeLibEx(string path, REGKIND regkind); enum REGKIND { REGKIND_NONE = 2 } public static void Main() { ITypeLib typeLib = LoadTypeLibEx(@"C:\Path\To\Bar.tlb", REGKIND.REGKIND_NONE); ITypeInfo typeInfo; typeLib.GetTypeInfo(0, out typeInfo); IntPtr typeAttrPtr; typeInfo.GetTypeAttr(out typeAttrPtr); //! COMException: TYPE_E_CANTLOADLIBRARY … // (HRESULT 0x80029c4a) } } 的行上抛出异常。已检查的//!ITypeInfo界面的一个。

据我所知,Automation API必须在找到继承的接口IBar时遇到问题,该接口包含在另一个未注册的类型库中。

但显然无论如何都应该可以检查IFooBar.tlb管理得很好:

Inspecting <code>Bar.tlb</code> in OleView works fine.

(是的,它会发出警告,说明无法重建外部类型库的文件名,这是因为我没有注册OleView.exe。这不是我担心的问题。)

如果Foo.tlb能够在不崩溃的情况下检查OleView.exe,为什么我的代码会因为IBar这么简单而崩溃?我该如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

TL; DR: Automation API似乎可以在后台访问当前工作目录中的类型库,以便解析ITypeInfo引用。这个问题可以通过以下方式解决:

// using static System.IO.Directory;
SetCurrentDirectory(@"C:\Path\To");
ITypeLib typeLib = LoadTypeLibEx("Bar.tlb", REGKIND.REGKIND_NONE);

而不是:

ITypeLib typeLib = LoadTypeLibEx(@"C:\Path\To\Bar.tlb", REGKIND.REGKIND_NONE);

我进行了更多实验,发现如果OleView.exe存在于同一目录中,IBar只能成功检查Bar.tlb中的Foo.tlb类型详细信息。一旦我将Foo.tlb移到其他地方,OleView.exe就像我自己的代码一样失败了:

Same COM error occurs in <code>OleView.exe</code> once <code>Foo.tlb</code> is moved away from <code>Bar.tlb</code>

我想看看用于访问类型库的API调用OleView.exe,所以我查了一下:

dumpbin.exe /imports OleView.exe
dumpbin.exe /exports C:\WINDOWS\…\oleaut32.dll

并发现它引用了LoadTypeLib - 而不是LoadTypeLibEx。这两个函数的类型库注册方式不同。所以我查了MSDN documentation for LoadTypeLib并发现了这个:

  如果指定了类型库的路径,

LoadTypeLib将不会注册类型库。”

这让我有了重写代码的想法,如本答案开头所示。

然而,重要的是要指出,与引用文本所暗示的相反,传递给LoadTypeLibEx的路径中是否存在目录 - 这是对{{1}的调用这解决了错误。以下也适用:

Directory.SetCurrentDirectory