Palm Programmer's Laboratory
Palm OS Programmer's Companion Volume II/2-2
2-2 Exchange ライブラリ コンポーネント
Exchange ライブラリ API
Palm OS の Exchange ライブラリ? APIは、すべての Exchange ライブラリが実装しているはずの関数の最小のセットを指定します。これらの関数は3つの大きな分類に分けることができます。すべての共有ライブラリに含まれるはずの関数(標準共有ライブラリ関数)、接続を確立しデータの送受信を行なう関数(データ送受信関数) 、色々なサポート関数(サポート関数) 、です。
標準共有ライブラリ関数
Palm OS 共有ライブラリはどれでも、開く(open)・閉じる(close)・眠る(sleep)・起こす(wake)の関数を実装しているはずです。
データ送受信関数
これらの関数は、接続の確立とデータの送受信を行ないます。
- ExgLibAccept?
- ExgLibConnect?
- ExgLibDisconnect?
- ExgLibGet?
- ExgLibPut?
- ExgLibReceive?
- ExgLibRequest?
- ExgLibSend?
これらの関数は、それぞれが Exchange マネージャ関数に直接相当するものであることに注意して下さい。多くの場合、Exchange マネージャは相当する Exchange ライブラリ関数を単にコールするだけです。
サポート関数
このカテゴリは、あなたの Exchange ライブラリについての情報を提供する関数と、イベントをハンドルする関数と、で構成されています。
これらの3つのカテゴリにおける各関数は Exchange ライブラリごとに存在しますが、Exchange ライブラリの特定の要求によっては、いくつかの関数は単に errNone または exgErrNotSupported を返します。
どの共有ライブラリでも、Exchange ライブラリのディスパッチテーブルに現れる関数の順序によって、ライブラリ内の関数を特定することができます。この順序は ExgLib.h で定義されています。それはディスパッチテーブルにおける関数の位置であり名前ではないので、与えられたExchange ライブラリで使われる実際の関数名は、ExgLib.h で定義されたものと異なるかもしれません。実際に、ホスト転送ライブラリが HostTransferLibPut、HostTransferLibSend、HostTransferLibDisconnect 等の関数で行なっているように、あなたの共有ライブラリでもユニークな関数名を使いたくなるかもしれません。あなたの Exchange ライブラリで特定する関数名を使用することにより、あなたの関数を Mac シミュレータにリンクしてデバッグすることができます。もしあなたの関数のために ExgLib.h で定義された関数名を使うなら、シミュレータがそれらの名前をあなたの関数からコールされる stub 関数のために使用するので、リンクエラーになるでしょう。
上記の関数だけでなく、exgLibTrapLastの後に、ライブラリ特有な追加の関数がライブラリのディスパッチテーブルに現れるはずです。
ディスパッチテーブル
ディスパッチテーブルは Palm OS が Exchange ライブラリの関数を探すために使用する地図です。リンク時に、Exchange ライブラリ関数への参照は SYS_TRAP マクロによるシステムトラップに解決されます。ランタイムでは、Exchange ライブラリ関数がコールされると、トラップが発生し、トラップがライブラリのディスパッチテーブルから関数を探し、Exchange ライブラリのコードリソースにおける関数のオフセットを計算します。ます。実行される関数によって、関数のアドレスへのJMP インストラクションがなされます。
- 注
- Exchange ライブラリ用ディスパッチテーブルの構造は、共有ライブラリのそれと同じです。Exchange ライブラリは共有ライブラリが行なうべきことすべてに加えて、Exchange マネージャを登録すること、を行なうべきです。これは、ExgPutのような Exchange マネージャ API によって、サービスへのアクセスをアプリケーションに与えます。
Listing 2.1  HostTransferDispatch.c
...
void *PrvHostTransferDispatchTable(void);
...
extern Err PrvInstallHostTransferDispatcher(UInt16 refNum, SysLibTblEntryType 
*entryP);
...
Err __Startup__(UInt16 refNum, SysLibTblEntryType *entryP)
{
   return PrvInstallHostTransferDispatcher(refNum, entryP);
}
...
asm void *PrvHostTransferDispatchTable(void)
{
   LEA  @Table, A0            // table ptr
   RTS                                 // exit with it
 
@Table:
   DC.W  @Name
   DC.W  (kOffset)       // Open
   DC.W  (kOffset+(1*4))   // Close
   DC.W  (kOffset+(2*4))   // Sleep
   DC.W  (kOffset+(3*4))   // Wake
   // Start of the exchange libary
   DC.W  (kOffset+(4*4)) // HostTransferLibHandleEvent
...
   DC.W  (kOffset+(12*4))  // HostTransferLibControl
   DC.W  (kOffset+(13*4))  // HostTransferLibRequest
 
@GotoOpen:
   JMP   HostTransferLibOpen
@GotoClose:
   JMP   HostTransferLibClose
@GotoSleep:
   JMP   HostTransferLibSleep
@GotoWake:
   JMP   HostTransferLibWake
 
@GotoHandleEvent:
   JMP   HostTransferLibHandleEvent
...
@GotoOption:
   JMP   HostTransferLibControl
@GotoCheck:
   JMP   HostTransferLibRequest
 
@Name:
   DC.B  HostTransferName
}
...
ディスパッチテーブルの最後のエントリは、Exchange ライブラリの名前です。これは Exchange ライブラリに含まれるデータベースの名前と一致しなければいけません。また、シミュレータでは "-crid" で終わってなければいけません。ただし crid はクリエータIDです。例えば、ホスト転送ライブラリは 「HostTransfer Library-HXfr」 を使用しています。
ディスパッチテーブル自身を有効なままにしておくために、コードセグメントとその中のルーチンアドレスはロックされている必要があります。ライブラリのデータベースは、削除できないように自動的に保護されます。
- 注
- システムの共有ライブラリテーブルは、ロードされた各ライブラリのライブラリグローバルなスロットを持っています。もし実際はグローバルにアロケートされていなくても、スタートアップルーチンはこのフィールドをとにかくゼロにすべきです。ライブラリの Open() エントリポイントによってライブラリが開かれたとき、いくつかのライブラリではopenCountで小さい構造体をアロケートし、後でより大きなアロケーションを開放します。
Err __Startup__(UInt16 refNum, SysLibTblEntryType *entryP)
通常、__Startup__ は 相当する MyLib.c ファイル内の実際のセットアップルーチンを呼ぶ MyLibDispatch.c ファイル内の一行コールから成ります。例えば、OS SDK で提供される下記の HostTransferDispatch.c サンプルファイルは、HostTransfer ディスパッチテーブルをインストールするのに使用されます。
extern Err PrvInstallHostTransferDispatcher(UInt16 refNum,
SysLibTblEntryType *entryP);
...
Err __Startup__(UInt16 refNum, SysLibTblEntryType *entryP)
{
	return PrvInstallHostTransferDispatcher(refNum, entryP);
}
...
__Startup__ は、SysLibLoad のコール時にディスパッチテーブルをセットアップするためにコールされます。例えば下記のようにです。
SysLibInstall(PrvInstallHostTransferDispatcher, &refNum);
Listing 2.2 は HostTransfer ディスパッチテーブルインストーラ関数をどのように実装するか、を示しています。この関数は、OS SDK のサンプルファイル(HostTransferLib.c)にあります。例えば下記のようにです。
entryP->dispatchTblP = (MemPtr *)PrvHostTransferDispatchTable();
ディスパッチインストーラルーチンは一般的に初期化を行います。
Listing 2.2  ホスト転送ディスパッチテーブルインストーラ関数
Err PrvInstallHostTransferDispatcher(UInt16 refNum, SysLibTblEntryType *entryP)
{
   Err err;
   HostTransferGlobalsType *gP;
   UInt32 value;
   Char macro[14];
   
   // Must be 4.0 or greator
   err = FtrGet(sysFtrCreator, sysFtrNumROMVersion, &value);
   if (err || value < kVersion4_0) return -1;
   
   // Allocate library globals and store pointer to them in the system's 
   // library table
   gP = MemPtrNew(sizeof(HostTransferGlobalsType));
   ErrFatalDisplayIf(!gP, "No memory for globals");
   if (gP)
      {
      MemPtrSetOwner(gP, 0);
      MemSet(gP, sizeof(HostTransferGlobalsType), 0);
      gP->refNum = refNum;   // make self reference
      entryP->globalsP = gP;
      }
 
   // Install pointer to our dispatch table in system's library table
   entryP->dispatchTblP = (MemPtr *)PrvHostTransferDispatchTable();
   
   // Check if we're running on the simulator or emulator. On a real device,
   // there's no host so we don't register this library with the Exchange
   // Manager. In this case, we should really abort the library installation
   // altogether, but this demonstrates how exchange libs can change their
   // registration status.
#if EMULATION_LEVEL == EMULATION_NONE
   if (FtrGet('pose', 0, &value) != ftrErrNoSuchFeature)
#endif
      {
      Char description[exgTitleBufferSize + 1];
      UInt16 descriptionSize = sizeof(description);
      Err err;
      
      // Get the title of the library
      err = HostTransferLibControl(refNum, exgLibCtlGetTitle, &description,
         &descriptionSize);
      if (! err)
         {
         // Register this library with the Exchange Manager
         err = ExgRegisterDatatype(HostTransferCreator, exgRegSchemeID,
           kHostTransferScheme "\t" exgSendScheme, description, 0 /*flags*/);
         }
      }
   
   // Add a magic macro to initiate ExgRequest
   StrCopy(macro, "\x01" "0117" "0000" "0408");
   // virtualkeycode,vchrIrReceive,refnum,libEvtHookKeyMask
   macro[7] = PrvHexToAscii((refNum>>4) & 0x0f); // put refnum into string as 
hex
   macro[8] = PrvHexToAscii(refNum & 0x0f);
   PrvDeleteExistingMacro(".r");
   GrfAddMacro(".r", (UInt8 *)macro, 13);
   
   return err;
}
