我有一个通用方法,可以使用and将任何struct
类型的数组序列化为 s 数组。完整的代码是:byte
Marshal.StructureToPtr
Marshal.Copy
internal static byte[] SerializeArray<T>(T[] array) where T : struct
{
if (array == null)
return null;
if (array.Length == 0)
return null;
int position = 0;
int structSize = Marshal.SizeOf(typeof(T));
byte[] rawData = new byte[structSize * array.Length];
IntPtr buffer = Marshal.AllocHGlobal(structSize);
foreach (T item in array)
{
Marshal.StructureToPtr(item, buffer, false);
Marshal.Copy(buffer, rawData, position, structSize );
position += structSize;
}
Marshal.FreeHGlobal(buffer);
return rawData;
}
它在 99.99% 的时间里都能完美运行。但是,对于我的一位 Windows 7 用户,对于某些输入数据,此代码可预见地会导致以下非 .NET 异常:
传递给系统调用的数据区域太小。(来自 HRESULT 的异常:0x8007007A)。
不幸的是,我无权访问用户的机器来附加调试器,即使处理与我的用户完全相同的输入数据,我也无法复制该问题。这只发生在一个用户的机器上并且只发生在某些输入数据上,但在她的机器上每次都会发生相同的输入数据,所以它绝对不是随机的。
该应用程序面向 .NET 4.5。
任何人都可以看到这段代码有什么问题吗?我唯一的猜测是报告的内容与数据结构的实际大小之间存在一些不匹配Marshal.SizeOf
,从而导致为结构分配的内存不足。
如果重要的话,这里是发生错误时被序列化的结构(它是由 OCR 产生的字符位置的表示):
public struct CharBox
{
internal char Character;
internal float Left;
internal float Top;
internal float Right;
internal float Bottom;
}
正如您所看到的,所有字段的大小都应该始终保持不变,所以我最初分配一个固定长度的非托管内存段来序列化每个字段struct
应该不是问题(应该吗?)。
虽然我欢迎替代或改进的序列化方法,但我更感兴趣的是确定这个特定的错误。谢谢!
更新
感谢 TnTnMn 向我指出这char
不是 blittable 类型,我在输入中查找 unicode 字符以查看它们是否正确编组。事实证明,他们不是。
对于CharBox { 0x2022, .15782328, .266239136, .164901689, .271627158 },
序列化(十六进制)应该是:
22 20 00 00(字符*)
6D 9C 21 3E(左)
7F 50 88 3E(上)
FD DB 28 3E(右)
B7 12 8B 3E (底部)
(* 由于我没有使用显式布局,它填充到四个字节;我现在对自己感到沮丧,因为我不必要地将数据大小增加了 11%...)
相反,它被序列化为:
95 00 00 00(字符)
6D 9C 21 3E(左)
7F 50 88 3E(上)
FD DB 28 3E(右)
B7 12 8B 3E (底部)
所以它将char
0x2022 编组为 0x95。碰巧的是,0x2022 Unicode 和 0x95 ANSI 都是项目符号字符。因此,这不是随机的,而是将所有内容编组到 ANSI,我现在记得,如果您不指定CharSet
.
好的,所以这至少证实了一些意外的行为正在发生,并进一步为我们提供了一个很好的工作理论,即哪些条件(即结构中的 unicode 字符)可能导致错误。
它没有解释的是为什么这会引发异常,更不用说为什么除了这个用户之外的任何机器上都没有引发异常。至于前者,byte
我认为 unciode 与 ANSI 的大小差异与错误消息(“传递给系统调用的数据区域太小”)一致,但非托管缓冲区 - 大小为 , 容纳 4 个完整字节char
会比必要的大,而不是更小。为什么 CLR 或操作系统会因为只将 1 个字节写入打算用于 2 且足够大用于 4 的区域而感到不安?
至于后者,我认为用户可能使用的 .NET 版本可能比其他所有人都低,如果她没有获得所有 Windows 7 更新,可能就是这种情况。但我只是在安装了全新 Windows 7 和 .NET 4.5(应用程序支持的最低版本)的 VM 上进行了尝试,但仍然无法重现该错误。我正试图找出她所拥有的 .NET 版本,以防万一是 4.5.1 或其他版本。不过,这似乎是一个远大的目标。
似乎唯一可以确定的方法是将Character
成员更改为int
(以保持现有数据的填充相同)并仅char
在必要时将其转换为,然后查看是否会更改用户机器上的结果。这也是一个很好的机会,可以将每个不同的Marshal
调用包装在异常处理程序中,正如 John 建议的那样,看看究竟是哪个导致了错误。
好消息是这是一个相当低优先级的功能,所以即使它继续发生,我也可以让它安全地失败。
会回来汇报的。谢谢大家。