【问题标题】:Unable to access a variable from explicit struct (bring up of union) C#无法从显式结构访问变量(启动联合)C#
【发布时间】:2014-07-23 07:40:46
【问题描述】:

我正在尝试从 C union 构建 C# 显式 struct。显式结构是:

[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct struct_1
{
   [FieldOffset(0)]
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
   public uint[] All32;

   [FieldOffset(0)]
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
   public struct_2[] bits;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct struct_2
{
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
   public byte[] Var56;
}

这些是结构,在从byte 数组实例化后,我无法访问All32 uint(智能感知显示'?')如下

Type structureType = typeof(struct_1);

byte[] b = new byte[4];
b[0] = 0xA0;
b[1] = 0x01;
b[2] = 0xF0;
b[3] = 0x00;

if (structureType != null)
{
   try
   {
      GCHandle handle = GCHandle.Alloc(b, GCHandleType.Pinned);
      struct_1 intpdObj = (struct_1)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), structureType);
      handle.Free();
   }
   catch
   {
   }
}

【问题讨论】:

  • 您的空 catch 块可能会抑制有价值的调试信息。
  • 我没有任何“?”问题。 However, here is what i got您的第一个字段指向与您的第二个字段相同的地址,并找到 struct_2 而不是 uint(参见我图片的“类型”列)
  • 你能发布 C 结构声明吗?

标签: c# struct unions


【解决方案1】:

好的。这是因为您在struct_2 中定义了一个数组并且它是一个对象,所以当PtrToStructure 创建结构时,内存地址将放在该位置内。你可以试试这个:

[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct struct_1
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValArray)]
    public uint[] All32;

    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.Struct)]
    public struct_2 bits;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct struct_2
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] Var56;
}

【讨论】:

  • 谢谢。但又是同样的问题。无法访问 All32。看来我必须按照@Chris 在下面指出的方式进行。
  • 奇怪的是,这对我来说没有问题。 Console.WriteLine(intpdObj.All32[0]);
  • 无论如何,你选择最佳答案:)
  • 对不起@Alireza 它正在工作。我检查了智能感知并错误地推断。我打印了,它工作正常。
【解决方案2】:

MarshalAs 属性不会改变数据在 .NET 类/结构中的表示方式,它只会改变数据在传递给其他代码时的编组方式。数组仍然是对数组的引用,而这一切都变成了地狱。

您可以使用此方法覆盖两个不同类型的数组,但存在一些严重的问题。最大的问题是所有数组的元素数量相同,但分配给数组的内存量相同。

以这个联合为例:

[StructLayout(LayoutKind.Explicit)]
public struct ArrayUnion
{
    [FieldOffset(0)]
    public byte[] bytes;

    [FieldOffset(0)]
    public int[] ints;
}

如果我使用 100 字节数组创建该结构的新实例,ints 数组也将声称长度为 100:

var u = new ArrayUnion { bytes = new byte[100] };
Console.WriteLine(u.ints.Length);

这当然是不正确的。如果我随后尝试访问 ints 数组中前 25 个条目之后的任何内容,我可能会遇到严重错误并崩溃。

您当然可以将其中一些内容包装到私有字段中并公开适当的方法。请注意您实际访问数据的方式...因为如果您弄错了,您破坏事情。

【讨论】:

  • 哇,粗略。突然不安全的代码似乎没那么糟糕了,呵呵 ;)
  • @Chris 是的,有 unsafe 代码和 unsafe 代码。这是第二种类型:P 另一方面,它是将任意长度的字节数组转换回它所表示的整数(或双精度数,或其他)的最快方法,而不使用unsafe。这是一种技巧,但如果您小心谨慎并了解风险,它确实有效。
【解决方案3】:

为什么All32 是一个数组?如果你尝试这样的事情会发生什么?

[StructLayout( LayoutKind.Explicit, Pack = 1 )]
public unsafe struct struct_1
{
    [FieldOffset( 0 )]
    public uint All32;

    [FieldOffset( 0 )]
    public fixed byte bits[4];
}

编辑:如果您确实希望 All32 成为一个数组:

[StructLayout( LayoutKind.Explicit, Pack = 1 )]
public unsafe struct struct_1
{
    [FieldOffset( 0 )]
    public fixed uint All32[1];

    [FieldOffset( 0 )]
    public fixed byte bits[4];
}

【讨论】:

  • 嗯,是的,但它有效..!我想说在 C# 中做联合和 Marshal.PtrToStructure 无论如何都离不安全的代码领域不远。
  • 是的,非常接近。你可以做到这一点,但大多数时候这是一个糟糕的主意。不过,对于托管“安全”代码中的超快速转换很方便:P
  • 谢谢@Chris。看来这是我必须走的路。其他尝试均无效。
  • 谢谢@Corey。如果存在不使用“不安全”的另一种方法,那会让我的工作变得轻松。
  • 其他方式的难点在于数组是引用类型,重叠这些引用充其量只是粗略的。使用fixed,它全部直接嵌入到结构中。
猜你喜欢
  • 2015-12-20
  • 1970-01-01
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-05
  • 1970-01-01
相关资源
最近更新 更多