QA@IT

GCHandle.Allocで取得される構造体のIntPtrについて

6989 PV

お世話になっております。
VBアプリ→ネイティブAPIへGCHandle.Allocで取得したIntPtrを渡して
ネイティブAPI側で構造体へ書き込んで貰うという処理を行おうとしたのですが、
何故か構造体の中身が書き込まれていませんでした。
要素1の構造体の配列を宣言して、それに対してAllocすると何故か上手くいきます。
配列に対してAllocした場合は、配列のアドレスが取得できるという認識なのですが、
構造体に対してAllocした場合、別の場所にメモリが確保されるのでしょうか?
簡単なサンプルプログラムを書いてみました。

Imports System.Runtime.InteropServices

Module Module1

    Public Structure Test1
        Public val1 As Integer
        Public val2 As Integer
        Public val3 As Integer
    End Structure

    Sub Main()
        Dim test1 As Test1
        Dim arrayTest1(0 To 0) As Test1
        Dim hTest1 As GCHandle = GCHandle.Alloc(test1, GCHandleType.Pinned)
        Dim hArrayTest1 As GCHandle = GCHandle.Alloc(arrayTest1, GCHandleType.Pinned)
        Try
            Dim pTest1 As IntPtr = hTest1.AddrOfPinnedObject
            Copy1(pTest1)
            Console.WriteLine(test1.val1)
            Console.WriteLine(test1.val2)
            Console.WriteLine(test1.val3)

            Dim pArrayTest1 As IntPtr = hArrayTest1.AddrOfPinnedObject
            Copy1(pArrayTest1)
            Console.WriteLine(arrayTest1(0).val1)
            Console.WriteLine(arrayTest1(0).val2)
            Console.WriteLine(arrayTest1(0).val3)
            Console.ReadLine()

        Finally
            hTest1.Free()
            hArrayTest1.Free()
        End Try
    End Sub

    Sub Copy1(ByVal ptr As IntPtr)
        Dim buf() As Byte = {1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0}
        Marshal.Copy(buf, 0, ptr, buf.Length)
    End Sub

End Module

(出力結果)
0
0
0
1
2
3

自分の期待する所としては、出力結果の1~3行目も1,2,3とならないといけないのではと思ってるのですが、認識が間違っているのでしょうか…

  • GCHandle.AllocはObject(つまり参照型)を受け取りますので、値型を渡すとボクシングされ、一時領域に対して実行することになってしまいます。 -
  • 値型のボクシング、アンボクシングについての理解が甘かったようです。
    勉強になりました。
    -

回答

すみません、コメントに書いてしまいました。
GCHandle.Allocは参照型のオブジェクトに対して動作するようになっていると言うことです。
例えば一度Object型の変数を経由して書き換えてもらい、値型にキャスト(アンボクシング)して取り出せば、書き換えされていると思います。

編集 履歴 (0)
  • あ、もしかするとボクシングされた値型ではダメかも、ちょっと未確認です。
    できるかも知れないしできないかも知れません。
    -
  • GCHandleはTargetで元の参照がとれるので、Object型変数にわざわざ入れる必要はないですね。
    TargetからTest1にキャスト(アンボクシング)すれば取り出せそうです。
    試してみましたけどいけましたね。
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。