EDB

 

大家都知道903固件之前的固件之间是没法备份和还原短信、通话记录等数据库的,原因在于每个版本的有关EDB数据库读写模块不兼容亦或EDB文件不兼容。本人有保存历史短信的爱好,为了保证每次升级系统均能保证短信不丢失,于是诞生了该软件:短信合并。时间证明一切,在实际使用中,效果相当不错(别看广告,看疗效)!
     如今新的固件自带备份和还原功能,该软件结束了历史使命。本人不才,在这里,我简单就前段时间有关此方面的东西做一个小小的总结,权当抛砖引玉吧。

    注意,本文并不站在教程的角度说话,并不指望作为一个教程,三下五除二就教会大家什么东西,严格来说,本文和M8SDK关系不大,只是决定和大家一起分享一下开发心得,也许你遇到问题时候,可以给你一个灵感。

    本章内容以"短信合并"软件为核心,主要分为3大块:

    1、有关EDB数据库

    2、sms.edb 文件格式分析

    3、EDB读写模块:SK_EDB_CLASS

一、有关EDB数据库

严格来说,EDB数据库并不能算是数据库,它无法使用通常所说的数据库结构化查询语言(SQL),只能通过一系列的API函数和结构体来对其访问,进行读写,比较复杂。而实质上,它只是一种结构化复合存储文件,换句话说,就仅仅是一种自定义的文件格式,只不过该作者是微软。可恶的是,EDB只能用在WinCE核心的平台上,你想在PC上直接操作EDB数据库,据我所知,目前没有任何办法。因此,对于该方面的资料,并非很多,如果你要使用EDB数据库,那只能靠网络上少量的信息加上MSDN 的说明,慢慢探索,除非有人替你封装好一切接口。

有关EDB的更多信息和说明,放狗出去,你会得到很多类似的、重复的,我并不打算在这里重复被人说了100000000+++遍的东西。

二、sms.edb 文件格式分析
第一点说完EDB 的不是,很不幸,M8上面的短信数据库偏偏采用了这种数据库。当初为了能够将短信从数据库中读取出来,又不想查阅(厚厚的?)MSDN,只好硬着头皮分析了sms.edb文件,依靠脑海中依稀的模糊的记忆,基本弄清楚了里面的秘密。
1、
既然称为数据库,那么应该有表吧?本想尝试通过MS提供的一系列API来获取信息,    最后决定放弃(有捷径不走是傻蛋),查阅相关资料后,直接用记事本打开sms.edb文件搜索 __SysObjects



看到没?有没有?有没有(要用侯总的语气读)?后面跟着的就是我们要的表名了——smsDB,记下来,有用的。

2、
好了,有了表名,那么字段名呢?呃。。。 就刚才那个地方旁边,你看。每个smsDB 后面的那个刺眼的E文就是我们要的字段名了,当然,这字段名,我们并不是非得要用。看实际用途,在本文中,必须用到。
3、
有了字段名了,那这个字段类型呢?这个没办法通过记事本来获取了,需要靠程序的帮助,我在后面会提到这一点。
4、
分析出来的sms.edb数据库信息,权当参考。

 


三、EDB读写模块SK_EDB_CLASS
要想提高代码可重用性,对一些函数进行封装是必须的。EDB数据库的访问和读写是一种体力活,在各种操作上显得复杂罗嗦,但是没办法,这是EDB文件格式以及提供的API带来的弊端。如下是MSDN描述的一部分的访问API
1)创建数据库
CeMountDBVol( )//创建卷
CeCreateDatabaseWithProps()//卷创建成功后创建EDB
CeCreateSession()//EDB创建成功后创建session,用于打开EDB
CeOpenDatabaseInSession()//打开EDB
创建EDB时前还要创建一个CEDBASEINFOEX对象,这个对象用于创建EDB中的info,用于设定EDB。在打开时还要维护一个全局的HANDLE,在以后的操作中是要使用的.
2)读,写,删操作
CeSeekDatabaseEx();//定位所要找的数据
CeReadRecordPropsEx();//读出定位的数据,创建一个CEPROPVAL对象,将所要定位数据的条件传给这个结构。
CeWriteRecordProps();//数据写入EDB,创建一个CEPROPVAL对象,或对象数组,将所要写入的数据传给这些对象。
CeSeekDatabaseEx();//定位要删除的数据
CeDeleteRecord();//删除定位的数据
CeWriteRecordProps();//数据写入新数据到EDB覆盖原数据
CloseHandle();
3)其他操作
CeFindFirstDatabaseExCeFindNextDatabase
//
列举数据库

CeOidGetInfoEx
//
获得数据库的信息

CeFreeNotification //释放通告消息结构体
CeGetDBInformationByHandle
//
获得打开的数据库所使用的句柄:

CeSetDatabaseInfoEx //设置数据库的各种参数
CeEnumDBVolumes //列举所有转载的数据库卷并返回卷名
上面列举的API只是其中一部分常用的,看着这些零零散散的函数,就让人有一种把作者痛扁一顿的冲动。。。。。。
至此,一定会有人问:既然操作这么复杂,为什么网络上没有人做好封装或者有实用的封装呢?我要说的是,EDB数据库很特殊,无法使用SQL语句。如何特殊法就放狗出去看看吧。
为了能够更好的、更方便地操作EDB数据库,本人不才,用c++做了一个简单的封装,提供了一个实用的操作类。该类解决了网络上所有封装类都存在一个问题,就是开发者无法直接使用这个类,需要对它做一定的修改,其中工作量不小。
具体来说,由于EDB数据库读写访问和字段类型结合非常紧密,需要开发者很清楚每个字段的类型,当遇到他人创建的数据库时候,就必须花费额外的时间去分析字段类型,比如此次分析sms.edb,加上EDB数据库只能在WinCE核心的系统上运行,所以在一定程度上,很少有人会去封装有关EDB 的操作类,即使封装,也无法做到通用。当然,该类同样无法做到完全通用,只是将通用性提高到一个目前来说最佳的状态。
废话不说了,来看封装吧。

复制内容到剪贴板
代码:
#ifndef DB_h__
#define DB_h__
// 定义使用 EDB 数据库,而非CEDB, 见 windbase.h 相关说明
#ifndef EDB
#define EDB 1
#endif
//仅用于标识参数输入输出属性
#define IN
#define OUT
// EDB 专用类型,来自 windbase.h
#ifndef CEVT_AUTO_I4
#define CEVT_AUTO_I4 (102)
#endif
// 自定义未知类型
#ifndef CEVT_UNKNOW
#define CEVT_UNKNOW (0)
#endif
//
// 【注意】用户必须改变的数据定义,根据实际数据库修改这些值
// 1、列数定义 MAX_COL ,用于写数据时候防止越界,最多16。
// 2、 FieldType 数组,必须按顺序定义字段类型,用于在向新建的空数据库中添加记录时,转换数据格式。
// 如果读取过任何一条有效记录,则这些顺序会自动修正,自定义的不再有效。
// 3、写入数据库时候:
// Add 和 Modify 函数的 szRecord 数组第一维必须保证有足够的个数,即每一行对应数据库每一列!
// 4、本类的使用请看例子
//
#define MAX_COL 10 //sms.edb 中smsDB 表有10个字段,注意edb数据库最多可定义16个字段!
//字段类型(按顺序)
static WORD FieldType[16]=
{
CEVT_LPWSTR,
CEVT_UI4,
CEVT_BOOL,
CEVT_UI4,
CEVT_FILETIME,
CEVT_LPWSTR,
CEVT_FILETIME,
CEVT_LPWSTR,
CEVT_LPWSTR,
CEVT_AUTO_I4,
CEVT_UNKNOW,
CEVT_UNKNOW,
CEVT_UNKNOW,
CEVT_UNKNOW
};
//
// SkCeDataBase 类定义
//
class SkCeDataBase
{
//属性
private:
CEGUID m_ceGuid; //存储数据库文件卷标识(GUID)PCEGUID m_pCeGuid;
CEOID m_ceOid; //DWORD型 存储数据库对象标识和记录对象标识,惟一标识数据库的ID,CeCreateDatabaseEx()返回
HANDLE m_hDB; //存储数据库句柄,CeOpenDatabaseEx()返回
HANDLE m_hSession; //
//DWORD m_RecCount; //记录数

TCHAR *m_DataBuffer[16]; //这里用于保存读出来的记录,字段最多16个,一个字段将作为一行保存。
TCHAR *m_DataBase; //数据库路径及卷文件名称
TCHAR *m_TableName; //操作的数据表名称
BOOL bFieldTypeIsReSet; //是否调整过字段类型数组的顺序
public:
SkCeDataBase(void);
~SkCeDataBase(void);

public:
//属性操作
void SetDataBase(const TCHAR *szDbFileName); //设置数据库文件
void SetDataBaseTable(const TCHAR *szTableName); //设置要操作的表名

BOOL Open(void); //打开数据库
BOOL Create( IN DWORD dwIndex); //创建数据库
BOOL Close(void); //关闭数据库
BOOL UnmountDBVol(void); //卸载数据库卷,仅在打开数据库完全失败时使用
//读操作
DWORD Seek(IN DWORD dwID); //移动记录指针
INT GetRecordCount(); //返回表内的记录总数
INT GetFiledsCount(); //返回表内字段个数(该函数尽量少用,GetRecord 本身也会返回字段个数)

//返回指定ID的记录,并返回字段个数,存放于m_DataBuffer
//可通过GetFieldValue(DWORD dwCol)来读取指定字段的值
//如果首次读取记录成功,则该函数会改变 FieldType 的顺序定义。
BOOL GetRecord( IN DWORD dwRecordID,OUT DWORD &dwFieldsCount);
CONST TCHAR *GetFieldValue(DWORD dwCol);//返回该字段值,所有字段均以字符串返回
WORD GetFieldType(DWORD dwCol);//返回该字段的类型

//写操作
BOOL Add( IN CONST TCHAR **szRecord); //添加一条记录
BOOL Delete( IN DWORD dwIndex); //删除某条记录
BOOL Modify( IN DWORD dwID,IN CONST TCHAR **szRecord); //修改某条记录
void ReSetBuffer(); //清理缓存
};
//
#endif // DB_h__

万年不变思路就是:打开,读取、修改、删除,关闭。围绕这五点,封装类提供的接口简单实用。具体的代码我就不解释了,文中的注释足够将我要说的话表达出来。

四、测试例子:
    为了更好地体现该类的优势,如下是测试代码:

复制内容到剪贴板

代码:
1、新建M8SDK工程。
2、将SK_EDB_CLASS文件夹的所有文件加入工程。
3、在工程中包含头文件(或LIB)
#include "DB.h"
//#pragma comment(lib,"coredll.lib") //添加lib
4、读写例子(该例子将 sms_old.edb 数据读出,写入 sms_new.edb )
TCHAR *buf[MAX_COL];
::SkCeDataBase db,db2;
db.SetDataBase(_T("/Disk/Test/sms_old.edb"));
db.SetDataBaseTable(_T("smsDB"));
db2.SetDataBase(_T("/Disk/Test/sms_new.edb"));
db2.SetDataBaseTable(_T("smsDB"));
if(!db.Open()||!db2.Open())
{
MzMessageBoxEx(m_hWnd, L"打开数据库失败!", m_btn.GetText().C_Str(), MB_OK, false);
return;
}
::MzBeginWaitDlg(m_hWnd);
int recnum=0;
DWORD fc=0;

//初始化buf
for(int i=0;i<MAX_COL;i++)
buf=new TCHAR[1024];

recnum=db.GetRecordCount();
for(int i=0;i<recnum;i++)
{
if(db.GetRecord(i,fc))
{
for(int k=0;k<fc-1;k++)
{
memset(buf[k],'/0',1024*sizeof(TCHAR));
_tcscpy(buf[k],db.GetFieldValue(k)) ;

//调试
//char tmp[1024];
//wcstombs(tmp,buf[k],1024);
//DB(LOG(("F[%d][%d]%s"),i,k,tmp));
}//for

//写入新数据库
db2.Add((const TCHAR**)buf);
}//if
}//for
//清场······
db.Close();
db2.Close();
::MzEndWaitDlg();
MzMessageBoxEx(m_hWnd, L"Read DB OK!", m_btn.GetText().C_Str(), MB_OK, false);
//释放buf
for(int i=0;i<MAX_COL;i++)
delete buf;

5、注意,如果读写其他数据库,请参考 DB.h 中的说明。


五、说明
1
、本类是开源的,任何人都可以无条件使用,但是请保留原有的一切注释和作者信息,并请将修改完善后的代码抄送一份给原作者。

2、封装类并不完善,存在的一些问题,请多多包涵。
3
、本文仅以sms.edb为例子,其他edb数据库都是一样的操作。目的在于为大家提供一个实用的封装,巩固一下OO概念,对于某些开发者来说,指出一条实际的思路。

4
、言语之间稍显不顺,权当抛砖引玉、点到即止。

http://bbs.meizu.com/thread-906265-1-1.html

 

 

一、WM5以前的系统中一般都是使用的CEDB数据库,EDB是WM5中的新特性之一。为了改善应用程序的性能和长期可移植性,CEDB 已经被 EDB 所取代。EDB 利用了 SQL Mobile 使用的存储子系统,并且提供了明显优于 CEDB 的性能(尤其是在与持久存储区一起使用时)。因为 CEDB 提供了与 EDB 完全相同的函数集 ,所有函数都具有相同的名称和参数列表。但是EDB中也包含了CEDB中所没有函数,并且创建方法也不相同了,要比CEDB复杂。

二、EDB数据库的创建和基本操作
1.创建数据库卷:CeMountDBVol( );//创建卷
CeCreateDatabaseWithProps();//卷创建成功后创建EDB
CeCreateSession();//EDB创建成功后创建session,用于打开EDB
CeOpenDatabaseInSession();//打开EDB
创建EDB时前还要创建一个CEDBASEINFOEX对象,这个对象用于创建EDB中的info,用于设定EDB。
在打开时还要维护一个全局的HANDLE,在以后的操作中是要使用的.
2.对数据库进行读,写,删除的操作
CeSeekDatabaseEx();//定位所要找的数据
CeReadRecordPropsEx();//读出定位的数据
创建一个CEPROPVAL对象,将所要定位数据的条件传给这个结构。
CeWriteRecordProps();//数据写入EDB
创建一个CEPROPVAL对象,或对象数组,将所要写入的数据传给这些对象。
CeSeekDatabaseEx();//定位要删除的数据
CeDeleteRecord();//删除定位的数据
CeWriteRecordProps();//数据写入新数据到EDB覆盖原数据
CloseHandle(打开时的句柄);
3.其他的一些数据库操作函数

列举数据库: CeFindFirstDatabaseEx和CeFindNextDatabase

获得数据库的信息:CeOidGetInfoEx

//在EDB的消息函数中用到


释放通告消息结构体:CeFreeNotification
将数据缓冲到flash上:CeFreeNotification
获得打开的数据库所使用的句柄:CeGetDBInformationByHandle

设置数据库的各种参数:CeSetDatabaseInfoEx

列举所有转载的数据库卷并返回卷名:CeEnumDBVolumes

http://blog.csdn.net/sc_valentine21/archive/2008/11/07/3245427.aspx

http://blog.csdn.net/ciahi/archive/2008/10/29/3177911.aspx

http://www.cnblogs.com/ty--90/archive/2008/02/20/1075299.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值