Thursday, March 29, 2007

Beaming Using the Palm OS Exchange Manager

As a final part of our "inter-application communication," let us discuss one more way of how to do it. As you may guess, I mean here the Exchange Manager API. This kind of communication differs from those ones we've spoke about in previous articles. When you hear 'beaming', yes—that's it. But, as you may discover, a request for exchange data is just one of the launch codes your application may receive (e.g. sysAppLaunchCmdExgReceiveData), so it may be useful to export data to other apps at the same device. So, let's go ahead and take a look at practical cases.


Case 1: Standard Beaming of a Whole Database or Specific Records:
The Palm OS's user interface doesn't allow beaming of an abitrary database, only prc-files, i.e. applications. So, it'd be nice for your program to offer such functionality. Fortunately, it becomes a pretty simple task with Exchange Manager. The normal working flow is as follows:
Define a variable of type ExgSocketType and initialize it to zeroes.
Assign appropriate values to description, target, and optionally to the type and name fields.
ExgSocketType is declared in ExgMgr.h as

typedef struct ExgSocketType {

UInt16 libraryRef; // identifies the Exg library in use

UInt32 socketRef; // used by Exg library to identify this connection

UInt32 target; // Creator ID of application this is sent to

UInt32 count; // # of objects in this connection (usually 1)

UInt32 length; // # total byte count for all objects being sent

UInt32 time; // last modified time of object

UInt32 appData; // application specific info

UInt32 goToCreator; // creator ID of app to launch

ExgGoToType goToParams; // If launchCreator then this

UInt16 localMode:1; // Exchange with local machine

UInt16 packetMode:1; // Use connectionless packet mode

UInt16 noGoTo:1; // Do not go to app (local mode only)

UInt16 noStatus:1; // Do not display status dialogs

UInt16 preview:1; // Preview in progress: don't

UInt16 reserved:11; // reserved system flags

Char *description; // text description of object

Char *type; // Mime type of object (optional)

Char *name; // name of object, generally a file name

} ExgSocketType;typedef ExgSocketType *ExgSocketPtr;

Note that the localMode field controls the operation mode, so you may want to set a 1 there to work locally.
To establish a connection, call ExgPut.
To send the whole database, call ExgDBWrite. An alternative way is to call ExgSend in a loop to send all desired data. Actually, these are the same methods because ExgDBWrite requires a callback function as a parameter, which uses ExgSend internally.
After all jobs are finished, call ExgDisconnect to close the connection.
The following code chunk illustrates all that was said above:

void BeamWholeDB(){
ExgSocketType exgSocket;
UInt16 cardNo;
LocalID dbID;
DmSearchStateType searchState;
Err err;

Char *appName = "TestDB";
err = DmGetNextDatabaseByTypeCreator(true, &searchState,'data', 'test', true,&cardNo, &dbID);
if (!err)
{
MemSet(&exgSocket,sizeof(exgSocket),0);
exgSocket.description = appName;
exgSocket.name = "TestDB.pdb";
err = ExgPut(&exgSocket);
if (err == 0)
{
ExgDBWrite(WriteProc, &exgSocket, appName, dbID,cardNo);
ExgDisconnect(&exgSocket,err);
}
}
}
As you can see, it's really not a big deal. Sure, we've left a lot of details behind the scene, such as sending multiple objects (refer to the count member of ExgSocketType), which application should be launched (if any) after receiving data, and so forth. Like any other Palm OS API, Exchange Manager API is rich, so you're welcome to investigate it more closely.
Okay, coming back to sending individual records, you may guess now that it differs from the previous example only in using ExgSend function in some loop instead of ExgDBWrite. Therefore, we'll consider the receiving part of flow; i.e. what the application should do when it is going to get some data via Exchange library.

A sequence of steps follows:
First, you should handle the sysAppLaunchCmdExgReceiveData launch code in PilotMain.
Call ExgAccept to accept the incoming request.
Call ExgReceive in a loop until it returns zero.
And finally, call ExgDisconnect to close the connection.

So, as we've just said:

// in PilotMainDmOpenRef g_dbP;

// our database...

if (cmd == sysAppLaunchCmdExgReceiveData){

DmOpenRef dbP;

if ((launchFlags & sysAppLaunchFlagSubCall) == sysAppLaunchFlagSubCall) {

dbP = g_dbP;

FrmSaveAllForms();

err = ReceiveData(dbP,(ExgSocketPtr)cmdBPB);

}

else

{

dbP = DmOpenDataByTypeCreator('data','test', dmModeReadWrite);

if (dbP) {

err = ReceiveData(dbP,(ExgSocketPtr)cmdBPB); DmClosedatabase(dbP);

}

}

}

Err ReceiveData(DmOpenRef dbP, ExgSocketPtr sockP){

Err err = 0;

UInt16 wIndex = -1;

err = ExgAccept(sockP);

if ( err == errNone ) {

char buffer[1024];

ULong lBytesRecv = 0;

while ((lBytesRecv = ExgReceive(sockP,buffer, sizeof(buffer)))

{

// process data ...

}

err = ExgDisconnect(sockP,err);

}

}
Now, a few words about the name member the of ExgSocketType structure. Starting from Palm OS 4.0, this field may contain a URL to identify the exchange library to connect with. Using a local exchange library may become handy if you want to debug your application locally or to send/request data to/from another application at the same device. On Palm OS 4.0 and later, if the URL starts from the '?' character, the user may be prompted to choose which exchange library to use if more than one library is registered for the selected scheme.
Case 2: Exchanging Data with the Local PIM
There are at least several situations when you might want to use Local Exchange Manager. A good example is exporting data to some built-in application (e.g. to MemoPad as the simpliest case). Even if the user has decided to use some third-party program instead of a standard one, you'll be sure that this program will correctly receive exported data. A small example looks like this:

void SendToLocalMemopad(){

ExgSocketType exgSocket;

Err err = 0;

Char *textBuf = "test";

UInt32 size = StrLen(textBuf) + 1;

MemSet(&exgSocket,sizeof(exgSocket),0);

exgSocket.description = "Test message";

exgSocket.name = "?_local:Sample.txt"; exgSocket.type = ".txt";

err = ExgPut(&exgSocket);

if (err == 0) {

ExgSend(&exgSocket,textBuf,size,&err);

ExgDisconnect(&exgSocket,err);

}

}
Well, after we've discussed the basic terms of Exchange Manager stuff, you are ready to go ahead and study additional details. Here I don't intend to copy the Palm OS Reference guide, so it'll be our homework. In real applications, you may want to use much more than we've covered here. Games, responding to 'Get' requests from a remote application, two-ways communications etc., are just instant examples. Good luck!
Communication with Devices Running an OS other than Palm OSOne other task that becomes relevant today is to exchange data between different devices with different OSes. Here, I'm speaking mainly about a PalmOS/Symbian <=> WinCE exchange. The main point is that Exchange manager uses the OBEX protocol to do its job. Early Pocket PC 2000 handhelds did not utilize OBEX at all. If you are facing such a case, you need to implement some kind of OBEX simulation on the Pocket PC device (refer to the OBEX specification for exact details). Fortunately, on Pocket PC 2002 and later, OBEX is supported, so data may be freely sent back and forth either from the OS GUI or programmatically. The only thing you might need to take care of in your Palm OS application is to register an application to handle desired types of data, say '.doc', by calling ExgRegisterDatatype. That's all, folks.

No comments: