- PR -

C# Marshal.AllocHGlobal領域を構造体経由で読み書きしたい

1
投稿者投稿内容
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-01-17 23:43
Marshal.AllocHGlobal領域を構造体経由で読み書きする方法を知りたい。

 マネージドコードからMarshal.AllocHGlobal領域を高速で読み書きしたい。
できればMarshal.AllocHGlobal領域を直接読み書きしたい。
現在は必要なデータをMarshal.Copyでbyte[]に毎回コピーしていますが、
容量が大きくなるとオーバーヘッドが大きくなるので最適なロジックではありません。
適切な方法を教えてください。
 例えば構造体のアドレスをMarshal.AllocHGlobal領域に重ね合わせる等できるでしょうか?あるいは、もっとエレガントな方法があれば知りたい。



例えば構造体test1の先頭アドレスをmemPtrに移動できれば良いのでしょうか?

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;

namespace WindowsApplication2
{
/// <summary>
/// Form1 の概要の説明です。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
/// <summary>
/// 必要なデザイナ変数です。
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Windows フォーム デザイナ サポートに必要です。
//
InitializeComponent();

//
// TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。
//
}

/// <summary>
/// 使用されているリソースに後処理を実行します。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows フォーム デザイナで生成されたコード
/// <summary>
/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
/// コード エディタで変更しないでください。
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(40, 16);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(56, 48);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void button1_Click(object sender, System.EventArgs e)
{
// 配列要素数
int pSize = 1000;

Test test1 = new Test(1000);


// アンマネージなメモリ領域を確保する
int memSize;
unsafe
{
memSize = sizeof(double)+sizeof(int)*pSize+sizeof(bool);
}
IntPtr memPtr = Marshal.AllocHGlobal(memSize);
// Test構造体経由で案マネージなメモリ領域を高速に読み書きしたい
// 例えば構造体test1をアンマネージなメモリ領域に重ねあわせることはできるか
// …
}

// テンプレーの構造体
[StructLayout(LayoutKind.Sequential)]
public struct Test
{
public Test(int pSize)
{

a = 12.34;
b = new int[pSize];
for(int i = 0;i < pSize;i++)
{
b[i] = i*10;
}
c = true;
}
public double a;
public int[] b;
public bool c;
}
}
}
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-01-18 03:08
Marshal.Write***メソッドを使うのが一番早いと思います。

構造体のコピーはMarshal.StructureToPtrメソッドで可能です。
しかし配列を含む構造体の場合、通常は配列へのポインタだけを持つことになります。
メンバにMarshalAs属性をUnmanagedType.ByValArrayで指定する事で配列各要素をフラットに持つ事が可能ですが、
同時にSizeConstも指定しなくてはいけない=固定長配列限定になります。
なにより構造体を一々マーシャリングするためか、思ったほどの効率にはなりません。

そこで、構造体の各メンバをMarshal.Write***メソッドで書き込んでいく手法です。
これなら、そもそも構造体である必要のないデータなら構造体にする事もなく書き込む事もできます。
ひろしさんが提示された構造体を書き込む場合は次のようなコードになります。

コード:

int size = 1000;
Test test = new Test(size);
int sizeOfA = Marshal.SizeOf(typeof(double));
int sizeOfB = Marshal.SizeOf(typeof(int)) * size;
int memorySize = sizeOfA + sizeOfB + Marshal.SizeOf(typeof(bool));
IntPtr ptr = Marshal.AllocHGlobal(memorySize);

int pointer = (int)ptr + sizeOfB;  //.Aを書き込んだ次のアドレスを指す事になる
Marshal.WriteInt64(ptr, BitConverter.DoubleToInt64Bits(test.A));
for (int i = 0; i < size; i++) {
	//アドレスをintのサイズ分ずつ進めながら書き込んでいく
	Marshal.WriteInt32((IntPtr)(pointer + i * sizeOfB), test.B[i]);
}
Marshal.WriteInt32((IntPtr)(pointer + size * 4), test.C ? 1 : 0);  //boolは4バイト

//そのまま何もせず解放
Marshal.FreeHGlobal(ptr);


[StructLayout(LayoutKind.Sequential)]
public struct Test {
	public Test(int size) {
		A = 12.34;
		B = new int[size];
		for (int i = 0; i < size; i++) {
			B[i] = i * 10;
		}
		C = true;
	}
	public double A;
	public int[] B;
	public bool C;
}




しかしながら、それほど速度が絶対的に必要ならば、
その部分をC++などで書く方がいいのではないでしょうか。
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-01-19 12:53
ご回答ありがとうございます。
ご指摘の回答で問題が解決しました。

ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-01-19 12:53
ご回答ありがとうございます。
ご指摘の回答で問題が解決しました。

1

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