Windows は、COM を通じて様々な機能を開発者に提供します。中には、Windows が搭載する機能を利用中、ハードウェアの状態変化や別スレッドでのロジック実行の完了など様々な要因をトリガーにして、ユーザー プログラム側のコードをコールバックする場合があります。そのようなケースでは、予め定義されているCOM インターフェイスの実装クラスを作成して、COM オブジェクトに登録する方法が採られます。

そして、このようなコールバック型のパターンは、センサーや Media Foundation、Animation Manager 等、様々な COM クラスで出てきます。ここでは、Media Foundation を使ったアプリケーションが、非同期メソッドの完了通知を受けるのに利用する、IMFAsyncCallback インターフェイスを例にとって説明します。

このインターフェイスは、以下の URL で説明されています。

このページを見ると、IMFAsyncCallback には GetParameters と Invoke の 2 つのメソッドが宣言されています。更にこのインターフェイスは COM であるが故に、IUnknown インターフェイスを継承しているので、このインターフェイスを実装するクラスを定義する場合には、IUnknown の仮想メソッドの、AddRef、Release、QueryInterface と前述の 2 メソッドの、計 5 つのメソッドを実装する必要があります。実装クラスの名前を CMyMFAsyncCallback として、このクラスを宣言するヘッダー ファイルを以下に示します。

C++
#include <shlwapi.h> 
#include <mfobjects.h>
 
class CMyMFAsyncCallback: public IMFAsyncCallback  

public
    CMyMFAsyncCallback () : m_cRef(1) { }    // ・・・・・・・・[1] 
    virtual ~ CMyMFAsyncCallback () { } 
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)    // ・・・・・・・・[2] 
    { 
        Static const QITAB qit[]={ 
            QITABENT(CMyMFAsyncCallback, IMFAsyncCallback),{0}}; 
    return QISearch(this, qit, riid, ppv); 
    } 
 
    STDMETHODIMP_(ULONG) AddRef()    // ・・・・・・・・ [3] 
    { 
        return InterlockedIncrement(&m_cRef); 
    } 
    STDMETHODIMP_(ULONG) Release()    // ・・・・・・・・ [4] 
    { 
        long cRef = InterlockedDecrement(&m_cRef); 
        if (cRef == 0
        { 
            delete this
        } 
        return cRef; 
    } 
 
    STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue);    // ・・・・・・・・ [5] 
    STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult);    // ・・・・・・・・ [6] 
private
    long    m_cRef;    // ・・・・・・・・ [7] 
}; 
 

予め定義された COM インターフェイスに対する実装クラス宣言コードのポイントは、IUnknown で定義されている 3 つの仮想関数を実装する [2]、[3]、[4] のメソッドと、COM オブジェクトの参照数を管理するカウンター [7] です。そして、実装対象のインターフェイスで定義されている仮想関数の実装メソッド、になります。

先ず、コンストラクター [1] は、オブジェクトをコンストラクターで生成した時、参照している数の初期値は 1 なので、[7] で宣言しているカウンター変数を 1 に初期化しています。[2] の QueryInterface は、引数 riid で指定されたインターフェイス型に COM オブジェクトを変更する為のメソッドです。実装クラスの名前と、インターフェイス クラスの名前に気をつけてください。[3]、[4] は、COM オブジェクトへの参照数を増やす、のと、減らす為のメソッドです。コールバック用の COM オブジェクトは常にマルチスレッド環境下で異なるスレッドからコールされる可能性があるので、Interlocked メソッドを使ってただ 1 つの制御スレッドしか参照カウンターを増減できないようになっています。

残りの [6]、[7] の 2 つのメソッドは、実装するインターフェイス毎に異なる部分です。それぞれのインターフェイス クラスで定義された仮想関数を宣言してください。この際、メソッドのロジックは、クラス宣言でインライン実装しても、ソース ファイルでロジックを実装しても構いません。

COM で定義されたコールバック用のインターフェイスは多数ありますが、全てここで説明したパターンで実装できます。

後は、

C++
IMFAsyncCallback* callback = new CMyMFAsyncCallback(); 
 

と、コンストラクターでオブジェクトを作成し、規定の COM オブジェクト、メソッドで使います。

COM オブジェクトは複数のロジックで共用されている可能性があるので、通常の C++ のオブジェクトの様にデストラクターでの削除は出来ません。COM オブジェクトを使い終わったら、以下のコードで示すように、Release() メソッドをコールしてください。参照カウンター (m_cRef) が 0 になれば自動的にメモリ上から削除されます。

C++
callback->Release(); 
 

Code Recipe Code Recipe

ページのトップへ