从 importlib 编辑的未注册类型库访问引用 ITypeInfo 的 ITypeInfo 会导致 TYPE_E_CANTLOADLIBRARY 错误
Accessing an ITypeInfo that references an ITypeInfo from an importlib-ed unregistered type library causes TYPE_E_CANTLOADLIBRARY error
我正在使用 .NET (System.Runtime.InteropServices.ComTypes
) 中的自动化 API 检查我自己生成的类型库 Bar.tlb
(见下文)。此类型库声明了一个接口 IBar
,它继承自在导入的类型库 Foo.tlb
中定义的接口 IFoo
。检查表示 IBar
的 ITypeInfo
会导致异常。 (代码如下。)
在开始我的代码之前,下面是我如何生成 Bar.tlb
类型库。
Bar.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
管理得很好:
(是的,提示无法重建外部类型库的文件名,这是因为我没有注册Foo.tlb
。我担心的不是这个。)
如果 OleView.exe
可以检查 IBar
而不会崩溃,为什么我的代码会因为像 typeInfo.GetTypeAttr()
这样简单的事情而崩溃?我该如何解决这个问题?
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);
我进行了更多实验,发现如果 Foo.tlb
存在于同一目录中,OleView.exe
只能成功检查 Bar.tlb
中的 IBar
类型详细信息。一旦我将 Foo.tlb
移到其他地方,OleView.exe
就会像我自己的代码一样失败:
我想查看哪些 API 调用 OleView.exe
用于访问类型库,所以我检查了:
dumpbin.exe /imports OleView.exe
dumpbin.exe /exports C:\WINDOWS\…\oleaut32.dll
并发现它引用了 LoadTypeLib
— 而不是 LoadTypeLibEx
。这两个函数的不同之处在于它们如何进行类型库注册。所以我查找了 MSDN documentation for LoadTypeLib
并发现了这个:
"LoadTypeLib
will not register the type library if the path of the type library is specified."
这让我产生了重写代码的想法,如本答案开头所示。
然而,重要的是要指出,与引用的文本所暗示的相反,它不是传递给 LoadTypeLibEx
的路径中是否存在目录的问题 — 这是对Directory.SetCurrentDirectory
解决了错误。 以下方法也有效:
// using static System.IO.Directory;
SetCurrentDirectory(@"C:\Path\To");
ITypeLib typeLib = LoadTypeLibEx("C:\Path\To\Bar.tlb", REGKIND.REGKIND_NONE);
我正在使用 .NET (System.Runtime.InteropServices.ComTypes
) 中的自动化 API 检查我自己生成的类型库 Bar.tlb
(见下文)。此类型库声明了一个接口 IBar
,它继承自在导入的类型库 Foo.tlb
中定义的接口 IFoo
。检查表示 IBar
的 ITypeInfo
会导致异常。 (代码如下。)
在开始我的代码之前,下面是我如何生成 Bar.tlb
类型库。
Bar.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
管理得很好:
(是的,提示无法重建外部类型库的文件名,这是因为我没有注册Foo.tlb
。我担心的不是这个。)
如果 OleView.exe
可以检查 IBar
而不会崩溃,为什么我的代码会因为像 typeInfo.GetTypeAttr()
这样简单的事情而崩溃?我该如何解决这个问题?
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);
我进行了更多实验,发现如果 Foo.tlb
存在于同一目录中,OleView.exe
只能成功检查 Bar.tlb
中的 IBar
类型详细信息。一旦我将 Foo.tlb
移到其他地方,OleView.exe
就会像我自己的代码一样失败:
我想查看哪些 API 调用 OleView.exe
用于访问类型库,所以我检查了:
dumpbin.exe /imports OleView.exe
dumpbin.exe /exports C:\WINDOWS\…\oleaut32.dll
并发现它引用了 LoadTypeLib
— 而不是 LoadTypeLibEx
。这两个函数的不同之处在于它们如何进行类型库注册。所以我查找了 MSDN documentation for LoadTypeLib
并发现了这个:
"
LoadTypeLib
will not register the type library if the path of the type library is specified."
这让我产生了重写代码的想法,如本答案开头所示。
然而,重要的是要指出,与引用的文本所暗示的相反,它不是传递给 LoadTypeLibEx
的路径中是否存在目录的问题 — 这是对Directory.SetCurrentDirectory
解决了错误。 以下方法也有效:
// using static System.IO.Directory;
SetCurrentDirectory(@"C:\Path\To");
ITypeLib typeLib = LoadTypeLibEx("C:\Path\To\Bar.tlb", REGKIND.REGKIND_NONE);