2012-05-09 5 views
0

一部のC++/CLIコードでは、独自のマネージ.NETラッパーオブジェクトを作成するファクトリメソッドGetWrapper()を持つネイティブクラスがあります。内部的には、GCHandleを介してラッパーへの弱い参照を保持します。 GetWrapper()が呼び出されると、GCHandleがチェックされ、既存のラッパーへのハンドルが返されます(または、古いラッパーオブジェクトがガーベジコレクターによって破棄されたためにオブジェクトを指していない場合)。新しいものが作成されます返される。GCHandle.IsAllocatedは、弱いハンドルを破棄したオブジェクトに対してfalseを返しません。

// .h 
class NativeClass 
{ 
public: 
    WrapperClass^ GetWrapper(); 
private: 
    WrapperClass^ GetNewWrapper(); 
    GCHandle m_wrapperGCHandle; 
}; 

// .cpp 
WrapperClass^ NativeClass::GetWrapper() 
{ 
    if(m_wrapperGCHandle.IsAllocated) 
    { 
     try 
     { 
      WrapperClass^ wrapper = nullptr; 
      wrapper = dynamic_cast<WrapperClass^>(wrapperGCHandle.Target); 

      if(wrapper == nullptr) 
      { 
       return GetNewWrapper(); 
      } 
      else 
      { 
       return wrapper; 
      } 
     } 
     catch(System::InvalidOperationException^) 
     { 
      return GetNewWrapper(); 
     } 
    else 
    { 
     return GetNewWrapper(); 
    } 
} 

WrapperClass^ NativeClass::GetNewWrapper() 
{ 
    WrapperClass^ wrapper = gcnew WrapperClass(/*some args*/); 
    m_wrapperGCHandle = GCHandle::Alloc(wrapper, GCHandleType::Weak); 
} 

奇妙なことが今m_wrapperGCHandle.IsAllocated常にラッパーがガベージコレクトされている場合でも、trueを返すということです。 MSDN tellsは、 "弱いハンドルを使用してGCHandleが利用可能かどうかを判断するときにこのプロパティを使用する"ように設定しました。しかし、それはいつも真実です。使用できない場合、ターゲットは代わりにnullptrです。

何かが見つからない、またはMSDNが間違っていますか?

+0

コードに暗黙のスレッド競合があります。 IsAllocatedをテストした直後にGCが実行されることがあります。 –

+0

@HansPassantはい、現在スレッドセーフではありません –

答えて

1

m_wrapprGCHandle.IsAllocatedは、m_wrapperGCHandle.Freeが呼び出されるまでtrueを返します。IsAllocatedプロパティは、ハンドルによって保持されている参照の状態ではなく、ハンドルの状態をチェックしています。

前述のとおり、オブジェクトがガベージコレクションされている場合、m_wrapperGCHandle.Targetはnullです。私は、マネージラッパークラスを生成するためにあなたのサンプルコードで持っているものと同様のメソッドを使用しています。Targetがnullであるかどうかを常に確認し、Targetがnullの場合はラッパーオブジェクトを再生成します。

また、提案は... GCHandle::Allocに対応するm_wrapperGCHandle.Free呼び出しを呼び出すため、コードにハンドルリークがあるかのように見えます。クラスのコンストラクタとデストラクタでFreeへの呼び出しにAllocに電話を入れてみてください:

NativeClass::NativeClass() 
{ 
    m_wrapperGCHandle = GCHandle::Alloc(nullptr, GCHandleType::Weak); 
} 

NativeClass::~NativeClass() 
{ 
    m_wrapperGCHandle.Free(); 
} 

、あなたのGetNewWrapper方法は単純です:

WrapperClass^ NativeClass::GetNewWrapper() 
{ 
    m_wrapperGCHandle.Target = gcnew WrapperClass(/*some args*/); 
} 

、あなたがGetWrapperからif(m_wrapperGCHandle.IsAllocated) - elseチェーンを削除することができます方法。

関連する問題