HREPLITEM 和HREPLFLD
(这俩句柄虽然都以H开头,但往往会通过强制类型转换转换为指针,另外注意,这俩句柄都只跟桌面端provider相关)
对于ActiveSync provider来讲,HREPLITEM是一个重要的数据类型,每个句柄唯一标示一个对象(通常就是一个指向item对象的指针。ActiveSync manager可以把这个句柄作为参数传递给IReplStore或IReplObjHandler中的方法,来了解关于某对象的一些事情。HREPLITEM对象只能被IReplStore::FindNextItem(或IReplStore::FindFirstItem)和IReplStore:: BytesToObject所创建。前者是在枚举同步对象时调用,后者我想是在读取需要同步的文件时,附带产生的(因为ActiveSync manager在作真正的同步时,假如根据两个文件的最后日期判定了要保留其中某端(桌面端或设备端)的一个,那么就会从存储中读取该文件,这时需要一个BytesToObject的过程,同理,在另一端写入时需要一个相反的过程,这些都是由两端的ActiveSync manager调度,我们只能提供manager调用的回调函数)。注意:ActiveSync provider必须可以根据HREPLITEM中的信息判断是否两个句柄来自同一对象,或者能够对来自不同对象的俩句柄按唯一标识符进行排序,因为ActiveSync manager在内部维护了一个句柄表,能够排序才可以进行二进制检索,比如把这个表放到一个数组或map中。
ActiveSync Provider也支持对某类型的同步对象有选择地同步,这类似于一个过滤器,比如outlook同步组件允许只对未来三周内的约会进行同步,这些工作在IReplStore::IsItemReplicated中实现,在这个函数中检查HREPLITEM句柄关联的同步对象,判断是否符合过滤标准,如果不复制这个对象,返回false,如果这样一个对象存在于设备中,ActiveSync manager会发布一条命令给设备,删除它,而桌面端的那个对象不会被碰到。
HREPLFLD指向一个表示Folder对象的指针,如果仅支持一种对象类型的同步,那么在IReplStore::GetFolderInfo中就可以只创建一个文件夹实例。(多个类型就得创建多个文件夹对象,用于存放不同的对象类型)。
ActiveSync manager对HREPLITEM和HREPLFLD一无所知,当需要删除内存中的句柄时,ActiveSync manager调用IReplStore::FreeObject,让ActiveSync provider来释放句柄中用到的资源(在ASPSample例子中,HREPLITEM对应的一个CReplItem对象会占用268个字节)。如果需要从表示同一个对象的一个句柄到另一个句柄复制资源,ActiveSync provider需要每次都确认这个句柄还在标识一个有效的对象(比如未被删除的文件),因此需要调用IReplStore::IsValidObject。HREPLITEM或HREPLFLD句柄所代表的对象可以被存储在一个叫做repl.dat的文件中。这个文件是ActiveSync manager为每个设备创建并维护的。(dat文件只存在于设备端?还是桌面端也有?)一个ActiveSync provider实现IReplStore:: ObjectToBytes来转换一个HREPLITEM或HREPLFLD为一串字节,用IReplStore:: BytesToObject把一系列字节转换回句柄。当设备被连接上时,repl.dat立刻被读取(设备端的provider程序规定了repl.dat中都存放了哪些对象类型和具体对象),前面提到的句柄:HREPLITEM和HREPLFLD会被创建。当设备被连接到桌面端时,无论何时,只要被句柄所代表的对象(比如说文件sample.txt)发生了变化。ActiveSync manager都会把这些句柄存入到repl.dat(若句柄代表是同一对象呢?那先前那个句柄怎么办?删除?)
ActiveSync manager为不同设备维护着不同的data文件,它保证了从连接设备上提取正确的数据文件...
存储信息和存储识别
对于ActiveSync提供者来说,断定现在ActiveSync需要作同步的那个store和上次最后一次同步中的那个store是否是同一个(哦,同一设备的store也许并不同吗?),如果不同,桌面端和设备端对象之间的映射需要使用一个类似于“联合/丢弃”(Combine/Discard)的处理来重新建立。联合意味着把所有桌面端和设备端所有对象联合到一起;丢弃意味着丢弃设备中的对象并用桌面端对象来替代它们。联合也许导致复制那些ActiveSync provider将有能力去删除的那些对象(自己指定的那些同步监视对象,比如simple.txt),也许就是这个原因导致使用ActiveSync缺省的文件同步provider产生问题,每次重新建立映射关系,先前那个映射关系作废
ActiveSync manager调用IReplStore::GetStoreInfo函数来返回一个store的相关信息,包括一个store的storeID(ProgID),这个storeID可以是任意大小,会被存入repl.dat文件中,这个ID在与设备的连接建立之后被正确的读出,ActiveSync manager把这个ID传递给IReplStore::CompareStoreIDs函数,ActiveSync Provider必须能够断定当前这个store ID和从repl.dat中读取的那个是否匹配
STROEINFO结构被传递给IReplStore::GetStoreInfo函数,这个结构被定义如下:
Typedef struct tagStoreInfo
{
UINT cbStruct;
UINT uFlags
TCHAR szProgId[256];
TCHAR szStoreDesc[200];
UINT uTimerRes;
UINT cbMaxStoreId;
UINT cbStoreId;
LPBYTE lpbStoreId
}STOREINO,*PSTOREINFO;
如果ActiveSync provider不支持实时地通知删除或改变的能力,它需要正确设置STOREINFO::uTimerRes。如果STOREINFO::uTimerRes在毫秒级设置为非零值,则ActiveSync manager每间隔这些时间,自动启动一次store中对象的枚举。如果STOREINFO::uTimerRes被设置为-1,ActiveSync Manager只是当Activesync状态窗口被激活时或者有一个同步开始之后,才会启动对象枚举。
为了取回可变size的storeID,ActiveSync manager首先把STOREINFO::cbMaxStoreID设置为0来调用IReplStore::GetStoreInfo函数,ActiveSync provider将通过STOREINFO::cbStoreID的值来设置storeId所需的大小,并返回E_OUTOFMEMORY,ActiveSync service manager会分配内存并传递指向该块内存的指针给STOREINOF::lpbStoreId,这块内存用于ActiveSync provider来存放这个存储id。
初始化和结束
IReplStore::Initialize确保了要同步的文件,存在并且是被打开了(比如ASPSample中的sample.txt文件),这个需要同步的文件的名字被存放在注册表中,这个文件名在注册表中的位置依赖于ActiveSync provider是为了连接还是为了选择设备。注意:在移动设备文件夹中,也许有多个设备备选,如果没有设备被连接,用户可以选择其中任何一个设备并设置ActiveSync选项,这也许会导致ActiveSync provider被初始化。如果一个设备被连接,provider将总是被这个连接的设备来初始化。如果provider是为了选中设备而被初始化的,那么传入IReplStore::Initialize函数的位标识符ISF_SELECTED_DEVICE被设置。为了获得被选中的设备的注册表键,用IReplNotify::QueryDevice(QDC_SEL_DEVICE_KEY,&hKey),为了获得断开连接的设备的注册表键,使用IReplNotify::QueryDevice(QDC_CON_DEVICE_KEY, &hKey),如果设备是远程连接的,provider会避免使用任何阻塞的用户界面,比如messageBox之类。Provider会在桌面端用一些缺省动作来替代提示用户的行为,这是因为当一个设备通过远程连接时,一个用户也许难以及时相应用户界面
通常情况下,IReplStore::Initialize在接口IReplStore中最先被ActiveSync manager所调用,也有另外一些情况,其他方法会先被调用,特别是当用户需要改变同步选项用于一个断开的设备时,下面是有可能在Initialize()之前调用的函数
你首先需要确定上述函数能够在IReplStore::Initialize被调用之前可以正常工作
ActiveSync manager调用IReplStore::GetObjTypeUIData来返回指定类型对象的数据,用于在ActiveSync状态窗口中显示。一个ActiveSync provider必须正确地设置给定的OBJUIDATA数据结构,这个typedef struct tagObjUIData
{
UINT cbStruct; // 结构大小
HICON hIconLarge; //列表视中使用的大图标
HICON hIconSmall; //列表视中使用的小图标
char szName[ MAX_PATH ]; // 名称栏显示文本
char szSyncText[ MAX_PATH ]; // 同步拷入一栏显示的文本
char szTypeText[ 80 ]; // 类型栏显示的文本
char szPlTypeText[ 80 ]; // Plural form of text displayed in the "Type"
} OBJUIDATA, *POBJUIDATA;结构定义如下:
当设备断开时,ActiveSync provider不会自动卸载,一般是ActiveSync manager最后调用一下IReplStore::Release函数,如果provider的引用计数达到了0,这个接口就会被删除。ActiveSync manager通常在它试图调用IReplStore::Release之前,用RSC_RELEASE参数调用IReplStore::ReportStatus,一个ActiveSync provider能够确认在RSC_RELEASE接受之后,所有的外部接口都已经被释放。这将确定当ActiveSync manager释放这个存储时引用计数已经达到了0。
所有桌面端对象的枚举
一个ActiveSync provider必须能够列举给定文件夹中的所有对象。IReplStore:: FindFirstItem通常在列举开始的时候被调用,ActiveSync provider能够初始化列举所需的任何事情。在例子代码中,它在内存映射文件中为所有存在对象搞了一个快照,并调用了IReplStore::FindNextItem。当所有对象的HREPLITEM返回时,代码设置了*pfExist为false,这结束了枚举,然后IReplStore::FindItemClose会被调用(释放资源)
如果ActiveSync provider是一个过滤器同步对象,它能够选择返回一个在列举中(FindFirst或next函数)通过了过滤器的对象。一旦一个对象在同步时被过滤器过滤出局,在桌面端看上去象是一个被删除的对象,因为桌面端的枚举不再返回这个对象。ActiveSync manager认定它已经被桌面端删除,并发布一条命令让设备端也去删除相应的对象。对于需要和多台设备相连的设备而言,这不是期待的行为,如果每台pc上的过滤器都不相同,PC端的对象有可能被删除,因为设备端对象被另一个桌面PC的过滤器过滤出局时已经被删掉了。为了防止这一点,ActiveSync provider通常在列举过程中,返回每个对象,并在IReplStore:: IsItemReplicated来实现过滤器
如果列举需要花费一段时间才能完成,ActiveSync provider能够调用IReplNotify:: SetStatusText来显示文本,让用户来了解列举的进度。
如果ActiveSync provider想用高效的方法来探测是否文件夹中的对象有改动或者被删除了,他应该实现IReplStore::IsFolderChanged。当没有对象被改变和删除时设置*pfChanged为false,,ActiveSync manager在列举对象时会忽略该文件夹。如果ActiveSync provider有实时探测改变和删除的能力,它将除连接设备后第一次调用IReplStore::IsFolderChanged外,每次都设置pfChanged为false。当IReplStore::IsFolderChanged在连接上设备之后,第一次被调用时,最重要的事情是让ActiveSync manager知道自从最后一次更新之后,有可能一个或多个对象已经被改变或删除了。比较典型的是:一个ActiveSync provider在IReplStore:: GetFolderInfo中为每个文件夹设置了一个标记?,并且,在IReplStore::IsFolderChanged中,如果这个标记已经被设置,那么就设置*pfChanged为false
对象列举过程的演示流程图在同步之间探测改变或删除
ActiveSync manager通过比较由当前列举返回的句柄链表和从repl.dat中加载的句柄表来自动探测变化和删除。内部,在列举开始之前,ActiveSync manager在它的句柄表中对每个handle都作了一个位标记,每次ActiveSync providers通过IReplStore::FIndFirstItem或者IReplStore::FindNextItem返回一个新句柄的时候,ActiveSync manager尝试在自己内部的句柄表中通过一个二进制搜索找出代表同一个对象的一个句柄(代表这个对象的句柄在这个表中唯一吗?从后面来看,唯一)。如果没有找到匹配的句柄,一个新对象就会在桌面端的store里面被创建,ActiveSync manager清除了表中这个句柄的这一“位”,并调用IReplStore::IsItemChanged来判断这个对象自从最后一次同步以来是否发生了变化,如果是,ActiveSync manager调用IReplStore::CopyObject从列举返回的句柄复制数据到它保存的那些句柄。然后ActiveSync manager调用IReplStore::IsItemReplicated来判断是否将它发送到设备端,在列举结束后,所有ActiveSync manager内部表中的那些标记为代表那些没有被列举返回的对象的句柄,将会在桌面端的Store中被删除。
IReplNotify
这是一个由ActiveSync manager实现的接口,任何ActiveSync provider能够使用这个接口中定义的方法。这些方法是:
OnItemNotify通知ActiveSync manager发生在一个对象上的任何改变或删除,也可以通知ActiveSync manager关闭ActiveSync provider。这使得manager能够实时地为provider自动更新同步状态(This enables the manager to update the synchronization status for the service provider automatically in real time)如果service provider没有能力实时探测对象变化或删除,它可以忽略这个方法
SetStatusText在ActiveSync 状态窗口的状态栏、移动设备窗口以及其他任何同步状态可以被现实的地方中设置显示文本。
GetWindow返回任何模式对话框或消息对话框的父窗口的句柄
QueryDevice返回连接的或被选中的设备的信息
改变或删除的实时报告
如果一个ActiveSync provider具备这样一种能力:能够在桌面端store发生改变和删除的时候立刻就能探测到,那么它可以调用IReplNotify::OnItemNotify,以便让ActiveSync manager立刻知道,如果对象只是简单地被创建或者修改,ActiveSync provider传递RNC_MODIFIED或者RNC_CREATED,如果对象被删除则传递RNC_DELETED,它也传递给这个对象,这允许ActiveSync manager在自己的表中搜索,并找出这个句柄对应了哪个设备对象。(aspsample例子中没有用到这个函数)略…
发送和接收对象
同步可以由用户来初始化或者在数据过时(作为上述IReplNotify::OnItemNotify调用的实现)的时候自动进行
IReplObjHandler是一个COM接口用于把一个对象转换为一个字节流。这属于序列化。转换一个字节流为一个对象,称为反序列化。ActiveSync管理器也使用这个接口从store中删除一个对象。IReplObjHandler接口在桌面和设备端都被实现,因此很多代码可以共享。这个接口的一个实例为每个对象类型创建一个。(我们不需要考虑这么多,因为我们只同步一种类型,就是CReplItem,存放需要同步文件的全路径和最后修改时间)
并没有限制或规范来指导一个对象如何被序列化的。ActiveSync manager从不知道字节的格式。一个ActiveSync provider能够序列化对象成为许多字节并能够把这些字节组织成一些包。ActiveSync manager保证这些将被发送到设备的包,以一种明确的和它们发送到ActiveSync manager相同的数量和顺序被发送出去。.
下面的方法经常被顺序调用,作用到一个对象被序列化为一串字节的时候。REPLSETUP结构被传入IReplObjHandler::Setup函数,这个结构被定义如下
在这个结构中ActiveSync provider需要下面的成员
fRead被设置为TRUE用于从一个桌面的store来读取一个对象,FALSE用于写入一个对象到桌面store中
dwFlags是一个bit标记的集合,用于描述对象序列化和反序列化
hFolder是一个文件夹对象的句柄
hItem是对象的句柄,需要被序列化的对象。ActiveSync将会使用包含在这个句柄中的信息来标记对象并把它转换为字节包
所有其他成员对ActiveSync manager来说都是内部的,都不会被改变。
从一个设备接受一个对象的过程和发送一个对象很类似。在数据包从设备到达之后,IReplObjHandler接口方法将会被调用来让ActiveSync provider转换这些包恢复成一个对象。
ActiveSync provider必须打包数据包并且创建一个对象。一个代表对象的新的HREPLITEM必须在REPLSETUP::hItem中被创建和设置,费解...
The ActiveSync provider must take the data packets and create an object.A new HREPLITEM representing the object must be created and set in REPLSETUP::hItem。
RERR_DISCARD: ActiveSync provider希望在变化被同步后立刻去删除设备对象,ActiveSync manager将会发送一条命令给设备对象去删除对应的对象