CONDUIT Developing Palm OS Conduits 08.96
CONDUIT Developing Palm OS Conduits 08.96
CONDUIT Developing Palm OS Conduits 08.96
Developing Palm OS
Conduits
08.96
Navigate this online document as follows:
To see bookmarks Type Command-7
To see information on Type Command-?
Adobe Acrobat Reader
To navigate Click on
any blue hypertext link
any Table of Contents entry
arrows in the menu bar
©1996 U.S. Robotics, Inc. All rights reserved.
Documentation stored on the compact disk may be printed by licensee for personal use.
Except for the foregoing, no part of this documentation may be reproduced or transmit-
ted in any form by any means, electronic or mechanical, including photocopying, record-
ing, or any information storage and retrieval system, without permission in writing from
U.S. Robotics.
U.S. Robotics, the U.S. Robotics logo and Graffiti are registered trademarks, and Palm
Computing, HotSync, Palm OS, and the Palm OS logo are trademarks of U.S. Robotics
and its subsidiaries.
All other trademarks or registered trademarks are the property of their respective
owners.
1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . .9
What’s a Conduit? . . . . . . . . . . . . . . . . . . . . .9
What Are Development System Requirements? . . . . . . . . 10
What’s in the Conduit SDK? . . . . . . . . . . . . . . . . 11
Overview of the Conduit SDK . . . . . . . . . . . . . . 11
Top-Level Directories . . . . . . . . . . . . . . . . . . 11
SDK Development Directories . . . . . . . . . . . . . . 12
Directories . . . . . . . . . . . . . . . . . . . . . 12
Files . . . . . . . . . . . . . . . . . . . . . . . . 12
Conduits Sample Source Code Directory Contents . . . . 12
What About HotSync1.1 . . . . . . . . . . . . . . . . . . 13
What’s in This Guide? . . . . . . . . . . . . . . . . . . . 13
2 Conduit Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Basic Approaches to Conduit Design . . . . . . . . . . . . 15
Conduit Basic Control Flow . . . . . . . . . . . . . . . . 16
Locating Records on the Device. . . . . . . . . . . . . . . 19
Minimum Conduit Requirements . . . . . . . . . . . . . . 21
Registering the Conduit . . . . . . . . . . . . . . . . . 21
Providing C Entry Points . . . . . . . . . . . . . . . . 21
Providing a DllMain() Routine . . . . . . . . . . . . . . 21
Sending Errors and Other Messages . . . . . . . . . . . 22
SyncManager Memory Management . . . . . . . . . . . . 24
Structures with Dynamically Allocated Memory: . . . . . 24
Conduits and the Windows Registry . . . . . . . . . . . . 24
Naming Third Party Conduits . . . . . . . . . . . . . . 25
Registering Third Party Conduits. . . . . . . . . . . . . 25
Providing the Conduit Name . . . . . . . . . . . . . 25
Providing Name/Data Pairs. . . . . . . . . . . . . . 26
Registry Entry Example. . . . . . . . . . . . . . . . 28
Default Registry Keys . . . . . . . . . . . . . . . . . . 28
Installing and Removing Your Conduit . . . . . . . . . . . 29
5 Implementing a Conduit . . . . . . . . . . . . . . . . . . . . . . 47
Providing “C” Entry Points . . . . . . . . . . . . . . . . 47
Providing a DllMain Routine . . . . . . . . . . . . . . 48
Providing Entry Point Routines . . . . . . . . . . . . . 50
The OpenConduit Function . . . . . . . . . . . . . . 50
The GetConduitName Function . . . . . . . . . . . . 52
The GetConduitVersion Function . . . . . . . . . . . 52
Creating a CBaseMonitor Subclass . . . . . . . . . . . . . 53
CBaseMonitor Basic Structure . . . . . . . . . . . . . . 53
CBaseMonitor Data Members . . . . . . . . . . . . . . 55
CBaseDTLinkConverter* m_pDTConvert . . . . . . . . 55
PROGRESSFN m_pfnProgress . . . . . . . . . . . . . 55
CBaseTable* m_LocRealTable . . . . . . . . . . . . . 56
CBaseTable* m_LocArchTable . . . . . . . . . . . . . 56
CBaseTable* m_BackupTable . . . . . . . . . . . . . 56
CBaseTable* m_RemRealTable . . . . . . . . . . . . . 56
CSyncProperties m_rSyncProperties . . . . . . . . . . 57
CCategoryMgr* m_LocCategory . . . . . . . . . . . . 57
CCategoryMgr* m_RemCategory . . . . . . . . . . . 57
BYTE m_RemHandle . . . . . . . . . . . . . . . . . 57
char m_ArchFileExt[5] . . . . . . . . . . . . . . . . 57
int m_TotRemoteDBs . . . . . . . . . . . . . . . . . 57
int m_CurrRemoteDB . . . . . . . . . . . . . . . . 58
CDbGenInfo m_DbGenInfo . . . . . . . . . . . . . . 58
HINSTANCE m_DllInstance . . . . . . . . . . . . . 58
CBaseMonitor Functions Must to Override . . . . . . . . 58
Monitor Constructor and Destructor . . . . . . . . . . 60
ObtainLocalTables . . . . . . . . . . . . . . . . . . 61
ObtainRemoteTables . . . . . . . . . . . . . . . . . 62
AddRecord . . . . . . . . . . . . . . . . . . . . . 64
AddRemoteRecord. . . . . . . . . . . . . . . . . . 65
ChangeRemoteRecord . . . . . . . . . . . . . . . . 66
CreateLocalArchTable . . . . . . . . . . . . . . . . 68
FastSyncRecords . . . . . . . . . . . . . . . . . . . 69
SlowSyncRecords . . . . . . . . . . . . . . . . . . 71
CopyRecordsPCtoHH . . . . . . . . . . . . . . . . 74
CopyRecordsHHtoPC . . . . . . . . . . . . . . . . 76
LogRecordData . . . . . . . . . . . . . . . . . . . 78
LogApplicationName . . . . . . . . . . . . . . . . 79
CBaseMonitor Functions You May to Override . . . . . . . 79
SaveLocalTables . . . . . . . . . . . . . . . . . . . 80
PurgeLocalDeletedRecs . . . . . . . . . . . . . . . . 81
ApplyRemotePositionMap . . . . . . . . . . . . . . 83
Creating a CBaseDTLinkConverter Subclass . . . . . . . . . 84
CBaseDTLinkConverter Basic Structure . . . . . . . . . . 84
The Log Object . . . . . . . . . . . . . . . . . . . 85
Casting of Member Functions . . . . . . . . . . . . . 85
Carriage Returns and Line Feeds. . . . . . . . . . . . 85
CBaseDTLinkConverter Data Members . . . . . . . . . . 85
CSyncLog* m_pLog . . . . . . . . . . . . . . . . . 86
TCHAR* m_TransBuff . . . . . . . . . . . . . . . . 86
HINSTANCE m_DllInstance . . . . . . . . . . . . . 86
CBaseDTLinkConverter Functions You Must Override . . . 87
SyncPurgeDeletedRecs . . . . . . . . . . . . . . . . . 123
SyncReadNextModifiedRec . . . . . . . . . . . . . . . 123
SyncReadRecordById . . . . . . . . . . . . . . . . . . 124
SyncReadRecordByIndex . . . . . . . . . . . . . . . . 125
SyncReadResRecordByIndex. . . . . . . . . . . . . . . 125
SyncWriteRec . . . . . . . . . . . . . . . . . . . . . 126
SyncWriteResourceRec . . . . . . . . . . . . . . . . . 127
Utility Calls . . . . . . . . . . . . . . . . . . . . . . . 128
SyncReadDBList . . . . . . . . . . . . . . . . . . . . 128
SyncReadSingleCardInfo . . . . . . . . . . . . . . . . 129
SyncReadSystemInfo . . . . . . . . . . . . . . . . . . 130
What’s a Conduit?
A conduit is a dynamic link library (DLL) running under Microsoft Win-
dows. Conduits exchange and synchronize data between an application
running on a PC under Windows and an application running on the Pilot or-
ganizer or another Palm OS based device.
End-users can push the HotSync button on the cradle to request synchroni-
zation of data between all device applications and the corresponding Win-
dows applications. To do so, the end-user must perform the following two
steps:
1. Insert the device into its cradle, which has to be connected to the PC
with a serial cable
2. Press the HotSync button on the cradle
The HotSync program, which runs under Windows, synchronizes each ap-
plication by executing its conduit.
Many conduits (including the conduits for the four native applications on
the first device) synchronize data between the device and the PC to be mir-
ror images after synchronization. Other conduits perform more complex
operations. The complexity of your conduit’s behavior determines the de-
velopment effort involved.
To make things easier for you, part of the conduit SDK consists of several
C++ classes that provide predetermined functionality that you may be able
to customize to suit your needs. The four applications included on the first
Pilot device (Date book, address book, ToDo list, and MemoPad) use con-
duits based on those classes and the associated synchronization logic.
Source code for each of the four native applications is part of the SDK.
If your application doesn’t sync with one of the four native Pilot applica-
tions, or your application’s behavior is so different from the existing con-
duits behavior (the native synchronization logic) that customizing becomes
impractical, you can still take advantage of the SyncManager API. In that
case, you should do the following:
• Read, at a minimum, the chapter Conduit Basics in this document.
You may find it helpful to look at other chapters as well.
• Look at the documentation in SyncManager Function Calls
• Examine a small example conduit (\poscond\txtcond) with more
simple behavior to write your conduit from scratch
Conduits are AFX extension modules; see MFC Tech Note 33 for more in-
formation. Note that the sample Makefile provided with the Conduit SDK
automatically makes your conduit an AFX extension.
Top-Level Directories
During installation, the SDK creates the following directory structure on
your system:
\POSCOND\—Main directory
CONDSDK—SDK development files and documentation
TODCOND—Code for the sample ToDo conduit
TXTCOND—Code for the sample text file conduit
ADDCOND—Code for the sample Address Book conduit
DATCOND—Code for the sample Date Book conduit
MEMCOND—Code for the sample Memo Pad conduit
Files
\POSCOND\CONDSDK\INCLUDE
\abrecord.h—Address Book record class definition \dataprv.h—
Definitions and structs for Pilot database records \syncmgr.h—Syn-
cManager public API and structures
\updcatid.h—Object used for updating categories
\logstrng.h—#defines for resource string IDs
\catmgr.h—Category manager class definitions
\basemon.h—BaseMonitor class definition
\synclog.h—CSyncLog class definition
\Nativetable.h—Table subclass definition for native app
\Nativerecord.h—Record class definition for native app
\basetabl.h—Base table class definition
\bfields.h—Field objects class definitions
\basemon.rc—Resource file containing error and log strings
\basemon.cpp—Source code to the base monitor class
\TXTCOND
\STEP01—A simple text file transfer to the Pilot MemoPad
\STEP02—More advanced version of Step01 source code
records
BaseLinkConverter
categories
AddrLinkConverter
records
device
iterator
CBaseMonitor
remote table
AddrMonitor schema
CategoryMgr records
...
local table
categories local
categories ObtainLocalTables()
records desktop
records data files
records
categories
Because the SyncManager DLL acts as a channel for byte traffic to and
from the device, a generic structure that handles any record format is
needed. This generic structure then becomes a parameter in the record-ori-
ented API.
To locate remote records, three different APIs are provided, allowing you to
do the following:
• Sequentially locate the next altered record using
SyncReadNextModifiedRec.
• An exact record lookup using SyncReadRecordById.
• Top to bottom iteration using SyncReadRecordByIndex.
The same object, CRawRecordInfo, is used by all three functions. How-
ever, different members of the object are used by each function call to help
indicate the nature of the remote lookup activity.
Figure 2.1 illustrates where the fixed-length data from a device record is
stored in the CRawRecordInfo object and also shows that the data mem-
ber m_pBytes points to the variable-length record body. These CRawRe-
cordInfo structure members are populated by the record-oriented API
when a record has been retrieved successfully.
class CRawRecordInfo{
public
BYTE m_FileHandle;
DWORD m_RecId;
WORD m_RecIndex; fixed length
BYTE m_Attribs;
short m_CatId;
int m_ConduitId;
DWORD m_RecSize;
WORD m_TotalBytes;
BYTE* m_pBytes; variable length
Note that the Makefile provided for compiling the library makes it an AFX
extension library, which means that certain classes outside the included
files are available to your application.
class CSyncLog {
public:
CSyncLog(int nFlushThreshold = 0);
~CSyncLog();
LogError AddEntry( const char* pszEntry,
Activity act=slText,
BOOL bTimeStamp = FALSE);
LogError SaveLog(const char* pszLogFile);
BOOL BuildRemoteLog(CString& csRemoteLog);
void GetWorkFileName(CString& csWorkFileName);
void CloseLog();
WORD TestCounters(); };
When all conduits have completed, HotSync saves the log object to disk.
The member function most often used by a conduit to log information is
the AddEntry() routine. In its simplest form, a string may be recorded
into the log:
The other parameters to AddEntry have default values that the caller may
but doesn’t have to override:
• The act parameter is a member of the Activity enum defined in
SYNCLOG.H and discussed below.
• The bTimeStamp parameter signals the log object to time stamp
the new entry as it is added to the log. In most cases, you can leave
this parameter undefined (the default).
Here’s some information about the values you’re most likely to supply as
the act parameter, and how to use them:
• slSyncStarted—send at the beginning of the synchronization pro-
cess, don’t supply a text string. This is required so the log knows
you are logging a new conduit.
• slSyncFinished—send at the end of the synchronization process
and pass in the name of the application.
• slSyncAborted—requests that HotSync put up banner that a prob-
lem occurred.
• Other enum values—these will signal HotSync to display banner to
the user at the conclusion of the sync. They may be passed with a
text string to be included in the log file.
The following example code shows a conduit reporting that it encountered
a problem adding a new record to the remote device database.
if (AddRemoteRecord(rLocRecord)!= 0)
{
char errBuff[MAX_LOG_STRING];
strcpy(errBuff, "Could not Add the Smith
address record");
m_rSyncProperties.m_pSyncLog->AddEntry(
(const char*)errBuff, slRemoteAddFailed);
}
Note that if you are supplying a de-installation procedure with your con-
duit, you need to be sure that all conduits loaded after it are renamed to
maintain the proper numbering sequence. You may archive this either by
changing the name of the last conduit to have the name of the deleted con-
duit or by changing the name of each conduit. The important issue is that
the sequence of numbers is not interrupted.
Integrate DWORD 0
Priority DWORD 2
Name Data
Conduit String indicating the disk filename of the third party conduit
DLL. This disk file needs to be placed somewhere within
the PATH environment variable. Conduits are generally in-
stalled in the same directory as HotSync.
Module A string which for third party entries can contain anything.
This string is not used, but must be present.
Name Data
Name A string which is displayed in the HotSync Progress dialog
to identify which conduit is currently executing.
[HKEY_CURRENT_USER\Software\Palm Computing\/
Pilot Desktop\Component2]
"Module"="todo.dll"
"Conduit"="todcn11d.dll"
"Creator"=dword:746f646f
"Directory"="todo"
"File0"="todo.dat"
"Remote0"="ToDoDB"
"Priority"=dword:1
HotSync -r
– BAKCN11.DLL
– INSCN11.DLL
– PDN11.DLL
– PDCMN11.DLL
4. Copy the 1.1 conduits to the Pilot Desktop directory:
– ADDCN11.DLL
– DATCN11.DLL
– MEMCN11.DLL
– TODCN11.DLL
5. For Windows 3.1 installation, edit hsm11.ini OR.
6. For Windows95 or Windows NT installation, make the following
changes to the registry:
This may be automated using a registry extract file. See the release
notes for more information.
Conduit Installation
After you’ve successfully installed HotSync1.1 and modified the registry
appropriately, you can install your conduit as follows:
1. Use GetProfileInt to look for the first open ApplicationX key,
starting with Application0. Increment until you’ve found the last
application (ApplicationN).
2. Add 1 to that number and add your application to the registry as
(ApplicationN + 1) using SetProfileInt
3. Add the standard name/data pairs for your application to the regis-
try using SetProfileInt (see Providing Name/Data Pairs)
NOTE: You have to restart HotSync at this time and at any other
time you’ve made changes to the registry (or .INI file).
In most cases, all other steps in the synchronization process are identical
regardless of whether cable or modem connection is specified, and there is
no impact on the conduit or the function calls needed for synchronization.
ers. Therefore, your conduit should check the user name field
passed with the SyncProperties structure and use it to decide which
PC data to sync with, if any.
• When synchronizing two applications (PC + Pilot) whose fields do
not match exactly, great care should be taken in mapping the fields
to one another. This is the most critical and user-noticeable design
decision you will make in your conduit development. Is data loss
acceptable when fields on one side don’t have counterparts on the
other side? If not, the following two strategies have been used suc-
cessfully:
– Hide the extra data fields on one side in the note field on the
other side
– Cache the extra fields (from either or both sides) in the same file
used for record ID mapping on the PC
Algorithm
Most of the default synchronization logic for the sample conduits resides in
BaseMon.cpp. This default synchronization will produce identical data on
both platforms (the PC and the Pilot) at the end of the sync.
The default synchronization logic assumes that each PC and device appli-
cation record has a status field to indicate that one of the following con-
ditions has occurred since the last synchronization:
• The record has not been modified (No Modify)
• The record has just been added (Add)
• The record has been modified (Modify)
• The record has been deleted (Delete)
• The record has been archived (Archive)
Note: Archive means to save the record in an Archive file and re-
move the record from the current platform.
The conduit compares Pilot records with records in the PC table, and takes
an action based on the status of each record. The following table summa-
rizes the possible synchronization cases and describes the action taken to
synchronize the records.
Pilot PC Action
Delete Modify Instead of deleting the Pilot record, replace the Pilot record
with the PC record. Message is sent to the log.
Modify Delete Instead of deleting the PC record, replace the PC record with
the Pilot record. Message is sent to the log.
Pilot PC Action
No Modify Modify Replace the Pilot record with the PC record.
Modify Modify If changes are different, add the Pilot record to the PC, and
add the PC record to Pilot. Message is sent to the log.
Archive No Record/ Archive the Pilot record. If the PC record exists, delete it.
No Modify
Archive Delete Archive the Pilot record. Delete the PC record.
Archive with Modify Instead of archiving the Pilot record, replace the Pilot record
No Modify with the PC record. Message is sent to the log.
Archive after Modify If the records are identical, archive the Pilot record and delete
Modify the record from the PC.
Archive after Modify If changes are different, do not archive the Pilot record. Add
Modify the Pilot record to the PC and add the PC record to Pilot.
Message is sent to the log.
No Record/ Archive Archive the PC record. If the Pilot record exists, delete it.
No Modify
Modify Archive with Instead of archiving the PC record, replace the PC record
No Modify with the Pilot record. Message is sent to the log.
Modify Archive after If the records are identical, archive the PC record and delete
Modify the record from Pilot.
Modify Archive after If changes are different, do not archive the PC record. Add
Modify the Pilot record to the PC, and add the PC record to Pilot.
Message is sent to the log.
Applications
To perform record-level synchronization with the four native applications
on the first Pilot device, the third-party database schema should meet the
following guidelines:
• A unique key (usually a record ID) must be present in each record
of the database. If the unique key of a record is a user-editable field,
or combination of fields, comparisons will be somewhat less reli-
able.
Unique ID’s for Pilot records should be assigned by the Pilot appli-
cation. To do this, a new record is passed to the Pilot (via a Sync-
Manager call) with an ID of 0. The Pilot will return the assigned ID
number.
• A one-to-one relationship must exist between individual records in
both databases.
While it may seem obvious when thinking about an address data-
base (for instance), this becomes an issue when dealing with the Pi-
lot's Date Book database. The Pilot Date Book stores all repeating
event information in a single physical database record, as it does
with non-repeating events. However, some Desktop PIMs produce
multiple physical database records when they store repeating event
data. This makes record-level synchronization much more difficult.
Archiving Records
For the sample Pilot Desktop conduits, records that are marked ‘archive’
are placed in the appropriate archive file depending on the application and
category. The archive filename is derived from the category name and ap-
plication extension. For example, an archived Address Book record under
the Unfiled category would be saved in a file called UNFILED.ABA. All
archived records, whether they originate from Pilot or the PC, are stored on
the PC. Archive files can be read into the desktop application using the
OpenArchive command.
During synchronization, after the records marked to be archived are added
to the PC Archive file, they are deleted from their current platform.
Consider using this feature or a similar one in your conduit. It can be a sim-
ple way of segmenting the data between the PC and Pilot.
We strongly recommend implementing this feature if your conduit sync’s
with one of the standard Pilot applications. Because archiving is a feature
Start
ObtainRemoteTables() Open device database
End
You might also need a postprocessor to do some work after the link con-
verter has done its conversion.
Synchronizing Categories
Categories are a central data handling concept for all Palm OS applications.
Pilot Desktop’s native logic synchronizes categories first. For more infor-
mation, see Considering Category Manager Modifications before starting
on the records.
extern “C” {
typedef long (*PROGRESSFN) (char*);
// Filename: mycond.cpp
// Description: Source code for the Windows
// DllMain() function and the Conduit routine
//'Open Conduit()'.
myInst = hInstance;
}
else if (dwReason ==DLL_PROCESS_DETACH)
{
TRACE0(“ADDCOND.DLL Terminating!”);
if (pMonitor)
{
retval = pMonitor->Engage();
delete pMonitor;
}
}
return(retval);
}
class CSyncProperties {
public:
eSyncTypes m_SyncType;
char m_PathName[256];
char m_LocalName[256];
char m_UserName [256];
char* m_RemoteName[DB_NAMELEN];
CDbList* m_RemoteDbList[DB_NAMELEN];
int m_nRemoteCount;
CSyncLog* m_pSyncLog;
DWORD m_Creator;
WORD m_CardNo;
DWORD m_DbType;
DWORD m_AppInfoSize;
DWORD m_SortInfoSize;
eFirstSync m_FirstDevice;
eConnType m_Connection;
char m_Registry[256];
HKEY m_hKey;
};
//
// Base Monitor
//
class CBaseConduitMonitor {
protected:
CBaseDTLinkConverter* m_pDTConvert;
PROGRESSFN m_pfnProgress;
CBaseTable* m_LocRealTable;
CBaseTable* m_LocArchTable;
CBaseTable* m_BackupTable;
CBaseTable* m_RemRealTable;
CSyncProperties m_rSyncProperties;
CCategoryMgr* m_LocCategory;
CCategoryMgr* m_RemCategory;
BYTE m_RemHandle;
char m_ArchFileExt[5];
int m_TotRemoteDBs;
int m_CurrRemoteDB;
CDbGenInfo m_DbGenInfo;
HINSTANCE m_DllInstance;
CBaseDTLinkConverter* m_pDTConvert
A converter object that is usually created from within the constructor for
the CBaseMonitor subclass. The converter must understand the record
layouts living on the Palm OS device; it has to transform that record infor-
mation from device format into a format the synchronization logic can use.
Most conduits derive a class from CBaseDTLinkConverter to handle
new file formats on the Palm OS device (see Creating a CBaseDTLinkCon-
verter Subclass).
PROGRESSFN m_pfnProgress
HotSync supplies this function pointer. It allows the conduit to call back
into HotSync and periodically report its progress. This function pointer
currently has no effect on HotSync, and is in place for possible future ex-
pansion. For now, the only requirement of a monitor subclass is to pass the
first parameter of its constructor down to its base classes constructor, and
ignore this data member.
CBaseTable* m_LocRealTable
This data member is a pointer to the table that contains all the records on
the PC. The function ObtainLocalTables opens and reads in this ta-
ble.
Your base monitor subclass must create an instance of your subclass of the
CBaseTable class (see Creating a CBaseTable Subclass), that is then
used to store the data retrieved by ObtainLocalTables.
CBaseTable* m_LocArchTable
Represents an archive database on the PC; a file which can store all records
from the main database the user marked for archiving. This table and
m_LocRealTable have to be an instance of the same subclass. Initialize
this table in the function CreateLocalArchTable.
CBaseTable* m_BackupTable
This data member and the m_LocRealTable data member must belong
to the same class. This table represents a backup of the original PC data-
base after the last synchronization. It provides a snapshot of the PC data as
it looked after the last synchronization session ended.
A backup table is important when HotSync has to perform a slow synchro-
nization. HotSync decides to perform a slow synchronization when it finds
that no record status flags are set on the device’s database. This may occur
if the device initiates a synchronization session with more than one PC be-
cause the built-in synchronization logic clears all the status flags at the end
of a session, in preparation for detecting future record alterations.
CBaseTable* m_RemRealTable
This table object represents the database on the device that will be synchro-
nized with its counterpart on the PC. This table and the
CSyncProperties m_rSyncProperties
This data member is a copy of the SyncProperties object passed from
HotSync into a CBaseMonitor subclass constructor. Normally, the con-
structor of a CBaseMonitor subclass should simply pass the CSyn-
cProperties parameter down to its base class constructor, where a copy
is made into this data member.
CCategoryMgr* m_LocCategory
A pointer to the category manager that contains all the categories that exist
on the PC.
CCategoryMgr* m_RemCategory
A pointer to the category manager that contains all categories that exist on
the device.
BYTE m_RemHandle
If the SyncManager calls ObtainRemoteTables, and if the device success-
fully opens the named database during execution, a handle is returned by
the function call. The returned handle should be stored in the data member
m_RemHandle, because it’s needed by several of the SyncManager func-
tions.
char m_ArchFileExt[5]
Holds the PC file extension that is used when creating a local archive disk
file. This data member may be populated within the virtual member func-
tion ObtainLocalTables. It should be a NULL-terminated string.
int m_TotRemoteDBs
Holds the number of remote databases to be opened during the current syn-
chronization session. This is currently always set to 1 but provided in case a
conduit has to open more than one remote database to synchronize cor-
rectly with a local PC database(s).
A limitation on the current Pilot device prevents more than one remote da-
tabase to be open concurrently.
int m_CurrRemoteDB
Holds the current offset into an array of remote database names (zero-
based). When a conduit is dealing with more than one remote database,
HotSync hands it an array of database names within the
CSyncProperties object.
CDbGenInfo m_DbGenInfo
Used by SyncManager function calls as a convenience to the built-in syn-
chronization logic. Subclasses of CBaseMonitor don’t need to perform
any actions on this data member.
HINSTANCE m_DllInstance
Used by the CBaseMonitor class for discovering strings from a resource
file and for logging conflicts. The third parameter in the constructor, which
represents the instance handle for the Conduit.DLL, must be passed down
to the base class constructor. The keyword ExportFunc used in the Open-
Conduit function prototype is defined in the header file SYNCMGR.H and
exports the OpenConduit function from the DLL. This eliminates the need
to place it in the EXPORTS section of the module definition file.
• CreateLocalArchTable
• FastSyncRecords
• SlowSyncRecords
• CopyRecordsPCtoHH
• CopyRecordsHHtoPC
• LogRecordData
• LogApplicationName
By default, these virtual functions in CBaseMonitor do nothing. Unless
the subclass supplies working code for them, the conduit fails.
NOTE: You can use the code in the Examples section, which is
from the CAddressConduitMonitor subclass, as a template for the
functions you write.
CBaseConduitMonitor(PROGRESSFN pFn,
CSyncProperties&,
HINSTANCE hInst = NULL)
~CBaseMonitor()
virtual long ObtainRemoteTables(void)
virtual long ObtainLocalTables()(void)
virtual long AddRecord( CBaseRecord& rFromRec,
CBaseTable& rTable)
virtual long AddRemoteRecord(CBaseRecord& rRec)
virtual long ChangeRemoteRecord(CBaseRecord& rRec)
virtual long CreateLocalArchTable(CBaseTable*&)
virtual long FastSyncRecords(void)
virtual long SlowSyncRecords(void)
virtual long CopyRecordsPCtoHH(void)
virtual long CopyRecordsHHtoPC(void)
virtual long LogRecordData( DBaseRecord& rRec,
char* fieldInfo)
virtual long LogApplicationName (char* appName,
WORD, len)
Purpose Every class derived from CBaseMonitor must supply its own construc-
tor. This constructor is called from the C entry point routine OpenCon-
duit as part of the conduit start-up. This constructor has to pass all three
parameters to the CBaseMonitor constructor. Additional responsibilities
are:
• To construct a proper converter object and populate the data mem-
ber m_pDTConvert with its address.
• To set the following data members:
m_TotRemoteDBs = 1
m_CurrRemoteDB = 0
Example
ObtainLocalTables
Parameters None.
Example
long CAddressConduitMonitor::ObtainLocalTables(void)
{
long retval= CONDERR_BAD_LOCAL_TABLES;
long lTblErr;
CString dataFile(m_rSyncProperties.m_PathName);
dataFile += m_rSyncProperties.m_LocalName;
dataFile += DATA_EXT;
// Create our local table object and open it's disk file.
m_LocRealTable = new CAddressTable();
if (m_LocRealTable) {
retval = 0;
if (m_rSyncProperties.m_SyncType != eHHtoPC) {
lTblErr = m_LocRealTable->OpenFrom(dataFile, 0);
if (!(lTblErr == 0 || lTblErr == DERR_FILE_NOT_FOUND))
retval = CONDERR_BAD_LOCAL_TABLES;
}
}
// Create our local archive table object
if (!retval)
m_LocArchTable = new CAddressTable();
if (m_LocArchTable)
{
// Set Archive File Extension
strcpy(m_ArchFileExt, ARCHIVE_FILE_EXT);
return(retval);
}
ObtainRemoteTables
Parameters None.
Example
long CAddressConduitMonitor::ObtainRemoteTables(void)
{
long retval;
// Call into SyncManager.DLL to open the Remote database
retval =
SyncOpenDB(m_rSyncProperties.m_RemoteName[m_CurrRemoteDB], 0,
m_RemHandle);
AddRecord
Purpose To populate the rTable table object with a new record using the rTable
record object). The main purpose of this routine is to cast the generic in-
coming parameters to the specific table object needed by the conduit. The
routine is called by the CBaseMonitor generic synchronization logic,
where it does not have the typing information necessary to deal with all
possible CBaseRecord subclasses. This function relies on a member
function of the CBaseTable class, which adds a new record then popu-
lates it with information passed into it. (See AppendDuplicateRecord
of the CBaseTable class.)
Example
long retval=0;
if (rTable.AppendDuplicateRecord(rFromRecord, toRec))
retval = CONDERR_ADD_LOCAL_RECORD;
return(retval);
}
AddRemoteRecord
Purpose Add a new record to the remote database and obtain the newly assigned
unique record ID.
• Allocate enough memory for the device format record layout.
• Convert the passed-in table record to the format needed by the de-
vice. This is done by the ConvertToRemote function in the link con-
verter.
• Use the SyncManager to send the device data to the Palm OS de-
vice.
• After the SyncManager call, obtain the new unique record ID as-
signed by the device and store it in the ID field of the passed-in
record object.
This function is called by the CBaseMonitor generic synchronization
logic, where it does not have the typing information necessary to convert
the base record object to the specific record layout used by the device.
CONDERR_ADD_REMOTE_RECORD
CONDERR_CONVERT_TO_REMOTE_REC
Example
memset(&rawRec, 0, sizeof(rawRec));
rawRec.m_FileHandle = m_RemHandle; // remote file handle
rawRec.m_RecId = 0 ;
// Palm OS device assigns new RecId
ChangeRemoteRecord
Purpose To alter an existing record in the remote database on the device. The record
is located by its unique key, the record ID.
• Ask for the unique record ID present in the passed-in record object.
(This will be used as a key to look up the matching record on the
device.)
• Allocate enough memory to hold the converted record format.
• Call on the converter data member to do the actual data conversion
from a base record object to the record layout acceptable by the re-
mote database.
• Call the SyncManager function SyncWriteRec to send the data
to the Palm OS device.
Example
long CAddressConduitMonitor::ChangeRemoteRecord
(CBaseRecord& rRec)
{
CRawRecordInfo rawRec;
CAddressRecord &rLocRec = (CAddressRecord&)rRec;
long retval = CONDERR_CHANGE_REMOTE_RECORD;
int locRecId;
memset(&rawRec, 0, sizeof(rawRec));
rLocRec.GetRecordId(locRecId);
rawRec.m_FileHandle = m_RemHandle;
// remote file handle
rawRec.m_RecId = (DWORD)locRecId;
// key used for record location
if (!m_pDTConvert->ConvertToRemote(rLocRec, rawRec))
retval = SyncWriteRec(rawRec);
else
retval = CONDERR_CONVERT_TO_REMOTE_REC;
CreateLocalArchTable
Purpose To create a conduit-specific table object to work with all archived records.
The generic synchronization engine calls this virtual function when it’s
processing deleted records that optionally get stored in an archive database
after removal from the main table. The archive table has to use the same
schema as the main table (see CBaseSchema Class).
Example
long CAddressConduitMonitor::CreateLocalArchTable(
CBaseTable*& pBase)
{
long retval = -1;
pBase = new CAddressTable();
if (pBase)
retval = 0;
return(retval);
FastSyncRecords
Parameters None.
Example
long CAddressConduitMonitor::FastSyncRecords(void)
{
long retval = 0, err = 0;
CRawRecordInfo rawRecord;
CAddressRecord locRecord(*m_LocRealTable, 0);
CAddressRecord backRecord(*m_BackupTable, 0);
memset(&rawRecord, 0, sizeof(rawRecord));
rawRecord.m_FileHandle = m_RemHandle;
if (!m_RemRealTable)
return(CONDERR_BAD_REMOTE_TABLES);
// Create record object to be a holding buffer for converted
// remote raw records. To store field values in a record
// object, our table object requires they be positioned in
// order.
CAddressRecord remRecord(*m_RemRealTable, 0);
if (m_RemRealTable->AppendBlankRecord(remRecord))
return(CONDERR_BAD_REMOTE_TABLES);
SlowSyncRecords
Parameters None
Purpose Applications use SlowSync when they can’t rely on the status flags to be
accurate. If the user has performed a HotSync with another PC, the status
flags are cleared. SlowSync uses the backup file, which is a copy of the file
after the last HotSync, to determine which records have been added,
changed, or deleted on the device since the last HotSync. To perform a
SlowSync, every record must be read in from the device. This contrasts
with FastSync which reads only the modified records.
All the PC records have already been read into memory (into the
m_LocRealTable table on the PC). For each PC record, if the statusFlag
is None, it’s set to Pending. Device records are read in one at a time.
Since the Palm OS device status flags may not be accurate, SlowSync pro-
ceeds as follows: If the device statusFlag is None, then that record is com-
pared against the Backup file record to determine if the device record has
been added, changed, or deleted. Then, each device record is compared
with the record in the PC table (which contains the PC records along with
the newly merged device records) to determine if the device record should
be added to the PC table, replace the current PC record, cause the PC
record to be deleted from the PC table, or be added to the Archive file. If
the record exists on both the device and the PC and the PC record has a
Example
long CAddressConduitMonitor::SlowSyncRecords(void)
{
long retval = 0, tErr, err = 0;
WORD rawRecIx = 0;
CRawRecordInfo rawRecord;
CAddressRecord backRecord(*m_BackupTable, 0);
CAddressRecord locRecord(*m_LocRealTable, 0);
CBaseIterator locIterator(*m_LocRealTable);
CString backFile(m_rSyncProperties.m_PathName);
backFile += m_rSyncProperties.m_LocalName;
backFile += BACK_EXT;
memset(&rawRecord, 0, sizeof(rawRecord));
rawRecord.m_FileHandle = m_RemHandle; // Remote File Handle
if (!m_RemRealTable)
return(CONDERR_BAD_REMOTE_TABLES);
if (m_RemRealTable->AppendBlankRecord(remRecord))
return(CONDERR_BAD_REMOTE_TABLES);
else if ((retval = AllocateRawRecordMemory(rawRecord,
ADDRESS_RAW_REC_MEM)))
return(retval);
err = 0;
rawRecIx = 0;
while (!err && !retval)
{
rawRecord.m_RecIndex = rawRecIx;
rawRecIx++;
}
if (err != SYNCERR_FILE_NOT_FOUND)
LogBadReadRecord(err);
return(retval);
}
CopyRecordsPCtoHH
Parameters None
Purpose Copies all records from the PC to the device with the exception of records
marked for archiving or deletion. Records marked for archiving are added
to the archive table and later added to the appropriate archive files.
Example
long CAddressConduitMonitor::CopyRecordsPCtoHH(void)
{
long retval = 0, err = 0;
CAddressRecord locRecord(*m_LocRealTable, 0);
CBaseIterator locIterator(*m_LocRealTable);
return(retval);
}
CopyRecordsHHtoPC
Parameters None
Purpose Copies all the records from the Palm OS device to the PC except for the
records marked for archiving or deletion. Records marked for archiving are
added to the archive table and later added to their appropriate archive files.
Example
long CAddressConduitMonitor::CopyRecordsHHtoPC(void)
{
long retval = 0, err = 0;
CRawRecordInfo rawRecord;
WORD recIx = 0;
memset(&rawRecord, 0, sizeof(CRawRecordInfo));
rawRecord.m_FileHandle = m_RemHandle; // remote file handle
rawRecord.m_RecIndex = recIx;
if (!m_RemRealTable)
return(CONDERR_BAD_REMOTE_TABLES);
{
rawRecord.m_RecIndex = recIx ;
if (!(err = SyncReadRecordByIndex(rawRecord)))
{
// Convert from raw record format to CAddressRecord
if (!m_pDTConvert->ConvertFromRemote(remRecord, rawRecord))
{
if (remRecord.IsArchived())
// Add device record to Archive table
retval = ClearStatusAddRecord(remRecord,
*m_LocArchTable);
return(retval);
}
LogRecordData
Example
rLocRec.GetName(csStr);
len = csStr.GetLength();
if (len > 20)
len = 20;
rLocRec.GetFirst(csStr);
len = csStr.GetLength();
if (len > 20)
len = 20;
rLocRec.GetCompany(csStr);
len = csStr.GetLength();
if (len > 30)
len = 30;
LogApplicationName
Purpose Retrieves the application name, for example “Address Book” that will ap-
pear in the log.
Example
SaveLocalTables
Default Logic
//
// This shows how a Monitor subclass may override the member
// function and invoke its own processing logic for the
// freshly synchronized table object. The new data member
// m_Generator is assumed to have been created during the
// overridden version of the ObtainLocalTables() member function.
//
long CMyConduitMonitor::SaveLocalTables(const char* fileName)
{
long lErr = 0;
CString destFile(fileName);
if (m_Generator) {
CAddressRecord addrRec(*m_LocRealTable);
lErr = m_Generator->PostProcessTables
(*m_LocRealTable, addrRec);
}
return(lErr);
}
PurgeLocalDeletedRecs
Parameters None
Purpose The default logic (provided by the base class CBaseMonitor) of this rou-
tine iterates through the data member m_LocRealTable and physically
removes each record marked for deletion. The native synchronization logic
processing or the desktop software may have marked records for deletion.
It’s not necessary to override this function unless a conduit’s concerned
about proprietary file formats.
If your conduit synchronizes with a file format that’s different from the
MFC serialization provided by CBaseTable, it should override this mem-
ber function. The core synchronization logic invokes this routine before
SaveLocalTables. A conduit performing a post-processing pass on the
m_LocRealTable object may actually want deleted records to remain in
the table so it can detect them. For this to occur, the conduit may need to
override this member function just to make it inactive.
Default Logic
//
// This shows how a Monitor subclass may override the member
// function and supply no code, in effect neutralizes this
// function.
long CMyConduitMonitor::PurgeLocalDeletedRecs()
{
return(0);
}
ApplyRemotePositionMap
Parameters None
Purpose The default logic (provided by the base class CBaseMonitor) issues a re-
quest to the device asking for a sorted list of its record IDs. Once obtained,
it’s applied to the order of records in the m_LocRealTable table object.
As a result, the desktop software will display its records in the same order
as the device.
If the destination for the synchronized data is a proprietary file format, your
conduit needs to override this function so it does nothing. This saves execu-
tion time by eliminating unnecessary traffic over the serial link.
Default Logic
//
// This is shows how a Monitor subclass may override the member
// function and supply no code, which in effect neutralizes this
// function.
long CMyConduitMonitor::ApplyRemotePositionMap()
{
return(0);
}
//
// Base Converter class
//
class CBaseDTLinkConverter {
protected:
CSyncLog* m_pLog;
TCHAR* m_TransBuff;
HINSTANCE m_DllInstance;
};
CSyncLog* m_pLog
A pointer to the log object created by the HotSync program and handed
into the link converter class as a parameter on its constructor line. The log
is available for recording short statements that alert the end user about ac-
tions to take. The link converter should neither create nor destroy this
pointer.
TCHAR* m_TransBuff
A pointer to memory which is allocated/destroyed by the CBaseDTLink-
Converter class. No subclass should attempt to maintain this memory
pointer. This memory buffer is used by some of the inherited utility func-
tions which adds or removes line feeds aon string buffers that are ex-
changed with the device.
HINSTANCE m_DllInstance
This instance handle is passed in on the constructor line, and originates
from the OpenConduit startup routine. This instance handle is made
available to the converter should it decide to extract strings from a resource
file for use in a log entry. It can also be used for other Windows-related
functions which need an instance handle.
Purpose Each class derived from CBaseDTLinkConverter must supply its own
constructor. It’s important that this constructor pass both parameters to its
base class constructor, where they are stored on the data members that are
then inherited to the subclass.
Example CAddressDTLinkConverter::CAddressDTLinkConverter
(CSyncLog* pLog, HINSTANCE hInst)
:CBaseDTLinkConverter(pLog, hInst)
{
}
ConvertToRemote
Purpose This member function prepares a data record for transmission from the PC
to the device. The first parameter contains a valid PC database record (in
the form of a CBaseRecord subclass) whose data must be extracted and
formatted into the a format the Palm OS device can read. The second pa-
rameter has a memory buffer rInfo.m_pBytes large enough to contain
the device version of the record data. The function must place the newly
formatted record information into this memory buffer, which the SyncMan-
ager transmits over to the device.
The example illustrates how to populate the CRawRecordInfo structure
handling both the fixed portion and variable length portion of an Address
Book record. Also shown is how the PC has to strip any carriage returns out
of its text fields before sending them to the device. This is a required activ-
ity and if omitted, may cause the device to crash when its application at-
tempts to read that data.
Example Body
CAddressDTLinkConverter::ConvertToRemote(CBaseRecord& rRec,
CRawRecordInfo& rInfo)
{
long retval = 0;
rAddrRec.GetRecordId(tempInt);
// set RecordID
rInfo.m_RecId = (long)tempInt;
rAddrRec.GetCategoryId(tempInt);
// set Category ID
rInfo.m_CatId = tempInt;
rInfo.m_Attribs = 0;
if (rAddrRec.IsPrivate())
// deal with attributes
rInfo.m_Attribs |= PRIVATE_BIT;
if (rAddrRec.IsArchived())
rInfo.m_Attribs |= ARCHIVE_BIT;
if (rAddrRec.IsDeleted())
rInfo.m_Attribs |= DELETE_BIT;
if (rAddrRec.IsModified() || rAddrRec.IsAdded())
rInfo.m_Attribs |= DIRTY_BIT;
pBuff = (char*)rInfo.m_pBytes;
// get a handy pointer
// FirstName field
retval = rAddrRec.GetFirst(tempStr);
len = tempStr.GetLength();
if (len != 0) {
flags.firstName = 1;
// Strip the CR's (if present)
// place result directly into pBuff
pSrc = tempStr.GetBuffer(len);
destLen = StripCRs(pBuff, pSrc, len);
tempStr.ReleaseBuffer(-1);
pBuff += destLen;
rInfo.m_RecSize += destLen;
// accumulate variable length
}
ConvertFromRemote
Purpose Converts the remote data record (which exists in the second parameter
rInfo in as packed bytes), into data that can be set into the first parameter
(a CBaseRecord subclass). Information is pulled out of the rInfo pa-
rameter and set into the rRec parameter.
Subclasses must override this routine; the CBaseRecord instance arriving
in the first parameter must be cast to the specific record object used on the
local PC database. Shown here is an example of adding carriage returns
into text fields (Last Name), coming from the Palm OS device that contain
only new lines.
Example
retval = rAddrRec.SetStatus(fldStatusNONE);
// clear record status field
pBuff = (char*)rInfo.m_pBytes;
// get a handy pointer
}
return(retval);
}
ConvertToRemoteCategories
Purpose Prepares the AppInfoBlock structure (which contains the categories and
is contained in dBInfo) to be sent to the Palm OS device. The second pa-
rameter holds the synchronized categories (in a PC formatted object) which
need to be converted and placed into the first parameter.
Each Palm OS database stores its categories inside the AppInfoBlock
along with other proprietary information. The categories exist at a well-
known byte offset into this AppInfoBlock.
The utility routine ReplaceCategories, which moves categories from
the CategoryManager object to the AppInfoBlock, is defined in the
CBaseDTLinkConverter class that all its subclasses may invoke.
Example
CAddressDTLinkConverter::ConvertToRemoteCategories
(CDbGenInfo& dbInfo,
CCategoryMgr* catMgr)
{
long retval = CONDERR_CONVERT_TO_REMOTE_CATS;
char* pBuff;
if (dbInfo.m_pBytes) {
pBuff = (char*)dbInfo.m_pBytes;
*((WORD *)pBuff) = 0;
// Clear the category dirty flags
pBuff += sizeof(WORD);
// offset to specific spot for cats
retval = CBaseDTLinkConverter::ReplaceCategories
(pBuff, catMgr);
}
return(retval);
}
ConvertFromRemoteCategories
Purpose Extracts the category strings and IDs (that have just been delivered from
the device) from dbInfo and places them into catMgr.
Categories generally exist at a well-known byte offset into the AppIn-
foBlock, and a given subclass should know its particular placement of
categories. A utility routine defined in the CBaseDTLinkConverter is
available for all subclasses to assist in extracting raw category information
to place into the PC-formatted CategoryManager object.
Example
CAddressDTLinkConverter::ConvertFromRemoteCategories
(CDbGenInfo& dbInfo,
CCategoryMgr* catMgr)
{
long retval = CONDERR_CONVERT_TO_LOCAL_CATS;
char* pBuff;
short wTemp;
if (dbInfo.m_pBytes) {
pBuff = (char*)dbInfo.m_pBytes;
wTemp = *((WORD*)pBuff);
wTemp = FlipWord(wTemp);
// two byte words arrive in Motorola format
pBuff += sizeof(WORD);
// offset into category area
retval = CBaseDTLinkConverter::ExtractCategories
(pBuff, wTemp, catMgr);
}
return(retval);
}
This routine does nothing but is available in case a conduit is aware of cus-
tom information stored in the AppInfoBlock by an application residing
on the device; in effect any information except the categories, which are
handled separately.
ping and adding carriage returns into text fields, and extracting category
strings and IDs.
CCategoryMgr* catMgr);
because its records are more complex than those of the address book used
in previous sections.
To use the native synchronization logic, you need to do the following:
1. Create a subclass of CBaseTable. This class is the “glue” that
holds all things together; some of the information it needs is in the
classes associated with it (which you create in the steps below).
– Create a subclass of CBaseTable with an appropriate con-
structor and destructor.
– Override the virtual function AppendDuplicateRecord.
The function lets each record work on all its fields; it takes care
of the details of copying from one record to a new record. See
AppendDuplicateRecord function from ToDo base table.
– If your application requires specialized sorting, optionally over-
ride AppendBlankRecord.
Note that you don’t have to override the standard OpenFrom and
SaveTo functions; the functions use the information in your sub-
class of CBaseSchema to determine how to write the data in and
out.
2. Create a subclass of CBaseSchema with an appropriate construc-
tor and destructor and override the DiscoverSchema virtual
function. (see DiscoverSchema function from CToDoSchema)
The schema is a template of the record, the table uses that informa-
tion when synchronizing the record.
3. Create a subclass of CBaseRecord
This subclass needs to have one virtual function for each applica-
tion-specific field of the record. For example, for records in the
ToDo PIM, functions SetDescription, SetDueDate, Set-
Completed, SetPriority, and so on are provided. The record
inherits the fields Status, RecordID and Category ID, so your sub-
class does not need to take care of them (see DiscoverSchema func-
tion from CToDoSchema)
Note that if the record class and the schema class don’t agree on the
fields in your records, problems will result.
4. Create a subclass of CBaseIterator
The iterator class contains behavior for sorting and finding things;
functions that apply to all records at once, for example, sorting by
field. You must override its virtual functions with functions that call
the same function in the base class. You may also decide to add
CBaseTable Class
A conduit using the native synchronization logic uses four table instances
while it’s executing: local table, remote table, archive table, and backup
table (SlowSync only). Each table has to be an instance of the same sub-
class of CBaseTable.
From CBaseTable, the table inherits some behavior as well as places to
store pointers to the schema, record, and iterator objects. These objects
contain some of the application-specific record information and are dis-
cussed below.
CBaseTable is defined in basetable.h
The only virtual function you must override in CBaseTable is
AppendDuplicateRecord. Here’s an example from the ToDo conduit.
//////////////////////////////////////////////////////////////////
// Function: AppendDuplicateRecord()
//
// Description: Appends a new blank record then fills it with
// the passed parameter 'rFromRec'.
//
// Allows a new set of fields (a row) to be added
// to table object. The set of fields comprises one
// full record, and initially each has blank data
// Next the passed in record object is used as a
// source of fields whose values are duplicated in
// the newly appended 'blank' set of fields.
//
// *Note* Generally only called from the ConduitMonitor /
/ object during synchronization procedures.
//
// Parameters:
// rFrom - Record object to copy data from
// rTo - Ends up positioned at the new row of fields
in the table
// bAllFlds - If true replicates ALL fields including **RecordID*
// - If false does *not* duplicate the recordId or Status
//
// Returns: 0 - Success
//////////////////////////////////////////////////////////////////
long CToDoTable::AppendDuplicateRecord(CBaseRecord& rFrom,
CBaseRecord& rTo, BOOL
bAllFlds)
{
int tempInt;
CString tempStr;
long tempLong, len, retval = -1;
//
// Source record must be positioned at valid data.
//
if (rFromRec.m_Positioned)
{
if (!CBaseTable::AppendBlankRecord(rToRec))
{
if (bAllFlds)
{
if (!rFromRec.GetRecordId(tempInt))
retval = rToRec.SetRecordId(tempInt);
if (!(retval = rFromRec.GetStatus(tempInt)))
retval = rToRec.SetStatus(tempInt);
retval = rToRec.SetArchiveBit(rFromRec.IsArchived());
}
if (!retval && !rFromRec.GetDescription(tempStr))
retval = rToRec.SetDescription (tempStr);
if (!retval)
retval = rToRec.SetCompleted(rFromRec.IsCompleted());
if (!retval)
retval = rToRec.SetPrivate(rFromRec.IsPrivate());
if (!retval)
{
rFromRec.GetNote(tempStr);
len = tempStr.GetLength();
if (len > 0)
retval = rToRec.SetNote(tempStr);
}
}
}
return(retval);
}
The table class relies on a schema, record, and iterator object for informa-
tion about the records your conduit synchronizes. You therefore must create
subclasses of CBaseSchema, CBaseRecord, and CBaseIterator,
discussed in the next three sections.
CBaseRecord Class
The CBaseRecord class is one of the places where information about your
records is stored.
This information is actually made known to the system in several ways:
• FieldIDs provide the ID for each field in your conduit’s header file.
Here’s a partial example from tdtable.h, which defines the To Do
table class:
#define tdFLDRecordID 0
#define tdFLDStatus 1
#define tdFLDPosition 2
#define tdFLDDesc 3
#define tdFLDDueDate 4
• A DiscoverSchema function you must supply inside your
schema subclass that defines the template of the record (see Discov-
erSchema function from CToDoSchema).
• A virtual set and a virtual get function for each record, for example,
SetDescription and GetDescription or SetDueDate
and GetDueDate. Each function fills in the corresponding record
using the information in the schema.
The MODFILTER_STUPID flag set by this function sets the record
dirty whenever it’s touched. This is usually recommended.
CBaseSchema Class
The Schema class contains information the record object uses inside the
SetDescription function to set up the record. Here’s an example from the
ToDo conduit:
long CToDoSchema::DiscoverSchema(void)
{
m_FieldsPerRow = 10;
m_FieldTypes.SetSize(m_FieldsPerRow);
m_FieldTypes.SetAt(tdFLDRecordID, (WORD)eInteger);
m_FieldTypes.SetAt(tdFLDStatus, (WORD)eInteger);
m_FieldTypes.SetAt(tdFLDPosition, (WORD)eInteger);
m_FieldTypes.SetAt(tdFLDDesc, (WORD)eString);
m_FieldTypes.SetAt(tdFLDDueDate, (WORD)eDate);
m_FieldTypes.SetAt(tdFLDCompleted, (WORD)eBool);
m_FieldTypes.SetAt(tdFLDPriority, (WORD)eInteger);
m_FieldTypes.SetAt(tdFLDPrivate, (WORD)eBool);
m_FieldTypes.SetAt(tdFLDCategoryID, (WORD)eInteger);
m_FieldTypes.SetAt(tdFLDNote, (WORD)eString);
return(0);
}
CBaseIterator Class
The CBaseIterator class holds functions that perform actions on all
records, such as searching and sorting them. Here’s how the class is defined
at the top level:
CBaseIterator (CBaseTable&);
~CBaseIterator ();
NOTE: Many developers find they can use the native category be-
havior as is.
public:
CCategoryMgr();
~CCategoryMgr();
int GetCount()
{ return (m_Categories.IsEmpty() ? 0 :
m_Categories.GetCount()); }
Session-Oriented Calls
The session-oriented API consists of two calls.
• SyncRegisterConduit
• SyncUnRegisterConduit
SyncRegisterConduit
Purpose Check whether a conduit is registered. If it isn’t, the conduit is registered
internally by the HotSync manager.
Result SYNCERR_NONE
SYNCERR_COMM_NOT_INIT
SYNCERR_REMOTE_CANCEL_SYNC
Description This routine is called when a conduit DLL first begins its synchronization
activities. It has to be called by every conduit to prepare the device for syn-
chronization. If the conduit doesn’t make this call, synchronization cannot
take place.
SyncUnRegisterConduit
Purpose Unregister a conduit.
Result SYNCERR_NONE
SYNCERR_COMM_NOT_INIT
Description This call allows the device to clean up memory and resources following
synchronization.
File-Oriented Calls
The file-oriented function calls provide file manipulation of the databases
on the device.
All remote databases exist on a memory card. In the first Pilot release, only
one memory card is present on the device, referred to as card #0. A memory
card may store databases in one of two areas, either RAM or ROM. When
opening or creating a remote database, it is necessary to indicate which of
the memory cards the database is to reside upon.
The figure below illustrates the layout of a remote Pilot database. It is not
necessary to know this layout, however it does show the components that
can be manipulated by the file-oriented API.
Database Name
Number of Records
• SyncReadDBAppInfoBlock
• SyncReadDBSortInfoBlock
• SyncResetSyncFlags
• SyncWriteDBAppInfoBlock
• SyncWriteDBSortInfoBlock
SyncCloseDB
Purpose Close the currently open database on the device
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SyncCreateDB
Purpose Create a new database on the Palm OS device.
Result SYNCERR_NONE,
SYNCERR_FILE_ALREADY_EXISTS
SYNCERR_FILE_TOO_MANY_FILES
SYNCERR_REMOTE_BAD_ARG
Description Creates a new database on the Palm OS device with the name specified in
the CDbCreateDB structure.
class CDbCreateDB
{
public:
BYTE m_FileHandle;
SyncDeleteDB
Purpose Delete a database
Result SYNCERR_NONE
SYNCERR_FILE_NOT_FOUND
SYNCERR_FILE_OPEN
Description Instructs the Palm OS device to delete the named database from its storage
on the specified card number. The database must be closed (not in use).
SyncOpenDB
Purpose Open a database on the Palm OS device.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_FOUND
SYNCERR_FILE_NOT_OPEN
SYNCERR_FILE_OPEN
SyncReadDBAppInfoBlock
Purpose Locate and retrieve information.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SYNCERR_REMOTE_SYS
SYNCERR_REMOTE_MEMES
class CDbGenInfocode {
public:
// Name of remote database file
char m_FileName[DB_NAMELEN];
//Length of m_pBytes buffer
allocated by the caller.
WORD m_TotalBytes;
// Byte length of 'pBytes'
WORD m_BytesRead;
// Inbound byte count
BYTE * m_pBytes;
};
The calling client conduit library must allocate enough memory in the gen-
eral data area to hold the information returned.
If the m_BytesRead value is > m_TotalBytes, then m_pBytes has not been
touched. The caller should reallocate m_pBytes to be at least m_BytesRead
and make the call again.
If m_BytesRead <= m_TotalBytes then it is the total number of bytes read
into m_pBytes.
It is in place to facilitate trading of database-specific information which
may assist in the synchronization process. Enough memory (less than 1K)
must be preallocated on the incoming pointer by the calling conduit library
to hold the response data returned by the devise (and placed in the
m_pBytes member).
The built-in applications on the device store categories in AppInfoB-
lock. See the Developing Palm OS Applications documentation set for
more information.
SyncReadDBSortInfoBlock
Purpose Read database information from the device.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SYNCERR_REMOTE_SYS
SYNCERR_REMOTE_MEME
Description This function lets you read database information from the device, storing it
in the CDbGenInfocode class.
class CDbGenInfocode {
public:
char m_FileName[DB_NAMELEN];
// Name of remote database file
??header conflict: NOT USE in doc
WORD m_TotalBytes;
// Byte length of 'pBytes'
WORD m_BytesRead;
// Inbound byte count
BYTE * m_pBytes;
};
The calling client conduit library must preallocate enough memory onto
the member m_pBytes to hold the incoming reply data. Upon return, the
member m_BytesRead holds the number of bytes actually transferred to
the m_pBytes buffer.
This function provides a way to exchange a block of information attached
to a database on the device. This function is not required; conduits may or
may not use it.
SyncResetSyncFlags
Purpose Reset flags of all open database records that is, clear dirty and archived
flags for the whole database.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN.
Description Instructs the Palm OS device to scan all the records of the open database
and clears dirty and archived flags. This may or may not be applicable for
every conduit.
Applications typically call this function before closing the database.
SyncWriteDBAppInfoBlock
Purpose Write information to the device.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SYNCERR_REMOTE_SYS
SYNCERR_REMOTE_MEME.
Description Instructs the device to write the information in the passed structure to the
device’s permanent storage associated with the open file handle.
class CDbGenInfocode {
public:
char m_FileName[DB_NAMELEN];
// Name of remote database file
??header conflict: NOT USE in doc
WORD m_TotalBytes;
// Byte length of 'pBytes'
WORD m_BytesRead;
// Inbound byte count
BYTE * m_pBytes;
};
SyncWriteDBSortInfoBlock
Purpose Write information to the device.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SYNCERR_REMOTE_SYS
SYNCERR_REMOTE_MEM
Description Instruct the device to write the information stored in the passed structure to
the device’s permanent storage associated with the open file handle. The
structure member m_TotalBytes should contain the number of bytes
within the m_pBytes buffer to actually write to the device.
class CDbGenInfocode {
public:
char m_FileName[DB_NAMELEN];
// Name of remote database file
??header conflict: NOT USE in doc
WORD m_TotalBytes;
// Byte length of 'pBytes'
WORD m_BytesRead;
// Inbound byte count
BYTE * m_pBytes;
};
Record-Oriented Calls
The record-oriented APIs are used to pass the representation of a record
(which resides in a database file) between the PC and Pilot. Because one
primary purpose of the SyncManager.DlL is to act as a ??shipping chan-
nel?? for byte traffic to the device, there is a need for a generic definition of
a structure which should handle any record format. This structure then be-
comes a parameter in these record-oriented APIs.
For reading records, three different APIs are provided, allowing for:
SyncDeleteAllResourceRec
Purpose Delete all resource records from the currently open database on the device.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SYNCERR_ROM_BASED
SYNCERR_READ_ONLY.
Description This routine instructs the device to delete all resource records from the cur-
rently open resource database. Use this routine on a remote database con-
SyncDeleteRecord
Purpose Delete a specified record on the device.
Description Instructs the device to delete the record specified in the structure member
m_RecId in the open database.
SyncDeleteResourceRec
Purpose Delete the passed resource on the device.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SYNCERR_FILE_NOT_FOUND
SYNCERR_REMOTE_SYS
SYNCERR_REMOTE_MEM
Description This routine instructs the device to delete resource identified by its unique
ID (passed in the structure member m_RecIndex) from the open data-
base. It is not necessary to allocate memory or fill out any structure mem-
bers other than the first three.
SyncGetDBRecordCount
Purpose Obtain total record count from currently open device database.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
Description This routine obtains the total record count for the currently open database
on the device.
SyncPurgeAllRecs
Purpose Delete all records from currently open database on device, regardless of
status.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SYNCERR_RECORD_BUSY
SYNCERR_ROM_BASE
SYNCERR_READ_ONLY
Description This routine instructs the device to delete every record from the currently
open database, regardless of the current status flags.
SyncPurgeDeletedRecs
Purpose Delete all records marked “deleted” from currently open database on the
device.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_OPEN
SYNCERR_ROM_BASED
SYNCERR_READ_ONLY
SYNCERR_REMOTE_RECS_NOT_PURGED
Description This routine instructs the device to delete all records from the currently
open database that have their status flags set to delete. When the user de-
letes a record on the device, the record is marked for deletion but not actu-
ally removed from the data file. This allows the conduit program on the PC
to delete matching records from the local data file and purge the record
after the PC record has been purged.
SyncReadNextModifiedRec
Purpose Traverse the currently open database on device and return the next modi-
fied record.
Result SYNCERR_NONE
SYNCERR_COM_NOT_INIT
SYNCERR_RECORD_BUSY
SYNCERR_FILE_NOT_FOUND
Description Instructs the Palm OS device to traverse its currently open database and re-
turn the next record it encounters that has been modified since the last syn-
chronization session.
class CRawRecordInfo
{
public:
BYTE m_FileHandle; // Supplied by caller
DWORD m_RecId; // Supplied by caller
(when appropriate)
WORD m_RecIndex; // Supplied by caller
(when appropriate)
BYTE m_Attribs; // Filled in by HH
short m_CatId; // Filled in by HH
int m_ConduitId; // Ignore
DWORD m_RecSize; // Filled in by HH
WORD m_TotalBytes; // Supplied by caller
BYTE * m_pBytes; // Allocated by caller
};
SyncReadRecordById
Purpose Search device database for match on a record.
Result SYNCERR_NONE
SYNCERR_COM_NOT_INIT
SYNCERR_RECORD_BUSY
SYNCERR_FILE_NOT_FOUND
Description This function can be thought of as a seek and find procedure. The device
searches its currently open database and looks for a match on the unique
record (supplied in the structure member m_RecId). Upon successful exe-
cution of the routine, the structure member m_pBytes contains the raw
record body from the device and the structure member m_RecSize is up-
dated with the length of the returned record body.
SyncReadRecordByIndex
Purpose Traverse Palm OS device database.
SyncReadResRecordByIndex
Purpose Traverse the currently open database on the device.
Result SYNCERR_NONE
SYNCERR_COM_NOT_INIT
SYNCERR_FILE_NOT_OPEN
SYNCERR_RECORD_BUSY
SYNCERR_FILE_NOT_FOUND
SYNCERR_REMOTE_SYS
SYNCERR_REMOTE_MEM
Description This routine provides a mechanism to traverse the currently open resource
database on the device from top to bottom. The structure member
m_RecIndex can be thought of as an array offset, in essence accessing a
specific record in an open database by its relative offset from the beginning
of the file. On success, the device returns the record body located at the
m_RecIndex position.
Upon successful execution of this routine, the structure member
m_pBytes will contain the raw record body from the device and the struc-
ture member m_RecSize is updated with the length of the returned record
body. Use this routine on a remote database consisting of resource type
records. These record types generally consist of code resources, such as an
executable program which runs on the device, as well as other types of re-
sources like preferences, images, and so on.
SyncWriteRec
Purpose Instruct the device to write the passed record into the open database.
Result SYNCERR_NONE
SYNCERR_COM_NOT_INIT
SYNCERR_FILE_NOT_OPEN
SYNCERR_RECORD_BUSY
SYNCERR_FILE_NOT_FOUND
SYNCERR_ROM_BASED
SSYNCERR_READ_ONLY
Description Instructs the device to write the passed record into the open database. The
caller must supply either a valid record ID in the member m_RecId or place
zero in this member. This instructs the device to append the record as a new
record to the open database. The record body is placed in the memory on
the pointer m_pBytes and should be formatted to match the record layout
in the open database on the device.
SyncWriteResourceRec
Purpose Write the passed resource into the open database.
Result SYNCERR_NONE
SYNCERR_COM_NOT_INIT
SYNCERR_FILE_NOT_OPEN
SYNCERR_RECORD_BUSY
SYNCERR_FILE_NOT_FOUND
SYNCERR_ROM_BASED
SYNCERR_READ_ONLY
SYNCERR_REMOTESYS
SYNCERR_REMOTE_MEM
Description This routine instructs the device to write the resource passed in the struc-
ture member m_RecId into the open database. The record body contained
in the memory on the pointer m_pBytes is sent as is and should be for-
matted to match the resource record layout in the currently open database
on the device.
Use this routine on a remote database consisting of resource type records.
These records typically consist of code resources, such as an executable
program which runs on the device, as well as other types of resource like
images or preferences.
Utility Calls
The calls provided by the utility API retrieve information on how the re-
mote device is configured. There is also a function that lets the caller obtain
the list of files present on any of the memory cards currently present in the
device. The API consists of these calls:
• SyncReadDBList
• SyncReadSingleCardInfo
• SyncReadSystemInfo
SyncReadDBList
Purpose Retrieve information about list of databases on Palm OS device.
Result SYNCERR_NONE
SYNCERR_FILE_NOT_FOUND
SYNCERR_COMM_NOT_INIT
SYNCERR_REMOTE_SYS
SYNCERR_REMOTE_MEM
Description This function allows the caller to discover a list of all the databases (both
data and program) that reside on a memory card within the Palm OS de-
vice. This is analogous to a directory listing on a PC; the result contains
both data files and program files.
class CDbList
{
public:
int m_CardNum;
WORD m_DbFlags;
// contains Res/Record/Backup/ReadOnly
DWORD m_DbType;
char m_Name[DB_NAMELEN];
DWORD m_Creator;
WORD m_Version;
DWORD m_ModNumber;
WORD m_Index;
long m_CreateDate;
long m_ModDate;
long m_BackupDate;
BOOL m_bReadOnly;
long m_RecCount;
long m_ModRecCount;
};
SyncReadSingleCardInfo
Purpose Retrieve information about the specified memory card.
Result SYNCERR_NONE
SYNCERR_COMM_NOT_INIT
SYNCERR_REMOTE_SYS
Description Retrieves information about a memory card. Memory card numbers on the
device start at zero. The caller must fill out the first member of the structure
mCardNo with the number of the memory card it wants to gather data
about (currently, only 0 is supported). When the call returns, the remaining
structure members are filled with data.
class CCardInfo
{
public:
BYTE m_CardNo;
WORD m_CardVersion;
long m_CreateDate;
DWORD m_RomSize;
DWORD m_RamSize;
DWORD m_FreeRam;
BYTE m_CardNameLen;
BYTE m_ManufNameLen;
char m_CardName[REMOTE_CARDNAMELEN];
char m_ManufName[REMOTE_MANUFNAMELEN];
SyncReadSystemInfo
Purpose Retrieve information from the Palm OS device.
Result SYNCERR_NONE
SYNCERR_COMM_NOT_INIT
SYNCERR_REMOTE_SYS
SYNCERR_LOCAL_BUFF_TOO_SMALL.
Description Instructs the Palm OS device to populate the passed CSystemInfo struc-
ture:
class CSystemInfo
{
public:
DWORD m_RomSoftVersion;
The information includes the revision level of the ROM software, the ID of
the device, a string buffer containing product text information. the caller
must preallocate memory on the m_productIdText pointer before call-
ing this routine, and initialize the m_AlloceedLen member with the size
of memory preallocated. If not enough memory (or none at all) is preallo-
cated, the function returns with error
SYNCERR_LOCAL_BUFF_TOO_SMALL
ther calls should be made into the SyncManager library. See also syncmgr.h.
The following fatal codes are currently defined: