ActiveSync Service Provider Development FAQ

How can I diagnose why my
ActiveSync provider has become disabled?
ActiveSync maintains a log file called wcesmgr.log,
which is located in the TEMP directory. This file should tell you why a provider
has become disabled. To find where the current user's TEMP directory is located,
run set TEMP. This will tell you the path.
There are numerous reasons why a provider will
become disabled. One common reason is if ActiveSync can't find the store DLL
which is listed in the registry key string value "Store":
[HLM\Windows CE Services\Synchronization\Objects\YourProvider]
If the "Store" string value is missing
the null terminator, this will also cause erratic behavior, where the provider
becomes disabled after a disconnect and reconnect of the device.
Why is it that ActiveSync
works fine until I disconnect the device and then reconnect, and then my
provider's icon becomes disabled?
The problem may be due to a malformed registry
setting on the device. If your "Store" string value in the
device's registry doesn't have a null terminator, then it will cause this
behavior. "The Store" string is located under the key:
[HLM\Windows CE Services\Synchronization\Objects\YourProvider]
You can diagnose this by looking at the wcesmgr.log
file.
What's going on with ObjectNotify?
Object notify is the most critical function on
the device side. Yet it is also the most cryptic. There are three
phases which need to be treated separately. It would have been far easier to
understand if the function had been split into three separate functions.
Instead you have to figure out which phase you are in by the flags which are
passed in. Ironically, the MS ActiveSync documentation actually gives a
complete explanation of how to treat each phase. Unfortunately, the samples seem
to lump the three phases together making it very difficult to understand what is
happening at any given time.
ObjectNotify is a method which allows you to
stake your claim on any object which is synced by active sync. If you want
to claim an object then this function should return true. Likewise, if the
object doesn't belong to your provider ObjectNotify should return
false.
The three phases of ObjectNotify, are
determined by whether or not the device has just connected or just synced an
object. The provider can decipherer which phase it is in by looking at the
dwFlags in the OBJNOTIFY structure. Depending upon the phase, the members of the
OBJNOTIFY structure change meaning, and may or may not be present. Also
depending on the phase, the OBJECTNOTIFY parameters which are passed back
including: cOidDel, cOidChg, and poid have different meanings.
Phase 1. The device has just
connected, and all objects are enumerated. Please note that a large number of
objects call this routine, it could be hundreds of objects. If you write to a
log file from this function you will see a whole rush of items pore into your
log file. It's important that this routine is compact and optimized. You
can determine if it is Phase 1 because neither the ONF_DELETED, ONF_CHANGED nor ONF_CLEAR_CHANGE
flags are set. However, one of the object type flags should be set such as ONF_RECORD.
If your provider only deals with a single object type such as records you return
false if the record type doesn't match. For this phase, all of the members of OBJNOTIFY
are present. The must useful member is the OBJNOTIFY ::oidInfo.infRecord.oidParent
member, because you can compare the OID of the object to your database OID to
see if the function should claim the object.
If the object belongs to your provider and has
changed since the last sync, then set cOidChg to 1 (assuming you are only
syncing a single object) and cOidDel to 0, and set poid to the SyncID. The
SyncID is normally the CEOID of the object, however you could set it to anything
with in the specified guidelines. If the object hasn't changed then set cOidChg
to 0 (assuming you are only syncing a single object) and cOidDel to 1, and set
poid to the SyncID. Please not that this is an example of why ObjectNotify
is so confusing. For this case cOidDel has nothing to do with deletion,
but is used to donote the number of unchanged object.
Please note that individual record objects from a
property database are not enumerated in phase 1. You need to use the FindObjects
function to enumerate all of the records. This fact of course is not documented,
and the documentation falsely leads you to believe that all objects are
enumerated.
Phase 2. The device has already connected
and gone through phase 2. The provider can tell if it is in this
phasebecause either the ONF_DELETED or
ONF_CHANGED will be set, and the ONF_CLEAR_CHANGE will be cleared. If you write
to a log file from this routine, you will see logs from this phase as you modify
records on the device from your device app. One of the object type
flags should be set such as ONF_RECORD.
If the ONF_DELETED flag has been set, then the
object has been deleted from the store, and the only way you can know if the
object belongs to your provider is by comparing the OBJNOTIFY::oidInfo.infRecord.oidParent
parameter. For the deleted case set cOidDel to 1, and cOidChg to 0 and set poid
to the SyncID.
If the ONF_CHANGED has been set, then the
object has been changed in the device store. For this case set cOidDel to
0, and cOidChg to 1 and set poid to the SyncID.
Phase 3. This phase can be called after
either phase 1 or 2. The provider can tell if it is in this phase because ONF_CLEAR_CHANGE
is set. This phase is called to let you determine if the object has been
changed on the device since it was sent up to the desktop. You can determine if
the object has changed, by having your device app set a dirty flag or flags
whenever it modifies an existing object. The ObjectNotify function should
clear this flag in phase 1 and 2. If the dirty flag is set in phase 3 then we
need to resync the object and should return true, otherwise we should return
false even though the object belongs to this provider. For this most of
the parameters in OBJNOTIFY are all set to zero except the flags. Also the OBJNOTIFY::oidObject
parameter is not the CEOID of the object but the SyncID which you passed back
the poid parameter from phase 1 or 2. Also the parameter OBJNOTIFY::oidInfo.infRecord.oidParent
is not valid, so you need to query the object to figure out it's parent to
determine if the provider owns the object.
| |
Phase 1 |
Phase 2 |
Phase 3 |
ONF_DELETED |
Not Present |
Present |
Not Present |
ONF_CHANGED |
Not Present |
Present |
Not Present |
ONF_CLEAR_CHANGE |
Not Present |
Not Present |
Present |
| return value meaning |
True: Claim Object
False: Don't Claim Object |
True: Claim Object
False: Don't Claim Object |
True: Object has been modified
since last sync
False: Object has not been modified since
last sync. |
| cOidDel |
If changed return 0.
if unchanged return 1. |
if deleted return 1.
else return 0 |
if changed since last sync
return 0, else return 1. |
cOidChg |
If changed return 1.
if unchanged return 0. |
If changed return 1
else return 0 |
if changed since last sync
return 1, else return 0. |
oidInfo.infRecord.oidParent |
valid |
valid |
not valid |
| |
|
|
|
| |
|
|
|
How do I implement a Provider
which will sync between two host computers?
The object that you would like to sync should be
have a defined flags member with two dirty bits, one for each host
computer. ActiveSync automatically keeps track of which computer you are
syncing two with the use of two partner bits. The device app should always
set both bits to dirty when an object is created or modified. The device
provider should clear the corresponding partner bit when an object is
synced. In the case, when an object is created on the desktop and sent to
the device, the partner bit should be cleared for the currently connected host,
and the other partner bit should be set by the provider.
In the function FindObjects what
do I set the lpbVolumeID parameter to. Microsoft
has inserted an unbelievably terrible bug into the FindObjects function.
If you set the parameter lpbVolumeID parameter to anything but 0, an Access
Violation error may occur in the desktop provider, right before ObjectToBytes()
gets called. Note that the StockPor sample sets this parameter to the GUID
of the database volume, so perhaps under some cases this works. This
bug is completely unintuitive, because it shows up so far down the stream that
it is near impossible to track down. Microsoft has no mention of this bug
in its knowledge base, which is absurd, since it has to know about it.
Daycounter specializes in contract
electronics design. Do you need some help on your project? Contact
us to get a quote.
[Employment]
[Downloads][Articles]
[Contact Us]
Call us now at
801-523-8444 - 136 E. Steep Mountain Dr. Draper, UT 84020
© Copyright 2004 Daycounter, Inc. All rights Reserved.
|