COM 面试题






2.80/5 (27投票s)
2005年6月24日
8分钟阅读

145169
良好的面试问题 COM
引言
- 什么是 IUnknown?IUnknown 提供了哪些方法? 如果你在简历中声称了解 COM,那么回答这个问题是一个普遍的好主意。否则,你可能会认为这次面试已经失败了。IUnknown 是 COM 的基接口。所有其他接口都必须直接或间接派生自 IUnknown。该接口中有三个方法:AddRef、Release 和 QueryInterface。
- AddRef、Release 和 QueryInterface 函数的目的是什么? AddRef 增加对象的引用计数,Release 减少对象的引用计数,QueryInterface 获取指向所请求接口的指针。
- 如果找不到请求的对象,QueryInterface 函数应该做什么? 返回 E_NOINTERFACE **并**将 out 参数置空。
- 如何在 COM 中创建对象的实例? 嗯,这取决于你的项目。从 CoCreateInstance 或 CoCreateInstanceEx 开始回答,解释它们之间的区别。如果面试官仍然不满意,你将不得不解释幕后的整个流程,包括本地服务器和进程内服务器的区别、类工厂的含义和机制等。你还可以提及其他对象创建方法,如 CoGetInstanceFromFile,但这很可能会将讨论引向moniker。
- 当客户端调用 CoCreateInstance 时会发生什么? 同样,这取决于面试官的详细程度和专业知识。从简单的类对象和类工厂机制的解释开始。更详细的说明将取决于具体情况。
- CoCreateInstance 有什么局限性? 那么,CoCreateInstance 的主要问题在于它只能创建一个对象,并且只能在本地系统上创建。要创建远程对象或同时获取多个基于单个 CLSID 的对象,应该使用 CoCreateInstanceEx。
- 什么是聚合?我们如何获得聚合对象的接口? 聚合是一种重用机制,其中外部对象将其接口暴露为好像它们是在外部对象本身上实现的。当外部对象总是将其某个接口的每个调用委托给内部对象的同一接口时,这会很有用。聚合实际上是包含/委托的一种特殊情况,它作为一种便利方式,可以避免在这些情况下外部对象产生额外的实现开销。我们可以通过使用内部接口的 IID 调用外部对象的 QueryInterface 来获取指向内部接口的指针。
- C 被 B 聚合,B 又被 A 聚合。我们的客户端请求 C。会发生什么? 对 A 的 QueryInterface 将请求委托给 B,B 又将接口的请求委托给 C。然后将此指针返回给客户端。
- 什么是 moniker? 实现 IMoniker 接口的对象。Moniker 充当唯一标识 COM 对象的名称。就像路径标识文件系统中的文件一样,moniker 在目录命名空间中标识 COM 对象。
- OLE 和 COM 之间有什么区别(如果有)? OLE 是构建在 COM 之上的。这个问题不严格,因为 OLE 在 COM 之上构建了很多年,而 COM 作为一种技术是几年前由微软提出的。你还可以提到 COM 是一种规范,而 OLE 是该规范的一种特定实现,尽管如今这也不完全准确,因为人们今天称之为 COM 的很可能是微软对 COM 规范的实现。
- COM 和 DCOM 有什么区别? 同样,这个问题不需要严格的答案。任何 DCOM 对象仍然是 COM 对象(DCOM **扩展**了 COM),并且任何 COM 对象都可以参与 DCOM 事务。DCOM 为分布式环境引入了几个改进/优化,例如 MULTI_QI(多个 QueryInterface())、安全上下文等。DCOM 证明了代理进程的重要性(你无法在远程机器上运行进程内服务器。你需要一个代理进程来完成)。DCOM 引入了*负载均衡*。
- 什么是 双重接口? 双重接口是同时支持 IDispatch 接口和基于 vtbl 的接口。因此,它可以在 VBScript 等脚本环境中使用,同时为非脚本环境利用基于 vtbl 的接口的强大功能和速度。然后,讨论很容易转变为分析双重接口问题——要为这种转折做好准备。
- 一个类可以有两个双重接口吗? 是的。你**可以**在一个类中有两个双重接口,但只有一个可以是默认的。关键是由于双重接口的性质,你无法同时处理两个双重接口!要在 VB 中支持两个双重接口,你可以这样写:
在 ATL 的类中,你必须使用宏 COM_INTERFACE_ENTRY2(IDispatch,dim d1 as IDualInterface1 dim d2 as IDualInterface2 set d1 = new MyClassWithTwoDuals set d2 = d1
IDualInterface1),以区分不同的双重接口。 - 什么是按值封送? 有些对象本质上可以被认为是静态的:无论调用哪个方法,对象的状态都不会改变。与其远程访问这样的对象,不如*复制对象的静态状态*并*在调用方侧创建具有相同状态信息的新对象*。调用方将无法注意到差异,但调用将更有效,因为它们不涉及网络往返。这称为“按值封送”。
- 什么是 单线程单元(MTA)?单线程单元(STA)? 这是一个很难简短描述的问题。总之,单元是在 NT 3.51 和后来的 Windows 95 中由微软引入的,用于隔离在多线程环境中运行旧式非线程安全代码的问题。每个线程被“封装”到所谓的单线程单元中。在单元中创建对象的目的是为了线程安全。*COM* 负责同步对对象的访问,即使单元内的对象不是线程安全的。多线程单元(MTA,或自由线程单元)是在 NT 4.0 中引入的。MTA 背后的思想是 COM 不负责同步线程之间的对象调用。在 MTA 中,开发者对此负责。有关此主题的进一步讨论,请参阅 Dr. Grimes 等人的《Professional DCOM Programming》或 Don Box 的《Essential COM》。
- 假设我们有一个对象 B 和由 B 创建的*聚合*对象 C(进程内服务器)。你能从 C 访问 B 的任何接口吗?*聚合*对象和*包含*对象之间有什么区别? 是的,你可以。这是 COM 的基本假设:“如果你能从这里到达那里,你就可以从任何地方到达那里”,也就是说,通过 QI() 来获取 IUnknown,你可以继续并获取对象支持的任何其他接口的指针。聚合对象直接暴露其接口,而不会有对象容器的可见干预。包含对象是在对象容器内创建的,其接口可能会被对象容器修改或过滤。
- 什么是 ROT?GIT?比较两者的优缺点。 根据定义,*运行对象表*(ROT)是每个计算机上的一个全局可访问表,它跟踪所有处于运行状态的 COM 对象,这些对象可以通过 moniker 识别。Moniker 提供程序在表中注册一个对象,该注册会增加对象的引用计数。在对象被销毁之前,其 moniker 必须从表中释放。*全局接口表*(GIT)允许进程中的任何单元(无论是单线程还是多线程)*获取对进程中任何其他单元中的对象上实现的接口的访问*。
- 如果你有一个有两个接口的对象,你能自定义封送其中一个吗? 不行!使用自定义封送的决定是一个全有或全无的决定;对象必须自定义封送所有接口,或者都不封送。
- 有没有不使用 regsvr32.exe 注册进程内服务器的方法? 是的。从客户端调用 DllRegisterServer()。不要忘记从同一个客户端调用 DLLUnregisterServer()。你还可以使用 Registrar 对象来实现相同目的,或者直接操作 Windows 注册表。
- 什么是 VARIANT?你为什么以及在哪里会使用它? VARIANT 是一个包含自动化类型的巨大联合体。这允许自动化类型之间的轻松转换。VARIANT 的最大缺点是联合体的大小。
- 如何保证客户端只创建远程服务器? 使用 CLSCTX_REMOTE_SERVER 标志创建一个对象(调用 CoCreateObjectEx())。
- 什么是 __declspec(novtable)?为什么需要它? __declspec(novtable) 是微软的编译器优化。此优化的主要思想是从抽象类中剥离 vtable 初始化代码(对于抽象类,vtable 是空的,而它是在构造函数中初始化的)
- 什么是 IDL? IDL 代表**I**nterface **D**efinition **L**anguage。IDL 是描述 COM 接口的语言。
- 什么是 In-proc? In-proc 是进程内 COM 对象,即实现为 DLL 并由容器托管的 COM 对象。当需要远程实例化进程内对象时,可以使用专门为此目的设计的 DLLHost.exe 应用程序。
- 什么是 OLE? OLE 是 COM 规范的第一个对象和嵌入实现,在 COM 正式命名为 COM 之前就已从 MS 提供。
- 举例说明 OLE 的用法。 最著名的例子可能是拖放和结构化存储的实现。
- 复合文档的 2 种存储类型是什么? Storage 和 Stream。
- .doc 文档是复合文档吗?是结构化存储吗? 复合文档是指包含有关在其中托管的其他文档信息的文档。所有 Office 文档*可能*是复合文档,但也*可能不是*。从 6.0 版本开始的 Word 文档存储为结构化存储。