简体   繁体   English

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

[英]Why does TypeName() return different results from .GetType and TypeOf when working with COM?

I feel like I would benefit greatly from understanding the differences in how these functions work so that I could better understand when to use each one. 我觉得我将从了解这些功能如何工作的差异中受益匪浅,以便更好地了解何时使用每个功能。

I'm having a very difficult time working with two different interops (Excel, and EPDM) which have both made extensive use of weak typed parameters. 我在使用两个互操作量(Excel和EPDM)时非常困难,这两个互操作量都大量使用了弱类型参数。 I keep running into problems using returned objects and casting them to the proper type (per the documentation). 我一直在使用返回的对象并将它们强制转换为正确的类型时遇到问题(根据文档)。 After wasting a ton of time, I've found that using TypeName , GetType , and a TypeOf operator with COM objects can yield different results, and in different circumstances each one can be more or less reliable than the next. 浪费了大量时间后,我发现将TypeNameGetTypeTypeOf运算符与COM对象一起使用可以产生不同的结果,并且在不同的情况下,每一个都比下一个更可靠。

Now, in most cases TypeName() seems to be the most reliable for determining type with COM objects. 现在,在大多数情况下, TypeName()似乎是确定COM对象类型的最可靠方法。 However, avoiding the other two functions entirely seems quite cargo cultish to me, and besides that today I ran into an interesting problem where I can't seem to cast an object to the type reported by TypeName() . 但是,完全避免使用其他两个函数对我来说似乎非常有用,此外,今天我遇到了一个有趣的问题 ,即我似乎无法将对象转换为TypeName()报告的类型。 An interesting notion was brought up in the comments on that problem that objects which implement IDispatch may actually return the dispatched interface typename, which could partially explain the differences. 关于该问题的评论提出了一个有趣的想法,即实现IDispatch对象实际上可能返回分派的接口类型名,这可以部分解释这些差异。

I'd really like to better understand how these functions actually work, but I get kind of lost running through the .NET ReferenceSource , so I'm offering a bounty on this question in hopes someone can explain how these different functions work and in what context each should be used. 我真的很想更好地了解这些功能的实际工作原理,但是由于.NET ReferenceSource的运行,我有点迷失了自己,因此我对这个问题提供了很多帮助,希望有人可以解释这些不同的功能如何工作以及如何工作。上下文应使用每个。

Here is a code excerpt from working with the Excel interop. 这是使用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)

The output is: 输出为:

False
DocumentProperties 文件属性
False
System.__ComObject System .__ ComObject

It is a long story and a bit doubtful that English is going to cut it. 这是一个漫长的故事,对于英语是否会削减它有些怀疑。 It does require understanding how COM works and how it was integrated into the Office products. 它确实需要了解COM的工作方式以及如何将其集成到Office产品中。

At breakneck speed, COM is very heavily an interface-based programming paradigm at its core. 以惊人的速度,COM的核心是非常重要的基于接口的编程范例。 Interfaces are easy, classes are hard. 接口容易,类很难。 Something you see back in the .NET design as well, a class can derive from only one single base class but can implement any number of interfaces. 您也可以在.NET设计中看到一些东西,一个类只能从一个单一的基类派生,但可以实现任何数量的接口。 To make language interop work smoothly, it is important to take as few dependencies on language implementation details as possible. 为了使语言互操作顺利进行,重要的是尽可能减少对语言实现细节的依赖。

There is a lot that COM does not do that you'd be used to in any modern language. 在很多现代语言中,COM都是很多您不习惯的。 It does not support exceptions, only error codes. 它不支持异常,仅支持错误代码。 No notion of generics at all. 根本没有泛型的概念。 No Reflection. 没有反思。 No support for method overloads. 不支持方法重载。 No support for implementation inheritance whatsoever, the notion of a class is completely hidden. 无论如何都不支持实现继承,类的概念完全被隐藏了。 It only appears as a number, the CLSID, a guid that identifies a class type. 它仅显示为数字,即CLSID,这是标识类类型的GUID。 With a factory function implemented in the COM component that creates an object of the class. 通过在COM组件中实现的工厂功能来创建类的对象。 The COM component retains ownership of that object. COM组件保留该对象的所有权。 The client code then only ever uses interfaces to make calls to use methods and get or set properties. 然后,客户端代码仅使用接口来调用使用方法以及获取或设置属性。 CoCreateInstance() is the primary runtime support function that does this. CoCreateInstance()是执行此操作的主要运行时支持功能。

This was further whittled down to a subset called OLE Automation, the flavor that you use when you interop with Office. 进一步将其缩减为称为OLE自动化的子集,这是与Office互操作时使用的风格。 It strictly limits the kind of types you can use for properties and method arguments with prescribed ways to deal with the difficult ones like strings and arrays. 它严格限制了您可以使用规定的方式处理属性和方法参数的类型,以处理诸如字符串和数组之类的难题。 It does add some capabilities, it supports late binding through the IDispatch interface, important to scripting languages. 它确实添加了一些功能,它支持通过IDispatch接口进行后期绑定,这对于脚本语言非常重要。 And VARIANTs, a data type that can store a value or object reference of an arbitrary type. VARIANTs,一种数据类型,可以存储任意类型的值或对象引用。 And supports type libraries, a machine-readable description of the interfaces implemented by the COM server. 并支持类型库,这是COM服务器实现的接口的机器可读描述。 .NET metadata is the exact analogue. .NET元数据是完全类似的。

And important to this question, it limit the number of interfaces that a class can implement to just one . 对于这个问题很重要,它将类可以实现的接口数量限制为一个 Important to languages that don't support the notion of interfaces at all, like VBA, scripting languages like Javascript and VBScript and early Visual Basic versions. 对于完全不支持接口概念的语言(例如VBA),脚本语言(例如Javascript和VBScript)以及早期的Visual Basic版本而言,这一点很重要。 The Office interop object model was very much designed with these limitations in mind. Office互操作对象模型在设计时充分考虑了这些限制。

So from the point of view from a programmer that uses such a language to automate an Office program, it is completely invisible that his language runtime is actually using interfaces. 因此,从使用这种语言自动执行Office程序的程序员的角度来看,他的语言运行库实际上正在使用接口是完全不可见的。 All he ever sees and uses in his program are identifiers that look like class names, not interface names. 他在程序中看到和使用的只是标识符,它们看起来像类名,而不是接口名。 That DocumentProperties is actually an interface name is something you can see in Object Browser. 实际上可以在对象浏览器中看到该DocumentProperties 实际上是一个接口名称。 Just type the name in the search box, it properly annotates "public interface DocumentProperties / Member of Microsoft.Office.Core" in the lower-right panel. 只需在搜索框中键入名称,即可在右下方面板中正确注释“公共接口DocumentProperties / Microsoft.Office.Core成员”。

One specific detail of the Office object model matters a great deal here, many properties and method return types are VARIANTs. Office对象模型的一个特定细节在这里非常重要,许多属性和方法返回类型都是VARIANT。 A OLE Automation type that can store an arbitrary value or object reference, it is mapped to System.Object when you use .NET. 一种OLE自动化类型,可以存储任意值或对象引用,当您使用.NET时,它将被映射到System.Object。 The Workbook.CustomDocumentProperties property is like that. Workbook.CustomDocumentProperties属性就是这样。 Even though the property is documented to actually return a DocumentProperties interface reference. 即使该属性已记录为实际上返回DocumentProperties接口引用。 They probably did this to leave elbow room to some day return another kind of interface. 他们可能这样做是为了离开肘部空间有一天还返回另一种界面。 Fairly necessary for "custom document properties". 对于“自定义文档属性”非常必要。

That the property is a VARIANT doesn't matter that much in languages that support dynamic typing, they take them with stride. 对于支持动态类型的语言,该属性是否为VARIANT无关紧要,他们会大步向前。 It is however pretty painful in a strongly typed language. 但是,使用强类型语言会非常痛苦。 And pretty unfriendly to programming editors that support auto-completion, like VS's IntelliSense. 对于支持自动补全的编程编辑器(如VS的IntelliSense)来说,这非常不友好。 What you normally do is declare your variable to the expected interface type: 通常 ,您需要将变量声明为所需的接口类型:

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

And now everything lights up. 现在一切都点亮了。 You don't need the CType() cast either if you favor programming VB.NET with Option Strict Off in effect. 如果您希望在启用Option Strict Off的情况下编程VB.NET,则也不需要CType()强制转换。 Which turns it into a programming language that supports dynamic typing well. 这使它成为一种很好地支持动态键入的编程语言。


We're getting there. 我们到了那里。 As long as you declare DocProps as Object then the compiler knows beans about the interface. 只要将DocProps声明为Object ,编译器就知道有关该接口的bean。 Nor does the debugger, it isn't helped by the variable declaration and can only see that it is a __System.ComObject from the runtime type. 调试器也没有,变量声明没有帮助,只能从运行时类型中看到它是__System.ComObject So it isn't Nothing, that's easy enough to understand, the property getter did not fail and the document has properties. 因此,它并非没有,它很容易理解,属性获取器没有失败,并且文档具有属性。

The TypeName() function uses a feature of the IDispatch interface, it exposes type information at runtime. TypeName()函数使用IDispatch接口的功能,它在运行时公开类型信息。 That happens to work in your case, it usually doesn't, the function first calls IDispatch::GetTypeInfo() to get an ITypeInfo interface reference, then calls ITypeLib::GetDocumentation() . 碰巧在您的情况下可行,通常不会,该函数首先调用IDispatch :: GetTypeInfo()以获取ITypeInfo接口引用,然后调用ITypeLib :: GetDocumentation() That works, you get the interface name back. 可行,您将获得接口名称。 Otherwise pretty comparable to Reflection in .NET, just not nearly as powerful. 否则,它可以与.NET中的Reflection完全媲美,但功能几乎不那么强大。 Do not rely on it heavily, there are lots of COM components that don't implement this. 不要过分依赖它,有很多COM组件没有实现它。

And crucial to your question, TypeOf (DocProps) Is DocumentProperties is a fail whale. 对于您的问题至关重要的是, TypeOf (DocProps) Is DocumentProperties是失败的鲸鱼。 Something you'll discover when you try to write the code I proposed earlier. 当您尝试编写我之前提出的代码时,您会发现一些东西。 You'll get a nasty runtime exception, System.InvalidCastException: 您将获得一个讨厌的运行时异常System.InvalidCastException:

{"Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.Office.Core.DocumentProperties'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{2DF8D04D-5BFA-101B-BDE5-00AA0044DE52}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."} {“无法将类型'System .__ ComObject'的COM对象转换为接口类型'Microsoft.Office.Core.DocumentProperties'。此操作失败是因为对ID为'{2DF8D04D-5BFA-101B”的接口的COM组件上的QueryInterface调用-BDE5-00AA0044DE52}'由于以下错误而失败:不支持此类接口(HRESULT的异常:0x80004002(E_NOINTERFACE))。“}

In other words, the Excel documentation is lying to you. 换句话说,Excel文档对您说谎。 You get an interface back that resembles DocumentProperties, it still has the members that this interface documents, but is no longer identical to the Microsoft.Office.Core.DocumentProperties. 您将获得一个类似于 DocumentProperties的接口,它仍然具有该接口记录的成员,但不再与Microsoft.Office.Core.DocumentProperties相同。 It probably once was, many moons ago. 它可能曾经是许多月前。 A nasty little detail that's buried inside this KB article : 这个知识库文章中隐藏了一个令人讨厌的小细节:

Note The DocumentProperties and the DocumentProperty interfaces are late bound interfaces. 注意DocumentProperty和DocumentProperty接口是后期绑定的接口。 To use these interfaces, you must treat them like you would an IDispatch interface. 要使用这些接口,必须像对待IDispatch接口一样对待它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM