Order the book from O'Reilly

Previous PageTable Of ContentsIndexNext Page

In this chapter:

 11.  Getting Started
with Conduits

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

Top Of Page

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

Top Of Page

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:

 CmSetCreatorName

Sets the required Conduit entry point

 CmSetCreatorDirectory

Sets the required Directory entry point

 CmSetCreatorFile

Sets the file entry point

 CmSetCreatorPriority

Sets the priority entry point

 CmSetCreatorTitle

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:

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

Top Of Page

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:

 GetConduitName

This returns the conduit's name.

 GetConduitVersion

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.

 OpenConduit

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:

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

 CfgConduit

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.

 GetConduitInfo

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):

 SubscriptionSupported

If this entry point exists and returns 0, File Linking is supported by this conduit.

 ConfigureSubscription

Called to provide information necessary for File Linking.

 ImportData

Imports data from a linked file and displays it to the user.

 UpdateTables

Called to update desktop files when File Linking information changes.

The HotSync Log

Top Of Page

The CDK provides routines that add to a HotSync log. There are several useful routines, but the main one to use is LogAddEntry.

 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:

 

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

Top Of Page

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

Using the Backup Conduit

Top Of Page

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:

Actually, when you create system preferences, you can specify whether you want them to be backed up or not (a true value for the saved parameter to  PrefSetAppPreferences means you want the preferences backed up). If you've got some information that you want to save between calls to your application but that you don't need backed up, you'll use the nonbacked-up preferences (a false value for the saved parameter).

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

Top Of Page

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
Copyright © 1999, O'Rielly and Associates, Inc.
Published on the web by permission of O'Rielly and Associates, Inc. Contents modified for web display.

Previous PageTop Of PageTable Of ContentsIndexNext Page