传统DLL和COM DLL的区别

35

我目前正在学习COM技术。我发现COM DLL在传统DLL架构的基础上进行了扩展。当我们创建COM DLL时,仍然依赖于传统的DLL导出方法,以便找到内部COM组件。

如果COM是用于在二进制级别重用组件,我认为传统的DLL也可以实现相同的功能。它们都公开函数,它们都是二进制的,那么转向COM的意义何在呢?

目前,我感觉传统的DLL以“平面”方式公开方法,而COM DLL则以“面向对象”的层次结构方式公开方法。面向对象的方式似乎更好。这是否是COM盛行的原因?

非常感谢。


2
你为什么认为COM占主导地位?如果更多的DLL是经典的“C” DLL而不是COM DLL,我也不会感到惊讶。 - David Gladfelter
谢谢您的评论。也许我是错的。但是似乎许多Windows功能都是建立在COM之上的,例如ActiveX、OLE,甚至.NET也是作为一组COM服务器构建的。所以我得出了这个结论。 - smwikipedia
8个回答

41

不,它们有很大的区别。COM拥有明确定义的协议用于创建对象、公开方法、管理内存、发布类型信息、管理线程等方面。几乎所有编程语言都支持使用COM服务器。

如果直接暴露你自己的函数,则无法获得这些功能。这种方式可能只能在使用C/C++编写的程序中使用(因此可以读取头文件),还要使用完全相同版本的C++编译器,并且存在各种互操作问题。暴露像std::string这样的C++类对象就不安全了。既不能保证内存布局兼容,也没有任何内存所有权协议。

虽然它可能更符合面向对象编程(OOP)的特点,但是COM不支持继承,因为在二进制级别上实现OOP非常困难。这个问题需要所有代码都采用运行时支持,例如.NET和Java的虚拟机。


1
关于第二段,有很多语言/框架等可以让你在C++之外使用本地(非COM)DLL。例如,.NET通过P/Invoke提供了一个简单易用的互操作机制。 - hemp
2
有天壤之别。P/Invoke 是一次只能调用一个函数的战斗。COM 服务器可以在不费力气的情况下工作95%。 - Hans Passant
什么被认为是“COM服务器”?它是一个公开的COM对象,为潜在客户提供服务以便消费它吗? - RollRoll
1
你可以通过所有的 COM 接口进行通信,但普通 DLL 不行。你可以通过调用 COM 接口在两个进程之间传递值。但是使用普通 DLL 无法实现此操作。 - Zhang

16

COM DLL是一个具有特定COM入口点的DLL。COM公开了用于创建COM对象的类工厂,因此需要一种获取由COM服务器实现的类工厂的方法。这就是DllGetClassObject的作用。此外,COM DLL是自注册的:它们可以通知Windows可用的类和接口。让DLL自行注册的入口点是DllRegisterServer。

还有一些其他的入口点,但它们大致如此。

如果没有明确定义的DllRegisterServer入口点,客户端将无法使DLL自行注册。这将使安装COM组件更加复杂。

如果没有标准化的入口点来获取类工厂,则每个DLL都必须定义自己的入口点,而该信息必须放在Windows注册表中,以便COM基础结构知道如何访问每个DLL的类工厂。没有必要增加额外的复杂性,因此也标准化了该入口点。

至于COM与“C”之间的区别,主要区别在于合同的概念。COM鼓励程序员从模块之间的抽象接口出发思考,而不是分层、自上而下的功能分解。这是一种“面向对象编程”的方式,但IMO这个术语太宽泛了,用处不大。面向合同的方法对于像C/C++这样的强类型、静态链接的语言有着多方面的优点。


9
我认为通过阅读Don Box的Essential COM第一章,链接在这里,你会很好地了解为什么我们使用COM。总之,COM确保二进制级别的兼容性,无论你使用哪种语言或哪个版本的编译器。这与“OOP”无关,你当然可以从DLL中公开C++类,但它们不是“二进制兼容的”。

5

如果你想了解引入COM的原因和背后的理念,我强烈推荐阅读从CPP到COM


我在哪里可以找到这个链接的示例? - Lakshman Rao

5
关键区别在于COM使二进制兼容性成为可能。
如果您添加/删除功能并重新构建传统DLL,则任何客户端应用程序在尝试使用该DLL时很可能会失败,因为它们是根据早期版本构建的。
COM引入了接口的概念,这些接口是不可变的,因此不应在构建之间进行更改等操作。每个COM对象都必须实现IUnknown接口,其中包含QueryInterface方法,用于请求指向其他支持的接口的指针。
COM规范确保IUnknown接口始终位于DLL中的相同位置,因此即使对象已修订以支持更多接口,也仍然可以安全地调用QueryInterface方法。

5

DLL在Windows中有许多用途,其中许多类型的库代码存储在DLL中。例如, .net assemblies 就是其中一种不同的东西。

COM DLL不比“普通二进制PE DLL”更好,因为COM DLL也是一个普通的DLL。将DLL变成COM DLL的原因是要暴露某些导出项(查找条目IUnknown),这些导出项匹配特定的契约(签名),或者甚至是多种经过规范化的接口(查找条目“dual interface”), 这允许在DLL内部实例化特定对象以及自动发现服务,包括功能名称和参数类型。

由于双重接口使得链接到脚本语言(在Web、shell脚本、教育程序等中使用)非常方便。由COM DLL公开的双重接口允许脚本运行时查询它所期望的确切类型并执行相应的强制转换,对用户来说无缝地完成。

这种灵活性使得整个巨大的COM基础设施被构建起来,包括组件注册、DCOM(网络调用)等。它使得将COM接口提供给windows组件(例如WMI)和Office组件变得相当方便。许多其他流行的接口都是在COM之上实现的,例如ADO等。

尽管如此,COM在任何意义上都不是“占主导地位”的。COM DLL仅是 DLL 的一小部分。 普通DLL和.NET DLL非常受欢迎。 Microsoft认为.NET接口优于COM。许多Unix狂热者和其他人认为DLL完全是个坏主意,因为它无法像Unix共享对象那样提供运行时链接服务。尽管我是一个Windows开发人员,但我也认为SO提供了更好的替代方案,并希望未来能够使用他们。


4
最好的理解COM的方式是将其想象为您创建的对象与使用该对象的人之间的合同。
COM处理以下内容:
1. 如何在不同版本中对您的对象进行版本控制 2. 如何查找您的对象,即使您的对象位于被重命名或来自不同来源的DLL中 3. 如何引用和销毁您的对象(关于堆的方面) 4. 您希望线程如何工作以及围绕您的对象的线程/锁定规则
由于您可以创建传统的DLL来处理上述每个项目,因此COM已成为标准。但是,当您发布DLL时,必须明确预期的合同。使用COM的规则,这种表述已经为您完成了。
您也是正确的,COM公开对象,而更传统的DLL只公开函数。您经常会看到开发人员尝试在纯C中模拟在COM中找到的合同。通常,您会看到他们在DLL中克隆COM的某些方面(例如,您将看到返回函数指针结构体的方法)... 我的经验是,如果您不使用COM来制作公共DLL,则增加错过某些情况的可能性,特别是当版本控制涉及其中时。

哦,我肯定漏掉了一些COM处理的项目 - 请将它们作为注释添加上去 :-) - stuck

3
除了已经发布的答案外,COM接口以独立于编程语言的方式公开二进制数据对象,允许在一个进程地址空间中分配这些对象的内存,并在另一个进程中释放。查询封送和解封送。
它还允许传递字符串,而不必关注编码细节来确定空终止符的位置。COM字符串是计数的UNICODE字符串。
加入IDL并将类型库编译到DLL中,就有了自描述接口。对于普通的DLL,您必须从外部文档中了解它们具有哪些方法和它们所带参数。
COM标准也可以跨平台。Mac版本的Office支持COM,并且已经为Unix和类Unix系统提供了实现,尽管它们从未流行起来。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接