BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース Marshal.ReleaseComObjectは危険な場合がある

Marshal.ReleaseComObjectは危険な場合がある

ブックマーク

原文(投稿日:2010/03/19)へのリンク

Visual StudioプラットフォームチームのプリンシパルデベロッパのPaul Harrington氏は、マネージドコードからCOMオブジェクトを破棄するためにMarshal.ReleaseComObject()を呼び出すことが、なぜ危険であり、推奨されていないかについて解説した。

Harrington氏は、Visual 2010のいくつかのコンポーネント、ウィンドウマネージャ、コマンドバー、テキストエディタをネイティブコードからマネージドコードに変更しているとき、Marshal.ReleaseComObjectを使用した問題に気づいた。VS 2005と2008のコンポーネントは、ネイティブコードで書かれており、それらをマネージドコードに移植するまでは、正常に動作していた。

マネージドコードが、COMの機能を呼び出すとき、COM 相互運用(Interop)を経由する。COMオブジェクトを呼び出すとき、CLRからランタイム呼び出しラッパ(RCW:Runtime Callable Wrapper)にラップされた、マネージドオブジェクトのルールに従ったオブジェクトが返され、ガベージコレクション(GC)の対象になる。これはRCWは、GCが動作するタイミングにクリーニングされ、アプリケーションが多くのリソースを使用しない場合、GCの実行がかなり遅延されるか、アプリケーションが終了するまで実施されない可能性があることを意味する。RCWが大きなCOMオブジェクトを保持して、GCが実行されないままアプリケーションが終了された場合、それがリークするという既存のリスクが存在する。

破棄されていないCOMオブジェクトが残るのを避けるために、COM相互運用には、いくつのクライアントがオブジェクトを呼び出しているかをカウントするRCWの参照カウンタを減少させるMarshal.ReleaseComObjectメソッドが提供されている。メソッドは、参照数の新しい値を返すが、「ランタイム呼出可能ラッパーがCOMオブジェクトをラップしたものを呼び出しているマネージドクライアントはひとつだけであるため、 通常はゼロ である」。そのとき、COMオブジェクトによって使用されている隠れたリソースは、解放されるべきである。

この仕組みは、以前のバージョンのVisual Studioでは、Marshal.ReleaseComObjectを呼び出しても問題なく動作していた。しかし、いくつかのコンポーネントがマネージドコードで書き直された時にそうではなくなった。新しいコンポーネントは、互換性を保つために相互互換レイヤであるCOM呼出可能ラッパ(CCW:COM Callable Wrapper)を経由してアクセスされる。そのため呼出側は、それがネイティブCOMオブジェクトであると考えるが、それは実際にはマネージドである。本来はCOMリソースを解放するという意味のMarshal.ReleaseComObjectが呼び出されるまではすべてうまく動作する。解放しようとしているオブジェクトがCOMではなく、マネージドのため、ランタイムは「オブジェクトの型は、__ComObjectか__ComObjectを継承したものである必要があります。」というメッセージとともにArgumentExceptionをスローする。

今のところMarshal.ReleaseComObjectを使った他の問題は存在しない。通常、メソッドを呼び出したときには、COMリソースがリリースされ、0が返される。これは、COMオブジェクトがRCWラッパーから解放されたことを意味する。もし、オブジェクトがガベージコレクトされておらずにキャッシュされている状態で、他のクライアントが同じCOMオブジェクトを呼び出した場合、「COMオブジェクトは、すでにRCWから解放されているため使用できません。」というメッセージとともにInvalidComObjectExceptionがスローされる。そのため、Marshal.ReleaseComObjectは、そのCOMオブジェクトを確実に使わないとわかった時点で呼び出す必要がある。

VS2010の問題を解決するためにチームは、すべてのMarshal.ReleaseComObjectの呼びだしを削除して、それを使用しないことを推奨した。彼らは、「VS2005とVS2008のManaged Package Frameworkにパッチを適用し、VS 2010から呼び出されたときにReleaseComObject問題が発生しないようにしたバージョンを作成した。パッチが適用されたバージョンは、“devenv.exe.config”でMicrosoft.VisualStudio.ShellとMicrosoft.VisualStudio.Shell.9.0のバインドがリダイレクトされていることを確認することができる」。この問題は、VS 2010上で、COM相互運用をしているプロジェクトでは、一般的な問題になる可能性がある。

この記事に星をつける

おすすめ度
スタイル

BT