テクニカル ノート 64: ActiveX コントロールにおけるアパートメント モデルのスレッド処理
注意
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。 結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。 最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。
このテクニカル ノートでは、ActiveX コントロールにおいてアパートメント モデルのスレッドを可能にする方法について説明します。 アパートメント モデルのスレッドは Visual C++ Version 4.2 以降でのみサポートされています。
アパートメント モデルのスレッドとは
アパートメント モデルとは、マルチスレッドのコンテナー アプリケーションで ActiveX コントロールのような埋め込みオブジェクトをサポートするための方法です。 アプリケーションが複数のスレッドを持っている場合でも、埋め込みオブジェクトの各インスタンスは 1 つの「アパートメント」に割り当てられ、1 つのスレッド上でのみ実行されます。 つまり、1 つのコントロールのインスタンスに対する呼び出しはすべて同じスレッドで行われます。
ただし、同じタイプのコントロールの異なるインスタンスは、異なるアパートメントに割り当てられることがあります。 したがって、あるコントロールの複数のインスタンスがデータ (静的データやグローバル データなど) を共有する場合は、この共有データへのアクセスはクリティカル セクションなどの同期オブジェクトによって保護される必要があります。
アパートメント モデルのスレッドの詳細については、『Windows Base Services』の「Processes and Threads」を参照してください。
アパートメント モデルのスレッドをサポートする理由
アパートメント モデルのスレッドをサポートするコントロールは、アパートメント モデルをサポートするマルチスレッドのコンテナー アプリケーションで使用できます。 アパートメント モデルのスレッド処理を有効にしないと、コントロールを使用できるコンテナーの能力が制限されることになります。
ほとんどのコントロールにおいて、特に共有データが少ないか存在しない場合には、容易にアパートメント モデルのスレッド処理を可能にできます。
共有データの保護
コントロールが静的メンバー変数のような共有データを使用する場合、クリティカル セクションを使用してそのデータへのアクセスを保護することで、複数のスレッドでデータを同時に変更することを防ぐ必要があります。 この目的のためにクリティカル セクションを設定するには、コントロール クラス内でクラス CCriticalSection の静的メンバー変数を宣言します。 コードで共有データにアクセスするたびに、このクリティカル セクション オブジェクトの Lock メンバー関数と Unlock メンバー関数を使用します。
たとえば、すべてのインスタンスで共有される文字列を管理する必要のあるコントロール クラスがあるとします。 この文字列を静的メンバー変数で管理し、クリティカル セクションによって保護できます。 このコントロールのクラス宣言は次のようになります。
class CSampleCtrl : public COleControl
{
...
static CString _strShared;
static CCriticalSection _critSect;
};
このクラスの実装には以下の定義が含まれます。
int CString CSampleCtrl::_strShared;
CCriticalSection CSampleCtrl::_critSect;
静的メンバー _strShared へのアクセスは、クリティカル セクションによって保護されます。
void CSampleCtrl::SomeMethod()
{
_critSect.Lock();
if (_strShared.Empty())
_strShared = "<text>";
_critSect.Unlock();
...
}
アパートメント モデル対応コントロールの登録
アパートメント モデルのスレッドをサポートするコントロールは、名前付き値 "ThreadingModel" とその値 "Apartment" を class id\InprocServer32 キーの下のクラス ID レジストリのエントリに追加することで、その機能をレジストリで示す必要があります。 このキーをユーザー定義のコントロールに自動的に登録するには、AfxOleRegisterControlClass の 6 番目のパラメーターで afxRegApartmentThreading フラグを渡します。
BOOL CSampleCtrl::CSampleCtrlFactory::UpdateRegistry(BOOL bRegister)
{
if (bRegister)
return AfxOleRegisterControlClass(
AfxGetInstanceHandle(),
m_clsid,
m_lpszProgID,
IDS_SAMPLE,
IDB_SAMPLE,
afxRegApartmentThreading,
_dwSampleOleMisc,
_tlid,
_wVerMajor,
_wVerMinor);
else
return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}
コントロールのプロジェクトを Visual C++ Version 4.1 以降の ControlWizard で生成した場合は、このフラグは最初からコードに含まれています。 スレッド処理モデルを登録するための変更は必要ありません。
Visual C++ Version 4.1 以前のバージョンの ControlWizard で生成したプロジェクトの場合は、既存のコードの 6 番目のパラメーターはブール値です。 既存のパラメーターが TRUE である場合、これを afxRegInsertable | afxRegApartmentThreading に変更します。 既存のパラメーターが FALSE である場合、これを afxRegApartmentThreading に変更します。
アパートメント モデルのスレッド処理の規則に従わないコントロールの場合、このパラメーターに afxRegApartmentThreading を渡さないでください。