尝试将结构转换为 byte[] 时的方法访问期望
本文关键字:方法 期望 访问 byte 结构 转换 | 更新日期: 2023-09-27 18:35:52
我需要将以下结构转换为字节数组:
[Serializable]
public struct newLeads
{
public string id;
public string first_name;
public string last_name;
}
我正在尝试使用以下代码转换为字节数组:
public class ConvertStruct
{
public static byte[] StructureToByteArray(object obj)
{
int Length = Marshal.SizeOf(obj);
byte[] bytearray = new byte[Length];
IntPtr ptr = Marshal.AllocHGlobal(Length);
Marshal.StructureToPtr(obj, ptr, false);
Marshal.Copy(ptr, bytearray, 0, Length);
Marshal.FreeHGlobal(ptr);
return bytearray;
}
}
我在网上收到异常:
IntPtr ptr = Marshal.AllocHGlobal(Length);
异常:安全透明方法"Classes.ConvertStruct.ConvertStruct.StructureToByteArray(System.Object)"访问安全关键方法"System.Runtime.InteropServices.Marshal.AllocHGlobal(Int32)"失败。
我的问题是?如何解决此问题以避免异常并将我的简单结构转换为byte[]
?
提前感谢!
更新:我在控制台应用程序中尝试过这个,它可以工作。我从一个 asp.net 页面代码隐藏中调用它,所以这肯定与它有关,但我无法弄清楚是什么!
- 检查结构的大小,不要将其框起来。
-
为字符串设置一个适当的封送处理(从 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 建议的那样),每次调用该方法时,它都会检查特定的性能。您可以尝试在没有它的情况下执行它,预期的结果就是您一开始得到的结果。
真正的答案
您必须在受约束的环境中运行,可能是某种不支持指针的沙盒或平台。在这种情况下,我们可能需要通过其他方式进行转换。
你说 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;
}
注意:我没有使用以空结尾的字符串,因为我不知道您最终会使用哪种编码。
优化
相同的逻辑,但用流实现(联合没有改变)[测试]。
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 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));
}
在结构中,使用正确的MarshalAs
属性标记字符串(MSDN 参考):
[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;
}
确保SizeConst
值足够大,以覆盖要使用的数据的最大长度。
看起来您可以将方法标记为SecurityCritical
http://msdn.microsoft.com/en-us/library/bb264475.aspx