次の方法で共有


テクニカル ノート 38: MFC/OLE IUnknown の実装

手記

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

OLE 2 の中心には、"OLE コンポーネント オブジェクト モデル" (COM) があります。 COM は、連携オブジェクトが相互に通信する方法の標準を定義します。 これには、オブジェクトに対するメソッドのディスパッチ方法など、"オブジェクト" の外観の詳細が含まれます。 COM では、すべての COM 互換クラスの派生元となる基本クラスも定義されます。 この基底クラスは IUnknown です。 IUnknown インターフェイスは C++ クラスと呼ばれますが、COM は 1 つの言語に固有のものではありません。C、PASCAL、または COM オブジェクトのバイナリ レイアウトをサポートできるその他の言語で実装できます。

OLE は、IUnknown から派生したすべてのクラスを "インターフェイス" として参照します。IUnknown などの "インターフェイス" には実装がないため、これは重要な違いです。 単に、オブジェクトが通信するプロトコルを定義します。これらの実装が行う内容の詳細ではありません。 これは、最大限の柔軟性を可能にするシステムにとって妥当です。 MFC/C++ プログラムの既定の動作を実装するのは MFC の仕事です。

IUnknown MFC の実装を理解するには、まずこのインターフェイスが何であるかを理解する必要があります。 IUnknown の簡略化されたバージョンを以下に定義します。

class IUnknown
{
public:
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
    virtual ULONG AddRef() = 0;
    virtual ULONG Release() = 0;
};

手記

この図では、例えば __stdcall など、ある程度の必要な呼び出し規則の詳細が省略されています。

AddRef および Release メンバー関数は、オブジェクトのメモリ管理を制御します。 COM では、参照カウントスキームを使用してオブジェクトを追跡します。 オブジェクトは、C++ と同じように直接参照されることはありません。 代わりに、COM オブジェクトは常にポインターを介して参照されます。 所有者が使用を完了したときにオブジェクトを解放するには、オブジェクトの Release メンバーが呼び出されます (従来の C++ オブジェクトの場合と同様に、演算子 delete を使用するのとは対照的です)。 参照カウント メカニズムを使用すると、1 つのオブジェクトへの複数の参照を管理できます。 AddRefRelease の実装では、オブジェクトの参照カウントが保持されます。オブジェクトは、参照カウントが 0 に達するまで削除されません。

AddRefRelease は、実装の観点から非常に簡単です。 簡単な実装を次に示します。

ULONG CMyObj::AddRef()
{
    return ++m_dwRef;
}

ULONG CMyObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

QueryInterface メンバー関数はもう少し興味深いものです。 唯一のメンバー関数が AddRef および Release しかないオブジェクトを持つことはあまり面白くありません。オブジェクトに IUnknown が提供する以上の機能を持たせると良いでしょう。 これは、QueryInterface が役立つ場所です。 これにより、同じオブジェクト上で別の "インターフェイス" を取得できます。 これらのインターフェイスは通常、IUnknown から派生し、新しいメンバー関数を追加することで追加の機能を追加します。 COM インターフェイスには、インターフェイスで宣言されたメンバー変数がなく、すべてのメンバー関数が純粋仮想として宣言されます。 例えば

class IPrintInterface : public IUnknown
{
public:
    virtual void PrintObject() = 0;
};

IUnknown しかない場合に IPrintInterface を取得するには、IPrintInterfaceIID を使用して QueryInterface を呼び出します。 IID は、インターフェイスを一意に識別する 128 ビットの数値です。 ユーザーまたは OLE が定義するインターフェイスごとに IID があります。 pUnkIUnknown オブジェクトへのポインターである場合、そこから IPrintInterface を取得するコードは次のようになります。

IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface, (void**)&pPrint) == NOERROR)
{
    pPrint->PrintObject();
    pPrint->Release();
    // release pointer obtained via QueryInterface
}

これは非常に簡単に思えますが、IPrintInterface と IUnknown の両方をサポートするオブジェクトをどのように実装しますか。この場合、IPrintInterface は IUnknown から直接派生しているため、IPrintInterface を実装することで IUnknown が自動的にサポートされ、簡単です。 例えば:

class CPrintObj : public CPrintInterface
{
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
    virtual ULONG AddRef();
    virtual ULONG Release();
    virtual void PrintObject();
};

AddRefRelease の実装は、上記で実装したものとまったく同じです。 CPrintObj::QueryInterface は次のようになります。

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

ご覧のように、インターフェイス識別子 (IID) が認識されると、ポインターがオブジェクトに返されます。それ以外の場合はエラーが発生します。 また、QueryInterface の処理が成功した場合は、AddRef が暗黙的に呼び出されます。 もちろん、CEditObj::P rint も実装する必要があります。 これは、IPrintInterface が IUnknown インターフェイスから直接派生しているため、簡単です。 ただし、IUnknownから派生した 2 つの異なるインターフェイスをサポートする場合は、次の点を考慮してください。

class IEditInterface : public IUnkown
{
public:
    virtual void EditObject() = 0;
};

C++ の多重継承の使用など、IEditInterface と IPrintInterface の両方をサポートするクラスを実装するさまざまな方法がありますが、この注では、入れ子になったクラスを使用してこの機能を実装することに重点を置きます。

class CEditPrintObj
{
public:
    CEditPrintObj();

    HRESULT QueryInterface(REFIID iid, void**);
    ULONG AddRef();
    ULONG Release();
    DWORD m_dwRef;

    class CPrintObj : public IPrintInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_printObj;

    class CEditObj : public IEditInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_editObj;
};

実装全体を次に示します。

CEditPrintObj::CEditPrintObj()
{
    m_editObj.m_pParent = this;
    m_printObj.m_pParent = this;
}

ULONG CEditPrintObj::AddRef()
{
    return ++m_dwRef;
}

CEditPrintObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = &m_printObj;
        AddRef();
        return NOERROR;
    }
    else if (iid == IID_IEditInterface)
    {
        *ppvObj = &m_editObj;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

ULONG CEditPrintObj::CEditObj::AddRef()
{
    return m_pParent->AddRef();
}

ULONG CEditPrintObj::CEditObj::Release()
{
    return m_pParent->Release();
}

HRESULT CEditPrintObj::CEditObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

ULONG CEditPrintObj::CPrintObj::AddRef()
{
    return m_pParent->AddRef();
}

ULONG CEditPrintObj::CPrintObj::Release()
{
    return m_pParent->Release();
}

HRESULT CEditPrintObj::CPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

IUnknown 実装のほとんどは、CEditPrintObj::CEditObj と CEditPrintObj::CPrintObj のコードを複製するのではなく、CEditPrintObj クラスに配置されていることに注意してください。 これにより、コードの量が減り、バグが回避されます。 ここで重要なのは、IUnknown インターフェイスから、QueryInterface を呼び出して、オブジェクトがサポートする可能性のあるインターフェイスを取得できることです。また、それらの各インターフェイスから同じ操作を行うことができます。 つまり、各インターフェイスから使用できるすべての QueryInterface 関数は、まったく同じように動作する必要があります。 これらの埋め込みオブジェクトが "外側のオブジェクト" の実装を呼び出すために、バック ポインターが使用されます (m_pParent)。 m_pParent ポインターは、CEditPrintObj コンストラクターの間に初期化されます。 次に、CEditPrintObj::CPrintObj::PrintObject と CEditPrintObj::CEditObj::EditObject も実装します。 1 つの機能 (オブジェクトを編集する機能) を追加するために、かなりのコードが追加されました。 幸い、インターフェイスに 1 つのメンバー関数しか持たないことは非常に珍しく (発生しますが)、この場合、EditObject と PrintObject は通常、1 つのインターフェイスに結合されます。

これは、このような単純なシナリオでは、多くの説明と多くのコードです。 MFC/OLE クラスは、より簡単な代替手段を提供します。 MFC の実装では、Windows メッセージをメッセージ マップでラップする方法と同様の手法を使用します。 この機能はインターフェイス マップ 呼び出され、次のセクションで説明します。

MFC インターフェイス マップ

MFC/OLE には、MFC の "メッセージ マップ" と "ディスパッチ マップ" に似た "インターフェイス マップ" の実装が概念と実行に含まれています。 MFC のインターフェイス マップの主な機能は次のとおりです。

  • CCmdTarget クラスに組み込まれている IUnknownの標準実装。

  • AddRef および Release によって変更された参照カウントのメンテナンス

  • QueryInterface のデータ ドリブン実装

さらに、インターフェイス マップでは、次の高度な機能がサポートされています。

  • 集計可能 COM オブジェクトの作成のサポート

  • COM オブジェクトの実装での集計オブジェクトの使用のサポート

  • 実装はフック可能で拡張可能です

集計の詳細については、集計の に関するトピックを参照してください。

MFC のインターフェイス マップのサポートは、CCmdTarget クラスに根ざしています。 CCmdTarget」には「」という参照カウントと、IUnknown の実装に関連するすべてのメンバー関数があります(例えば、参照カウントは CCmdTargetにあります)。 OLE COM をサポートするクラスを作成するには、CCmdTarget からクラスを派生させ、さまざまなマクロと CCmdTarget のメンバー関数を使用して目的のインターフェイスを実装します。 MFC の実装では、入れ子になったクラスを使用して、上記の例と同様に各インターフェイスの実装を定義します。 これは、IUnknown の標準的な実装と、繰り返しコードの一部を排除するマクロの数によって容易になります。

インターフェイス マップの基本

MFC のインターフェイス マップを使用してクラスを実装するには

  1. CCmdTargetから直接または間接的にクラスを派生させます。

  2. 派生クラス定義で DECLARE_INTERFACE_MAP 関数を使用します。

  3. サポートするインターフェイスごとに、クラス定義でBEGIN_INTERFACE_PARTマクロとEND_INTERFACE_PARTマクロを使用します。

  4. 実装ファイルで、BEGIN_INTERFACE_MAPマクロとEND_INTERFACE_MAPマクロを使用して、クラスのインターフェイス マップを定義します。

  5. サポートされている各 IID について、BEGIN_INTERFACE_MAP マクロと END_INTERFACE_MAP マクロの間の INTERFACE_PART マクロを使用して、その IID をクラスの特定の "部分" にマップします。

  6. サポートするインターフェイスを表す入れ子になった各クラスを実装します。

  7. METHOD_PROLOGUE マクロを使用して、CCmdTargetから派生したオブジェクトの親にアクセスします。

  8. AddRefRelease、および QueryInterface は、これらの関数 (ExternalAddRefExternalRelease、および ExternalQueryInterface) の CCmdTarget 実装に委任できます。

上記の CPrintEditObj の例は、次のように実装できます。

class CPrintEditObj : public CCmdTarget
{
public:
    // member data and member functions for CPrintEditObj go here

// Interface Maps
protected:
    DECLARE_INTERFACE_MAP()

    BEGIN_INTERFACE_PART(EditObj, IEditInterface)
        STDMETHOD_(void, EditObject)();
    END_INTERFACE_PART(EditObj)

    BEGIN_INTERFACE_PART(PrintObj, IPrintInterface)
        STDMETHOD_(void, PrintObject)();
    END_INTERFACE_PART(PrintObj)
};

上記の宣言では、CCmdTargetから派生したクラスが作成されます。 DECLARE_INTERFACE_MAP マクロは、このクラスにカスタム インターフェイス マップがあることをフレームワークに指示します。 さらに、BEGIN_INTERFACE_PARTマクロとEND_INTERFACE_PARTマクロは、入れ子になったクラスを定義します。この場合、CEditObj と CPrintObj という名前を使用します (X は、入れ子になったクラスと、"C" で始まるグローバル クラスと "I" で始まるインターフェイス クラスを区別するためにのみ使用されます)。 これらのクラスの 2 つの入れ子になったメンバー (それぞれ m_CEditObj と m_CPrintObj) が作成されます。 マクロは、AddRefRelease、および QueryInterface 関数 自動的に宣言します。したがって、このインターフェイスに固有の関数である EditObject と PrintObject のみを宣言します (OLE マクロ STDMETHOD を使用して、ターゲット プラットフォームに適した _stdcall と仮想キーワードが提供されるようにします)。

このクラスのインターフェイス マップを実装するには:

BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
    INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

これにより、IID_IPrintInterface IID がm_CPrintObjに接続され、IID_IEditInterfaceがそれぞれm_CEditObjに接続されます。 QueryInterface (CCmdTarget::ExternalQueryInterface) の CCmdTarget 実装では、このマップを使用して、要求されたときにm_CPrintObjとm_CEditObjへのポインターを返します。 IID_IUnknownのエントリを含める必要はありません。フレームワークは、IID_IUnknown が要求されたときにマップ内の最初のインターフェイス (この場合はm_CPrintObj) を使用します。

BEGIN_INTERFACE_PART マクロは、AddRefReleaseQueryInterface 関数を自動的に宣言しましたが、それらを実装する必要があります。

ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalAddRef();
}

ULONG FAR EXPORT CEditPrintObj::XEditObj::Release()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalRelease();
}

HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface(
    REFIID iid,
    void FAR* FAR* ppvObj)
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

void FAR EXPORT CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    // code to "Edit" the object, whatever that means...
}

CEditPrintObj::CPrintObj の実装は、CEditPrintObj::CEditObj の上記の定義に似ています。 これらの関数を自動的に生成するために使用できるマクロを作成することは可能ですが (ただし、MFC/OLE 開発では以前はそうでした)、マクロが複数のコード行を生成するときにブレークポイントを設定することが困難になります。 このため、このコードは手動で展開されます。

メッセージ マップのフレームワーク実装を使用すると、多くの操作を行う必要はありませんでした。

  • QueryInterface を実装する

  • AddRef とリリースを実装する

  • 両方のインターフェイスでこれらの組み込みメソッドのいずれかを宣言する

さらに、フレームワークは内部的にメッセージ マップを使用します。 これにより、既に特定のインターフェイスをサポートし、フレームワークによって提供されるインターフェイスに置換または追加を提供するフレームワーク クラス (COleServerDocなど) から派生できます。 これは、フレームワークが基底クラスからのインターフェイス マップの継承を完全にサポートしているためです。 これが、BEGIN_INTERFACE_MAPが 2 番目のパラメーターとして基底クラスの名前を受け取る理由です。

手記

一般に、MFC バージョンからそのインターフェイスの埋め込み特殊化を継承するだけで、MFC の組み込み OLE インターフェイスの実装を再利用することはできません。 これは、METHOD_PROLOGUE マクロを使用して含まれる CCmdTarget派生オブジェクトにアクセスすることが、CCmdTarget派生オブジェクトから埋め込みオブジェクトまでの固定オフセット を意味するため不可能です。 つまり、たとえば、XAdviseSink は COleClientItem オブジェクトの上部からの特定のオフセットに依存するため、COleClientItem::XAdviseSinkでの MFC の実装から埋め込まれた XMyAdviseSink を派生させることはできません。

手記

ただし、MFC の既定の動作が必要なすべての関数について、MFC 実装に委任できます。 これは IOleInPlaceFrame クラスで MFC 実装の COleFrameHook (XOleInPlaceFrame) によって行われます。このクラスは多くの関数に対して m_xOleInPlaceUIWindow に処理を任せます。 この設計は、多くのインターフェイスを実装するオブジェクトのランタイム サイズを小さくするために選択されました。これにより、(前のセクションで使用したm_pParent方法など) バック ポインターが不要になります。

集約とインターフェイス マップ

MFC では、スタンドアロン COM オブジェクトのサポートに加えて、集計もサポートされています。 集計自体は複雑すぎるため、ここでは説明できません。集計の詳細については、集計 トピックを参照してください。 このメモでは、フレームワークとインターフェイス マップに組み込まれている集計のサポートについて簡単に説明します。

集計を使用する方法は 2 つあります。(1) 集計をサポートする COM オブジェクトを使用する方法と、(2) 別のオブジェクトで集計できるオブジェクトを実装する方法です。 これらの機能は、"集計オブジェクトの使用" および "オブジェクトの集計可能化" と呼ばれます。 MFC は両方をサポートします。

集計オブジェクトの使用

集計オブジェクトを使用するには、集計を QueryInterface メカニズムに関連付ける何らかの方法が必要です。 つまり、集計オブジェクトは、オブジェクトのネイティブ部分であるかのように動作する必要があります。 そのため、これは MFC のインターフェイス マップ メカニズムにどのように結び付けられますか。入れ子になったオブジェクトが IID にマップされる INTERFACE_PART マクロに加えて、集計オブジェクトを CCmdTarget 派生クラスの一部として宣言することもできます。 これを行うには、INTERFACE_AGGREGATE マクロが使用されます。 これにより、インターフェイス マップ メカニズムに統合されるメンバー変数 (IUnknown または派生クラスへのポインターである必要があります) を指定できます。 CCmdTarget::ExternalQueryInterface が呼び出されたときにポインターが NULL でない場合、要求された IIDCCmdTarget オブジェクト自体でサポートされているネイティブ IIDの 1 つでない場合、フレームワークは集計オブジェクトの QueryInterface メンバー関数を自動的に呼び出します。

INTERFACE_AGGREGATE マクロを使用するには

  1. 集計オブジェクトへのポインターを格納するメンバー変数 (IUnknown*) を宣言します。

  2. インターフェイス マップにINTERFACE_AGGREGATE マクロを含めます。これは、名前によってメンバー変数を参照します。

  3. ある時点 (通常、CCmdTarget::OnCreateAggregates中) に、メンバー変数を NULL 以外の値に初期化します。

例えば:

class CAggrExample : public CCmdTarget
{
public:
    CAggrExample();

protected:
    LPUNKNOWN m_lpAggrInner;
    virtual BOOL OnCreateAggregates();

    DECLARE_INTERFACE_MAP()
    // "native" interface part macros may be used here
};

CAggrExample::CAggrExample()
{
    m_lpAggrInner = NULL;
}

BOOL CAggrExample::OnCreateAggregates()
{
    // wire up aggregate with correct controlling unknown
    m_lpAggrInner = CoCreateInstance(CLSID_Example,
        GetControllingUnknown(), CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID*)&m_lpAggrInner);

    if (m_lpAggrInner == NULL)
        return FALSE;
    // optionally, create other aggregate objects here
    return TRUE;
}

BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget)
    // native "INTERFACE_PART" entries go here
    INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

m_lpAggrInner変数は、コンストラクター内で NULL に初期化されます。 このフレームワークは、QueryInterface の既定の実装で NULL メンバー変数無視します。 OnCreateAggregates は、集計オブジェクトを実際に作成するのに適した場所です。 COleObjectFactoryの MFC 実装の外部でオブジェクトを作成する場合は、明示的に呼び出す必要があります。 CCmdTarget::OnCreateAggregates で集計を作成する理由と、集計可能なオブジェクトを作成するときに、CCmdTarget::GetControllingUnknown の使用が明らかになります。

この手法により、集計オブジェクトがサポートするすべてのインターフェイスと、そのネイティブ インターフェイスがオブジェクトに提供されます。 集約がサポートするインターフェイスのサブセットのみが必要な場合は、CCmdTarget::GetInterfaceHookオーバーライドできます。 これにより、QueryInterfaceと同様に、非常に低レベルのフック可能性を実現できます。 通常、集計でサポートされるすべてのインターフェイスが必要です。

オブジェクト実装を集約可能にする

集計可能なオブジェクトを作成するには、AddRefRelease、および QueryInterface の実装を "制御中の不明" に委任する必要があります。つまり、オブジェクトの一部にするには、AddRefRelease、および QueryInterface を別のオブジェクト IUnknownから派生させる必要があります。 この「制御不明」は、作成時にオブジェクトに提供されます。つまり、COleObjectFactoryの実装に提供されます。 これを実装するとオーバーヘッドが少なく、場合によっては望ましくない場合があるため、MFC では省略可能です。 オブジェクトを集計できるようにするには、オブジェクトのコンストラクターから CCmdTarget::EnableAggregation を呼び出します。

オブジェクトで集約を使用するときは、適切な "controlling unknown" が集約オブジェクトに渡されるようにする必要があります。 通常、この IUnknown ポインターは、集計の作成時にオブジェクトに渡されます。 たとえば、pUnkOuter パラメーターは、CoCreateInstanceで作成されたオブジェクトの "制御不明" です。 正しい "制御不明" ポインターは、CCmdTarget::GetControllingUnknownを呼び出すことによって取得できます。 ただし、その関数から返される値は、コンストラクターの間は無効です。 このため、COleObjectFactory 実装から作成された場合でも、GetControllingUnknown からの戻り値が信頼できる CCmdTarget::OnCreateAggregatesのオーバーライドでのみ集計を作成することをお勧めします。

また、人工参照カウントを追加または解放するときに、オブジェクトが正しい参照カウントを操作することも重要です。 これが確実に行われるようにするには、InternalReleaseInternalAddRefの代わりに常に ExternalAddRefExternalRelease を呼び出します。 集計をサポートするクラスで InternalRelease または InternalAddRef を呼び出すのはまれです。

参考資料

独自のインターフェイスの定義や、フレームワークの OLE インターフェイスの実装のオーバーライドなど、OLE の高度な使用方法には、基になるインターフェイス マップ メカニズムを使用する必要があります。

このセクションでは、これらの高度な機能を実装するために使用される各マクロと API について説明します。

CCmdTarget::EnableAggregation — 関数の説明

void EnableAggregation();

注釈

この型のオブジェクトの OLE 集計をサポートする場合は、派生クラスのコンストラクターでこの関数を呼び出します。 これにより、集計可能なオブジェクトに必要な特別な IUnknown 実装が準備されます。

CCmdTarget::ExternalQueryInterface — 関数の説明

DWORD ExternalQueryInterface(
    const void FAR* lpIID,
    LPVOIDFAR* ppvObj
);

パラメーター

lpIID
IID への遠いポインター (QueryInterface の最初の引数)

ppvObj
IUnknown* へのポインター (QueryInterface の 2 番目の引数)

注釈

クラスが実装するインターフェイスごとに、IUnknown の実装でこの関数を呼び出します。 この関数は、オブジェクトのインターフェイス マップに基づく QueryInterface の標準的なデータ ドリブン実装を提供します。 戻り値を HRESULT にキャストする必要があります。 オブジェクトが集計されている場合、この関数はローカル インターフェイス マップを使用するのではなく、"制御する IUnknown" を呼び出します。

CCmdTarget::ExternalAddRef — 関数の説明

DWORD ExternalAddRef();

注釈

クラスが実装するインターフェイスごとに、IUnknown::AddRef の実装でこの関数を呼び出します。 戻り値は、CCmdTarget オブジェクトの新しい参照カウントです。 オブジェクトが集計されている場合、この関数はローカル参照カウントを操作するのではなく、"制御する IUnknown" を呼び出します。

CCmdTarget::ExternalRelease — 関数の説明

DWORD ExternalRelease();

注釈

クラスが実装するインターフェイスごとに、IUnknown::Release の実装でこの関数を呼び出します。 戻り値は、オブジェクトの新しい参照カウントを示します。 オブジェクトが集計されている場合、この関数はローカル参照カウントを操作するのではなく、"制御する IUnknown" を呼び出します。

DECLARE_INTERFACE_MAP - マクロの説明

DECLARE_INTERFACE_MAP

注釈

インターフェイス マップを持つ CCmdTarget から派生した任意のクラスで、このマクロを使用します。 DECLARE_MESSAGE_MAPとほぼ同じ方法で使用されます。 このマクロ呼び出しは、通常、ヘッダー (.H) ファイル内のクラス定義に配置されるべきです。 DECLARE_INTERFACE_MAPを持つクラスは、実装ファイル(.CPP)内で、BEGIN_INTERFACE_MAPマクロとEND_INTERFACE_MAPマクロを使用して、インターフェイスマップを定義する必要があります。

「BEGIN_INTERFACE_PART」と「END_INTERFACE_PART」— マクロの説明

BEGIN_INTERFACE_PART(localClass, iface);
END_INTERFACE_PART(localClass)

パラメーター

localClass
インターフェイスを実装するクラスの名前

iface
このクラスが実装するインターフェイスの名前

注釈

クラスが実装するインターフェイスごとに、BEGIN_INTERFACE_PARTとEND_INTERFACE_PARTのペアが必要です。 これらのマクロは、定義する OLE インターフェイスから派生したローカル クラスと、そのクラスの埋め込みメンバー変数を定義します。 AddRefRelease、および QueryInterface メンバーは自動的に宣言されます。 実装するインターフェイスの一部である他のメンバー関数の宣言を含める必要があります (これらの宣言は、BEGIN_INTERFACE_PARTマクロと END_INTERFACE_PART マクロの間に配置されます)。

iface 引数は、IAdviseSinkIPersistStorage (または独自のカスタム インターフェイス) など、実装する OLE インターフェイスです。

localClass 引数は、定義されるローカル クラスの名前です。 名前の先頭に 'X' が自動的に付加されます。 この名前付け規則は、同じ名前のグローバル クラスとの競合を回避するために使用されます。 さらに、埋め込みメンバーの名前は、localClass 名と同じですが、プレフィックスとして 'm_x' が付いています。

例えば:

BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink)
    STDMETHOD_(void, OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
    STDMETHOD_(void, OnViewChange)(DWORD, LONG);
    STDMETHOD_(void, OnRename)(LPMONIKER);
    STDMETHOD_(void, OnSave)();
    STDMETHOD_(void, OnClose)();
END_INTERFACE_PART(MyAdviseSink)

は、IAdviseSink から派生した XMyAdviseSink というローカル クラスと、それが宣言されているクラスのメンバーを m_xMyAdviseSink.Note と呼びます。

手記

STDMETHOD_ で始まる行は、基本的に OLE2.H からコピーされ、わずかに変更されています。 OLE2 からコピーします。H は、解決が困難なエラーを減らすことができます。

BEGIN_INTERFACE_MAPとEND_INTERFACE_MAP — マクロの説明

BEGIN_INTERFACE_MAP(theClass, baseClass)
END_INTERFACE_MAP

パラメーター

theClass
インターフェイス マップを定義するクラス

baseClass
クラスから派生する クラス。

注釈

実装ファイルでは、BEGIN_INTERFACE_MAPマクロとEND_INTERFACE_MAP マクロを使用して、インターフェイス マップを実際に定義します。 実装されるインターフェイスごとに、1 つ以上のINTERFACE_PARTマクロ呼び出しがあります。 クラスが使用する集計ごとに、INTERFACE_AGGREGATEマクロの呼び出しが1回あります。

INTERFACE_PART — マクロの説明

INTERFACE_PART(theClass, iid, localClass)

パラメーター

theClass
インターフェイス マップを含むクラスの名前。

iid
埋め込みクラスに割り当てられる IID

localClass
ローカルクラスの名前('X' を除く)。

注釈

このマクロは、オブジェクトがサポートするインターフェイスごとに、BEGIN_INTERFACE_MAP マクロと END_INTERFACE_MAP マクロの間で使用されます。 この機能により、IIDをtheClassおよびlocalClassによって示されるクラスのメンバーにマップできます。 'm_x' は、localClass に自動的に追加されます。 複数の IID が 1 つのメンバーに関連付けられている場合があることに注意してください。 これは、"最も派生した" インターフェイスのみを実装していて、すべての中間インターフェイスも提供する場合に非常に便利です。 この良い例は、IOleInPlaceFrameWindow インターフェイスです。 その階層は次のようになります。

IUnknown
    IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

オブジェクトが IOleInPlaceFrameWindowを実装する場合、クライアントは、"最も派生した" インターフェイス IOleInPlaceFrameWindow (実際に実装しているインターフェイス) に加えて、IOleUIWindowIOleWindow、または IUnknown のいずれかのインターフェイスで QueryInterface できます。 これを処理するには、複数のINTERFACE_PART マクロを使用して、各基本インターフェイスを IOleInPlaceFrameWindow インターフェイスにマップします。

クラス定義ファイルで次の操作を行います。

BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)

クラス実装ファイル内:

BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd)
    INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

フレームワークは常に必要であるため、IUnknown の処理を行います。

INTERFACE_PART — マクロの説明

INTERFACE_AGGREGATE(theClass, theAggr)

パラメーター

theClass
インターフェイス マップを含むクラスの名前。

theAggr
集計するメンバー変数の名前。

注釈

このマクロは、クラスが集計オブジェクトを使用していることをフレームワークに通知するために使用されます。 BEGIN_INTERFACE_PARTマクロとEND_INTERFACE_PART マクロの間に表示する必要があります。 集計オブジェクトは、IUnknownから派生した独立したオブジェクトです。 集計と INTERFACE_AGGREGATE マクロを使用すると、集計でサポートされているすべてのインターフェイスがオブジェクトで直接サポートされているように見せることができます。 theAggr 引数は、IUnknown から(直接的または間接的に)派生したあなたのクラスのメンバー変数の名前です。 すべてのINTERFACE_AGGREGATEマクロは、インターフェイス マップに配置されるときに、INTERFACE_PART マクロに従う必要があります。

関連項目

数値別のテクニカルノート
カテゴリ別 テクニカルノート