0

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

在我开始编写代码之前,这里是我生成Bar.tlb类型库的方式。

酒吧.idl:

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

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

Foo.idl:

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

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

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

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的是IBar接口。

我了解自动化 API 必须无法找到继承的接口IFoo,该接口包含在另一个未注册的类型库中。

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

在 OleView 中检查 <code>Bar.tlb</code> 工作正常。

(是的,它给出了一个关于无法重建外部类型库文件名的警告,这是因为我没有注册Foo.tlb。这不是我担心的。)

如果OleView.exe可以检查IBar而不会崩溃,为什么我的代码会因为简单的事情而崩溃typeInfo.GetTypeAttr()?我该如何解决?

4

1 回答 1

0

TL;DR:自动化 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只能在同一目录中存在if 时成功检查IBar类型详细信息。一旦我搬到其他地方,就像我自己的代码一样失败:Bar.tlbFoo.tlbFoo.tlbOleView.exe

一旦 <code>Foo.tlb</code> 从 <code>Bar.tlb</code> 移开,<code>OleView.exe</code> 中也会出现相同的 COM 错误

我想看看哪些 API 调用OleView.exe用于访问类型库,所以我检查了:

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

并发现它引用了LoadTypeLib- 而不是LoadTypeLibEx. 这两个函数在进行类型库注册的方式上有所不同。所以我查阅了MSDN 文档LoadTypeLib,发现了这个:

"LoadTypeLib如果指定了类型库的路径,则不会注册类型库。"

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

然而,重要的是要指出,与引用的文本所暗示的相反,传递给的路径中不存在或不存在目录LoadTypeLibEx——它是解决错误的调用。Directory.SetCurrentDirectory以下内容也可以:

// using static System.IO.Directory;
SetCurrentDirectory(@"C:\Path\To");
ITypeLib typeLib = LoadTypeLibEx("C:\Path\To\Bar.tlb", REGKIND.REGKIND_NONE);
于 2016-11-22T22:05:17.303 回答