2006 年 11 月 21 日 23 時 59 分

クライアントから見たインスタンスの作成


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


これまでは、COM クラスの実装や利用について書いていたが、
肝心のインスタンスの作成に関しては説明していなかった。

COM クライアントが COM クラスを利用するためには、
既存のインスタンスを取得するか、
新しいインスタンスを作成する必要がある。
一般的には後者の方が良く使われる。

COM はクライアントサーバシステムであるため、
クライアントがクラスのインスタンスを作成する際には、
C++ 固有の new 演算子を使うことはできない。

COM には CoCreateInstance という関数が用意されており、
COM クラスの新しいインスタンスを作成する機能を持つ。
VBA の CreateObject 関数も、これを内部で使っている。

STDAPI CoCreateInstance(
        /* [in] */ REFCLSID rclsid,
        /* [in] */ IUnknown* pUnkOuter,
        /* [in] */ DWORD dwClsContext,
        /* [in] */ REFIID riid,
        /* [out] */ void** ppvObject);

引数が多いが、とりあえず pUnkOuter と dwClsContext は、
今のところは無視することにしよう。

rclsid は、クラス ID である。
インタフェースに ID があるように、クラスにも ID がある。
クラスのインスタンスを作成するためには、
そのクラスの ID を知っている必要がある。

riid は、要求するインタフェースの ID である。
COM には「クラスのポインタ」というのは存在しないので、
クラスのインスタンスを作成したとしても、
必ずインタフェースのポインタでそれを受け取る。
そのため、インタフェースの ID の指定が必要なのだ。

ppvObject には、生成されたインスタンスが返る。
riid と ppvObject は、IUnknown の、
QueryInterface メソッドの引数と同じような意味である。

riid に、インスタンスがサポートしていない
インタフェース ID を指定すると、
インスタンスは作成されずにエラー値が返却される。

ほとんどのサーバでは、サーバでインスタンスを作成した後、
QueryInterface を呼ぶことで実装しているため、
インタフェースの不一致で失敗すると、
インスタンスはすぐに削除されてしまう事になる。

なので、最初は IID_IUnknown を指定しておき、
後で QueryInterface を呼ぶことで、
実装しているインタフェースを問い合わせるのが良い方法だ。

もちろん、実装しているインタフェースが明らかな場合は、
限定的なインタフェース ID を指定しても構わない。

クライアントは、CoCreateInstance を呼び出すことにより、
サーバの提供するクラスのインスタンスを作成し、
それを操作するインタフェースポインタを得ることができる。

HRESULT ToggleDesktop() {
    IUnknown* instance = NULL;
    HRESULT result = S_OK;

    result = CoCreateInstance(
            CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
            IID_IUnknown, (void**)&instance);

    if (FAILED(result)) return result;

    IShellDispatch4* disp4 = NULL;
    result = instance->QueryInterface(
            IID_IShellDispatch4, (void**)&disp4);

    if (SUCCEEDED(result)) {
        result = disp4->ToggleDesktop();
    }

    if (disp4) disp4->Release();
    if (instance) instance->Release();

    return result;
}

これはデスクトップの表示を切り替える例である。

CLSID_Shell は、シェルオブジェクトのクラスだが、
IShellDispatch4 は、シェルのバージョンによって、
実装されていない可能性もあるので、
IUnknown で確実にインスタンスを取得してから、
QueryInterface で IShellDispatch4 を問い合わせている。

この例では、ToggleDesktop メソッドを呼ぶだけなので、
最初から IShellDispatch4 を要求したほうがシンプルだが、
作成したインスタンスに対して、
別のインタフェースを要求して使い続ける場合は、
上記のように IUnknown を保持しておく方が好ましいのだ。



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