The <dll.h> Header File

Declarations for creating and using nostub libraries

Language Extensions

DLL_EXPORTS
Exports symbols from the DLL.
DLL_ID
Defines the DLL identification number.
DLL_IMPLEMENTATION
Ends the DLL interface section.
DLL_INTERFACE
Starts the DLL interface section.
DLL_VERSION
Defines the DLL major and minor version numbers.

Functions

_DLL_call_attr
Constructs a function definition with attributes for a DLL call.
_DLL_call
Constructs a function definition for a DLL call.
_DLL_entry
Returns a pointer to a symbol exported from a DLL.
_DLL_glbvar
Returns a dereferenced pointer to a global variable in a DLL.
_DLL_reference
Returns a pointer to a global variable in a DLL.
LoadDLL
Loads a DLL into memory and prepares it for use.
LoadDLLThrow
Loads a DLL into memory, throwing an error if not successful.
UnloadDLL
Unloads a previously loaded DLL.

Predefined Types

DLL_ErrorCodes
An enumeration to describe possible errors when trying to load a DLL.

See also: How to use DLLs


DLL_EXPORTS

Exports symbols from the DLL.

The DLL_EXPORTS macro is used for defining the list of symbols which will be exported from the DLL. Note that just putting a symbol declaration or a function prototype into the DLL interface section does not mean that this symbol or function will automatically be exported: you need to put the names of all objects which should be exported after the DLL_EXPORTS interface item.

DLL_EXPORTS must be followed by a list of symbols which should be exported from the DLL. It is possible to exports function names, array names, or addresses of global variables (i.e. names of global variables preceded by the address operator '&'). More precisely, all types of pointers may be exported; functions and arrays are always referenced automatically. For example, suppose that the interface section contains the following declarations:

int foofunc (int fooarg1, const char *fooarg2);
char foostring [] = "Foo";
int fooarray [5] = {1, 2, 3, 4, 5};
unsigned short fooglbvar;

Then, to export these symbols from the DLL, use:

DLL_EXPORTS foofunc, foostring, fooarray, &fooglbvar

Each item in the DLL_EXPORTS declaration is assigned an index number, which is important later for importing symbols from the DLL (see _DLL_call, _DLL_call_attr, _DLL_reference, and _DLL_glbvar for info about how to import symbols from the DLL). The first item gets the index number 0, the second gets the index number 1, etc. So, in the above example, foofunc, foostring, fooarray, and fooglbvar get the index numbers 0, 1, 2, and 3, respectively.

DLL_EXPORTS must appear at the very end of the DLL interface section, just after the DLL_VERSION entry, and just before the DLL_IMPLEMENTATION delimiter, which terminates the DLL interface section and starts the implementation section. See DLL_INTERFACE for a detailed layout of the DLL interface structure.

See also: DLL_INTERFACE, _DLL_entry


DLL_ID

Defines the DLL identification number.

The DLL_ID macro is used for defining the DLL indentification number. It must be followed by a 32-bit constant unsigned integer, which will be embedded in the DLL image as a part of the internal signature. For example:

DLL_ID 372331723

Later, when a program wants to load the DLL, it passes the expected ID number as a parameter to the LoadDLL function. If the expected and embedded ID numbers are not the same, the DLL is simply ignored (skipped). As LoadDLL searches through all folders for a matching DLL, it is completely legal to have several DLLs with the same name (in different folders) which differ only in their ID numbers.

DLL_ID must appear at the end of the DLL interface section, after all prototypes and global declarations, and just before the DLL_VERSION entry. See DLL_INTERFACE for a detailed layout of the DLL interface structure.

See also: DLL_INTERFACE, LoadDLL


DLL_IMPLEMENTATION

Ends the DLL interface section.

This macro ends the DLL interface section. Any symbol declared after it cannot be exported from the DLL. Usually, the implementation part contains the bodies of all exported functions, and possible some more (helping) functions. See DLL_INTERFACE for more information about the structure of the interface section.

See also: DLL_INTERFACE


DLL_INTERFACE

Starts the DLL interface section.

The DLL_INTERFACE macro starts the DLL interface section and tells the linker that a DLL image should be produced instead of the standard executable image. The DLL interface section must have the following general layout:

DLL_INTERFACE

// Declarations of global symbols which should be exported,
// and prototypes of functions which should be exported.

DLL_ID identification_number
DLL_VERSION major_version_number, minor_version_number
DLL_EXPORTS list_of_exported_symbols

DLL_IMPLEMENTATION

// Now the implementation follows

In principle, in addition to function prototypes, it is legal to put function implementations in the DLL interface section as well, although this is not a good practice from the aspect of readability and good structuring. So, a well-designed interface should contain only prototypes.

Note that just putting a symbol declaration or a function prototype into the DLL interface section does not mean that this symbol or function will automatically be exported: you need to put the names of all objects which should be exported after the DLL_EXPORTS interface item. Also, note that DLL_IMPLEMENTATION ends the interface section, so any symbol declared after it cannot be exported from the DLL.

See also: DLL_ID, DLL_VERSION, DLL_EXPORTS, DLL_IMPLEMENTATION


DLL_VERSION

Defines the DLL major and minor version numbers.

The DLL_VERSION macro is used for defining the DLL major and minor version numbers. It must be followed by two 16-bit constant unsigned integers, which represent the major and minor version numbers, respectively. For example:

DLL_VERSION 2,13

Later, when a program wants to load the DLL, it passes the expected major and minor version numbers as parameters to the LoadDLL function. If the expected and actual major version numbers are not the same, or if the expected minor version number is greater than actual minor version number, the DLL is simply ignored (skipped). As LoadDLL searches through all folders for a matching DLL, it is legal to have several DLLs with the same name (in different folders) and with the same ID numbers, but with different version numbers. LoadDLL will load the first DLL found (if any) with a matching name, ID number and major version number, and whose minor version number is greater or equal to the expected minor version number.

DLL_ID must appear at the end of the DLL interface section, just after the DLL_ID entry, and just before the DLL_EXPORTS entry. See DLL_INTERFACE for a detailed layout of the DLL interface structure.

See also: DLL_INTERFACE, LoadDLL


_DLL_call_attr

#define _DLL_call_attr(type,args,attr,index) (*(type (* attr) args) _DLL_entry (index))

Constructs a function definition with attributes for a DLL call.

_DLL_call_attr gets a void pointer to the location of the index-th exported symbol of the currently loaded DLL (using _DLL_entry), casts this pointer to a temporarily-defined pointer to a function which returns type, requires args as arguments, and has the attributes defined by attr, and then dereferences it. _DLL_call_attr is very similar to _DLL_call, but allows for defining function attributes, too. For example, suppose that the DLL contains the following declarations in the DLL interface section (see DLL_INTERFACE):

void foo (int, const char *) __attribute__((__regparm__(4)));
int bar (long) __ATTR_STD__;
...
DLL_EXPORTS foo, bar

Then, to 'import' foo and bar from the DLL (assuming that it has been loaded sucessfully using LoadDLL), you should use the following defines:

#define foo _DLL_call_attr (void, (int, const char *), 0, __attribute__((regparm(4))))
#define bar _DLL_call_attr (int, (long), 1, __ATTR_STD__)

See also: _DLL_call, _DLL_reference, _DLL_glbvar, Declaring Attributes of Functions


_DLL_call

#define _DLL_call(type,args,index) (*(type(*)args) _DLL_entry (index))

Constructs a function definition for a DLL call.

_DLL_call gets a void pointer to the location of the index-th exported symbol of the currently loaded DLL (using _DLL_entry), casts this pointer to a pointer to a function which returns type and which requires args as arguments, and dereferences it. This macro is usually used for accessing functions exported from the DLL. For example, suppose that the DLL contains the following declarations in the DLL interface section (see DLL_INTERFACE):

void foo (int, const char *);
int bar (long);
...
DLL_EXPORTS foo, bar

Then foo will get the index number 0, and bar will get the index number 1. To call foo from the main program (assuming that the DLL has been loaded sucessfully using LoadDLL) with arguments 123 and "foostr", you can principally do

_DLL_call (void, (int, const char *), 0) (123, "foostr");

But this is awkward, of course. So, to 'import' foo and bar from the DLL, you should use the following defines:

#define foo _DLL_call (void, (int, const char*), 0)
#define bar _DLL_call (int, (long), 1)

Then you can use foo and bar just as normal functions defined in the main program. For example:

foo (123, "foostr");

Principally, _DLL_call is quite similar to _rom_call, which constructs TIOS function calls instead of DLL function calls. But note that index in _DLL_call is an ordinary integer, which is not true for _rom_call.

See also: _DLL_call_attr, _DLL_reference, _DLL_glbvar


_DLL_entry

void *_DLL_entry (int index);

Returns a pointer to a symbol exported from a DLL.

_DLL_entry returns a void pointer to the location of the index-th exported symbol of the currently loaded DLL (assuming that it has been loaded sucessfully using LoadDLL). Note that _DLL_entry returns garbage if the DLL is not loaded.

This function is used internally in _DLL_call, _DLL_call_attr, _DLL_reference, and _DLL_glbvar.


_DLL_glbvar

#define _DLL_glbvar(type,index) (*(_DLL_reference (type, index)))

Returns a dereferenced pointer to a global variable in a DLL.

_DLL_glbvar gets a void pointer to the location of the index-th exported symbol of the currently loaded DLL (using _DLL_entry), casts this pointer to a pointer of type type, and then dereferences it. As the constructed object is a dereferenced pointer, it is a valid lvalue, so it can be used as a global variable. This macro is usually used for accessing global variables exported by the DLL. For example, suppose that the DLL contains the following declarations in the DLL interface section (see DLL_INTERFACE):

unsigned short foo;
...
DLL_EXPORTS &foo

Then foo will get the index number 0. To 'import' this global variable from the DLL (assuming that the DLL is sucessfully loaded using LoadDLL), you should use the following definition:

#define foo _DLL_glbvar (unsigned short, 0)

Then you can use foo just like a normal global variable. For example:

printf ("%d", foo);
foo = 50;

See also: _DLL_call, _DLL_call_attr, _DLL_reference


_DLL_reference

#define _DLL_reference(type,index) ((type*const) _DLL_entry (index))

Returns a pointer to a global variable in a DLL.

_DLL_reference gets a void pointer to the location of the index-th exported symbol of the currently loaded DLL (using _DLL_entry), then casts this pointer to a constant pointer of type type. This macro is usually used for accessing arrays exported from the DLL. For example, suppose that the DLL contains the following declarations in the DLL interface section (see DLL_INTERFACE):

char foo[] = "FooString";
int bar[5] = {10, 20, 30, 40, 50};
...
DLL_EXPORTS foo, bar

Then foo will get the index number 0, and bar will get the index number 1. To 'import' these two arrays from the DLL (assuming that the DLL has been loaded sucessfully using LoadDLL), you should use the following defines:

#define foo _DLL_reference (const char, 0)
#define bar _DLL_reference (int, 1)

Then you can use foo and bar just like normal arrays defined in the main program. Note that the elements of foo cannot be changed due to the const qualifier in the definition of foo (at least writing to it will trigger a warning). However, you can access the elements of bar. For example:

printf ("%s %d", foo, bar[2]);
bar[2] = 100;
printf (" %d", bar[2]);

See also: _DLL_call, _DLL_call_attr, _DLL_glbvar


LoadDLL

short LoadDLL (const char *VarName, unsigned long ID, unsigned short Major, unsigned short Minor);

Loads a DLL into memory and prepares it for use.

LoadDLL tries to load a DLL into memory and to prepare it for use. It traverses the complete VAT to find a fitting version of the DLL (so it is not necessary for the DLL to be in the same directory as the main program). The DLLName parameter is the standard ANSI C name of the DLL file which is to be loaded. The ID parameter is a DLL identification number introduced for safety reasons: any file which does not have the extension 'DLL', a valid embedded signature and an embedded identification number which is equal to ID is ignored (the identification number in the DLL itself is set using the DLL_ID entry in the DLL interface section). The parameters Major and Minor are the expected major and minor version numbers of the DLL (the actual major and minor version numbers in the DLL itself are set using the DLL_VERSION macro in the DLL interface section). LoadDLL will refuse to load a DLL if the expected and actual version numbers are not the same, or if the expected minor version number is greater than the actual minor version number. As LoadDLL searches trough all folders for a matching DLL, it is completely legal to have several DLLs with the same name (in different folders), but which differ in their ID numbers or version numbers. LoadDLL will load the first DLL found (if any) with a matching name, ID number and major version number, and whose minor version number is greater or equal to the expected minor version number.

LoadDLL returns one of the following values:

DLL_OK The DLL was loaded and initialized successfully.
DLL_NOTINGHOSTSPACE The DLL could not be loaded because the program counter is not in the "ghost space" (the virtual address space where HW2 protections do not take effect). Note that exe-packed programs are always executed in the ghost space; if you do not want to compress your program, you need to define EXECUTE_IN_GHOST_SPACE.
DLL_NOTFOUND The DLL is not found. This means that either a file with the name DLLName is not found in any folder, or such files exist, but none of them has the correct extension ('DLL'), the correct embedded signature, and the correct identification number (determined by the ID parameter).
DLL_LOCKFAILED The attempt to lock the DLL in memory has failed due to some strange reason. This error code is very unlikely.
DLL_OUTOFMEM There is not memory to load the DLL into RAM.
DLL_ALREADYLOADED There is already another DLL loaded in the RAM. Due to efficiency reasons, only one DLL is allowed to be loaded at the same time. You need to unload the current DLL using UnloadDLL before loading another one. Anyway, using more than one DLL is strongly deprecated if you don't know exactly what you are doing and why you are doing so.
DLL_WRONGVERSION There is at least one valid DLL file with the name DLLName and with the correct extension, signature, and identification number, but none of them has a major version number which is equal to the expected major number (determined by the Major parameter) and a minor version number which is greater or equal than the expected minor number (determined by the Minor parameter).

Only if LoadDLL returns DLL_OK, it is valid to proceed further and to use functions imported from the DLL. No further checking is done by the functions from the DLL, so your program will definitely crash if you try to call any function from the DLL if LoadDLL has not returned DLL_OK. Also, don't forget to call UnloadDLL when the DLL is not needed any more (usually at the end of the program). So, any program which uses DLLs must have the following code fragment:

if ((status = LoadDLL (DLLName, ID, Major, Minor)) != DLL_OK)
{
  // Perform some kind of error processing
  // and terminate the program in some way
}

// Proceed further

UnloadDLL ();

You may use LoadDLLThrow instead, which throws an error instead of returning an error code, but this means that you cannot determine what caused the problem. As another solution, you may use ER_throw from error.h to throw your own error if loading the DLL fails.

Typically, if you use functions which may throw errors, you can either catch these errors using TRY...ONERR...ENDTRY blocks, or, if you want to pass them on to the AMS, you can use a single TRY...FINALLY...ENDFINAL block:

if ((status = LoadDLL (DLLName, ID, Major, Minor)) != DLL_OK)
{
  // Perform some kind of error processing
  // and terminate the program in some way
}
TRY

  // Proceed further

FINALLY
  UnloadDLL ();
ENDFINAL

Or, using LoadDLLThrow:

LoadDLLThrow (DLLName, ID, Major, Minor);
TRY

  // Proceed further

FINALLY
  UnloadDLL ();
ENDFINAL

See also: UnloadDLL, LoadDLLThrow, _DLL_call, _DLL_call_attr, _DLL_reference, _DLL_glbvar


LoadDLLThrow

void LoadDLLThrow (const char *VarName, unsigned long ID, unsigned short Major, unsigned short Minor);

Loads a DLL into memory, throwing an error if not successful.

LoadDLLThrow calls LoadDLL, and throws an error if the result is not DLL_OK. This is useful if you want to display an error message on the Home Screen.

See also: LoadDLL, UnloadDLL, error.h


UnloadDLL

void UnloadDLL (void);

Unloads a previously loaded DLL.

UnloadDLL unloads the previously loaded DLL and frees the memory used by it. It does nothing if the DLL was not previously loaded. Each program which uses DLLs must execute UnloadDLL at the end, else the memory used by the DLL will not be freed. After unloading the DLL, it is legal to load another DLL. It is even legal to load and unload the same DLL several times in the program (if you know exactly what you are doing and why you are doing so).

See also: LoadDLL


DLL_ErrorCodes

enum DLL_ErrorCodes {DLL_OK, DLL_NOTINGHOSTSPACE, DLL_NOTFOUND, DLL_LOCKFAILED, DLL_OUTOFMEM, DLL_ALREADYLOADED, DLL_WRONGVERSION};

An enumeration to describe possible errors when trying to load a DLL.

This enumeration describes possible values returned from the LoadDLL function. The value DLL_OK means correct loading. All other values indicate that something is wrong. See LoadDLL for the meaning of these codes.


Return to the main index