【问题标题】:Newbie questions about COM关于 COM 的新手问题
【发布时间】:2011-02-01 10:24:37
【问题描述】:

我对 COM 很陌生,所以这个问题可能看起来很幼稚。

第一季度。关于 Windows DLL

根据我的理解,Windows DLL 可以导出函数、类型(类)和全局变量。这种理解可以吗?

第二季度。关于COM

我的幼稚理解是:COM DLL 似乎只是一种新的逻辑方式来组织标准 Windows DLL 导出的函数和类型。 COM DLL 导出 DllRegisterServer()DllGetClassObject() 等函数,以及实现 IUnknownClasses em> 界面。这种理解可以吗?

第三季度。 *.def & *.idl

*.def 用于定义Windows DLL 以传统方式导出的函数,如DllGetClassObject()。 *.idl 用于定义 COM coclass 实现的接口。

提前致谢。

【问题讨论】:

    标签: c++ com dll


    【解决方案1】:

    将 COM 视为跨 DLL 边界共享接口的二进制兼容方式。由于在各种编译器版本之间进行的非标准名称修改,C++ 类不能轻易地从 DLL 中导出。 COM 允许一个 DLL 或可执行文件中的代码从另一个 DLL 或 EXE 创建接口的实现,只要该实现遵循定义的接口和调用约定。这就是允许使用 C# 编写 COM 类并从 C++、Python 和许多其他支持 COM 的语言调用的原因。

    COM 接口只是标准 C++ 类,包含所有纯虚函数并从 IUnknown 派生。 IUnknown 是一个预定义的接口,所有兼容的 COM 接口都必须从该接口派生,它提供了通用工具,如引用计数和查询对象是否实现特定接口的能力。

    希望宣传他们可以创建 COM 接口实现的 DLL 可以通过导出 4 个函数来实现:

    • DllGetClassObject => 返回所请求接口的类工厂
    • DllCanUnloadNow => 分发的所有实例是否已被释放
    • DllRegisterServer => 在注册表中注册此 DLL 提供的类型
    • DllUnregisterServer => 从注册表中取消注册此 DLL 及其类型

    所以回答你的问题:

    第一季度。关于 Windows DLL 根据我的理解,一个 Windows DLL 可以导出函数, 类型(类)和全局变量。这种理解可以吗?

    DLL 可以导出函数和类(不确定全局变量,但即使可以,您也不希望导出它们 DLL!:-))但是,导出的类将被名称损坏,因此只能由其他碰巧具有相同名称的 DLL 或 EXE(这不是开展业务的好方法)。具有 C 风格调用约定的函数没有名称混乱,因此可以毫无问题地从其他地方导出和调用。

    第二季度。 COM DLL 导出两个函数,例如 DllRegisterServer() 和 DllGetClassObject(), 以及实现 IUnknown 接口的类。这是理解的全部吗 对吧?

    中途,有 4 个函数可以导出为完全兼容 COM 的 DLL(如上所示)。您可以在 MSDN 中搜索这些名称中的任何一个,以查看它们的完整签名。您对 DllGetClassObject 的实现将成为外部各方将使用的主要实现。他们可以使用它来获取 IClassFactory 到您的 DLL 提供的接口,然后使用它来创建实例。

    COM 是一个庞大而复杂的野兽,但它的基础相当简单。您还可以查看此链接 COM Intro 了解更多信息。祝你好运!

    【讨论】:

    • +1 COM 没那么疯狂。它使您能够跨越低级差距弥合高级语言。
    【解决方案2】:

    到目前为止,您已经完全正确。我要澄清的唯一一点是“实现 IUnknown 接口的类”。类由实现IClassFactory 接口的对象表示。你可以通过在DLL中调用DllGetClassObject来得到这样一个类工厂,然后你可以要求类工厂制作一个类的对象。这与许多其他面向对象的系统类似:有多种对象表示类,可用于制造实例。

    关于 COM 的其他有用信息:

    • 有一个小而复杂的辅助函数库,大多以前缀Co 命名。正是这些实际加载 DLL、定位导出的函数并调用它们。客户端代码通常不会直接调用 DLL 导出(尽管为简单的进程内 COM 对象编写自己的托管系统是一件相当简单的事情)。
    • 有一个重要的接口IDispatch,它增加了另一层抽象,这样就可以动态识别调用对象的方法,使用字符串查找方法并传递参数值数组。这允许脚本语言安全地调用 COM 对象(即以允许程序员犯错而不是崩溃的方式)。
    • 有一个更复杂的“编组”系统,允许从其他线程、其他进程甚至其他计算机调用对象;在其远程(和安全意识)形式中,它被称为 DCOM。它没有像其他 COM 使用那样大规模成功。
    • 跨进程支持是 OLE2 的基础,这是 1990 年代初期每个应用程序都急于支持的一个噱头,其中大多数都错误地实现了(非常复杂的)接口,因此到处崩溃。
    • 更成功的是 OLE 控件,它在技术上要简单得多(不使用编组),并且提供了一种扩展 Visual Basic 的方法。
    • 至少有一个广泛分布的受 COM 启发的系统与 COM 不兼容但共享完全相同的概念(至少是简单的东西),称为 XPCOM,是 Mozilla Firefox Web 浏览器的基础。
    • 如果您普遍使用 COM,您将有一个与 .NET 框架集成的好方法,因为它包括与 COM 的出色互操作性。但是你必须严格遵循 COM 的规则:AddRef/Release 必须完全按照定义工作,所以一个对象在其引用计数大于零时决不能被销毁,必须可以使用QueryInterface 来查找IUnknown 从任何接口,并从那里回到原始接口,从任何接口获得的IUnknown 在对象的生命周期内必须始终具有相同的内存地址(因此可以用于身份比较)。因此例如:不要在堆栈上创建 COM 对象,也不要从 QueryInterface 返回单独的对象(即具有自己的 QueryInterface 的对象返回不同的接口)。
    • COM 的一个主要问题是它至少有两种表示字符串的标准方法。两者都不使用引用计数。为什么他们从未定义过IString 接口,我无法理解,但他们没有。
    • 如果您尝试编写 COM 代码而没有智能指针来保存引用,或者没有基类来帮助您实现接口,例如,您将发疯。 class MyClass : COM::Object<IThis, IThat> {...)

    【讨论】:

      【解决方案3】:

      为了补充 cpalmer 的好答案,您没有特别的理由需要使用注册表和四个推荐的函数从 DLL 导出 COM。

      您可以使用无注册表的 COM 或 COM-lite 方法,您只需导出工厂方法,例如

      __declspec(dllexport) void MyDllCreateFoo(IFoo ** ppFoo);

      DLL 用户会调用您的工厂来创建实现 IFoo 的类 CMyFoo。什么 DllRegisterServer 等。人。这样做是允许在注册表中查找 CMyFoo 和其他类。

      Q3:事实上你是正确的,如果不是在精神上。 .def 文件和 .idl 文件是完全不同的野兽。 .def 文件仅由链接器使用,甚至不是必需的 - 您可以导出您想要使用的所有函数 __declspec(dllexport) 无效 foo() { }

      在您的 C++ 代码中。

      .idl 文件用于生成 C++ 头文件(.h 文件),DLL 及其客户端都包含这些头文件。它会生成接口以及一些胶水代码,这些代码可以执行参数编组等操作。

      同样,您在技术上不需要使用 IDL 来使用 COM,但它可以让事情变得更方便。

      我这样分解它的原因是为了说明 COM 不是一个庞大的单片机,​​而是一个非常小的东西,围绕它构建了一堆你可以选择使用或不使用的东西,品尝。

      【讨论】:

      • 非常感谢,德鲁。是的,这正是我正在考虑的直接导出工厂方法。因为我只是觉得导出一个 DllGetClassObject() 方法很繁琐。在看到您的回答之前,我没有意识到它已经存在并且被称为无注册 COM,再次感谢。
      猜你喜欢
      • 2011-02-17
      • 1970-01-01
      • 1970-01-01
      • 2012-09-30
      • 2011-11-11
      • 1970-01-01
      • 1970-01-01
      • 2021-06-23
      相关资源
      最近更新 更多