I would like to get the offset of a field in an unmanaged structure. For this I use the Marshal.OffsetOf
method and I realized that the result does not reflect the Packing used with StructLayout
Take this example:
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, Pack = 4)]
unsafe public struct NETGROUP
{
public bool Advise;
public bool Active;
public int UpdateRate;
public double DeadBand;
}
namespace MyApp // Note: actual namespace depends on the project name.
{
internal class Program
{
static void Main(string[] args)
{
unsafe
{
// Technique 1 (not correct)
var oA = Marshal.OffsetOf(typeof(NETGROUP), "Advise").ToInt32();//0
var oB = Marshal.OffsetOf(typeof(NETGROUP), "Active").ToInt32();//4
var oC = Marshal.OffsetOf(typeof(NETGROUP), "UpdateRate").ToInt32();//8
var oD = Marshal.OffsetOf(typeof(NETGROUP), "DeadBand").ToInt32();//12
// Technique 2 (correct)
NETGROUP ex = new NETGROUP();
byte* addr = (byte*)&ex;
var oAa = (byte*)(&ex.Advise) - addr;//0
var oBb = (byte*)(&ex.Active) - addr;//1
var oCc = (byte*)(&ex.UpdateRate) - addr;//4
var oDd = (byte*)(&ex.DeadBand) - addr;//8
}
}
}
}
I need to retrieve this offset in a generic constructor but the second technique (which is the correct one) does not allow me to achieve it without specifying the type explicitly
public CMember(Type type, string pszName, CType pType, uint uMod = Modifier.TMOD_NON, int nDim = 0) : base(DefineConstants.TOKN_MBR)
{
m_sName = pszName;
m_nOffset = Marshal.OffsetOf(type, pszName).ToInt32(); // !!!
m_pType = pType;
m_uMod = uMod;
m_nDim = nDim;
}
Do you have an idea ?
CodePudding user response:
The OffsetOf only returns the layout of the unmanaged instances of a struct
OffsetOf provides the offset in terms of the unmanaged structure layout, which does not necessarily correspond to the offset of the managed structure layout. Marshaling the structure can transform the layout and alter the offset.
See also StructLayout
The common language runtime controls the physical layout of the data fields of a class or structure in managed memory. However, if you want to pass the type to unmanaged code, you can use the StructLayoutAttribute attribute to control the unmanaged layout of the type.
'ex' is a managed instance so you get the default layout
CodePudding user response:
Marshal.OffsetOf
is working as expected, the "issue" is the System.Boolean
which is a non-blittable type and has unmanaged size of 4:
Console.WriteLine(sizeof(bool)); // 1
Console.WriteLine(Marshal.SizeOf(typeof(bool)));// prints 4
From UnmanagedType
enum docs:
Bool A 4-byte Boolean value (true != 0, false = 0). This is the Win32 BOOL type
Changing struct to contain byte
fields instead of bool
ones produces the expected output:
[StructLayout(LayoutKind.Sequential, Pack = 4)]
unsafe public struct NETGROUP
{
public byte Advise;
public byte Active;
...
}
Console.WriteLine(Marshal.OffsetOf(typeof(NETGROUP), "Advise")); // 0
Console.WriteLine(Marshal.OffsetOf(typeof(NETGROUP), "Active")); // 1
Another approach is to marshal bool
as UnmanagedType.I1
:
A 1-byte signed integer. You can use this member to transform a Boolean value into a 1-byte, C-style
bool
(true
= 1,false
= 0).
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct NETGROUP
{
[MarshalAs(UnmanagedType.I1)]
public byte Advise;
[MarshalAs(UnmanagedType.I1)]
public byte Active;
...
}
Some more info here.