ゼロからのCOM - class object

いちごパック > COM/ActiveXの解説 > ゼロからのCOM - class object

ファクトリクラス

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利用状態の管理