2006 年 11 月 25 日 21 時 53 分

HardLinkIconIDClass クラス


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


HardLinkIconID クラスをクライアントに公開するためには、
専用のクラスオブジェクトを作成する必要がある。

クラスオブジェクトには、IClassFactory を実装する。
パフォーマンスを考え、クラスオブジェクトは
簡単なシングルトンとして作成し、
1 つのインスタンスが常に存在するようにしておこう。

そのため、クラスオブジェクトに対しては、
参照カウンタを使った生存管理は必要ない。
そのため、Object クラスは継承させないことにする。

クラス名は HardLinkIconIDClass としよう。
まず、最初にクラスの定義を作成する。

========== HardLinkIconIDClass.hpp ==========

#ifndef hardlinkiconidclass_hpp_included
#define hardlinkiconidclass_hpp_included

// IClassFactory
#include <unknwn.h>

class HardLinkIconIDClass : public IClassFactory {

// インタフェース
public:

    // IUnknown のメソッド

    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
            /* [in] */ REFIID riid,
            /* [out] */ void** ppvObject);

    virtual ULONG STDMETHODCALLTYPE AddRef();

    virtual ULONG STDMETHODCALLTYPE Release();

    // IClassFactory のメソッド
   
    virtual HRESULT STDMETHODCALLTYPE CreateInstance(
        /* [in] */ IUnknown* pUnkOuter,
        /* [in] */ REFIID riid,
        /* [out] */ void** ppvObject);

    virtual HRESULT STDMETHODCALLTYPE LockServer(
        /* [in] */ BOOL fLock);

// コンストラクタ・デストラクタ
public:
    HardLinkIconIDClass();
    virtual ~HardLinkIconIDClass();

// 複製の禁止
private:
    HardLinkIconIDClass(const HardLinkIconIDClass&);
    HardLinkIconIDClass& operator=(const HardLinkIconIDClass&);

};

#endif // !hardlinkiconidclass_hpp_included

========== end of HardLinkIconIDClass.hpp ==========

ヘッダの定義は、以前の Object の時に似ている。
インタフェースのメソッドを全て明示し、複製を禁止する。
ここまでくると、特に引っかかる点もないと思う。

では、実装を見ていこう。

========== HardLinkIconIDClass.cpp ==========

// ビルド環境用のヘッダ
#include "global.hpp"

#include "HardLinkIconIDClass.hpp"
#include "HardLinkIconID.hpp"

// std::nothrow
#include <new>

HardLinkIconIDClass::HardLinkIconIDClass() {
}

HardLinkIconIDClass::~HardLinkIconIDClass() {
}

========== (HardLinkIconIDClass.cpp) ==========

cpp ファイルはコンパイルされるので、
まずは共通となる global.hpp を取り込んでおく。

そして、HardLinkIconIDClass 自身の定義と、
作成するインスタンスである、
HardLinkIconID の定義も合わせて取り込んでおく。

コンストラクタやデストラクタは使わないので空にしておく。

========== (HardLinkIconIDClass.cpp) ==========

// IUnknown メソッド

HRESULT HardLinkIconIDClass::QueryInterface(
        /* [in] */ REFIID riid,
        /* [out] */ void** ppvObject) {

    if (ppvObject == NULL) return E_POINTER;
    *ppvObject = NULL;

    if (IsEqualIID(riid, IID_IClassFactory)) {
        *ppvObject = static_cast<IClassFactory*>(this);
    } else if (IsEqualIID(riid, IID_IUnknown)) {
        *ppvObject = static_cast<IUnknown*>(this);
    } else {
        return E_NOINTERFACE;
    }

    static_cast<IUnknown*>(*ppvObject)->AddRef();

    return S_OK;
}

ULONG HardLinkIconIDClass::AddRef() {
    // サーバをロック
    ++g_serverLockCount;
    // インスタンスの参照カウンタは使わない
    return 1;
}

ULONG HardLinkIconIDClass::Release() {
    // サーバをアンロック
    --g_serverLockCount;
    // インスタンスの参照カウンタは使わない
    return 1;
}

========== (HardLinkIconIDClass.cpp) ==========

まずは、IUnknown の実装だ。
QueryInterface は毎度おなじみ。
IUnknown と IClassFactory に対応しておく。

DllCanUnloadNow に対応するためには、
AddRef や Release は、インスタンス自身と、
それを含むサーバのロック状態を管理する必要がある。

クラスオブジェクトは常時存在させるので、
インスタンス自身の参照カウンタは不要なのだが、
AddRef や Release は、0 以外の値を返す必要がある。
これは、クラスオブジェクトも COM クラスであるためだ。

========== (HardLinkIconIDClass.cpp) ==========

HRESULT STDMETHODCALLTYPE HardLinkIconIDClass::CreateInstance(
        /* [in] */ IUnknown* pUnkOuter,
        /* [in] */ REFIID riid,
        /* [out] */ void** ppvObject) {

    // ppvObject が NULL なら例外
    if (ppvObject == NULL) return E_POINTER;

    // COM 集約はサポートしない
    if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION;

    // ppvObject は out なので、
    // ポインタが指す元の値は無視できる
    *ppvObject = NULL;

    // インスタンスを作成
    HardLinkIconID* instance = new (std::nothrow) HardLinkIconID();
    if (instance == NULL) return E_OUTOFMEMORY;

    // 参照を追加
    instance->AddRef();

    // インタフェースを要求
    HRESULT result = instance->QueryInterface(riid, ppvObject);

    // 参照を解放
    instance->Release();

    return result;
}

HRESULT STDMETHODCALLTYPE HardLinkIconIDClass::LockServer(
        /* [in] */ BOOL fLock) {

    if (fLock) {
        ++g_serverLockCount;
    } else {
        --g_serverLockCount;
    }

    return S_OK;
}

========== end of HardLinkIconIDClass.cpp ==========

そして、IClassFactory の実装だ。

CreateInstance は、新しいインスタンスを作成する。

HardLinkIconID クラスは集約に対応していないため、
pUnkOuter 引数が NULL 以外の場合は、
CLASS_E_NOAGGREGATION を返却しておく。

HardLinkIconID の新しいインスタンスを作成した後は、
AddRef を呼び出して、参照カウンタを増やしておく。

HardLinkIconID* はインタフェースではないが、
作成当初はカウンタが 0 なので、
そのままだと参照が存在しない不安定な状態となるからだ。

作成が成功すると、QueryInterface を呼び出して、
クライアントが要求したインタフェースポインタを得る。

それが終われば、Release で解放しておく。
この Release は HardLinkIconID* を解放する意味だ。

QueryInterface が成功した場合、AddRef が呼ばれるため、
この Release が呼ばれてもインスタンスは破棄されない。
QueryInterface が失敗した場合、
この Release によってインスタンスが破棄される。
なのでうまくいくというわけだ。

さて、コンパイルしてみよう。
今回はエラーが発生するはずだ。
HardLinkIconID を new する行や、
AddRef, Release 呼び出しでエラーがでるはずである。

例えば、Visual C++ の場合は、
「抽象クラスをインスタンス化できません」となる。
何かしらの理由で、HardLinkIconID クラスの
新しいインスタンスが作成できないのである。

これは、以前も話題に取り上げた多重継承が原因である。
明日は、このコンパイルエラーを解決しよう。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.