参照カウントによるオブジェクトの有効期間の管理

Tip

手動 AddRef/Releaseの代わりにスマート ポインターを使用します。 手動参照カウントは、COM バグの #1 ソース (リークしたオブジェクト、use-after-free、double-release) です。 最新の C++ コードでは、参照カウントを自動的に処理するスマート ポインターを使用する必要があります。

#include <wrl/client.h>   // Microsoft::WRL::ComPtr

// ComPtr automatically calls AddRef on copy and Release on destruction
Microsoft::WRL::ComPtr<IStream> stream;
HRESULT hr = CreateStreamOnHGlobal(nullptr, TRUE, &stream);
if (FAILED(hr)) return hr;

// Pass to another interface — use .Get() for raw pointer
// Note: operator& on ComPtr asserts if it already holds a value.
// Use .ReleaseAndGetAddressOf() if the ComPtr may be non-null.
Microsoft::WRL::ComPtr<IUnknown> unknown;
hr = stream.As(&unknown);  // Safe QueryInterface — no manual Release needed

選択肢:

  • winrt::com_ptr<T> — C++/WinRT スマート ポインター (C++17 が必要、WinRT プロジェクションと統合されます)
  • wil::com_ptr_nothrow<T>Windows実装ライブラリ (例外なし代替)

以下で説明する手動の AddRef/Release 規則は、COM の内部的な動作の理解と COM サーバーの実装に関連していますが、クライアント コードでこれらのメソッドを直接呼び出す必要はほとんどありません。

従来のオブジェクト システムでは、オブジェクトのライフ サイクル (つまり、オブジェクトの作成と削除に関する問題) は、言語 (または言語ランタイム) によって暗黙的に処理されるか、アプリケーション プログラマによって明示的に処理されます。

再利用されたコンポーネントで構成される、進化し、分散型に構築されたシステムでは、コンポーネントの有効期間に対処する方法を常にクライアントやプログラマが常に "知っている" ことは事実ではなくなりました。 適切なセキュリティ特権を持つクライアントの場合、単純な要求を使用してオブジェクトを作成するのは比較的簡単ですが、オブジェクトの削除は完全に別の問題です。 オブジェクトが不要になり、削除する必要がある場合は、必ずしも明確ではありません。 (ガベージ コレクションされたプログラミング環境 (Java など) に慣れている読者は、一致しない場合があります。ただし、Java オブジェクトはマシンやプロセスの境界をまたがらないため、ガベージ コレクションは単一プロセス空間内のオブジェクトに制限されます。また、Javaは単一のプログラミング言語を強制的に使用します)。元のクライアントがオブジェクトを使用して実行された場合でも、他のクライアントまたはクライアントがオブジェクトへの参照を持っている可能性があるため、単にオブジェクトをシャットダウンすることはできません。

オブジェクトが不要になっていることを確認する 1 つの方法は、クロスプロセス またはクロスチャネル オブジェクトへのすべての接続が消えたときにシステムに通知するために、基になる通信チャネルに完全に依存することです。 ただし、このメソッドを使用するスキームは、いくつかの理由で受け入れられません。 1 つの問題は、クロスプロセス/クロスネットワーク プログラミング モデルと単一プロセス プログラミング モデルの間に大きな違いが必要になる可能性があるということです。 クロスプロセス/クロスネットワークプログラミングモデルでは、通信システムはオブジェクトの有効期間管理に必要なフックを提供しますが、単一プロセスプログラミングモデルでは、通信チャネルを介入することなくオブジェクトが直接接続されます。 もう 1 つの問題は、このスキームによって、インプロセス の場合にコンポーネントのパフォーマンスを妨げるシステム提供ソフトウェアのレイヤーが発生する可能性もあるということです。 さらに、明示的な監視に基づくメカニズムは、数千または数百万のオブジェクトにスケーリングする傾向はありません。

COM は、この一連の問題に対してスケーラブルで分散型のアプローチを提供します。 クライアントは、オブジェクトを使用しているとき、および使用が完了したときにオブジェクトに通知し、オブジェクトは不要になったときにオブジェクト自体を削除します。 このアプローチでは、すべてのオブジェクトが自身への参照をカウントすることが義務付けられています。 本質的にガベージ コレクションなどの独自の有効期間管理スキームを持つ Java などのプログラミング言語では、COM の参照カウントを使用して COM オブジェクトを内部的に実装して使用できるため、プログラマは COM オブジェクトの処理を回避できます。

アプリケーションは、メモリが使用されなくなったら割り当てたメモリを解放する必要があるのと同様に、オブジェクトのクライアントは、そのオブジェクトが不要になったときにオブジェクトへの参照を解放する必要があります。 オブジェクト指向システムでは、クライアントはオブジェクトに自身を解放する命令を与えるだけでこれを行うことができます。

オブジェクトは、使用されなくなったときに割り当てを解除することが重要です。 難しいのは、オブジェクトの割り当てを解除することが適切なタイミングを判断することです。 これは自動変数 (スタックに割り当てられている変数) では簡単です。宣言されているブロックの外側では使用できないため、コンパイラはブロックの末尾に達したときに割り当てを解除します。 動的に割り当てられる COM オブジェクトの場合、オブジェクトを使用する必要がなくなったタイミング (特に複数のクライアントが同時に使用する可能性があるローカル オブジェクトまたはリモート オブジェクト) を決定するのは、オブジェクトのクライアントの責任です。 オブジェクトは、すべてのクライアントがそのオブジェクトの使用を終えるまで待機してから、自分自身を解放する必要があります。 COM オブジェクトはインターフェイス ポインターを介して操作され、異なるプロセス内のオブジェクトや他のマシンで使用できるため、システムはオブジェクトのクライアントを追跡できません。

オブジェクトの割り当てを解除するのが適切なタイミングを判断する COM の方法は、手動での参照カウントです。 各オブジェクトは、接続されているクライアントの数 (つまり、任意のクライアント内の任意のインターフェイスに存在するポインターの数) を追跡する参照カウントを保持します。

詳細については、次のトピックを参照してください。

IUnknown の使用と実装