2016-08-30 14 views
2

次のサンプルでは、​​Microsoft's documentationからです:安全な方法

public delegate bool CallBack(int handle, IntPtr param); 

public class LibWrap 
{ 
    // passing managed object as LPARAM 
    // BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam); 

    [DllImport("user32.dll")] 
    public static extern bool EnumWindows(CallBack cb, IntPtr param); 
} 

public class App 
{ 
    public static void Main() 
    { 
    Run(); 
    } 

    [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)] 
    public static void Run() 
    { 
    TextWriter tw = System.Console.Out; 
    GCHandle gch = GCHandle.Alloc(tw); 

    CallBack cewp = new CallBack(CaptureEnumWindowsProc); 

    // platform invoke will prevent delegate to be garbage collected 
    // before call ends 

    LibWrap.EnumWindows(cewp, GCHandle.ToIntPtr(gch)); 
    gch.Free(); 
    } 

    private static bool CaptureEnumWindowsProc(int handle, IntPtr param) 
    { 
    GCHandle gch = GCHandle.FromIntPtr(param); 
    TextWriter tw = (TextWriter)gch.Target; 
    tw.WriteLine(handle); 
    return true; 
    } 
} 

私を神秘2つのものがあります。

まず、GCHandle.Allocのドキュメントでは、オブジェクトがガベージコレクションされないようにすることについてのみ説明しています。それがすべてだった場合は、GCHandle.Allocは必要ありません。明らかに、サンプルではtwは、EnumWindowsへのコール中に収集されません。機能スコープに参照があります。

に移動していないことを確認する必要があります。しかし、GCHandle.Allocのドキュメントではそのことについて話しません。どうしたの?

第2に、代議員はどうですか?このサンプルに問題はないかもしれませんが、デリゲートがオブジェクト(クラスのクロージャまたは非スタティックメソッドを持つラムダ)にバインドされている場合はどうなりますか?その場合、デリゲートの世話をする必要があります。それは別のGCHandle.Alloc(myDelegate)ですか、それとももっと検討する必要がありますか?

答えて

2

移動は問題になりません。 GCHandle.ToIntPtrは、後で元のハンドルを取得するためにGCHandle.FromIntPtrに渡すことができる整数値を与えることを約束します。それで全部です。メモリ内を移動するオブジェクトを停止する必要がある場合は、オブジェクトを固定する必要があります。しかし、実際にオブジェクトを固定する必要はありません。収集するのをやめ、コールバックで取り出すことができます。

p/invokeフレームワークは、コメントに記されているように、EnumWindowsへの外部呼び出し中に収集されないことを確認するため、ここでは問題は発生しません。デリゲートをアンマネージコードに渡し、アンマネージコードがデリゲートへの参照を保持している場合は、行う必要があります。デリゲートが管理されていない参照よりも優先されるようにする必要があります。

+0

ああ、私は実際に参照を使用するものの管理されていない側ではないので、ピンで固定する必要はありません。管理されていない側が参照解除されている場合のみ、ピン設定が必要になります。最後の2つの文で説明したシナリオで、デリゲート(または関連オブジェクト)を固定する必要がない理由はまだ分かりません。 – John

+1

p/invoke marshallerは、呼び出しが返るまでデリゲートが存続することを保証します。それで十分です。デリゲートオブジェクトは固定する必要はありません。マーシャラは、アドレスがアンマネージコードに渡されるアンマネージサンク(デリゲートを呼び出す)を作成します。非管理コードは直接デリゲートを呼び出すことができないため、デリゲートオブジェクトのアドレスはアンマネージコードに渡されません。したがって、サンクの必要性。 –

+0

そして、サンクは私が推測するスタックに住んでいます。それは理にかなっている。私がまだ理解していない唯一のことは、私がそうする必要がある場合にオブジェクトを固定するために何をしなければならないかということです。アンマネージコードが参照解除されていて、元の呼び出しが戻った後でそうしています。 'fixed'ステートメントはスコープ用のものだけを固定しますので、何かを無期限に修正したいのであれば(あるいはプログラムで固定するまで)何を使用しますか? – John

関連する問題