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つ用意します。
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サーバの自己登録