Declarations for creating and using nostub libraries
See also: How to use DLLs
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
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
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
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
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
#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
#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
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.
#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
#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
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). |
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
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
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
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.