单个类对象用于多个 COM 类






3.20/5 (10投票s)
2003年8月3日
6分钟阅读

76361

976
使用单个类工厂实现创建多个 COM 对象类型
引言
类工厂是 COM 中的一个重要概念。COM 提供了多种方式来控制组件的创建过程。正确理解类工厂将有助于程序员更好地设计 COM 组件(即 coclass)及其对应的类对象(即类工厂)。本文将介绍类工厂的重要性,并提供一个为多个 COM 类实现单个类对象的示例。类工厂是一个主要目的是创建其他组件的组件。类对象也称为类工厂(类对象和类工厂可互换),其主要目的是通过实现名为 IClassFactory
的标准接口来创建其他组件。请勿混淆这些技术术语,因为不同的作者在解释时使用不同的术语。
COM 提供了一种通用的组件创建方式。所有组件都以类似的方式创建。CoCreateInstance
不提供对组件创建过程的控制。当 CoCreateInstance
返回时,组件已在内存中创建,因此您无法控制组件的创建过程。
IClassFactory
对组件的创建过程提供了更多的控制,但它比 CoCreateInstance
更令人困惑。这可以看作是程序员为了更好地控制创建过程而必须付出的代价。
CoCreateInstance
提供了对 IClassFactory
接口的 CoGetClassObject
和 CreateInstance
方法的封装。CoCreateInstance
在内部为指定的 CLSID
创建一个类工厂,获取 IClassFactory
接口指针,然后通过调用 IClassFactory
接口指针上的 CreateInstance
来创建组件。然后,它通过调用 IClassFactory
的 CreateInstance
方法中的 QueryInterface
方法,将请求的接口指针返回给客户端。
对象创建过程
CoGetClassObject
提供与 CLSID 关联的类对象上的接口指针,它需要 DLL 中的一个入口点来创建组件的类工厂。在大多数情况下,类工厂与组件在同一个 DLL 中实现。入口点称为 DllGetClassObject
,它创建类工厂。客户端通过调用 CoGetClassObject
函数来启动创建过程。CoGetClassObject
在系统注册表中查找组件。如果找到组件,它将加载 COM 服务器(即 DLL)到内存中,然后调用 DllGetClassObject
。DllGetClassObject
的目的是通过为类对象调用 new 运算符来创建类对象。DllGetClassObject
查询类对象以获取 IClassFactory
接口,该接口将返回给客户端。客户端在接收到 IClassFactory
接口指针后,调用 CreateInstance
方法。IClassFactory::CreateInstance
方法调用 new 运算符来创建组件。除了调用 new 运算符,它还在组件上为 iid
接口调用 QueryInterface
。
一旦组件创建完成并将(请求的)接口指针返回给客户端,类对象的目标(即创建组件)就已实现,客户端可以释放该特定组件(CLSID)的类工厂。客户端可以使用返回的组件指针来调用该组件的方法。
每个 COM DLL 服务器都应该实现并导出名为 DllGetClassObject
的函数。如果 COM DLL 未提供 DllGetClassObject
,则调用 CoGetClassObject
或 CoCreateInstance
将返回错误“类未注册”。DllGetClassObject
是一个入口点,它基本上为特定类(CLSID)创建类工厂。CoGetClassObject
在注册表中查找组件,如果找到,则将组件的 CLSID 作为参数传递给 CoGetClassObject
。如果找到服务器,则它加载封装该特定组件的 COM DLL 服务器(调用 COM 服务器的 DllMain
)。加载 DLL 后,CoGetClassObject
尝试通过调用 GetProcAddress
函数来获取 DllGetClassObject
(由 COM DLL 导出)的地址。如果失败,则 COM SCM 返回错误“类未注册”,因为没有办法为请求的组件创建类工厂。
示例代码说明
在示例代码中,为多个 COM 类(COM 组件)实现了一个类对象。类对象和支持的 COM 组件之间可能存在一对一的映射。在这种情况下,DllGetClassObject
应该创建与请求的 CLSID 对应的类对象。这种情况很容易,因为我们可以在 DllGetClassObject
中有多个 if
情况,并为与请求的 CLSID 对应的类对象调用 new
运算符。类对象的目的是创建另一个对象(COM 组件),因此不同类的不同类工厂将导致不必要的代码重复并降低代码的可读性。COM 服务器可以设计为单个类对象能够支持多个 COM 组件。这正是示例代码中实现的内容。
这是通过使用辅助函数(创建器函数)实现的,每个 COM 类都必须支持其创建。有一个名为 FactoryInfo
的结构,它将所有由 COM 服务器公开的 COM 类的创建函数与相应的 CLSID 进行映射。
struct FactoryInfo
{
const CLSID *pCLSID;
FPCOMPCREATOR pFunc;
};
当客户端调用 CoGetClassObject
时,DllGetClassObject
函数会以请求的 CLSID 作为参数被调用。DllGetClassObject
函数会查找全局的 FactoryInfo
结构数组,并遍历该数组以获取与请求的 CLSID 映射的创建器函数的地址。类工厂类将此地址存储在其名为 pCreator
的数据成员中,其类型为 FPCOMPCREATOR
。CFactory
类中存储的此创建函数地址在 IClassFactory
接口的 CreateInstance
方法中使用。创建函数地址在创建 CFactory
时通过在 CFactory
类的构造函数中传递 FPCOMPCREATOR
类型的参数来传递给 CFactory
。// Code snippet.
// Traverse a list to find the helper function which corrosponds to
// the requested CLSID.
for (int iCount = 0; iCount < 2; iCount++)
{
if (*gFactoryData[iCount].pCLSID == clsid)
{
break;
}
}
CFactory *pFactory = new CFactory(gFactoryData[iCount].pFunc);
以上代码是 DlllGetClassObject
函数的一部分。此函数遍历 FactoryData
结构的全局数组,并查找与请求的 CLSID 对应的创建函数地址。一旦获取到创建器函数的地址,它就停止遍历数组并将该地址传递给 CFactory 类的构造函数。CFactory
存储此创建器函数的地址以供后续使用。一旦类对象上的 IClassFactory
接口返回给客户端,客户端就可以调用 IClassFactory
的 CreateInstance
方法来创建 COM 类的实例。CreateInstance
调用是创建 COM 组件并将请求的接口返回到新创建的 COM 组件实例的地方。存储在 CFactory
(类对象)数据成员中的创建器函数地址在 CreateInstance
方法中被调用,并将请求的接口返回给客户端。
//// Code snippet.
typedef HRESULT (*FPCOMPCREATOR) (const IID&, void**);
class CFactory : public IClassFactory
{
public:
// Rest of the code has been removed from here to make it readable.
CFactory(FPCOMPCREATOR);
~CFactory();
private:
/* This is to store the address of the creator function of the
* COM component with the requested CLSID.
*/
FPCOMPCREATOR pCreator;
long m_cRef;
};
这是在 CFactory
类的 CreateInstance
方法中对创建器函数的调用。hResult = (*pCreator)(iid,ppv);
FPCOMPCREATOR
是“指向接受 const IID & 和 void** 作为参数并返回 HRESULT 的函数”的同义词。代码已进行适当注释,使其自解释。多 COM 类的单个类对象将帮助您掌握 COM 中的类工厂概念。