次の方法で共有


TN039: MFC/OLE オートメーションの実装

次のテクニカル ノートは、最初にオンライン ドキュメントに含まれてから更新されていません。 その結果、一部の手順やトピックが古くなっているか、正しくない可能性があります。 最新情報については、オンライン ドキュメント インデックスで関心のあるトピックを検索することをお勧めします。

OLE IDispatch インターフェイスの概要

IDispatch インターフェイスは、Visual BASIC などの他のアプリケーションや他の言語がアプリケーションの機能を利用できるように、アプリケーションがメソッドとプロパティを公開する手段です。 このインターフェイスの最も重要な部分は、 IDispatch::Invoke 関数です。 MFC では、"ディスパッチ マップ" を使用して IDispatch::Invokeを実装します。 ディスパッチ マップは、オブジェクトのプロパティを直接操作したり、IDispatch::Invoke要求を満たすためにオブジェクト内のメンバー関数を呼び出したりできるように、CCmdTarget派生クラスのレイアウトまたは "図形" に関する MFC 実装情報を提供します。

ほとんどの場合、ClassWizard と MFC は連携して、OLE オートメーションのほとんどの詳細をアプリケーション プログラマに隠します。 プログラマは、アプリケーションで公開する実際の機能に集中し、基になる配管について心配する必要はありません。

ただし、MFC がバックグラウンドで実行していることを理解する必要がある場合があります。 このメモでは、フレームワークが DISPIDをメンバー関数とプロパティに割り当てる方法について説明します。 MFC が DISPIDの割り当てに使用するアルゴリズムに関する知識は、アプリケーションのオブジェクトの "タイプ ライブラリ" を作成する場合など、ID を知る必要がある場合にのみ必要です。

MFC DISPID の割り当て

オートメーションのエンド ユーザー (Visual Basic ユーザーなど) は、オートメーションが有効になっているプロパティとメソッドの実際の名前をコード (obj など) で確認します。ShowWindow)、 IDispatch::Invoke の実装は実際の名前を受け取りません。 最適化の理由から、DISPID を受け取ります。 DISPID は、アクセスされるメソッドまたはプロパティを記述する 32 ビットの "マジック Cookie" です。 これらの DISPID 値は、IDispatch::GetIDsOfNamesと呼ばれる別のメソッドを介して、IDispatch実装から返されます。 オートメーション クライアント アプリケーションは、アクセスするメンバーまたはプロパティごとに GetIDsOfNames を 1 回呼び出し、後で IDispatch::Invokeを呼び出すためにキャッシュします。 この方法では、高価な文字列検索は、 IDispatch::Invoke 呼び出しごとに 1 回ではなく、オブジェクトの使用ごとに 1 回だけ実行されます。

MFC は、次の 2 つのことに基づいて、メソッドとプロパティごとに DISPIDを決定します。

  • ディスパッチ マップの上端からの距離 (1 相対)

  • 最も派生したクラスからのディスパッチ マップの距離 (相対 0)

DISPID は 2 つの部分に分かれています。 DISPIDLOWORD には、ディスパッチ マップの上端からの距離である最初のコンポーネントが含まれています。 HIWORD には、最も派生したクラスからの距離が含まれています。 例えば次が挙げられます。

class CDispPoint : public CCmdTarget
{
public:
    short m_x, m_y;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

class CDisp3DPoint : public CDispPoint
{
public:
    short m_z;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

BEGIN_DISPATCH_MAP(CDispPoint, CCmdTarget)
    DISP_PROPERTY(CDispPoint, "x", m_x, VT_I2)
    DISP_PROPERTY(CDispPoint, "y", m_y, VT_I2)
END_DISPATCH_MAP()

BEGIN_DISPATCH_MAP(CDisp3DPoint, CDispPoint)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
END_DISPATCH_MAP()

ご覧のように、2 つのクラスがあり、どちらも OLE オートメーション インターフェイスを公開しています。 これらのクラスの 1 つは他のクラスから派生しているため、OLE オートメーション パーツ (この場合は "x" プロパティと "y" プロパティ) を含む基底クラスの機能を活用します。

MFC では、クラス CDispPoint の DISPIDが次のように生成されます。

property X    (DISPID)0x00000001
property Y    (DISPID)0x00000002

プロパティは基底クラスに含まれていないため、DISPIDHIWORD は常に 0 です (CDispPoint の最も派生クラスからの距離は 0 です)。

MFC では、クラス CDisp3DPoint の DISPIDが次のように生成されます。

property Z    (DISPID)0x00000001
property X    (DISPID)0x00010001
property Y    (DISPID)0x00010002

Z プロパティには、プロパティ CDisp3DPoint を公開しているクラスで定義されているため、HIWORD が 0 の DISPID が指定されます。 X プロパティと Y プロパティは基底クラスで定義されているため、DISPIDHIWORD は 1 です。これらのプロパティが定義されているクラスは、最も派生したクラスから 1 つの派生の距離に存在するためです。

LOWORD は、明示的な DISPID を持つエントリがマップ内に存在する場合でも、常にマップ内の位置によって決定されます (DISP_PROPERTYおよびDISP_FUNCTIONマクロの_IDバージョンについては、次のセクションを参照してください)。

MFC ディスパッチ マップの高度な機能

この Visual C++ リリースでは、ClassWizard でサポートされていない追加機能がいくつかあります。 ClassWizard では、メソッド、メンバー変数プロパティ、および get/set メンバー関数プロパティをそれぞれ定義する DISP_FUNCTIONDISP_PROPERTY、および DISP_PROPERTY_EX がサポートされています。 これらの機能は通常、ほとんどの自動化サーバーを作成するために必要なすべてです。

ClassWizard でサポートされているマクロが適切でない場合は、 DISP_PROPERTY_NOTIFYDISP_PROPERTY_PARAMの追加マクロを使用できます。

DISP_PROPERTY_NOTIFY — マクロの説明

DISP_PROPERTY_NOTIFY(
    theClass,
    pszName,
    memberName,
    pfnAfterSet,
    vtPropType)

パラメーター

theClass
クラスの名前。

pszName
プロパティの外部名。

memberName
プロパティが格納されているメンバー変数の名前。

pfnAfterSet
プロパティが変更されたときに呼び出すメンバー関数の名前。

vtPropType
プロパティの型を指定する値。

注釈

このマクロは、追加の引数を受け取る点を除いて、DISP_PROPERTYとよく似ています。 追加の引数 pfnAfterSet は、 何も返せず、パラメーター 'void OnPropertyNotify()' を受け取らないメンバー関数である必要があります。 メンバー変数が変更された に呼び出されます。

DISP_PROPERTY_PARAM — マクロの説明

DISP_PROPERTY_PARAM(
    theClass,
    pszName,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

パラメーター

theClass
クラスの名前。

pszName
プロパティの外部名。

memberGet
プロパティの取得に使用するメンバー関数の名前。

memberSet
プロパティの設定に使用するメンバー関数の名前。

vtPropType
プロパティの型を指定する値。

vtsParams
各パラメーターのスペース区切りVTS_の文字列。

注釈

DISP_PROPERTY_EX マクロと同様に、このマクロは、個別の Get および Set メンバー関数を使用してアクセスされるプロパティを定義します。 ただし、このマクロを使用すると、プロパティのパラメーター リストを指定できます。 これは、他の方法でインデックス付けまたはパラメーター化されるプロパティを実装する場合に便利です。 パラメーターは常に最初に配置され、その後にプロパティの新しい値が続きます。 例えば次が挙げられます。

DISP_PROPERTY_PARAM(CMyObject, "item", GetItem, SetItem, VT_DISPATCH, VTS_I2 VTS_I2)

は、メンバー関数の取得と設定に対応します。

LPDISPATCH CMyObject::GetItem(short row, short col)
void CMyObject::SetItem(short row, short col, LPDISPATCH newValue)

DISP_XXXX_ID — マクロの説明

DISP_FUNCTION_ID(
    theClass,
    pszName,
    dispid,
    pfnMember,
    vtRetVal,
    vtsParams)
DISP_PROPERTY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    vtPropType)
DISP_PROPERTY_NOTIFY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    pfnAfterSet,
    vtPropType)
DISP_PROPERTY_EX_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType)
DISP_PROPERTY_PARAM_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

パラメーター

theClass
クラスの名前。

pszName
プロパティの外部名。

dispid
プロパティまたはメソッドの固定 DISPID。

pfnGet
プロパティの取得に使用するメンバー関数の名前。

pfnSet
プロパティの設定に使用するメンバー関数の名前。

memberName
プロパティにマップするメンバー変数の名前

vtPropType
プロパティの型を指定する値。

vtsParams
各パラメーターのスペース区切りVTS_の文字列。

注釈

これらのマクロを使用すると、MFC に自動的に割り当てるのではなく 、DISPID を指定できます。 これらの高度なマクロの名前は同じですが、マクロ名 ( 例: DISP_PROPERTY_ID) に ID が追加され、ID は pszName パラメーターの直後に指定されたパラメーターによって決まります。 AFXDISP を参照してください。これらのマクロの詳細については、H を参照してください。 _IDエントリは、ディスパッチ マップの末尾に配置する必要があります。 これらは、マクロの非_IDバージョンと同じように、自動 DISPID 生成に影響します (DISPIDは位置によって決定されます)。 例えば次が挙げられます。

BEGIN_DISPATCH_MAP(CDisp3DPoint, CCmdTarget)
    DISP_PROPERTY(CDisp3DPoint, "y", m_y, VT_I2)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
    DISP_PROPERTY_ID(CDisp3DPoint, "x", 0x00020003, m_x, VT_I2)
END_DISPATCH_MAP()

MFC では、次のように CDisp3DPoint クラスの DISPID が生成されます。

property X    (DISPID)0x00020003
property Y    (DISPID)0x00000002
property Z    (DISPID)0x00000001

固定 DISPID を指定すると、以前の既存のディスパッチ インターフェイスとの下位互換性を維持したり、特定のシステム定義のメソッドまたはプロパティ (通常は、DISPID_NEWENUM コレクションなどの負の DISPID で示される) を実装したりするのに役立ちます。

COleClientItem の IDispatch インターフェイスの取得

多くのサーバーは、OLE サーバー機能と共に、ドキュメント オブジェクト内の自動化をサポートします。 このオートメーション インターフェイスにアクセスするには、 COleClientItem::m_lpObject メンバー変数に直接アクセスする必要があります。 次のコードでは、COleClientItemから派生したオブジェクトのIDispatch インターフェイスを取得します。 この機能が必要な場合は、次のコードをアプリケーションに含めることができます。

LPDISPATCH CMyClientItem::GetIDispatch()
{
    ASSERT_VALID(this);
    ASSERT(m_lpObject != NULL);

    LPUNKNOWN lpUnk = m_lpObject;

    Run();      // must be running

    LPOLELINK lpOleLink = NULL;
    if (m_lpObject->QueryInterface(IID_IOleLink,
        (LPVOID FAR*)&lpOleLink) == NOERROR)
    {
        ASSERT(lpOleLink != NULL);
        lpUnk = NULL;
        if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
        {
            TRACE0("Warning: Link is not connected!\n");
            lpOleLink->Release();
            return NULL;
        }
        ASSERT(lpUnk != NULL);
    }

    LPDISPATCH lpDispatch = NULL;
    if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch) != NOERROR)
    {
        TRACE0("Warning: does not support IDispatch!\n");
        return NULL;
    }

    ASSERT(lpDispatch != NULL);
    return lpDispatch;
}

この関数から返されたディスパッチ インターフェイスは、直接使用することも、型セーフ アクセスのために COleDispatchDriver にアタッチすることもできます。 これを直接使用する場合は、ポインターを使用するときにその Release メンバーを呼び出してください ( COleDispatchDriver デストラクターは既定でこれを行います)。

こちらも参照ください

番号別テクニカル ノート
カテゴリ別テクニカル ノート