- PR -

構造体をbyte[]にコピーしたい

投稿者投稿内容
やむ
会議室デビュー日: 2003/08/02
投稿数: 17
投稿日時: 2003-10-20 15:16
構造体をbyte[]にコピーしたいのですが、C#でのやり方がわかりません。
過去ログも検索してみたのですが見つかりませんでした。参考になるサイトや方法があれば教えてもらえますか? MSDNも一通り見たのですが、見つけられませんでした。

具体的には、構造体として生成したデータをTCP/IPで送信したいのです。
それと、構造体のサイズを取得するunsafeでない方法もあれば教えてください。

// 構造体宣言
struct SND{
protected short no;
protected int len;
protected byte sw;
protected byte dir;
};

TcpClient tcp = new TcpClient( host, port );
NetworkStream strm = tcp.GetStream();
BinaryWriter bw = new BinaryWriter(strm);

SND snd = new SND(); // 構造体生成
snd.no = 123;
snd.len = 456;
snd.sw = '1';
snd.dir = '4';

byte[] buf = new byte[64]; // コピー先生成
int size;
int offset = 0;

@@@ ここでbufにコピーしたい (可能ならキャストでも)
@@@ 構造体のサイズも取得したい

bw.Write( buf, offset, size ); // 書き込み
bw.Flush();
bw.Close();
tcp.Close();

Cでの↓のようなことがしたいのです。
memcpy( buf, (char *)&snd, sizeof(snd) );




かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-10-20 16:41
C#は触った事ないので分かりませんが、この手の問題は、永続化(persistence)や直列化(serialize)をキーワードに検索すると何かヒントぐらいは見つかりそうな気がします。

的外れだったらごめんね。
やむ
会議室デビュー日: 2003/08/02
投稿数: 17
投稿日時: 2003-10-20 17:43
ご回答ありがとうございます。

直列化はやってみたのですが、データにゴミが入ってしまい使えませんでした。通信相手が逆シリアル化してくれたらそれでいいんですが、電文フォーマットが決まっているので、こちらのオブジェクトとしての情報は無意味というか、ゴミ扱いです。

別の方法として、
ArrayList al = new ArrayList();
al.Add( BitConverter.GetBytes( snd.no ) );
al.Add( BitConverter.GetBytes( snd.len ) );
al.Add( BitConverter.GetBytes( snd.sw ) );
al.Add( BitConverter.GetBytes( snd.dir ) );

↑のようにbyte[]をArrayListに入れたんですが、
そのままではbyte[]に渡せないようです。
どのように書けばいいでしょうか?

// 試したコード
al.CopyTo( buf ); // ↓の例外発生
//ソース配列にある、少なくとも 1 つの要素をターゲット配列の型にキャストできませんでした。

buf = al.ToArray( buf.GetType() ); // コンパイルエラー
// 型 'object[]' を型 'byte[]' に暗黙的に変換できません。

buf = (byte[])al.ToArray( buf.GetType() ); // コンパイルエラー
// 型 'object[]' を型 'byte[]' に変換できません。
架空兎
ベテラン
会議室デビュー日: 2003/08/18
投稿数: 78
お住まい・勤務地: さいたま氏
投稿日時: 2003-10-20 19:05
これでどうでしょう?

using System.Runtime.InteropServices;

[DllImport("Kernel32.dll")]
extern public static uint CopyMemory(byte[] Destination, ref SND Source, uint Length);

CopyMemory(buf, ref snd, (uint)Marshal.SizeOf(typeof(SND)));
ishisaka
常連さん
会議室デビュー日: 2001/10/10
投稿数: 23
投稿日時: 2003-10-20 23:03
結局のところ、自分でやるしかないかーって感じですかね。
コード:
namespace ConsoleApplication1
{
	
	
	/// <summary>
	/// 構造体もしくはクラスのシリアライズ。
	/// 結局自分でやるしかないのよねー。
	/// </summary>
	class Class1
	{
		/// <summary>
		/// アプリケーションのメイン エントリ ポイントです。
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			
			sendData sd = new sendData();
			sd.number1 = 100;
			sd.number2 = 200;
			sd.number3 = 300;
			byte[] bytes = sd.SendByteArray();
			int i;
			for (i = 0; i < bytes.Length ; i++) {
				Console.WriteLine("byte {0} is {1}", i, bytes[i]);
			}
			Console.ReadLine();
		}
	}
	struct sendData {
		public int number1;
		public int number2;
		public int number3;

		/// <summary>
		/// 実際にはこの中でエンディアン変換とかも必要なはず。
		/// </summary>
		/// <returns></returns>
		public byte[]  SendByteArray() {
			int size;
			byte[] temp1 = BitConverter.GetBytes(number1);
			byte[] temp2 = BitConverter.GetBytes(number2);
			byte[] temp3 = BitConverter.GetBytes(number3);
			size = temp1.Length + temp2.Length + temp3.Length;
			byte[] sendBytes = new byte[size +1];
			temp1.CopyTo(sendBytes, 0);
			temp2.CopyTo(sendBytes, temp1.Length - 1);
			temp3.CopyTo(sendBytes, temp1.Length + temp2.Length - 1);
			return sendBytes;
		}
	}
}

ishisaka
常連さん
会議室デビュー日: 2001/10/10
投稿数: 23
投稿日時: 2003-10-20 23:08
ミスりました。
47行目は正しくは
byte[] sendBytes = new byte[size];
です。
お詫びして訂正いたします。
_________________
いしさかただひろ(*^^)v
やむ
会議室デビュー日: 2003/08/02
投稿数: 17
投稿日時: 2003-10-21 10:52
架空兎さん、ありがとうございます。
上手くいきました。
sizeof()はunsafeじゃないとダメだったんで、どうしようかと思いましたが、Marshal.SizeOf()はOKだったんですね。

CopyMemory()はstaticなので、使う場所を考えてボトルネックにならないように気をつけて使わせてもらいます。

やむ
会議室デビュー日: 2003/08/02
投稿数: 17
投稿日時: 2003-10-21 11:20
ishisakaさん、ありがとうございます。
これでも上手くいきました。

struct sendDataの定義を一部変えて、

int size;
int offset = 0;
byte[] temp1 = BitConverter.GetBytes(number1);
byte[] temp2 = BitConverter.GetBytes(number2);
byte[] temp3 = BitConverter.GetBytes(number3);
size = Marshal.SizeOf(typeof(sendData));
byte[] sendBytes = new byte[size];
temp1.CopyTo(sendBytes, offset);
offset += temp1.Length;
temp2.CopyTo(sendBytes, offset);
offset += temp2.Length;
temp3.CopyTo(sendBytes, offset);

こんな感じで使わせてもらってます。
なんせ、メンバーの数が多い(10〜15くらい)のでoffsetに位置情報を加算しているのと、架空兎さんに教えてもらったMarshal.SizeOf()を使いました。

それにしても、SizeOf()もsizeof()も、データ部分のサイズだけを返してくれるというのを知らなくて驚きでした。structの中にコードを書いたら、コード部分もサイズに加算されるのかと思ってました。

おまけで、BitConverter.GetBytes()ですが、引数をbyte型データにすると、byte[]に2バイト確保されます。
byte dat;
byte[] temp = BitConverter.GetBytes( dat );
の場合、temp.Lengthは2です。temp[0]にdatが入り、temp[1]は0です。
なので、byteデータをtempに入れる場合は、
byte[] temp = { dat };
と、書く必要があります。 めんどくさ〜い。
マニュアルにbyteを引数にしたローバーロードは無かったのでしょうがないのですが・・・。

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