2006 年 11 月 24 日 23 時 56 分

DllCanUnloadNow への対応


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


DllCanUnloadNow に対応するには、
サーバ単位でインスタンスの参照を数えておく必要がある。

参照の確認には、AddRef と Release が使われるが、
これはインスタンス自身の生存期間の管理だけでなく、
それを含むサーバの生存期間の管理も必要となるのだ。

そのためには、サーバのロックカウントを用意し、
AddRef, Release で増減してやる必要がある。
ここでは、単純にグローバル変数としておこう。
例によって、global.hpp に宣言しておく。

========== global.hpp ==========

//(…前略…)

#include <ole2.h>

// グローバル変数
extern HMODULE g_moduleHandle;
extern ULONG g_serverLockCount;

#endif // !global_hpp_included

========== end of global.hpp ==========

実体は global.cpp に追加する。

========== global.cpp ==========

#include "global.hpp"

// グローバル変数の実体
HMODULE g_moduleHandle = NULL;
ULONG g_serverLockCount = 0;

========== end of global.cpp ==========

このロックカウント変数を使えば、
DllCanUnloadNow は以下のように実装できる。

========== library.cpp ==========

//(…前略…)
//(…DllMain…)

STDAPI DllCanUnloadNow() {
    return (g_serverLockCount == 0) ? S_OK : S_FALSE;
}

========== end of library.cpp ==========

後は、適切なタイミングでロックカウントを増減することだ。

ロックカウントは、あらゆるインスタンスの
AddRef, Release の呼び出しに依存している。

従来の Object クラスの実装では、
サーバの生存期間の管理を考慮していなかったが、
ロックカウントを更新するように書き換えてみよう。

========== Object.cpp ==========

//(…前略…)
//(…Object::Object…)
//(…Object::~Object…)
//(…Object::QueryInterface…)

ULONG Object::AddRef() {
    // サーバをロック
    ++g_serverLockCount;

    // 参照カウンタを 1 加算
    return ++referenceCount;
}
ULONG Object::Release() {

    // 参照カウンタを 1 減算
    ULONG ref = --referenceCount;

    // カウンタが 0 なら自身を破棄
    if (ref == 0) {
        delete this;
    }

    // サーバをアンロック
    --g_serverLockCount;

    return ref;
}

========== end of Object.cpp ==========

こうしておくと、Object から派生する全クラスで、
自動的なサーバのロックが行われるようになる。

ロックはサーバの全インスタンスのカウンタとなるので、
インスタンスの利用頻度が高い場合、
相当な回数ロックされる可能性がある。

実質 4,294,967,295 まで数えられるので、
カウンタが溢れてしまう可能性はないと思うが、
心配な方は、最初の AddRef の時のみロックし、
最後の Release の時のみロック解除するようにすれば、
さらにカウンタを節約できるようになる。

明日は、クラスオブジェクトの作成を行おう。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.