|
|
|
|
Programming Visual Basic for the Palm OSBy Roger Knoell, Matthew Holmes, Patrick BurtonApril 2002 0-596-00200-9, Order Number: 2009 375 pages, $39.95 US $61.95 CA £28.50 UK |
Chapter 4
Conduit DevelopmentThe original concept of the Palm was of a device tethered to data on the desktop. The PDA is an extension of the desktop, not its replacement. 3Com (the Palm PDA manufacturer) has expanded this design concept; it now calls the Palm device the "connected organizer." Palm wants you to build applications that function in today's mobile and connected world.
In this chapter, we look at building conduits using Microsoft Visual Basic (VB). Conceptually, a conduit is the tether that moves data back and forth, connecting your Palm application and its data store. A conduit is a piece of software that runs on the desktop when the PDA is synchronizing, under the control of the Palm HotSync manager.[1] In this book, we are going to build conduits using VB and ActiveX.
By design, a conduit is dedicated to a single Palm application, which will have one or more associated Palm databases. Keep in mind that, even though the conduit is a piece of desktop software, the application data may be located anywhere--on the desktop, in a relational database, or even on the Internet. Once you have decided where your application's data resides, it is then your responsibility to build a conduit capable of delivering that data to the Palm device. The Palm Conduit Development Kit provides a framework for ActiveX conduit development, which we'll explain in detail later in this chapter.
Note that conduits developed in VB can be used with any application and database on the PDA, not only those created with AppForge. The high-level HotSync architecture is shown in Figure 4-1.
Figure 4-1. Overview of HotSync COM architecture
![]()
The Palm HotSync manager is in control of the synchronization process. It maintains a list of configured conduits in the system registry, and it handles the interface with the Palm PDA. The HotSync manager uses a set of COM objects--the Sync Suite API--to communicate with ActiveX conduits. The conduit manages application-specific data and supplies a userinterface if appropriate.
This chapter covers how to compile and register an ActiveX conduit with the HotSync manager, how to use the Sync Suite API to synchronize Palm PDA data with the desktop, and how to handle the user interface.
Applications and Conduits
Before we get to the mechanics of conduit development, let's review the concept of data synchronization as it applies to Palm applications and databases. If the handheld is an extension of the desktop, then it is natural to ask: How should the data flow between the handheld and the desktop? This is going to depend in great deal on the design and purpose of your application and its databases.
Entertainment or utility programs rarely have a conduit--they don't have data worth moving to the desktop. These applications usually save any data in the system
Preferencesdatabase, which is automatically backed up and restored by the HotSync manager. We covered application preferences in Chapter 3.More typical is the unidirectional conduit, in which the data flows from the Palm device to the desktop, or the other way around. This type of conduit is useful in applications like questionnaires, where the Palm is used primarily as a remote data collection tool.
The Palm PDA's native applications, like Address and To-Do, use a mirror-image conduit, where changes on the desktop and the device are replicated in both directions. This type of synchronization is so important that Palm has documented exactly how a mirror-image conduit should behave. We will cover mirror-image synchronization in detail later in this chapter.
A transactional conduit processes data to produce intermediate results, which are written back to the Palm device. The data flow in a transactional conduit can be in one or both directions. You could implement this sort of conduit for an application that uploads orders to a SQL database for fulfillment processing and then downloads invoices to the Palm. We discuss this type of conduit in Chapter 5.
Finally, there are system functions such as installation and backup that use special- purpose conduits to perform their functions. The default backup conduit synchronizes databases whose applications don't have a custom conduit, or databases that have a conduit but whose type is not
DATA. The backup conduit, which is supplied by the Palm desktop installation, simply makes an exact copy of your application's database or databases on the desktop. (Technically, to be backed up by the default conduit, a Palm database must have the backup bit set.)Conduit Types
Palm had to design the HotSync manager and conduit interface to support a wide variety of data synchronization and replication needs. Each conduit registers its unique creator identifier and synchronization type with the HotSync manager. Note that your conduit might support more than one type of synchronization; in that case, you should provide a user interface to allow the user to customize the behavior of your conduit. We'll show you how to do this later in the chapter.
Here are the types of conduits supported by the Palm Conduit Development Kit, as documented in the Conduit Reference manual:
Fast- Performs a fast synchronization
Slow- Performs a slow synchronization
HHtoPC- Copies handheld database to the desktop and overwrites all old records
PCtoHH- Copies desktop database to the handheld and overwrites all old records
Install- Installs new application to the handheld (system function)
Backup- Backs up handheld database to the desktop (system function)
DoNothing- Doesn't perform any synchronization
ProfileInstall- Performs a profile download (system function)
The HotSync manager on the desktop keeps track of when the user last synchronized his or her Palm device with this desktop. It uses this information to determine which conduits to call, and the type of synchronization each conduit is to perform.
TIP: The HotSync manager supports multiple users on the desktop. It also supports a single user with multiple Palm devices. The HotSync API provides identifiers during a HotSync session to enable a conduit to determine which user and/or device is synchronizing. We don't discuss this capability further in this book, but it is covered in the Palm Windows Conduit Companion and Reference.
Of course, the user may set the type of synchronization manually, as illustrated in Figure 4-2.
Figure 4-2. User dialog for HotSync preferences
![]()
If a conduit does not support a user interface, the HotSync manager uses the conduit's default synchronization type. We show how to implement a custom user interface for your conduit later in this chapter.
TIP: Conduits should always have a UI. Otherwise, your user will click the
Changebutton in theCustomdialog and nothing will happen. Not only is this rude, it will leave your user wondering if something is broken.Mirror Synchronization
As we mentioned earlier, mirror-image synchronization requires that a conduit replicate changes between the desktop and the Palm device. Mirror-image conduits should support both fast and slow synchronization. When your conduit is called to do a FastSync, it only needs to look for new or dirty records in your application's Palm database and on the desktop. If your conduit is called for a slow sync, it must look at every record on the PDA and the desktop. The HotSync manager requests a slow sync whenever it determines that the last sync was not with this desktop. Otherwise, it requests a FastSync.
The Palm database manager supports per-record flags that track any changes to records in the database. These flags are listed in Table 4-1. In order to successfully implement a mirror-image conduit, your desktop database must support some or all of these flags on a per-record basis.
Table 4-1: Palm database record flags Flag
Meaning
Changed
Either create a new record or edit an existing record
Deleted
Delete the record
Archived
Make an archive copy, then delete the record
Secret
Mark record as private
The Windows CDK Companion has a section on design decisions and tradeoffs that you can use to evaluate the kind of conduit your application can support. We'll summarize a few key questions here:
- Do you have unique record identifiers that can be mapped into Palm record identifiers?
- Do you support per-record attributes, such as dirty, deleted, and archived?
- Is it easy to detect changes to desktop records?
- Are the desktop records categorized?
- Is it simple to map database records to desktop records or entities?
- Is it possible to partition the application and conduit to minimize synchronization times?
Unfortunately, there are no generic answers to these design questions. We will address these questions as they apply to a sample application and conduit that we will present later in this chapter. You'll have to consider them from the context of your own application.
If your user has experience with any of the Palm native applications, then he'll expect your conduit to replicate changes in the same manner. Palm has gone to great lengths to document this behavior, as it pertains to the native applications. (The synchronization logic is spelled out in the Palm Conduit Programmer's Companion for Windows, which is part of the CDK.) Your conduit should emulate as much of this behavior as makes sense for your application.
Table 4-2 shows the possible states for each record in an application. The desktop record states run along the top of the table and the Palm record states run down the left side.
Table 4-2: Mirror conduit record states
No record
No change
Change
New
Delete
No record
D
P
No change
D
P
Remove DP
Change
P
D
Conflict
P
D
New
P
D
Delete
Remove DP
D
P
The action that your conduit should take is found in the intersection of each of the possible states, and is spelled out in the following list:
- D
P
- Desktop record replaces Palm record
- P
D
- Palm record replaces desktop record
- Remove (DP)
- Delete the record from the desktop and/or the Palm
- Conflict
- Follow application rule to resolve synchronization conflict
After the conduit takes the indicated action, the record on the desktop is in the same state as the record on the Palm.
A conflict arises when a record is changed simultaneously on the Palm device and on the desktop. Palm recommends that this conflict be resolved by migrating the change in both directions. The user can then edit or delete the data on either the Palm or the desktop. At the next synchronization, the conduit will clean up all the changes.
TIP: Note that the blank cells in Table 4-2 correspond to conditions that Palm considers logically impossible or irrelevant to conduit design. Palm derived these conditions from the behavior of its native applications. Your conduit and application might not need to support all these conditions, or they might need to support different possibilities.
The situation is slightly more complicated if your application supports archive records. A Palm application such as
Addressoffers to archive a record when it is deleted. This is an offer to preserve the data somewhere on the desktop; presumably, the desktop provides the corresponding restore operation.The ability to archive older records, instead of deleting them forever, was crucial to the early Palm devices, which sported 128 KB of memory. Users were forever shuffling data between the device and the desktop. While it is less critical now, memory is still a scarce resource. So if you can support the archive option in your application, you certainly should.
In general, when a Palm record is marked for archival, the conduit archives the record in an application-specific fashion, and then deletes the record from both the Palm and the desktop.
Table 4-3 shows how to handle Palm database records that are marked for archiving. In this table, the desktop record states appear along the top of the table and the Palm record archive states appear on the left side.
Table 4-3: Mirror sync actions with Palm archive request
Delete
No change
No record
Change
Archive
Archive, Remove DP
Archive, Remove DP
Archive, Remove DP
Archive, Change
Conflict
Archive, No Change
D
P
A conflict arises if a record has changed in both databases as the same time. In such a case, the conflict is deepened because the user has also requested that the record be archived--this means that she doesn't want to see it again any time soon. Here's how the Palm CDK Companion says you should handle the conflict:
If the changes are identical, archive both the device record and the desktop record. If the changes are not identical, do not archive the device record; instead, add the desktop record to the device database, and add the device record to the desktop database.Table 4-4 shows what to do when a desktop record has been marked for archival. The most common case, in which the Palm device record has not changed, is handled by archiving the record and then removing it from both the device and the desktop.
Table 4-4: Mirror sync actions with desktop archive request
Archive
Archive, change
Archive, no change
Delete
No change
Archive, Remove DP
No record
Change
Conflict
P
D
Not surprisingly, a conflict occurs when the record to be archived has been changed in both databases. According to the Palm CDK Companion:
If the changes are identical, archive the device record and then delete the records from both the device and desktop databases. If the changes are not identical, do not archive the desktop record; instead, add the desktop record to the device database and add the device record to the desktop database.A mirror-image conduit should also support the
HHtoPCandPCtoHHsynchronization types. Recall that these sync types cause the handheld data to overwrite the desktop or vice versa. At first it might seem counter-intuitive that your conduit could be called to blindly overwrite user data, on either the desktop or the Palm device. But keep in mind that your conduit is almost always called this way as a result of user intervention.For example, your user might accidentally delete an entire category of data records on the Palm device. Instead of then synchronizing these changes, and thereby deleting the data from the desktop as well, the user can direct the conduit to overwrite all the Palm data, effectively restoring the deleted records.
Categories
A conduit must synchronize changes in category data as well as record data. Because a category is actually a compound data type--it has both a name and a numeric ID--the synchronization logic is a little harder. Palm addresses the default logic for the native conduits in the CDK documentation.We summarize it here:
- If the category ID has changed on either the desktop or the PDA, but the name is the same, update all desktop records to use the PDA category ID.
- If the desktop category name has changed, but the category ID is the same, then update the PDA category name. Note that this only holds true if the new desktop category name is not already in use on the PDA.
- If there is a category with a new name and ID on the desktop, and neither is in use on the PDA, then create a new category on the PDA. If the index is already in use, then assign a new index from the PDA, and update all the desktop records with it.
If your application supports categories, give careful consideration to the mapping between category names and identifiers on the Palm PDA and the desktop. And be aware that there is native support in the Palm operating system for only 15 active categories.
Other Types of Conduits
Your conduit will never be called for
Install,Backup, orProfileInstallsynchronization. These are reserved to the HotSync manager to perform special system functions. Your conduit can be called forDoNothingsynchronization, always at the user's request.If you have a transactional conduit, you will have to masquerade as a mirror-image conduit. We don't discuss transactional conduits in this book. These conduits have the same structure and logic as the conduits we have already described, but they must handle distributed transactions and error rollback and recovery as well. We do cover the use of conduits to manage SQL data in Chapter 7.
Conduit Design
There are several major design principles for a conduit. The first, and most important, is that the conduit runs as quickly as possible. No one wants to wait while synchronization grinds on and on. Honoring the fast and slow sync flags will help you to optimize performance. A fast conduit also minimizes the use of the Palm device's serial port. This is important, because serial port use can drain the PDA's batteries very quickly.
The second principle is that a conduit must always move application data in a natural way between the Palm device and the desktop. Your user relies completely on this behavior. She also relies on the conduit to restore all data in case of a disaster. If your conduit is not correctly implemented, or has bugs, user acceptance of your application will suffer greatly.
A third principle is that your conduit must be able to run without attention from the user. While this is a good design principle for software in general, it is especially important for a conduit. This is because the user might be synchronizing from a remote location, communicating with the HotSync manager over a modem or network connection. The user will not be able to respond to any prompts or dialog boxes that a conduit might display on the desktop. This will cause the entire HotSync session to hang.
Palm provides some more design guidelines in the C/C++ Conduit Companion; you should certainly study those before implementing your own conduit.
If you are building a mirror-image conduit, then follow the logic diagrammed earlier in Tables 4-2, 4-3, and 4-4, as they apply to your application. If you don't allow desktop editing of data in your application, for example, then your conduit doesn't have to support it, either.
Having a user interface and allowing the user to override the conduit's default behavior will help you achieve acceptance. If at all possible, support uploading or downloading all records from your user interface.
Note that if your Palm database design maps into a relational database, and you used the AppForge database tools described in Chapter 4, then you can likely use the Universal Conduit. We discuss the pros and cons of the Universal Conduit in Chapter 5, and show you how to use it to integrate SQL Server data into your applications.
When Not to Use a Conduit
Not all synchronization situations require you to implement a conduit. It is perfectly appropriate to make a new database from scratch and download it to the device, if you know that all or most of your application's data has changed.
Simply generate your database from the new data, and put it in the HotSync installation directory.[2] The database will be automatically deployed to the device with the next synchronization, overwriting any existing PDA data in the process. If your application has a database that is read-only, and that is periodically refreshed, you can use this technique. If your sales department produces a list of prices that changes monthly, then you don't need to execute a conduit every day.
Not having a conduit can greatly simplify the distribution of your application. You don't have to worry about developing an installation script for your conduit and its associated runtime objects.
If the Palm Desktop is configured properly, the Windows default shell action for PRC and PDB files is to call the Palm Instapp.exe program, which will copy the file into the Palm desktop installation directory.
This means that you can post your database files on the Web or in ZIP archives. When your user downloads or extracts your file and double-clicks it, it is queued for installation at the next HotSync.
Installing the CDK
The Conduit Development Kit (CDK) is freely downloadable from the Palm web site.[3] The CDK contains libraries and runtime bindings for VB and COM, as well as the C/C++ language. The InstallShield wizard puts everything into the directory you select (see Figure 4-3).
Figure 4-3. CDK directory structure
![]()
You can choose to install only the COM libraries, samples, and documentation by using the custom installation procedure and deselecting the C/API. We recommend performing a full installation, because the C/API contains some additional documentation you will find useful.
The Com folder contains the VB tutorial and sample projects, an installation tool, and another directory that simply contains links to the documents mentioned above. Most of the sample projects in the CDK are primers that focus on explaining at most one or two parts of the CDK. There is also a longer example, SyncSamp, which replaces the default Memo conduit and illustrates a complete conduit.
The Common folder contains the CDK programs and libraries used when developing conduits. This folder also contains the documentation, in both Adobe PDF and Microsoft Help formats. Earlier releases of the CDK runtime are supplied for backward compatibility; these won't help you, because only the most recent release supports VB development.
If you have the Palm desktop software installed on your development machine, as is required to run AppForge, there are some issues you should be aware of when you install the CDK. By default, your system uses the HotSync manager that was installed with the Palm desktop software. Unfortunately, the ActiveX objects needed to develop and debug conduits are not installed with earlier releases of the Palm desktop.
If you have the luxury of developing your applications and software on separate computers, the following step is unnecessary. Otherwise, you will have to alter the desktop configuration so that the new HotSync manager runs during the synchronization process. Use the Microsoft RegEdit.exe tool, and alter the two registry keys as shown in Example 4-1. Note that the location of the updated HotSync manager depends on where you installed the CDK on your filesystem.
Example 4-1: Registry settings for HotSync manager
HKEY_CURRENT_USER\Software\U.S. Robotics\Pilot Desktop\CoreHotSyncPath = C:\CDK401\Common\Bin\C4.01\Hotsync.exePath = C:\CDK401\Common\Bin\C4.01This is a good time to run the CDK tutorial SimpleDB, which shows how to run and debug a program under the control of the HotSync manager. This is how all conduits are executed, and it is important that you get comfortable with this environment. Running the tutorial also ensures that all of the CDK components are properly installed, and that the new HotSync manager is running correctly.
To run the tutorial, use the CondCfg.exe program to register the VB development environment as a conduit. Once the HotSync manager calls the VB IDE, you are in control and can test and debug your conduits with all the powerful VB features to which you are accustomed.
Let's walk through the steps required to configure the tutorial. Start the CondCfg.exe tool to show all the currently registered conduits, and to add, edit, and delete conduits. If you are developing on your own Palm desktop, be careful not to disturb the settings for the native Palm applications! We disable all conduits except those actually under development; this both increases the speed of the synchronization process and provides some security from data corruption. Press the
Addbutton to bring up theConduit Informationdialog, into which you enter the settings for your conduit (see Figure 4-4). First, you must enterComConduit.dllin the name field. The HotSync manager uses this DLL to locate and instantiate COM conduits.
Figure 4-4. CondCfg.exe registration screen
![]()
Because each conduit handles exactly one application on the Palm device, you must enter the unique Creator ID of your application. (There must be only one conduit registered for any Palm application. CondCfig.exe will not allow you to register a second conduit for the same application, or, more precisely, an application with the same Creator ID).
TIP: If there is no application on the Palm device with this ID, then the conduit will not run. You must install your Palm application first.
For our sample conduit, we entered
Ch4aas the Creator ID. In addition, we enteredCh4aDBin the optional remote database field. As we'll see later, the HotSync manager provides the conduit with a more accurate list of databases during an actual synchronization session.In the extra information group box at the bottom of the dialog, select the
COMConduitradio option button. Enter the full path to the VB IDE in the COM client field.Leave all the other fields blank or with the default values. These settings were shown earlier in Figure 4-4. Press the
OKbutton to register the new settings, and restart the HotSync manager. Then exit the configuration tool.Now put the Palm device into the cradle and press the HotSync button. Although the synchronization process runs normally, the VB IDE doesn't activate. That is because the HotSync manager could not find an application on the Palm device with a Creator ID of
Ch4a.You will need to download our sample application for this chapter and install the application file Ch4a.prc. After the application is installed, perform a HotSync one more time. This time, the HotSync manager on the desktop displays the message
Status: Synchronizing COMConduit, and the VB IDE pops up.Now you can test your environment by opening SimpleDB.vbp in the CDK Tutorial folder. This VB project opens the native Memo application, and reads all the records that match a search term. Set a breakpoint in the button click event, and press
F8to step through the program. Note that Palm device displaysSynchronizing Memo Pad,because the Memo database has been opened during a HotSync session. You are now communicating as a conduit with the Palm device from VB!If the VB IDE doesn't appear, put the HotSync manager into its verbose mode. First, stop the HotSync manager by right-clicking on its icon in the Windows system tray, and select
Exit. Next, restart the HotSync manager with the-voption from the command line. After synchronizing, you can review the diagnostic log for errors. To do this, right-click on the icon and selectView Log.If the SimpleDB project doesn't connect to the Palm, or exhibits other errors, check that the Palm conduit references are set correctly (these are shown later in this chapter in Figure 4-7). You do this from the VB IDE by choosing the
Referencesoption from theProjectmenu. If these references are missing, try registering those ActiveX libraries by hand and restarting VB.Nuts and Bolts
If you stepped through the SimpleDB CDK sample project, you saw that the VB code manipulated the Palm device's databases and records using COM objects. Let's dig a little deeper into the HotSync architecture to see how a conduit communicates with the Palm device.
Recall from Figure 4-1 that the COM Sync Suite provides the interface to the HotSync manager and the Palm device.
The Sync Suite itself has several layers to isolate its interface from the underlying details of the HotSync application and to support both VB/COM and C/C++ conduits. This layered approach frees the VB programmer from worrying about messy details such as which serial port the HotSync manager is using to communicate with the Palm device.
To simplify conduit development, the Sync Suite provides COM objects and classes that encapsulate the HotSync manager, the user, and databases on the Palm device. A utility class is provided to handle things like Motorola byte ordering and unique record identifiers.[4] These classes and their relationships are shown in Figure 4-5.
Figure 4-5. Sync Suite class hierarchy
![]()
There are VB projects in the CDK that cover these objects and interfaces. Rather than enumerate all of them here, we will discuss the major ones we encounter as we develop our simple conduit. Once you understand the framework, you can use the VB Object Browser and the Conduit Reference manual to find the special properties you need for your conduit.
The HotSync manager expects your ActiveX conduit to implement the IPDClientNotify interface, which it calls when it needs your services, either to synchronize or to access configuration settings and preferences.
Sample Application and Conduit
The sample code for this chapter includes an AppForge application project, Ch4a.vbp, and an ActiveX conduit project, Ch4aCond.vbp.
You can use the application to create, edit and delete records on the Palm device. This application creates a database that consists of text records with a single field. Figure 4-6 shows the application's user interface on the Palm PDA. The application has a Creator ID of
Ch4a, and the database is namedCh4aDB.
Figure 4-6. Main screen for sample application Ch4a
![]()
There is no corresponding Windows application for this example. Instead, we represent desktop records using text files in a desktop folder. We will use the record identifier from the Palm database as the filename--this will guarantee uniqueness in our naming system. Synchronized desktop records have the extension .REC. Newly added desktop records will have the extension .NEW, deleted records will have the extension .DEL, and changed desktop records will have the extension .CHG.
By following this scheme, we can implement all the synchronization possibilities shown previuosly in Table 4-2. Note that to simplify this example, we aren't going to support archived records or database categories.
Let's outline what this conduit example is going to demonstrate:
- How to implement all required COM interfaces
- How to support user customization
- How to read and write data on the Palm device (FastSync and HHtoPC sync)
- Log activity and errors
- Ways to demonstrate interactive debugging
Configure the VB Conduit Project
We start building the conduit by creating a new VB project. Using the New Project Wizard, choose
ActiveX EXE. This will create the conduit as an out-of-process COM server. Normally, this is inefficient due to the marshaling of data between processes during method calls. But it gives us the ability to debug by running the conduit as a standalone process, and intercepting HotSync manager calls.After creating the project, add in the references to two Palm COM Sync Suite type libraries used by all conduits: ComStandard.dll and ComDirect.dll (see Figure 4-7). As usual, you do this from the VB IDE by choosing the
Referencesoption from theProjectmenu.
Figure 4-7. Sync Suite COM references
![]()
Save your project after renaming the default Class1 component and file to something more appropriate; we use
SyncNotifyand Ch4aNotify.cls in the example. We set the project name asCh4aCond; note that you should use the defaultThread Poolthreading model.Support IPDClientNotify
The HotSync manager expects COM conduits to support the IPDClientNotify public interface. It calls this interface to get information about your conduit, to allow the user to change settings for your conduit, and to synchronize your Palm application with its desktop data.
IPDClientNotify has four member routines that must be supported; these are summarized in Table 4-5.
Table 4-5: IPDNotifyClient public interface Interface method
Functionality
GetConduitInfo
Return conduit name, version, and default synchronization type to HotSync manager
CfgConduit
Allow user customization of sync type and return new settings to HotSync manager
ConfigureConduit
Same as CfgConduit, but called by earlier versions of HotSync manager
BeginProcess
Perform synchronization
To support a COM interface in VB, use the
Implementskeyword. We put this right at the top of our class module:Implements IPDClientNotifyAny conduits registered with the HotSync manager as ActiveX or COM clients that do not respond to these calls are dropped from the list of active conduits. This happens if you don't implement some part of the interface, or if you throw a runtime error during processing.
You should review the VB documentation if you have never implemented multiple COM interfaces in your class objects before.
GetConduitInfo
When the HotSync manager is initialized, it looks at all the conduits that have been installed on the desktop. The HotSync manager calls the function GetConduitInfo for all registered conduits several times, each time requesting different information. Table 4-6 details the parameters to GetConduitInfo.
Table 4-6: Parameters to GetConduitInfo Parameter
Direction/Type
Purpose
InfoType
[IN] EgetConduitInfo
Type of HotSync request
CreatorId
[IN] Long
Creator ID of current application
UserId
[IN] Long
Numeric ID of current user
UserName
[IN] String
String ID of current user
The HotSync manager passes the Creator ID of the application your conduit is registered to handle. This is not redundant, as nothing prevents the registration of your conduit for more than one application.
The HotSync manager also passes the identity of the current desktop user in
UserIdandUserName. Again, your application might be required to support more than one user. Note that the username is not the Windows login name; instead, it is whatever name the Palm device user chose when installing the HotSync software.The HotSync manager passes a request for information in the infoType parameter. The request is one of the constants in the public enumeration
EGetConduitInfo; GetConduitInfo returns a variant appropriate to the type of request made by the HotSync manager, as shown in Table 4-7.
Table 4-7: Return data types for GetConduitInfo Request type
Function return value
EgetConduitNameString
EgetConduitVersionDouble
EgetDefaultActionValue from enumeration
ESyncType
EgetMfcVersionValue from enumeration
EMfcVersionWe have coded GetConduitInfo using a simple
Select Case ... End Casestatement, with a case for each possible request type. The code for GetConduitInfo is shown in Example 4-2.Example 4-2: Listing for SyncNotify.GetConduitInfo
Private Function _IPDClientNotify_GetConduitInfo(ByVal infoType As EGetConduitInfo, _ByVal dwCreatorId As Long, _ByVal dwUserId As Long, _ByVal bstrUserName As String) As VariantSelect Case infoTypeCase eGetConduitNameIPDClientNotify_GetConduitInfo = "Ch4a Conduit"Case eGetConduitVersionIPDClientNotify_GetConduitInfo = 3#Case eGetDefaultActionIPDClientNotify_GetConduitInfo = ESyncTypes.eFastCase eGetMfcVersionIPDClientNotify_GetConduitInfo = EMfcVersion.ePDMFC_NOT_USEDEnd SelectEnd FunctionThe conduit name is displayed when the user selects the
Customoption from the HotSync icon in the system tray, so this should be a string that is meaningful to your users. Palm does not document how the HotSync manager uses your conduit version, so it appears that a conduit can supply any double value.The HotSync manager uses your conduit's default action when synchronizing, unless the user sets a new default type (see the following section). As with most conduits, we specify fast synchronization as the default:
Case eGetDefaultActionIPDClientNotify_GetConduitInfo = PDDirectlib.eFastThe HotSync manager supports multiple conduit architectures, among them conduits implemented using the Microsoft Foundation Class framework. ActiveX conduits should return
ePDMFC_NOT_USEDwhen asked for the MFC version, to avoid confusing the HotSync manager.CfgConduit
When the user needs to change your conduit's behavior, the HotSync manager calls the interface function CfgConduit. This call is always in response to user interaction with a dialog similar to that shown in Figure 4-2. Table 4-8 details the parameters to CfgConduit.
Table 4-8: Parameters to CfgConduit Parameter
Type/direction
Purpose
CreatorId
[IN] Long
Creator ID of current application
UserId
[IN] Long
Numeric ID of current user
UserId
[IN] String
String ID of current user
PathName
[IN] String
User folder in HotSync directory
SyncPerm
[IN/OUT]
EsyncTypesSee text
SyncTemp
[IN/OUT]
EsyncTypesSee text
SyncNew
[IN/OUT]
EsyncTypesSee text
SyncPref
[OUT]
ESyncPrefTell HotSync manager that changes are permanent or temporary
Just like GetConduitInfo, the HotSync manager passes CfgConduit the application's Creator ID and the identity of the current desktop user. In addition, the HotSync manager passes the location of a folder on the desktop for this user. The folder location is usually relative to the HotSync manager; with our configuration, this looks something like:
C:\CDK401\Common\Bin\C4.01\HolmesMHere's what the Palm Windows Conduit Reference says about the three sync-type parameters:
- SyncNew
- The type of synchronization to perform for a new device
- SyncTemp
- The type of synchronization to perform on a onetime (temporary) basis
- SyncPerm
- The type of synchronization to perform on an ongoing (permanent) basis
The implication is that you set these variables to tell the HotSync manager how to run your conduit under different circumstances. Unfortunately, reading and setting these variables from a COM conduit does not work exactly as documented.
WARNING: On entry, you will find that all the variables have the same value, usually the default synchronization type for your conduit. On exit, you must set all three variables to the same value.
The HotSync manager uses the
SyncPrefvariable to determine if the synchronization choice is to be made permanent, or if it is for the next synchronization session only. Set this value to eitherePermanentPreferenceoreTemporaryPreferenceas appropriate.Let's look at our implementation of this interface function; it is really quite simple. Example 4-3 shows the code for CfgConduit.
Example 4-3: Listing for SyncNotify.CfgConduit
Private Sub IPDClientNotify_CfgConduit(ByVal nCreatorId As Long, _ByVal nUserId As Long, _ByVal bstrUserName As String, _ByVal bstrPathName As String, _ByVal nSyncPerm As ESyncTypes, _ByRef nSyncTemp As ESyncTypes, _ByRef nSyncNew As ESyncTypes, _ByRef nSyncPref As ESyncPref)' Set up the form: type of sync to perform, user directorySyncForm.SetFields nSyncNew' Let the user make choices, then retrieve them from the form. The' form must be modal, this is required by COM.SyncForm.Show vbModalSyncForm.GetFields nSyncNew, nSyncTemp, nSyncPerm, nSyncPrefEnd SubThe project includes a form called
SyncForm, through which the user makes changes. The first thing we do is to call SetFields, a public function in the form module. It sets private form variables that are used in the Load event to initialize the controls. Without it, we'd have to interact with the form elements directly. This is risky because accessing a form element usually causes the form to be shown before you are ready.Next, we show the form and let the user interact with the dialog. Note that the form must be shown modally: a COM object cannot display a non-modal form without a lot of extra steps. The form will fail to load if you do not supply the VBModal parameter:
SyncForm.Show vbModalSyncForm
Since this is a mirror-image conduit, our custom user interface is designed to look just like the Palm native applications.
The form is simple, consisting of a group of radio buttons for the sync types, a checkbox for making the selected sync preference the default, and
OKandCancelbuttons. We added an image control, to depict graphically what each type of synchronization does. The control's bitmap is taken from a screen shot of the Palm Address conduit.The form user interface was shown earlier, in Figure 4-2. In our example, the form name is
SyncForm, and it is saved as Ch4aForm.frm.Your conduit might not need to support all these synchronization types, so feel free to remove choices. For example, we have disabled the
PCtoHHoption in our sample conduit (see Figure 4-2). But you should always include an option for your conduit to do nothing. We guarantee that this option will be used more often than you think!And if your conduit has special requirements or extra configuration options, this is the place to expose them.
Here is the implementation of SetFields, which was discussed earlier:
Public Sub SetFields(ByVal nSyncPerm As Long)m_nSyncType = nSyncPermEnd SubThe code for the form Load event is shown in Example 4-4. Load assumes that the private form variables have already been set to appropriate values--so be sure to call SetFields before loading the form. In Load, we set the radio buttons and checkbox to the states indicated by the public form variable values.
Example 4-4: Listing for SyncForm.Load
Private Sub Form_Load( )' Set the radio buttons based on the HotSync informationoptSync.Value = FalseoptHHToPC.Value = FalseoptPCToHH.Value = FalseoptDoNothing.Value = FalseSelect Case m_nSyncTypeCase eHHtoPCoptHHToPC.Value = TrueCase ePCtoHHoptPCToHH.Value = TrueCase eDoNothingoptDoNothing.Value = TrueCase ElseoptSync.Value = TrueEnd Select' Set the preference check box - default to temporary by conventionm_nSyncPref = eTemporaryPreferencechkDefault.Value = Unchecked' Assume the user will cancelm_bCancel = TrueEnd SubThe only remarkable thing about Load is that it assumes any user changes will be temporary, unless explicitly made permanent. This is the conventional behavior for the Palm native conduits, and it is a good idea for your user interface to follow suit:
m_nSyncPref = eTemporaryPreferencechkDefault.Value = UncheckedThe user can exit the form a variety of ways. For this reason, the values in the form controls are only transferred to the form variables in the
OKbutton's Click event (see Example 4-5). At this time, the cancellation flag is set to false, the form is unloaded, and control returns to CfgConduit.Example 4-5: Listing for SyncForm.btn_OK.Click
Private Sub btnOk_Click( )' Transfer form variables toIf optSync.Value Thenm_nSyncType = ESyncTypes.eFastElseIf optPCToHH.Value Thenm_nSyncType = ESyncTypes.ePCtoHHElseIf optHHToPC.Value Thenm_nSyncType = ESyncTypes.eHHtoPCElseIf optDoNothing.Value Thenm_nSyncType = ESyncTypes.eDoNothingEnd IfIf chkDefault.Value = Checked Then m_nSyncPref = ePermanentPreference' Flag used in GetFields( ) to see if the variables are validm_bCancel = FalseUnload MeEnd SubIf the user exits the form, either by pushing the
Cancelbutton directly or pressing theESCkey, control is transferred to theCancelbutton's Click event. We don't show that routine here, but in it, the cancellation flag is set totrue,the form is unloaded, and control is returned to CfgConduit.At this point, the user has dismissed the form. Now we retrieve the new variable settings using the GetFields routine (which is smart enough not to overwrite any values if the user canceled rather than applied the changes). The code for GetFields is shown in Example 4-6.
Example 4-6: Listing for SyncForm.GetFields
Public Sub GetFields(ByRef nSyncNew As Long, _ByRef nSyncTemp As Long, _ByRef nSyncPerm As Long, _ByRef nSyncPref As Long)If m_bCancel Then Exit Sub' Retrieve the "default" setting from the check boxnSyncPref = m_nSyncPref' Retrieve the action setting from the radio buttonsnSyncNew = m_nSyncTypenSyncTemp = m_nSyncTypenSyncPerm = m_nSyncTypeEnd SubIf the user has canceled, then none of the form variables are transferred in GetFields. And note that all three sync-type parameters are set to the same user-configured value, despite the Conduit Reference documentation, for the reasons discussed earlier.
ConfigureConduit
This interface function is used by versions of the HotSync manager prior to release 3.0.1. For later releases of the HotSync manager, this function is only called if your conduit doesn't implement the CfgConduit interface. Table 4-9 lists the parameters for SyncNotify.ConfigureConduit.
Table 4-9: Parameters for SyncNotify.ConfigureConduit Parameter
Type/direction
Purpose
PathName
[IN] String
User folder in HotSync directory
Registry
[IN] String
Numeric ID of current user
SyncPref
[IN/OUT]
EsyncPrefTell HotSync manager that changes are permanent or temporary
SyncType
[IN/OUT]
EsyncTypesThe kind of synchronization to perform
The code for ConfigureConduit simply returns default values for the two output parameters:
Private Sub IPDClientNotify_ConfigureConduit(ByVal bstrPathName As String, _ByVal bstrRegistry As String, _ByRef nSyncPref As ESyncPref, _ByRef nSyncType As ESyncTypes)nSyncType = PDdirectlib.eFastnSyncPref = PDdirectlib.ePermanentPreferenceEnd SubBeginProcess
The HotSync manager calls the interface function BeginProcess when an actual synchronization should occur. At this point, you can assume that the Palm device is in the cradle and the user has pressed the HotSync button.
Our implementation of BeginProcess is intentionally minimal; we delegate all the real work of synchronization to a private class. The code for BeginProcess is shown in Example 4-7.
Example 4-7: Listing for SyncNotify.BeginProcess
Private Function IPDClientNotify_BeginProcess( ) As Boolean' Create our sync object and do the workDim Worker As New SyncLogicWorker.Synchronize' Return false to signal completion!IPDClientNotify_BeginProcess = FalseEnd FunctionBeginProcess should return
Falsewhen it has completed. That signals the HotSync manager that it can skip to the next conduit. ReturningFalsedoesn't indicate that your conduit was successful; instead, it means that it is finished executing. Later, we will see how to return status information to the user in the HotSync log.The real work of synchronization occurs in these lines:
Dim Worker As New SyncLogicWorker.SynchronizeRight away, you should notice that the Worker.Synchronize method doesn't take any arguments. It gets all the information it needs to synchronize from publicly creatable COM objects supplied by the Sync Suite API.
We have finished implementing the IPDClientNotify interface, including the user interface required to support configuring the conduit. The conduit is complete from the perspective of the HotSync manager: the conduit can identify itself and its properties, it can be configured, and it responds to synchronization requests.
Synchronization Logic
The
SyncLogicobject created in the BeginProcess routine encapsulates all of our synchronization logic. In our example, the class instancing property is set to Private, and the class file is saved as Ch4aLogic.cls. By partitioning a conduit in this fashion, between the public interface and the internal synchronization logic, we ensure that the interface code can be reused in other conduits.Let's look at the Synchronize method, which is called to handle all of the synchronization requests made of this conduit. The code for Synchronize is shown in Example 4-8.
Example 4-8: Listing for SyncLogic.Synchronize
Public Sub Synchronize( )' Get the HotSync information object for this conduitDim pdSys As New PDSystemAdapterDim pdHSInfo As PDHotsyncInfoSet pdHSInfo = pdSys.PDHotsyncInfo' Route the requested synchronization to the local handlerSelect Case pdHSInfo.SyncTypeCase eFastFastSync pdSys, pdHSInfoCase eSlowSlowSync pdSys, pdHSInfoCase eHHtoPCHHtoPC pdSys, pdHSInfoCase ElseLogSync pdSys, pdHSInfo.SyncTypeEnd SelectEnd SubThe first thing the method does is to declare and initialize a Palm Sync Suite COM object:
Dim pdSys As New PDSystemAdapterThe
PDSystemAdapterclass represents the Palm device. This powerful class provides access to most features of the device (except databases, which are handled by a separate class). ThePDSystemAdapterclass has the methods and properties shown in Table 4-10.
Table 4-10: Properties and methods of PDSystemAdapter Property or method name
Description
AddLogEntry
Makes a local or device HotSync log entry
CallRemoteModule
Runs a program on the device
DateTime
Retrieves the date/time on the device
HHOsVersion
Retrieves the device operating system version
LocalizationID
Retrieves the device localization setting
PDHotSyncInfo
Object representing current HotSync
PDMemoryCardInfo
Object representing device memory
PDUserInfo
Object representing current user
ProductId
Retrieves the device product ID
ReadAppPreference
Retrieves a device application setting
ReadFeature
Retrieves device feature memory
RebootSystem
Performs a device soft-reset
RomSoftwareVersion
Retrieves the device ROM version
SyncManagerAPIVersion
Retrieves HotSync API Version
WriteAppPreference
Stores an application setting on device
The
PDSystemAdapterand its subobjects exist only when synchronization is actually occurring. You cannot create this object, or its subobjects, outside the scope of BeginProcess. Our example conduit uses only a fraction ofPDSystemAdapter's features; you can explore the CDK samples to see how to use the other features.If you look at the Sync Suite class hierarchy shown earlier in Figure 4-5, you see that one of the subobjects of the system adapter is
PDHSInfo. This object represents the current HotSync session. From it, we can get the synchronization type that the HotSync manager wants our conduit to run:Dim pdHSInfo As PDHotsyncInfoSet pdHSInfo = pdSys.PDHotsyncInfoNote that the
PDHSInfoclass is not a publicly creatable object. You must use the system adapter to get one, as shown. ThePDHSInfoclass has the methods and properties shown in Table 4-11.
Table 4-11: Properties and methods of PDHSInfo Property or method name
Description
CardNum
Memory card on device for this application
ConnectionType
Indicator of local, modem, or network connection
Creator
Application Creator ID for this conduit
DbType
Database type for this conduit
FirstSync
Indicator of first synchronization for device or desktop
LocalName
Application name on device
NameList
Database(s) for this Creator ID on device
PathName
Path for user area in HotSync directory
RegistryKey
Registry key for this conduit
RegistryPath
Registry path for this conduit
RemoteNameCount
Number of databases for this Creator ID on device
SyncType
Type of synchronization to perform
UserName
Username on device for this conduit
As with
PDSystemAdapter, we use only a couple of features from thePDHSInfoobject. While our sample conduit only has one database, the HotSync manager provides your conduit with a list of all remote databases that belong to your application. (Even though a conduit might be responsible for synchronizing more than one database, it can only have one open at a time. This complicates the design if the conduit needs to enforce relationships between the databases (tables)).We use the SyncType object property to route program flow to the function that handles the requested synchronization type, supplying the newly created COM objects as reference parameters:
Select Case pdHSInfo.SyncTypeCase eFastFastSync pdSys, pdHSInfoCase eSlowSlowSync pdSys, pdHSInfoCase eHHtoPCHHtoPC pdSys, pdHSInfoCase ePCtoHHPCtoHH pdSys, pdHSInfoBecause this conduit only supports
Fast,Slow,HHtoPCandPCtoHHsynchronization, we direct all other synchronization types to a function that simply logs the request:Case ElseLogSync pdSys, pdHSInfo.SyncTypeEnd SelectWe won't spend any time looking at the code for LogSync; it consists of a large select statement that builds a string identifying the conduit and synchronization type, and then uses the pdSystemAdapater.AddLogEntry method to write the entry to the HotSync log:
pdSys.AddLogEntry "Ch4a - " + strType, eText, False, FalseCalling AddLogEntry with an option other than
eText, such aseWarning, causes the HotSync manager to alert the user after all conduits have finished executing, as shown in Figure 4-8. See the enumeration typeElogActivitityfor the supported log types.
Figure 4-8. HotSync warning dialog
![]()
The AddLogEntry method supports writing to either the desktop or the device HotSync log. Set the optional fourth parameter to
Trueto write to the device. Take care when writing to the device log to keep the amount of information to a minimum.That wraps up the high-level presentation of the
Synchronizeobject: we've seen how it is created, how it routes HotSync commands to the correct internal functions, and how it logs information to the HotSync log. Next, we are going to look at the low-level synchronization functions HHtoPC and FastSync.HHtoPC
In our simple conduit, the HHtoPC routine is called only at the user's request. The purpose of this routine couldn't be simpler: it deletes all records from the desktop and then copies any records from the Palm device. The code for HHtoPC is shown in Example 4-9.
Example 4-9: Listing for SyncLogic.HHtoPC
Private Sub HHtoPC(ByRef pdSys As PDSystemAdapter, _ByRef pdHSInfo As PDHotsyncInfo)' Data is under user's directory in HotSync area.Dim DBPath As StringDBPath = pdHSInfo.PathName + "Ch4a"' Purge the PC data - force a new directory if necessary.Dim FSO As New FileSystemObjectOn Error Resume NextFSO.DeleteFile DBPath + "\*.*", TrueFSO.CreateFolder DBPathOn Error GoTo 0' Get the Palm database from the HotSync managerDim DBName As StringDBName = pdHSInfo.NameList(0)Dim pdQuery As New PDDatabaseQueryDim pdRecords As PDRecordAdapterSet pdRecords = pdQuery.OpenRecordDatabase(DBName, "PDDirect.PDRecordAdapter")' Open the handheld database, and iterate over the recordsDim Index As LongDim RecordId As VariantDim Category As LongDim Attributes As ERecordAttributesDim Data As VariantpdRecords.IterationIndex = 0Data = pdRecords.ReadNext(Index, RecordId, Category, Attributes)Do While Not pdRecords.EOF' In a HHtoPC sync, process all but deleted recordsIf Not CBool(CByte(Attributes) And CByte(eDelete)) ThenWriteRecContents DBPath, RecordId, DataEnd If' Read the next record and skip to the top of the loopData = pdRecords.ReadNext(Index, RecordId, Category, Attributes)Loop' Remove any deleted records, clear flags in Palm databasepdRecords.RemoveSet eRemoveAllDeletedRecordspdRecords.ResetAllModifiedFlagsLogSync pdSys, pdHSInfo.SyncTypeEnd SubLet's examine the HHtoPC routine a little bit at a time. First, the routine locates the desktop data store, which is located under the user's Palm desktop directory. It is customary for Palm conduits to keep their information in this directory. The directory is available as the PathName method of the
PDHSInfoobject; the routine tacks on a folder name to create a subdirectory.Dim DBPath As StringDBPath = pdHSInfo.PathName + "Ch4a"The routine then deletes any files in that folder. Wrapping the delete operation in an error handler is necessary, because the
FileSystemObjectthrows an error if the folder doesn't exist or if it is empty.Dim FSO As New FileSystemObjectOn Error Resume NextFSO.DeleteFile DBPath + "\*.*", TrueFSO.CreateFolder DBPathNext, HHtoPC creates an instance of the Sync Suite
PDDatabaseQueryclass:Dim pdQuery As New PDDatabaseQueryThis class provides programmatic access to the Palm database manager on the device. Remember, instances of this class are only available when the user is synchronizing the device, not during conduit configuration. This class has methods and properties, as shown in Table 4-12, that allow us to manage Palm databases.
Table 4-12: Properties and methods of PDDatabaseQuery Property or method name
Description
AddLogEntry
Makes an entry in either the desktop or device HotSync log.
CreateRecordDatabase
Creates a data type database. Records are unstructured.
CreateResourceDatabase
Creates a resource-type database. Each record has a structure, such as an icon or form.
MaxAllowedRecordSize
Retrieves maximum supported record size on the device.
OpenRecordDatabase
Opens an existing data type database.
OpenResourceDatabase
Opens an existing resource-type database.
RamDbCount
Retrieves number of databases in device RAM.
ReadDbInfoByCreatorType
Retrieves statistics and settings for a database.
ReadDbInfoByName
Retrieves statistics and settings for a database.
ReadDbNameList
Retrieves list of databases in RAM or ROM.
RemoveDatabase
Deletes a RAM or ROM database.
RomDbCount
Retrieves number of databases in device ROM.
Again, look at the class hierarchy shown earlier in Figure 4-5.
PDDatabaseQueryis a publicly created object. Our main interest in this class is its ability to return objects representing actual databases on the Palm device. We get the name of our database from thePDHSInfoobject:' Get the Palm database from the HotSync managerDim DBName As StringDBName = pdHSInfo.NameList(0)Note that
NameListis an array. If your application has more than one database on the Palm PDA, each is listed in the array. The total number of databases is available in the RemoteNameCount property.The Sync Suite API provides the
PDRecordAdapterto access the contents of any one database on the Palm device. This object is created in an unusual way, by passing the programmatic identifier of a class factory into the database query object.The class factory is responsible for producing an object that satisfies all the interface requirements for an instance of
PDRecordAdapter. The CDK provides this unusual construction technique so developers can subclass the record adapter, and supply extra capabilities tailored for a specific application. In our conduit, we use the default record adapter supplied by Palm:Dim pdRecords As PDRecordAdapterSet pdRecords = pdQuery.OpenRecordDatabase(DBName, "PDDirect.PDRecordAdapter")Table 4-13 shows the many methods and properties of
PDRecordAdapter. This large class is heavily used in our sample conduit. In the table, two sets of functions have been grouped together: direct record access, denoted in the table by ReadBy*, and iterator access, denoted in the table by ReadNext*. The direct access functions allow the retrieval of a single record, either by index or record identifier. The iterator access functions allow the sequential retrieval of many records, either by index or by category or other attribute.
Table 4-13: Properties and methods of PDRecordAdapter Property or method name
Description
AccessMode
Retrieves mode(s) used to open database
AddLogEntry
Makes an entry in either the desktop or device HotSync log
ChangeCategory
Changes the Category ID for a group of records
CloseOptions
Sets modification date/time on database prior to close
DbName
Retrieves the database name
EOF
Indicates end-of-file when using an iterator
InputBufferSize
Sets the maximum size for read/write buffers
IterationIndex
Sets the start offset in the database for an iterator
PDCategories
Represents category data for this database
PDDatabaseInfo
Represents database for this record adapter
ReadAppInfoBlock
Reads application-specific data, including categories
ReadBy*
Gets record information by index or identifier
ReadNext*
Gets record information from an iterator
ReadSortInfoBlock
Retrieves application-specific data, notionally used for sorting
ReadUniqueIdList
Retrieves list of record identifiers in database
RecordCount
Retrieves count of records in database
Remove
Permanently erases a record from database
RemoveSet
Permanently erases a group of records from database
ResetAllModifiedFlags
Clears the dirty bit for all records in database
Write
Creates or updates a database record and attributes
WriteAppInfoBlock
Writes application-specific data, including categories
WriteSortInfoBlock
Writes application-specific data, notionally used for sorting
All the iterator functions support a starting index position. In HHtoPC, we use the simple iterator to process all the records in the database, starting at index zero, and reading both the actual data and other record attributes:
pdRecords.IterationIndex = 0Data = pdRecords.ReadNext(Index, RecordId, Category, Attributes)Do While Not pdRecords.EOFAll of the
PDRecordAdapterread functions return a VB variant, with the record data actually stored in a byte array. The read functions also set an attribute byte indicating the status of the current record. If the record is not marked for deletion, we copy it to the desktop by calling WriteRecContents.If Not CBool(CByte(Attributes) And CByte(eDelete)) ThenWriteRecContents DBPath, RecordId, DataEndIf your conduit supports record archival, then you should test for that condition as well, with the eArchive attribute. After we have processed the record, we read the next record, and skip to the top of the loop:
Data = pdRecords.ReadNext(Index, RecordId, Category, Attributes)LoopEventually, the read function will cause the record adapter to reach the end-of-file condition and we will exit the loop after all of the database records have been processed. HHtoPC then purges the Palm database of any logically deleted records, and clears the modification flag for all dirty records:
pdRecords.RemoveSet eRemoveAllDeletedRecordspdRecords.ResetAllModifiedFlagsNow let's look at how the WriteRecContents routine stores the Palm device record data to a desktop file. We aren't going to show all the code in WriteRecContents, but just the highlights. This routine uses a new Sync Suite object,
PDUtility, to transform the Palm Record ID into a string:Dim pdUtil As New PDUtilityFilename = DBPath + "\" + pdUtil.RecordIdToString(RecordId) + ".REC"The Palm CDK documentation strongly encourages developers to use this function, rather than dissecting the variant data type holding a Record ID. This is because the Record ID format, currently a long integer, may change in the future. The
PDUtilityclass has other methods to convert data between the Palm and desktop formats. We will see some of these as we pack and unpack string data for our records.This completes the presentation of the HHtoPC synchronization logic for our simple conduit. Although this section presented a great deal of information quickly, you should now have an appreciation of how to use the Sync Suite COM objects to access features and data on the Palm device.
FastSync
The HHtoPC sync didn't require much of a design--just take the Palm records and write them to the desktop, removing any existing desktop records in the process. Mirror synchronization is much harder.
Before we start into coding the FastSync, let's reexamine what mirror synchronization means for the Ch4a application. In Table 4-14, we've recast our table of possibilities to include the desktop file extensions instead of attribute flags. The code for our conduit has to identify all these possibilities, and then take the action indicated in the table. Preparing a table or state diagram like this when designing your conduit will prove helpful.
Table 4-14: Ch4a conduit actions
No record
No change
.CHG
.NEW
.DEL
No record
D
P
No change
D
P
Remove DP
Change
P
D
Conflict
P
D
New
P
D
Delete
Remove DP
D
P
In this simple conduit, the change conflict is handled in a straightforward manner: changes to a record on the Palm device take precedence over changes to the same desktop record. This is different from the Palm CDK recommendation of creating two new records, one on each platform that mirrors the changes. Of course, you will have to decide how to resolve any conflict in a fashion that is appropriate for your application.
Now let's look at FastSync, the low-level function that actually moves the data between the Palm device and the Windows desktop. It is shown in Example 4-10.
Example 4-10: Listing for SyncLogic.HHtoPC
Private Sub FastSync(ByRef pdSys As PDSystemAdapter, _ByRef pdHSInfo As PDHotsyncInfo)Dim Index As LongDim Category As LongDim Data As VariantDim RecordId As VariantDim Attributes As ERecordAttributes' Data is under user's directory in HotSync area.Dim DBPath As StringDBPath = pdHSInfo.PathName + "Ch4a"' Get Palm database name from HotSync managerDim DBName As StringDBName = pdHSInfo.NameList(0)' Open the handheld database, and get the record interface.Dim pdQuery As New PDDatabaseQueryDim pdRecords As PDRecordAdapterSet pdRecords = pdQuery.OpenRecordDatabase(DBName, "PDDirect.PDRecordAdapter")' Iterate over the *modified* recordspdRecords.IterationIndex = 0Data = pdRecords.ReadNextModified(Index, RecordId, Category, Attributes)Do While Not pdRecords.EOFDim strID As StringDim pdUtil As New PDUtilitystrID = pdUtil.RecordIdToString(RecordId)If CBool(CByte(Attributes) And CByte(eDelete)) ThenDeletedRec DBPath, strID, DataElseDirtyRec DBPath, strID, DataEnd If' Get the next recordData = pdRecords.ReadNext(Index, RecordId, Category, Attributes)LoopDim File As FileDim Folder As FolderDim FSO As New FileSystemObjectSet Folder = FSO.GetFolder(DBPath)For Each File In Folder.FilesSelect Case UCase(FSO.GetExtensionName(File.Name))Case "NEW"NewFile DBPath, File, pdRecords, pdUtilCase "DEL"DeletedFile File, pdRecords, pdUtilCase "CHG"DirtyFile DBPath, File, pdRecords, pdUtilEnd SelectNextpdRecords.RemoveSet eRemoveAllDeletedRecordspdRecords.ResetAllModifiedFlagsLogSync pdSys, pdHSInfo.SyncTypeEnd SubWe won't go over every line, as we have seen a lot of this code already in HHtoPC. The routine consists of some setup code, and then two main loops. The first loop pulls changes from the Palm device, and the second loop writes changes to the Palm device.
One large difference between FastSync and HHtoPC is that with FastSync, we only want to process records that have changed since the last synchronization. Remember, this is the definition of a FastSync. The
PDRecordAdapterprovides the ReadNextModified iterator that is specially designed for this circumstance. Each call to this iterator skips through the Palm database, returning only the changed records. As a side effect, the index variable will be incremented, not by one, but by however many records the iterator skipped over to find the next changed record.Data = pdRecords.ReadNextModified(Index, RecordId, Category, Attributes)In a Palm database, it is possible for a record to be both deleted and dirty at the same time. Actually, it can be archived as well, but remember that we don't support the Palm archive attribute.
A Palm application usually asks the user if the deleted record should be archived or simply removed from the database. Deleted records have no data, but they are still present in the physical database; you test for them using the eDelete attribute.
If CBool(CByte(Attributes) And CByte(eDelete)) ThenDeletedRec DBPath, strID, DataElseDirtyRec DBPath, strID, DataEnd IfArchived records have data in order to support the archive operation; typically, the archived record is also marked for deletion.
TIP: If your application uses the AppForge database library, call the extended delete function PDBDeleteRecordEx to mark a record as both archived and deleted. You do this by passing
afDeleteModeArchiveto the function.For our conduit, there is no difference between a new record and a changed record; both are dirty relative to the desktop. Note that the CDK doesn't provide a method to distinguish the two cases (there is no eNew attribute). You can tell, of course, because a new Palm database record won't have a corresponding desktop file.
At this point, all the changed records from the Palm device are safely written to the desktop folder. As we'll see later, DirtyRec and DeletedRec take care of any conflicts between desktop and Palm device records. Now FastSync needs to write any changed data from the desktop to the Palm database.
FastSync loops through the files on the desktop, looking for those with extensions that require some processing. For each file found, it calls a routine to do the actual work, supplying the file object, the record adapter, and the utility object as reference parameters:
Select Case UCase(FSO.GetExtensionName(File.Name))Case "NEW"NewFile DBPath, File, pdRecords, pdUtilCase "DEL"DeletedFile File, pdRecords, pdUtilCase "CHG"DirtyFile DBPath, File, pdRecords, pdUtilEnd SelectThe last thing the FastSync routine has to do is to clean up deleted records on the Palm device, and clear the change bit(s). The data is now synchronized, so nothing is dirty! FastSync uses the same calls we saw in HHtoPC to do this cleanup work.
Now that the top-level structure of FastSync is clear, let's look at the auxiliary functions that move the bits and bytes. The implementation of DirtyRec is shown in Example 4-11. To understand its logic, recall that in our conduit, a change to a Palm record has precedence over a change to the corresponding desktop record.
Example 4-11: Listing for SyncLogic.DirtyRec
Private Sub DirtyRec(ByVal DBPath As String, _ByVal strID As String, _ByRef Data As Variant)Dim Filenum As IntegerDim Filename As StringDim FSO As New FileSystemObject' Remove any changed or deleted desktop recordOn Error Resume NextFSO.DeleteFile DBPath + "\" + strID + ".DEL", TrueFSO.DeleteFile DBPath + "\" + strID + ".CHG", TrueOn Error GoTo 0' Write device data to desktop recordFilenum = FreeFileFilename = DBPath + "\" + strID + ".REC"Open Filename For Output As #FilenumWrite #Filenum, StrConv(Data, vbUnicode)Close #FilenumEnd SubBecause changes to desktop records are stored in files with the extension .DEL or .CHG, DirtyRec simply removes those files. Then the contents of the Palm record are written into the desktop file. This process overwrites the old desktop record, if it existed, or creates a new record file with the correct name and extension.
This is in accordance with our design decision that changes to the Palm record have precedence over the desktop. A conduit that implemented Palm's recommended mirroring strategy would have to reconcile the contents of the files with the Palm record data.
The implementation of DeletedRec is very similar.
Private Sub DeletedRec(ByVal DBPath As String, _ByVal strID As String, _ByRef Data As Variant)Dim FSO As New FileSystemObjectOn Error Resume NextFSO.DeleteFile DBPath + "\" + strID + ".REC", TrueOn Error GoTo 0End SubHowever, note that DeletedRec does not remove the .CHG record, which gets processed later. This is because a change on the desktop has precedence over deletions on the Palm device. If this seems unclear, look over Table 4-14 again.
The NewFile function creates a new record in the Palm database when the user has created one on the desktop:
Private Sub NewFile(ByVal DBPath As String, _ByRef File As File, _ByRef pdRecords As PDRecordAdapter, _ByRef pdUtil As PDUtility)Dim Data As VariantDim RecordId As VariantGetFileContents File, Data, pdUtil' Create a new Palm device recordRecordId = vbEmptypdRecords.Write RecordId, 0, 0, DataFile.Move DBPath + "\" + pdUtil.RecordIdToString(RecordId) + ".REC"End SubDespite its simplicity, there is a lot going on in NewFile. First, the routine calls GetFileContents to read the file data into a variant byte array for uploading to the Palm database record. We'll see how this is done later.
Next, we create a new record in the Palm database. The
PDRecordAdapterclass doesn't have an explicit record creation method; instead, you call its Write function with a special record identifier. Passing a variant set tovbEmptydoes the trick. When the Write function returns, it has replacedvbEmptywith the new Record ID.TIP: It is not always possible to create a record on the device--for example the storage heap could be exhausted. We don't handle that error in our simple conduit, but your conduit should.
The last thing NewFile does is to rename the .NEW desktop file so we don't process it again later. We generate a filename using the new Record ID and an extension of .REC. The records are now synchronized on the desktop and the device. If the user later changes this record on the Palm, our conduit will be able to locate the corresponding desktop file using the Record ID as filename.
Now let's look at GetFileContents, shown in Example 4-12. Reading in the file contents is simple enough; the routine assumes all the text is a single input field delimited by quotation marks, and reads it into the string variable
sBuf.Example 4-12: Listing for SyncLogic.GetFileContents
Private Sub GetFileContents(ByRef File As File, _ByRef Data As Variant, _ByRef pdUtil As PDUtility)Dim Filenum As IntegerDim sBuf As VariantDim RecordId As VariantDim bArray( ) As ByteFilenum = FreeFileOpen File.Path For Input As #FilenumInput #Filenum, sBufClose #Filenum' Convert to a byte arrayData = bArrayReDim Data(0 To Len(sBuf))pdUtil.BSTRToByteArray Data, 0, sBufEnd SubNext, we convert the input string,
sBuf(which may or may not be Unicode, depending on your operating system), into a byte array. To do this, declare an empty byte array and assign the reference parameter Data to it:Dim bArray( ) As Byte...Data = bArrayThis effectively converts Data, which is a type-less variant, into a byte array. Re-dimension Data to hold the input string, and use the utility function BSTRToByteArray to pack the string data into the array:
ReDim Data(0 To Len(sBuf))pdUtil.BSTRToByteArray Data, 0, sBufWe resort to this trickery because you cannot pass a VB byte array directly into a COM function call. If your conduit's data is more complicated, you should look at the other conversion functions in
PDUtility.The conduit calls DeletedFile to handle desktop files that the user has marked for deletion. This function is very straightforward: convert the desktop filename into a Palm Record ID using the StringToRecordId utility function, and then call the
PDRecordAdapterRemove function to erase the database record. Here's the code for DeletedFile:Private Sub DeletedFile(ByRef File As File, _ByRef pdRecords As PDRecordAdapter, _ByRef pdUtil As PDUtility)Dim RecordId As VariantDim FSO As New FileSystemObjectRecordId = pdUtil.StringToRecordId(FSO.GetBaseName(File.Name))On Error Resume Next