このアーカイブは同期化されません。 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 クラスの
新しいインスタンスが作成できないのである。
これは、以前も話題に取り上げた多重継承が原因である。
明日は、このコンパイルエラーを解決しよう。