asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Programming for the Palm Part 2 - The Synchronization Process
By Robert Chartier
Rating: 4.2 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    This article will show how to establish a connection with the Palm synchronization process in order to upload the Palm Blog application data to various upload destinations.

    If you recall in the first part, I indicated that we will have three main methods for uploading data, WebLogApi, Ftp, and Http. For simplicity sake, I will only cover the WebLogApi method in this article. You can easily adapt my solution to suit your needs for the other two methods, or even add your own methods to the project.

    The synchronization process is fairly simple to understand. You have the Palm itself, which is connected to your PC or Laptop via a cable. On the computer you have the HotSync Manager running (it should be located in your system tray), and you have "Conduits", which are essentially plug-ins for the Manager which allow us to control the synchronization process for our specific application.

    In part one, I explained the "Creator ID", a unique ID representing our application and databases. During the synchronization process the HotSync Manager needs to determine which conduit it needs to call in order to handle the incoming data. This decision is based on the Creator ID. Since we used the Creator ID of "PBlg" in our application, we know that whenever the HotSync Manager encounters that specific ID, it will attempt to call our conduit.

    When creating any conduit we need to download and install the Conduit Developer Kit. This can be downloaded from the Palm Site (http://www.palmos.com/dev/tech/conduits/). I have downloaded and will be using CDK 4.0.3 for Windows. Also download and install the Palm COM Installer Update. It ships a few bug fixes and such for COM Conduits, which we will be creating. You may want to take time to browse the documentation that ships with the CDK, there is a lot of very useful information available.

    Step 1: Setting up VS .NET

    In Visual Studio .NET (VS .NET), create a new C# Windows application named "PBlgConduit". I decided to use a Windows application because we can use the default Form to show the status of the data synchronization and uploading. You can optionally add a Progress Bar and/or Label to the default form to programmatically indicate the status to the user. In order to keep this article simple and code sections short, I will not update any UI elements.

    In order for our Conduit to communicate with the HotSync Manager, we need to import two libraries from the CDK. These are both located at %install%\CDK403\Common\Bin where %install% is the path you chose to install the CDK to. For example, mine is located at c:\CDK403\Common\Bin\. Add a reference to both "ComDirect.dll" and "ComStandard.dll" in your project. VS .NET should take care of the COM Interop for you.

    The last thing you need to do is make sure that this project builds to the "Palm\" (the installation folder for all of the Palm files) folder. This is needed for debugging correctly.

    Now that we have the bare requirements for the VS .NET setup, we will create more specific classes and projects to handle our data, including a special project will help import Palm databases into our managed environment.

    Step 2: Creating the Data Tier and Getting Ready To Upload

    In this step we will create a few classes (essentially our Data Tier) to hold the UDTs that we created in the first article; we will also create a reusable project that will allow us to easily import Palm databases into this data tier, and finally we will hook into an existing project to handle the WebLogAPI details.

    In the default project within VS .NET create a new class named "DataTier.cs". We will use this to hold all three of the data tier classes. Figure 1 below is the final output of all three of these classes. Notice that each class has a few properties associated with the Palm database itself. It is important to note the actual order of the properties in each class because we rely on these when we move on to importing the data from the synchronization process.

    Figure 1 List of BlogEntry, BlogSetting, sTypes Types

    using System;

     

    namespace PBlgConduit.DataTier {

           ///<summary>

           /// Used to be the managed representation of the BlogEntry UDT from the Palm Database

           /// Type BlogEntry

           ///           subject as String 'subject of the entry

           ///           dDate as String  'date of the entry

           ///           entry as String  'the actuall full text entry

           ///           settingsName as String 'matching settings name

           ///           End Type

           ///</summary>

           publicclass BlogEntry {

                  /*app specific attributes*/

                  string key;

                  string subject;

                  string dDate;

                  string entry;

                  string settingsName;

                  publicstring Key{get{return key;}set{key=value;}}

                  publicstring Subject{get{return subject;}set{subject=value;}}

                  publicstring DDate{get{return dDate;}set{dDate=value;}}

                  publicstring Entry{get{return entry;}set{entry=value;}}

                  publicstring SettingsName{get{return settingsName;}set{settingsName=value;}}

                  /*Palm DB Specific attributes for each record*/

                  private PDStandardLib.ERecordAttributes eAttributes;

                  privateint nIndex;

                  privateint nCategory;

                  privateobject vUniqueId;

                  privatebyte[] raw;

                  publicbyte[] Raw{get{return raw;}set{raw=value;}}

                  public PDStandardLib.ERecordAttributes EAttributes{get{return eAttributes;}set{eAttributes=value;}}

                  publicint Index{get{return nIndex;}set{nIndex=value;}}

                  publicint Category{get{return nCategory;}set{nCategory=value;}}

                  publicobject UniqueId{get{return vUniqueId;}set{vUniqueId=value;}}

     

                  public BlogEntry() {

                  }

                  public BlogEntry(string Subject, string DDate, string Entry, string SettingsName) {

                         subject=Subject;

                         dDate=DDate;

                         entry=Entry;

                         settingsName=SettingsName;

                  }

           }

           ///<summary>

           /// Used to represent the managed version of the BlogSetting UDT

           /// Type BlogSetting

           ///           Name as String  'name of the setting, key

           ///           isDefault as Integer 'if this is the default setting

           ///           sType as Integer    'type of setting it is

           ///           username as String 'username to use during auth

           ///           password as String 'password to use during auth

           ///           port as Integer 'what port is the remote service running on

           ///           domain as String 'what domain/ip is the remote server

           ///           path as String 'file path to the save location

           ///           filename as String 'ftp file name,

           ///           'http variable post name For xml upload

           ///           'or url end point

           ///           End Type

           ///</summary>

           publicclass BlogSetting {

                  string key;

                  string name;

                  int isDefault;

                  string sType;

                  string username;

                  string password;

                  int port;

                  string domain;

                  string path;

                  string filename;

                  publicstring Key{get{return key;}set{key=value;}}

                  publicstring Name{get{return name;}set{name=value;}}

                  publicint IsDefault{get{return isDefault;}set{isDefault=value;}}

                  publicstring SType{get{return sType;}set{sType=value;}}

                  publicstring Username{get{return username;}set{username=value;}}

                  publicstring Password{get{return password;}set{password=value;}}

                  publicint Port{get{return port;}set{port=value;}}

                  publicstring Domain{get{return domain;}set{domain=value;}}

                  publicstring Path{get{return path;}set{path=value;}}

                  publicstring Filename{get{return filename;}set{filename=value;}}

     

                  /*Palm DB Specific attributes for each record*/

                  private PDStandardLib.ERecordAttributes eAttributes;

                  privateint nIndex;

                  privateint nCategory;

                  privateobject vUniqueId;

                  privatebyte[] raw;

                  publicbyte[] Raw{get{return raw;}set{raw=value;}}

                  public PDStandardLib.ERecordAttributes EAttributes{get{return eAttributes;}set{eAttributes=value;}}

                  publicint Index{get{return nIndex;}set{nIndex=value;}}

                  publicint Category{get{return nCategory;}set{nCategory=value;}}

                  publicobject UniqueId{get{return vUniqueId;}set{vUniqueId=value;}}

     

                  public BlogSetting() {

                  }

     

                 

                  public BlogSetting(string Name,int IsDefault,string SType,string Username,string Password,int Port,string Domain,string Path,string Filename) {

                         name=Name;

                         isDefault=IsDefault;

                         sType=SType;

                         username=Username;

                         password=Password;

                         port=Port;

                         domain=Domain;

                         path=Path;

                         filename=Filename;

                  }

           }

    }

    Next we will add a new C# Class Library project to the solution named "PalmDBImporter", which will contain a simple class that will allow us to easily import the Palm databases into our data tier classes above. The sole reason why I want to create this as a new project is so that we can reuse this project in future solutions, since it will be a generic Palm database importer. Make sure you add a project reference in the PBlgConduit solution to the new project. Figure 2 below shows the code listing for this class.

    Figure 2: Palm Database Importer

    using System;

     

    namespace PalmDBImporter {

           ///<summary>

           /// Class used to import the byte[] from the Palm Synch process and bind it to a Class, based on NULL \0 values and reflection

           ///</summary>

           publicclass Importer {

                  ///<summary>

                  /// called with an instantiated class and the byte[] of data.  It will attempt to move the data into the class based on its public properties.

                  ///</summary>

                  ///<param name="Class"></param>

                  ///<param name="data"></param>

                  ///<returns></returns>

                  publicstaticbool BindToClass(object Class, byte[] data) {

                         try {

                               //get a list of all the available properties

                               System.Reflection.PropertyInfo[] props = Class.GetType().GetProperties();

                               int pCounter=0;

                               int bCounter=0;

                               //create a temp buffer to hold current data

                               byte[] propData = newbyte[data.Length];

                               //if we actually have any public properties

                               if(props!=null && props.Length>0) {

                                      //grab the current property

                                      System.Reflection.PropertyInfo currentProp = props[pCounter];

                                      //iterate over the entire length of the data

                                      for(int x=0;x<data.Length;x++) {

                                             //if it is a null value, then we hit a field terminator

                                             if(data[x]==0) {

                                                    //grab the bytes from the data into our temp buffer

                                                    byte[] newData = newbyte[bCounter];

                                                    newData = GetBytes(propData, 0, bCounter);

                                                    //convert the bytes to a string

                                                    string val = System.Text.ASCIIEncoding.ASCII.GetString(newData);

                                                    //set the value into the property

                                                    try {

                                                           //we are only gonna handle 3 data types right now

                                                           //boolean, string and numbers

                                                           string propType=currentProp.PropertyType.FullName.ToLower().Replace("system.","");

                                                           object[] propValue=GetValue(propType, val);

                                                           Class.GetType().InvokeMember(currentProp.Name, System.Reflection.BindingFlags.SetProperty, null, Class, propValue);

                                                    }catch(Exception exc) {

                                                           string f = exc.ToString();

                                                           //do nothing

                                                    }

                                                    pCounter++;

                                                    //if we have any more properties, lets continue

                                                    if(pCounter<props.Length) {

                                                           currentProp = props[pCounter];

                                                           propData = newbyte[data.Length];

                                                           bCounter=0;

                                                    } else {

                                                           break;

                                                    }

                                             } else {

                                                    //shove the current value into the temp buffer

                                                    propData[bCounter]=data[x];                                  

                                                    bCounter++;

                                             }

                                      }

                               }

                               returntrue;

                         }catch(Exception e) {

                               returnfalse;

                         }

                  }

                  ///<summary>

                  /// Fix up the string to match HTML output for Weblogs

                  ///</summary>

                  ///<param name="str"></param>

                  ///<returns></returns>

                  publicstaticstring FixString(string str) {

                         str = str.Replace("\r\n", "<br />");

                         str = str.Replace("\r", "<br />");

                         str = str.Replace("\n", "<br />");

                         return str;

                  }

                  publicstaticobject[] GetValue(string propType, string val) {

                         object[] propValue=newobject[]{null};

                         try {

                               #region switch block for all data types

                               switch(propType) {

                                      case "boolean":

                                      case "bool":

                                             propValue[0] = System.Boolean.Parse(val);

                                             break;

                                      case "float":

                                             propValue[0] = float.Parse(val);

                                             break;

                                      case "decimal":

                                             propValue[0] = decimal.Parse(val);

                                             break;

                                      case "double":

                                             propValue[0] = double.Parse(val);

                                             break;

                                      case "int16":

                                             propValue[0] = int.Parse(val);

                                             break;

                                      case "int32":

                                             propValue[0] = int.Parse(val);

                                             break;

                                      case "int64":

                                             propValue[0] = long.Parse(val);

                                             break;

                                      case "single":

                                             propValue[0] = short.Parse(val);

                                             break;

                                      case "long":

                                             propValue[0] = long.Parse(val);

                                             break;

                                      case "short":

                                             propValue[0] = short.Parse(val);

                                             break;

                                      case "uint":

                                             propValue[0] = uint.Parse(val);

                                             break;

                                      case "uint16":

                                             propValue[0] = uint.Parse(val);

                                             break;

                                      case "uint32":

                                             propValue[0] = ulong.Parse(val);

                                             break;

                                      case "uint64":

                                             propValue[0] = ulong.Parse(val);

                                             break;

                                      case "ulong":

                                             propValue[0] = ulong.Parse(val);

                                             break;

                                      case "ushort":

                                             propValue[0] = ushort.Parse(val);

                                             break;

                                      default:

                                             propValue[0]=FixString(val);

                                             break;

                               }

                               #endregion

                         }catch{}

                         return propValue;

                  }

                  ///<summary>

                  /// used to get a range of bytes out of an existing byte[] block

                  ///</summary>

                  ///<param name="data"></param>

                  ///<param name="start"></param>

                  ///<param name="count"></param>

                  ///<returns></returns>

                  publicstaticbyte[] GetBytes(byte[] data, int start, int count) {

                         byte[] b = newbyte[count];

                         int ctr=0;

                         //copy over the bytes

                         for(int x=start;x<count;x++) {

                               b[ctr]=data[x];

                               ctr++;

                         }

                         return b;

                  }

           }

    }

    Essentially what we are dong in this block of code is looping over all of the data coming back for each record. In the example of a "blogdb" record, it is an "Entry" UDT that is being stored. The byte[] for each entry is fixed, thus we can rely on the null terminated string values to represent each field in each record. When we reach the null value, we know that the field is finished. We take that value and plug it into the current property.

    Notice that this does force the properties in our class to match, in order, the properties in the UDT. As long as this is done, database importing should go smoothly.

    Take time to review the code and pay close attention to the comments. If you are unsure about reflection you can read up on it in my past article on reflection at http://www.15seconds.com/issue/020618.htm.

    We now have a way to represent the data coming from the Palm device and a way to transform the native data (byte[]) into our fully managed code. In the next step I will show you how to register our conduit with the Conduit Configuration tool included in the CDK and how to test out our conduit during the synchronization process.

    Step 3: Registering our Conduit & Testing the Conduit

    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