ゼロからのCOM - DLL利用状態の管理

いちごパック > COM/ActiveXの解説 > ゼロからのCOM - DLL利用状態の管理

DllCanUnloadNow

coclass作成のためにDLLが読み込まれた後、 class objectやcoclassは参照カウントで管理されることになります。 しかしながら、参照カウントが0になった時点で解放されるのはこれらのクラスであって、DLLそのものではありません。 DLLが確実に解放されるのは、CoUninitialize()によりCOMシステムが終了する時点です。
これに対し、COMは使わなくなったDLLを積極的に解放するCoFreeUnusedLibraries()という関数を用意しています。 この関数はDLLが使用中であるかを各DLLのDllCanUnloadNow()という関数に問い合わせたうえで、解放可能なDLLを解放します。 したがって、DLLのCOMサーバでは、DllCanUnloadNow()というDLL外からアクセス可能な関数を実装する必要があります。
DllCanUnloadNow()の形は次の通りです。 この関数はDLLが解放可能であればS_OKを、そうでなければS_FALSEを返します。
EXTERN_C HRESULT STDAPICALLTYPE DllCanUnloadNow();

DLL利用状態の管理

関数DllCanUnloadNow()を実装するためには、 クラスオブジェクトによるDLL利用状態を管理する必要があります。 これをDLLのロック数と呼ぶことにします。
DLLのロック数を管理するには、まずグローバル変数を1つ用意します。
LONG        g_nLockDLL = 0;
coclassが作成されるたびに、ロック数を増やします。 これは、コンストラクタとデストラクタで管理できます。
CIchigo1::CIchigo1() : m_nRef(1)
{
    ++g_nLockDLL;
}
CIchigo1::~CIchigo1()
{
    --g_nLockDLL;
}
staticなclass objectが参照されている間、ロック数を増やします。 class objectをstaticにしていますので、AddRef()とRelease()で管理します。
ULONG STDMETHODCALLTYPE CIchigo1Factory::AddRef()
{
    if (m_nRef++ == 0) {
        ++g_nLockDLL;
    }
    return m_nRef;
}
ULONG STDMETHODCALLTYPE CIchigo1Factory::Release()
{
    if (--m_nRef == 0) {
        --g_nLockDLL;
    }
    return m_nRef;
}
LockServerされたときにも、ロック数を増やします。
HRESULT STDMETHODCALLTYPE CIchigo1Factory::LockServer( BOOL fLock )
{
    if ( fLock ) {
        ++g_nLockDLL;
    } else {
        --g_nLockDLL;
    }
    return S_OK;
}
これらの準備を行うことで、DllCanUnloadNow()の呼び出し時点でDLLが解放可能であるかを正しく判断できます。
EXTERN_C HRESULT STDAPICALLTYPE DllCanUnloadNow()
{
    return (g_nLockDLL <= 0) ? S_OK : S_FALSE;
}

利用状態を管理しない実装

DLL利用状態を管理しない場合は、LockServerは無視して、常に解放不可能と返す実装になります。 これでも動作に大きな支障はありませんが、できれば解放可能な形で実装しておくと良いでしょう。
EXTERN_C HRESULT STDAPICALLTYPE DllCanUnloadNow()
{
    return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CIchigo1Factory::LockServer( BOOL fLock )
{
    return S_OK;
}

関連ページ

  • COM/ActiveXの解説ページ 目次
  • 前の項目: class object
  • 次の項目: DLLサーバの自己登録