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.[30] These classes and their relationships are shown in Figure 4-5.
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.
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 named Ch4aDB
.
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
Log activity and errors
Ways to demonstrate interactive debugging
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 References
option from the
Project
menu.
Save your project after renaming the default Class1 component and
file to something more appropriate; we use
SyncNotify
and
Ch4aNotify.cls
in the example. We set the
project name as Ch4aCond
; note that you should use
the default Thread Pool
threading model.
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 |
---|---|
|
Return conduit name, version, and default synchronization type to HotSync manager |
|
Allow user customization of sync type and return new settings to HotSync manager |
|
Same as |
|
Perform synchronization |
To support a COM interface in VB, use
the Implements
keyword. We put this right at the
top of our class module:
Implements IPDClientNotify
Any 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.
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 |
---|---|---|
|
[IN] EgetConduitInfo |
Type of HotSync request |
|
[IN] Long |
Creator ID of current application |
|
[IN] Long |
Numeric ID of current user |
|
[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 UserId
and UserName
.
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 |
---|---|
|
String |
|
Double |
|
Value from enumeration |
|
Value from enumeration |
We have coded GetConduitInfo
using a simple
Select Case ... End Case
statement, 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 Variant Select Case infoType Case eGetConduitName IPDClientNotify_GetConduitInfo = "Ch4a Conduit" Case eGetConduitVersion IPDClientNotify_GetConduitInfo = 3# Case eGetDefaultAction IPDClientNotify_GetConduitInfo = ESyncTypes.eFast Case eGetMfcVersion IPDClientNotify_GetConduitInfo = EMfcVersion.ePDMFC_NOT_USED End Select End Function
The conduit name is displayed when the user selects the
Custom
option 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 eGetDefaultAction IPDClientNotify_GetConduitInfo = PDDirectlib.eFast
The HotSync manager supports multiple conduit architectures, among
them conduits implemented using the Microsoft Foundation Class
framework. ActiveX conduits should return
ePDMFC_NOT_USED
when asked
for the MFC version, to avoid confusing the HotSync manager.
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] |
See text |
SyncTemp |
[IN/OUT] |
See text |
SyncNew |
[IN/OUT] |
See text |
SyncPref |
[OUT] |
Tell 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\HolmesM
|
Here’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 SyncPref
variable 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
either ePermanentPreference
or
eTemporaryPreference
as 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 directory SyncForm.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 vbModal SyncForm.GetFields nSyncNew, nSyncTemp, nSyncPerm, nSyncPref End Sub
The 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 vbModal
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 OK
and Cancel
buttons. 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 PCtoHH
option 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 = nSyncPerm End Sub
The 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 information optSync.Value = False optHHToPC.Value = False optPCToHH.Value = False optDoNothing.Value = False Select Case m_nSyncType Case eHHtoPC optHHToPC.Value = True Case ePCtoHH optPCToHH.Value = True Case eDoNothing optDoNothing.Value = True Case Else optSync.Value = True End Select ' Set the preference check box - default to temporary by convention m_nSyncPref = eTemporaryPreference chkDefault.Value = Unchecked ' Assume the user will cancel m_bCancel = True End Sub
The 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 = eTemporaryPreference chkDefault.Value = Unchecked
The 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 OK
button’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 to If optSync.Value Then m_nSyncType = ESyncTypes.eFast ElseIf optPCToHH.Value Then m_nSyncType = ESyncTypes.ePCtoHH ElseIf optHHToPC.Value Then m_nSyncType = ESyncTypes.eHHtoPC ElseIf optDoNothing.Value Then m_nSyncType = ESyncTypes.eDoNothing End If If chkDefault.Value = Checked Then m_nSyncPref = ePermanentPreference ' Flag used in GetFields( ) to see if the variables are valid m_bCancel = False Unload Me End Sub
If the user exits the form, either by pushing the
Cancel
button directly or pressing the
ESC
key, control is transferred to the
Cancel
button’s
Click event. We don’t show that
routine here, but in it, the cancellation flag is set to
true,
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 box nSyncPref = m_nSyncPref ' Retrieve the action setting from the radio buttons nSyncNew = m_nSyncType nSyncTemp = m_nSyncType nSyncPerm = m_nSyncType End Sub
If 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.
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] |
Tell HotSync manager that changes are permanent or temporary |
SyncType |
[IN/OUT] |
The 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.eFast nSyncPref = PDdirectlib.ePermanentPreference End Sub
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 work Dim Worker As New SyncLogic Worker.Synchronize ' Return false to signal completion! IPDClientNotify_BeginProcess = False End Function
BeginProcess
should return
False
when it has completed. That signals the
HotSync manager that it can skip to the next conduit. Returning
False
doesn’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 SyncLogic Worker.Synchronize
Right 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.
[30] The Palm device currently uses the Motorola 68000 series processor. This CPU represents numbers in little-Endian order, which is different from the Intel 80x86 processors. Your conduit must handle the conversion if your application stores numeric data.
Get Programming Visual Basic for the Palm OS now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.