COMクライアントの流れ
COMをライブラリとして使うソフトウェアをCOMクライアントといいます。
COMクライアントは次の手順でCOMのクラスインスタンスを利用します。
現在のスレッドに対してCOMシステムを初期化処理を行う。
COMのクラスインスタンスをCoCreateInstance()により作成する。
必要なCOMのインターフェースを取得する。
インターフェースとして提供されたメソッドを呼び出す。
取得済みインターフェースへのポインタをすべてRelease()することにより、クラスインスタンスを解放する。
現在のスレッドに対するCOMシステムの終了処理を行う。
サンプルCOMクライアントの内容
ここではCOMクライアントのサンプルとして、
メディアファイル再生プログラムを作成します。
.wavや.aviといったメディアファイルの再生は、
DirectShowライブラリ内のCLSID_FilterGraphクラスによって提供されています。
CLSID_FilterGraphクラスはIMediaControlインターフェース、
IMediaEventインターフェースを含む複数のインターフェースを実装しています。
その概略は次のようになります。
class IMediaControl // IID_IMediaControl
{
public:
virtual HRESULT RenderFile(BSTR strFilename) = 0;
virtual HRESULT Run() = 0;
その他のメソッド
};
class IMediaEvent // IID_IMediaEvent
{
public:
virtual HRESULT WaitForCompletion(
long msTimeout,long *pEvCode) = 0;
その他のメソッド
};
class CFilterGraph // CLSID_FilterGraph
: public IMediaControl
, public IMediaEvent
{
メソッドの実装
};
サンプルCOMクライアントでは、
CLSID_FilterGraphで識別されるCOMのクラスを作成し、そのメソッドを次の順に呼び出します。
RenderFile()によりメディアファイルを読み込む。
Run()によりメディアファイルを再生する。
WaitForCompletion()により再生終了を待つ。
COMの初期化と終了時処理
COMを使うスレッドは、COMを初期化する必要があります。
また、スレッド終了時にはCOMの終了時処理が必要になります。
これらを行う関数は次の通りです。
HRESULT CoInitialize(void* pReserved);
HRESULT CoUninitialize();
CoInitialize()は、スレッドと1対1に対応付けされるApartmentである
Single Thread Apartment(STA)を作成するための初期化関数です。
その動作は次のようになります。
スレッドの状態 | 動作 |
未初期化 | Single Thread Apartment(STA)を作成し、スレッドをSTAに所属させてS_OKを返す。 |
STAに所属 | 内部の初期化カウントを増やしてS_FALSEを返す。 |
別のApartmentに所属 | エラーを返す。 |
CoInitialize()の引数pReservedにはNULLを与えます。
また、CoInitialize()がS_FALSEを返した場合も処理としては成功であり、
hrを戻り値としてSUCCEEDED(hr)が成功であればCoUninitialize()を呼び出す必要があります。
このサンプルでは扱いませんが、
STA以外のApartmentを扱う場合にはCoInitializeEx()により初期化します。
Apartmentについては、マルチスレッドの説明の際に詳しく説明します。
COMクラス(coclass)の確保
COMのクラスはコクラス(coclass)と呼ばれています。
C++のクラスインスタンスをnewによって確保するのと同じように、
coclassのインスタンスはCoCreateInstnace()関数によって確保します。
CoCreateInstance()はC++でのファクトリクラスに似た機能を持ち、
クラスのインスタンスを確保してインターフェースへのポインタを返します。
CoCreateInstance()は次の通りです。
HRESULT CoCreateInstance(
REFCLSID rclsid, IUnknown* punkAggOuter,
DWORD dwCLSCTX, REFIID riid, LPVOID* ppvObj);
rclsidとriidでcoclassとインターフェースの128ビットGUIDを指定し、
確保したいcoclassとそのインターフェースを識別します。
確保したcoclass内インターフェースへのポインタは *ppvObj に返されます。
dwCLSCTXはクラスの検索範囲を表すフラグをORで指定するもので、例えば次のフラグが利用できます。
通常は CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER を指定しておけば良いでしょう。
CLSCTX_INPROC_SERVER | DLLとして実装されたCOMサーバを検索します。 |
CLSCTX_INPROC_HANDLER | インターフェース呼び出し時の中間ハンドラとして、カスタムハンドラを利用可能にします。 |
CLSCTX_LOCAL_SERVER | EXEとして実装されたCOMサーバを検索します。 |
CLSCTX_REMOTE_SERVER | リモートマシンで起動されたCOMサーバを検索します。 |
CLSCTX_INPROC_HANDLERを指定しておかないと、カスタムハンドラを必要とするCOMサーバは動作しなくなります。
punkAggOuterはAggregationと呼ばれるクラスの継承機能を使ってCOMサーバを実装する際に使用します。
この例のように、COMクライアントとしてcoclassを作成する場合はNULLを与えます。
Aggregationを使ったCOMサーバの例は別途説明します。
coclassの作成例
以上を踏まえ、coclassとしてCLSID_FilterGraphを作成し、
IID_IMediaControlで識別されたインターフェースを取得するコードは次のようになります。
説明のため、エラー処理は省略しています。
IMediaControl* pIFMediaControl = NULL;
HRESULT hr;
hr = CoInitialize(NULL);
hr = CoCreateInstance(
CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER,
IID_IMediaControl, (LPVOID*)&pIFMediaControl);
別インターフェースの取得
すでに説明した通り、coclassのすべてのインターフェースはQueryInterface()メソッドを備えています。
QueryInterface()メソッドは次の通りです。
coclassオブジェクトのインターフェースであればどのインターフェースであっても、
QueryInterface()メソッドによってその他のインターフェースへのポインタを取得できます。
HRESULT QueryInterface(REFIID riid, LPVOID* ppv);
QueryInterfaceの使用例
IID_IMediaEventで識別されたインターフェースを取得するコードは次のようになります。
IMediaEvent* pIFMediaEvent = NULL;
HRESULT hr;
hr = pIFMediaControl->QueryInterface(
IID_IMediaEvent, (LPVOID*)&pIFMediaEvent );
文字列のBSTR変換
このクラスの場合、RenderFile()メソッドは引数としてBSTRを必要とします。
UNICODE文字列であれば、次の例のようにBSTRに変換できます。
#define PLAYSAMPLEW L"c:\\windows\\media\\chimes.wav"
BSTR bstr = SysAllocString(PLAYSAMPLEW);
BSTRの利用後は、SysFreeString()を呼び出して解放します。
CLSID、IIDのリンク
DirectShowの場合、dshow.hを#includeするとCLSIDやIIDをDEFINE_GUIDしたヘッダファイルが取り込まれます。
したがって、initguid.hを定義すればリンクエラーは起こりません。
また、DirectShowのようにマイクロソフトが配布するライブラリであれば、
initguid.h済みのライブラリを配布していますので、リンク時にこちらを指定してもかまいません。
DirectShowの場合は、initguid.h済みのライブラリとしてamstrmid.libが配布されています。
今回はプロジェクトの設定により、このファイルをリンクすることにします。
メソッドの呼び出し
coclassを作成し、必要なインターフェースへのポインタを取得できたら、
インターフェースのマニュアル等にしたがってメソッドを呼び出します。
DirectShowを用いてファイル再生を行う場合は、次の手順で呼び出すことでファイルを再生できます。
この例ではWindowsのchimes.wavを再生していますが、
DirectShowが対応していれば、動画ファイルなども再生できます。
HRESULT hr;
BSTR bstr = NULL;
long evCode = 0;
hr = pIFMediaControl->RenderFile( bstr );
hr = pIFMediaControl->Run();
hr = pIFMediaEvent->WaitForCompletion( INFINITE, &evCode );
サンプルプログラム
エラー処理を追加した、ビルド可能なコードを
ゼロからのCOM - サンプルCOMクライアントにおきましたので、
こちらをご覧ください。
関連ページ
COM/ActiveXの解説ページ 目次
前の項目: COMの目的とその仕組み
次の項目: IUnknownとその実装