メモリ管理規則

インターフェイスへのポインターの有効期間は常に、すべての COM インターフェイスの AddRef および Release メソッドによって管理されます。 詳細については、「参照カウント を管理するためのルール」を参照してください。

その他のすべてのパラメーターについては、メモリを管理するための特定の規則に従うことが重要です。 次の規則は、インターフェイス メソッドのすべてのパラメーター (戻り値を含む) に適用され、値渡しされません。

  • インパラメーターは、呼び出し元によって割り当てられ、解放される必要があります。
  • 出力パラメーターは、呼び出されたパラメーターによって割り当てられる必要があります。これらは、標準の COM タスク メモリ アロケーターを使用して呼び出し元によって解放されます。 詳細については、「OLE メモリ アロケーター」を参照してください。
  • In/out パラメーターは、最初は呼び出し元によって割り当てられ、必要に応じて呼び出されたパラメーターによって解放され、再割り当てされます。 out パラメーターの場合と同様に、呼び出し元は最終的な戻り値を解放する必要があります。 標準の COM メモリ アロケーターを使用する必要があります。

後者の 2 つのケースでは、1 つのコードでメモリが割り当てられ、別のコードによって解放されます。COM アロケーターを使用すると、2 つのコードで同じ割り当て方法が使用されます。

特に注意が必要なもう 1 つの領域は、故障状態でのアウトパラメーターとインアウト パラメーターの処理です。 関数がエラー コードを返す場合、呼び出し元には通常、out パラメーターまたは in-out パラメーターをクリーンアップする方法はありません。 これにより、次の追加ルールが作成されます。

  • エラー状態が発生した場合、パラメーターは常に、呼び出し元によるアクションなしでクリーンアップされる値に確実に設定する必要があります。
  • すべての出力ポインター パラメーターは、NULL 明示的に設定する必要があります。 これらは通常、ポインターからポインターへのパラメーターで渡されますが、呼び出し元が割り当て、呼び出されたコードが塗りつぶす構造体のメンバーとして渡すこともできます。 これを確実に行う最も簡単な方法は、これらの値を関数エントリで NULL 設定することです。 この規則は、より堅牢なアプリケーションの相互運用性を促進するため、重要です。
  • エラー状態では、out パラメーターエラーの戻り値のように、すべてのインアウト パラメーターを呼び出されたコードによって単独にするか (したがって、呼び出し元によって初期化された値に残る) か、明示的に設定する必要があります。

COM アプリケーションに対するこれらのメモリ管理規則は、パブリック インターフェイスと API 間でのみ適用されることに注意してください。COM アプリケーションに対して厳密に内部的にメモリ割り当てを行う必要がある場合、これらのメカニズムを使用する必要はありません。

Warning

.NET/マネージド コード相互運用における一般的な落とし穴: .NET (C# または VB.NET から COM インターフェイスを使用する場合、ランタイム呼び出し可能ラッパー (RCW) はほとんどのメモリ管理を自動的に処理します。 ただし、CoTaskMemAlloc割り当て文字列 (out LPWSTR) を返す COM スタイルの関数を P/Invoke する場合は、Marshal.FreeCoTaskMemを使用して解放する必要があります。ガベージ コレクターは COM タスク メモリを解放しません。

// C# example — COM function returns a CoTaskMemAlloc'd string
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
static extern int SHGetKnownFolderPath(
    ref Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr ppszPath);

// Caller must free the returned string
IntPtr pathPtr = IntPtr.Zero;
try
{
    Guid folderId = new("374DE290-123F-4565-9164-39C4925E467B"); // Downloads
    int hr = SHGetKnownFolderPath(ref folderId, 0, IntPtr.Zero, out pathPtr);
    Marshal.ThrowExceptionForHR(hr);
    string path = Marshal.PtrToStringUni(pathPtr)!;
}
finally
{
    if (pathPtr != IntPtr.Zero)
        Marshal.FreeCoTaskMem(pathPtr); // Required — prevents memory leak
}

おおまかなやり方: 呼び出し元が CoTaskMemFree を使用して out-parameter を解放する必要があることをネイティブ COM 関数が文書化している場合、マネージド同等の関数は Marshal.FreeCoTaskMem。 COM タスク メモリには Marshal.FreeHGlobal を使用しないでください。これらは異なるアロケーターです。

COM は、クライアントとサーバー間の通信にリモート プロシージャ コール (RPC) を内部的に使用します。 RPC サーバー スタブでのメモリ管理の詳細については、「Server-stub Memory Management」トピックを参照してください。