デュアルインターフェース - タイプライブラリの登録とその利用

いちごパック > COM/ActiveXの解説 > デュアルインターフェース - タイプライブラリの登録とその利用

タイプライブラリの取り込み

タイプライブラリは.tlbファイルのままでも利用できますが、 DLLやEXEにリソースとして取り込んでおくと、 COMシステムにタイプライブラリのファイルとしてDLLやEXEを渡すことができるようになります。
DLLやEXEにリソースとして取り込むには、次の1行を含むリソースファイルを作成し、 他のファイルと一緒にビルドします。
1 typelib "ichigocom4_coclass_if.tlb"
最初の1はリソースのIDで、ほかのIDと別の数字であれば何でもかまいません。 次のtypelibというキーワードは、リソースの種類がタイプライブラリであることを表します。 最後の文字列には、タイプライブラリのファイル名を指定します。
他のリソースを利用している場合は、この行をリソースファイルに追加してください。

タイプライブラリの登録

COMシステムは、タイプライブラリの情報をレジストリに登録するためのAPIを提供しています。 タイプライブラリを読み込み、読み込んだタイプライブラリの情報をレジストリに登録するとそのすべての機能が利用できます。
タイプライブラリの読み込みには、次のLoadTypeLib()を利用します。 LoadTypeLib()は読み込んだタイプライブラリにアクセスするためのITypeLibインターフェースを返します。
HRESULT LoadTypeLib(
    LPCOLESTR pwszFile,
    ITypeLib** pptlib );
LoadTypeLib()の戻り値はSUCCEEDED(hr)またはFAILED(hr)で確認する必要があります。 pwszFileはファイル名で、 正しく読み込まれた場合、 pptlibにタイプライブラリにアクセスするためのインターフェースが返されます。
読み込んだタイプライブラリをシステムに登録するには、 RegisterTypeLib()もしくはRegisterTypeLibForUser()を利用します。
HRESULT RegisterTypeLib(
    ITypeLib* ptlib,
    LPOLESTR pwszFullPath,
    LPOLESTR pwszHelpDir );
HRESULT RegisterTypeLibForUser(
    ITypeLib* ptlib,
    LPOLESTR pwszFullPath,
    LPOLESTR pwszHelpDir );
ptlibはLoadTypeLib()で返されたインターフェース、 pwszFullPathはタイプライブラリのフルパス、 pwszHelpDirはライブラリユーザ向けヘルプファイルのおかれたディレクトリ名です。 ヘルプファイルを用意していない場合は、pwszHelpDirにNULLを指定します。
RegisterTypeLibForUser()に与えた文字列が書き換えられることは考えにくいのですが、 この引数はconstではありませんので、書き換え可能なメモリを渡したほうが良いでしょう。
RegisterTypeLib()は管理者権限が必要なAPIで、システム全体に対してタイプライブラリを登録します。 RegisterTypeLibForUser()は現在のユーザに対してタイプライブラリを登録します。 RegisterTypeLibForUser()には管理者権限は必要ありません。
RegisterTypeLib()を使った例を示します。
std::wstring    wstrName = suppGetModuleFileName( hinstEXE );
ITypeLib* ptl = NULL;
HRESULT hr;
hr = LoadTypeLib( wstrName.c_str(), &ptl );
if ( SUCCEEDED(hr) ) {
    hr = RegisterTypeLib( ptl, wstrName.c_str(), NULL );
    ptl->Release();
}

タイプライブラリの登録解除

タイプライブラリの登録を解除したい場合、タイプライブラリを読み込む必要はありません。 その代わり、タイプライブラリに含まれるLIBID、バージョン、ロケールID、ターゲットOSを引数として与える必要があります。
システム全体に対してタイプライブラリを登録した場合はUnRegisterTypeLib()、 現在のユーザに対してタイプライブラリを登録した場合はUnRegisterTypeLibForUser()で、 COMシステムに登録されたタイプライブラリ情報を削除できます。 UnRegisterTypeLib()は管理者権限が必要になります。
HRESULT UnRegisterTypeLib(
    REFGUID libID,
    WORD wVerMajor, WORD wVerMinor,
    LCID lcid, SYSKIND syskind );
HRESULT UnRegisterTypeLibForUser(
    REFGUID libID,
    WORD wVerMajor, WORD wVerMinor,
    LCID lcid, SYSKIND syskind );
libIDはlibraryで指定したLIBID、 wVerMajorおよびwVerMinorはlibraryで指定したバージョンの整数部および小数部、 lcidはlibraryで指定したロケール(指定していなければLOCALE_NEUTRAL)です。 syskindには、64bitのCOMサーバで登録を行った場合はSYS_WIN64、 32bitのCOMサーバで登録を行った場合はSYS_WIN32を指定します。

IDispatchの実装

タイプライブラリは、スクリプト言語からIDispatchを介してメソッド呼び出しを行うための情報を含んでいます。
タイプライブラリはLoadTypeLib()を用いて読み込むこともできますが、 システムに登録済みのタイプライブラリであれば、LoadRegTypeLib()を使っても読み込めます。
HRESULT LoadRegTypeLib(
    REFGUID libID,
    WORD wVerMajor, WORD wVerMinor,
    LCID lcid, ITypeLib** pptlib );
wVerMajorおよびwVerMinorはlibraryで指定したバージョンの整数部および小数部、 lcidはlibraryで指定したロケール(指定していなければLOCALE_NEUTRAL)です。 OSが64bit、32bitのいずれであっても、同じAPIで読み込めます。
タイプライブラリは複数のインターフェース情報を含んでいることがありますので、 ITypeLib::GetTypeInfoOfGuid()を利用して、タイプライブラリから指定されたIIDのタイプ情報を取得する必要があります。 GetTypeInfoOfGuid()の第1引数はREFIIDで、第2引数に渡したポインタの指すメモリにITypeInfo*が返されます。
IID_IIchigo4のタイプ情報をITypeInfo* m_ptiに取得する例を示します。
ITypeLib* ptl = NULL;
hr = LoadRegTypeLib( LIBID_Ichigo4TypeLib, 1, 0, LOCALE_NEUTRAL, &ptl );
if ( SUCCEEDED(hr) ) {
    hr = ptl->GetTypeInfoOfGuid( IID_IIchigo4, &m_pti );
    ptl->Release();
}

COMシステムは、タイプ情報を利用したIDispatchの実装をAPIとして提供しています。 COMサーバでこれらのAPIを使うとIDispatchを簡単に実装できます。
COMのAPI内容
DispGetIDsOfNames()タイプライブラリを用いたIDispatch::GetIDsOfNames()の実装です。
DispInvoke()タイプライブラリを用いたIDispatch::Invoke()の実装です。
これらのAPIの引数はIDispatchの引数に似ていますが、 次の点が異なります。
COMのAPI変更点
DispGetIDsOfNames()第1引数にITypeInfoへのポインタを渡します。IDispatch::GetIDsOfNames()にあったriidNullとlcidはありません。
DispInvoke()第1引数にcoclass自身のIDispatchへのポインタ、第2引数にITypeInfoへのポインタを渡します。IDispatch::Invoke()にあったriidNullとlcidはありません。
これらのAPIを使うと、m_ptiをITypeInfoへのポインタとして、IDispatchは次のように簡単に実装できます。 また、IDispatchを介してタイプ情報を返すこともできるようになります。
HRESULT STDMETHODCALLTYPE CIchigo4::GetTypeInfoCount(UINT* pcTInfo)
{
    *pcTInfo = (m_pti != NULL) ? 1 : 0;
    return S_OK;
}
HRESULT STDMETHODCALLTYPE CIchigo4::GetTypeInfo(UINT iTInfo, LCID lcid,
        ITypeInfo** ppTInfo)
{
    *ppTInfo = NULL;
    if ( iTInfo != 0 ) {
        return DISP_E_BADINDEX;
    }
    *ppTInfo = m_pti;
    m_pti->AddRef();
    return S_OK;
}
HRESULT STDMETHODCALLTYPE CIchigo4::GetIDsOfNames(
    REFIID riidNull, LPOLESTR* rgszNames, UINT cNames,
    LCID lcid, DISPID* rgDispId)
{
    if ( riidNull != IID_NULL ) {
        return DISP_E_UNKNOWNINTERFACE;
    }
    return DispGetIDsOfNames( m_pti, rgszNames, cNames, rgDispId );
}
HRESULT STDMETHODCALLTYPE CIchigo4::Invoke(
    DISPID dispIdMember, REFIID riidNull, LCID lcid,
    WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
    EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
    if ( riidNull != IID_NULL ) {
        return DISP_E_UNKNOWNINTERFACE;
    }
    return DispInvoke(
               this, m_pti, dispIdMember, wFlags, pDispParams,
               pVarResult, pExcepInfo, puArgErr );
}

関連ページ

  • COM/ActiveXの解説ページ 目次
  • 前の項目: MIDL
  • 次の項目: proxyとstubの利用
  • サンプルコード: サンプルCOMサーバ