更新时间:2024-03-30 13:31
class factory(类工厂) 一个实现了IClassFactory接口的类,这允许它创建特定类的对象,也被称为COM Class Object。类工厂的作用是在COM中,客户程序必须通过类工厂(Class Factory)来完成创建COM对象的任务。
类工厂的实质是一个COM对象,它定义了一个IClassFactory接口,正是这个接口中的CreateInstance成员函数,对实例化COM组件起到了核心作用。
另外,根据COM规范,COM Class和类工厂是配对出现的。也就是说,只要有一个实现某一个或某几个接口的类被编写出来,若客户程序想对其进行实例化,就必须相应地实现与这个COM Class配对的类工厂。更具体地说,就是要实现IClassFactory接口。
举个实际的例子,如果想要使用已经编写的一些类作一些事情,就必须实现类工厂。反之,客户程序将无法访问到类的接口,那就不可能调用其成员函数。
在COM中,客户程序必须通过类工厂(Class Factory)来完成创建COM对象的任务。
注意:
类工厂也被称为COM Class Object,可能这个叫法对理解类工厂的用途更有帮助。
为了透彻了解类工厂的运作机制,让我们看一看类工厂的定义:
class IClassFactory : public IUnknown
{
virtual HRESULT CreateInstance(LPUNKNOWN pUnk ,
REFIID riid, void ** ppv) = 0
virtual HRESULT LockServer(BOOL fLock) = 0;
}
为COM对象实现类工厂,首先,进行类工厂的声明,代码如下:
class CSimpleMath_ClassFactory : public IClassFactory
// 声明用于实现IClassFactory接口的对象
{
protected:
long m_RefCount;
public:
CSimpleMath_MathClassFactory();
~ CSimpleMath_MathClassFactory();
// 由于IClassFactory接口继承自IUnknown接口
// 所以,也继承了IUnknown接口的三个方法
HRESULT QueryInterface(REFIID, void** );
ULONG AddRef();
ULONG Release();
// IClassFactory接口本身需要实现的方法
HRESULT CreateInstance(LPUNKNOWN, REFIID, void**);
HRESULT LockServer(BOOL bLock);
};
然后,具体实现的代码如下:
CSimpleMath_ClassFactory:: CSimpleMath_ClassFactory ()
{
// 对象构造时初始化引用计数为0
m_lRef = 0;
}
CSimpleMath_ClassFactory::~ CSimpleMath_ClassFactory ()
{
}
HRESULT CSimpleMath_ClassFactory::QueryInterface( REFIID riid, void** ppv )
{
*ppv = 0;
// 如果查询的接口为IUnknown或类工厂接口,则返回此实例
if ( riid == IID_IUnknown || riid == IID_ISimpleMathClassFactory )
*ppv = this;
if ( *ppv )
{
// 如果成功地引用到接口,则增加接口的引用计数
AddRef();
return S_OK;
}
return(E_NOINTERFACE);
}
ULONG CSimpleMath_ClassFactory::AddRef()
{
m_RefCount ++;
return m_RefCount;
}
ULONG CSimpleMath_ClassFactory::Release()
{
m_RefCount --; // 减小引用计数
if (m_RefCount == 0 )
{
// 如果引用计数减少至0,则释放之
delete this;
return 0;
}
return m_RefCount;
}
// *****************************//
// 用来产生COM对象的重要方法
// *****************************//
HRESULT CSimpleMath_ClassFactory::CreateInstance
( LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj )
{
CSimple_Math * p; // 声明指向COM Class的指针
HRESULT hr;
*ppvObj = 0;
// 创建COM Class的实例
p = new CSimple_Math;
// 如果没有成功地创建对象,则返回错误值
if (!p)
return(E_OUTOFMEMORY)
// 如果得到了对象,则可以调用QueryInterface方法
hr = p->QueryInterface( riid, ppvObj );
// 如果调用失败则做好善后工作,并将最终的状态返回
if ( FAILED( hr ) )
delete p;
return hr;
}
HRESULT CSimpleMath_ClassFactory::LockServer( BOOL bLock )
{
if ( bLock )
{
// 注意:g_LockedCount是COM对象中声明的一个全局计数变量
InterlockedIncrement(g_LockedCount) ;
}
else
{
// 注意:g_LockedCount是COM对象中声明的一个全局计数变量
// 为了保障线程安全,需要对全局变量进行保护
InterlockedDecrement(g_LockedCount);
}
return S_OK;
}
这样就完成了类工厂的具体实现。在整个实现代码中,最关键的部分使用粗体字进行了标注。经过这样一番精心实现之后,客户程序便可以通过实例化类工厂COM对象,进而得到类工厂的IClassFactory接口指针,然后,通过对该接口中核心方法CreateInstance的调用完成COM对象的实例化。剩下的事情不言自喻—— 尽情调用COM对象中的服务吧。
在开始介绍类工厂之前,先提一个小问题:
下面代码产生错误的原因是什么?
class CFoolish
{
public:
CFoolish();
~CFoolish();
int m_count;
…
}
int main()
{
CFoolish::m_count = 100;
return 0;
}
答案实在太明显了—— 没有创建对象就要访问类对象的非静态成员,才出错。创建完一个接口,并按照标准COM的要求实现了它,使客户程序可以通过QueryInterface方法来得到对象中的接口。但是,有一个问题尚未解决,那就是,我们没有看到一行可以令COM对象在内存中实例化的代码,因此,如果此时使用这个接口,就会犯与前面所提的简单问题中同种类型的错误。并且,需要提醒大家的是,由于COM对象的复杂性(与前面所讲的引用计数一样),COM实例的创建仍然要依赖内部机制来管理,而不能像普通语言对象一样,由客户程序使用简单的new、delete来控制。