VB.NET Structures and Union Help.

mavromatis

Member
Joined
Feb 7, 2006
Messages
19
Programming Experience
1-3
I have a chunk of VC.NET code (below) that I need to convert to VB.NET syntax. Could someone help me get started? I'm new to structures and unions and I don't understand how to nest them in VB.NET. Ideally, I'd like to see an example using the code I have below, so I it can start me off or is there a better way to handle this type of structure in VB.NET?

VB.NET:
' ----- VC.NET CODE THAT I NEED TO CONVERT TO VB.NET ----
 
struct R_OMNI_LINK_MESSAGE {
  //unsigned char StartChar;
  unsigned char MessageLength;
  union {
    unsigned char Data[255];
    struct {
      unsigned char MessageType;
      union {
        struct /* olmNAME_DATA (8 bit) */ {
          unsigned char ItemType8;
          unsigned char ItemNumber8;
          unsigned char ItemName8[16];
        };
        struct /* olmNAME_DATA (16 bit) */ {
          unsigned char ItemType16;
          unsigned char ItemNumber16MSB;
          unsigned char ItemNumber16LSB;
          unsigned char ItemName16[16];
        };
        struct /* olmEVENT_LOG_DATA */ {
          unsigned char EventNumber;  // (1-N, With 1 Being Most Recent)
          unsigned char EventTimeDateValid;
          unsigned char EventMonth;  // (1-12)
          unsigned char EventDay;  // (1-31)
          unsigned char EventHour;  // (0-23)
          unsigned char EventMinute;  // (0-59)
          unsigned char EventType;
          unsigned char EventParameter1;
          unsigned char EventParameter2High;
          unsigned char EventParameter2Low;
        };
        struct /* olmCOMMAND_MESSAGE */ {
          unsigned char Command;
          unsigned char Parameter1;
          unsigned char Parameter2High;
          unsigned char Parameter2Low;
        };
        struct /* olmSET_TIME */ {
          unsigned char stYear;
          unsigned char stMonth;
          unsigned char stDay;
          unsigned char stDOW;
          unsigned char stHour;
          unsigned char stMinute;
          unsigned char stDST;
        };
        struct /* olmSYSTEM_INFORMATION */ {
          unsigned char ModelNumber;
          unsigned char MajorVersion;
          unsigned char MinorVersion;
          unsigned char Revision;
          unsigned char LocalPhoneNumber[25];
        };
        struct /* olmSYSTEM_STATUS */ {
          unsigned char TimeDateValidFlag;
          unsigned char Year;  // (0-99)
          unsigned char Month;  // (1-12)
          unsigned char Day;  // (1-31)
          unsigned char DayOfWeek;  // (1-7)
          unsigned char Hour;  // (0-23)
          unsigned char Minute;  // (0-59)
          unsigned char Second;  // (0-59)
          unsigned char DaylightSavingsTimeFlag;
          unsigned char CalculatedSunriseHour;  // (0-23)
          unsigned char CalculatedSunriseMinute;  // (0-59)
          unsigned char CalculatedSunsetHour;  // (0-23)
          unsigned char CalculatedSunsetMinute;  // (0-59)
          unsigned char BatteryReading;
          unsigned char AreaSecurityMode[8];  // index 0-7
          struct {
            unsigned char Status;
            unsigned char BatteryReading;
          } ExpansionEnclosure[8];  // index 0-7
        };
        struct /* olmREQUEST_ZONE_STATUS */ {
          unsigned char StartingZone;
          unsigned char EndingZone;
        };
        struct /* olmZONE_STATUS */ {
          unsigned char ZoneStatus;
          unsigned char AnalogLoopReading;
        } Zone[127];
        struct /* olmREQUEST_UNIT_STATUS (8 bit) */ {
          unsigned char StartingUnit;
          unsigned char EndingUnit;
        };
        struct /* olmREQUEST_UNIT_STATUS (16 bit) */ {
          unsigned char StartingUnitMSB;
          unsigned char StartingUnitLSB;
          unsigned char EndingUnitMSB;
          unsigned char EndingUnitLSB;
        };
        struct /* olmUNIT_STATUS */ {
          unsigned char CurrentCondition;
          unsigned char HighByteOfTime;
          unsigned char LowByteOfTime;
        } Unit[84];
        struct /* olmREQUEST_AUXILIARY_STATUS */ {
          unsigned char StartingTemperatureSensor;
          unsigned char EndingTemperatureSensor;
        };
        struct /* olmAUXILIARY_STATUS */ {
          unsigned char RelayStatus;
          unsigned char CurrentTemperature;
          unsigned char LowHeatTemperatureSetpoint;
          unsigned char HighCoolTemperatureSetpoint;
        } TempSensor[63];
        struct /* olmREQUEST_THERMOSTAT_STATUS */ {
          unsigned char StartingThermostat;
          unsigned char EndingThermostat;
        };
        struct /* olmTHERMOSTAT_STATUS */ {
          unsigned char StatusByte;
          unsigned char CurrentTemperature;
          unsigned char HeatSetpoint;
          unsigned char CoolSetpoint;
          unsigned char SystemMode;
          unsigned char FanMode;
          unsigned char HoldStatus;
        } Thermostat[36];
        struct /* olmLOGIN */ {
          unsigned char LoginCode1;
          unsigned char LoginCode2;
          unsigned char LoginCode3;
          unsigned char LoginCode4;
        };
        struct /* olmSYSTEM_EVENTS */ {
          unsigned char High;
          unsigned char Low;
        } SystemEvent[127];
        struct /* olmMESSAGE_STATUS */ {
          unsigned char Data;
        } MessageStatus[33];
        struct /* olmREQUEST_SECURITY_CODE_VALIDATION */ {
          unsigned char AreaNumber;  // (1-8)
          unsigned char Code1;  // First Digit Of Code
          unsigned char Code2;  // Second Digit Of Code
          unsigned char Code3;  // Third Digit Of Code
          unsigned char Code4;  // Fourth Digit Of Code
        };
        struct /* olmSECURITY_CODE_VALIDATION */ {
          unsigned char UserCodeNumber;  // (1-99, 251 for duress, 0 if 
invalid)
          unsigned char AuthorityLevel; 
//(0=Invalid,1=Master,2=Manager,3=User)
        };
      };  // union
    };  // struct
  }; // union
};

Thanks,
Danny
 
Last edited:
The intuitive thing to do with this in VB.Net is to translate each Union to an Object. (It's a variant type, union can only be one of it's members at a time.) Then you can set up the different structures and choose between them to assign the Object.

Only problem is that an Object may be allocated more memory space than choosing any of the elected Union members, thus be too large for the function that requires such input to read (since these usually operate on memory pointers..).

I think the solution to this, if you need it, is to translate all unions to structures, set StructLayout to Explicit and define every member with FieldOffset. You will need to know the allocation size for the different data types.

MSDN StructLayout: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemruntimeinteropservicesstructlayoutattributeclasstopic.asp
MSDN Union: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/class_29.asp
 
John,

Thanks for the reply and the help! I was heading down the route of your last suggestion, since I think that one will keep the structure like the original VC.NET code.

I think the solution to this, if you need it, is to translate all unions to structures, set StructLayout to Explicit and define every member with FieldOffset. You will need to know the allocation size for the different data types.

However, I do have a question on how to do this StructLayout syntax. Let's say I have:

VB.NET:
struct R_OMNI_LINK_MESSAGE {
  //unsigned char StartChar;
  unsigned char MessageLength;
  union {
    unsigned char Data[255];
    struct {
      unsigned char MessageType;
      union {
        struct /* olmNAME_DATA (8 bit) */ {
          unsigned char ItemType8;
          unsigned char ItemNumber8;
          unsigned char ItemName8[16];
        };
        struct /* olmNAME_DATA (16 bit) */ {
          unsigned char ItemType16;
          unsigned char ItemNumber16MSB;
          unsigned char ItemNumber16LSB;
          unsigned char ItemName16[16];
        };
      };  // union
    };  // struct
  }; // union
};

Would it be something like the following in VB.NET?

HTML:
<StructLayout(LayoutKind.Explicit)> _
Private Structure R_OMNI_LINK_MESSAGE
' struct {
<FieldOffset(0)> Public MessageLength As Integer
<FieldOffset(2)> Public MessageType As Char
<FieldOffset(4)> Public ItemType8 As Char
<FieldOffset(6)> Public ItemNumber8 As Char
 
<FieldOffset(0)> Public MessageLength As Integer
<FieldOffset(2)> Public MessageType As Char
<FieldOffset(4)> Public ItemNumber16MSB As Char
<FieldOffset(6)> Public ItemNumber16LSB As Char
<FieldOffset(8)> Public ItemName16(16) As Char
 
' }
End Structure
 
Perhaps, I am not a C programmer, but from what I read around I think this is a better translation of that:
VB.NET:
<StructLayout(LayoutKind.Explicit)> _
Private Class R_OMNI_LINK_MESSAGE
  <FieldOffset(0)> Public MessageLength As Byte
  Structure A
    <FieldOffset(1)> Public Data As Byte
  End Structure
  Structure B
    <FieldOffset(1)> Public MessageType As Byte
    Structure X
      <FieldOffset(2)> Public ItemType8 As Byte
      <FieldOffset(3)> Public ItemNumber8 As Byte
      <FieldOffset(4)> Public ItemName8(16) As Byte
    End Structure
    Structure Y
      <FieldOffset(2)> Public ItemType16 As Byte
      <FieldOffset(3)> Public ItemNumber16MSB As Byte
      <FieldOffset(4)> Public ItemNumber16LSB As Byte
      <FieldOffset(5)> Public ItemName16(16) As Byte
    End Structure
  End Structure
End Class
Where you must choose between structure A or B, and if you use B must choose between structure X or Y. Unsigned Char is an unsigned byte with a range of 0-255 that translates to the Byte type in VB.Net. The FieldOffset is in bytes, so there is one for each member here. One byte is 8 bits.

:):edit:: fixed the ItemNames to arrays.)
 
Last edited:
Great... It's starting to make a little bit of sense, however, I don't think it's exactly right...

Here's why:

I declare Msg variable of type R_OMNI_LINK_MESSAGE, I set the messageLength. Everything is fine so far...

VB.NET:
' //declare a Msg variable of type R_OMNI_LINK_MESSAGE
Dim Msg As New R_OMNI_LINK_MESSAGE
' //assign the MessageLength from the COPYDATASTRUCT
Msg.MessageLength = data.cbData

But, when I try to set the Msg.MessageType, it doesn't show up as available. Instead, I have Msg.A and Msg.B available, but no the elements below it, like, Msg.B.ItemType8... Ideally, I should be able to do the following with that structure.

VB.NET:
' //declare a Msg variable of type R_OMNI_LINK_MESSAGE
Dim Msg As New R_OMNI_LINK_MESSAGE
' //assign the MessageLength from the COPYDATASTRUCT
Msg.MessageLength = data.cbData
' // populate the data fields
Msg.MessageType = [SIZE=2]&HF[/SIZE]
[SIZE=2]Msg.StartingZone = 1[/SIZE]
[SIZE=2]Msg.EndingZone = 2[/SIZE][SIZE=2]
[/SIZE]

And be able to send that Msg structure back through the DLL to request more data or set the structure via Msg returned from the DLL buffer.

Note: That MessageType (&HF) is a request_zone_status structure (RED ELEMENT IN THE CODE BELOW)... which is in another struct in the full R_OMNI_LINK_MESSAGE structure.

VB.NET:
struct R_OMNI_LINK_MESSAGE {
  //unsigned char StartChar;
  unsigned char MessageLength;
  union {
    unsigned char Data[255];
    struct {
      unsigned char MessageType;
      union {
        struct /* olmNAME_DATA (8 bit) */ {
          unsigned char ItemType8;
          unsigned char ItemNumber8;
          unsigned char ItemName8[16];
        };
        struct /* olmNAME_DATA (16 bit) */ {
          unsigned char ItemType16;
          unsigned char ItemNumber16MSB;
          unsigned char ItemNumber16LSB;
          unsigned char ItemName16[16];
        };
        struct /* olmEVENT_LOG_DATA */ {
          unsigned char EventNumber;  // (1-N, With 1 Being Most Recent)
          unsigned char EventTimeDateValid;
          unsigned char EventMonth;  // (1-12)
          unsigned char EventDay;  // (1-31)
          unsigned char EventHour;  // (0-23)
          unsigned char EventMinute;  // (0-59)
          unsigned char EventType;
          unsigned char EventParameter1;
          unsigned char EventParameter2High;
          unsigned char EventParameter2Low;
        };
        struct /* olmCOMMAND_MESSAGE */ {
          unsigned char Command;
          unsigned char Parameter1;
          unsigned char Parameter2High;
          unsigned char Parameter2Low;
        };
        struct /* olmSET_TIME */ {
          unsigned char stYear;
          unsigned char stMonth;
          unsigned char stDay;
          unsigned char stDOW;
          unsigned char stHour;
          unsigned char stMinute;
          unsigned char stDST;
        };
        struct /* olmSYSTEM_INFORMATION */ {
          unsigned char ModelNumber;
          unsigned char MajorVersion;
          unsigned char MinorVersion;
          unsigned char Revision;
          unsigned char LocalPhoneNumber[25];
        };
        struct /* olmSYSTEM_STATUS */ {
          unsigned char TimeDateValidFlag;
          unsigned char Year;  // (0-99)
          unsigned char Month;  // (1-12)
          unsigned char Day;  // (1-31)
          unsigned char DayOfWeek;  // (1-7)
          unsigned char Hour;  // (0-23)
          unsigned char Minute;  // (0-59)
          unsigned char Second;  // (0-59)
          unsigned char DaylightSavingsTimeFlag;
          unsigned char CalculatedSunriseHour;  // (0-23)
          unsigned char CalculatedSunriseMinute;  // (0-59)
          unsigned char CalculatedSunsetHour;  // (0-23)
          unsigned char CalculatedSunsetMinute;  // (0-59)
          unsigned char BatteryReading;
          unsigned char AreaSecurityMode[8];  // index 0-7
          struct {
            unsigned char Status;
            unsigned char BatteryReading;
          } ExpansionEnclosure[8];  // index 0-7
        };
        [B][COLOR=red]struct /* olmREQUEST_ZONE_STATUS */ {[/COLOR][/B][COLOR=red]
[B]   unsigned char StartingZone;[/B]
[B]   unsigned char EndingZone;[/B]
[B] };[/B]
[/COLOR] struct /* olmZONE_STATUS */ {
          unsigned char ZoneStatus;
          unsigned char AnalogLoopReading;
        } Zone[127];
        struct /* olmREQUEST_UNIT_STATUS (8 bit) */ {
          unsigned char StartingUnit;
          unsigned char EndingUnit;
        };
        struct /* olmREQUEST_UNIT_STATUS (16 bit) */ {
          unsigned char StartingUnitMSB;
          unsigned char StartingUnitLSB;
          unsigned char EndingUnitMSB;
          unsigned char EndingUnitLSB;
        };
        struct /* olmUNIT_STATUS */ {
          unsigned char CurrentCondition;
          unsigned char HighByteOfTime;
          unsigned char LowByteOfTime;
        } Unit[84];
        struct /* olmREQUEST_AUXILIARY_STATUS */ {
          unsigned char StartingTemperatureSensor;
          unsigned char EndingTemperatureSensor;
        };
        struct /* olmAUXILIARY_STATUS */ {
          unsigned char RelayStatus;
          unsigned char CurrentTemperature;
          unsigned char LowHeatTemperatureSetpoint;
          unsigned char HighCoolTemperatureSetpoint;
        } TempSensor[63];
        struct /* olmREQUEST_THERMOSTAT_STATUS */ {
          unsigned char StartingThermostat;
          unsigned char EndingThermostat;
        };
        struct /* olmTHERMOSTAT_STATUS */ {
          unsigned char StatusByte;
          unsigned char CurrentTemperature;
          unsigned char HeatSetpoint;
          unsigned char CoolSetpoint;
          unsigned char SystemMode;
          unsigned char FanMode;
          unsigned char HoldStatus;
        } Thermostat[36];
        struct /* olmLOGIN */ {
          unsigned char LoginCode1;
          unsigned char LoginCode2;
          unsigned char LoginCode3;
          unsigned char LoginCode4;
        };
        struct /* olmSYSTEM_EVENTS */ {
          unsigned char High;
          unsigned char Low;
        } SystemEvent[127];
        struct /* olmMESSAGE_STATUS */ {
          unsigned char Data;
        } MessageStatus[33];
        struct /* olmREQUEST_SECURITY_CODE_VALIDATION */ {
          unsigned char AreaNumber;  // (1-8)
          unsigned char Code1;  // First Digit Of Code
          unsigned char Code2;  // Second Digit Of Code
          unsigned char Code3;  // Third Digit Of Code
          unsigned char Code4;  // Fourth Digit Of Code
        };
        struct /* olmSECURITY_CODE_VALIDATION */ {
          unsigned char UserCodeNumber;  // (1-99, 251 for duress, 0 if invalid)
          unsigned char AuthorityLevel;  //(0=Invalid,1=Master,2=Manager,3=User)
        };
      };  // union
    };  // struct
  }; // union
};

I think I'm in way over my head... but I'm enjoying the learning process. The application I am writing is to poll my home automation controller and get/send data through the provided DLL. The DLL isn't COM so I have to hit the DLL directly and watch WM_Copydata then process the Msg. They suggest one uses their unions, structure format so it is easy to reference the devices easier. Like Msg.Thermostat[7].FanMode. I would like to keep it that simple! Sample Code was provided by them but it's in VC.NET. My program is in VB.NET and rewritting it is a bit too late. I've got everthing pretty much done... except I'm stuck on the whole structure union part... :(
 
Last edited:
What I see they do is to 'flatten' such nested structures, like this:
VB.NET:
new structures code posted below... modified from new knowledge :)
You can then call it like you wanted (but still have to be careful not mixing input for the different original union members):
VB.NET:
Dim my_R_OMNI_LINK_MESSAGE As R_OMNI_LINK_MESSAGE
my_R_OMNI_LINK_MESSAGE.MessageLength = data.cbData
my_R_OMNI_LINK_MESSAGE.Messages.MessageType = &HF
my_R_OMNI_LINK_MESSAGE.Messages.msg_olmREQUEST_ZONE_STATUS.StartingZone = 1
my_R_OMNI_LINK_MESSAGE.Messages.msg_olmREQUEST_ZONE_STATUS.EndingZone = 2
(You may also have to set the total expected size for the structures with the StructLayout Size attribute because of the byte array members.)
 
Last edited:
Yeah... I think we're getting there...

I'm going to try both ways. Maybe a mix of the two... what do you think about this way:

VB.NET:
<StructLayout(LayoutKind.Explicit)> _
Public Class R_OMNI_LINK_MESSAGE

  <FieldOffset(0)> Public MessageLength As Byte

  <FieldOffset(1)> Public Data As Byte
  <FieldOffset(1)> Public MessageType As Byte

  <FieldOffset(2)> Public ItemType8 As Byte
  <FieldOffset(3)> Public ItemNumber8 As Byte
  <FieldOffset(4)> Public ItemName8() As Byte

  <FieldOffset(2)> Public ItemType16 As Byte
  <FieldOffset(3)> Public ItemNumber16MSB As Byte
  <FieldOffset(4)> Public ItemNumber16LSB As Byte
  <FieldOffset(5)> Public ItemName16() As Byte
End Class

The VC.NET union and structures are annonomous, so I take it they are just used to get the same data but with different names. So for all the structures they will be using <FieldOffset(2)> thru <FieldOffset(N)>. So if I get a Msg with a type of Themostats, then I ask for the names associated with the themostat. Such as Msg.Thermostat(7).FanMode.

Now, the one part I missed is an array structure like Thermostat:

VB.NET:
        struct /* olmTHERMOSTAT_STATUS */ {
          unsigned char StatusByte;
          unsigned char CurrentTemperature;
          unsigned char HeatSetpoint;
          unsigned char CoolSetpoint;
          unsigned char SystemMode;
          unsigned char FanMode;
          unsigned char HoldStatus;
        } [COLOR="Red"][B]Thermostat[36][/B][/COLOR];

They are setting up that structure as an array. Is that possible in VB.NET structure with offsets? Because I was thinking I would just do the following. But, I don't think there is a way to turn it into an array.

VB.NET:
<FieldOffset(2)> Public StatusByte As Byte
  <FieldOffset(3)> Public CurrentTemperature As Byte
  <FieldOffset(4)> Public HeatSetpoint As Byte
  <FieldOffset(5)> Public CoolSetpoint As Byte
  <FieldOffset(6)> Public SystemMode As Byte
  <FieldOffset(7)> Public FanMode As Byte
  <FieldOffset(8)> Public HoldStatus As Byte

Unless I do something like this:

VB.NET:
<StructLayout(LayoutKind.Explicit)> _
Public Class R_OMNI_LINK_MESSAGE

  <FieldOffset(0)> Public MessageLength As Byte

  <FieldOffset(1)> Public Data As Byte
  <FieldOffset(1)> Public MessageType As Byte

  <FieldOffset(2)> Public ItemType8 As Byte
  <FieldOffset(3)> Public ItemNumber8 As Byte
  <FieldOffset(4)> Public ItemName8() As Byte

  <FieldOffset(2)> Public ItemType16 As Byte
  <FieldOffset(3)> Public ItemNumber16MSB As Byte
  <FieldOffset(4)> Public ItemNumber16LSB As Byte
  <FieldOffset(5)> Public ItemName16() As Byte

  <FieldOffset(2)> Public Thermostat() As olmTHERMOSTAT_STATUS
End Class

public structure olmTHERMOSTAT_STATUS  
  <FieldOffset(2)> Public StatusByte As Byte
  <FieldOffset(3)> Public CurrentTemperature As Byte
  <FieldOffset(4)> Public HeatSetpoint As Byte
  <FieldOffset(5)> Public CoolSetpoint As Byte
  <FieldOffset(6)> Public SystemMode As Byte
  <FieldOffset(7)> Public FanMode As Byte
  <FieldOffset(8)> Public HoldStatus As Byte
end structure
 
Last edited:
It will be too confusing, in my opinion, if you start to mix the structures.
From what I read in linked forum discussion, you can either use the plain Data byte array (and look up the bytes from some documentation) or use the 'convenient' nested structures.
http://www.mcse.ms/message1672362.html see last post

Here is a modification of the flattened structures I introduced above, usage is the same. I have included fixed sizing of arrays (SizeConst is number of elements) plus handled arrays of structures. The Marshal SizeOf doesn't work with this, but the total size for the R_OMNI_LINK_MESSAGE structure should be 256 bytes (MessageLength member 1 byte + Data member 255 bytes).

VB.NET:
<StructLayout(LayoutKind.Explicit)> _
Public Structure R_OMNI_LINK_MESSAGE
  <FieldOffset(0)> Public MessageLength As Byte
  <FieldOffset(1), MarshalAs(UnmanagedType.ByValArray, SizeConst:=255)> _
    Public Data() As Byte
  <FieldOffset(1)> Public Messages As Message
End Structure
 
<StructLayout(LayoutKind.Explicit)> _
Public Structure Message
  <FieldOffset(0)> Public MessageType As Byte
  <FieldOffset(1)> Public msg_olmNAME_DATA8bit As olmNAME_DATA8bit
  <FieldOffset(1)> Public msg_olmNAME_DATA16bit As olmNAME_DATA16bit
 
  '... more structures
 
  <FieldOffset(1), MarshalAs(UnmanagedType.ByValArray, SizeConst:=36)> _
    Public msg_olmTHERMOSTAT_STATUS() As olmTHERMOSTAT_STATUS
 
  '... more structures
 
  <FieldOffset(1)> Public msg_olmREQUEST_ZONE_STATUS As olmREQUEST_ZONE_STATUS
End Structure
 
<StructLayout(LayoutKind.Sequential)> _
Public Structure olmNAME_DATA8bit
  Public ItemType8 As Byte
  Public ItemNumber8 As Byte
  <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _
    Public ItemName8() As Byte
End Structure
 
<StructLayout(LayoutKind.Sequential)> _
Public Structure olmNAME_DATA16bit
  Public ItemType16 As Byte
  Public ItemNumber16MSB As Byte
  Public ItemNumber16LSB As Byte
  <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _
    Public ItemName16() As Byte 
End Structure
 
<StructLayout(LayoutKind.Sequential)> _
Public Structure olmTHERMOSTAT_STATUS
  Public StatusByte As Byte
  Public CurrentTemperature As Byte
  Public HeatSetpoint As Byte
  Public CoolSetpoint As Byte
  Public SystemMode As Byte
  Public FanMode As Byte
  Public HoldStatus As Byte
End Structure
 
<StructLayout(LayoutKind.Sequential)> _
Public Structure olmREQUEST_ZONE_STATUS
  Public StartingZone As Byte
  Public EndingZone As Byte
End Structure

 
I really appriciate the help John! I'm learning a ton!!!

Yes! I think Fred Hebert is the guy that wrote the library in Borland C++ Version 5, that I'm trying to use in .NET.

I'll try out your code... do you think it would be wrong to do the following, so it stays more consistant with the original? It seems to work fine so far... after I'm done setting it all up... I'll try to connect to the OmniPro II and see if the data comes thru. I wonder if there is a way to test the structure without the device connected... just pass it a byte string to the structure then access the values by name...

VB.NET:
<StructLayout(LayoutKind.Explicit)> _
Public Structure R_OMNI_LINK_MESSAGE
<FieldOffset(0)> Public MessageLength As Byte
<FieldOffset(1), MarshalAs(UnmanagedType.ByValArray, SizeConst:=255)> _
Public Data() As Byte
' <FieldOffset(1)> Public Messages As Message
<FieldOffset(1)> Public MessageType As Byte
<FieldOffset(2)> Public NAME_DATA8bit As olmNAME_DATA8bit
<FieldOffset(2)> Public NAME_DATA16bit As olmNAME_DATA16bit
<FieldOffset(2)> Public EVENT_LOG_DATA As olmEVENT_LOG_DATA
<FieldOffset(2)> Public COMMAND_MESSAGE As olmCOMMAND_MESSAGE
<FieldOffset(2)> Public SET_TIME As olmSET_TIME
'... more structures
<FieldOffset(2), MarshalAs(UnmanagedType.ByValArray, SizeConst:=36)> _
Public THERMOSTAT_STATUS() As olmTHERMOSTAT_STATUS
'... more structures
<FieldOffset(2)> Public REQUEST_ZONE_STATUS As olmREQUEST_ZONE_STATUS
End Structure

This above structure gives me access to the values like the VC.net code, with Msg.COMMAND_MESSAGE.Command, rather than accessing them with Msg.Messages.olmCOMMAND_MESSAGE.Command.


 
Oh more question ... :D

In the system_status... I see that it has a nested structure for ExpansionEnclosures.

How would that be handled inside of the olmSYSTEM_STATUS structure?

VB.NET:
 struct /* olmSYSTEM_STATUS */ {
          unsigned char TimeDateValidFlag;
          unsigned char Year;  // (0-99)
          unsigned char Month;  // (1-12)
          unsigned char Day;  // (1-31)
          unsigned char DayOfWeek;  // (1-7)
          unsigned char Hour;  // (0-23)
          unsigned char Minute;  // (0-59)
          unsigned char Second;  // (0-59)
          unsigned char DaylightSavingsTimeFlag;
          unsigned char CalculatedSunriseHour;  // (0-23)
          unsigned char CalculatedSunriseMinute;  // (0-59)
          unsigned char CalculatedSunsetHour;  // (0-23)
          unsigned char CalculatedSunsetMinute;  // (0-59)
          unsigned char BatteryReading;
          unsigned char AreaSecurityMode[8];  // index 0-7
[COLOR=red][B]     struct {[/B][/COLOR]
[COLOR=red][B]       unsigned char Status;[/B][/COLOR]
[COLOR=red][B]       unsigned char BatteryReading;[/B][/COLOR]
[COLOR=red][B]     } ExpansionEnclosure[8];  // index 0-7[/B][/COLOR]
        };

I've got it like:

VB.NET:
[COLOR=#008000]' olmSYSTEM_STATUS[/COLOR]
[SIZE=2]<StructLayout(LayoutKind.Sequential)> _
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Structure[/COLOR][/SIZE][SIZE=2] olmSYSTEM_STATUS
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] TimeDateValidFlag [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] Year [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (0-99)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] Month [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (1-12)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] Day [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (1-31)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] DayOfWeek [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (1-7)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] Hour [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (0-23)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] Minute [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (0-59)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] Second [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (0-59)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] DaylightSavingsTimeFlag [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] CalculatedSunriseHour [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (0-23)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] CalculatedSunriseMinute [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (0-59)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] CalculatedSunsetHour [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (0-23)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] CalculatedSunsetMinute [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'// (0-59)
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] BatteryReading [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]
[/COLOR][/SIZE][SIZE=2]<MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> _
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] AreaSecurityMode() [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]
[/COLOR][/SIZE][SIZE=2][COLOR=red][B]<MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> _[/B][/COLOR]
[/SIZE][COLOR=red][B][SIZE=2]Public[/SIZE][SIZE=2] ExpansionEnclosure() [/SIZE][SIZE=2]As[/SIZE][/B][/COLOR][SIZE=2][COLOR=red][B] olmExpansionEnclosure[/B][/COLOR]
[/SIZE][SIZE=2][COLOR=#0000ff]End[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Structure[/COLOR][/SIZE]
[SIZE=2][COLOR=#0000ff]
[/COLOR][/SIZE][SIZE=2][COLOR=#008000]' olmExpansionEnclosure
[/COLOR][/SIZE][SIZE=2]<StructLayout(LayoutKind.Sequential)> _
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Structure[/COLOR][/SIZE][SIZE=2] olmExpansionEnclosure
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] Status [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] BatteryReading [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
End[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Structure[/COLOR][/SIZE]

UPDATE: I think I got this to work... just wanted to have you check it too.
 
Last edited:
I think I'm almost there... really! :p

Is there a way to combine these... should they use fieldoffset to achieve this?

VB.NET:
' ========================================================
' =========> COMBINE THE FOLLOWING
' olmNAME_DATA (8 bit)
<StructLayout(LayoutKind.Sequential)> _
Public Structure olmNAME_DATA8bit
Public ItemType8 As Byte
Public ItemNumber8 As Byte
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _
Public ItemName8() As Byte
End Structure
' olmNAME_DATA (16 bit)
<StructLayout(LayoutKind.Sequential)> _
Public Structure olmNAME_DATA16bit
Public ItemType16 As Byte
Public ItemNumber16MSB As Byte
Public ItemNumber16LSB As Byte
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _
Public ItemName16() As Byte
End Structure
' ========================================================

Like This:

UPDATED: THE FOLLOWING DOESN'T COMPILE, I GET: System.TypeLoadException: Could not load type 'FlashServer.olmNAME_DATA' from assembly 'FlashServer, Version=1.0.2228.19435, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 5 that is incorrectly aligned or overlapped by a non-object field.

VB.NET:
[SIZE=2][COLOR=#008000]' olmNAME_DATA
[/COLOR][/SIZE][SIZE=2][SIZE=2]<StructLayout(LayoutKind.Explicit)> _
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Structure[/COLOR][/SIZE][SIZE=2] olmNAME_DATA
[/SIZE][SIZE=2][COLOR=#008000]' ++++++++++++++++++++++++++++++++++++
[/COLOR][/SIZE][SIZE=2][COLOR=#008000]' 8 bit
[/COLOR][/SIZE][SIZE=2]<FieldOffset(3)> [/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] ItemType8 [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2]<FieldOffset(4)> [/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] ItemNumber8 [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2]<FieldOffset(5), MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] ItemName8() [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'
[/COLOR][/SIZE][SIZE=2][COLOR=#008000]' 16 bit
[/COLOR][/SIZE][SIZE=2]<FieldOffset(3)> [/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] ItemType16 [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2]<FieldOffset(4)> [/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] ItemNumber16MSB [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2]<FieldOffset(5)> [/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] ItemNumber16LSB [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2]<FieldOffset(6), MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2] ItemName16() [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte
[/COLOR][/SIZE][SIZE=2][COLOR=#008000]' ++++++++++++++++++++++++++++++++++++
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]End[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Structure
[/COLOR][/SIZE][/SIZE]

So if the data is 8bit I reference the 8bit names and if 16bit I access the 16 bit names? Reason, the olmNAME_DATA message type is &H0B for both. The data determines if it's 8bit or 16bit...

The way it would get set is either by:

Dim Msg As New R_OMNI_LINK_MESSAGE
Msg.MessageLength = XX
Msg.MessageType = &H0B
Msg.ItemType8 = XX
Msg.ItemNumber8 = XX
Msg.ItemName8(XX) = XX

or

Dim Msg As New R_OMNI_LINK_MESSAGE
Msg.MessageLength = XX
Msg.MessageType = &H0B
Msg.ItemType16 = XX
Msg.ItemNumber16MSB = XX
Msg.ItemNumber16LSB = XX
Msg.ItemName16(XX) = XX
 
Last edited:
I am suddenly running into this error no matter what I do, it is the arrays I think.
"Could not load type 'Message' from assembly 'WindowsApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 1 that is incorrectly aligned or overlapped by a non-object field."

Your last question above - it should do (if you don't get the same error) but FieldOffset is only valid for LayoutKind.Explicit
 
Yup... I'm getting that error too for everything... :mad: :confused: :eek:

System.TypeLoadException: Could not load type 'FlashServer.Message' from assembly 'FlashServer, Version=1.0.2228.19435, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 2 that is incorrectly aligned or overlapped by a non-object field.

Damn! Because, everything seemed to be looking so good, until I tried to compile!
 
Last edited:
Until someone finds a fix...

I thought of a hack, why not use the data only structure?
From the Data byte array you can read messagetype in first byte, and from that knowledge 'throw' the rest of the bytes (messagelength - 1) into the appropriate sub-structure using Marshal PtrToStructure, and the other way around Marshal StructureToPtr to put some structure into Data.

VB.NET:
[SIZE=2]<StructLayout(LayoutKind.Sequential, Size:=256)> _
[/SIZE][SIZE=2][COLOR=#0000ff]Public[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff] Structure[/COLOR][/SIZE][SIZE=2] R_OMNI_LINK_MESSAGE_data[/SIZE]
[SIZE=2][COLOR=#0000ff] Public[/COLOR][/SIZE][SIZE=2] MessageLength [/SIZE][SIZE=2][COLOR=#0000ff]As [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE]
[SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=black] <MarshalAs(UnmanagedType.ByValArray, SizeConst:=255)> _
[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2][COLOR=#0000ff] Public[/COLOR][/SIZE][SIZE=2] Data() [/SIZE][SIZE=2][COLOR=#0000ff]As [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Byte[/COLOR][/SIZE][SIZE=2][COLOR=#008000]
[COLOR=#0000ff][COLOR=blue]End S[/COLOR]tructure[/COLOR][SIZE=2] [/SIZE][/COLOR][/SIZE][SIZE=2][COLOR=#0000ff][/COLOR][/SIZE]

 
Back
Top