ファクトリクラス
COMでは、coclassの作成にclass objectと呼ばれるファクトリクラスを介することになっています。
class objectは次のように数種類ありますが、標準でつかわれるclass objectのインターフェースはIClassFactoryです。
これらすべてのクラスはIUnknownインターフェースを継承しています。
インターフェース名 | 内容 |
IClassFactory | 標準のファクトリクラス |
IClassFactory2 | ライセンス認証つきのファクトリクラス |
IParseDisplayName | モニカと呼ばれる、特別なクラス作成専用のファクトリクラス |
class objectはcoclassのファクトリクラスですので、coclassとは別のクラスを用意します。
CIchigo1ではIClassFactoryとしてCIchigo1Factoryを実装します。
IClassFactory
IClassFactoryのインターフェースは次のようになっています。
class IClassFactory : public IUnknown
{
public:
HRESULT STDMETHODCALLTYPE CreateInstance( IUnknown* pUnkOuter,
REFIID riid, LPVOID* ppv );
HRESULT STDMETHODCALLTYPE LockServer( BOOL fLock );
};
CreateInstance()はcoclassを作成し、IIDで指定されたインターフェースへのポインタを返します。
pUnkOuterはAggregationと呼ばれるクラスの継承機能に対応する場合に使用します。(pUnkOuterはCoCreateInstance()の引数がそのまま渡されます。)
pUnkOuterがNULL以外であればAggregationのためのクラス作成を意味します。
Aggregationに対応するためにはIUnknownに特別な細工が必要になるため、別途説明します。
初期のサンプルでは、pUnkOuterがNULL以外であればエラー(CLASS_E_NOAGGREGATION)を返すことにします。
LockServer()はDLLの利用状態を制御するためのメソッドです。
LockServer()にTRUEを与えて呼ばれるとDLLのロックカウントを1増やし、
FALSEを与えて呼ばれるとDLLのロックカウントを1減らす機能を持ちます。
LockServer()の実装については次のページで説明します。
Aggregationに対してエラーを返すCreateInstance()の実装は、次のようになります。
CreateInstance()はcoclassを作成して返します。また、エラー時には*ppvにNULLをセットします。
この例では、QueryInterface()のエラーコードをそのまま利用して、
IIDが未知のインターフェースである場合にエラーを返しています。
HRESULT STDMETHODCALLTYPE CIchigo1Factory::CreateInstance(
IUnknown* pUnkOuter,
REFIID riid, LPVOID* ppv )
{
*ppv = NULL;
if ( pUnkOuter != NULL ) {
return CLASS_E_NOAGGREGATION;
}
CIchigo1* pObj = new (std::nothrow) CIchigo1();
if ( pObj == NULL ) {
return E_OUTOFMEMORY;
}
HRESULT hr = pObj->QueryInterface( riid, ppv );
pObj->Release();
return hr;
}
DllGetClassObject
class objectはDllGetClassObject()というDLL外からアクセス可能な関数により、外部に返します。
DllGetClassObject()は次の関数です。
EXTERN_C HRESULT STDAPICALLTYPE DllGetClassObject(
REFCLSID rclsid, REFIID riid, LPVOID* ppv );
DllGetClassObject()はCLSIDで指定されたcoclassに対するclass objectの、
IIDで指定されたインターフェースへのポインタを返す関数です。
COMではclass objectの作成段階で、CLSIDによりcoclassを一意に識別しています。
(CLSIDで区別することにより、1つのDLLで複数のCLSIDに対応することもできます。)
DllGetClassObject()が呼ばれるたびにnewしてもかまいませんし、
作成済みのclass objectの参照カウントを増やし、そのポインタを返すだけでも構いません。
また、CLSIDが未知の場合にはエラーコードCLASS_E_CLASSNOTAVAILABLEを返します。
CIchigo1ではclass objectをstaticに確保しておき、そのポインタを返すことにします。
CIchigo1の実装は次の通りです。
EXTERN_C HRESULT STDAPICALLTYPE DllGetClassObject( REFCLSID rclsid,
REFIID riid, LPVOID* ppv )
{
static CIchigo1Factory g_factory;
*ppv = NULL;
if ( rclsid == CLSID_Ichigo1 ) {
return g_factory.QueryInterface( riid, ppv );
}
return CLASS_E_CLASSNOTAVAILABLE;
}
返すポインタは、QueryInterface()により内部的にAddRef()されることに留意してください。
staticなclass objectの実装
このようにclass objectをstaticに確保する場合には、
クラス作成時点での参照カウントは0になり、また、Release()はdeleteを呼ばないように変更する必要があります。
したがって、CIchigo1Factoryに対するIUnknownのうち、コンストラクタとRelease()は次のようになります。
CIchigo1Factory::CIchigo1Factory() : m_nRef(0)
{
}
ULONG STDMETHODCALLTYPE CIchigo1Factory::Release()
{
--m_nRef;
return m_nRef;
}
関連ページ
COM/ActiveXの解説ページ 目次
前の項目: クラス機能の実装
次の項目: DLL利用状態の管理