Palm Programmer's Laboratory
Introduction to Conduit Development601/10
この章では、作成するコンジットの必要条件に最も適した開発アプローチはどれか、そして使用可能な CDK スイートのどれがそのアプローチをサポートするかを決定するための基礎知識を提供します。
この章には以下の節があります。
開始点の選択 ^TOP^
この節では、あなたが最初にする必要がある決定を示し、それから CDK の Sync Suite の機能比較を提供します。
最初の決定 ^TOP^
ここでは、どの Sync Suite があなたのニーズに最も適合するかを決定するときに考慮すべき問題を示します:
- 1. 低レベル インターフェイス、Generic Conduit Framework のどちらを使用するか選択します。表 10.2 が示すように、C/C++ Sync Suite は Generic Conduit Framework の実装を提供します; すべてのスイートは、C-ベース同期マネージャ API と直接的、間接的に対応するより低いレベルのインターフェイスを提供します。
あなたの設計要求に基づき、これらの開始点を比較するための助けとして 表 10.1 を使用してください。
表 10.1 Generic Conduit Framework と低レベル API の比較
| コンジット設計要求 | 低レベル同期マネージャ API [1] | Generic Conduit Framework [2] |
|---|---|---|
| 標準データ ソース(ACCESS, ODBC, CSV など)へのインターフェイス | 最適 | |
| MFC-ベース アプリケーション | 適 | |
| プラットフォームをまたがるソース コード | 最適 | |
| 素早い試作アプリケーション開発 | 適 | 適(コンジット ウィザード [3] を使用します) |
| データのアップロード/ダウンロード | 最適 | 適 |
| オンライン/オフライン トランザクション処理 | 最適 |
- [1]すべてのスイートで低レベル インターフェイスは使用可能です。C/C++ Sync Suite では、コンジットは同期マネージャに直接アクセスすることができます。COM Sync Suite では、コンジットは COM 同期モジュールを経由して間接的に同期マネージャにアクセスすることができます。
- [2]C/C++ Sync Suite で使用可能です。
- [3]C/C++ Sync Suite でのみ使用可能です。
- 2. スイートを選択します。表 10.2 は、CDK の同期スイートの高レベル比較を提供しています。どれがあなたの必要性に最も良く適合するかを決定するための助けとして、この表と各スイートに添付されている Companion ドキュメントを使用してください(「関連文書」 を参照してください)。
それぞれのスイートの主な利点は:- C/C++ Sync Suite はすべての機能のスーパーセットを提供するので、ある開発者はそれを使用してすべての要求を満たすことになります。
- COM Sync Suite は、あなたが COM を使用可能な言語 - Visual Basic のような手続き型言語を含む - のいずれかでコンジットを開発することを可能にします。COM Sync Suite はあなたのコンジットが基本的な C API のいくつかにアクセスするための単純な COM インターフェイスを提供します。
キーとなる機能の比較 ^TOP^
表 10.2 は各同期スイートのキーとなる機能を比較しています。この表は CDK 機能の包括的なリストではありません。各スイートについて更に学ぶには、「関連文書」 を参照してください。
表 10.2 は以下の機能を比較しています:
- サポートされるデスクトップ機能 は、各同期スイートの API 経由で使用可能な機能をリスト アップしています。言及されている他の機能は、デスクトップ アプリケーション、コンジットとハンドヘルド アプリケーション インストーラを開発するために重要です。
- サポートされる IDE と開発プラットフォーム は、PalmSource 社が各スイートでサポートする統合開発環境をリスト アップしています。
- サポートされる使用プラットフォーム は、PalmSource 社がコンジットの使用をサポートするプラットフォームをリスト アップしています。
| スイート | COM Sync Suite | C/C++ Sync Suite |
|---|---|---|
| サポートされるデスクトップ機能 | ||
| ユーザ データ(またはユーザ マネージャ)API - HotSync マネージャ ユーザとハンドヘルドの拡張カードについての情報にアクセスするためのもの | Yes | Yes |
| コンジット マネージャ - コンジットを HotSync マネージャに登録するインストーラのためのもの | Yes | Yes |
| 通知機能 DLL - デスクトップ アプリケーションに HotSync 実施が開始/停止したことを通知するためのもの | No | Yes |
| 通知機能インストール マネージャ - 通知機能を HotSync マネージャに登録するインストーラのためのもの | Yes | Yes |
| インストール コンジット マネージャ - インストール コンジットを HotSync マネージャに登録するインストーラのためのもの | Yes | Yes |
| インストール援助機能 - アプリケーションをハンドヘルドの主記憶装置にインストールする、または、ファイルを拡張カードにインストールするインストーラのためのもの | Yes | Yes |
| サポートされる IDE と開発プラットフォーム | ||
| Microsoft Visual Studio .NET (Visual C++ と Visual Basic) 2003 [4]: Windows 2000 と XP |
Yes | Yes |
| サポートされる使用プラットフォーム | ||
| Windows 98SE, ME, 2000, XP | Yes | Yes |
| その他 | ||
| プロジェクトと開始コードを自動的に生成するウィザード | No | Yes |
- [4]Visual Studio .NET 2002 と Visual Basic 6 が機能することは実証されてきました。しかし、公式にはサポートされません。
Generic Conduit Framework ^TOP^
Generic Conduit Framework を使ってコンジットを作成するということは、ある特定のクラスを引き出してきて、それらをあなたがハンドヘルドまたはデスクトップ コンピュータで使用したいデータ フォーマットに合わせてカスタマイズするということです。
図 10.1 は、Generic Conduit Framework の構造を描いています。これらのクラス名は C++ Generic Conduit クラス名です。他のスイートの Generic Conduit の構造を知りたければ、「関連文書」 で説明されている各スイートのためのドキュメントを参照してください。
図 10.1 Generic Conduit Framework
Generic Conduit Framework は以下のベース クラスから成り立ちます:
- カテゴリ クラスは、標準 Palm OS カテゴリ データにアクセスし、管理します。
- レコード クラスは、レコードをハンドヘルドでのフォーマットとデスクトップ コンピュータでのフォーマット間で変換します。
- 同期ロジック クラスは、以下のグループに分けられます:
- データベース マネージャ クラスは、ハンドヘルドまたはデスクトップ コンピュータからのレコードを保存、取得します。
- 同期機能クラスは、ハンドヘルドのデータベースからのレコードとアプリケーション情報ブロックの同期をとります。コアの同期ロジックはこれらのクラスの中に実装されます。
- ログ クラスは、診断情報を HotSync マネージャによって維持されるログ ファイルに書き出すために使用されます。
以下のステップは、Generic Conduit Framwork をベースにするコンジットをどのように実装し、それをどのようにあなたのレコード フォーマットに適合するようにカスタマイズするのかを要約しています。
- Step 1: あなたのレコード フォーマットを記述します。
- Step 2: あなたのレコードの保存と取得を実装します。
- Step 3: バックアップ データベースまたはアーカイブ データベースの保存と取得を実装します。
- Step 4: Generic CPalmRecord フォーマットとのレコードの変換を実装します。
- Step 5: AppInfo データの同期を実装します。
これらの基本ステップはすべてのスイートで同じです。しかし、厳密な実装方法はさまざまです。更なる情報は、各スイートのドキュメントを参照してください。
以下のステップは、HotSync マネージャが Generic Conduit Framework をベースにしたコンジットに制御を渡すときの一般的な同期実施の基本的な制御フローの概要です:
- HotSync マネージャはあなたの OpenConduit() 関数(他のスイートでは、あなたの同期処理を開始するメソッド)を呼び出します。呼び出された関数は、渡された同期プロパティを検証して同期のタイプ(高速同期または低速同期)を決定しなければなりません。
- あなたの OpenConduit() 関数は CSynchronizer オブジェクトをインスタンス化して以下のタスクを実行します:
- コンジットを登録します(C API-ベース コンジットでのみ)。
- 渡された同期プロパティを検証して同期のタイプを決定します。一般には高速同期か低速同期です。
- データベース マネージャ オブジェクト(ハンドヘルド、デスクトップ、アーカイブ、バックアップ)を作成します。
- 提供される同期ロジックを使ってカテゴリ情報の同期をとります。
- 高速同期、低速同期、ハンドヘルドがデスクトップを上書き、デスクトップがハンドヘルドを上書き、何もしないを使用してレコードの同期をとります。
- マネージャ オブジェクトを削除します。
- 結果を HotSync ログに書き込みます。
実行フローを追っていくために各スイートで提供されるサンプル コンジットを参照してください。
クラシック同期マネージャ API ^TOP^
この節では、一般的なコンジットから C/C++ Sync Suite のクラシック同期マネージャへの呼び出しフローについて説明します。これらの基本的なコンセプトはすべてのスイートで同じです。しかし、厳密な実装方法はさまざまです。更なる情報は、「関連文書」 で説明されている各スイートのドキュメントを参照してください。
クラシック同期マネージャへの呼び出しは以下のカテゴリに分類されます:
- コンジットの登録(C API-ベース コンジットでのみ)。
- データベースのオープン。
- レコードの読み込み、書き込み。
- レコード カテゴリに対する動作。
- データベースのクローズ。
- コンジットのアンインストール。
この節の中のコードの断片は、C/C++ Sync Suite で書かれたサンプル コンジットがどのようにクラシック同期マネージャ呼び出しを使用するのかを簡潔に示すのを手助けします。使用方法と主要なステップは他のスイートでも類似です。
コンジットの同期マネージャへの登録 ^TOP^
あなたは、SyncRegisterConduit() 関数を呼び出すことによって、あなたのコンジットを同期マネージャに登録しなければなりません同期マネージャはあなたのコンジットのハンドルを返してきて、あなたはそれをその後に続く呼び出しの中で使用します。あなたのコンジットが完了したとき、あなたは、SyncUnRegisterConduit() 関数を呼び出すことによって、コンジットを登録解除します。たいていのコンジットはこれらの呼び出しを自身の OpenConduit() 関数の中に作成します。
NOTE:同期マネージャへの登録と HotSync マネージャへの登録を混同しないように注意してください。あなたのコンジットは実行時に同期マネージャに登録されなければなりません。あなたはあなたのコンジットをインストール時に HotSync マネージャに登録しなければなりません。さもなければ、HotSync マネーははあなたのコンジットを実行しません(各スイートの Dompanion Document の「インストーラの作成」を参照してください)。
データベースのオープン ^TOP^
あなたは SyncCreateDB() 関数を使ってハンドヘルド上に新しいオープンされたデータベースを作成することができます。また、SyncOpenDB() 関数を使って存在するハンドヘルド データベースをオープンすることができます。コード 10.1 は、見本の OpenDataBase() 関数からのコードを示しています。この関数は SyncCreateDB() 関数と SyncOpenDB() 関数の両方を呼び出しています。
BOOL OpenDataBase( CLogMgr& theLog, const char * pName, DWORD dbCreator,
DWORD dbType, BYTE& hDataBase, WORD& dbRecordCount,
BOOL clearDB )
{
long retVal;
// Open the remote database.
retVal = SyncOpenDB( pName, 0, hDataBase );
if ( retVal )
{
if ( retVal == SYNCERR_FILE_NOT_FOUND )
{
TRACE( "SyncOpenDB( %s ) failed [ %lX ], attempting to create DB.\n",
pName, retVal );
// The database does not exist, so create it.
CDbCreateDB createInfo;
memset( &createInfo, 0, sizeof( CDbCreateDB ) ) ;
createInfo.m_Creator = dbCreator;
createInfo.m_Type = dbType;
createInfo.m_Flags = eRecord; // eRecord;
createInfo.m_CardNo = 0; // Target card number
createInfo.m_Version = 0;
ASSERT( strlen( pName ) <= DB_NAMELEN );
strcpy( createInfo.m_Name, pName );
retVal = SyncCreateDB( createInfo );
if ( retVal )
{
CString theDBName( pName );
// fatal error
theLog.Message( IDS_CANT_CREATE_DB, theDBName, TRUE );
TRACE( "SyncCreateDB( %s ) failed [ %lX ].\n", pName, retVal );
return FALSE;
}
else
{
// Database is open with handle createInfo.m_FileHandle.
hDataBase = createInfo.m_FileHandle;
TRACE( "SyncCreateDB( %s ) succeeded.\n", pName );
}
}
else
{
CString theDBName( pName );
// fatal error
theLog.Message( IDS_CANT_OPEN_DB, theDBName, TRUE );
TRACE( "SyncOpenDB( %s ) failed [ %lX ].\n", pName, retVal );
return FALSE;
}
}
// If clearDB, then clear all the existing records in the database.
if ( clearDB )
{
retVal = SyncPurgeAllRecs( hDataBase );
if ( retVal )
{
CString theDBName( pName );
// fatal error
theLog.Message( IDS_CANT_PURGE_DB, theDBName, TRUE );
TRACE( "SyncPurgeAllRecs( %s ) failed [ %lX ].\n", pName, retVal );
return FALSE;
}
else
{
TRACE( "SyncPurgeAllRecs( %s ) succeeded.\n", pName );
}
}
// Determine how many records are in the remote database.
retVal = SyncGetDBRecordCount( hDataBase, dbRecordCount );
if ( retVal )
{
if ( hDataBase ) SyncCloseDB( hDataBase );
CString theDBName( pName );
// fatal error
theLog.Message( IDS_CANT_GET_DBRECCOUNT, theDBName, TRUE );
TRACE( "SyncGetDBRecordCount( %s ) failed [ %lX ].\n", pName, retVal );
return FALSE;
}
return TRUE;
} // OpenDataBase
データベースへの動作 ^TOP^
各データベースはいかの特性を持つレコードの集まりです:
- データベースの中の各レコードは、そのデータベースの中で固有の ID を持ちます。
- データベースの中の各レコードは、そのレコードがプライベートである、削除された、変更された、アーカイブされた、Busy 状態であるというマークを付けられているかどうかを示すフラグを持ちます。
- データベースの中の各レコードは、カテゴリに属します。
- データベースはデータベースについてのグローバル情報を保存するアプリケーション情報ブロックを保持します。このブロックは通常、データベースの中のレコードのカテゴリ名を保存するために使用されます。
- データベースはデータベースの中のレコードの順序情報を保存するソート情報ブロックを保持します。このブロックはアプリケーションによって使用されます; Palm OS はそれを使用しません。
表 10.3 の中で示されているように同期マネージャはハンドヘルド上のデータベースからレコードとリソースを取得するためのいくつかの関数を提供します。これらの関数はそれぞれレコードまたはリソースを CRawRecordInfo クラスのオブジェクトに読み込みます。
あなたは、各関数を呼び出す前に、ある特定の未加工レコード オブジェクト フィールドに情報を詰めなければなりません。関数はハンドヘルドからレコードを取得し、他のフィールドにレコード情報を詰め込みます。各関数のために要求されるフィールドは、各関数のためのドキュメントの中で説明されます。
表 10.3 ハンドヘルド データベースからレコードを読み込むための関数
| 関数 | 説明 |
|---|---|
| SyncReadNextModifiedRec | ハンドヘルド上のオープンされたレコード データベースから次の変更された、アーカイブされた、削除されたレコードを取得する反復関数。 |
| SyncReadNextModifiedRecInCategory | ハンドヘルド上のオープンされたデータベースから、あるカテゴリの中にある変更されたレコード - 削除されたレコードとアーカイブされたレコードを含む - を取得する反復関数。 |
| SyncReadNextRecInCategory | ハンドヘルド上のオープンされたデータベースから、あるカテゴリの中にあるすべてのレコード - 削除されたレコード、アーカイブされたレコード、変更されたレコードを含む - を取得する反復関数。 |
| SyncReadRecordById | ハンドヘルド上のオープンされたデータベースから、ID によって、レコードを取得します。 |
| SyncReadRecordByIndex | ハンドヘルド上のオープンされたデータベースから、インデックスによって、レコードを取得します。 |
| SyncReadResRecordByIndex | ハンドヘルド上のオープンされたリソース データベースから、インデックスによって、リソース レコードを取得します。 |
コード 10.2 は、インデックスによってレコードを読み込むために SyncReadRecordByIndex() を呼び出す見本の ImportPrefs() メソッドを示しています。
コード 10.2 ハンドヘルド データベースからのレコードの読み込み
BOOL CNetPrefs::ImportPrefs( BYTE hHHDataBase, WORD recIndex )
{
BYTE readBuf[ READ_BUF_SIZE ];
BOOL importSuccess = TRUE;
fHDataBase = hHHDataBase;
// Read from the HH database at index recIndex.
CRawRecordInfo rrInfo;
::memset( &rrInfo, 0, sizeof( rrInfo ) );
rrInfo.m_FileHandle = hHHDataBase;
rrInfo.m_RecIndex = recIndex;
rrInfo.m_pBytes = (BYTE *) &readBuf[ 0 ];
rrInfo.m_RecSize = READ_BUF_SIZE;
rrInfo.m_TotalBytes = READ_BUF_SIZE;
long retVal = ::SyncReadRecordByIndex( rrInfo );
if ( retVal )
{
importSuccess = FALSE;
fPLogMgr->Message( IDS_CANT_READ_DB );
}
else
{
// The read succeeded, so copy the data onto fCompressedBuffer.
fHHDBRecID = rrInfo.m_RecId;
ASSERT( rrInfo.m_RecSize != 0 );
if ( fCompressedBuffer != NULL ) delete fCompressedBuffer;
fCompressedBuffer = new BYTE[ rrInfo.m_RecSize ];
fCompressedLength = rrInfo.m_RecSize;
::memcpy( (void *) fCompressedBuffer, (void *) rrInfo.m_pBytes,
fCompressedLength );
}
return importSuccess;
} // CNetPrefs::ImportPrefs
Sync マネージャはまた、ハンドヘルド上のデータベースの始めから終わりまでを反復して処理するのに使用できる関数も提供します。データベースの中のレコードを始めから終わりまで反復処理するには、まず SyncResetRecordIndex 関数を呼び出すことによってデータベースの現在のインデックスをリセットし、それから以下の取得するための関数の 1 つを繰り返し呼び出します:
- SyncReadNextRecInCategory はある特定のレコード カテゴリの中にある次のレコードを取得します。
- SyncReadNextModifiedRec は(最後の同期の後に)変更された次のレコードを取得します。
- SyncReadNextModifiedRecInCategory はある特定のレコード カテゴリーの中にある変更された次のレコードを取得します。
Sync マネージャは、データベースのオープン時に、データベースのための現在のデータベース反復インデックスをリセットします。その後に続く反復のためのインデックスをリセットするには、SyncResetRecordIndex 関数を呼び出さなくてはなりません。
コード 10.3 はハンドヘルド データベースの中の変更された各レコードを取得するために SyncReadNextModifiedRec() を使用するコードを示しています。
コード 10.3 ハンドヘルド レコードを読み込むための反復関数の使用
CRawRecordInfo rawRecord;
retval = AllocateRawRecordMemory(rawRecord, EXPENSE_RAW_REC_MEM);
// Read in each modified remote record one at a time.
while (!err && !retval)
{
if (!(err = SyncReadNextModifiedRec(rawRecord)))
{
// Convert from raw record format to CExpenseRecord.
if (!m_pDTConvert->ConvertFromRemote(remRecord, rawRecord))
{
// Synchronize the record obtained from the handheld.
retval = SynchronizeRecord(remRecord, locRecord,
backRecord);
}
else
retval = CONDERR_CONVERT_FROM_REMOTE_REC;
}
memset(rawRecord.m_pBytes, 0, rawRecord.m_TotalBytes);
}
if (err && err != SYNCERR_FILE_NOT_FOUND)
LogBadReadRecord(err);
Sync マネージャは、データベースの始めから終わりまでの反復処理とデータベースの変更を同時にインタリーブ(訳者注: 多重プログラミング技法)することをサポートしません。これは、データベースの始めから終わりまでの反復処理している間にデータベースを変更しないようにロジックを組み立てる必要があることを意味します。Palm OS のバージョン 2.0 から、データベースの始めから終わりまでの反復処理中に、安全にデータベースからレコードを削除できるようになりました(SyncPurgeAllRecsInCategory 関数を除きます)。
Sync マネージャは、ハンドヘルド上のデータベースにレコードまたはリソースを書き込むための関数をいくつか提供します。これらの関数はそれぞれ、CRawRecordInfo クラスのあるオブジェクトの中にあなたが保存したレコード情報を送り出します。これらの関数を使って、存在するレコードを変更するまたは新しいレコードをデータベースに追加することができます。
ハンドヘルド データベースにレコードを書き込むために、未加工レコード オブジェクトのある特定のフィールドに情報を詰め、それから同期マネージャのレコード書き込み関数の 1 つを呼び出します。関数はレコードをハンドヘルド上のデータベースに送ります。SyncWriteRec 関数を使ってレコードをハンドヘルド データベースに書き込むことができ、SyncWriteResourceRec 関数を使ってリソースをハンドヘルド データベースに書き込むことができます。コード 10.4 は、SyncWriteRec() 関数を呼び出す見本の ExportPrefs() メソッドを示しています。
BOOL CNetPrefs::ExportPrefs( BYTE hHHDataBase )
{
BOOL exportSuccess = TRUE;
fHDataBase = hHHDataBase;
if ( fCompressedLength != 0 )
{
ASSERT( fCompressedBuffer != NULL );
// write to the HH database.
CRawRecordInfo rrInfo;
::memset( &rrInfo, 0, sizeof( rrInfo ) );
rrInfo.m_FileHandle = hHHDataBase;
rrInfo.m_RecId = fHHDBRecID;
rrInfo.m_pBytes = fCompressedBuffer;
rrInfo.m_RecSize = fCompressedLength;
long retVal = ::SyncWriteRec( rrInfo );
if ( retVal )
{
exportSuccess = FALSE;
fPLogMgr->Message( IDS_CANT_WRITE_DB );
}
}
return exportSuccess;
} // CNetPrefs::ExportPrefs
ハンドヘルド データベースの中のレコードの削除 ^TOP^
データベースからレコードまたはリソースを削除するために同期マネージャ関数を使用するとき、レコードまたはリソースはデータベースから直ちに削除され、取り外されます(原文: deleted and removed)。これは、レコードを削除するハンドヘルド上のアプリケーション呼び出し - 削除(delete)するときはレコードにマークを付けるだけで、取り外す(remove)ときはただちに取り外します - とは対照的です。
- NOTE
- Delete と Purge で始まる同期マネージャ関数は両方ともただちにオブジェクトを削除します。
Sync マネージャはデータベースからレコードまたはリソースを削除するためにいくつかの関数を提供します。これらの関数を使用するために、データを CRawRecordInfo クラスのあるオブジェクトの指定されたフィールドに代入します。それから 表 10.4 の中で説明されている関数の 1 つを呼び出します。
| 関数 | 説明 |
|---|---|
| SyncDeleteRec | レコード データベースからレコードを削除します。 |
| SyncDeleteResourceRec | リソース データベースから指定されたリソースを削除します。 |
表 10.5 の中で説明されている関数の 1 つを呼び出すことによって複数のレコードを削除することができます。
| 関数 | 説明 |
|---|---|
| SyncPurgeDeleteRecs | レコード データベースの中の先立って削除されたまたはアーカイブされたというマークを付けられているすべてのレコードを削除します。 |
| SyncPurgeAllRecs | ハンドヘルド上のレコード データベースの中のすべてのレコードを削除します。 |
| SyncPurgeAllRecsInCategory | ハンドヘルド上のレコード データベースから指定されたカテゴリの中にあるすべてのレコードを削除します。 |
| SyncDeleteAllResourceRec | リソース データベースからすべてのリソースを削除します。 |
レコードのクリーン アップ ^TOP^
表 10.6 の中で示されている関数の 1 つを使って、同期操作の終了時に、レコードの状態フラグをクリアすることができます。
| 関数 | 説明 |
|---|---|
| SyncResetSyncFlags | ハンドヘルド上のオープンされているレコード データベースの中のすべてのレコードの変更されたフラグをリセットします。オープンされているレコード データベースまたはリソース データベースのためのバックアップ日付をリセットします。 |
データベースのクローズ ^TOP^
ハンドヘルド データベースとの同期が終了した後、コード 10.1 の中で示されているように、それをクローズするために SyncCloseDB 関数を呼び出さなくてはなりません。C API-ベース コンジットと Java-ベース コンジットはいつでもただ 1 つのデータベースをオープンすることを許可します。COM-ベース コンジットは 1 度に 1 つ以上のデータベースをオープンすることができます。
- 重要
- C API-ベース コンジットまたは Java-ベース コンジットの中でデータベースをオープンする場合、コンジットを終了する前にそれをクローズしなくてはなりません; さもないと、他のコンジットはそれらのデータベースをオープンすることができなくなります。
