@IT会議室は、ITエンジニアに特化した質問・回答コミュニティ「QA@IT」に生まれ変わりました。ぜひご利用ください。
- PR -

Marshal.UnsafeAddrOfPinnedArrayElementについて

1
投稿者投稿内容
gv73a
会議室デビュー日: 2005/11/30
投稿数: 2
投稿日時: 2005-11-30 16:05
初の投稿になります。

掲題の件ですが、
今C#のコードからアンマネージドC++で作成された
ライブラリ関数に対してbyte配列のポインタを渡す
時にUnsafeAddrOfPinnedArrayElementを使用して渡しています。

内容としては、TCPで受信したbyte配列データの先頭ポインタをC++側のライブラリに渡してC++側で受信データの最後まで走査しているのですがごく稀に成功するはずの
データがNGとなります。

NG条件の一つとして参照アドレスの内容がNULLのパターンがあります。

UnsafeAddrOfPinnedArrayElementに対する
MSDNの日本語のHELPですと
・配列は、 GCHandle を使用して pin を実行してあります。
とありますが、
英語のHELPですと
・The array must be pinned using a GCHandle before it is passed to this method
とあり、メソッドをCallする前に「GCHandle でpinningしなさい」と解釈できます。

現状は引数として渡す配列に対して「pinningしない」でC++側へ
渡していますが、C++側でポインタ参照している間でもガベージコレクタ側で
メモリの再配置が起きたりするのでしょうか?

初歩的な質問で申し訳ありません。

ご意見頂けたら幸いです。

_________________
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-11-30 16:22
しろと書かれてるんだからしましょうよ。

Marshal.UnsafeAddrOfPinnedArrayElementじゃあ、その場でアドレスを取った後はそのアドレスと配列は完全に切り離されますから。
素直に配列としてアンマネージド関数の引数(や構造体メンバ)に定義してやればマーシャラが呼び出し中の固定もやってくれるんですけどね。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2005-11-30 16:26
こんにちは。

引用:

配列は、 GCHandle を使用して pin を実行してあります。


「配列は、 GCHandle を使用して pin を実行します。」と書いていないところを汲み取れという事でしょうか。
多分、そのメソッドを実行する前提条件なんでしょうね。英語のほうも読む限り。
なかなか辛いものがありますねぇ。

いろいろテストしてみてはどうでしょう。
(強制でガベージコレクションを何度も起動させ、アドレスを見てみるとか。)

#余談
引用:

内容としては、TCPで受信したbyte配列データの先頭ポインタをC++側のライブラリに渡してC++側で受信データの最後まで走査しているのですがごく稀に成功するはずの
データがNGとなります。


だと、「ごく稀にしか成功しないデータが、NG になります」と読めます。
「成功するはずのデータが、ごく稀に NG になります」とかのほうが。
#もしや前者であってた?
_________________
囚人のジレンマな日々
gv73a
会議室デビュー日: 2005/11/30
投稿数: 2
投稿日時: 2005-11-30 16:49
早速のご意見ありがとうございます。

Hongliangさん>
引用:始まり
「Marshal.UnsafeAddrOfPinnedArrayElementじゃあ、その場でアドレスを取った後はそのアドレスと配列は完全に切り離される」
引用:終わり

とありますが、実際にウォッチメモリで関数の戻り値が指している
アドレスを覗いて見たのですが、アドレスが指している番地からサ
イズ分の領域に全て受信した内容が残っていました。
切り離されているようには見えなかったのですが「関数仕様」なのでしょうか?
この辺の情報元があれば心強いです。
何れにせよ使い方の問題と解りましたのですっきりしています。

囚人さん>
今、GC.Collect()でまさにテストをしています。
何かわかりましたらここでまたご報告したいと思っています。

記述が曖昧で申し訳ないです。
意図としては「成功するはずのデータが、ごく稀に NG になります」
になります。

現状GCHandleを使ったことがあまりないので
AllocHGlobalでアンマネージド領域にメモリを確保しコピーして
C++側の関数をCallしようと考えています。



_________________
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-11-30 17:03
引用:

とありますが、実際にウォッチメモリで関数の戻り値が指している
アドレスを覗いて見たのですが、アドレスが指している番地からサ
イズ分の領域に全て受信した内容が残っていました。
切り離されているようには見えなかったのですが「関数仕様」なのでしょうか?
この辺の情報元があれば心強いです。
何れにせよ使い方の問題と解りましたのですっきりしています。


いえ、単にGCが動かす対象になるというだけの話ですが。
Pinnedされていない配列は、GCの最適化処理によって要素やアドレスが動いたりする可能性があります。
で、Pinnedでない配列のMarshal.UnsafeAddrOfPinnedArrayElementをとった後、関数呼び出しが終了するまでにGCが配列の移動を行わない保証はありません。
ですからMarshal.UnsafeAddrOfPinnedArrayElementを使うにはGCHandle/GCHandleType.Pinnedで固定する必要があります。
もちろんアドレスをとった後Pinnedするのも無意味です。その間にGCが動く可能性があります。
アドレスをとる前から関数を呼び出すまでは固定しておかなければなりません。

で、自然な解としては私が先に提示した
引用:

素直に配列としてアンマネージド関数の引数(や構造体メンバ)に定義してやればマーシャラが呼び出し中の固定もやってくれるんですけどね。


なんですけどね。
//アドレスがアンマネージド側に保存され、後で使われるようなのには向きませんが。
1

アイティメディアの提供サービス

ホワイトペーパー(TechTargetジャパン/閲覧には会員登録が必要です)

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