不透明なハンドルと内部参照カウントを使用するネイティブ Dll を使用して C# で作業すると、次の P/Invoke シグネチャがあります (すべて DllImport 属性で装飾されています)。
[DllImport("somedll.dll"]
public extern IntPtr getHandleOfA(IntPtr handleToB, int index); //(1)
public extern IntPtr makeNewHandleOfA(); //(2)
public extern void addRefHandleToA(IntPtr handleToA); //(3)
public extern void releaseHandleToA(IntPtr handleToA); //(4)
public extern void doSomethingWithHandle(IntPtr handleToA) //(5)
これらの呼び出しの意味は次のとおりです。
既存のハンドル B から不透明な型 A へのポインター/ハンドルを取得します。返されたハンドルの内部参照カウントは影響を受けません。
A の新しいハンドルを作成します。内部参照カウントは事前にインクリメントされており、関数 4 を使用してクライアントがハンドルを解放する必要があります。そうしないと、リークが発生します。
ハンドル A の参照カウントを内部的に増やすように dll に指示します。これにより、関数 1 で取得したハンドルを dll が内部的に解放しないことが保証されます。
ハンドルの参照カウントを減らすように dll に指示します。ハンドルの参照カウントを増やした場合、または関数 2 を介して取得した場合に呼び出す必要があります。
ハンドルで何らかの操作を行う
IntPtr を SafeHandle の独自のサブクラスに置き換えたいと思います。新しいハンドルを作成してハンドルを取得するときの手順は明らかです。ハンドルの参照カウントは dll 内で事前にインクリメントされているため、SafeHandle の Release 関数をオーバーライドして releaseHandleToA(handle) を呼び出します。この新しいクラス「MySafeHandle」を使用して、上記の P/Incvoke シグネチャを次のように変更できます。
public extern MySafeHandleA getHandleOfA(MySafeHandleB handleToB, int index); //(1)
public extern MySafeHandleA makeNewHandleOfA(); //(2)
public extern void addRefHandleToA(MySafeHandleA handleToA); //(3)
public extern void releaseHandleToA(MySafeHandleA handleToA); //(4)
public extern void doSomethingWithHandle(MySafeHandleA handleToA) //(5)
ただし、ここにはエラーがあります。関数 1 では、取得したハンドルの参照カウントが増加していないため、ハンドルを解放しようとするとエラーになります。
したがって、次のように、getHandleOfA 呼び出しがすぐに addRefHandleToA とペアになっていることを常に確認する必要があります。
[DllImport("somedll.dll"]
private extern MySafeHandleA getHandleOfA(MySafeHandleB handleToB, int index); //(1)
[DllImport("somedll.dll"]
private extern void addRefHandleToA(MySafeHandleA handleToA); //(3)
public MySafeHandleA _getHandleOfA(MySafeHandleB handleToB, int index)
{
var safehandle = getHandleOfA(handleToB, index);
addRefHandleToA(safeHandle);
return safeHandle;
}
これは安全ですか?
編集:まあ、それは明らかに安全ではありません.addRefHandleToA(safeHandle); 失敗する可能性があります。安全にする方法はありますか?