Win32 App Managing the Lifetime of an Object - yoshimune/LearningDirectX11 GitHub Wiki
This page refers to 【Managing the Lifetime of an Object】
まだ触れていないCOMインターフェースのルールがあります。全てのCOMインターフェースは、IUnknownと呼ばれるインターフェースを直接的もしくは間接的に継承する必要があります。このインターフェイスは、COMオブジェクトがサポートしなければならないいくつかのベースライン機能を提供します。
IUnknownインターフェイスは以下のメソッドを定義します。
-
QueryInterface
- プログラムが実行中に機能の照会を出来るようになります。詳細は次のセクションで扱います。
- AddRef
-
Release
- AddRef と Release はオブジェクトのライフタイムコントロールに使用されます。これはこのトピックで扱います。
プログラムが何をしているかにかかわらず、リソースの割当、解放は行われます。リソースの割当は簡単です。いつリソースが開放されるかを知ることは困難です。特に、リソースのライフタイムが現在のスコープを超えている場合はより困難となります。この問題はCOMに限ったことではありません。ヒープメモリに割り当てる形式のプログラムのうちいくつかは、同じ問題を解決しています。例えば、C++は自動デストラクタが使用され、C#とJavaではガベージコレクションが使用されます。COMは参照カウント(reference counting)という方法を用います。
全てのCOMオブジェクトは内部的なカウントを保持しています。これは参照カウントとして知られています。参照カウントは、いくつのアクティブなオブジェクトへの参照があったかを追跡しています。参照カウントが0になったとき、オブジェクトは自身を削除します。大事なことなので二回言います。オブジェクトは自身を削除します。プログラムは決して明示的にオブジェクトを削除することはありません。
ここでは、参照カウントのルールを説明します。
- 最初にオブジェクトを生成したとき、参照カウントは1となる。この時点でプログラムにはオブジェクトに対する単一のポインタがあります。
- プログラムは新しい参照をポインタから複製する。ポインタを複製するとき、AddRefをオブジェクトから呼び出す必要がある。このメソッドは参照カウンタを一つ増やす。
- ポインタを使い終えたら、Releaseを呼び出す必要がある。Releaseメソッドは参照カウンタを1減らす。またポインタを無効にする。Releaseを呼んだ後に再びポインタを使用することはできない。
- 全てのポインタでReleaseを呼んだら、オブジェクトの参照カウンタは0となり、オブジェクトは自身を削除する
プログラムはオブジェクトの作成とオブジェクトへのポインタ(p)を保持します。この時点で参照カウントは1です。プログラムがポインタの使用を終了するとき、Releaseを呼び出します。参照カウントは0にデクリメントされます。そして、オブジェクトは自身を削除します。今、pは使用不可能になります。pを別のメソッド呼び出しで使うとエラーになります。
次に、プログラムは新しい変数qにpをコピーにします。この時点で、プログラムはAddRefを呼び出して参照カウントをインクリメントしなくてはなりません。参照カウントは2になります。また、2つのオブジェクトへのポインタが存在します。いま、プログラムがpの使用を終了したとします。プログラムはReleaseを呼び出して、参照カウントは1となり、pは使用できなくなります。しかし、qは使用可能です。後に、プログラムがqの使用を終了しました。したがって、Releaseが再びよばれます。参照カウントは0になります。そしてオブジェクトは自身を削除します。
なぜプログラムがpをコピーするのか不思議に思うかもしれません。2つの理由があります。最初に、データ構造(リストなど)内にポインタを保持したいことがあるということ、2つ目はポインタを現在のスコープをこえて保持したい場合があることです。したがって、より広いスコープを使うため、新しい変数にポインタをコピーします。
参照カウントの利点の一つは、オブジェクトを削除するために様々なコードパスを調整することなく、コードの異なるセクション感でポインタを共有できることです。代わりに、それぞれのコードパスは、そのコードパスがオブジェクトの使用を終了するときに、単にReleaseを呼び出すだけです。そのオブジェクトは適切なタイミングで自身を削除します。
[Win32 App Example The Open Dialog Box|Win32 App Example The Open Dialog Box]で試したコードを使います。
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
IFileOpenDialog *pFileOpen;
// Create the FileOpenDialog object.
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
if (SUCCEEDED(hr))
{
// Shoe the Open dialog box.
hr = pFileOpen->Show(NULL);
// Get the file name from the dialog box.
if (SUCCEEDED(hr))
{
IShellItem *pItem;
hr = pFileOpen->GetResult(&pItem);
if (SUCCEEDED(hr))
{
LPWSTR pszFilePath;
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
//Display the file name to the user.
if (SUCCEEDED(hr))
{
MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
CoTaskMemFree(pszFilePath);
}
}
pItem->Release();
}
pFileOpen->Release();
}
CoUninitialize();
}
二回Releaseを呼んでいます。
どちらのケースも、Release呼び出しはポインタがスコープを抜ける直前に行われています。また、ReleaseはHRESULTのチェックが成功の後に呼び出されています。