Within the CDK there is a tool named "CondCfg.exe" that we need to examine. Find it within the "CDK403\Common\Bin" folder and execute it. You will see a list of existing install conduits including their CreatorIDs and a Name description option. Let's go ahead and add a new conduit for our application. Figure 3 below lists all of the items you must set in the "Conduit Information" screen. Figure 3: New Conduit Information

Make sure you first choose "Component" as the Conduit Type and then point the "COM Client" to your version of VS .NET to the devenv.exe executable. This will force the HotSync Manager to open VS .NET,so we can choose our solution to load, allowing us to debug the conduit synchronization process. The HotSync Manager will stay idle until we close VS .NET. Of course this will stop the entire synchronization process, so later we will change it to our compiled application, the output of the PBlgConduit WinForm application.
Next we need to wire up the incoming data to our data tier. I chose to put this code in my Form1_Load() event handler and also create a new class "PalmBlogWorker.cs". Together they will be responsible for iterating over all of the incoming databases, records, and fields and loading the appropriate managed object, and eventually posting the data to the correct site. Figure 4 shows the full PalmBlogWorker.cs class.
Figure 4: PalmBlogWorker.cs class
using System;
using System.Runtime.InteropServices; //need to import .net's InteropServices namespace
namespace PBlgConduit {
publicclass PalmBlogWorker {
/*Settings properties*/
PDStandardLib.PDRecordAdapter blogSettings;
//represents the query for
our database
PDDirectLib.PDDatabaseQuery settingsQuery = new PDDirectLib.PDDatabaseQuery();
//all of the stored settings
on the palm
System.Collections.ArrayList settings = new System.Collections.ArrayList();
public System.Collections.ArrayList
Settings{get{return settings;}}
int dirtyCountSettings=0;
publicint DirtyCountSettings{get{return dirtyCountSettings;}}
int archiveCountSettings=0;
publicint ArchiveCountSettings{get{return archiveCountSettings;}}
int deleteCountSettings=0;
publicint DeleteCountSettings{get{return deleteCountSettings;}}
int secretCountSettings=0;
publicint SecretCountSettings{get{return secretCountSettings;}}
/*Entries properties*/
PDStandardLib.PDRecordAdapter blogrecord;
//entries database
PDDirectLib.PDDatabaseQuery query = new PDDirectLib.PDDatabaseQuery();
//entries collection
System.Collections.ArrayList entries = new System.Collections.ArrayList();
public System.Collections.ArrayList Entries{get{return entries;}}
int dirtyCount=0;
publicint DirtyCount{get{return dirtyCount;}}
int archiveCount=0;
publicint ArchiveCount{get{return archiveCount;}}
int deleteCount=0;
publicint DeleteCount{get{return deleteCount;}}
int secretCount=0;
publicint SecretCount{get{return secretCount;}}
///<summary>
/// open all the settings entries in the database and import
them
///</summary>
privatevoid ImportSettings() {
int nIndex=0;
object vUniqueId=null;
int nCategory=0;
PDStandardLib.ERecordAttributes
eAttributes=PDStandardLib.ERecordAttributes.eDirty;
object vData=null;
long nCount=0;
blogSettings.IterationIndex = 0;
try {
//attempt to
read an the first item out of our database
vData = blogSettings.ReadNext(out nIndex,out vUniqueId,out nCategory,out eAttributes);
}catch(Exception
exc) {
exc.ToString();
}
try {
//loop for each
settings item
while(!(blogSettings.EOF))
{
if(vData!=null) {
//create
a place holder class for the new settings
PBlgConduit.DataTier.BlogSetting
setting = new PBlgConduit.DataTier.BlogSetting();
//using
our importer bind the byte[] to our settings class
PalmDBImporter.Importer.BindToClass(setting,
(byte[])vData);
//set
the miscel properties
setting.EAttributes=eAttributes;
setting.Index=nIndex;
setting.UniqueId=vUniqueId;
setting.Category=nCategory;
//increment
the miscel counters -not really needed
//but
nice to have if you plan on expanding the app more
if(eAttributes==PDStandardLib.ERecordAttributes.eDirty) dirtyCountSettings++;
if(eAttributes==PDStandardLib.ERecordAttributes.eArchive)
archiveCountSettings++;
if(eAttributes==PDStandardLib.ERecordAttributes.eDelete) deleteCountSettings++;
if(eAttributes==PDStandardLib.ERecordAttributes.eSecret) secretCountSettings++;
setting.Raw=(byte[])vData;
//add
the current settings to our collection
settings.Add(setting);
nCount++;
}
//Read
the next record
vData = blogSettings.ReadNext(out nIndex,out vUniqueId,out nCategory,out eAttributes);
}
}catch(Exception){}
}
///<summary>
/// open all the blog entries in the database and import them
///</summary>
privatevoid ImportEntries() {
int nIndex=0;
object vUniqueId=null;
int nCategory=0;
PDStandardLib.ERecordAttributes
eAttributes=PDStandardLib.ERecordAttributes.eDirty;
object vData=null;
long nCount=0;
blogrecord.IterationIndex = 0;
try {
//read the first
record, if any
vData = blogrecord.ReadNext(out nIndex,out vUniqueId,out nCategory,out eAttributes);
}catch(Exception
exc) {
exc.ToString();
}
try {
//loop for each
record
while(!(blogrecord.EOF))
{
if(vData!=null) {
//create
the entry placeholder
PBlgConduit.DataTier.BlogEntry
entry = new PBlgConduit.DataTier.BlogEntry();
//bind
the data to the entry class
PalmDBImporter.Importer.BindToClass(entry,
(byte[])vData);
//set
miscel items
entry.EAttributes=eAttributes;
entry.Index=nIndex;
entry.UniqueId=vUniqueId;
entry.Category=nCategory;
if(eAttributes==PDStandardLib.ERecordAttributes.eDirty) dirtyCount++;
if(eAttributes==PDStandardLib.ERecordAttributes.eArchive)
archiveCount++;
if(eAttributes==PDStandardLib.ERecordAttributes.eDelete) deleteCount++;
if(eAttributes==PDStandardLib.ERecordAttributes.eSecret) secretCount++;
entry.Raw=(byte[])vData;
//add
new entry to our collection of entries
entries.Add(entry);
nCount++;
}
//Read
the next record
vData = blogrecord.ReadNext(out nIndex,out vUniqueId,out nCategory,out eAttributes);
}
}catch(Exception){}
}
///<summary>
/// Main caller for worker
///</summary>
public PalmBlogWorker()
{
//import all the
entries
//query and grab only
the blogdb database entries
blogrecord=(PDStandardLib.PDRecordAdapter)query.OpenRecordDatabase("blogdb",
"PDDirect.PDRecordAdapter", PDDirectLib.EAccessModes.eRead | PDDirectLib.EAccessModes.eShowSecret |
PDDirectLib.EAccessModes.eWrite);
ImportEntries();
//import all the
settings
//query and grab only
the blogSettings database entries
blogSettings=(PDStandardLib.PDRecordAdapter)query.OpenRecordDatabase("blogSettings",
"PDDirect.PDRecordAdapter", PDDirectLib.EAccessModes.eRead | PDDirectLib.EAccessModes.eShowSecret |
PDDirectLib.EAccessModes.eWrite);
ImportSettings();
//upload all the items
UploadEntries();
}
publicvoid UploadEntries() {
//if we have any ditry
entries, meaning any entries that we havnt dealt with yet
if(this.entries.Count>0 && DirtyCount>0) {
//for each entry
for(int x=0;x<entries.Count;x++) {
string results = "";//worker.UploadEntry(entry,
settings);
PBlgConduit.DataTier.BlogEntry
entry = (PBlgConduit.DataTier.BlogEntry)entries[x];
//if it
is dirty
if(entry.EAttributes==PDStandardLib.ERecordAttributes.eDirty)
{
//find
the associated settings
for(int y=0;y<settings.Count;y++) {
PBlgConduit.DataTier.BlogSetting
setting = (PBlgConduit.DataTier.BlogSetting)settings[y];
if(setting.Name==entry.SettingsName) {
//upload the entry with the given settings
results = this.UploadEntry(entry, setting);
break;
}
}
//upload
it
//if
we have no errors during upload, delete the record
if(results!="")
{
//delete the entry from the db
DeleteRecord(entry.UniqueId,
"blogdb");
}
}
}
} else {
//Nothing new to
upload. do nothing right now
}
}
publicstring UploadEntry(PBlgConduit.DataTier.BlogEntry
entry, PBlgConduit.DataTier.BlogSetting settings) {
//can handle multiple
upload types, but right now we only
//want to use the
WebLogApi
//implement more if
you like
string ret="";
if(settings.SType=="WebLogApi")
{
ret = UploadMetaWebLogAPI(entry,
settings);
} else {
return "Nothing done";
}
return ret;
}
publicstring UploadMetaWebLogAPI(PBlgConduit.DataTier.BlogEntry
entry, PBlgConduit.DataTier.BlogSetting settings) {
//use JDSolutions
XmlRPC library to handle the WebLogApi uploads
//it can be downloaded
at: http://www.jondavis.net/JDSolutions/XmlRpc/readme.htm
//ive included the
full download with this zip
//create a new
xmlrpcclient
JDSolutions.XmlRpc.XmlRpcClient xrc = new JDSolutions.XmlRpc.XmlRpcClient();
//create xmlrpc
strings to hold our auth data
JDSolutions.XmlRpc.Elements.XmlRpcString blogid
= new JDSolutions.XmlRpc.Elements.XmlRpcString(entry.Subject);
JDSolutions.XmlRpc.Elements.XmlRpcString
username = new JDSolutions.XmlRpc.Elements.XmlRpcString(settings.Username);
JDSolutions.XmlRpc.Elements.XmlRpcString
password = new JDSolutions.XmlRpc.Elements.XmlRpcString(settings.Password);
JDSolutions.XmlRpc.Elements.XmlRpcBoolean
publish = new JDSolutions.XmlRpc.Elements.XmlRpcBoolean(true);
//Post is an object i
needed to create in order to handle the actual post data being sent
//It is located in the
MetaBlogApi.cs file included in this project
//load up the new post
Post post=new Post();
post.dateCreated=System.DateTime.Parse(entry.DDate);
post.description=entry.Entry;
post.title=entry.Subject;
//conver our post
class to an xmlrpcstruct
JDSolutions.XmlRpc.Elements.XmlRpcStruct ST2 =
(JDSolutions.XmlRpc.Elements.XmlRpcStruct)JDSolutions.XmlRpc.XmlRpcTools.ConvertElement(post, true);
//gather the
parameters into our object[]
object[] p = newobject[]{blogid,
username, password, ST2, publish};
string s="";
try {
//call the
method on the client, passing all needed data
s = (string)xrc.Call(settings.Domain,settings.Port,
settings.Path, "metaWeblog.newPost", p);
}catch(Exception
e){
s = "";
}
return s;
}
///<summary>
/// Delete the record based on uniqueid from the given db
///</summary>
///<param
name="UniqueId"></param>
///<param
name="database"></param>
///<returns></returns>
publicbool DeleteRecord(object UniqueId, string database) {
try {
if(database.ToLower()=="blogdb")
blogrecord.Remove(UniqueId);
else
blogSettings.Remove(UniqueId);
returntrue;
}catch(Exception
exc) {
string e =
exc.ToString();
returnfalse;
}
}
}
}
Take time to review the above code. Make sure you are familiar with the XML RPC
library I'm using and the fact that it will publish the data live to your server.
So now that we have this setup, make sure you have uploaded the PalmBlog application to your Palm and that you have made an entry or two (including valid settings) because we will need data to pull down out of the device in order to fully test this conduit. If you have a lengthy synchronization process you may consider deleting all of the data on your device (after a good backup of the ChartiR folder), and then just synchronizing the NSBRuntime and the Palm Blog application itself to the device. It makes things run much quicker.
At this point make sure your solution builds. If it doesn't, review my sample solution and try to determine what you missed and correct any errors. Once you get it to build, you are ready to debug the conduit. Place your device into the cradle and begin the synchronization process.
Once the HotSync Manager reaches the data that is marked with our Creator ID, it will execute the application that we specified in our conduit's settings, "Com Client", which should be the VS .NET IDE. Once VS .NET finishes loading, choose our PBlgConduit application. You will now be able to step into the application with full debugging available. Make sure it follows the path that you expect and that your data is being uploaded to the appropriate destination.
Once you have completed the conduit and are happy with its results, don't forget to change the "Com Client" in the Conduit Configuration tool; point it to the "PBlgConduit.exe" executable that should be in your "Palm\" folder.