将非托管数据映射到.NET中的托管结构

时间:2013-04-15 10:24:06

标签: c# .net winapi pinvoke unmanaged

我花了很多时间处理非托管代码,并在.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 ++结构的顺序相同,并且它使用相同的变量名称。

我对LayoutKind.Sequential的假设意味着非托管数据按照它在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;
}

猜测一下,这会像这样映射:

  

top = left

     

right = top

     

bottom = right

     

左=底

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

4 个答案:

答案 0 :(得分:7)

如果您确实想要更改成员变量的顺序,可以使用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 :(得分:2)

是的,看来你的想法还可以。 StructLayout(LayoutKind.Sequential)是应用于C#struct的默认值,因此您甚至不需要这样做。但是,如果您希望使用不同的字段顺序,则可以使用StructLayout(LayoutKind.Explicite)然后将FieldOffset属性应用于每个字段 - 这是更好的方法,因为您明确表达了什么是隐含的,不再是你是否依赖于像字段定义顺序那样容易改变的东西。

看看MSDN示例:StructLayoutAttribute Class,它应该更清晰。另外 - 用C ++和C#创建示例应用程序并使用它来获取它 - 它将使您受益匪浅。

答案 2 :(得分:1)

C#中struct的默认映射为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的值表示变量开头结构中的字节位置。

答案 3 :(得分:0)

  

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

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