c# ネイティブリソースをラップする方法 (その1´)

その1では、ネイティブリソースをイミュータブルなクラスでラップする方法を示します。イミュータブルとは、インスタンスの内部状態が、変化しないクラスのことです。ナイーブな実装は↓のようなものです。

class A
{
  protected IntPtr handle;

  public A()
  {
    handle = Api.Alloc();
  }

  ~A()
  {
    Api.Release(handle);
  }
  
  public void Method()
  {
    Api.Method(handle);
  }
}

Api クラスでネイティブ API の呼びだしが実装されていることを想定した疑似コードです。実際には、Api.Alloc では、ILCreateFromPathW でアイテムIDリストを作成したり、Api.Release では、ILFree で開放したりします。

しかし、c# では、ファイナライザーの実行タイミング がかなりアグレッシブなため、(環境によっては)、稀に不正なメモリーアクセスなどの原因で、クラッシュします。 Api.Method(handle) 実行中に、~A() が呼ばれる場合があるためです。これを避けるには、↓のようにします。

class A
{
  protected IntPtr handle;

  public A()
  {
    handle = Api.Alloc();
  }

  ~A()
  {
    Api.Release(handle);
  }
  
  public void Method()
  {
    try
    {
      Api.Method(handle);
    }
    finally
    {
      GC.KeepAlive(this);
    }
  }
}

handle を使う場合には、handle の必要がなくなる場所で、 GC.KeepAlive(this) を呼びだして、早期の開放を阻止します。 handle はスレッド間で共有されるので一般的には、ロックが必要ですが、handle をコンストラクターとファイナライザー以外で変更しなければロックは不要だと思います。(イミュータブルなクラスを採用したのはこのためです)

handle を外部で使用しない方が安全ですが、公開する場合には、やはり、GC.KeepAlive で早期開放を阻止する必要があります。

ミュータブルなクラスでは、lock の必要があるので、ネイティブリソースをラップする方法 (その2) のように保護すると良いでしょう。

となりのページ

このサイトについて

このサイトのページへのリンクは自由に行っていただいてかまいません。
このサイトで公開している全ての画像、プログラム、文書の無断転載を禁止します。

連絡先

ここをクリック すると表示されるページから作者へメールで連絡できます。

共有