テクニカル ノート 65: OLE オートメーション サーバー用デュアル インターフェイス サポート
[!メモ]
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。
ここでは、 MFC ベースの OLE オートメーション サーバー アプリケーションにデュアルインターフェイス サポートを追加する方法について説明します。ACDUAL のサンプルはデュアルインターフェイス サポートについて説明し、このメモのコード例は、 ACDUAL から取得されます。このメモで、 DECLARE_DUAL_ERRORINFOなど、 DUAL_ERRORINFO_PART説明されているマクロは、 IMPLEMENT_DUAL_ERRORINFO、 ACDUAL サンプルの一部で、 MFCDUAL.H. で確認できます。
デュアル インターフェイス
OLE オートメーションの実行がどちらも含む) IDispatch VTBL インターフェイス、インターフェイス、またはデュアル インターフェイス (許可していますが、 Microsoft は、厳密に公開されているすべての OLE オートメーション オブジェクトのデュアル インターフェイスを実装することをお勧めします。デュアル インターフェイスに IDispatchでのみまたは VTBL 専用インターフェイス上重要な長所があります:
バインディングは IDispatchによって VTBL インターフェイスを通じてコンパイル時または実行時に発生する可能性があります。
VTBL インターフェイスを使用して OLE オートメーション コントローラーは、パフォーマンスの向上の余地があります。
IDispatch インターフェイスを使用する既存の OLE オートメーション コントローラーは引き続き機能します。
VTBL インターフェイスは C++ から、ダイヤルが簡単です。
Visual Basic のオブジェクトがサポート機能を持つデュアル インターフェイスは互換性に必要です。
CCmdTarget ベースのクラスにデュアルインターフェイス サポートを追加できます。
デュアル インターフェイスは IDispatchから派生した実際に、カスタム インターフェイスです。CCmdTargetのベースのクラスのデュアルインターフェイス サポートを実装する最も簡単な方法は、 MFC と ClassWizard を使用して最初に実装しているクラスの正常なディスパッチ インターフェイスを、後で追加できます。カスタム インターフェイスがあります。ほとんどの場合、カスタム インターフェイスの実装は、 MFC の IDispatch に再度実装に単純に委任します。
最初に、オブジェクトのデュアル インターフェイスを定義するサーバーの ODL ファイルを変更します。デュアル インターフェイスを定義するには、 Visual C++ ウィザードが生成する DISPINTERFACE ステートメントの代わりにインターフェイス ステートメントを使用します。DISPINTERFACE 既存のステートメントを削除してではなく、新しいインターフェイス ステートメントを追加します。DISPINTERFACE フォームを保持することによって、オブジェクトのプロパティおよびメソッドを追加するために ClassWizard を使用し続けることができますインターフェイス ステートメントに同等のプロパティとメソッドを追加します。
デュアル インターフェイスのインターフェイス ステートメントは OLEAUTOMATION と DUAL の属性を持つインターフェイスは IDispatchから派生する必要があります。デュアル インターフェイスの IID を作成するに GUIDGEN のサンプルを使用する場合:
[ uuid(0BDD0E81-0DD7-11cf-BBA8-444553540000), // IID_IDualAClick
oleautomation,
dual
]
interface IDualAClick : IDispatch
{
};
適切なインターフェイスのステートメントがある場合は、メソッドおよびプロパティのエントリの追加を開始します。デュアル インターフェイスでは、デュアル インターフェイスのメソッドおよびプロパティ アクセサー関数が HRESULT を返し、属性 [retval,out]のパラメーターとして戻り値を渡すようにパラメーター リストを並べ替える必要があります。プロパティに、読み取り (propget)と同じ ID を持つ書き込みpropput()のアクセス関数を追加する必要があることに注意してください。次に例を示します。
[propput, id(1)] HRESULT text([in] BSTR newText);
[propget, id(1)] HRESULT text([out, retval] BSTR* retval);
メソッドおよびプロパティの後に、インターフェイス ステートメントへの参照を追加する必要があります。コクラス ステートメントで定義されます。次に例を示します。
[ uuid(4B115281-32F0-11cf-AC85-444553540000) ]
coclass Document
{
dispinterface IAClick;
[default] interface IDualAClick;
};
ODL ファイルが更新された場合、クラス オブジェクトのデュアル インターフェイスの実装クラスを定義し、 MFC の QueryInterface の機能の対応するエントリを設定するために MFC のインターフェイス マップの機能を使用します。ディスパッチ インターフェイスのエントリが ODL のインターフェイス ステートメントの各エントリの INTERFACE_PART ブロックに 1 個のエントリが必要です。propput の属性の ODL 各エントリは put_propertynameという名前の関数を必要とします。propget の属性の各エントリは get_propertynameという名前の関数を必要とします。
デュアル インターフェイスの実装クラスを定義するには、オブジェクト クラス定義に DUAL_INTERFACE_PART ブロックを追加します。次に例を示します。
BEGIN_DUAL_INTERFACE_PART(DualAClick, IDualAClick)
STDMETHOD(put_text)(THIS_ BSTR newText);
STDMETHOD(get_text)(THIS_ BSTR FAR* retval);
STDMETHOD(put_x)(THIS_ short newX);
STDMETHOD(get_x)(THIS_ short FAR* retval);
STDMETHOD(put_y)(THIS_ short newY);
STDMETHOD(get_y)(THIS_ short FAR* retval);
STDMETHOD(put_Position)(THIS_ IDualAutoClickPoint FAR* newPosition);
STDMETHOD(get_Position)(THIS_ IDualAutoClickPoint FAR* FAR* retval);
STDMETHOD(RefreshWindow)(THIS);
STDMETHOD(SetAllProps)(THIS_ short x, short y, BSTR text);
STDMETHOD(ShowWindow)(THIS);
END_DUAL_INTERFACE_PART(DualAClick)
MFC の QueryInterface の機能にデュアル インターフェイスを接続するには、インターフェイス マップに INTERFACE_PART エントリを追加します:
BEGIN_INTERFACE_MAP(CAutoClickDoc, CDocument)
INTERFACE_PART(CAutoClickDoc, DIID_IAClick, Dispatch)
INTERFACE_PART(CAutoClickDoc, IID_IDualAClick, DualAClick)
END_INTERFACE_MAP()
次に、インターフェイスの実装を設定する必要があります。主として、既存の MFC IDispatch に実装に委任できます。次に例を示します。
STDMETHODIMP_(ULONG) CAutoClickDoc::XDualAClick::AddRef()
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CAutoClickDoc::XDualAClick::Release()
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
return pThis->ExternalRelease();
}
STDMETHODIMP CAutoClickDoc::XDualAClick::QueryInterface(
REFIID iid, LPVOID* ppvObj)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
return pThis->ExternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetTypeInfoCount(
UINT FAR* pctinfo)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
ASSERT(lpDispatch != NULL);
return lpDispatch->GetTypeInfoCount(pctinfo);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetTypeInfo(
UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
ASSERT(lpDispatch != NULL);
return lpDispatch->GetTypeInfo(itinfo, lcid, pptinfo);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetIDsOfNames(
REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames,
LCID lcid, DISPID FAR* rgdispid)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
ASSERT(lpDispatch != NULL);
return lpDispatch->GetIDsOfNames(riid, rgszNames, cNames,
lcid, rgdispid);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::Invoke(
DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult,
EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
ASSERT(lpDispatch != NULL);
return lpDispatch->Invoke(dispidMember, riid, lcid,
wFlags, pdispparams, pvarResult,
pexcepinfo, puArgErr);
}
オブジェクトのメソッドおよびプロパティ アクセサー関数の場合、実装を設定する必要があります。メソッドおよびプロパティ関数は ClassWizard を使用して生成されたメソッドに対して一般に委任できます。ただし、変数に直接アクセスするためのプロパティを設定して取得するコードを記述する必要があります。変数に値を取得します。次に例を示します。
STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
// MFC automatically converts from Unicode BSTR to
// Ansi CString, if necessary...
pThis->m_str = newText;
return NOERROR;
}
STDMETHODIMP CAutoClickDoc::XDualAClick::get_text(BSTR* retval)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
// MFC automatically converts from Ansi CString to
// Unicode BSTR, if necessary...
pThis->m_str.SetSysString(retval);
return NOERROR;
}
デュアルインターフェイスのポインターを渡します。
デュアルインターフェイスのポインターを渡すことは特に CCmdTarget::FromIDispatchをダイヤルする必要がある場合は、簡単ではありません。FromIDispatch は、 MFC の IDispatch のポインターでのみ動作します。これを回避する 1 とおりの方法は、 MFC によって元の IDispatch のポインター セットアップにクエリ、それを必要とする関数にそのポインターを渡すことです。次に例を示します。
STDMETHODIMP CAutoClickDoc::XDualAClick::put_Position(
IDualAutoClickPoint FAR* newPosition)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDisp = NULL;
newPosition->QueryInterface(IID_IDispatch, (LPVOID*)&lpDisp);
pThis->SetPosition(lpDisp);
lpDisp->Release();
return NOERROR;
}
デュアルインターフェイスのメソッドで、ポインターを渡す前に、 MFC の IDispatch のポインターからデュアルインターフェイスのポインターに変換する必要がある場合があります。次に例を示します。
STDMETHODIMP CAutoClickDoc::XDualAClick::get_Position(
IDualAutoClickPoint FAR* FAR* retval)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDisp;
lpDisp = pThis->GetPosition();
lpDisp->QueryInterface(IID_IDualAutoClickPoint, (LPVOID*)retval);
return NOERROR;
}
アプリケーションのタイプ ライブラリの登録
AppWizard はシステムを使用して OLE オートメーション サーバー アプリケーションのタイプ ライブラリを登録するコードを生成しません。タイプ ライブラリを登録する他の方法では、アプリケーションが実行されたスタンドアロンである場合は、 OLE 型情報を、つまり更新中のアプリケーションを登録されているタイプ ライブラリと便利です。
アプリケーションが単独で実行されたスタンドアロンである場合、アプリケーションのタイプ ライブラリを登録するには:
標準に AFXCTL.H を AfxOleRegisterTypeLib 関数の定義にアクセスするためのメソッドが含まれますヘッダー ファイル、 STDAFX.H を、含めます。
アプリケーションの InitInstance 関数では、 COleObjectFactory::UpdateRegistryAllに呼び出しを探します。この呼び出しの後に、タイプ ライブラリの名前とともにタイプ ライブラリに対応する LIBID を指定する AfxOleRegisterTypeLibに呼び出しを追加します:
// When a server application is launched stand-alone, it is a good idea // to update the system registry in case it has been damaged. m_server.UpdateRegistry(OAT_DISPATCH_OBJECT); COleObjectFactory::UpdateRegistryAll(); // DUAL_SUPPORT_START // Make sure the type library is registered or dual interface won't work. AfxOleRegisterTypeLib(AfxGetInstanceHandle(), LIBID_ACDual, _T("AutoClik.TLB")); // DUAL_SUPPORT_END
タイプ ライブラリの変更を受け入れるためのプロジェクト ビルド構成
タイプ ライブラリを再度ビルドするたびに UUID の定義を含むヘッダー ファイルが MkTypLib によって生成されるようにプロジェクトのビルド設定を変更するには:
ビルド のメニューのをクリック 構成は、各構成のファイル リストからの ODL ファイルを選択します。
OLE Types のタブをクリックし、 Output header ファイル名フィールドにファイル名を指定します。MkTypLib が既存のファイルを上書きするため、プロジェクトで既に使用されていないファイル名を使用します。Build Settings のダイアログ ボックスを閉じ OK 。
UUID の定義を MkTypLib 生成されるヘッダー ファイルからプロジェクトに追加するには:
標準に MkTypLib 生成されるヘッダー ファイル。ヘッダー ファイル、 STDAFX.H. を含めます。
新しいファイル、 INITIIDS.CPP を作成し、プロジェクトに追加します。このファイルでは、 OLE2.H と INITGUID.H を含む MkTypLib 生成されるヘッダー ファイルを後で追加します:
// initIIDs.c: defines IIDs for dual interfaces // This must not be built with precompiled header. #include <ole2.h> #include <initguid.h> #include "acdual.h"
ビルド のメニューのをクリック 構成は、各構成のファイル リストから INITIIDS.CPP を選択します。
C++ のタブをクリックし、カテゴリ プリコンパイル済みヘッダーをクリックし、 Not using precompiled headers のオプション ボタンを選択します。Build Settings のダイアログ ボックスを閉じる [OK]
タイプ ライブラリで正確なオブジェクト クラス名を指定できます。
ウィザードは、 OLE 作成可能なクラスに対して、 Visual C++ で不適切に使用するサーバーの ODL ファイルでコクラスを指定するには、実装クラス名を提供します。これが機能するが、実装クラス名はおそらく、が使用するオブジェクトのユーザーが目的のクラス名ではありません。正しい名前を指定するには、 ODL ファイルを開き、各コクラス ステートメントを探し、正しい外部名と実装クラス名を置き換えます。
コクラス ステートメントが変更されると、 MkTypLib 生成されるヘッダー ファイルの CLSIDの秒の変数名はそれに応じて変更されることに注意してください。新しい変数名を使用するコードを更新する必要があります。
例外とオートメーション エラー インターフェイスの処理
オートメーション オブジェクトのメソッドおよびプロパティ アクセサーは例外をスローすることがあります。その場合、 IErrorInfoをデュアルインターフェイスの実装でそれらを処理し、コントローラーに、 OLE オートメーションのエラー処理インターフェイスを通じて例外に関する情報を渡します。このインターフェイスは IDispatch と VTBL インターフェイスの両方を使用して、コンテキスト詳細なエラー情報を提供します。エラー ハンドラーが使用できることを示すには、 ISupportErrorInfo インターフェイスを実装する必要があります。
例外処理機構を説明するために、と標準ディスパッチ サポート例外のスローを実行するために使用される ClassWizard 生成された関数とします。MFC の IDispatch::Invoke の実装は、通常はこれらの例外をキャッチし、 Invoke の呼び出しによって返される EXCEPTINFO の構造体に変換します。ただし、 VTBL インターフェイスを使用すると、例外のキャッチを自分で管理します。デュアルインターフェイスのメソッドの保護として:
STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
TRY_DUAL(IID_IDualAClick)
{
// MFC automatically converts from Unicode BSTR to
// Ansi CString, if necessary...
pThis->m_str = newText;
return NOERROR;
}
CATCH_ALL_DUAL
}
CATCH_ALL_DUAL は例外が発生したときに正しいエラー コードを返します。処理します。CATCH_ALL_DUAL は ICreateErrorInfo インターフェイスを使用して OLE オートメーションのエラー処理情報に MFC 例外を変換します。(例の CATCH_ALL_DUAL のマクロは ACDUAL のサンプル ファイル MFCDUAL.H にあります。その例外処理がある関数、 DualHandleExceptionは、発生した例外の種類に基づいて戻りにファイル MFCDUAL.CPP)。 CATCH_ALL_DUAL に、をエラー コードです:
COleDispatchException (この場合は HRESULT は、次のコードを使用して作成されます:
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, (e->m_wCode + 0x200));
これは、例外の原因となったインターフェイスに HRESULT の設計を作成します。エラー コードは 0x200 して、標準 OLE インターフェイスの HRESULTのシステム定義の秒の競合を回避するために短縮されます。
CMemoryException (この場合は E_OUTOFMEMORY が返されます。
他の例外 (この場合は E_UNEXPECTED が返されます。
OLE オートメーション エラー ハンドラーが使用されていることを示すために、 ISupportErrorInfo インターフェイスを実装する必要があります。
最初に ISupportErrorInfoをサポートするには、ことを示すために、オートメーション クラス定義にコードを追加します。
第 2 に、 MFC の QueryInterface の機能によって ISupportErrorInfo の実装クラスを関連付けるには、オートメーション クラスのインターフェイス マップにコードを追加します。INTERFACE_PART ステートメントは ISupportErrorInfoに対して定義されているクラスに一致します。
最後に、 ISupportErrorInfoをサポートするために定義されたクラスを実装します。
( ACDUAL のサンプルから 3 種類のして、このマクロを 3 ステップ、 DECLARE_DUAL_ERRORINFO、完全に MFCDUAL.H. に含まれる DUAL_ERRORINFO_PARTと IMPLEMENT_DUAL_ERRORINFOが含まれます)
次の例は ISupportErrorInfoをサポートするために定義されたクラスを実装します。CAutoClickDoc はオートメーション クラスの名前で、 IID_IDualAClick は、 OLE オートメーション エラーのオブジェクトで報告される間違いのソースのインターフェイスの IID です:
STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::AddRef()
{
METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::Release()
{
METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
return pThis->ExternalRelease();
}
STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::QueryInterface(
REFIID iid, LPVOID* ppvObj)
{
METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
return pThis->ExternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::InterfaceSupportsErrorInfo(
REFIID iid)
{
METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
return (iid == IID_IDualAClick) ? S_OK : S_FALSE;
}