This document was published by Handheld Basic and has been taken from their website at http://www.handheld-basic.com. The embedded links in this distribution file are not active. Please access this page at their website to obtain the active links or more information from Handheld Basic. Scanning Devices Inc. thanks Handheld Basic for the use of this material.
![]() | ![]() |
Home > Programmer's Guide > Advanced Issues
Shared Libraries
The HB++ language allows easy access to functions located in a shared library using the LoadLibrary and RemoveLibrary functions and the Declare statement. A shared library can be provided by a third party, or written specifically with MetroWorks CodeWarrior™ or GNU C Compiler. You cannot write a shared library with the current version of HB++.
Shared Library Functionality
A library is an executable file, stored in a resource database. But unlike a standard application, a library cannot be launched directly. It provides a table of functions to the system, which other applications can access to call the routines contained in the library. So that the system can distinguish these libraries, they should have the type "libr".
Before being able to use it, a library must be explicitly loaded. This operation is carried out using the LoadLibrary function, which returns a unique identifier called the reference number. This identifier allows the exported library functions to be called, and must be stored in a global variable of type Integer.
Each exported function is identified by a trap number whose value is greater than &HA800. Technically, the 16 least significant bits of this trap number are used as an index in the exported functions table. In addition, the first parameter of all exported functions must be of type Integer, which receives the reference number returned by the system when loading the library. When calling a function in a library, the system uses both the reference number and trap number to uniquely identify the function to call.
Finally, a library must be unloaded once it is no longer needed. This operation is carried out using RemoveLibrary. However, if several applications are simultaneously using the same library, you must check that none of them are still using it before it is unloaded. The system offers no facility for this, so a shared library must have a means of counting the number of its users itself. A simple method is to provide two functions Open and Close, called respectively when loading and unloading, to increase or decrease a global variable. When this variable is zero, this indicates that the library is not being used; the Close function then returns a specific code that indicates to the calling application that the library can be closed.
The following example, from the MathLib Sinus example, shows how to load the MathLib library (some declarations are omitted for simplicity). If you want to know about the internal functionality of this library, you can obtain the source from http://www.radiks.net/~rhuebner/mathlib.html.
The following example shows how to call a function in the library:
Finally, the following example shows how to unload the library:
Declaring external functions and passing parameters
As shown above, declaring shared library functions is carried out using the Declare statement. In most cases, converting parameters accepted by an external function written in C into data types specific to HB++ does not present any particular problems. The documentation of the Declare statement shows a comparison between the most common C data types and HB++ types. Sometimes, certain constructions in the C language can cause a problem: these concern return values of type Double or pointers, and some parameters taking up more that 4 bytes, like structures.
As already mentioned, the different development tools for the Palm OS® platform do not use the same conventions for returning values of type Double. The HB++ compiler, as well as the GCC compliler expect these values in the register pair D0/D1, whilst the CodeWarrior™ compiler (with which Palm OS® is compiled) transforms the return value into a hidden parameter of type pointer. Take the following C function for example:
Another problem is posed by the return pointers. Whilst return values are normally stored in the D0 register (or in the D0/D1 register pair for Double values), pointers are always returned in the A0 register. This convention is respected by all Palm OS® platform development tools. To be able to call a C function returning a pointer, you should indicate to the HB++ compiler that the return value should be read into the correct register, by declaring the return value with the Pointer keyword. Refer to the documentation on the Declare statement for more information.
Note that the HB++ language ignores the notion of pointer (at least from the programmer's point of view). When an external function returns a pointer, it must be stored in a variable of type Long, and cannot be used directly. It can however be passed to another external function which will know how to use it. One such example will be illustrated later on in the chapter.
Finally, the last problem is posed by structures. The solution in HB++ is to pass a StreamMemory object to an external function. In fact, only a pointer on the first byte of the memory block contained in the object will be passed to the external function. It is only necessary therefore to fill this memory block, in order that the data it contains will correspond to the C structure expected by the function. Consider the following C structure for example:
Another frequently encountered case is the case of structures containing pointers to a character string. Such a pointer always contains 4 bytes, whatever the size of the string it is pointing to. A solution consists to allocate a memory block using the Palm OS® API allocation functions, to initialize it if necessary, then write the pointer returned by the system in the required place in the structure. In the example below, we pass a string initialized to an external function. For simplicity, the structure only contains a single field, of type char * :
The same type of construction can be used to construct a pointer on any type of data, for example a pointer to another structure. This can have some drawbacks in terms of execution time. When writing a shared library, you must avoid therefore as much as possible C functions accepting a pointer to a structure that itself contains other pointers.
Memory management
The HB++ execution library contains its own memory allocation routines, and includes notably a Garbage Collector that detects and frees up unused memory blocks. These routines ignore all allocations that could have taken place within external functions, hence certain rules must be respected to avoid conflicts.
If you want to write a shared library, you will need to read the Palm OS® Developers Kit documentation which contains important information not included here for brevity. The last versions of the CodeWarrior™ compiler contains a wizard that automatically generates the skeleton of a shared library. Finally, you can also refer to the source code of numerous existing external libraries, such as MathLib for example.Private Declare Function MathLibOpen(ByVal iRef as Integer, ByVal iVersion as Integer) as Integer Trap &HA801
Private iRefNum as Integer
Public Sub MLInit()
Dim e as Integer
iRefNum=LoadLibrary("MathLib","libr","MthL")
e=MathLibOpen(iRefNum,1)
If e<>0 Then Err.Raise 67
End Sub
In this function, we begin by loading the library, and we store the reference number returned in a global variable. We then call the MathLibOpen function passing it the reference number, and a parameter indicating the required version. Within the library, this function initializes some internal structures, verifies the version number requested, and increases a global variable that counts the number of users.Private Declare Sub MathLibSin(ByVal iRef as Integer, ByVal x as Double, ByRef r as Double) Trap &HA805+5
Public Function MLSin(ByVal x as Double) as Double
Dim r as Double
MathLibSin iRefNum, x, r
MLSin=r
End Function
In this function, we simply call the external function providing the reference number as the first parameter. It is worth remembering that the result of this function is not returned directly, but indirectly by the intermediary of the last parameter passed ByRef. The reason is that not all compilers use the same convention for returning values of type Double. Consequently, MathLib uses this return mode to ensure compatibility of the library with all the existing development tools.Private Declare Function MathLibClose(ByVal iRef as Integer, ByRef iUseCount as Integer) as Integer Trap &HA802
Public Sub MLClose()
Dim e as Integer, u as Integer
If iRefNum<>0 Then
e=MathLibClose(iRefNum,u)
If e<>0 And u=0 Then RemoveLibrary(iRefNum)
End If
End Sub
Here, we call the MathLibClose function, still passing the reference number as the first argument, and a pointer to a local variable of type Integer. Within this library, this function decreases the global variable that counts the number of users, and copies its value into the local variable passed in the argument. If this value is null, the library is no longer being used and we call the RemoveLibrary function to unload it.double MyFunction(int n)
{
return 1.0;
}
The code generated by the CodeWarrior™ compiler for this function would correspond in reality to the following C function:void MyFunction(double * ret, int n)
{
*ret = 1.0;
}
If you want to interface with HB++ a shared library compiled with CodeWarrior™, you must take this particularity into account, and declare the function not in the way it is written, but in the way it is compiled, as follows:Public Declare Sub MyFunction(ByRef ret As Double, ByVal n As Integer) Trap &HA8xx
To avoid unforseen events and ensure the portability of your code, it is recommended to write a shared library in such a way that no function returns a type occupying more than 4 bytes. Thus, you are sure that the code generated corresponds exactly to the code written, and you do not encounter difficulties when converting your declarations from C functions in the Declare statements.typedef struct _Params
{
int x, y;
char sz[32];
long z;
}
Params;
To fill a StreamMemory object so that the different values occupy in memory the same location as those in this structure could be done in the following way:Dim struct As New StreamMemory
Dim x As Integer, y as Integer, z As Long, sz As String
Write struct, x
Write struct, y
Write struct, sz[32]
Write struct, z
Note that the specification of a size of 32 bytes when serializing the sz character string, in order that this occupies the correct size, and that the following field is found in the right place. However, it is possibe that for alignment reasons, the different fields in a structure do not occupy consecutive places. In this case, it is necessary to write null bytes in order for the different fields to be in the correct place. Refer to the documentation of your C compiler for more information.Public Declare Sub MyFunction(ByVal iRefNum As Integer, ByRef objStruct As StreamMemory) Trap &HA8xx
Public Declare Function MemPtrNew(ByVal lSize As Long) As Pointer Trap &HA013
Public Declare Sub MemPtrFree(ByVal lPtr As Long) Trap &HA012
Public Declare Sub MemMove(ByVal lPtr As Long, ByRef sString As String, ByVal lSize As Long) Trap &HA026
Public Sub CallMyFunction(ByRef s As String)
Dim struct As New StreamMemory
Dim addr As Long, n As Long
n=Len(s)+1
addr=MemPtrNew(n)
If addr=0 Then Err.Raise 1
MemMove addr, s, n
Write struct, addr
MyFunction iRefNum, struct
MemPtrFree addr
End Sub
Here, we determine the size of the string and add 1 to take into account the final null character. We then allocate a memory block using the MemPtrNew function of the Palm OS® API, and then copy into it the string including the null character using the MemMove function.We can then write in the StreamMemory that will be passed to our external function the address of this copy of the string, and carry out the call. Finally, we free the memory block using the MemPtrFree function.
The following rules apply to pointers received by external functions:
Writing a shared library