ゼロからのCOM - IUnknownとその実装

いちごパック > COM/ActiveXの解説 > ゼロからのCOM - IUnknownとその実装

COMの基本インターフェース

今までの説明でも少し触れてきましたが、COMのインターフェースは共通メソッドを3つ持ちます。 それらのメソッドをまとめたインターフェースはIUnknownと呼ばれ、 COMのインターフェースはすべてIUnknownを継承したインターフェースになっています。 IUnknownの正確なインターフェースは次の通りです。
class IUnknown
{
public:
    HRESULT STDMETHODCALLTYPE QueryInterface(
        REFIID riid, LPVOID* ppv );
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
};
今までの説明で省略してきた、STDMETHODCALLTYPEという見慣れないキーワードが指定されています。 STDMETHODCALLTYPEはメソッド呼び出しをバイナリコードとして表現する際の方法を指定するキーワードで、 Visual C++では__stdcallキーワードに対応します。 STDMETHODCALLTYPEを指定すると、他の言語とのバイナリ互換性が高い方法でメソッドを呼び出すようコンパイラに指示します。 coclassを自分で実装する場合には、STDMETHODCALLTYPEで呼び出すことを覚えておいてください。
以下、各メソッドとその実装についてみていきます。

AddRef

coclassの参照カウントを1だけ増やし、 参照カウントが0以外であれば0以外の値を、0であれば0を返します。 参照カウントそのものを返してもかまいません。
マルチスレッドを考慮しない実装の1例を示します。
ULONG STDMETHODCALLTYPE CIchigo1::AddRef()
{
    return ++m_nRef;
}

Release

coclassの参照カウントを1だけ減らし、 0の場合にはcoclassのインスタンスを解放します。 また、参照カウントが0以外であれば0以外の値を、0であれば0を返します。 参照カウントそのものを返してもかまいません。
マルチスレッドを考慮しない実装の1例を示します。
ULONG STDMETHODCALLTYPE CIchigo1::Release()
{
    if ( --m_nRef == 0 ) {
        delete this;
        return 0;
    }
    return m_nRef;
}

QueryInterface

coclassが実装しているインターフェースに対し、 そのインターフェースへのポインタとS_OKを返します。 このメソッドを実装する際は、必ずIUnknownインターフェースにも対応する必要があります。 coclassが実装していないインターフェースが要求された場合には、 ポインタをNULLとしてE_NOINTERFACEを返します。 たとえば、ISequentialStreamインターフェースのみを実装するクラスであれば次の実装になります。
HRESULT STDMETHODCALLTYPE
CIchigo1::QueryInterface( REFIID riid, LPVOID* ppv )
{
    *ppv = NULL;
    if ( riid == IID_IUnknown || riid == IID_ISequentialStream ) {
        *ppv = static_cast<ISequentialStream*>( this );
        AddRef();
        return S_OK;
    }
    return E_NOINTERFACE;
}
QueryInterface()で同一のcoclassに対するIUnknownへのポインタを取得した場合、 そのポインタが常に同一になる必要があります。 この例ではインターフェースが1つしかありませんので問題ありませんが、 多数のインターフェースをサポートする場合は注意が必要です。
この実装例にはシングルスレッドに依存したコードは含まれていませんが、 AddRef()がシングルスレッドに依存していればシングルスレッド依存のコードになります。

クラスの宣言とその確保

ISequentialStreamを実装し、 参照カウントに対応したCIchigo1クラスの宣言は次のようになります。
class CIchigo1 : public ISequentialStream
{
public:
    CIchigo1() : m_nRef(1) {}
    // IUnknown
    HRESULT STDMETHODCALLTYPE QueryInterface(
        REFIID riid, LPVOID* ppv );
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();

... ISequentialStreamの実装 ... private: LONG m_nRef; };
このcoclassを確保する場合は、次のようにnewを使います。
CIchigo1* pobj = new CIchigo1();
コンストラクタにより、newされた時点で参照カウントが1であることに留意してください。 このcoclassに対して参照カウントの操作なくRelease()が呼ばれれば、このcoclassはdeleteされることになります。

関連ページ

  • COM/ActiveXの解説ページ 目次
  • 前の項目: クラスの作成とその利用
  • 次の項目: クラス機能の実装