为什么要注册COM接口?

时间:2015-09-08 16:08:02

标签: c# .net vba interface com

我已经使用 COM 多年了,但我一直在学习新的(和奇怪的)事情。

最近我意识到 COM接口并不是必须在注册表中注册,才能让实现它们的组件工作。

我在分析 COM DLL 的工作站的注册表之后得出了这个结论(在 .Net / C#中实现 )已注册.regRegAsm创建的文件,因为该用户不是管理员。并且RegAsm仅为 COM类生成注册表项,而不是接口

如果这是真的,我的猜测是接口对于早期绑定非常重要,并且只需要存在于 TLB文件中。相反,注册实现(类)是必不可少的,因为它们由需要引用的文件系统上的物理代码支持。

1)我疯了,丢失了什么,或者接口可以省略?

2)如果可以省略它们会产生什么后果?

3 个答案:

答案 0 :(得分:4)

如果没有注册界面,你可以做很多事情。 COM的许多功能 - 编组,代理,异步调用 - 都有标准的实现,可以防止你自己滚动这些东西。例如,CoMarshalInterface是获取任何COM对象接口并将该接口封送到流中的标准方法,以便可以在另一个线程,进程或计算机中对其进行解组。接口信息在这方面至关重要 - 没有接口元数据,这样的事情的标准COM实现将无法工作,因为基础设施根本不了解您的接口以完成它需要做的事情以适用于所有COM对象的通用方式。

此外,虽然大多数自动化客户端(如VBA,C#和C ++)可以直接引用类型库文件以进行早期绑定,但仍然存在一些限制。例如,假设您正在使用包含一些实现不同类型库接口的类的类型库,或者第一个类型库中的接口可能接受参数或返回由interfaces / enums / etc定义的值在另一个类型库中。为了使自动化客户端能够使用包含交叉引用的这些接口,必须以某种方式发现交叉引用的类型库。注册是实现这一目标的方式。

值得注意的是:根据我的经验,当在整个机器上注册COM对象(在HKLM中注册)时,几乎所有工作都在每个用户注册时(在HKCU中)完全相同。这通常使COM注册在不能执行机器范围注册的情况下更加可口(例如,用户不是管理员)。但是,有一些重要的问题,最值得注意的是http://blogs.msdn.com/b/cjacks/archive/2008/06/06/per-user-com-registrations-and-elevated-processes-with-uac-on-windows-vista-sp1.aspx

答案 1 :(得分:3)

非常模糊,不确定我能读出大胆之间的所有单词。通常不止一种方法可以给这只猫上皮。 COM需要使用类工厂来创建对象,通用工作马是CoCreateInstance()。 CreateObject()在脚本环境中很流行。你给它一个数字,然后向后吐出一个接口指针。使用COM运行时负责定位包含coclass的可执行文件,加载它并找到正确的类工厂实现。

查找可执行文件是棘手的部分,这通常由注册表中的信息完成。在组件注册时进入那里。不仅仅是,清单也可以是此信息的来源。它需要嵌入客户端应用程序,这是它不是通用解决方案的一个原因。更现代的是Windows商店/电话/通用应用程序中的包清单。必需的,只有非常特权的组件仍然可以使用注册表让自己被发现。 Microsoft组件。

完全不同的方法是拥有自定义类工厂。例如,在DirectX中完成它的方式,根本不依赖于注册表。您可以调用CreateDevice()。仍称这个COM有点拉伸,它是一种更通用的技术,称为interface-based programming

这一切都适用于对象,接口是不同的。您调用IUnknown :: QueryInterface()来获取接口指针。不需要注册,它是处理它的coclass。

尽管如此,您还可以在HKLM \ Software \ Classes \ Interface注册表项中找到许多与Regedit.exe注册的接口。它们处理另一个COM细节,如果组件不在同一台机器或相同的进程或与客户机代码相同的线程中,则必须进行额外的工作以使调用在机器/进程/线程边界上序列化。在.NET Remoting中发生同样的事情,它需要一个代理。一个也实现相同接口但不直接执行该方法的对象,将参数传递给存根,以便它可以进行调用。

在.NET中很简单,Reflection使它变得非常简单。在COM中并不简单,需要一个知道如何将参数序列化为互操作数据包的额外组件。并以相同的方式返回返回值。代理/存根通常是从IDL自动构建的。或者在.NET中很常见,因为它不使用IDL,你使用编组器从类型库中挖掘出方法细节。这种机制与.NET Reflection高度可比,类型库与.NET元数据的作用完全相同。

Interface键中的ProxyStubClsId32注册表项包含该组件的CLSID。你会经常在那里找到{00000320-0000-0000-C000-000000000046},这是系统提供的使用类型库的编组器。

Regasm不编写接口密钥,它将.NET [ComVisible]类的ThreadingModel密钥设置为“Both”。这样就可以从STA和MTA线程调用这些方法,而无需编组。这是非常乐观的,很少测试,编写线程安全的.NET代码并不容易。

答案 2 :(得分:2)

关于你的第一个问题,如果不应该在COM上下文中使用该接口,或者如果接口派生自IDispatch并且你只使用后期绑定,那么你不需要注册它

但是,如果您使用早期绑定,或者该接口应该在COM上下文中使用,则需要注册它。

只是注册一个接口并不能启用编组,所有参数类型和返回类型也必须是可编组的,即不是HANDLE或类似的。

关于你的第二个问题,我希望你在阅读到目前为止的答案后能够自己回答。如果没有,

  

如果您没有注册界面,则无法在COM上下文中直接使用它。如果它派生自某个已注册的接口,则可以使用该接口,例如基于IDispatch的接口。  

 但是,很少有接口与IDispatch一样通用,因此对于任何其他基本接口,您都无法使用派生接口的新方法。

在类型库中,如果您没有注册事件dispinterface,那么开发工具(通常是IDE)将无法向您显示可以触发哪些事件,或者在所有。唯一的另一个选择是手动实现dispinterface,如果您的编程语言具有该选项,那么首先需要与缺少的IDL相当的文档。

一个常见的极端是让所有对象只是实现IDispatch而没有其他接口,但这又会阻碍开发工具对方法列表,代码完成和/或参数选择所做的任何努力(例如智能感知)。请注意,有时这已经足够了,例如在为IE的JScript实现window.external对象时,但在更常见的对象中完成它时会有点懒惰。

一般情况下,如果您只需要很少的额外工作就可以注册接口,只要您已经定位到COM,请执行此操作。