【问题标题】:Mapping unmanaged data to a managed structure in .NET将非托管数据映射到 .NET 中的托管结构
【发布时间】:2013-04-15 10:24:05
【问题描述】:

我花了很多时间处理非托管代码和 .NET 中的平台调用。下面的代码说明了一些让我感到困惑的事情,即非托管数据如何映射到 .NET 中的托管对象。

对于这个例子,我将使用RECT 结构:

C++ RECT 实现(非托管 Win32 API)

typedef struct _RECT {
  LONG left;
  LONG top;
  LONG right;
  LONG bottom;
} RECT, *PRECT;

C# RECT 实现(托管 .NET/C#)

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left, top, right, bottom;
}

好的,所以我的 C# 等效项应该可以工作,对吧?我的意思是,所有变量都与 C++ 结构的顺序相同,并且使用相同的变量名。

我对@9​​87654324@ 的假设意味着非托管数据按照它出现在 C++ 结构中的相同顺序映射到托管对象。即数据将被映射,从左开始,然后是顶部,然后是右,然后是底部。

在此基础上,我应该能够修改我的 C# 结构...

C# RECT 实现(更简洁)

[StructLayout(LayoutKind.Sequential)]
public struct Rect //I've started by giving it a .NET compliant name
{
    private int _left, _top, _right, _bottom; // variables are no longer directly accessible.

    /* I can now access the coordinates via properties */
    public Int32 Left
    {
        get { return _left; }
        set { this._left = value; }
    }

    public Int32 Top
    {
        get { return _top; }
        set { this._top = value; }
    }

    public Int32 Right
    {
        get { return _right; }
        set { this._right = value; }
    }

    public Int32 Bottom
    {
        get { return _bottom; }
        set { this._bottom = value; }
    }
}

那么如果变量的声明顺序错误会发生什么?大概这搞砸了坐标,因为它们将不再映射到正确的东西?

public struct RECT
{
    public int top, right, bottom, left;
}

猜想,这会像这样映射:

上=左

右=顶部

底部=右

左 = 底部

所以我的问题很简单,我的假设是否正确,我可以根据每个变量的访问说明符甚至变量名来修改托管结构,但我不能改变变量的顺序?

【问题讨论】:

    标签: c# .net winapi pinvoke unmanaged


    【解决方案1】:

    如果你真的想改变你的成员变量的顺序,你可以使用FieldOffsetAttribute。只是让它的可读性降低了一点。

    您还需要将StructLayout 设置为LayoutKind.Explicit,以表明您正在自己设置偏移量。

    例子:

    [StructLayout(LayoutKind.Explicit)]
    public struct RECT
    {
        [FieldOffset(4)]  public int top;
        [FieldOffset(8)]  public int right;
        [FieldOffset(12)] public int bottom;
        [FieldOffset(0)]  public int left;
    }
    

    【讨论】:

    • 优秀的答案。简洁易懂。 +1
    • 你真的真的真的不想这样做。
    • @DavidHeffernan 为什么不呢?
    【解决方案2】:

    是的,看来你的想法还可以。 StructLayout(LayoutKind.Sequential) 是应用于 C# struct 的默认值,因此您甚至不需要这样做。但是,如果您想拥有不同的字段顺序,您可以使用 StructLayout(LayoutKind.Explicite) 来做到这一点,然后将 FieldOffset 属性应用于每个字段 - 这是更好的方法,因为您明确了隐含的内容并且不再依赖可以轻松更改的内容,例如字段定义顺序。

    看一下 MSDN 示例:StructLayoutAttribute Class,应该会更清楚。此外 - 使用 C++ 和 C# 创建示例应用程序并使用它来掌握它 - 这将使您受益匪浅。

    【讨论】:

      【解决方案3】:

      struct 在 C# 中的默认映射是 LayoutKind.Sequential。这会阻止编译器通过重新排列变量并确保正确映射来优化内存。

      但是,您可以使用LayoutKind.ExplicitFieldOffsetAttribute 告诉编译器变量的不同顺序:

      [StructLayout(LayoutKind.Explicit)]
      public struct Rect
      {
          [FieldOffset(8)]
          public int right;
      
          [FieldOffset(4)]
          public int top;
      
          [FieldOffset(0)]
          public int left;
      
          [FieldOffset(12)]
          public int bottom;
      }
      

      FieldOffsetAttribute 的值表示变量开始的结构体中的字节位置。

      【讨论】:

        【解决方案4】:

        我的假设是否正确,我可以根据每个变量的访问说明符甚至变量名称来修改托管结构,但我不能更改变量的顺序?

        是的,没错。访问说明符和变量名都不会影响结构的布局方式。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-24
          • 2023-03-03
          • 2011-03-13
          • 2015-10-26
          • 1970-01-01
          • 2018-03-19
          相关资源
          最近更新 更多