今まで、Native を叩くコードしか書いてなかったのだけど、
コールバックの中で Managed なオブジェクトを触りたくて、
Native に Managed なオブジェクトを渡す必要が生じた。
なんか、ちょこちょこ見たんだけどイマイチわからん。
とりあえず書いてみようということで実験
// Native に渡したいのは System::IO::Stream^ の stream です。 // まずは GCHandle の割り当て GCHandle normalGCHandle = GCHandle::Alloc(stream, GCHandleType::Normal); // GCHandle pinnedGCHandle = GCHandle::Alloc(stream, GCHandleType::Pinned); // POD な Managed しかピン止めできないらしい GCHandle weakGCHandle = GCHandle::Alloc(stream, GCHandleType::Weak); GCHandle weakTRGCHandle = GCHandle::Alloc(stream, GCHandleType::WeakTrackResurrection); // これは全滅。ピン止めしてないとできないらしい // System::IntPtr normalGCPointer = (System::IntPtr)normalGCHandle.AddrOfPinnedObject(); // System::IntPtr pinnedGCPointer = (System::IntPtr)pinnedGCHandle.AddrOfPinnedObject(); // System::IntPtr weakGCPointer = (System::IntPtr)weakGCHandle.AddrOfPinnedObject(); // System::IntPtr weakTRGCPointer = (System::IntPtr)weakTRGCHandle.AddrOfPinnedObject(); // 代わりにこっち System::IntPtr normalGCPointer = GCHandle::ToIntPtr(normalGCHandle); // System::IntPtr pinnedGCPointer = GCHandle::ToIntPtr(pinnedGCHandle) System::IntPtr weakGCPointer = GCHandle::ToIntPtr(weakGCHandle); System::IntPtr weakTRGCPointer = GCHandle::ToIntPtr(weakTRGCHandle); // 生 void* を取る void* pNormalRawPtr = normalGCPointer.ToPointer(); // void* pPinnedRawPtr = pinnedGCPointer.ToPointer(); void* pWeakRawPtr = weakGCPointer.ToPointer(); void* pWeakTRRawPtr = weakTRGCPointer.ToPointer(); // 生 void* を確認 fprintf(stdout, "normal: %p\n", pNormalRawPtr); // fprintf(stdout, "pinned: %p\n", pPinnedRawPtr); fprintf(stdout, " weak: %p\n", pWeakRawPtr); fprintf(stdout, "weakTR: %p\n", pWeakTRRawPtr); // 生 void* から IntPtr に戻す System::IntPtr restoredNormalIntPtr = IntPtr(pNormalRawPtr); System::IntPtr restoredWeakIntPtr = IntPtr(pWeakRawPtr); System::IntPtr restoredWeakTRIntPtr = IntPtr(pWeakTRRawPtr); // ちゃんと IntPtr ができてるか確認 System::Diagnostics::Debug::Print("restoredNormalIntPtr:" + restoredNormalIntPtr); System::Diagnostics::Debug::Print("restoredWeakIntPtr:" + restoredWeakIntPtr); System::Diagnostics::Debug::Print("restoredWeakTRIntPtr:" + restoredWeakTRIntPtr); // GCHandle を復元してみる GCHandle restoredNormalGCHandle = GCHandle::FromIntPtr(restoredNormalIntPtr); GCHandle restoredWeakGCHandle = GCHandle::FromIntPtr(restoredWeakIntPtr); GCHandle restoredWeakTRGCHandle = GCHandle::FromIntPtr(restoredWeakTRIntPtr); // GCHandle を復元できてるか確認 System::Diagnostics::Debug::Print("restoredNormalGCHandle:" + restoredNormalGCHandle); System::Diagnostics::Debug::Print("restoredWeakGCHandle:" + restoredWeakGCHandle); System::Diagnostics::Debug::Print("restoredWeakTRGCHandle:" + restoredWeakTRGCHandle); // GCHandle から元のオブジェクトに戻す Stream^ normalStream = (Stream^)restoredNormalGCHandle.Target; Stream^ weakStream = (Stream^)restoredWeakGCHandle.Target; Stream^ weakTRStream = (Stream^)restoredWeakTRGCHandle.Target; // 元の System::IO::Stream^ になってるか確認 System::Diagnostics::Debug::Print("restoredNormalGCHandle:" + normalStream); System::Diagnostics::Debug::Print("weakStream:" + weakStream); System::Diagnostics::Debug::Print("weakTRStream:" + weakTRStream); // GCHandle は Free() する(FromIntPtr で取得した分は Free() する必要があるのか?) normalGCHandle.Free(); // pinnedGCHandle.Free(); weakGCHandle.Free(); weakTRGCHandle.Free();
GCHandleType::Normal, GCHandleType::Weak, GCHandleType::WeakTrackResurrection を指定した分は
なんとなく出来てるっぽい気がするが、MSDN に
Normal ハンドルは非透過です。つまり、このハンドルによって、その中に格納されているオブジェクトのアドレスを解決することはできません。
って書いてあるのが気になる。デリファレンスできてる気がするんですが…?
まあ Weak でも、処理が終わるまで Managed 側で元オブジェクトを保持しておけばいいんだと思うんだけど…。
なんかやっぱり色々小難しいなぁ。
っていうか、IntPtr って "InternalPointer" の略語だと思ってきたんだけど、
もしかして "InteriorPointer" のことだったのかな?
cli::interior_ptr
cli::interior_ptr
対象がコンパクションで移動しても大丈夫ってことなのかな?