【问题标题】:Struct With Fixed Sized Array Of Another Struct具有另一个结构的固定大小数组的结构
【发布时间】:2013-02-25 16:36:46
【问题描述】:

如何在 C# 中用另一个结构的固定大小数组声明一个结构?我需要声明 this 以便它工作。或者如果我想要创建位图信息(-header),我的方法是否错误?

[StructLayout(LayoutKind.Sequential)]
public struct RGBQUAD
{
    public byte b;
    public byte g;
    public byte r;
    public byte reserved;
}

[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
    public BITMAPINFOHEADER bmiHeader;
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStruct, SizeConst = 1)]
    public RGBQUAD[] bmiColors;
}

编辑: 我自己学到的是,如果我分配 256 大小的 RGBQUAD 数组,UnmanagedType.ByValArraySizeConst = 1 在这里很重要,以使 BITMAPINFO 编组大小始终保持在 44 字节事件。

编辑 2: 但 SizeConst 不能小于实际数组大小,否则非托管代码可能会使应用程序崩溃。

【问题讨论】:

标签: c# arrays winapi struct


【解决方案1】:

如 cmets 中所述,在 C# 中,您无法在结构中创建固定大小的结构数组。

但这不是你想要的。在声明中

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER bmiHeader;
    RGBQUAD          bmiColors[1];
} BITMAPINFO, *PBITMAPINFO;

bmiColors 被声明为定长数组,但它实际上是RGBQUAD 变长数组的占位符。数组的实际长度取决于(in a slightly complicated waybmiHeader.biClrUsed 的值。

在 C# 中如何处理这完全取决于您对 BITMAPINFO 结构所做的工作。

更新

我刚刚看到你的另一个问题。您将 BITMAPINFO 传递给 SetDIBits 并且您的颜色表始终有 256 个条目。所以在BITMAPINFO.bmiColors的声明中设置SizeConst为256。

【讨论】:

  • 我需要一个适用于任何调色板尺寸的可重复使用的解决方案。所以我想保持原来的大小为1。这是否意味着我在这里的声明是正确的?
  • 当您将 .Net 中的结构编组到本机 API 时,实际上会复制数据。如果将 SizeConst 设置为 1,则只会复制调色板的一个条目。充其量,您的图像颜色将是错误的。在最坏的情况下,SetDIBits 在读取超过分配缓冲区的末尾时会崩溃。如果将其设置为 256,则不必使用所有条目。
  • 是的,这至少修复了 CreatePalette 的崩溃。如果我没有 256 色而是 16 或 8 色,我可以动态制作 Marshal 副本吗?
  • 结构没有动态长度,因此编组器不支持将其作为内置选项。您始终可以编写自己的编组代码(使用 Marshal 类中的方法),但在每次调用 SetDIBits 时执行此操作可能比使用内置支持要慢。复制 256 个 RGBQUAD 并不需要很长时间。
  • 但是你可以让它更快。大概在 BITMAPINFO 中传递的信息(即位图尺寸和调色板)在调用之间不会改变。因此,您可以将结构一次编组到非托管内存中,并在每次调用 SetDIBits 时使用它。但在您分析代码并确定编组 BITMAPINFO 的开销确实很大之前,我真的不会费心进行任何更改。
【解决方案2】:

不幸的是(恕我直言,考虑到 .net 的设计目标之一是促进与 COM 的互操作),.net 运行时除了独立的 System.Array 对象或构建的字符数组之外,不了解任何类型的数组进入System.String。尽管 C# 提供了 fixed 数组类型,但这些类型是使用指针算法以 .net 运行时无法真正理解且无法验证的方式进行操作的。

可以通过定义一个包含a0a1a2 等元素的结构并编写一个使用 switch 语句或其他类似的索引属性来创建行为类似于固定大小数组的东西构造来访问其中的元素,但这种构造的执行速度可能比普通数组慢得多。

如果您不需要使用safe 代码(并且考虑到您正在与可能不受信任的托管代码进行互操作,“安全”代码可能不是考虑因素)我建议您定义您的RGBQUAD 使用显式布局,将int 与您的其他数据重叠,然后让您的其他结构包含fixed int[whateverSize]; 来保存RGB 数据。它的索引访问器应该能够通过读取/写入其“int”成员而相当有效地将int 转换为RGBQUAD

【讨论】:

    猜你喜欢
    • 2015-06-07
    • 1970-01-01
    • 2011-11-07
    • 2020-08-20
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多