Posts
7
Comments
2
Trackbacks
0
July 2009 Entries
Unmanaged arrays in C# structs
I am creating a Memory Mapped File (MMF) that will act like a circular buffer because it will store "live" 100msec data from a PLC.  The controls engineer wanted to send the data to me as tag arrays.  So, to mimic these tag arrays, I decided to create a structure the reflects these tag arrays. 

There are ten arrays in the structure. 

The first array is an array of two short integers (16-bits) that contain a write and read pointer.
The next three array is an array of 1024 bytes, an array of 1024 short integers and an array of 1024 floats.  This set of three arrays is repeated three times.

A structLayout attribute is used to place these arrays in sequential order and I specify the size so that I can reserve that amount of space on the disk for the MMF when it is created.  Each array is marshalled as an  UnmanagedType.ByValArray. 

Here is the structure as I first created it:

[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 150532)]
public struct strL1CmnRec
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.I2)] public short[] shtL1Ptrs;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.U1)] public byte[] byEntryBool;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.I2)] public short[] shtEntryInts;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.R4)] public float[] sngEntryReals;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.U1)] public byte[] byProcBool;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.I2)] public short[] shtProcInts;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.R4)] public float[] sngProcReals;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.U1)] public byte[] byDelvBool;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.I2)] public short[] shtDelvInts;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024, ArraySubType = UnmanagedType.R4)] public float[] sngDelvReals;
}

To access this structure and its arrays, I declared this variable:

strL1CmnRec L1CRec;

I then attempted to write into one the arrays as follows:

L1CRec.shtL1Ptrs[0] = i;
L1CRec.shtL1Ptrs[1] = 0;

And, when i was attempted to be placed into shtL1Ptrs[0], I received a runtime error because shtL1Ptrs[0] was null!

What to do? What to do?


It turns out that in a C# struct, the arrays are declared but its memory is not allocated. So I wrote a create method to allocate memory
for each array in the struct. The create method is part of the C# struct.

public static strL1CmnRec create()
{
return new strL1CmnRec
{
shtL1Ptrs = new short[2],
byEntryBool = new byte[1024],
shtEntryInts = new short[1024],
sngEntryReals = new float[1024],
byProcBool = new byte[1024],
shtProcInts = new short[1024],
sngProcReals = new float[1024],
byDelvBool = new byte[1024],
shtDelvInts = new short[1024],
sngDelvReals = new float[1024]
};
}


So, now to access these arrays, I first declared the variable:

strL1CmnRec L1CRec;

then I call the create function.  I place this in the constructor of the class that is using this structure:

L1CRec = strL1CmnRec.create();
I can now successfully access the arrays in the struct. 

 
posted @ Thursday, July 09, 2009 5:13 PM | Feedback (1)
C# Constants & Global Variables
Moving from C++ to C#, I discovered that constants in the way I was using them are not supported; however, they can be emulated.  they are now defined in their own class as static variables; I go a step further by making them private and only available through a property. 

Before I show a code example, let's explore static further.  When a variable is declared as static, the variable is essentially global.  All instances of the class share the same static variable.  A static variable is initialized to zero unless an explicit initializer is specified.

FIrst, let's look at the class that defines the constants:

namespace Globals
{
    public class clsGlobalDefs
    {
        private static string _c1 = "Constant 1";
        private static string _c2= "This is a constant string";

        public string c1
        {
            get { return _c1; }
        }

        public string c2
        {
            get { return _c2; }
        }

        public clsGlobalDefs()
        {
 
        }
}

A few notes on the above class:

  1. I like placing the constants in their own namespace and their own DLL.  This allows me to use the same constants across multiple projects.
  2. Note the use of the static qualifier
  3. Note the use of properties
To use class clsGlobalDefs:

  1. Reference clsGlobalDefs through a using statement : using Globals
  2. Instantiate clsGlobalDefs: clsGlobalDefs GlobalDefs = new clsGlobalDefs();
  3. Access the constant through the class' property:
String s;
s = GlobalDefs.c1;

-or-

s = "This is constant #1: " + GlobalDefs.c1


posted @ Friday, July 03, 2009 8:16 AM | Feedback (0)
News