Navigation:  Programming Cookbook > External Interfacing > External Callbacks >

ExternalCallback Objects

Previous pageReturn to chapter overviewNext page

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.