MobileDeveloping ActiveSync Service Providers: The First Look

Developing ActiveSync Service Providers: The First Look

Why You Need It

Mobile applications are getting more powerful even faster than you can imagine. Data management gets more complex, functionality becomes richer, and so forth. In such situations, the role of data synchronization between PDA and desktop computer cannot be underestimated. If you develop a companion application, you should implement ActiveSync facilities wherever you can. There is no doubt that it will make your product more attractive, no matter what type of data synchronization you will choose: manual or continuous. The only sad point here is that it is really annoying. You have to write a lot of code at both sides, device and desktop, to get something worked. Nevertheless, it is worth doing.

What ActiveSync Service Provider Is

Following the common ActiveSync trend, ActiveSync Service Providers are DLLs that either export a set of predefined functions (PDA) or implement a full-featured COM (desktop). Yes, there are two parts to this story, PDA and desktop, and they are the players that will interact during synchronization sessions via ActiveSync as an intermediate entity.

MS ActiveSync Data Structure

First, overview how ActiveSync manages its data. If you have dealt with Pocket Outlook, you definitely paid attention to how data is organized:

  • Store
    • Folder 1
      • Item 1
      • Item 2
    • Folder 2
      • Item 1
      • Item 2

ActiveSync uses the same hierarchy for its data. The very basic unit of synchronization process is the Item. It has two main features:

  1. Unique object identifier (OID) that separates this very item from all others; usually, OIDs are just timestamps
  2. Some value to make a decision whether the item was modified; once again, the timestamp of the latest modification will do

Due to their uniqueness, Item identifiers cannot be reused. Moreover, they have to be ordered to determine where to place a particular Item.

ActiveSync stores data items in a repl.dat file for each device profil founde. Therefore, you should make Item as small as possible. ActiveSync will manipulate this data via the HREPLITEM pointer type.

The next level of hierarchy is the Folder. It contains a group of Items of similar type. Using a single folder for your Items makes all programming easier. Folder identification is up to you; ActiveSync will manage it through a HREPLFLD pointer type.

And finally, Store keeps folders. It has a unique identifier. Both desktop and PDA should use the same store ID to be linked. Actually, the store ID is ProgID. You can utilize a database, or even a flat file, for storage.

The last, but not the least thing, is that all ActiveSync data things (HREPLITEM, HREPLFLD) are used only for the desktop component; the Windows CE DLL has nothing to do with it.

Implementing device part of ASP

Your tour of ActiveSync Service Providers will commence with the handheld DLL because it is much simpler than its desktop buddy. As you have heard before, the device ASP is a standard DLL thT exports the following functions:

; DevASPSimple.def - Declares the module parameters for the DLL.
LIBRARY DEVASPSIMPLE.DLL
EXPORTS
   InitObjType
   ObjectNotify
   GetObjTypeInfo
   ReportStatus

In addition, you have to implement the IReplObjectHandler interface. It will be used to perform required conversions of your items from/to byte representation. You don’t need to code ‘real’ COM object here; simple inheritance will do. All the rest of the device ASP components will deal with data manipulation, and can be shared with your main application.

So, consider what is inside a device-part of ASP. You will see a purpose and an implementation of each exported function one by one.

InitObjType

This function is called by the ActiveSync manager when your provider is about to be started and terminated. Here is a place where you may perform any desired initialization or cleanup. Besides, InitObjType creates an instance of the IReplObjHandler object. Here is the exact signature:

typedef BOOL (*PINITOBJPROC)( LPWSTR lpszObjType,
                              IReplObjHandler **ppObjHandler,
                              UINT uPartnerBit );

The typedef above reveals all the business. Your custom ASP may support several object types, so you have a lpszObjType parameter to indicate which one is required. At the termination stage, it will have a NULL value. Next, ppObjHandler will get a pointer to the newly created object. The last parameter is uPartnerBit. A Windows CE powered handheld can establish a partnership with only two desktop computers at a time, so this parameter indicates which one it is recently connected to, being equal to 1 or 2 respectively.

ObjectNotify

When any change occurs in a Windows CE object store, all available (loaded, actually) ActiveSync Service Providers are notified of what has happened. ObjectNotify is a way that your provider will get such notification:

typedef BOOL (*POBJNOTIFYPROC)( POBJNOTIFY );

The only parameter is a pointer to OBJNOTIFY struct, which keeps all information about changes. Its members give you a detailed picture of the change: type, object identifier, and so forth. This function should return TRUE if the particular item can be synchronized by your provider, or FALSE otherwise. Probably an obvious yet important thing is that the provider must check to whom the changed item belongs.

GetObjTypeInfo

At the moment ActiveSync manager is about to perform some action on your data, it needs to gather information about what’s being synchronized. The GetObjTypeInfo exported function serves this purpose:

typedef BOOL (*PGETOBJTYPEINFO)( POBJTYPEINFO );

The OBJTYPEINFO struct contains a few members that provide ActiveSync with details about the name of the object, total data size, number of synchronized items, and the last modification time stamp.

ReportStatus

ActiveSync calls this function to inform ASP about various events occuring during the synchronization session:

typedef BOOL (*PREPORTSTATUS)( LPWSTR lpszObjType,
                               UINT uCode,
                               UINT uParam );

uCode and uParam give you synchronization status info:

BOOL CASPSimpleStore::ReportStatus(LPWSTR lpszObjType,
                                   UINT uCode,
                                   UINT uParam)
{
   //
   // TODO: Called to inform provider of sync events.
   // Add code to handle these events.
   //
   switch(uCode)
   {
   case RSC_BEGIN_SYNC:
      break;
   case RSC_END_SYNC:
      break;
   ...
   }
   return TRUE;
}

IReplObjHandler Implementation

As noted above, the second part of the device part implementation of ASP is the IReplObjHandler interface. In the case of Windows CE, you don’t need to code all standard COM orchestration, but rather develop a class that simply inherits from this interface and fulfills its methods. Hence, it may be declared like this:

class CASPSimpleFolder;
class CASPSimpleObjHandler : public IReplObjHandler
{
protected:
   long              m_cRef;
   PREPLSETUP        m_pWriteSetup;
   PREPLSETUP        m_pReadSetup;
   CASPSimpleFolder *m_pFolder;
   LPBYTE            m_pReadPacket;
private:
   virtual ~CASPSimpleObjHandler();
public:
   CASPSimpleObjHandler(CASPSimpleFolder *pFolder);
   //======== IUnknown methods ==============//
   STDMETHODIMP_(ULONG)    AddRef(void);
   STDMETHODIMP_(ULONG)    Release(void);
   STDMETHODIMP            QueryInterface(const IID& iid,
                                          void **ppv);
   //======== IReplObjHandler methods ==========//
   STDMETHODIMP Setup(PREPLSETUP pSetup);
   STDMETHODIMP Reset(PREPLSETUP pSetup);
   STDMETHODIMP GetPacket(LPBYTE *lppbData,
                          DWORD *pcbData,
                          DWORD cbRecommend);
   STDMETHODIMP SetPacket(LPBYTE lpbData, DWORD cbData);
   STDMETHODIMP DeleteObj(PREPLSETUP pSetup);
};

As you can see, this object is responsible for Item’s data manipulation (serialization/deserialization/deletion). It also is notified when ‘send’ and ‘receive’ operations are about to begin or to end. You will shortly cover them all here; you will find additional details when the sample project is posted.

Setup and Reset

This function is called before ActiveSync does anything with a synchronized item. Therefore, you can perform all required initialization here. The Windows CE part of ASP uses only a few members of the REPLSETUP struct:

  • fRead—Detects whether Item is about to be read or written. ActiveSync is a mutlithreaded application, so it may send and receive data at the same time.
  • oid—Identifies the object.
  • oidNew—Identifies the newly created object.
  • dwFlagsRSF_NEW_OBJECT designates that the new object should be added to the property database.

The simplest implementations looks like this:

STDMETHODIMP CASPSimpleObjHandler::Setup(PREPLSETUP pSetup)
{
   if(pSetup == NULL)
   return E_INVALIDARG;
   //
   // TODO: Called before GetPacket() or SetPacket() is called.
   // We could be reading and writing at the same time, so we
   // need to store the setup ptr away. Add any initialization
   // code here, before starting serializing/de-serializing.
   //
   if(pSetup->fRead)
   {
      m_pReadSetup = pSetup;
      m_pReadPacket = new BYTE[MAXDATASIZE];
      // we can handle upto MAXDATASIZE bytes of data
      if(m_pReadPacket == NULL)
         return E_OUTOFMEMORY;
   }
   else
   {
      m_pWriteSetup = pSetup;
   }
   return S_OK;
}

In turn, the Reset method is the right place to release all allocated resources:

STDMETHODIMP CASPSimpleObjHandler::Reset(PREPLSETUP pSetup)
{
   if(pSetup == NULL)
   return E_INVALIDARG;
   //
   // TODO: Called right after GetPacket() or GetPacket() calls
   // are completed. Add any cleanup code here.
   //
   if(m_pReadPacket)
   {
      delete [] m_pReadPacket;
      m_pReadPacket = NULL;
   }
   else
   {
      //
      // On writing, update the object ID and time stamp
      // stored in the data file
      //
      m_pFolder->SetFileOID(m_pWriteSetup->oidNew);
      m_pFolder->UpdateFileTime();
   }
   return S_OK;
}

GetPacket and SetPacket

These functions are called to perform a conversion of a synchronized item to or from byte stream representation. There is nothing much to comment about here because actual implementation depends on your data structure. Let me just note a few things.

GetPacket can create several packets from one data chunk. For efficiency, the packet size shouldn’t exceed 8K. In such multipacket conversions, GetPacket will return NOERROR for all created packets and RWRN_LAST_PACKET for the last one.

SetPacket adds given data into ASP storage and places an object identifier into the oid member of the previously saved REPLSETUP variable. If the update has failed for some reason, SetPacket returns RERR_SKIP_ALL to discard all upcoming packets.

DeleteObj

This function is called to delete items from the store, that’s all. It takes an object identifier from the REPLSETUP parameter to determine what to do.

Conclusion

First, the accompanying code’s zip file will be posted along with next article describing the desktop part of ASP. Recently, you were acquainted with the simplest part of all the business. Even though, an application developer always has ‘black job’ to do, so be it. In the next article, you will complete the desktop ASP components and also cover the registration procedure.

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers’ lives in the mobile jungles a little bit simpler.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories