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

本文关键字:结构 net 托管数据 映射 | 更新日期: 2023-09-27 18:09:17

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

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

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

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

c#实现矩形(托管。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

left = bottom

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

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

如果你真的想改变成员变量的顺序,你可以使用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;
}

是的,看来你的想法还不错。StructLayout(LayoutKind.Sequential)是c# struct的默认值,所以你甚至不需要这样做。但是如果你想要不同的字段顺序,你可以通过使用StructLayout(LayoutKind.Explicite),然后对每个字段应用FieldOffset属性来实现这一点——这是一个更好的方法,因为你使隐式显式,而不再依赖于一些很容易改变的东西,比如字段定义顺序。

看看MSDN样本:StructLayoutAttribute Class,它应该更清楚。另外-用c++和c#创建样例应用程序,并使用它来掌握它的窍门-它会给你带来很大的好处。

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的值表示该变量开始的结构体中的字节位置

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

是的,那是正确的。访问说明符和变量名都不会对结构体的布局方式产生任何影响。