- PR -

[C#] デリゲートをGCの対象から外す方法

投稿者投稿内容
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2007-01-24 11:40
質問
デリゲートのアドレスが移動しないようにするにはどうすれば良いですか?
例えばGC.KeepAliveメソッドと適切な属性を組み合わせるのでしょうか?

背景
 あるボードのデバイスドライバーが初期化時にコールバックメソッドのポインターを渡す仕様になっています。
ボードは初期化されると一定時間毎にコールバックメソッドを呼び出し続けます。
デリゲート経由でコールバックメソッドを呼び出したところ、初期化後しばらくは正常に機能しますが、
やがてWindowsXPをフリーズ(ブルースクリーン)させてしまいます。
 デバイスドライバーは初期化時のアドレスが動かないものとして処理しているため、
GCによるデリゲートの移動でボードが誤ったアドレス参照してしまい、ハングアップするものと思われます。
デリゲートがインスタンス化している期間アドレスを固定する方法があるでしょうか?
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2007-01-24 11:51
・デリゲートインスタンスをフィールドにおいておく
・GCHandle 構造体をフィールドにおく
たぶん後者が必要ですが、前者でなんとかなるかもしれません。実験してみていただけたら幸いです。

GC.KeepAlive は、あるメソッド内で回収されるタイミングを遅らせるためだけのものです。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2007-01-24 12:06
GCHandleType.Pinned で GCHandle.Alloc かと思っていたのですが、
引用:

・デリゲートインスタンスをフィールドにおいておく


がどういった方法を指すのか分からないので、もし宜しければ教えて頂けないでしょうか。

[ メッセージ編集済み 編集者: 囚人 編集日時 2007-01-24 12:31 ]
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2007-01-24 13:12
引用:

囚人さんの書き込み (2007-01-24 12:06) より:
GCHandleType.Pinned で GCHandle.Alloc かと思っていたのですが、


単に GC による移動を防ぐだけなら Pinned は不要です。
そもそもプリミティブな型以外を含むオブジェクトには Pinned は不可能だったはずです。

引用:

引用:

・デリゲートインスタンスをフィールドにおいておく


がどういった方法を指すのか分からないので、もし宜しければ教えて頂けないでしょうか。


デリゲートのマーシャリングでやりやすいのは、デリゲートオブジェクトをメソッドローカルで作ってしまうことです。
当然、アンマネージドな世界にデリゲート(マーシャリング済みの関数ポインタ)がわたったなんてことはマネージド世界では認識されませんから、メソッドを抜けた時点で到達不能インスタンスになり、GC に片付けられます。
// と言うのは釈迦に説法かと思いますが。
今回のもそれじゃないかなぁと。
フィールドにデリゲートインスタンスを持たせれば、(null を入れない限り)そのオブジェクトが存在している間はデリゲートインスタンスは残りますから、短期的な解決にはなります。
当然これだけでは GC による移動は制限できないわけなので、多分不十分な解決のはずですが。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2007-01-25 07:55
引用:

単に GC による移動を防ぐだけなら Pinned は不要です。
そもそもプリミティブな型以外を含むオブジェクトには Pinned は不可能だったはずです。

デリゲートのマーシャリングでやりやすいのは、デリゲートオブジェクトをメソッドローカルで作ってしまうことです。
当然、アンマネージドな世界にデリゲート(マーシャリング済みの関数ポインタ)がわたったなんてことはマネージド世界では認識されませんから、メソッドを抜けた時点で到達不能インスタンスになり、GC に片付けられます。
// と言うのは釈迦に説法かと思いますが。
今回のもそれじゃないかなぁと。
フィールドにデリゲートインスタンスを持たせれば、(null を入れない限り)そのオブジェクトが存在している間はデリゲートインスタンスは残りますから、短期的な解決にはなります。
当然これだけでは GC による移動は制限できないわけなので、多分不十分な解決のはずですが。


なるほど。そういう事だったのですね。ありがとうございます。となるとアドレスが移動してしまうのは避けようがないんですね。


[ メッセージ編集済み 編集者: 囚人 編集日時 2007-01-25 07:57 ]
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2007-01-25 12:00
引用:

フィールドにデリゲートインスタンスを持たせれば、(null を入れない限り)そのオブジェクトが存在している間はデリゲートインスタンスは残りますから、短期的な解決にはなります。
当然これだけでは GC による移動は制限できないわけなので、多分不十分な解決のはずですが。



あれー? アンマネージドに渡したデリゲートがマネージ側で参照を維持してないとGCで片付けられてしまう、というのは MSDN のどこかに注意書きがあったはずです。

ですが、マネージ側でデリゲートの参照を維持している限り、デリゲートのアドレスが変わったりはしないんじゃないですかね???

移動する前提に立つと、アンマネージにデリゲートを渡すこと自体そもそも無意味な行為になってしまいませんか?
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-01-25 12:23
こんにちは。

引用:

囚人さんの書き込み (2007-01-25 07:55) より:
なるほど。そういう事だったのですね。ありがとうございます。となるとアドレスが移動してしまうのは避けようがないんですね。



デリゲートのアドレスがGCによって移動させられることがあるのであれば、
.NET Framework内部はどう対処しているのでしょう?

逆アセンブルすると、以下のようなネイティブへ引き渡されるデリゲートがたくさん定義されているのですが。

コード:
public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);



なにかアドレスを固定しておく方法がないと、おかしくないでしょうか。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-01-25 13:13
なんとなく想像で考えると、デリゲートのマーシャリングは、
・デリゲートが示すコールバックを代わりに受け取るメソッドスタブを生成する
・ネイティブコードには上記スタブのアドレスを渡す
・スタブは、対象となるデリゲートを保持していて、デリゲートにデリゲートする
という感じな気もしますが…
これだとスタブがいつ掃除されるのか微妙ですね…
※アンマネージから戻ったあとで掃除してしまうと、
 使われ続けるかもしれないコールバックを破壊してしまうし…

まあ、少なくともデリゲートインスタンスが固定されている必要はないわけで。
※固定されている必要があるのは、アンマネージの世界から直接呼び出される
 メソッド実体のアドレスであって、デリゲートインスタンスではない、と

上記は単なる想像であって、実際どういう仕組みになってるのかは知りません。

スキルアップ/キャリアアップ(JOB@IT)