为什么TypeName()在使用COM时返回.GetType和TypeOf的不同结果?

时间:2015-06-24 22:51:24

标签: vb.net types com windows-shell typename

我觉得我会从理解这些功能如何工作的差异中受益匪浅,这样我就能更好地理解何时使用每个功能。

我在使用两种不同的互操作(Excel和EPDM)时非常困难,这两种互操作都广泛使用弱类型参数。我一直在使用返回的对象并将它们转换为正确的类型(根据文档)。在浪费了大量时间之后,我发现使用TypeNameGetTypeTypeOf operator与COM对象可以产生不同的结果,并且在不同情况下每个可以更多或者比下一个更不可靠。

现在,在大多数情况下TypeName()似乎是确定COM对象类型最可靠的。但是,完全避免使用其他两个函数对我来说是非常危险的,而且今天我遇到了interesting problem,我似乎无法将对象强加给TypeName()报告的类型。在关于该问题的评论中提出了一个有趣的概念,即实现IDispatch的对象实际上可能返回调度的接口类型名称,这可以部分解释这些差异。

我真的想更好地理解这些功能是如何运作的,但我在.NET ReferenceSource中遇到了一些失误,所以我在这个问题上提供了一笔赏金,希望有人可以解释这些不同之处功能起作用,在每种情况下都应该使用。

以下是使用Excel互操作的代码摘录。

Dim DocProps As Object 
DocProps = WeeklyReports.CustomDocumentProperties 'WeeklyReports is a Workbook object
Debug.Print(DocProps Is Nothing)
Debug.Print(TypeName(DocProps))
Debug.Print(TypeOf (DocProps) Is DocumentProperties)
Debug.Print(DocProps.GetType.ToString)

输出结果为:

  


  DocumentProperties
  假
  系统.__ ComObject

1 个答案:

答案 0 :(得分:3)

这是一个漫长的故事,有点怀疑英语会削减它。它确实需要了解COM的工作原理以及它如何集成到Office产品中。

以极快的速度,COM的核心是interface-based programming范式。接口很简单,类很难。您在.NET设计中看到的东西,类只能从一个基类派生,但可以实现任意数量的接口。为了使语言互操作顺利进行,重要的是尽可能少地依赖语言实现细节。

有很多COM没有这样做,你已经习惯了任何现代语言。它不支持异常,只支持错误代码。根本没有泛型的概念。没有反思。不支持方法重载。不支持实现继承,类的概念是完全隐藏的。它只显示为一个数字,CLSID,一个标识类类型的guid。使用在COM组件中实现的工厂函数来创建类的对象。 COM组件保留该对象的所有权。然后,客户端代码只使用接口来调用以使用方法并获取或设置属性。 CoCreateInstance()是执行此操作的主要运行时支持功能。

这进一步削弱为一个名为OLE Automation的子集,这是您与Office互操作时使用的风格。它严格限制了可用于属性和方法参数的类型类型,并采用规定的方法来处理字符串和数组等难点。它添加一些功能,它支持通过IDispatch接口进行后期绑定,这对脚本语言很重要。和VARIANT,一种可以存储任意类型的值或对象引用的数据类型。并支持类型库,COM服务器实现的接口的机器可读描述。 .NET元数据是完全类似的。

对于这个问题很重要,它将一个类可以实现的接口数限制为一个。对于那些根本不支持接口概念的语言很重要,比如VBA,Javascript和VBScript等脚本语言以及早期的Visual Basic版本。 Office互操作对象模型的设计考虑到了这些限制。

因此,从使用这种语言的程序员来自动化Office程序的角度来看,完全不可见,他的语言运行时实际上是在使用接口。他在程序中看到并使用的所有标识符都是类名,而不是接口名。 DocumentProperties 实际接口名称是您可以在对象浏览器中看到的。只需在搜索框中键入名称,即可正确注释"公共接口DocumentProperties / Microsoft.Office.Core"在右下方的小组中。

Office对象模型的一个具体细节在这里非常重要,许多属性和方法返回类型都是VARIANT。一种可以存储任意值或对象引用的OLE自动化类型,当您使用.NET时,它将映射到System.Object。 Workbook.CustomDocumentProperties属性就是这样。即使该文件被记录为实际返回DocumentProperties接口引用。他们可能做到这一点离开肘部空间到某一天返回另一种界面。对于"自定义文档属性非常必要"。

该属性是VARIANT在支持动态类型的语言中并不重要,他们大步采取。然而,在强类型语言中它是相当痛苦的。并且对支持自动完成的编程编辑器非常不友好,比如VS的IntelliSense。您通常做的是将变量声明为预期的接口类型:

  Dim DocProps As DocumentProperties
  DocProps = CType(WeeklyReports.CustomDocumentProperties, Microsoft.Office.Core.DocumentProperties)

现在一切都亮了起来。如果你喜欢使用Option Strict Off编程VB.NET,你也不需要CType()。这使它成为一种支持动态类型的编程语言。

我们到了那里。只要您将DocProps声明为Object,编译器就会知道有关接口的bean。调试器也没有,它不受变量声明的帮助,只能看到它是运行时类型的__System.ComObject。所以它没有什么,这很容易理解,属性getter没有失败,文档也有属性。

TypeName()函数使用IDispatch接口的一个功能,它在运行时公开类型信息。这恰好适用于您的情况,通常不会,函数首先调用IDispatch::GetTypeInfo()来获取ITypeInfo接口引用,然后调用ITypeLib::GetDocumentation()。这有效,你得到了接口名称。否则与.NET中的Reflection相当,只是不那么强大。不要过分依赖它,有很多COM组件不能实现它。

对你的问题至关重要,TypeOf (DocProps) Is DocumentProperties是失败的鲸鱼。当你尝试编写我之前提出的代码时,你会发现一些东西。您将获得一个讨厌的运行时异常,System.InvalidCastException:

  

{"无法转换类型为' System .__ ComObject'的COM对象接口类型' Microsoft.Office.Core.DocumentProperties'。此操作失败,因为QueryInterface调用COM组件上的IID& {2DF8D04D-5BFA-101B-BDE5-00AA0044DE52}'由于以下错误而失败:不支持此类接口(来自HRESULT的异常:0x80004002(E_NOINTERFACE))。"}

换句话说,Excel文档对您说谎。你得到一个类似于 DocumentProperties的界面,它仍然有这个界面记录的成员,但不再与Microsoft.Office.Core.DocumentProperties相同。它可能曾经是很多个月前。埋藏在this KB article内的令人讨厌的小细节:

  

注意DocumentProperties和DocumentProperty接口是后期绑定接口。要使用这些接口,您必须像处理IDispatch接口一样对待它们。