In this chapter:
It is time to discuss conduits-what they do, how to create them, what's involved in getting a minimal conduit working. It would also help if you understood (codewise) what happens when a Palm device is plopped into a cradle and the user pushes the HotSync button.
NOTE: This is useful if you want a detailed understanding of what happens when your conduit code is called, and how it interacts with the Sync Manager to perform its tasks. |
We take a brief detour to discuss the types of applications that can profitably use the Backup conduit (a conduit that simply archives application data on the desktop). We also show you the code changes required to do this. Last, we create an actual conduit. As you might imagine, we build a conduit for the Sales application using Visual C++. This conduit doesn't do much; it just writes a message to the log file. However, it's still quite useful. You can see very easily what is involved in creating a minimal conduit and what it takes to get to the point where syncing is ready to begin.
Overview of Conduits |
A conduit can be simple or complex, depending on the job it has to do. Regardless of its complexity, you create it in the same way-a conduit is a desktop plug-in made in a desktop development environment. This isn't code that runs on the Palm handheld, but an executable library that runs during the HotSync synchronization.
What Does a Conduit Do?
A conduit is responsible for the application's data during a synchronization between the handheld and a desktop computer. The conduit needs to:
Your conduit is responsible for saving the data on the desktop in whatever way makes sense. If your conduit syncs to a file for a desktop application, it needs to read and write data in that application's file format. Your conduit may read and write records from a database on the desktop or some database on the network. As a result, each conduit handles storing and retrieving desktop data differently.
There are three broad categories of conduits:
Upload- or download-only
Conduits that just copy a database to or from the handheld.
Mirror-image record synchronization
Conduits that do a two-way synchronization. The conduits for Address Book, Memo Pad, and To Do are examples.
Transaction processing
Conduits that do some sort of processing of records but aren't doing a mirror-image synchronization. A good example might be an order entry application on the handheld that sends transactions out the conduit to be processed on the desktop.
Conduit Development on Windows
At the current time, conduit development is only possible on Windows 95/98/NT. Releases of the Conduit Development Kit (CDK) in the very near future will see this change, but for now we are discussing only Windows.
CDK 3.0
At the time this book was written, the final version of CDK 3.0 had not yet been released. We, of course, did the logical thing and used the beta version. As a result, our information is based on that and on the planned content of the final version.
The final version should include the following important features:
NOTE: Conduits developed using the older 2.1 version of the CDK work with both the PalmPilot Desktop 2.0 (shipped with the PalmPilot and PalmPilot Pro) and the Pilot Desktop 1.1 (shipped with various versions of the Pilot 1000 and 5000). 2.1-based conduits are also upward compatible with Palm Desktop 3.0 (shipped with the Palm III). |
Certain new features of Palm Desktop 3.0 are only supported for conduits created with CDK 3.0. The major new feature is File Linking, which provides a way to copy information from an external file to a separate category. An example of this would be as part of syncing to a user's personal address book on the desktop, to copy entries from a company-wide address book to a special category on the handheld. There are some other slight API changes in 3.0.
Conduits created with CDK 3.0 are generally backward-compatible with older versions of the desktop software, although the new API calls can't be called in earlier versions (your application can make a call to find out what version of the APIs is available).
Using Visual C++
The beta version of CDK 3.0 requires Visual C++ 5.0 or later running on Windows 95/NT or later. We use Visual C++ 5.0 to create our Sales conduit.
Using Java
Using the CDK Java Edition you can also create a conduit using everyone's favorite caffeinated development language.
This CDK supports development using Visual J++ or Symantec Visual Cafe for Java.
We don't cover creating Java conduits in this book.
NOTE: Although other Java development environments aren't officially supported, it seems to us that they should work. Remember, however, this is advice coming from people who haven't actually used these products. |
Conduit Development on Macintosh
As the time of this book's writing, the CDK C/C++ version for Macintosh was still in its infancy (read: an almost unusable alpha version). When Palm finishes the Mac OS HotSync Manager and this development kit, you will be able to create conduits on Macintosh using CodeWarrior for Macintosh, developing in C/C++. Hopefully, by the time this book is in your hands, it will be out. See http://www.palm.com/devzone for the status of this project.
Required Elements in a Minimal Conduit
In a little while, we will show you how to create a minimal conduit. That conduit will contain a few essential elements that we want to tell you about now:
A mechanism for installation and uninstallation
Different versions of the CDK require different mechanisms for registering and unregistering your application.
Three C entry points
One entry point registers the conduit's name, another its version number, and the last serves as an entryway into the conduit.
Log messages
You need to provide log messages to the user. Among other things, you must tell the user whether the sync was successful or not.
We first look at installation and uninstallation issues and then discuss the entry points. Last, we discuss log messages.
Registering and Unregistering a Conduit |
Before the conduit can be used, it needs to be registered. This is how the HotSync Manager application knows that it exists and knows which databases the conduit is responsible for syncing. Depending on which version of the CDK you have, there are differences in what you do to register. We talk about the old, difficult way and then the new improved methods.
The Old, Ugly Way
In version 2.1 (and earlier versions) of the CDK, this registration was done by adding entries to the Windows Registry. Unregistration required removing entries from the Registry (and possibly renaming existing entries). Further, this process for adding entries was fragile-one developer modifying the registry incorrectly could cause some or all of the other conduits to fail.
These troubles only increased during the acquisition frenzy, when the keys used for the Windows Registry by various versions of HotSync Manager and the Desktop Manager changed from Palm Computing to U.S. Robotics.*
Conduits then needed to be aware of various registry keys and needed to perform a careful set of steps when registering and an even more careful set when unregistering.
The time was ripe for a better approach to registration.
The New, Sleek Way
The Conduit Manager, provided as part of Palm Desktop 3.0 and as part of the 3.0 version of the CDK, contains an API for registration and unregistration. It knows about the various versions of HotSync, the different keys used in the Windows Registry, and the careful steps needed for registering and unregistering.
The Conduit Manager functionality is provided in a DLL that ships with the new version of the Palm Desktop. As we discuss later in "Finding the Correct Conduit Manager DLL," you also need to include the DLL as part of your installer.
The 3.0 version of HotSync Manager continues to use the Windows Registry for the sake of older conduits that don't use the Conduit Manager. You should expect, however, that future versions of HotSync may not use the Windows Registry at all.
As long as you use the Conduit Manager, you'll be shielded from any such changes to the underlying registry mechanism.
CDK 3.0-Information Needed to Register
There are different types of entries that you need to have in order to register a conduit. Some are required; others are optional.
Required conduit entries
The following entries are required to register a conduit:
Conduit
The name of the conduit DLL. If this entry doesn't include a directory, the name must be found in the HotSync directory or current PATH; otherwise, it should include the full pathname to the DLL. (Generally, you keep your DLLs in the HotSync directory.) If your conduit is written using Java, this entry should be "JSync.DLL", a C++ shim that translates between C++ and Java.
Creator
The four-character creator ID of the database(s) your conduit is responsible for. Your conduit will be called during a HotSync only if an application with this creator ID exists on the handheld.
Directory
In the HotSync directory, each user has a subdirectory. Within each user's directory, each conduit has its own directory where it can store files. This string specifies the conduit's directory name.
Optional entries
The optional entries are more numerous. They include the following:
File
A string specifying a file (if the string doesn't include a directory, it is assumed to be within the conduit's directory). This is intended to be the local file that the conduit will sync the handheld against. However, your conduit is not restricted to using only this file (some conduits may need to read/write multiple files on the desktop).
Information
A string that provides information about your conduit. This string can be used to resolve conflicts. If more than one conduit wants to handle the same creator ID, an installation tool could display this string and ask the user which conduit should be used for syncing.
Name
A string that is the user-visible name of the conduit.
Priority
A value between 0 and 4, this controls the relative order in which conduits run. Conduits registered with a lower priority run before conduits registered with higher priorities. If you don't set this value, the HotSync Manger uses a default value of 2 for your application.
Remote DB
A string specifying a database name on the handheld. This string is provided for you to use in your conduit when it runs; your conduit isn't required to use it, however.
Username
The name of the user for which this conduit is installed. Note that this entry is not currently used.
Java-only entries
Finally, there are entries relevant only if the conduit is written in Java:
Class name
The name of the Java conduit class (including package).
Class path
The directory that contains all the classes used by the Java conduit.
VM
Specify "Sun" for the Sun Java Virtual Machine or "MS" for the Microsoft Java Virtual Machine. This is provided since some Java code is, unfortunately, sensitive to the virtual machine on which it runs.
Registering and Unregistering Manually
Using CondCfg
Along with the Conduit Manager DLL is an application, CondCfg, that uses the Conduit Manager (see Figure 11-1). This application displays all the registered conduits and allows you to register conduits, change registered information, and delete conduits.
Figure 11- 1.
CondCfg-a developer utility for registering and unregistering conduits
Your end users won't use or even see CondCfg, however, as you automate the conduit registration process as part of installing and uninstalling it on the desktop.
Automatically Installing and Uninstalling
a Conduit
A small command-line program (ConduitInstall.exe) is going to install and register our conduit. We use a separate one (ConduitDeinstall.exe) to uninstall.
Installing the conduit
As ConduitInstall.exe executes, it makes calls to the Conduit Manager API to install and register our conduit. It also needs to make calls to the three required entry points of the conduit (Conduit, Creator, Directory) and to any of the optional entry points we want to set.
NOTE: We use ConduitInstall.exe, a simple command-line program, to avoid clouding the relevant issues with a lot of technical details concerning Windows application programming. We couldn't possibly cover all the available methods. You could fold your installation into a program that handles other installations, as well. You could be using the popular installer utility InstallShield (the CDK contains a sample that shows how to use this). In any event, we keep things simple so that you can understand exactly what is necessary to install and register a conduit. |
The first call you make is one that registers the Creator entry point of the conduit:
int CmInstallCreator(const char *creatorString, int conduitType);
If that succeeds, you call a different CmSetCreator routine for all the rest of your entry points. Most of the CmSetCreator routines match the entry point name and are easy to figure out (the two exceptions are CmSetCreatorName and CmSetCreatorTitle). Here are the routines we use and the entry points they register:
Sets the required Conduit entry point
Sets the required Directory entry point
Sets the file entry point
Sets the priority entry point
Sets the name entry point
ConduitInstall.exe
Here's our command-line program, ConduitInstall.exe, that registers a conduit:
#include <Windows.h> #include "CondMgre.h" #include <stdio.h> int main(int argc, char **argv) { const char *kCreator = "Sles"; err = CmInstallCreator(kCreator, CONDUIT_APPLICATION); if (err == 0) err = CmSetCreatorName(kCreator, "C:\\SalesCond\\Debug\\SalesCond.DLL"); if (err == 0) err = CmSetCreatorDirectory(kCreator, "Sales"); if (err == 0) err = CmSetCreatorFile(kCreator, "Sales"); if (err == 0) err = CmSetCreatorPriority(kCreator, 2); if (err == 0) printf("Registration succeeded\n"); else printf("Registration failed %d\n", err); return err; }
Automatically uninstalling a conduit
Uninstalling is just as simple. Our application, ConduitDeinstall.exe, uses CmRemoveConduitByCreatorID, which removes all the conduits registered with a particular creator ID. It returns with the number of conduits removed (or a negative number in the case of an error). The application prints the number of conduits it unregistered.
ConduitDeinstall.exe
#include <Windows.h> #include "CondMgre.h" int main(int argc, char **argv) { const char *kCreator = "Sles"; int numConduitsRemoved = CmRemoveConduitByCreatorID(kCreator); if (numConduitsRemoved >= 0) printf("Unregistration succeeded for %d conduits\n", numConduitsRemoved); else printf("Unregistration failed %d\n", numConduitsRemoved); }
Finding the Correct Conduit Manager DLL
The Conduit Manager calls that our installation program relies on are in a DLL, specifically the CondMgr.DLL. This is quite useful, as we are not required to recompile if the underlying registration architecture changes. A new DLL could register in a different way, and our code won't need to know about it.
There is a problem, however, and it doesn't have a very simple workaround. You might wonder how it is that your installation code could use a new version of the conduit manager DLL. You might assume that Palm Computing would help you out here and ensure that CondMgr.DLL would always be found in the same place. For example, if CondMgr.DLL were installed in the system directory, it would be part of the path that the system searched to load DLLs and would be automatically found and loaded when your installation program ran. Well, things are not that simple.
CondMgr.DLL is not (currently) installed in the system directory when the user installs the Palm Desktop software. Instead, it is put in the same directory as the Palm Desktop software. You might say that this is no big deal; you just need to know where the Palm Desktop software is. The folks at Palm Computing are happy to provide that information-they tell you the path to that directory in the Windows Registry.
Here is the problem. You may remember how we get into the Registry. Yep-using the Conduit Manager APIs that are in the Conduit Manager DLL. It's a chicken-and-egg problem. Fortunately, we have a solution.
The solution to finding CondMgr.DLL
Here's the solution:
1. Check to see if the CondMgr.DLL is in the system path. If so, use it (it was probably installed by a later version of the Palm Desktop software).
2. If not, use a copy of CondMgr.DLL that you ship with your installation program to find the directory containing the Palm Desktop software (CmGetCorePath returns the directory). Check in that directory for CondMgr.DLL. If it's there, use it (it may be newer than the version you are shipping in your installation program).
3. If there's no CondMgr.DLL in the system path, and no CondMgr.DLL in the Palm Desktop software, revert to using the CondMgr.DLL that you ship along with your installation program (Palm Desktop software prior to version 3.0 didn't have CondMgr.DLL).
Implementing the solution
We use a separate program that checks for which CondMgr.DLL to use. Once this program finds that slippery little DLL, it changes the current directory to that location. This location is one of the following:
Then this program launches our real installation program, which automatically loads the CondMgr.DLL from the current directory.
An alternative approach would have been to have one program and call LoadLibrary to explicitly load the CondMgr.DLL we wanted. We didn't go this route because it's not as simple to call routines in an explicitly loaded DLL as it is in an implicitly loaded DLL.
The elements in our installation
Our installation directory contains:
Install.exe
This is the program that figures out which CondMgr.DLL to use. It then changes the current directory and runs ConduitInstall.exe.
ConduitInstall.exe
This application just makes Conduit Manager API calls and is blissfully unaware of the trouble of finding the correct CondMgr.DLL. It implicitly loads CondMgr.DLL (that is, the system loads it when the application starts; if the system can't find CondMgr.DLL, it produces an error-Install.exe sets things up to guarantee the system can find CondMgr.DLL).
CondMgr
A subdirectory containing one file:
CondMgr.DLL
The Palm DLL that we ship with our installation. We use it to find the Palm Desktop directory. In the case that Install.exe can't find an installed CondMgr.DLL, we also use this DLL for our registration.
Here's the entire code for Install.exe (LoadLibrary, GetProcAddress, FreeLibrary, _getcwd, _chdir, and system are all calls provided by the Windows OS):
#include <Windows.h> #include <Condmgre.h> #include <stdio.h> #include <direct.h> #include <process.h> typedef int (WINAPI *CmGetCorePathPtr)(TCHAR *pPath, int *piSize); int main(int argc, char **argv) { int result = 0; char conduitExecutable[_MAX_PATH]; /* Get the current working directory: */ if( _getcwd( conduitExecutable, _MAX_PATH ) == NULL ) { fprintf(stderr, "_getcwd error" ); result = 3; } else strcat(conduitExecutable, "\\ConduitInstall.exe"); if (LoadLibrary("Condmgr.dll")) printf("loaded library using normal path\n"); else { printf("didn't find library in normal path\n"); HINSTANCE lib; if ((lib = LoadLibrary(".\\CondMgr\\CondMgr.dll")) != NULL) { printf("loaded my version of condmgr\n"); char buffer[512]; int size = sizeof(buffer); CmGetCorePathPtr corePathFunc; corePathFunc = (CmGetCorePathPtr) GetProcAddress( lib, "CmGetCorePath"); if (corePathFunc) { if ((*corePathFunc)(buffer, &size) == 0) { char fullPathnameConduitMgr[512]; printf("path = \"%s\"\n", buffer); FreeLibrary(lib); strcpy(fullPathnameConduitMgr, buffer); strcat(fullPathnameConduitMgr, "\\CondMgr.dll"); HINSTANCE full = LoadLibrary(fullPathnameConduitMgr); if (full != NULL) { printf("Found %s\n", fullPathnameConduitMgr); FreeLibrary(full); result = _chdir(buffer); } else { printf("must use our conduit mgr\n"); result = _chdir(".\\CondMgr"); } } } else { fprintf(stderr, "couldn't load CmGetCorePath\n"); result = 1; } } else { fprintf(stderr, "Couldn't load .\\CondMgr\\CondMgr.dll\n"); result = 2; } } if (result == 0) { // we found a library and we've changed directories, // if necessary fprintf(stderr, "running \"%s\"\n", conduitExecutable); result = system(conduitExecutable); if (result != 0) fprintf(stderr, "Calling ConduitInstall failed\n"); } return result; }
As it runs, it prints a commentary of what is happening. When it's complete, it returns 0 in case of success; a nonzero result indicates an error.
Conduit Entry Points |
We told you before that a conduit has three required entry points. There are also some optional ones (including some that are only for CDK 3.0), which we look at next.
Required Entry Points
The required entry points are as follows:
This returns the conduit's name.
This function returns the version of the conduit as a four-byte value. The minor version is in the low byte. The major version is in the next byte. The upper two bytes are unused. A conduit with Version 2.1 would return 0x21.
It is from this entry point that the conduit actually does its work. This point passes a parameter that is a class object with information about the sync. The information includes:
· The username
· Remote database and filename
· The type of synchronization to be performed-copy handheld to desktop, copy desktop to PC, fast sync, slow sync, or do nothing
Optional Entry Points
The optional entry points have to do with customization (and File Linking in 3.0):
ConfigureConduit (CfgConduit is a newer version of this)
This is called when the user wants to customize the conduit by pressing Change in the Custom Hotsync dialog (see Figure 11-2). The conduit is responsible for displaying a dialog and saving user choices. A mirror-image synchronization conduit is responsible for displaying the dialog shown in Figure 11-3. The user chooses what action should happen when a sync occurs (unchecking the permanent checkbox in the dialog specifies that the dialog setting should occur only on the next sync).
Specific conduits may also have different things the user can configure. In any case, conduit configuration should always allow the user the option to do nothing. This way, the user can pick and choose which conduits are active (for example, to expedite syncing just the address book before rushing to a meeting).
If this entry point isn't present in your conduit, pressing the Change button does nothing-an action guaranteed to be confusing and annoying to users. Even if you are unwilling to provide a way for the user to configure your conduit to do nothing, you should provide this entry point and have it tell the user that the conduit can't be configured.
NOTE: Our reasoning relies on an age-old adage of good design: every allowable user action should produce a visible effect. Words to warm a designer's heart. |
Figure 11- 2.
HotSync dialog for customizing conduits
Figure 11- 3.
A conduit's configuration dialog
This is a newer entry point that replaces ConfigureConduit. Its purpose is the same as that of ConfigureConduit, but it receives more information when called. Because it is extensible (due to a variable-size argument block), even more information will probably be provided in the future.
It's called by HotSync Manager 3.0 and later. If this entry point isn't there, HotSync Manager 3.0 reverts to calling ConfigureConduit.
NOTE: Support for calling ConfigureConduit may be phased out in future versions of the HotSync Manager. |
This is called by the HotSync Manager to return the name of the conduit (as an alternative to GetConduitName), the version of Microsoft Foundation Classes (MFC) used to build the conduit, and the default action of the conduit (the choices being: no action, sync, handheld overwrites desktop, or desktop overwrites handheld).
These are the entry points used only for File Linking. (File Linking is provided in HotSync 3.0 or later and is not covered in this book):
If this entry point exists and returns 0, File Linking is supported by this conduit.
Called to provide information necessary for File Linking.
Imports data from a linked file and displays it to the user.
Called to update desktop files when File Linking information changes.
The HotSync Log |
The CDK provides routines that add to a HotSync log. There are several useful routines, but the main one to use is LogAddEntry.
Use this routine to add entries to the HotSync all the time.
LogAddEntry(logString, activity, timestamp)
timestamp
This is a boolean. True means that the log entry will be timestamped.
activity
This is an enumerated type. There are many different enumeration constants available for your use.
The enumerated types used most often as a value for activity are:
Tells the log that your conduit is beginning synchronization. Call the following when you begin the sync process:
LogAddEntry("", slSyncStarted, false)
Tells the log that your conduit is done and that there was an error. Call:
LogAddEntry(your conduit name, slSyncAborted, false)
when you finish syncing with an error.
Tells the log that your conduit is done without errors. Call:
LogAddEntry(your conduit name, slSyncFinished, false)
when you finish syncing without an error.
Adds the specified logString to the log and tells the user at the end of the HotSync that there are messages in the log.
slText
Adds the specified logString to the log, but doesn't tell the user about the message.
LogAddFormattedEntry
Another useful routine is LogAddFormattedEntry. It acts as a combination of sprintf and LogAddEntry and helps if you need to construct the log string from numbers or other strings. Here's an example of its use:
LogAddFormattedEntry(slText, false, "The number (%d) is bad", myNumber)
This is all that you need to know about installation, entry points, and log messages. Next, we discuss the events that occur when the user does a sync.
When the HotSync Button Gets Pressed |
It is worth going through a step-by-step sequence of the events that occur when the user pops a Palm device into the cradle and pushes the HotSync button. From this sequence (started here and continued in the next chapter), you can see exactly when and how the code in your conduit interacts with the desktop, the Palm device, and the Sync Manager.
For the purposes of this example, you should assume that our sample application has been successfully installed and contains no problems. Table 11-1 contains a description on the left of what the user does or what activity is occurring; the right column indicates what's going on programmatically in your conduit or on the desktop.
NOTE: For now, we are just going to wave our hands around when we get to a description of data up/downloading, and exporting and importing. We fill in these gaps in the next chapter. The whole grand system should be clear by that point. |
-Table 11- 1.
What Happens When a Synchronization Occurs
Action (by the User or by the System) |
What Is Happening Programmatically |
User pushes the HotSync Button. |
The handheld sends an "Are you there" message out the serial port until the HotSync Manger on the desktop notices that someone is knocking. |
HotSync synchronizing starts. |
The HotSync Manager negotiates a baud rate with the handheld and begins communication. It reads the user ID and name from the handheld and tries to find a corresponding HotSync user. If it doesn't find one, it prompts on the desktop for the user to select one or to create a new one. |
The user gets the message: Connecting with the desktop. HotSync retrieves from the handheld a list of all databases and their creators. |
For each database on the handheld, the HotSync Manager tries to find a conduit registered for that creator. Databases that don't have a corresponding conduit but that have the backup bit set get added to the list to be backed up by the Backup conduit. Remaining databases are ignored completely. |
3.0 or later-Sync Manager installs new databases. |
The Install conduit gets called to install databases. |
The HotSync Manager determines whether a fast sync is possible (if this is the same desktop machine last synced with) or whether a slow sync is required (if it is different). |
Conduits can take advantage of a fast sync by only reading from the handheld records marked as modified; nonmarked records won't have changed since the last sync. |
The user gets notified that syncing has now started. |
Install conduit gets run and new applications are installed. The HotSync Manager starts the iteration through its list of conduits based on their priority code (as specified when the conduit was registered). |
The HotSync Manager finishes with the conduit prior to ours. |
|
The HotSync Manager prepares to sync. |
Our conduit gets loaded. |
The HotSync Manager checks the conduit's version number. |
GetConduitVersion is called and returns the conduit's version number. |
The HotSync Manager gets the conduit name so that is can display information in the Status dialog. |
GetConduitName is called and returns the name of the conduit. |
The HotSync Manager prepares to sync by passing the synchronization off to the conduit. |
OpenConduit gets called, and the conduit's DLL gets loaded into memory. It is told whether to do a fast sync, a slow sync, a copy from handheld to desktop, a copy from desktop to handheld, or nothing. When OpenConduit returns, it has completed the task. |
The HotSync Manager runs the remaining conduits. |
|
The HotSync Manager backs up modified databases that don't have a corresponding conduit but do have the backup bit set. |
The Backup conduit gets called. |
2.0 or earlier-Sync Manager installs new databases. |
The Install conduit gets called to install databases. |
Handheld notifies applications whose conduits have run that their database(s) have been synced. |
Your handheld application gets a sysAppLaunchCmdSyncNotify launch code if any of its databases have been modified during the sync. |
Syncing is complete. |
Using the Backup Conduit |
You may have an application that doesn't require its own conduit. In such cases, you can rely on the Backup conduit. First, let's discuss the types of applications that can profitably use this approach and then tell you what you need to do to your application.
The Backup conduit works on any application's database that:
Whenever the Backup conduit is used, the data in the database is completely copied from the Palm device to the desktop and saved as a PDB (database) or PRC (application) file. This type of backup occurs during every sync, which is why you don't want to use this as a solution for large databases or most applications.
Applications That Might Use the Backup Conduit
The Backup conduit is well suited to the following types of applications:
Games
Where you save top score information
Utilities
Where you save some user settings
Alarm clocks or other timers
Where you save world clock information or other types of alarm settings
Electronic books
Where you save display information, bookmarks, or the books themselves
Newsreaders
Where you save newsgroup lists
Using System Prefs Instead
Another approach for these types of applications is to use the System Prefs database. This database contains a record for each application that stores preferences. These preferences are automatically backed up because the Systems Prefs database has the backup bit set.
NOTE: A game might want the 512 bytes of high scores backed up (heaven forbid they get lost!), but not the 6K of information about what level the user was on, what weapons were in what hands, etc. |
Setting the Backup Bit for a Database
To set the backup bit, you can use the DmSetDatabaseInfo call on the handheld to change the attributes of a database. Here's code for the handheld that changes the open database myDB:
LocalID theLocalID; UInt theCardNum; UInt theAttributes; DmOpenDatabaseInfo(myDB, &theLocalID, NULL, NULL, &theCardNum, NULL); DmDatabaseInfo(theCardNum, theLocalID, NULL, &theAttributes, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); theAttributes |= dmHdrAttrBackup; DmSetDatabaseInfo(theCardNum, theLocalID, NULL, &theAttributes, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
Note that the backup bit isn't reset automatically after a backup. With devices running versions of the Palm OS prior to 2.0, as long as the backup bit of a database is set-and there is no conduit installed for it-the database is backed up every time the user syncs. With Palm OS 2.0 and later, the database is backed up only if it has been modified since the last sync.
Creating a Minimal Sales Conduit |
Using Visual C++ and the development kit, only a few steps are required to create a minimal conduit. We assume that you've installed the CDK on your C: drive in the \CDK folder. Let's walk through the steps.
NOTE: The final 3.0 version of the CDK promises a Conduit Wizard, which may make this creation process even easier. |
1. Create a new project of type MFC AppWizard (dll) (see Figure 11-4).
2. Specify that the project is a regular DLL using the MFC shared library as shown in Figure 11-5.
3. Add the Conduit SDK's include directory to the list of places the compiler searches for include files. To do this, after you've created the project, open the Project Settings dialog and, in the C/C++ settings panel (see Figure 11-6), add the following to the Project Options area:
/I "C:\CDK\INCLUDE"
4. Add needed libraries to the project in the Link panel of the same dialog (see Figure 11-7). For this minimal conduit, you need to add three libraries, one containing entry points for logging, one containing entry points for the HotSync dialog, and the last containing entry points for the Sync Manager initialization/deinitialization:
C:\CDK\lib\hslog20d.lib C:\CDK\lib\pdcmn21d.lib C:\CDK\lib\sync20d.lib
If you edit the Win32 version of your DLL, link with the nondebug versions of the libraries (hslog20.lib, pdcmn21.lib, and sync20.lib).
-Figure 11- 4.
A new MFC AppWizard project for our do-nothing minimal conduit
Figure 11- 5.
Selecting the type of MFC DLL
Figure 11- 6.
C/C++ Project Settings to add to the include search path
-Figure 11- 7.
Adding libraries to link with
Code for the Sales Conduit
As we said before, this is a conduit that does very little. It considers itself successful if it writes a message to the log file. It's great, however, at distilling the process you use for creating the outer shell of the conduit. We first cover the code and then look at registering and testing the conduit.
The SalesCond.cpp file already contains the shell of a DLL as created by the MFC DLL wizard.
Let's add some include files we need:
#include <afxwin.h> // MFC core and standard components #include <HSLog.h> // for LogAddEntry #include <SyncMgr.h> #include <CondAPI.h> #include <pdcmnDll.h> // for the dialogs #include <cmnres.h> #include <ActDlg.h>
We'll create some constants that define the conduit name and major and minor version numbers:
#define kConduitName "Sales" #define kMajorVersion 1 #define kMinorVersion 0
Adding GetConduitName
The first entry point we look at is GetConduitName. It gets passed a buffer in which it writes the name and the length of that name. It returns 0 in the case of no error.
NOTE: Like all the entry points of the conduit, GetConduitName must call the AFX_MANAGE_STATE macro before doing anything else (this is a requirement of these types of MFC DLLs). All the entry points must also be declared with the __declsepc(dllexport) type modifiers. |
__declspec(dllexport) long GetConduitName(char *name, WORD maxLen) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); memset(name, 0, maxLen); strncpy(name, kConduitName, maxLen-1); return 0; }
Adding GetConduitVersion
Here is GetConduitVersion, whose low byte is the minor version and whose next higher byte is the major version:
__declspec(dllexport) DWORD GetConduitVersion() { AFX_MANAGE_STATE(AfxGetStaticModuleState()); return (kMajorVersion << 8) | kMinorVersion; }
Adding OpenConduit
OpenConduit is passed a class, CSyncProperties, which contains information about the sync that will take place. We're interested in the m_SyncType field of that class. This tells us what type of sync we have. The only type of sync we can handle is eDoNothing. In that case, we write an appropriate message to the log and then return.
For any other type of sync, we begin the sync process by calling SyncRegisterConduit (if that fails, we return the error) then we write to the log that we've begun. When we finish, we write to the log that we've finished (or, if an error had occurred, that we've aborted). We call SyncUnRegisterConduit and return any error:
__declspec(dllexport) long OpenConduit(PROGRESSFN progress, þþþþCSyncProperties &sync) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); long err = 0; if (sync.m_SyncType == eDoNothing) { LogAddEntry("Sales - sync configured to Do Nothing", slText, þþþþfalse); return 0; } CONDHANDLE myConduitHandle; if ((err = SyncRegisterConduit(myConduitHandle)) != 0) { return err; } LogAddEntry("", slSyncStarted, false); // this is where we'll actually sync LogAddEntry(kConduitName, err ? slSyncAborted : slSyncFinished, þþþþfalse); SyncUnRegisterConduit(myConduitHandle); return err; }
Adding ConfigureConduit
Although these three functions, GetConduitName, GetConduitVersion, and OpenConduit, are the only required entry points, we also provide ConfigureConduit, so that the user can change what happens in our conduit on a sync:
__declspec(dllexport) long ConfigureConduit(CSyncPreference& pref) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); long nRtn = -1; CHotSyncActionDlg actDlg; pref.m_SyncPref = eNoPreference; actDlg.m_csGroupText = kConduitName; switch (pref.m_SyncType) { case eFast: case eSlow: actDlg.m_nActionIndex = 0; break; case ePCtoHH: actDlg.m_nActionIndex = 1; break; case eHHtoPC: actDlg.m_nActionIndex = 2; break; case eDoNothing: default: actDlg.m_nActionIndex = 3; } if (actDlg.DoModal() == IDOK) { switch (actDlg.m_nActionIndex) { case 0: pref.m_SyncType = eFast; break; case 1: pref.m_SyncType = ePCtoHH; break; case 2: pref.m_SyncType = eHHtoPC; break; case 3: default: pref.m_SyncType = eDoNothing; break; } pref.m_SyncPref = (actDlg.m_bMakeDefault) ? ePermanentPreference : eTemporaryPreference; nRtn = 0; } return nRtn; }
The code gets a standard HotSync dialog, putting the conduit's name and current sync type setting in it. After the dialog is dismissed, it updates the sync type.
NOTE: We use ConfigureConduit instead of CfgConduit because we want our conduit to work on versions of HotSync earlier than 3.0. Plus, we really don't need the additional information that CfgConduit provides. |
The dialog that ConfigureConduit uses is provided by the pdcmn DLL. In order to use it, our DLL must initialize the pdcmn DLL. We'll do that when our DLL starts.
DLL doings
Here's our DLL's class declaration (as created by the Visual C++ automatically) to which we've overridden InitInstance and ExitInstance:
class CSalesCondDll : public CWinApp { public: //CSalesCondDll(); virtual BOOL InitInstance(); // Initialization virtual int ExitInstance(); // Termination // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CSalesCondDll) //}}AFX_VIRTUAL //{{AFX_MSG(CSalesCondDll) // NOTE - the ClassWizard will add/remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() };
We define a global count to keep track of how many instances of our DLL are active:
static int ClientCount = 0;
Our InitInstance will increment the ClientCount and initialize the pdcmn DLL:
BOOL CSalesCondApp::InitInstance() { // DLL initialization TRACE0("SalesCond.DLL initializing\n"); if (!ClientCount ) { // add any extension DLLs into CDynLinkLibrary chain InitPdcmn5DLL(); } ClientCount++; return TRUE; }
Our ExitInstance will decrement the ClientCount:
int CSalesCondApp::ExitInstance() { TRACE0("UpDownCond.DLL Terminating!\n"); // Check for last client and clean up potential memory leak. if (--ClientCount <= 0) { } // DLL clean up, if required return CWinApp::ExitInstance(); }
Adding GetConduitInfo
We also provide GetConduitInfo. It can return the name of the conduit, the default action of the conduit, as well as the version of MFC used by the conduit:
__declspec(dllexport) long GetConduitInfo(ConduitInfoEnum infoType, void *pInArgs, void *pOut, DWORD *pdwOutSize) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); if (!pOut) return CONDERR_INVALID_PTR; if (!pdwOutSize) return CONDERR_INVALID_OUTSIZE_PTR; switch (infoType) { case eConduitName: if (!pInArgs) return CONDERR_INVALID_INARGS_PTR; ConduitRequestInfoType *pInfo; pInfo = (ConduitRequestInfoType *)pInArgs; if ((pInfo->dwVersion != CONDUITREQUESTINFO_VERSION_1) || (pInfo->dwSize != SZ_CONDUITREQUESTINFO)) return CONDERR_INVALID_INARGS_STRUCT; strncpy((TCHAR*) pOut, kConduitName, (*pdwOutSize) - 1); break; case eDefaultAction: if (*pdwOutSize != sizeof(eSyncTypes)) return CONDERR_INVALID_BUFFER_SIZE; (*(eSyncTypes*)pOut) = eFast; break; case eMfcVersion: if (*pdwOutSize != sizeof(DWORD)) return CONDERR_INVALID_BUFFER_SIZE; (*(DWORD*)pOut) = MFC_VERSION_50; break; default: return CONDERR_UNSUPPORTED_CONDUITINFO_ENUM; } return 0; }
Registering the Conduit
We run CondCfg and add a new entry. Figure 11-8 shows the settings we use.
Figure 11- 8.
Registering the Sales conduit in CondCfg
Testing
Once you've registered the conduit, start the HotSync Manager (quit it first if it is already running so that the registry gets properly updated). If you've registered a debug version of your conduit, make sure you start the debug version of HotSync Manager. Then choose Custom from the HotSync menu. You should see the Sales conduit in the list of conduits (see Figure 11-9).
Figure 11- 9.
The Custom dialog of HotSync showing the list of registered conduits
Proving ChangeConduit works
Select the Sales application and click the Change button. You should see the dialog shown in Figure 11-10. Bringing this dialog up proves that your conduit's ChangeConduit function gets called.
Figure 11- 10.
Changing the HotSync settings of the Sales conduit
Next, it's time to test syncing. First, make sure that the Sales application has been installed on the handheld (otherwise, a database with the correct creator won't exist on the handheld, and the Sales conduit won't be invoked).
Seeing the conduit in the HotSync log
When you sync, you should see the message "Synchronizing Sales" as part of the process. Once a sync has been completed, open the HotSync log for that device. You should see information that includes a line about the Sales conduit. For example:
HotSync started 07/30/98 11:59:53 OK Date Book OK Address Book OK To Do List OK Memo Pad OK Sales OK Expense
Setting the conduit to Do Nothing
Now change the HotSync settings for the Sales conduit to Do Nothing. After you sync, the log should show the following:
HotSync started 07/30/98 12:02:37 OK Date Book OK Address Book OK To Do List OK Memo Pad Sales - sync configured to Do Nothing OK Expense
If you run into any problems getting the conduit to work, see Chapter 14, Debugging Conduits.
Now that you have a conduit shell that has been tested and works correctly, we can continue adding functionality to it. Let's start in the next chapter by uploading and downloading data.
* Palm Computing was acquired by U.S. Robotics, which was in turn acquired by 3Com.
Palm Programming: The Developer's Guide