- 構造体のサイズを確認し、ボックス化しないでください。
文字列に適切なマーシャリングを設定します(goricによる回答から取得)。それにもかかわらず、得られるのはバイト配列内の文字列のメモリアドレスです(良いことではありません)。
[Serializable]
public struct newLeads
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5000)]
public string id;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5000)]
public string first_name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5000)]
public string last_name;
}
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public static byte[] ToByteArray(newLeads value)
{
int length = Marshal.SizeOf(typeof(newLeads));
var result = new byte[length];
IntPtr sourcePtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(value, sourcePtr, false);
Marshal.Copy(sourcePtr, result, 0, length);
Marshal.FreeHGlobal(sourcePtr);
return result;
}
あなたのコメントでは、このコードはすぐに失敗すると言いました。それは、メソッドが呼び出されるたびに特定の権限をチェックする際に、(.NET 4 で推奨されているように) Security Permission Demand を使用します。それなしでそれを実行しようとするかもしれません。期待される結果は、最初に得たものです。
本当の答え
おそらくある種のサンドボックスまたはポインターをサポートしていないプラットフォームなど、制約のある環境で実行している必要があります。その場合、別の方法で変換を行う必要がある場合があります。
あなたはASP.NETと言いましたか?それだけです。
ポインターを使用せずに変換するには、次の手法を試してください。
//You will have to decide an encoding.
//If nto ASCII, try UTF8Encoding, UnicodeEncoding or (Hopefully not) UTF7Encoding
void Main()
{
Encoding encoding = new ASCIIEncoding();
newLeads target = GetNewLeads();
byte[] id = EncodeString(target.id, encoding);
byte[] first_name = EncodeString(target.first_name, encoding);
byte[] last_name = EncodeString(target.last_name, encoding);
}
byte[] EncodeString(string str, Encoding encoding)
{
byte[] data;
if (ReferenceEquals(str, null))
{
data = new byte[0];
}
else
{
data = encoding.GetBytes(str);
}
return data;
}
この時点で、より良い解決策を提供するために、あなたの状況についてもう少し知る必要があります。特に、誰が、または何がそのバイト配列を読み取るのでしょうか?. とにかく、次のように文字列の長さをエンコードできます (null の場合は -1 を予約します):
byte[] EncodeString(string str, Encoding encoding)
{
byte[] data;
byte[] data_length;
Union union = new Union();
if (ReferenceEquals(str, null))
{
data = new byte[0];
union.data = -1;
}
else
{
data = encoding.GetBytes(str);
union.data = str.Length;
}
data_length = new byte[]{union.a, union.b, union.c, union.c};
int length = data.Length;
byte[] result = new byte[4 + data.Length];
System.Buffer.BlockCopy(data_length, 0, result, 0, 4);
System.Buffer.BlockCopy(data, 0, result, 4, length);
return result;
}
//I hope endianess doesn't bite
[StructLayout(LayoutKind.Explicit)]
struct Union
{
[FieldOffset(0)]
public int data;
[FieldOffset(0)]
public byte a;
[FieldOffset(1)]
public byte b;
[FieldOffset(2)]
public byte c;
[FieldOffset(3)]
public byte d;
}
最後に、これらの配列を結合する必要があります。私はこれがもっと最適化できることを知っています... (MemoryStream と StreamWriter を使用するための良い考えです) とにかく、これは私の最初の実装です [テスト済み]:
byte[] ToByteArray(newLeads value)
{
Encoding encoding = new ASCIIEncoding(); //Choose some encoding
byte[] id = EncodeString(value.id, encoding);
byte[] first_name = EncodeString(value.first_name, encoding);
byte[] last_name = EncodeString(value.last_name, encoding);
byte[] result = new byte[id.Length + first_name.Length + last_name.Length];
System.Buffer.BlockCopy(id, 0, result, 0, id.Length);
System.Buffer.BlockCopy
(
first_name,
0,
result,
id.Length,
first_name.Length
);
System.Buffer.BlockCopy
(
last_name,
0,
result,
id.Length + first_name.Length,
last_name.Length
);
return result;
}
byte[] EncodeString(string str, Encoding encoding)
{
byte[] data;
byte[] data_length;
Union union = new Union();
if (ReferenceEquals(str, null))
{
data = new byte[0];
union.data = -1;
}
else
{
data = encoding.GetBytes(str);
union.data = str.Length;
}
data_length = new byte[]{union.a, union.b, union.c, union.c};
int length = data.Length;
byte[] result = new byte[4 + data.Length];
System.Buffer.BlockCopy(data_length, 0, result, 0, 4);
System.Buffer.BlockCopy(data, 0, result, 4, length);
return result;
}
//I hope endianess doesn't bite
[StructLayout(LayoutKind.Explicit)]
struct Union
{
[FieldOffset(0)]
public int data;
[FieldOffset(0)]
public byte a;
[FieldOffset(1)]
public byte b;
[FieldOffset(2)]
public byte c;
[FieldOffset(3)]
public byte d;
}
注: 最終的にどのエンコーディングを使用するかわからないため、null で終了する文字列は使用しませんでした。
最適化
同じロジックですが、ストリームで実装されています (Union は変更されていません)[テスト済み]。
byte[] ToByteArray(newLeads value)
{
Encoding encoding = new ASCIIEncoding(); //Choose some encoding
var stream = new MemoryStream();
EncodeString(value.id, stream, encoding);
EncodeString(value.first_name, stream, encoding);
EncodeString(value.last_name, stream, encoding);
int length = (int)stream.Length;
byte[] result = new byte[(int)stream.Length];
System.Buffer.BlockCopy(stream.GetBuffer(), 0, result, 0, length);
stream.Close();
return result;
}
void EncodeString(string str, Stream stream, Encoding encoding)
{
Union union = new Union();
if (ReferenceEquals(str, null))
{
union.data = -1;
stream.Write(new byte[]{union.a, union.b, union.c, union.c}, 0, 4);
}
else
{
union.data = str.Length;
stream.Write(new byte[]{union.a, union.b, union.c, union.c}, 0, 4);
var tmp = encoding.GetBytes(str);
stream.Write(tmp, 0, tmp.Length);
}
}
弦を張り直す
データを取得するには、文字列の長さを読み取ることから始めます (同じタイプの Union を使用)。
var newLeads = GetNewLeads();
var z= ToByteArray(newLeads); //we have the byteArray in z
var data = new MemoryStream(z); //Create an stream for convenience
//Use the union to get the length
var union = new Union()
{
a = (byte)data.ReadByte(),
b = (byte)data.ReadByte(),
c = (byte)data.ReadByte(),
d = (byte)data.ReadByte()
};
Console.WriteLine(union.data); //the length of the first string
次のステップは、多くの文字を読み取ることです。そのために、StreamReader を使用します。
var newLeads = GetNewLeads();
var z = ToByteArray(newLeads);
var data = new MemoryStream(z);
var union = new Union()
{
a = (byte)data.ReadByte(),
b = (byte)data.ReadByte(),
c = (byte)data.ReadByte(),
d = (byte)data.ReadByte()
};
var encoding = new ASCIIEncoding();
string result = null;
if (union.data != -1)
{
char[] finalChars = new char[union.data];
var reader = new StreamReader(data, encoding);
reader.Read(finalChars, 0, union.data);
result = new string(finalChars);
}
Console.WriteLine(result);
これにより、文字列をデコードするメソッドを構築します。
string DecodeString(Stream data, Encoding encoding)
{
//TODO: You may want to validate that data and encoding are not null
//or make this private
var union = new Union()
{
a = (byte)data.ReadByte(),
b = (byte)data.ReadByte(),
c = (byte)data.ReadByte(),
d = (byte)data.ReadByte()
};
string result = null;
if (union.data != -1)
{
char[] finalChars = new char[union.data];
var reader = new StreamReader(data, encoding);
reader.Read(finalChars, 0, union.data);
result = new string(finalChars);
}
return result;
}
//Convenience method, not needed:
string DecodeString(byte[] data, Encoding encoding)
{
//TODO: You may want to validate that data and encoding are not null
//or make this private
return DecodeString(new MemoryStream(data), encoding);
}
そして最後に newLeads を回復する方法 (再び同じ Union タイプ)[テスト済み]:
newLeads FromByteArray(byte[] data)
{
//TODO: Validate that data is not null
Encoding encoding = new ASCIIEncoding(); //Choose the same encoding
newLeads result = new newLeads();
var reader = new StreamReader(new MemoryStream(data), encoding);
result.id = DecodeString(reader);
result.first_name = DecodeString(reader);
result.last_name = DecodeString(reader);
reader.Close();
return result;
}
//Changed to reuse StreamReader...
//Because closing it will close the underlying stream
string DecodeString(StreamReader reader)
{
//TODO: You may want to validate that reader is not null
//or make this private
var data = reader.BaseStream;
var union = new Union()
{
a = (byte)data.ReadByte(),
b = (byte)data.ReadByte(),
c = (byte)data.ReadByte(),
d = (byte)data.ReadByte()
};
string result = null;
if (union.data != -1)
{
char[] finalChars = new char[union.data];
reader.Read(finalChars, 0, union.data);
result = new string(finalChars);
}
return result;
}
//Convenience method, not needed:
string DecodeString(byte[] data, Encoding encoding)
{
//TODO: You may want to validate that data and encoding are not null
//or make this private
return DecodeString(new StreamReader(new MemoryStream(data), encoding));
}