|
Navigation: Programming Cookbook > External Interfacing > External Callbacks > ExternalCallback Objects |
![]() ![]()
|
The fundamental requirement is to be able to provide the address of a function that can be directly called by some external library using one of the standard calling conventions (stdcall or cdecl). We cannot directly provide the address of a Smalltalk CompiledMethod because:
| • | Dolphin's CompiledMethods do not contain raw machine code. |
| • | Smalltalk does not have the concept of 'static' methods (there must always be a receiver). |
| • | The memory address of a CompiledMethod is not fixed and may change during a garbage collect. |
| • | Synchronisation with the activities of the VM is required. |
| • | Conversion of arguments from raw data on the machine stack to Smalltalk objects on a Process stack is required. |
Consequently we need to wrap each callback in an object that includes:
| • | A receiver for the callback (and, optionally, any other context we might want to use in the callback) |
| • | An evaluable action to perform when the callback is received. |
| • | A description of the types of the arguments to the callback so that they may be converted to Smalltalk objects. For consistency this argument conversion is essentially the same as that performed by the external call interface primitives for converting return values from external functions to Smalltalk objects. |
| • | A machine code thunk (at a fixed, immovable, address) that calls a generic entry point in the VM passing it the address of the arguments in the stack and the id of the callback object. The VM can then perform one of its callbacks to pass these details into Smalltalk so that the external callback can be handled. |
This class of objects is called, not surprisingly, ExternalCallback. One typically creates an ExternalCallback by supplying a block to be evaluated, and the types of the arguments expected. For example here is a method from Font class:
fonts: aString do: operation
"Enumerate the fonts in a specified font family that are available
on the receiver's device. The triadic valuable argument, operation,
is passed the LOGFONT, TEXTMETRIC and font type as its
three arguments, and should answer true to continue the enueration,
false to terminate it (it must not contain a ^-return).
int CALLBACK EnumFontsProc(
lplf lplf, // pointer to logical-font data
lptm lptm, // pointer to physical-font data
DWORD dwType, // font type
LPARAM lpData // pointer to application-defined data
);"
| callback answer |
callback := ExternalCallback
block: [ :lplf :lptm :dwType :lpData | operation value: lplf value: lptm value: dwType ]
argumentTypes: 'LOGFONT* lpvoid dword dword'.
answer := GDILibrary default
enumFonts: self asParameter
lpFaceName: aString
lpFontFunc: callback asParameter
lParam: 0.
callback free.
^answer
This callback can be tested by evaluating the expression:
View desktop canvas fonts: nil do: [:lf :tm :type |
Transcript print: lf faceName; cr. true]
The expression will print the names of all available screen fonts to the Transcript.