13 Developing Mobile Applications for PalmOS Devices

Oracle Database Lite for Palm OS supports Open Database Connectivity (ODBC) and Simple Object Database Access (SODA) programming interfaces. In addition, you can build and execute Oracle Database Lite applications using Metrowerks CodeWarrior 9. These subjects are described in the following sections:

13.1 Installing Oracle Database Lite Runtime on the Device

You need to install the Runtime\olSetup.prc on the device to run Oracle Database Lite applications. If you are installing on the emulator, click the olSetup icon in the "Oracle Lite" program group; if you are installing on the device using HotSync, olSetup is executed automatically.


Note:

A sync with Mobile Server replaces the Oracle Database Lite runtime on the device with what is installed on the server. Choose the "Ignore apps" option in the synchronization settings to suppress this behavior.

The tutorial.html file demonstrates how you build a small application for the Palm environment. The file is located in the following directory:

<ORACLE_HOME>\Mobile\Sdk\Palm\doc\tutorial.html

Access additional Palm-specific documentation, including the tutorial, in the following directory:

<ORACLE_HOME>\Mobile\Sdk\Palm\doc\index.html

13.2 Uninstalling or Replacing Oracle Database Lite Runtime

Oracle Database Lite runtime includes deLite, an application that can be used for the following tasks:

  • Remove all Oracle Database Lite, but leave applications and shared libraries in place. This option can be used if Oracle Database Lite becomes corrupted (the application crashes or displays invalid data). Do a synchronization to restore the user's data to a device.

  • Remove both Oracle Database Lite runtime and the databases. Use this option if you no longer need Oracle Database Lite on the device or before manually installing a new version.

  • Uninstall Oracle Database Lite and then install a current version from the specified Mobile Server (by default, the same one used for synchronization). Use this to reset the device to a known version of the Oracle Database Lite or upgrade from a version prior to 5.0.2.9.0, that does not support automatic upgrade.

13.3 Running Oracle Database Lite on Palm OS Emulator

To install Oracle Database Lite runtime on the PalmOS emulator, choose "Install application/database" from the right-click menu and select olSetup.prc in the Mobile client or Mobile SDK directory. Run olSetup once to install the Oracle Database Lite runtime. Navigate to the emulator debug options and clear the "MemMgr semaphore" and "Proscribed function calls" checkboxes. Oracle Database Lite runtime uses these features properly and the emulator warnings for these conditions should be disabled or ignored.

Go to Preferences and check "Redirect NetLib calls to Host TCP/IP". This enables you to synchronize with the Mobile Server using the emulator.

13.4 Running Oracle Database Lite on Palm OS Simulator

Oracle Database Lite works on PalmOS 5.x devices; however, the debug version of the PalmOS Simulator fails when executing olSetup, because of a bug in the simulator. The release version of the simulator works properly.

13.5 Using Oracle Database Lite Base Libraries

Oracle Database Lite provides several libraries that do not directly provide database functionality, but are used by the rest of the runtime. Follow the following steps to add these libraries to your project:

  1. Replace the CodeWarrior with cwStartup.lib (or cwStartup4B.lib if using 4-byte integers). Make sure the library is in the first segment. Add pslm_app.lib to the first segment as well. See pslm.html for more details.

  2. Add libc_stub.lib and olstd.lib to any segment of your application.

  3. Include olstd.h in your source files

  4. If your PilotMain is started with a launch code that allows access to global variables, call psCLibrary.open(true) before using Oracle Database Lite.

  5. Use Constructor to generate a PREF resource and set stack size of your application to 8 KB.

  6. Oracle Database Lite interfaces may change between versions. To avoid compatibility problems, Oracle Database Lite shared libraries return errors if the application is not linked with the matching version of the stub library. When upgrading Oracle Lite runtime, re-link your application with the new stubs.

These steps enable access to the Oracle Database Lite C library, which provides many of the ANSI C functions that are otherwise missing from the PalmOS platform. Examine libc.h for a list. The olstd.h file defines C++ classes such as a hash table, which are documented as a part of SODA.

The libc.h file defines standard I/O functions such as printf for debugging purposes. To see the output, run java BigBrother on the same PC that is running Palm emulator or java BigBrother <IP Address> to capture output from a PalmOS device connected through PPP. Note that the program will be blocked when it tries to use printf until the listener is connected.

13.6 Building a SODA Application

To use SODA, add the following libraries to your project: olSDT.lib, okapi_stub.lib, soda1.lib and soda2.lib. Include soda.h in the source files. See Chapter 12, "Using Simple Object Data Access (SODA) for PalmOS and PocketPC Platforms" for more details.

13.7 Building a SODA Forms Application

To use SODA Forms, include all the SODA libraries and also add sodaform.lib and sodares.rsrc into your application. The later file contains UI resources used by SODA Forms. Avoid using resource ids above 30000 for your own resources to prevent conflicts.

Include "sodaform.h" in your source files.

The file sodaforms.htm discusses SODA Forms, a library for rapid development of data entry applications on Palm. The file is located in the following directory.

<ORACLE_HOME>\Mobile\Sdk\Palm\doc\index.html

13.8 Building an ODBC Application

To use ODBC (actually a subset of standard ODBC that we support on Palm), include "odbc.h" and link with odbc_stub.lib.

13.9 Packaging your Application with Oracle Database Lite Runtime

The file olSetup.prc is the Oracle Database Lite installer which extracts a number of .prc files when synchronized with a device or run on the emulator. It is possible to make a version of olSetup with additional applications by modifying and running makesetup.bat in the Sdk/setup directory.

The next step is to remove "olSetup application" in ORACLE_HOME\Mobile\sdk\Palm\sdk\setup directory from the Mobile Server and publish another application with the new version of olSetup.prc as one of the deployed files. The following events will happen on the next sync:

  1. Any changes to data will be first pushed to the server.

  2. Oracle Database Lite libraries and databases will be uninstalled from the device.

  3. New Oracle Database Lite runtime (and any applications you added) will be installed from olSetup.prc

  4. A full synchronization will be done with the server to restore the databases.

This process will upgrade the version of Oracle Database Lite on the device and avoid any database compatibility problems.

13.10 Customizing Oracle Database Lite Runtime

In addition to adding your application, you might want to customize Oracle Database Lite runtime itself. The following changes can be made in makesetup.bat:

Table 13-1

Change Effect
Add olEncryptTransport.prc Enable AES encryption of data during synchronization. Note that this does not work with external authentication on the server side.
Remove olLibCrypto.prc Disable AES encryption altogether, including database encryption.
Remove olCompressTransport.prc Disable compression during sync. Can be useful on devices with very little memory
Remove odbc.prc If you are only using SODA
Remove msql.prc You may not need this tool on end-user devices
Substitute okapi.prc from Sdk\setup directory with okapi.prc from Sdk\setup\card subdirectory (copy okapi.prc from Sdk\setup\card one level up) and rerun makesetup.bat Will create olSetup.prc for the storage card version of Oracle Database Lite. Enables you to use the storage card on palm device to store Oracle Database Lite databases instead of main memory. This will greatly relax the limits on the database size (will be limited only by storage card size). Olite databases will be located in OLDB directory of the storage card. Since storage card support constitutes different Olite installation, we do not currently support accessing both storage card and in-memory databases from the same application.

13.11 Using Mobile Sync for Palm

Mobile Sync (mSync) for Palm enables a user or a developer to synchronize data with the Mobile Server. The user can configure and execute the application manually. Then, tpa the Sync button to manually initiate synchronization over the default network connection configured on the PDA.

Alternatively, you can invoke synchronization with pre-configured settings, as follows:

  • HotSync automatically synchronizes the Oracle Database Lite databases if the Oracle Database Lite conduit is installed on the desktop.

  • The DBSession::sync() method, which is part of SODA interface, launches msync and attempts to synchronize over the network connection.

  • SODA Forms applications have the "Sync data with the Mobile Server" option in their "Form" menu.

13.11.1 Configuring msync

When msync executes, it displays a screen with the most common synchronization settings. Table 13-2 shows the controls for the synchronization settings that are displayed by msync.

Table 13-2 List of Controls for Synchronization Settings

Option Use
User Name Case-insensitive user name on the Mobile Server
Password Case-insensitive password on the Mobile Server
Change (password) If selected, New and Confirm fields are shown. Enter the new password twice to guard against typing mistakes. On the next successful sync, Mobile Server password is changed.
Save password Tap this checkbox to save the password on the PalmOS PDA. You will not have to reenter password every time you sync, however anyone with physical access to your Palm will be able to sync, and possibly discover your password. This option is required to use HotSync and automatic sync through SODA or SODA Forms applications.
Server Enter hostname or hostname:port of your Mobile Server. When synchronizing over the network, you can enter the IP address to bypass DNS configuration problems.
Proxy When this checkbox is active, an additional Proxy field appears on screen. Enter the hostname or hostname:port of your HTTP proxy server. This option only applies during HTTP sync. To configure a proxy server for HotSync, check Internet Explorer settings on your desktop.
Secure Activates the secure sync over HTTPS rather than plain HTTP. You need a PalmOS 5.2 or later PDA to do a secure network sync. However, any device can HotSync using this option. HTTPS sync normally requires a valid certificate to be purchased and installed on the Mobile Server. For development purposes, you may prepend @! to server's hostname to test without a valid certificate. This option should never be suggested to end users, as it undermines the security provided by HTTPS.
Forced Tap this checkbox to do a complete refresh on the next sync. This can solve some data consistency problems. Note that this option is automatically cleared after one sync.
Log button Tap this button to launch LiteLog application. You will be able to see the log of the recent failed and successful sync attempts, as well as any crashes or critical errors encountered by Oracle Database Lite applications.
Sync button Starts a sync over the default network connection.
Cancel button Exits msync and re-starts the SODA or SODA Forms application, if any, that initiated it.

The msync executable also provides the synchronization settings item under the Options menu, which can be used to adjust less frequently used settings, as described in Table 13-3.

Table 13-3 Synchronization Settings in the Options Menu

Option Use
Hangup after sync Hangup the network connection immediately after the sync is done. Normally, the connection will be left on and disconnected when the timeout configured in the Network settings panel expires.
Push only Send locally made changes to the server, but do not get any data back. This is a quick way to backup local data to the server.
Ignore apps Disable application deployment and auto-upgrade of the Oracle Database Lite runtime. Tap this checkbox to sync with a version of Mobile Server different from the client version without causing an upgrade.
Remote Hotsync Tap this checkbox when doing a Network hotsync to enable successful retries if the Palm Desktop times out.
NLS Code Enter an Oracle-supported language code to sync using the appropriate character set. Most Japanese, Chinese or Korean devices are automatically detected by Oracle Database Lite, but some third-party language add-ons are not.

13.11.2 Using HotSync to Synchronize Data with the Mobile Server

The following sections discuss how to use HotSync to synchronize data with the Mobile Server.

13.11.2.1 Configuring HotSync for a PalmOS Device

To synchronize Oracle Database Lite databases over HotSync, perform the following:

  1. Install Mobile client for PalmOS on a machine that already has Palm Desktop.

  2. Start the msync executable on the PDA and configure all the sync options, including the "Save password" checkbox.

  3. Find and enable "Stay on in Cradle" options in the PalmOS Prefs application.

Every HotSync automatically synchronizes Oracle Database Lite data with the Mobile Server. After HotSync completes, refer to the LiteLog on the device or HotSync log on the desktop to check for errors.

13.11.2.2 HotSync Timeout Errors

If the Mobile Server sends a large volume of data to the PDA, then the Palm Desktop may timeout during HotSync and a message box pops up on the desktop while the PDA is still processing sync data. Dismiss the dialog or let it time out automatically. Then, msync detects this condition and automatically performs another HotSync, which should process successfully.


Note:

Note that if you are viewing the HotSync log when the PDA reconnects and in some other conditions the retry may fail. Generally, the error can be ignored, except if another application registered a low priority conduit that is supposed to run after Oracle Database Lite. In this case, manually perform another HotSync.

This timeout issue exists within the Palm Desktop software, rather than within Oracle Database Lite. In particular, the SyncCallRemoteModule API that is used by a conduit to invoke an application on Palm can be problematic. If the application takes a lot of time to execute, Palm Desktop times out and aborts HotSync. If PalmSource provides a configurable timeout in a future release of Palm Desktop, the message box can be avoided by increasing the timeout value.

13.11.2.3 Configuring PalmOS Emulator for HotSync

It is possible to perform a Network HotSync with the Palm emulator with the following steps:

  1. Enable Network in HotSync tray menu on the desktop running the Palm emulator.

  2. Check the "Remote HotSync" checkbox in msync options menu.

  3. Make sure that "Redirect NetLib calls to Host TCP/IP option" is enabled.

  4. Run the HotSync application. Click the "Modem" (rather than "Local") push button.

  5. In Modem sync preferences, choose "Network" rather than "Direct to modem".

  6. In "Primary PC setup", enter "localhost" as the hostname and "127.0.0.1" as the IP address.

  7. Click on the connection field below the HotSync icon and choose "AT&T WorldNet" or any other connection.

  8. Click on the HotSync icon in the middle of the screen to do a network HotSync.

13.11.3 Using Network Sync

The msync executable can open a direct connection to the Mobile Server without going through the Palm Desktop. If you have a valid network connection, such as through a modem, integrated cellular phone or Bluetooth, then you can tap the Sync button on the msync main screen.

13.11.3.1 Synchronizing Using a Cradle and Windows Desktop

It is possible to connect a PalmOS device to the network using a cradle connected to your PC rather than a dedicated modem. First, you need to enable Incoming Connections. Modern PalmOS devices use a USB cradle, that requires 3rd-party software to establish a network connection. One such product is Softick PPP, which can be purchased from http://www.synclive.com/ppp. Note that we don't offer support for any issues you may encounter with such 3rd-party software.

For serial cradles, follow the following steps:

Setting up the desktop

  1. Uncheck "Local Serial" option in the HotSync pop-up menu and leave it off while you are using the cradle for networking

  2. Go to Modem control panel and create a new modem of type "Communications cable between two computers". Set "Maximum port speed" to 56K and "Flow control" to Hardware.

  3. In Windows XP, open the Network control panel, choose "Create a new connection", then "Setup a new connection" and finally "Accept incoming connections". Choose the modem you just added in "Devices for Incoming Connections". Select at least one user in "Users you allow to connect". When you get to TCP/IP settings, choose "Allow callers to access my local area network".

  4. If your PC is not using DHCP, you need to find two sequential unused IP addresses on your local subnet and enter them in the fields under "Specify TCP/IP address.

  5. Older versions of Windows have different ways to enable incoming connections. For example, in Windows NT 4.0 the equivalent functionality is known as "Remote Access". You may need to adapt the instructions for the version running on your desktop.

Setting up the device

  1. Go to Connections panel in Palm preferences. Edit the "Cradle/Cable" connection details by setting speed to 56K and flow control to Hardware.

  2. In Network panel, create a new service. Enter username and password of the user you selected while setting up the desktop. Set the "Connection" to "Cradle/Cable".

  3. Choose Details/Script for your service. Enter the following script:

    Send: CLIENT

    Send: CLIENT

    Wait For: CLIENTSERVER

  4. Test the service by clicking Connect button. Once the connection is successful, choose Options/View Log from the Preferences menu. Type ping servername or ping serverip. If the ping succeeds, you should be able to sync with that Mobile Server by tapping the appropriate button in msync.

13.11.3.2 Network Sync With PalmOS Emulator

To use msync on the emulator, make sure "Redirect NetLib calls to Host TCP/IP" option is set in the preferences. To test SSL, run PalmOS simulator version 5.2 or later.

13.12 Palm Shared Library Manager (PSLM)

This document discusses the Palm Shared Library Manager (PSLM). It includes the following topics:

13.12.1 Overview

PalmOS provides built-in facilities to load and use shared libraries. However, native support has severe limitations that make it virtually impossible to port existing code of significant size. The size of native shared libraries is limited to 32-64K, depending on code structure. In addition, global variables and many important C++ features, such as virtual functions and exceptions, can not be used. There are a couple of techniques to overcome these limitations, such as PRC-Tools glib support and CodeWarrior 9 "expanded mode". However, none of them succeeds in making a shared library as easy to develop as an application.

Oracle's solution, PSLM, allows one to build a shared library as a regular Palm application. It is possible to use multiple segments, C++ virtual tables and exceptions and global variables, even ones with constructors and destructors. PSLM doesn't require any support from the OS and only uses limited compiler support (a patch the the publicly available sources of the CW runtime library). It does require some extra code to be written, but the existing code of the application and an existing static library does not need to be modified.

13.12.2 Trying out PSLM

There is a sample using the framework in <ORACLE_HOME>\Mobile\Sdk. After you do "Build All" on the project file, there will be two PRC files in that directory - SampleLibrary.prc and SampleApp.prc. Install Oracle Database Lite Runtime using olSetup.If you touch the "PSLM Sample" icon on the Palm now, it will just print a couple of message boxes and exit. What happens inside is considerably more interesting. SampleLibrary.prc is a PSLM shared library that has global variables and even makes use of another shared library, the ANSI C library that comes with Oracle Database Lite. Look at Sample.cpp for the implementation of the shared library and AppStart of Starter.cpp to see how it is called.

13.12.3 Writing a PSLM Library

A PSLM library is a C++ class that extends PSLibrary. It exposes all it's functionality as virtual functions. Here is how the SampleLibrary class looks like.

class SampleLibrary : public PSLibrary {
protected:
        /*
         * Overloaded PSLM functions.
         */
        virtual pslmError startup();
        virtual void cleanup(bool isFinal);
public:
        /**
         * Increment an internal counter by a specified value and then
         * return a result as a string (global buffer that will be reused
         * on next call).
         */
        virtual const char *getCounter(int incVal);
        /**
         * Reset a counter to the specified value
         */
        virtual void setCounter(int newVal);
};

This library defines two APIs - getCounter and setCounter. The remaining two virtual methods - startup and cleanup are called by PSLM itself and are very important. Basically, a PSLM library must use it's startup and cleanup methods rather than constructor and destructor to manage it's state. Also, it must be able to handle another startup after cleanup is done. This is one of the few artifacts caused by lack of the compiler/OS support. The constructor of a shared library is called normally, but must not use PSLM itself or even call the methods of it's own object. The destructor is actually not called at all. Note that it's perfectly Ok to have a pointer to another object that is constructed during startup and deleted during cleanup.

startup() method is the place to do initialization, including loading additional libraries. Let's look at the startup method of SampleLibrary:

pslmError SampleLibrary :: startup() {
     PSLibContext ps(this); // Establish access to globals
     cleanOrder = 5; // Unload before libraries with clean order 4 and below on exit
     return psCLibrary.open();}
 

The first line of this method is the most important, but we'll come back to it in a moment. The second line lets you specified the order in which the libraries will be unloaded on program exit. If B depends on A, A should have a smaller cleanOrder. The last line loads the C library and returns success or error of that application.

startup() function can fail by returning a value rather than 0. In this case, the library being loaded is removed and the error is returned to the caller.

cleanup() method should free all the memory, closing network connections, unload dependencies and so on. However, if the isFinal argument is set to true it must not unload other libraries because the program is exiting and it will interfere with PSLM closing libraries correctly. Here is the cleanup method of SampleLibrary:

void SampleLibrary :: cleanup(bool isFinal) {
        PSLibContext ps(this);
        if (!isFinal)
                psCLibrary.close();}
 

Let's look at a regular method of SampleLibrary, together with the variables it's using:

class SampleBuf {
        char *buf;
public:
        SampleBuf(int size) : buf(new char[size]) {}
        ~SampleBuf() { delete[] buf; }
        operator char *() { return buf; }
};
SampleBuf myBuf(128);
int counter;
const char * SampleLibrary :: getCounter(int incVal) {
        PSLibContext ps(this);
        counter += incVal;
        sprintf(myBuf, "%d", counter); // Use LibC - another shared library
        return myBuf;}
 

Note that this method uses two global variables and one of them even has a constructor and a destructor. This is Ok, although global constructors and destructors can only do simple things. They shouldn't call PSLM and shouldn't use other shared libraries unless you are sure they are always loaded.

Look at the highlighted line, PSLibContext ps(this). Every exposed virtual method of a PSLM shared library must start with this line. The constuctor of a PSLibContext sets the context to that of the library passed as an argument. This is what enables a library to use it's global variables, virtual functions and exceptions. If you omit this line, you will get crashes, call random places in memory or even introduce hard-to-track memory corruption. Also, remember to delete the context before calling any callback in the main program and re-create it afterwards. If you get this right, you have mastered PSLM.

The last piece to consider is the library's PilotMain, which is very simple:

UInt32 PilotMain( UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) {
        if (cmd == psLibLaunchCode)
                psLibInit(cmdPBP, new SampleLibrary()); // Never returns
        return 0;}
 

psLibLaunchCode is what PilotMain gets when the library is open. psLibInit takes a pointer to a subclass of PSLibrary. It never returns directly. Instead, it returns the control back to the program that opened the library.

13.12.4 Building a Shared Library Project

The following illustration, Figure 13-1, shows the CodeWarrior project for SampleLibrary:

Figure 13-1 The CodeWarrior Project for SampleLibrary

The CodeWarrior Project
Description of the illustration sampleSeg.gif

First, note that the usual CodeWarrior startup library (PalmOSRuntime_2i_A5.lib) has been replaced with cwStartup.lib from our distribution. You can use this patched startup library for any project, but it must be used to build a PSLM shared library. I tried to avoid requiring a custom runtime, but recent Metrowerks changes and especially PalmOS5 support made a patch necessary. Use cwStartup4B.lib if you are building a project using 4-byte integers. Finally, you might want to patch your own runtime if you are using a version of CodeWarrior newer than 8.3.

Both cwStartup.lib and pslm_lib.lib must be in the first segment of the application, otherwise it will crash when loaded. Other files can be in any number of segments. In this example, the included SampleLib.cpp and libc_stub.lib libraries are static helpers for ANSI C shared libraries. A pslm library must not contain any UI resources, because they will be used instead the corresponding ones of the application. Be sure to exclude your Starter.rsrc from shared library targets.

The following illustration, Figure 13-2, shows the "PalmRez Post Linker" section of the SampleLibrary project:

Figure 13-2 The PalmRez Post Linker Section

This graphic displays the PalmRez Post Linker section.
Description of the illustration samplePost.gif

A PSLM library is linked as an application, but we do not want it to show up as a Launcher icon. Change type and creator of the .PRC file to PSLM in order to hide it. Also, set the database name to whatever name you are planing to use when you load the library.

13.12.5 Calling a PSLM Library from Your Application

To call a shared library from your application, first use a template class to declare a proxy object for that library:

PSLibObject<SampleLibrary> sampleLib("SampleLibrary");

The quoted SampleLibrary is the Palm Database Name you specified in the project settings, while the quoteless one if the name of the class that exposes the APIs. You can make these two different if you want. You can load the library using:

sampleLib.open(true);

In the above statement, "true" argument means that a fatal exception will be displayed on the device if the library can not be open. For a nicer error handling, and especially if the library is optional, just do sampleLib.open() without arguments and process the returned Err value if not errNone.

Once opened, you can pretend that sampleLib is a SampleLibrary * and write code such as the following:

StrPrintF(buf, "Value after increment by 3: %s", sampleLib->getCounter(3));

This is actually not very convenient if you originally just had a static library that defined getCounter. Remedy this problem with preprocessor directives like this one:

#define getCounter (sampleLib->getCounter)
#define setCounter (sampleLib->setCounter)

At this point, you can use getCounter(3), same as with a static library.

Should you call sampleLib.close() to unload SampleLibrary? You can if you need to free the resources immediately. open() and close() keep a use count and only unload when it drops to 0. Note though that all the libraries will be automatically unloaded (ordered by increasing cleanOrder) when the program exits.

If you use sampleLib in more than one file in your program, you should load it in your AppStart and then declare it as follows in other files:

extern PSLibObject<SampleLibrary> sampleLib;

In the other extreme, you can have a function that loads a plugin, lets it do some work and then unloads it before returning. In this case, you can declare a local variable of type PSLibObject and even pass a dynamic argument as a library name to support user-defined plugins.

The last technique is linking statically to the code that was intended to be a shared library. If you add SampleLib.cpp to the project (comment out it's tiny PilotMain), you can do sampleLib.init() before open. This will call a statically linked default constructor of the template argument and then register the object as a fake shared library. One interesting result is if MAIN loads LIB1 statically and LIB2 dynamically and then LIB2 tries to load LIB1, it will get a static copy embedded in main and the dynamic LIB1 doesn't need to be installed on a device. This allows the main program to determine exactly which components are statically linked.

13.12.6 Building an Application Using PSLM

An application using PSLM must be linked with pslm_app.lib and it must reside in the first segment. Although this example uses cwStartup.lib, applications can use a regular runtime libraries and only shared libraries need a patched one. Figure 13-3 shows a PSLM sample application.

Figure 13-3 PSLM Sample Application

This graphic displays a sample PALM application.
Description of the illustration sampleApp.gif

13.12.7 Exceptions Across Modules

You are free to use exceptions inside the shared library, as long as they are also caught inside. Unfortunately, its currently not possible to throw an exception in a library and catch it in the main program. What you want to do, is catch the exception at the top level API method and store it as a private field in the PSLibrary subclass. Then add a non-virtual method that checks that field and re-throws an exception. Basically, non-virtual methods are always static. If you use them both in the library and it's caller, you must link them with both. For this case, it's the easiest to use an inline method for re-throwing the exception.

13.12.8 Cloaked Shared Libraries

Certain C++ features, such as global variables and exception handling, allocate large amounts of dynamic heap when the program is loaded into memory. PSLM has a feature that allows allocating a shared library's data segment in storage heap instead. To use this feature, add one more argument to the PSLibObject template:

PSLibObject<SampleLibrary, true> sampleLib("SampleLibrary");

Internally, this will cause PSLibContext to call MemSemaphoreReserve(true) in the constructor and MemSemaphoreRelease(true) in the destructor to temporarily un-protect storage heap while a method of the cloaked library is executing. In some cases, for example if a shared library returns a pointer to its global variable to the caller, you may need to do it yourself to modify the data. You can declare a variable of PMLock class on the stack to unprotect the storage heap for the duration of its scope.

Note that you can not get input from the user while the memory semahore is locked. Anything that calls EvtGetEvent directly or through another system call will hang. Therefore, cloaked libraries are only suitable for tasks that don't require user's input.

13.12.9 Patching the CodeWarrior Runtime

PSLM requires a patched version of CodeWarrior runtime libraries to link a shared library. The Oracle Database Lite build includes cwStartup.lib and cwStartup4B.lib, which are pre-patched versions of the runtime libraries that come with CodeWarrior 8.3. If you want to make your own patched runtime, you need to modify PalmOS_Startup.cpp.

This section is much more difficult than the rest of the document. You need some experience reading system-level code and applying other people's patches to follow it. Otherwise, you may want to stick with our pre-patched version or ask someone with the experience for help.

Let's start with a unified diff generated for CodeWarrior 8.3:

--- PalmOS_Startup_old.cpp      2002-07-19 18:41:26.000000000 -0700
+++ PalmOS_Startup.cpp  2002-09-08 15:51:29.000000000 -0700
@@ -396,6 +396,12 @@ 
 
 #endif /* SUPPORT_A4_CONST_GLOBALS */ 
 
+SysAppInfoPtr pslmGetAppInfo(
+       SysAppInfoPtr *rootAppPP, 
+       SysAppInfoPtr *actionCodeAppPP)
+       SYS_TRAP(sysTrapSysGetAppInfo);+
 +
 
 /*
  *     Main entry point for applications
  */
@@ -408,8 +414,9 @@
        SysAppInfoPtr   appInfoP;
        Int16                   abort_result = 0;
        Boolean                 globals_are_setup;
-       _CW_Features    features;
-#if SUPPORT_A4_CONST_GLOBALS   
+       _CW_Features    lFeatures;
+       static _CW_Features gFeatures;
+ #if SUPPORT_A4_CONST_GLOBALS  
        UInt32                  originalA4 = GetA4();
        MemPtr                  originalExtraP; 
 
@@ -435,18 +442,31 @@
     }
 #endif
        +
 
        /*
         *      Call the standard system code for allocating and initializing globals and
         *      setting up A5, and getting the command line arguments
         */
-       err = SysAppStartup(&appInfoP, &prevGlobalsP, &globalsP);
+       // PSLM - try to find and execute custom startup code
+#define psLibLaunchCode ((UInt16)0xC001)
+       typedef Err (*appStartup)(SysAppInfoPtr* appInfoPP, MemPtr* prevGlobalsPtrP, 
+                                                       MemPtr* globalsPtrP);
+       appStartup start = NULL;
+       SysAppInfoPtr uiP, curP;
+       appInfoP = pslmGetAppInfo(&uiP, &curP);
+       if (appInfoP->cmd == psLibLaunchCode)
+               start = (appStartup)appInfoP->extraP;
+       if (start)
+               err = start(&appInfoP, &prevGlobalsP, &globalsP);
+       else
+               err = SysAppStartup(&appInfoP, &prevGlobalsP, &globalsP);
        if (err) {
                ErrDisplay("Error launching application");
                return 0;
        } 
 
        globals_are_setup = (appInfoP->launchFlags & sysAppLaunchFlagNewGlobals) != 0;-
 
+       _CW_Features &features = globals_are_setup ? gFeatures : lFeatures;
        /* initialize runtime globals */
 #if SUPPORT_A4_CONST_GLOBALS   
        originalExtraP = appInfoP->extraP;

If you have a similar version of PalmOS_Startup.cpp, you can place this diff into a patch program. But a patch will fail if Metrowerks made a lot of code changes. Let me walk you through the changes so that you can still make a functionally equivalent patch.

First, we need to declare a prototype of a PalmOS system function that is not declared in regular Palm SDK. Put the prototype just before __Startup__:

SysAppInfoPtr pslmGetAppInfo(
        SysAppInfoPtr *rootAppPP, 
        SysAppInfoPtr *actionCodeAppPP)
        SYS_TRAP(sysTrapSysGetAppInfo);
/*
 *      Main entry point for applications
 */
extern "C" UInt32 __Startup__(void)

Next, 8.3 version of __Startup__ declares a local variable of type _CW_Features and then stores it in a global pointer that is used elsewhere. This is not good for PSLM, because it will continue calling functions in a shared library after __Startup__ is removed from the stack (by a longjmp in psLibInit). Find the declaration of the variable:

_CW_Features features;

Instead we need to declare both a global version (for PSLM) and a local version (for a sublaunch without access to globals in other projects):

_CW_Features lFeatures;
static _CW_Features gFeatures;

Now, find this line right after the call to SysAppStartup:

globals_are_setup = (appInfoP->launchFlags & sysAppLaunchFlagNewGlobals) != 0;

At this point, we know if the program has global access and if it uses a correct version of features:

globals_are_setup = (appInfoP->launchFlags & sysAppLaunchFlagNewGlobals) != 0;
_CW_Features &features = globals_are_setup ? gFeatures : lFeatures;

Consider the following call:

err = SysAppStartup(&appInfoP, &prevGlobalsP, &globalsP);

The following code shows what the call should be turned into:

#define psLibLaunchCode ((UInt16)0xC001)
        typedef Err (*appStartup)(SysAppInfoPtr* appInfoPP, MemPtr* prevGlobalsPtrP, MemPtr* globalsPtrP);

        appStartup start = NULL;
        SysAppInfoPtr uiP, curP;
        appInfoP = pslmGetAppInfo(&uiP, &curP);
        if (appInfoP->cmd == psLibLaunchCode)
                start = (appStartup)appInfoP->extraP;
        if (start)
                err = start(&appInfoP, &prevGlobalsP, &globalsP);
        else
                err = SysAppStartup(&appInfoP, &prevGlobalsP, &globalsP);

SysAppStartup initializes global variables and multiple code segments for normally loaded applications. PSLM libraries are loaded somewhat abnormally and, in PalmOS 5, SysAppStartup can no longer initialize them correctly without messing up the calling program. An equivalent process is now performed by the code inside PSLM and we must modify CodeWarrior runtime to call this custom function for a shared library launch code. To save space, the internal function only does the same work as PalmOS 1.0, so do npt disable the support for old devices in build options.