MarshalAsAttribute array of strings

2019-05-20 01:36发布

问题:

Im trying to read an alarm structure from a Beckhoff - PLC into a c# class. First i have to make the exact same structure in c# and it currently looks like this:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class Alarm
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
        public string text;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
        public string objectName;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public string[] instancePath = new string[6];
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
        public string timeStamp;
        public int priority;
        [MarshalAs(UnmanagedType.I1)]
        public bool acknowledge;
        [MarshalAs(UnmanagedType.I1)]
        public bool disabled;
        [MarshalAs(UnmanagedType.I1)]
        public bool alarmIn;
    }

Whats causing me problems is the "instancePath" field. When the field is a string i can use the "UnmanagedType.ByValTStr" attribute with SizeConst and when it's an array "UnmanagedType.ByValArray" but when i want to use a string[] i don't know what to do.

I've tried creating a new class:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class InstancePathDefinition
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
        public string instancePath;
    }

And used in my alarm class:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class Alarm
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
        public string text;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
        public string objectName;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public InstancePathDefinition[] instancePath = new InstancePathDefinition[6];
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
        public string timeStamp;
        public int priority;
        [MarshalAs(UnmanagedType.I1)]
        public bool acknowledge;
        [MarshalAs(UnmanagedType.I1)]
        public bool disabled;
        [MarshalAs(UnmanagedType.I1)]
        public bool alarmIn;
    } 

But when I use Marshal.SizeOf on my Alarm-class it gives me a size or 147 bytes instead of 189 bytes as I would expect.

EDIT: I think the reason for the difference in size is that only the array gets initiated and the class "InstancePathDefinition" doesn't.

I tried changing it from a class to a struct and now the sizes match.

I still find it strange though that I can't combine both UnmanagedType.ByValArray and UnmanagedType.ByValTStr as a sub type with different SizeConst.

Next I will need to create an array of the alarm class and that will get me into the same trouble again.

回答1:

Make it an array of structures instead of an array of class objects.

[StructLayout(LayoutKind.Sequential)]
public struct InstancePathDefinition {
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
    public string path;
}

Marshal.SizeOf() returned 189 when I tried it.