Main Page | Features | Central Services | csv-Files | Types | Transfer | Access | API-C | API-.NET | API-Java | Examples | Downloads
page generated on 18.05.2024 - 04:45
Using Tagged Structures in Visual Basic 6

Tagged structures in Visual Basic are in close analogy with their counterparts in C. The principal caveates include

  • In Visual Basic 6.0, structures (i.e. 'Type's) fundementally have 4-byte alignment. Thus a 'Single' followed by a 'Double' will likely leave a 4-byte gap in the real underlying structure but nonetheless report '12 bytes' when the 'Len' function is used. So one should always add an additional 'reserved' or 'spare' Single in the Type definition so as to reflect this fact and avoid confusion (i.e. 2 Singles followed by a Double will not have a hidden gap).
  • String fields should be declared as arrays of bytes and converted to and from strings when needed. Visual Basic 6.0 uses unicode exclusively whereas the underlying libraries use ascii.
  • Fields which are arrays (String are arrays of bytes!) should remember that Visual Basic array declarations specify the minimum and maximum indicies and NOT an array length.
    The minimum index if often omitted and is understood to be '0' in such a case. Thus

    Dim a(10)

    declares an array with indicies 0 to 10, an array length of 11 and not 10!
  • The ActiveX controls (srv.ocx or acop.ocx) that you will likely use are based on OLE, which does not understand user-defined types. So you cannot simply pass a reference to a tagged structure type in any of the API calls that expect a data object. Instead you will have to pass the initial field in the initial element (if you are dealing with an array of structures).

An in C, the first step is to register the structure. You do this at initialization time by declaring a structure tag name and filling in the sizes and locations of the individual elements. Then when a client sends or asks for a structure and it sends the structure tag along with the request, the server knows all about the structure and how to handle it.

In Visual Basic this might look like

' make sure tine.bas is included in the project !
Option Explicit
Private Const NUM_DEVICES = 10
Private Const NUM_VALUES = 1024 * 8
Private Type SineInfo
amplitude As Single
frequency As Single
noise As Single
phase As Single
numberCalls As Long
Description(63) As Byte
End Type
Dim sineInfoTable(NUM_DEVICES - 1) As SineInfo
'other variables omitted ...
Private Sub Form_Load()
' other initialization omitted ....
Call RegisterStructs
' other initialization omitted ....
End Sub
Sub RegisterStructs()
Dim offset As Long
' this must follow the order of the structure explicitly!
' note: in VB the alignment is always 4-bytes (do not leave alignment gaps!)
' offset is then always the accumulated size in bytes (Len) of the structure fields
AddFieldToStruct "SineInfo", offset, 1, CF_FLOAT, "amplitude"
offset = offset + Len(sineInfoTable(0).amplitude)
AddFieldToStruct "SineInfo", offset, 1, CF_FLOAT, "frequency"
offset = offset + Len(sineInfoTable(0).frequency)
AddFieldToStruct "SineInfo", offset, 1, CF_FLOAT, "noise"
offset = offset + Len(sineInfoTable(0).noise)
AddFieldToStruct "SineInfo", offset, 1, CF_FLOAT, "phase"
offset = offset + Len(sineInfoTable(0).phase)
AddFieldToStruct "SineInfo", offset, 1, CF_LONG, "numberCalls"
offset = offset + Len(sineInfoTable(0).numberCalls)
AddFieldToStruct "SineInfo", offset, 64, CF_TEXT, "Description"
' terminate the registration :
SealTaggedStruct "SineInfo", Len(sineInfoTable(0)), NUM_DEVICES
End Sub

A Visual Basic server then might make use of something like the following within its equiment module dispatch event handler:

Private Sub Srv1_EqpFcn(ByVal DevName As String, ByVal devProperty As String, ByVal outArrayLen As Long, ByVal inArrayLen As Long, ByVal devAccess As Integer)
Dim devnr As Integer, i As Integer, cc As Integer
Dim sinf As SineInfo
devnr = Srv1.EqpGetModuleNumber(DevName)
If (devnr < 0 Or devnr >= NUM_DEVICES) Then
Srv1.EqpSetCompletion illegal_equipment_number, ""
Exit Sub
End If
Select Case devProperty
Case "Sine":
If (devAccess And CA_WRITE) = CA_WRITE Then
Srv1.EqpSetCompletion illegal_read_write, ""
Exit Sub
End If
Srv1.EqpSendData sinbuf(0, devnr)
sineInfoTable(devnr).numberCalls = sineInfoTable(devnr).numberCalls + 1
' other properties omitted ...
Case "SineInfo":
If (inArrayLen > 0) Then
If (devAccess And CA_WRITE) <> CA_WRITE Then
Srv1.EqpSetCompletion illegal_read_write, ""
Exit Sub
End If
' allow an atomic set of all relevant values this way ...
cc = 0
Srv1.EqpRecvData sinf.amplitude
If (sinf.amplitude < 1 Or sinf.amplitude > 1000) Then cc = out_of_range
If (sinf.frequency < 1 Or sinf.frequency > 100) Then cc = out_of_range
If (sinf.phase < 1 Or sinf.phase > 512) Then cc = out_of_range
If (sinf.noise < 1 Or sinf.noise > 100) Then cc = out_of_range
If cc <> 0 Then
Srv1.EqpSetCompletion cc, ""
Exit Sub
End If
sineInfoTable(devnr) = sinf
End If
If (outArrayLen > 0) Then
Srv1.EqpSendData sineInfoTable(0).amplitude
End If
Case Else:
Srv1.EqpSetCompletion illegal_property, ""
Exit Sub
End Select
Srv1.EqpSetCompletion 0, ""
End Sub

A Visual Basic client application would access a user-defined structure using code something like the following:

' Example using an ACOP control to get an array of structures ...
Dim sininf(9) As SineInfo
sub GetSineInfo()
Dim rc As Integer
ACOP1.DeviceContext = "TEST"
ACOP1.DeviceGroup = "SineServer"
ACOP1.DeviceName = "SineGen0"
ACOP1.AccessRate = 1000 '
ACOP1.AccessMode = "READ"
ACOP1.DeviceProperty = "SineInfo"
rc = ACOP1.Execute(sininf(0).amplitude, 10, "STRUCT/SineInfo")
End Sub

Impressum   |   Imprint   |   Datenschutzerklaerung   |   Data Privacy Policy   |   Declaration of Accessibility   |   Erklaerung zur Barrierefreiheit
Generated for TINE API by  doxygen 1.5.8