構造体->Byte配列->構造体とコピーする方法は?

解決


M  2005-03-31 02:02:51  No: 89161

VB.NETを使用してます。

そこで、構造体からByte配列に複写、後にByte配列から構造体に複写する必要があります。
その方法を教えてください。

構造体は自分で組んだもので、IntegerやらStringやらがぐちゃぐちゃに組み込まれています。
それを一旦Byte配列に複写し(ファイル保存等のため)、その後別の構造体(フォーマットは同じ)に複写しなおします。
いろいろと過去の質問をチェックしたのですが、どうもはっきり判りません。

ご存知の方、ご教授ください。


特攻隊長まるるう  2005-03-31 02:48:39  No: 89162

過去ログを見たところ…
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200407/04070122.txt
でサイズを求めてそのサイズ分のバイト配列と構造体で CopyMemory ?
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200501/05010106.txt

Marshal.Copy メソッドだとどうなるんだろ?構造体の先頭の要素を渡すのかなぁ???
詳しくは調べてみないと分からないです。

ファイル保存だけなら構造体のまま保存できるから必要ないんですけどね。
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200308/03080028.txt


M  2005-03-31 04:25:39  No: 89163

ご回答ありがとうございます。

しかしながら、う〜ん…(汗)
Marshal.Copyを使ってみると、Byte配列とInteger配列の間だとうまくいっているみたいなんですけど、Byte配列の相手を構造体に変えた時点で、エラーまみれになってしまいました。宣言の仕方が悪いのでしょうか?(汗)

>サイズを求めてそのサイズ分のバイト配列と構造体で CopyMemory ?
そして、CopyMemoryなんですけど、
本当は初めは「これ!」と思ったのですが、宣言のところで「as AnyはDeclareステートメントではサポートしていない」とのメッセージがでてしまいます。これは.NETになってからでしょうか?

******** Marshal.Copyはこんな感じでテストしました *********

    Private Sub Button2_Click(ByVal sender As System.Object, 
            ByVal e As System.EventArgs) Handles Button2.Click

        Dim B() As Byte = {37, 82, 154, 68, 22, 66, 33, 44}
        Dim I(9) As Integer
        Dim P As System.IntPtr
        Dim test As TEST1_T

        P = System.Runtime.InteropServices.Marshal.AllocHGlobal(8)
        System.Runtime.InteropServices.Marshal.Copy(B, 0, P, 8)
        System.Runtime.InteropServices.Marshal.Copy(P, test, 0, 2)
        System.Runtime.InteropServices.Marshal.FreeHGlobal(P)

    End Sub

*****

    Structure TEST1_T
        Dim test1_1 As Short
        Dim test1_2 As Short
        Dim test1_3 As Short
        Dim test1_4 As Short
    End Structure


魔界の仮面弁士  2005-03-31 06:47:22  No: 89164

> Dim B() As Byte = {37, 82, 154, 68, 22, 66, 33, 44}
>    Structure TEST1_T
>        Dim test1_1 As Short
>        Dim test1_2 As Short
>        Dim test1_3 As Short
>        Dim test1_4 As Short
>    End Structure

こんな感じ。

Dim B() As Byte = {37, 82, 154, 68, 22, 66, 33, 44}
Dim test As TEST1_T
Dim P As System.IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TEST1_T)))
Marshal.Copy(B, 0, P, B.Length)
test = DirectCast(Marshal.PtrToStructure(P, GetType(TEST1_T)), TEST1_T)
System.Runtime.InteropServices.Marshal.FreeHGlobal(P)

Trace.WriteLine(String.Format("{0}(0x{0:x}),{1}(0x{1:x}),{2}(0x{2:x}),{3}(0x{3:x})", _
    test.test1_1, test.test1_2, test.test1_3, test.test1_4))


特攻隊長まるるう  2005-03-31 18:51:15  No: 89165

>as AnyはDeclareステートメントではサポートしていない
はい、[VB.NET]はサポートしてません。使用する構造体を引数
として宣言すれば使えると思います。他にも多少考慮すべき点が…
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200311/03110097.txt

…型が変わるとその分だけ宣言を増やす事になって非常に面倒
ですが…解決策は見つけてません。


M  2005-03-31 19:15:05  No: 89166

魔界の仮面弁士さん、ご回答サンプルありがとうございました。
いつも、初めて見るメソッドや使用法のはっきりわからなかったメソッドに感心感服しております。
さて、魔界の仮面弁士さんのサンプルを拝見し、自分なりに納得して以下の通りに改造してみたのですが、なかなかうまくいきません。

*********** ソース ************
Dim B() As Byte = {&H25, &H52, &H9A, &H44, &H16, &H42, &H21, &H2C}
Dim test As TEST1_T
Dim P As System.IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TEST1_T)))
Marshal.Copy(B, 0, P, B.Length)
test = DirectCast(Marshal.PtrToStructure(P, GetType(TEST1_T)), TEST1_T)
System.Runtime.InteropServices.Marshal.FreeHGlobal(P)

Trace.WriteLine(String.Format("{0}(0x{0:x}),{1}(0x{1:x}),{2}(0x{2:x}),{3}(0x{3:x})", _
    test.test1_1, test.test1_2, test.test1_3, test.test1_4))
----------------------------------------
    Structure TEST1_T
        Dim test1_1 As Short
        Dim test1_2 As Integer
        Dim test1_3 As Byte
        Dim test1_4 As Byte
    End Structure

改造といっても、構造体のフォーマットを、いままで全てshortだったのを
ShortやIntegerを織り交ぜただけですが…

そして、これで得られる値が…
test1_1=[0x5225] test1_2=[0x4216449a] test1_3=[0x21] test1_4=[0x2c]
と思っていたのですが、実際は…
test1_1=[0x5225] test1_2=[0x2c214216] test1_3=[0xb4] test1_4=[0x6d]
となってしまいました。
なぜかtest1_1とtest1_2の間に2Byteの空きが出てしまいます。(test1_3,test1_4は領域外を見ているのでしょうか?)

いろいろと調べいているつもりですが、原因・対策・打開策がみつかりません。
原因など、ご存知でしょうか?


M  2005-03-31 19:26:07  No: 89167

特攻隊長まるるうさん、ご回答ありがとうございます。

そうですか…やはり.NETではサポートはしていないのですね…
しかしながら、過去ログのご提示ありがとうございました。
CopyMemoryをキーに検索をかけてみたのですが、見つけきれませんでした。
まだまだ自分の力不足を感じます。

ご提示いただいた過去ログを元に、CopyMemoryからもがんばってみます。
ありがとうございました。


外してるかな?  2005-03-31 19:35:42  No: 89168

> なぜかtest1_1とtest1_2の間に2Byteの空きが出てしまいます。(test1_3,test1_4は領域外を見ているのでしょうか?)
パッキングサイズの問題のような気がしますね。


魔界の仮面弁士  2005-03-31 19:52:48  No: 89169

> Dim B() As Byte = {&H25, &H52, &H9A, &H44, &H16, &H42, &H21, &H2C}
実験段階では、
  {&H11, &H22, &H33, &H44, &H55, ……… }
のようなデータにしておいた方が、どのデータがどこに入るのか、
確認しやすいかも。

> なぜかtest1_1とtest1_2の間に2Byteの空きが出てしまいます。(test1_3,test1_4は領域外を見ているのでしょうか?)

アライメントの問題でしょうね。構造体の定義を、

  <StructLayout(LayoutKind.Sequential, Pack:=1)> Structure TEST1_T
      Dim test1_1 As Short
      Dim test1_2 As Integer
      Dim test1_3 As Byte
      Dim test1_4 As Byte
  End Structure

に変更して、もう一度試してみてください。

また、Pack:=1 の部分を、Pack:=2、Pack:=4、Pack:=8 にした場合に、
それぞれどうなるかも確認してみてください。


M  2005-03-31 20:27:15  No: 89170

外してるかな?さん(?)ご回答ありがとうございます。

パッキングサイズですか…
またまた自分の無知を嘆くことに…(汗)
つまりパッキングのサイズを設定することと思うので(言葉そのまま…)
たしかにあやしいですね。

調べてみます。
ありがとうございました。


M  2005-03-31 20:42:28  No: 89171

魔界の仮面弁士さん、早速のご回答ありがとうございます。

>実験段階では、
>  {&H11, &H22, &H33, &H44, &H55, ……… }
ご助言(&h11,&h22…)ありがとうございます。
確かにです。自分の設定で自分の確認作業を大きくしてました(汗)
先日ご提示いただいたサンプルに16進表示を入れてくれていたので、
その負荷もかなり軽くはなっていたのですが、さらに楽になりますね。

>アライメントの問題でしょうね。
アライメントですか…(汗)
アライメントというものをよく知らなかったので、いろいろ調べて納得。
動作させてさらに納得です。
イメージ通りの結果が得られました。
Pack:=1,2,4,8に関しても確認してみて、パックする(?)サイズで結果が
異なってくる事も確認できました。
(外してるかな?さんの話につながってくるかな?)

参考にさせてもらって、本来の構造体・Byte配列に組み込んでみたいと
思います。
ありがとうございました。


M  2005-03-31 23:47:46  No: 89172

もう1点質問させてください。

バイト配列を構造体にコピーする方法は魔界の仮面弁士さんに教えて
いただいたとおり、Marshal.CopyとDirectCastを使用して行う事が
できることはわかったのですが、
逆に、構造体からバイト配列にコピーする方法がよくわかりません。
おそらくは同様の方法だとは思うのですが、DirectCastのパラメータ
など、どのように設定すべきなのでしょうか?
よろしくお願いいたします。


魔界の仮面弁士  2005-04-01 00:39:25  No: 89173

> DirectCastのパラメータなど、どのように設定すべきなのでしょうか?

あれれ。DirectCastって、単なる「型を変換するだけ」の物ですよ。(^_^;)
第1引数に変換元データ、第2引数に変換させたい型を指定すればOKです。

# このあたりで詰まっているようであれば、
# IntPtr操作には手を出さない方が無難かも……。

先に提示したコードの場合、PtrToStructureメソッドの戻り値が
(TEST1_T型ではなく)Object型でしたから、型変換が必要だったわけですね。

ところで、「Option Strict On」を使っている場合には、
   Dim O As Object = 123
   Dim I As Integer
という変数定義に対して、
   I = O
と書く事はできない、という事は理解されていますでしょうか?

こうした場合には、「明示的な型変換」として、
   I = CInt(O)
   I = CType(O, Integer)
   I = DirectCast(O, Integer)
などの操作が必要になるわけです。
(それぞれの関数の意味は、ヘルプで調べてみてください)
http://www5b.biglobe.ne.jp/~yone-ken/VBNET/special/sp03_ConvertType.html
http://www.ailight.jp/blog/jeanne/archive/2005/03/18/5072.aspx


M  2005-04-01 01:30:48  No: 89174

魔界の仮面弁士さん、ご回答ありがとうございます。

># このあたりで詰まっているようであれば、
># IntPtr操作には手を出さない方が無難かも……。
なかなか痛いところです(汗)

>先に提示したコードの場合...型変換が必要だったわけですね。
>ところで、「Option Strict On」を使っている場合には、...という事は理解されていますでしょうか?
理解はしていたつもりでしたが、object型をなかなか使う機会が
なかなかなかったせいか、なかなか頭の中で整理がつかず混乱して
しまい、的外れとなってしまいました。まだまだです…

そして、質問の続きになるのですが…
諦め悪く、IntPtr操作を使って以下の様に組んでみました。
これでいいのでは?と思っておりますが、どうでしょう(汗)

*********** 構造体->バイナリ->構造体 ***********

        Dim B(19) As Byte
        Dim P As System.IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TEST1_T)))
        Dim test As TEST1_T
        Dim test2 As TEST1_T

        test.test1_1 = 10
        test.test1_2 = 20
        test.test1_3 = 30
        test.test1_4 = 40
        test.test1_5 = 50

        Marshal.StructureToPtr(test, P, True)
        Marshal.Copy(P, B, 0, 20)
        test2 = DirectCast(Marshal.PtrToStructure(P, GetType(TEST1_T)), TEST1_T)
        Marshal.FreeHGlobal(P)

        Trace.WriteLine(String.Format("test :{0}(0x{0:x}),{1}(0x{1:x}),{2}(0x{2:x}),{3}(0x{3:x}),{4}(0x{4:x})", _
            test.test1_1, test.test1_2, test.test1_3, test.test1_4, test.test1_5))
        Trace.WriteLine(String.Format("test2:{0}(0x{0:x}),{1}(0x{1:x}),{2}(0x{2:x}),{3}(0x{3:x}),{4}(0x{4:x})", _
            test2.test1_1, test2.test1_2, test2.test1_3, test2.test1_4, test2.test1_5))

***

    <System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, Pack:=1)> Structure TEST1_T
        Dim test1_1 As Integer
        Dim test1_2 As Short
        Dim test1_3 As Long
        Dim test1_4 As Short
        Dim test1_5 As Integer
    End Structure


魔界の仮面弁士  2005-04-01 02:14:50  No: 89175

> 諦め悪く、IntPtr操作を使って以下の様に組んでみました。
> これでいいのでは?と思っておりますが、どうでしょう(汗)

そもそもやりたい事って、構造体とバイナリの相互変換だったのですよね。

提示されたサンプルって、構造体→バイナリ へのコピーはありますが、
バイナリ→構造体へのコピーが無いようですが、それで良いのでしょうか?

実際、『Marshal.Copy(P, B, 0, 20)』があっても無くても、同じ結果になりますし、

で、Byte配列を経由する必要が無いなら、そもそも今回のコードは、
   test.test1_1 = 10
   test.test1_2 = 20
   test.test1_3 = 30
   test.test1_4 = 40
   test.test1_5 = 50

   test2 = test
だけで、話が済んでしまいますよ。(^^;

そもそも、何故今回のようなアンマネージドな操作を必要とされているのでしょうか?

速度上の問題やP/Invokeの利用など、IntPtr を使った方がスマートな
ケースというのは、さほど珍しくありませんが、使用する目的が、
単にファイル保存したいというだけであれば、Marshal.Copy操作に
頼るべきでは無い様な気もするのですけれども……。


M  2005-04-01 03:15:14  No: 89176

ご回答ありがとうございます。

>バイナリ→構造体へのコピーが無いようですが、それで良いのでしょうか?
すみません。先ほどソースを書き込んで「送信」をクリックした後に、
バイナリ->構造体の記述がないのに気が付き、
    Marshal.Copy(B, 0, P, 20)
を追加したのですが、くどくなりそうなので送信はやめておきました。
言葉足らずになってしまい、申し訳ありませんでした。

>そもそも、何故今回のようなアンマネージドな操作を必要とされているのでしょうか?
ご提示したソースでは完全に省いていたのですが、バイナリ変換後にこの
バイナリデータをデータベース(ORACLE)に保存する必要がありまして、
その保存形式もバイナリの羅列で…と指定されていましたので、
構造体->バイナリの変換。逆に読み出しの場合にバイナリ->構造体…が
必要になってしまったわけです。
なぜにORACLEに保存するのにバイナリ形式に?と思われるかもしれませんが、
それは私も手を出せないところなので、ご一笑のうえ納得されてください(汗)

魔界の仮面弁士さん・特攻隊長まるるうさん・外してるかな?さん
御三方にはお世話になりました。
また、私の不勉強さにご迷惑をおかけしましたこと、お詫びいたします。
今後もお世話になる事もあるかとは思いますが、よろしくお願いいたします。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加