In addition to the record wrapper (AbtForeignOSObject subclass) technique, you can also use the "generic" record structure class AbtRecord to pass parameters to an external function. You might want to do this if your IBM Smalltalk code needs to interact with an application built using VisualAge. The VisualAge visual External Function parts use the AbtRecord technique instead of the record wrapper technique for external function calls.
If you use AbtRecord record structures instead of record wrappers, you must do several things differently:
anAbtRecord := (anObjectTable dataStructures at: 'STRUCT_NAME' asSmalltalkGlobalIdentifier) newRecord.
When a field is added to an AbtCompoundType instance, the field offset will be calculated based upon the field type and the instance's alignmentBoundary attribute. The default alignmentBoundary is 1. You can choose an alignmentBoundary of 1, 2, 4, or 8 depending upon the desired byte alignment.
Note: | The alignmentBoundary must be set prior to adding the field. |
You must understand the alignment options used when when building your DLL and adjust alignmentBoundary accordingly, so that VisualAge and the DLL map memory in the same way.
VisualAge will align data in the following manner:
Table 1. VisualAge data alignment using alignmentBoundary:
Data Type | 1 | 2 | 4 | 8 |
char | 1 | 1 | 1 | 1 |
short | 1 | 2 | 2 | 2 |
int, long | 1 | 2 | 4 | 4 |
float, double | 1 | 2 | 4 | 4 |
pointer | 1 | 2 | 4 | 4 |
struct | 1 | 2 | 4 | 8 |
The actual alignment boundary used is the lesser of the alignmentBoundary value and the sizeInBytes of the data item. Structures are always aligned according to the alignmentBoundary value.
This is best seen in an example. Consider the following C structure:
struct abc { char x ; long y ; }
This structure can be created in VisualAge using the following code:
AbtCompoundType new alignmentBoundary: b; addField: AbtCCharField new name: 'x'; addField: AbtCLongField new name: 'y'.
The offset of y will vary with the value of b.
anAbtRecord := (anObjectTable cobol01s at: 'STRUCT_NAME' asSmalltalkGlobalIdentifier) newRecord.
anAbtRecord value: 'Input Value'.
anAbtRecord at: 'inputParm1' put: 'Input Value 1'. anAbtRecord at: 'inputParm2' put: 'Input Value 2'.
anItem := anAbtRecord at: #inputParm subscript: 3 put: 'Input Value'.
aPlatformFunction coroutineCallWith: anAbtRecord abtAsExternalPassedPointer.
To try using this technique with the sample, type the following example code in the System Transcript window, select it, and run it. You should get the same results on the System Transcript window that you saw in the earlier examples.