Implementing the Desktop Provider

< BACK  NEXT >
[oR]

Implementing the desktop provider takes more time and effort. This is, in the main, because ActiveSync makes no assumptions about where items are stored and when they are changed.

You need to create a full COM component with a class factory, registration, and other features. The component will need to implement the COM component IReplStore, which is used to manage the store, folder, and item manipulation. The desktop provider also needs to implement the IReplObjHandler interface.

Representing HREPLITEM and HREPLFLD

HREPLITEM and HREPLFLD are pointers used by ActiveSync to point at your data associated with items and folders. In certain circumstances, ActiveSync passes a HREPLOBJ that can point either to a HREPLITEM or HREPLFLD. You therefore need a storage mechanism that can store either a HREPLITEM or HREPLFLD and be able to determine which type is currently being stored. Perhaps the most straightforward technique is to use a structure containing a union (Listing 17.10).

Listing 17.10 REPLOBJECT structure
 // structure and define for HREPLITEM and // HREPLFLD structures #define RT_ITEM     1 #define RT_FOLDER   2 typedef struct tagREPLOBJECT {   // uType indicates if a folder (RT_FOLDER)   // or item (RT_ITEM) is currently being stored   UINT uType;   // Create a union so folder and item information can be   // stored in the same structure   union   {     // for folder, has the contents been changed     BOOL fChanged;     // for item, creation time (unique identifier)     // and last modify time     struct     {       FILETIME ftCreated, ftModified;     };   }; } REPLOBJECT, *LPREPLOBJECT; 

The member uType can contain either RT_ITEM or RT_FOLDER; they indicate the current use of the structure. Folders use the member fChanged and items use ftCreated or ftModified.

ActiveSync uses the data you place in this structure to track changes to folders and items. There is generally a separate structure for each item and folder in store, and ActiveSync stores these structures in repl.dat.

You can place any type of data in the structure that is applicable to your application. However, you should attempt to limit the amount of data you store in the structure. There will always be far more items than folders, so you should focus on the amount of data stored in the item. If you use a structure with a union (as shown in Listing 17.10), ensure that the amount of data associated with the folder is less than that used for the item the overall size of the structure is determined by the largest members in the union.

Storing Data on the Desktop

ActiveSync makes no assumptions about where the data being synchronized is being stored you can use flat files, local databases, or server databases. In this example a simple flat file is used, and each item is stored as a NOTE structure (which has a fixed size). The code to access this file is located in ListDB.h, and uses the standard file I/O techniques outlined in Chapter 2.

Implementing IReplStore

The IReplStore COM interface declares functions that ActiveSync uses to obtain information and manipulate the store, folder, and items being synchronized. Table 17.3 shows the functions categorized by function.

Table 17.3. IReplStore interface functions
Category Functions
Initialization Initialize
Store information and manipulation GetStoreInfoCompareStoreIDs
Folder information and manipulation GetFolderInfoIsFolderChanged
Iterate all items in a folder FindFirstItemFind
NextItemFindItemClose
Manipulate HREPLITEM or HREPLFLD objects ObjectToBytes
BytesToObject
FreeObjectCopy
ObjectIsValidObject
HREPLITEM item synchronization CompareItem
IsItemChanged
IsItemReplicated
UpdateItem
Configuration dialog, provider icon, and name information and activity reporting ActivateDialog
GetObjTypeUIDataReportStatus
Conflict resolution and duplicate removal GetConflictInfoRemoveDuplicates

Because IReplStore is a COM interface, you must provide an implementation of all these functions; otherwise, your provider will not compile. However, only the functions shown in this chapter are essential in a simple provider.

In the example, IReplStore is implemented by the class CActiveSyncEg. The declaration of this class is in Component.h, and the implementation of IReplStore is in the file IReplStore.cpp.

IReplStore Initialization

ActiveSync calls the IReplStore::Initialize function when the provider is first loaded (Listing 17.11). The function passes a pointer to a IReplNotify interface provided by ActiveSync and used by the provider to notify ActiveSync when item changes occur in the store. Since the example doesn't implement continuous synchronization, this pointer is ignored. The uFlags parameter will contain ISF_REMOTE_CONNECTED if synchronization is being carried out over a dialup or other type of remote connection. In this case, you should avoid anything that requires user intervention (such as showing a dialog).

Listing 17.11 IReplStore::Initialize implementation
 STDMETHODIMP CActiveSyncEg::Initialize(       IReplNotify*pNotify,UINT uFlags ) {   m_bInitialized = TRUE;   return NOERROR; } 

Some IReplStore functions can be called before Initialize is called, so you should be careful not to rely on this initialization. The functions are GetStoreInfo, GetObjTypeUIDate, GetFolderInfo, ActivateDialog, BytesToObject, ObjectToBytes, and ReportStatus.

Store Information and Manipulation

ActiveSync calls the function GetStoreInfo (Listing 17.12) and passes a pointer to a STOREINFO structure that the provider populates with information about its store.

Listing 17.12 IReplStore:: GetStoreInfo implementation
 STDMETHODIMP CActiveSyncEg::GetStoreInfo(       PSTOREINFO pStoreInfo ) {   // Check correct version of StoreInfo structure   if(pStoreInfo->cbStruct  sizeof(*pStoreInfo))   {     MessageBox(NULL,       _T("GetStoreInfo Invalid Arg"), NULL, 0);     return E_INVALIDARG;   }   // we only support single-threaded operation   pStoreInfo->uFlags = SCF_SINGLE_THREAD;   // Set store's progid and description   strcpy(pStoreInfo->szProgId, g_szVerIndProgID);   strcpy(pStoreInfo->szStoreDesc, g_szFriendlyName);   // this is as far as we get if we're not Initialized   if(!m_bInitialized)   {     return NOERROR;   }   // Create the store's unique identifier   // Set the length of the store identifier   pStoreInfo->cbStoreId =       (strlen(g_szStoreFile) + 1) * sizeof(TCHAR);   // ActiveSync calls GetStoreInfo twice. Once to   // get the size of the store id (when lpbStoreId is   // NULL), and a second time, providing a buffer pointed   // to by lpbStoreId where the store id can be placed.   if(pStoreInfo->lpbStoreId == NULL)     return NOERROR;   memcpy(pStoreInfo->lpbStoreId, g_szStoreFile,     (strlen(g_szStoreFile) + 1) * sizeof(TCHAR));   return NOERROR; } 

The function GetStoreInfo is called twice by ActiveSync, the first time to determine the size of the buffer required to hold the store's unique id (cbStoreId), and the second time to copy the store id into a buffer (lpbStoreId). Table 17.4 describes the STOREINFO members used by this implementation of GetStoreInfo.

Table 17.4. StoreInfo members used in CActiveSyncEg::GetStoreInfo
Member Purpose
uFlags Use SCF_SINGLE_THREAD if your provider is single-threaded.
szProgId The store's ProgID, such as "Asdesktop.ActiveSyncEg".
szStoreDesc Description of store displayed to user, such as "ActiveSync Example".
cbStoreId Length of the store's id in bytes.
lpbStoreId Store's unique id, for example, the name of the data file "\Active-SynNotes. dat".

The function CompareStoreIDs is called by ActiveSync to determine if two store ids are actually the same. Listing 17.13 compares cbID1 and cbID2 to determine whether the number of bytes in the store ids are the same and, if they are, uses memcmp to perform a byte-wise comparison of the two strings. The function returns 0 if the lpbID1 and lpbID2 are ids that refer to the same store.

Listing 17.13 IReplStore::CompareStoreIDs implementation
 STDMETHODIMP_(int) CActiveSyncEg::CompareStoreIDs       (LPBYTE lpbID1, UINT cbID1,       LPBYTE lpbID2, UINT cbID2) {   if(cbID1  cbID2)     // first store is smaller than the second store     return -1;   if(cbID1 > cbID2)     // first store is larger than the second store     return 1;   // now compare the store ids byte by byte.   return memcmp(lpbID1, lpbID2, cbID1); } 

Folder Information and Manipulation

A store can contain one or more folders in which items are placed. ActiveSync calls GetFolderInfo for each object type (for example, "AsyncSample") configured in the registry for the provider. The implementation of GetFolderInfo returns a pointer to the IReplObjHandler interface associated with this folder (the m_DataHandler member is a CDataHandler class object that implements IReplObjHandler) and to a HREPLFLD object (Listing 17.14).

Listing 17.14 IReplStore:: GetFolderInfo implementation
 STDMETHODIMP CActiveSyncEg::GetFolderInfo(LPSTR     lpszObjType,     HREPLFLD *phFld, IUnknown ** ppObjHandler) {   LPREPLOBJECT pFolder = (LPREPLOBJECT) *phFld;   if(pFolder == NULL)           // new folder required   {     pFolder = new REPLOBJECT;   }   pFolder->uType = RT_FOLDER;   pFolder->fChanged = TRUE;   *phFld = (HREPLFLD)pFolder;   // CDataHandler member m_DataHandler   // implements IReplObjHandler   *ppObjHandler = &m_DataHandler;   return NOERROR; } 

In the example, HREPLFLD is actually a pointer to a REPLOBJECT. The HREPLFLD parameter can be NULL, in which case a new REPLOBJECT is created, or, if not NULL, the existing REPLOBJECT is used.

The function IsFolderChanged is called by ActiveSync to determine whether items in a folder need to be synchronized. With continuous synchronization this function is called frequently, but with manual synchronization it is only called when synchronization starts. In Listing 17.15 the function always sets pfChanged to TRUE, indicating that the folder needs to be synchronized.

Listing 17.15 IReplStore:: IsFolderChanged implementation
 STDMETHODIMP CActiveSyncEg::IsFolderChanged(HREPLFLD hFld,       BOOL *pfChanged ) {   *pfChanged = TRUE;   return NOERROR; } 

Iterate Items in a Folder

ActiveSync requests the provider to iterate through the items in a folder by calling the functions FindFirstItem, FindNextItem, and FindItemClose. The provider reads the first item (FindFirstItem) or the next item (FindNextItem) from the store and creates a HREPLITEM for each item. In the example, the functions GetFirstNote or GetNextNote (located in db. cpp) read the items, and a HREPLITEM is created represented by the REPLOBJECT structure (Listing 17.16). The three REPLOBJECT members are initialized with appropriate values read from the store. The HREPLITEM item is returned through the phItem parameter.

Listing 17.16 IReplStore:: FindFirstItem and FindNextItem implementations
   // Returns an HREPLITEM structure for the first item in // the .DAT file. The data in HREPITEM is the OriginalTime // (the unique identifier) and ModifyTime // (to determine if the item has changed); STDMETHODIMP CActiveSyncEg:: FindFirstItem(HREPLFLD hFld,       HREPLITEM *phItem, BOOL *pfExist ) {   WCHAR szNote[STRLEN_NOTE];   FILETIME ftCreateTime, ftModifyTime;   // attempt to get first record   *pfExist = m_ListDB.GetFirstNote(&ftCreateTime,       szNote, &ftModifyTime);   if(!*pfExist)     return NOERROR;   // now make up the HREPLFLD   LPREPLOBJECT lpRepl = new REPLOBJECT;   lpRepl->uType = RT_ITEM;   lpRepl->ftCreated = ftCreateTime;   lpRepl->ftModified = ftModifyTime;   // set our pointer into HREPLITEM   *phItem = (HREPLITEM)lpRepl;   return NOERROR; } // Find the next item from the .DAT file STDMETHODIMP CActiveSyncEg::FindNextItem(HREPLFLD hFld,       HREPLITEM *phItem, BOOL *pfExist ) {   WCHAR szNote[STRLEN_NOTE];   FILETIME ftCreateTime, ftModifyTime;   // attempt to get first record   *pfExist = m_ListDB.GetNextNote(&ftCreateTime,       szNote, &ftModifyTime);   if(!*pfExist)   {     return NOERROR;   }   // now make up the HREPLFLD   LPREPLOBJECT lpRepl = new REPLOBJECT;   lpRepl->uType = RT_ITEM;   lpRepl->ftCreated = ftCreateTime;   lpRepl->ftModified = ftModifyTime;   // set our pointer into HREPLITEM   *phItem = (HREPLITEM)lpRepl;   return NOERROR; } 

FindFirstItem and FindNextItem set the pfExist BOOL parameter to TRUE if a HREPLITEM is returned, or FALSE if no more items exist. FindItemClose is called when pfExist is set to FALSE (Listing 17.17).

Listing 17.17 IReplStore:: FindItemClose implementation
 // Finished going through all records. // Nothing to do in this case. STDMETHODIMP CActiveSyncEg::FindItemClose(HREPLFLD hFld ) {   return NOERROR; } 

Manipulating HREPLITEM and HREPLFLD Objects

The provider must implement functions that allow ActiveSync to manipulate HREPLITEM and HREPLFLD objects. Table 17.5 shows the functions that must be implemented. Many of these functions operate on HREPLITEM or HREPLFLD objects, and the functions may need to take different actions depending on which is passed. Remember that the generic type HREPLOBJ is used to refer to both HREPLITEM and HREPLFLD objects.

Table 17.5. Functions to manipulate HREPLITEM and HREPLFLD objects
Function Description
ObjectToBytes Convert a HREPLITEM or HREPLFLD into a stream of bytes.
BytesToObject Convert a stream of bytes into a HREPLITEM or HREPLFLD.
FreeObject Free memory used by a HREPLITEM or HREPLFLD.
CopyObject Copy one HREPLITEM or HREPLFLD into another.
IsValidObject Determine whether a HREPLITEM or HREPLFLD still represents a valid object.

Using the structure/union REPLOBJECT to store both HREPLITEM and HREPLFLD objects greatly simplifies the coding of these functions.

ActiveSync calls ObjectToBytes and BytesToObject when writing and reading objects to and from the file repl.dat. ObjectToBytes (Listing 17.18) will be called twice for each conversion. In the first call, ObjectToBytes simply returns the number of bytes required to write the object. In the second call ActiveSync provides a buffer of the correct length into which the copy is made.

Listing 17.18 IReplStore:: ObjectToBytes implementation
 STDMETHODIMP_(UINT) CActiveSyncEg::ObjectToBytes       (HREPLOBJ hObject, LPBYTE lpb ) {   // buffer has been created to requested size   if(lpb != NULL)     memcpy(lpb, (LPREPLOBJECT)hObject,         sizeof(REPLOBJECT));   return sizeof(REPLOBJECT); } 

Both HREPLITEM and HREPLFLD objects are passed into ObjectToBytes, but because REPLOBJECT is used for both folders and items, the same code can be used for both.

BytesToObject (Listing 17.19) creates a new REPLOBJECT, copies from the stream of bytes into this new structure, and returns a pointer cast to a HREPLOBJ.

Listing 17.19 IReplStore:: BytesToObject implementation
 STDMETHODIMP_(HREPLOBJ) CActiveSyncEg::BytesToObject         (LPBYTE lpb, UINT cb ) {   if(cb != sizeof(REPLOBJECT))     MessageBox(NULL,       _T("Not correct size in Bytes to object"),       NULL,0);   LPREPLOBJECT lpReplObject = new REPLOBJECT;   // perform the copy   memcpy(lpReplObject, lpb, cb);   return (HREPLOBJ)lpReplObject; } 

ActiveSync calls FreeObject (Listing 17.20); the implementation should free any memory associated with the HREPLOBJ object.

Listing 17.20 IReplStore:: FreeObject implementation
 STDMETHODIMP_(void) CActiveSyncEg::FreeObject(       HREPLOBJ hObject ) {   LPREPLOBJECT pItem = (LPREPLOBJECT)hObject;   delete (LPREPLOBJECT) hObject; } 

From time to time, ActiveSync needs to copy HREPLOBJ objects. The function CopyObject does this by copying a REPLOBJECT from one location to another (Listing 17.21).

Listing 17.21 IReplStore:: CopyObject implementation
   STDMETHODIMP_(BOOL) CActiveSyncEg::CopyObject(         HREPLOBJ hObjSrc, HREPLOBJ hObjDest) {   LPREPLOBJECT lpRepObjSrc = (LPREPLOBJECT)hObjSrc;   LPREPLOBJECT lpRepObjDest = (LPREPLOBJECT)hObjDest;   *lpRepObjDest = *lpRepObjSrc;   return TRUE; } 

Finally, IsValidObject is called when ActiveSync needs to determine whether the HREPOBJ still refers to a valid item or folder. For folders, Listing 17.22 simply checks that the REPLOBJECT uType is RT_FOLDER, returning NOERROR if it is, or RERR_CORRUPT to indicate the object is no longer valid.

Listing 17.22 IReplStore:: IsValidObject implementation
 STDMETHODIMP CActiveSyncEg::IsValidObject(     HREPLFLD hFld, HREPLITEM hItem, UINT uFlags ) {   LPREPLOBJECT lpRepObj;   if(hFld != NULL)   {     lpRepObj = (LPREPLOBJECT)hFld;     if(lpRepObj->uType == RT_FOLDER)       return NOERROR;     else       return RERR_CORRUPT;   }   if(hItem!= NULL)   {     lpRepObj = (LPREPLOBJECT)hItem;     if(lpRepObj->uType != RT_ITEM)       return RERR_CORRUPT;     NOTE aNote;     // attempt to find the item     if(m_ListDB.FindNote(&lpRepObj->ftCreated,           &aNote))       return NOERROR;     else       return RERR_OBJECT_DELETED;   }   return NOERROR; } 

For items, a check needs to be made that uType is RT_ITEM. Additionally, the function needs to check that the item referred to by the HREPLFLD is still present in the store. Calling FindNote (implemented in db.cpp) does this.

HREPLITEM Synchronization

HREPLITEM objects are maintained to allow ActiveSync to track changes to objects either on the desktop or the Windows CE device. The provider must implement the functions listed in Table 17.6.

Table 17.6. Functions to Synchronize HREPLITEM items
Function Description
CompareItem Do two HREPLITEMs refer to the same item?
IsItemChanged Has the HREPLITEM changed?
IsItemReplicated Is the HREPLITEM to be replicated?
UpdateItem Update the information in the HREPLITEM based on what is stored in the desktop database.

In general, at least two pieces of information are held in a HREPLITEM to enable these functions:

  1. The unique identifier, so items can be compared. In REPLOBJECT this is ftCreated, the FILETIME timestamp of when the item was created.

  2. The last modification identifier, so changes to items can be tracked. In REPLOBJECT this is the ftModified FILETIME timestamp.

CompareItem is called when ActiveSync needs to know whether two HREPLITEMs refer to the same item in the store. This would be called, for example, when an item from the Windows CE device is updated and ActiveSync needs to find the corresponding item in the store. CompareItem (Listing 17.23) simply passes the ftCreated members to CompareFileTime, which returns 0 if the FILETIMEs are the same, 21 if the first is earlier, or 1 if the first is later.

Listing 17.23 IReplStore:: CompareItem implementation
 STDMETHODIMP_(int) CActiveSyncEg::CompareItem(     HREPLITEM hItem1, HREPLITEM hItem2 ) {   LPREPLOBJECT lpRepObj1 = (LPREPLOBJECT)hItem1;   LPREPLOBJECT lpRepObj2 = (LPREPLOBJECT)hItem2;   int nRet = CompareFileTime(&lpRepObj1->ftCreated,       &lpRepObj2->ftCreated);   return nRet; } 

HREPLITEM maintains a modification timestamp that may be different from the item in the database. For example, the database item may have changed since the time the HREPLITEM was created. ActiveSync calls IsItemChanged to determine if this is the case (Listing 17.24).

IsItemChanged passes in two HREPLITEM objects. If both are non-NULL, the function compares the two ftModified members in the REPLOBJECT structures pointed to by hItem and hItemComp. If hItemComp is NULL, IsItemChanged compares the ftModified for hItem with the modification time of the item in the database. This is obtained by finding the item in the database using FindNote (db.cpp).

Listing 17.24 IReplStore:: IsItemChanged implementation
 STDMETHODIMP_(BOOL) CActiveSyncEg::IsItemChanged(     HREPLFLD hFld,     HREPLITEM hItem,     HREPLITEM hItemComp ) {   LPREPLOBJECT lpRepObj1 = (LPREPLOBJECT)hItem;     if(hItemComp != NULL)   {     LPREPLOBJECT lpRepObj2 = (LPREPLOBJECT)hItemComp;     return CompareFileTime(       &lpRepObj1->ftModified,       &lpRepObj2->ftModified);   }   else   {     // need to compare this object with the     // one in the .DAT file     NOTE aNote;     if(m_ListDB.FindNote(         &lpRepObj1->ftCreated, &aNote))       return CompareFileTime(         &lpRepObj1->ftModified,         &aNote.ftLastUpdate);     else     {       MessageBox(NULL,         _T("Could not find record for \           IsItemChanged"), NULL, 0);       return FALSE;     }   } } 

Generally, all items in the store are synchronized. However, there are times when you may want to filter the items being synchronized for example, you may only want to filter appointments from the last two weeks. The function IsItemReplicated is passed a HREPLITEM, and returns TRUE if the item is to be synchronized (Listing 17.25).

Listing 17.25 IReplStore:: IsItemReplicated implementation
   STDMETHODIMP_(BOOL) CActiveSyncEg::IsItemReplicated(     HREPLFLD hFld, HREPLITEM hItem ) {   return TRUE; } 

Implementing the Desktop IReplObjHandler COM Interface

The desktop application must implement the same IReplObjHandler interface functions as the device, but the implementations will typically be different. In the example the class CDataHandler in the file ReplObjHandler.cpp implements the IReplObjHandler interface.

IReplObjHandler:: Setup

ActiveSync calls this function before any item is received or sent. This provides an opportunity to perform initialization. A pointer to a REPLSETUP structure is passed in; this provides information such as the direction of the transfer.

The Setup function will normally save a pointer to the REPLSETUP structure for future use. Since ActiveSync is multithreaded, it is possible that a read (outgoing transfer) occurs at the same time as a write (incoming transfer). Therefore, the IReplObjHandler class has two members, m_pReadSetup and m_p- WriteSetup to store separate pointers (Listing 17.26). The pointer will be used later in GetPacket and SetPacket.

Listing 17.26 IReplObjHandler:: Setup implementation
 STDMETHODIMP CDataHandler::Setup(PREPLSETUP pSetup) {   // Can be reading and writing at the same time,   // so need two setups   if(pSetup->fRead)     m_pReadSetup = pSetup;   else     m_pWriteSetup = pSetup;   return NOERROR; } 

IReplObjHandler:: Reset

The Reset function provides an opportunity to free any resources created during the serialization or deserialization. In this case, there is nothing to do (Listing 17.27).

Listing 17.27 IReplObjHandler:: Reset implementation
 STDMETHODIMP CDataHandler::Reset(PREPLSETUP pSetup) {   return NOERROR;    // no resources to be freed } 

IReplObjHandler::GetPacket

ActiveSync calls the function IReplObjHandler::GetPacket to request a packet for a particular item being synchronized. Your implementation should produce a byte stream representing the entire item (if it fits into a single packet) or the next packet in sequence. The function passes in the recommended maximum size of the packet in cbRecommend.

Listing 17.28 shows the implementation of GetPacket. The function calls FindNote (located in ListDB.cpp) to read the record for the timestamp of when the item was created (lpRepObj->ftCreated, which is the unique identifier for the note). The function serializes the record into a NOTE structure pointed to by pNote, returns a pointer to the structure in lpByte, and returns the size of the NOTE structure in dwLen. The pointer and size are returned to ActiveSync through the parameters lppbData and pcbData. The function returns RWRN_LAST_PACKET, indicating this is the one and only packet for thisitem.

Listing 17.28 IReplObjHandler:: GetPacket implementation
 STDMETHODIMP CDataHandler::GetPacket(LPBYTE *lppbData,     DWORD *pcbData, DWORD cbRecommend) {   NOTE * pNote = new NOTE;   LPREPLOBJECT lpRepObj =        (LPREPLOBJECT)m_pReadSetup->hItem;   if(m_pReadSetup->hItem == NULL)     return E_UNEXPECTED;   // locate the note in the file   if(!m_pListDB->FindNote(&lpRepObj->ftCreated, pNote))   {     MessageBox(NULL,       _T("GetPacket: Could not find record"),           NULL, MB_OK);     return RERR_BAD_OBJECT;   }   else   {     *lppbData = (LPBYTE)pNote;     *pcbData = sizeof(NOTE);   }   return RWRN_LAST_PACKET; } 

IReplObjHandler::SetPacket

SetPacket does the opposite of GetPacket it is passed a pointer to a stream of bytes and writes the data to a new or existing record in the data file. The REPLSETUP structure member dwFlags contains the value RSF_NEW_OBJECT if this item is a new record; otherwise, an existing record is to be updated.

In Listing 17.29, SetPacket casts the incoming lpbData pointer to a NOTE pointer. If HREPLITEM is non-NULL, the function FindNote (in ListDB.CPP) is used to locate the item (since it already exists) and then calls UpdateNote to update the new information for the item. For a new object (when HREPLITEM is NULL), a new note is added to the desktop database using the function AddNote.

Listing 17.29 IReplObjHandler:: SetPacket implementation
 STDMETHODIMP CDataHandler::SetPacket(LPBYTE lpbData,       DWORD cbData) {   NOTE* lpNote = (NOTE*)lpbData;   LPREPLOBJECT lpRepl = new REPLOBJECT;   // Have a HREPLITEM must be an existing record   if(m_pWriteSetup->hItem != NULL)    {     if(!m_pListDB->FindNote(&lpNote->ftOriginal))     {       MessageBox(NULL,         _T("Could not find existing note"),         NULL, MB_OK);       return E_UNEXPECTED;     }     // update record     m_pListDB->UpdateNote(&lpNote->ftOriginal,         lpNote->szNote);   }   else   {     // add record     m_pListDB->AddNote(&lpNote->ftOriginal,           &lpNote->ftLastUpdate,           lpNote->szNote);   }   lpRepl->uType = RT_ITEM;   lpRepl->ftModified = lpNote->ftLastUpdate;   lpRepl->ftCreated = lpNote->ftOriginal;   m_pWriteSetup->hItem = (HREPLITEM)lpRepl;   return NOERROR; } 

IReplObjHandler::DeleteObj

DeleteObj is called when an item needs to be deleted from the database. The function is passed a REPLSETUP pointer as a parameter and calls DeleteNote (located in ListDB.CPP) to delete the record (Listing 17.30).

Listing 17.30 IReplObjHandler:: DeleteObj implementation
 STDMETHODIMP CDataHandler::DeleteObj(PREPLSETUP pSetup) {   LPREPLOBJECT lpRepObj = (LPREPLOBJECT)pSetup->hItem;   if(!m_pListDB->DeleteNote(&lpRepObj->ftCreated))     MessageBox(NULL,       _T("Could not delete record"),       NULL, 0);   return NOERROR; } 

< BACK  NEXT >


Windows CE 3. 0 Application Programming
Windows CE 3.0: Application Programming (Prentice Hall Series on Microsoft Technologies)
ISBN: 0130255920
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net