Software systems do not exist in isolation, but need to interface with other systems, and make use of pre-existing software components and services. Commonly interfaces to external system, services, and components are supplied in shared libraries (DLLs in Windows™). A means of calling the functions in such shared libraries from Dolphin is required.
Represent each shared library with a Dolphin class which manages loading and accessing that library, and which includes a method for each function of the library one wishes to call. Each external library class has a singleton instance which is then sent messages to invoke the external functions. This scheme fits neatly into the Smalltalk message passing paradigm, and allows for polymorphic behaviour between libraries too.
WinMMLibrary is the representative of WinMM.DLL, the Windows™ multimedia extensions library. Its class #filename method is simply:
fileName "Answer the file name of the external library which the receiver represents." ^'WinMM'
We can play sounds via WinMMLibrary if we implement the PlaySound() functions:
playSound: aString hmod: anExternalHandle fdwSound: anInteger "Plays a sound specified by the given filename, resource, or system event. A system event may be associated with a sound in the registry. Answers whether successful. BOOL PlaySound(LPCSTR pszSound, HMODULE hmod, DWORD fdwSound);" <stdcall: bool PlaySoundA lpvoid handle dword> ^self invalidCall
As you can see there is a simple and fairly direct mapping between the function prototype and the Dolphin external call specification.
Sound has a simple wrapper for this method:
play: flags "Private - Play the receiver with the specified flags. Answer whether it succeeded." ^WinMMLibrary default playSound: name hmod: location fdwSound: flags
Sound then provides even simpler messages, such as:
woofAndWait "Play the receiver, waiting for the woof to finish" | flags | flags := (type bitAnd: ##(SND_ASYNC bitInvert)) bitOr: SND_SYNC. self play: flags
Any particular external library may contain a large number of functions. Implementing them all may consume quite a lot of memory in unused methods, and may take a lot of time. It is advisable to implement as needed and employ a naming convention to avoid duplication.
Where external functions capture the addresses of objects passed to them, those objects must be allocated from the fixed memory space, as otherwise they may be moved by the memory manager during a garbage collection, invalidating the address. The fixed memory space carries more overhead than the normal object spaces.
Where external functions capture the addresses of objects passed to them, a reference to those objects must be maintained in the image to prevent them from being garbage collected while the external function is still using them.
External interfaces frequently define structured data types, and these must be defined in Dolphin too if the interface is to be used successfully (see External Interfacing).
There are quite a few examples of external library interface classes in the base system, for example the core set of Windows™ libraries all have representatives (KernelLibrary, UserLibrary, GDILibrary), and these contain large numbers of functions. In fact Dolphin performs almost all interfacing with the operating system directly from Smalltalk; very little is hidden in primitives.