Navigation:  Programming Cookbook > External Interfacing >

Parameter Types, Validation and Conversion

Previous pageReturn to chapter overviewNext page

The external method parameter and return types at the time of writing are:

bool

Boolean. As an argument type, accepts true (translated to 1) or false (translated to 0). Also accepts SmallInteger values, pushing their host machine representation. As a return type, if the result is 0 answers false, if the result is non-zero, answers true.

bstr

BSTR

BSTR, Basic String. A counted string type commonly used in conjunction with COM Automation objects. Accepts BSTRs, Strings or UnicodeStrings (for which a BSTR is allocated), or Integers assumed to be the address of a BSTR. As a return type answers an instance of BSTR referencing the basic string.

byte

Unsigned byte. Accepts SmallIntegers only. Passes a 32-bit value generated by zero extending the least significant byte. Fails if not in range 0..255. Zero extends into a positive SmallInteger when used as a return type.

char

Signed character. Accepts Characters only. The integer code point of the character is passed. As a return type answer the Character with code point corresponding to the integer return value.

date

DATE, COM date type, which is actually a 64-bit float. Validation as double.

double

64-bit floating point. Accepts instances of Float (which contain a host machine representation of a double precision floating point number). SmallIntegers may also be passed (they are promoted to the double precision floating point representation of their integral value). Following the Microsoft calling conventions, doubles are passed on the machine stack (not the FP stack) as 64-bit values. As a return type, answers an instance of Float.

dword

Unsigned double word (32-bits), accepts 32-bit Integers in the range -16r80000000..16rFFFFFFFF. Positive integers are passed as unsigned, and negative integers in their two's complement representation. The most negative LargeInteger, which can be passed, is -16r80000000 (or -2147483648) because this is the largest negative number that can be represented in 32-bits of two's complement notation. Also accepts byte objects of length 4, assumed to be in an unsigned bit representation. nil is passed as 0. As a return type, answers a SmallInteger, or a LargeInteger if the result cannot be represented as a positive SmallInteger (i.e. in 30 bits).

float

32-bit floating point. Accepts instances of class Float, or SmallIntegers (as double). The conversion of Floats (64-bit double precision) to float (32-bit single precision) may result in silent loss of precision. Floats are passed on the machine stack (not the FP stack) as 32-bit values. As a return type answers an instance of class Float (i.e. promotes to double precision).

guid

GUID, or UUID. As 16-byte pass or return by value structure.

handle

32-bit handle. Accepts 32-bit integers, nil, or a byte object of size 4. As a return type, answers an ExternalHandle, unless the returned handle is NULL, in which case answers nil. handle is a useful shortcut for specifying ExternalHandle as a pass or return by value struct type.

hresult

32-bit signed integer value. Validation as sdword. As a return type, if less than 0 (i.e. severity is error), causes the external call primitives to fail with a negative failure reason which is the HRESULT value. This is convenient (especially for OLE) because it means an HRESULTError exception is automatically generated when an external function returns an HRESULT error.

lpstr

char*

Pointer to C (null-terminated) ASCII string type. Accepts null-terminated byte objects (e.g. Strings, Symbols) or nil (null pointer). When used as a return type, answers a String containing the characters of the C string up to the null terminator. Unlike lpvoid, does not accept integer values as pointers, or ExternalAddress (indirection) objects. If this validation is too tight for your requirements, then use lpvoid. Do not use this return type where an external function is called which expects the caller to assume ownership of the returned string and to delete it when it is no longer required, as a memory leak will result (use lpvoid instead and provide appropriate memory management code).

lpwstr

Pointer to null-terminated wide (Unicode) string. Primarily present as a placeholder for a future Unicode version of Dolphin, and is currently synonymous with lpstr.

lppvoid

void**

Pointer to pointer. Used for functions that take a parameter into which they write an address. The corresponding argument must be an ExternalAddress (or other indirection object), or an object whose first instance variable is such (e.g. an ExternalStructure). The address of the ExternalAddress itself is passed (i.e. the argument is passed by reference), so that on return it contains the address written back by the external function. nil is not a valid argument value. As a return type answers a pointer instance of LPVOID (i.e. void**) containing the address returned from the function.

lpvoid

void*

General pointer type, accepts byte objects e.g. Strings (pointer to contents passed), nil (null pointer), SmallIntegers (passes as address), or ExternalAddresses (the contained address is passed, not a pointer to the ExternalAddress object). Where the pointer is captured by the external function, care should be taken to ensure that the object whose address was passed is not garbage collected. When used as a return type, the method answers an ExternalAddress with the returned value.

oop

Reserved for future use.

ote

Reserved for future use

qword

Unsigned quad word. Similar to dword, but 64-bit (i.e. the range is -16r8000000000000000, the largest 64-bit two's complement negative integer, up to 16rFFFFFFFFFFFFFFF the largest positive unsigned 64-bit integer). 8-byte objects are acceptable and assumed to contain the correct unsigned bit representation. nil is passed as 0.

sbyte

Signed byte. Accepts SmallIntegers only. Passes a 32-bit value generated by sign extending the least significant byte. Fails if not in range -128..127. Sign extends into a positive or negative SmallInteger when used as a return type.

sdword

Signed double word, accepts any Integer in the range -16r80000000..16r7FFFFFFF (i.e. Integer's with a 32-bit two's complement representation - all SmallIntegers and 4-byte LargeIntegers). May also be other byte objects of length 4, which are assumed to contain a 2's complement 32-bit number. As a return type answers a SmallInteger, or a LargeInteger if more than 31-bits are required to represent the two's complement result depending on sign. Also accepts nil (passed as 0).

sqword

Signed quad word. Accepts any Integer in the range which can be represented as a two's complement number in 64 bits (i.e. -16r8000000000000000 to 16r7FFFFFFFFFFFFFFF). Also accepts 8 byte objects, which are assumed to contain 64-bit two's complement numbers. nil is passed as 0. As a return type answers the smallest Integer form that can contain the 64-bit two's complement integer.

sword

Signed word. As sbyte, but 16-bit, acceptable range -32768..32767. Also accepts a byte object of size 2, which is sign extended to 32 bits.

word

Unsigned word. As byte, but 16-bit, acceptable range 0..65535. Also accepts a byte object of size 2, which is zero extended to 32 bits.

variant

COM Automation VARIANT type. Passes by value as a 16-byte structure (i.e. 16-bytes are pushed on the stack). Accepts 16-byte ExternalStructures or byte objects, or indirection objects (integers, ExternalAddresses) treated as references to a VARIANT and automatically indirect.

varbool

COM Automation VARIANT_BOOL type. As bool but true is passed as the 16-bit negative integer –1 (i.e. 0xFFFF).

void

Only valid as a return type - the method answers self.

<struct>

Where struct is an ExternalStructure class name. Structure passed by value. Accepts only the exact matching structure class. Again, the ExternalStructure arguments may be reference/pointer instances. Note that it is very important to define the associated structure correctly; as if it has an incorrect size an unrecoverable stack fault is the likely result when passing by value. When used as a return value, an instance of the ExternalStructure class is answered, with the bytes of the returned structure as its contents (copied into a ByteArray). Such newly created instances are created directly by the VM, and thus subsidiary initialisation may be necessary. We suggest performing any such initialisation in a wrapper function. Note that the calling conventions for returning structures by value vary depending on whether the structure is 4, 8, or >8 bytes long, with only the latter being returned on the stack. 4 and 8 byte structures are returned quite efficiently in registers.

<struct>*

Where struct is an ExternalStructure class name. This is equivalent to lpvoid as a argument type - no validation is currently performed. When used as a return type, a pointer instance of the specified ExternalStructure class is answered, containing an ExternalAddress pointing at the externally stored value as its first instance variable. Note that the ExternalStructure is instantiated directly by the VM, and will have the correct form, but may not be correctly initialised. Any subsidiary initialisation required is best performed in a wrapper function.

<N>

Where N is the byte size of a pass-by-value structure of unspecified type (check carefully to make sure the specified size is correct). Accepts either byte objects (of the correct size) or ExternalStructure instances with the correct byte size (or other classes with the same shape as ExternalStructures). ExternalStructures passed to such arguments can be reference instances (i.e. ones containing a pointer to the actual structure bytes, rather than the structure bytes themselves). As a return type, the result is a ByteArray of the specified size.

Additional types may be added from time to time, if they are of sufficient utility. Please check ExternalStructure's class comment for the latest details of all supported external call types.

Integer Range Checks

It should be noted that external call primitives fail when the actual argument for an integer typed argument is out of range. It is considered more within the spirit of Smalltalk to generate an error when something is out of range, than to silently truncate it. Indeed, for signed types in particular, truncation is unlikely to produce the correct result. In the case of return values, it is often the case, particularly with Win32™ routines (which are often coded in assembler in Windows95™), that a function which is specified as returning a WORD value, actually returns a DWORD value (as the return value is in EAX, and the function may not clear the high order 16-bits), so in this case silent truncation must occur.

The treatment of the unsigned integer dword and qword types is slightly inconsistent with other unsigned integer types, as they do not insist that their arguments are positive. This is because no promotion is necessary (i.e. there is no need to zero/sign extend) so this may offer more flexibility without being a significant source of errors. Experience has shown this approach to be useful.

Passing Null or Zero

In general, the UndefinedObject, nil, is interchangeable with 0, or NULL, at least when interfacing with external library functions. 'Nullness' can be tested with the #isNull message, with the UndefinedObject and SmallInteger zero answering true. The lpstr argument type is an exception to this, in that it only accepts nil as the null pointer, not zero. This is designed to catch a wider range of type errors for string arguments, and can be avoided by simply using the looser lpvoid type if desired.

Invalid External Calls

If an external call fails a walkback will occur describing which argument was incorrect. The error specifies what type of argument was actually passed, and which external type (e.g. dword) was expected. When the external call primitive fails, the #invalidCall method of ExternalLibrary (or ExternalStructure in the case of a failing COM function invocation) an InvalidExternalCall exception is invoked, whereupon it which examines the primitive failure details for the process, and generates an appropriate exception. The exception will normally be an instance of InvalidExternalCall, but in the case of a method returning a negative HRESULT (i.e. an error code) an HRESULTError exception will be raised instead.

Should the default error detection and handling prove inadequate or inappropriate, then one or other #invalidCall method can be overridden on a library or COM interface specific basis.

Another type of external call failure that one is quite likely to encounter, particularly during the early stages of interfacing to an external library, is Access Violations, or General Protection Faults. Dolphin will attempt to trap these and display a walkback. Frequently these are recoverable, particularly if resulting from the inability to read an area of memory, and one can continue regardless. One should bear in mind, however, that memory corruption might have occurred, and the image may be damaged in a way that is not immediately obvious. Consequently one must be particularly careful to maintain good image backups when doing external interfacing work.

Saving a corrupt image is possible after a GPF, but thankfully quite unlikely because a full compacting garbage collection is performed before the image save is initiated, and because this has to walk the entire object memory it typically stumbles on any corruption and fails. Such failures to complete a garbage collection show up as failure in #primitiveCollectGarbage, usually with the code 2, and when this is encountered it is wise to abandon the image and start again from the last known good state.