胡兆峰,史高揚
(西安電子科技大學電子信息攻防對抗與仿真重點實驗室,陜西西安 710071)
在編程過程中,數(shù)據(jù)庫毫無疑問是一個不可忽視的部分,數(shù)據(jù)庫可以完成對數(shù)據(jù)的存儲和檢索,可以方便人們的工作,提高工作效率。然而,因為嵌入式智能設備的資源有限,對數(shù)據(jù)庫的使用有較大限制。在Windows CE操作系統(tǒng)中,提供了一套自帶的數(shù)據(jù)庫。該數(shù)據(jù)庫具有數(shù)據(jù)結(jié)構簡單,操作靈活等特性,在存儲數(shù)量較小的情況下可以方便使用[1]。
Windows CE操作系統(tǒng)專門供掌上電腦和嵌入式設備使用的系統(tǒng)環(huán)境。Windows CE被設計成針對小型設備的通用操作系統(tǒng)。與其它的微軟Windows操作系統(tǒng)不同,Windows CE并不代表一個采用相同標準對所有平臺都適用的軟件。為了足夠靈活以適應廣泛產(chǎn)品,Windows CE可采用不同的標準模式,即它能從一系列軟件模式中做出選擇,從而使產(chǎn)品得到定制。通過選擇,Windows CE能達到系統(tǒng)要求的最小模式,從而減少存儲腳本和操作系統(tǒng)的運行。
從Windows CE 5.0開始,Windows CE自帶數(shù)據(jù)庫系統(tǒng)就包括了 EDB和 CEDB,CEDB是 Windows CE 5.0之前就支持的數(shù)據(jù)庫系統(tǒng),EDB是 Windows CE5.0之后新增的數(shù)據(jù)庫系統(tǒng),EDB可以理解成對CEDB的升級。EDB是一個基于SQL Server CE的嵌入式數(shù)據(jù)庫引擎,為基于Windows CE的應用程序提供核心功能,通過使用EDB,開發(fā)者能夠創(chuàng)建一個對象存儲,成為數(shù)據(jù)庫卷,其中可以包含多個數(shù)據(jù)庫,該數(shù)據(jù)庫卷是基于文件的,因此可以被復制和移動[2]。
在Windows CE系統(tǒng)中,學生信息存儲的數(shù)據(jù)都是以EDB形式存儲的。本文結(jié)合學生信息管理系統(tǒng)的實例介紹了EDB數(shù)據(jù)庫的設計與應用開發(fā)。建立一個EDB數(shù)據(jù)庫的基本思路是:首先加載數(shù)據(jù)庫卷,然后在加載的數(shù)據(jù)庫卷中建立數(shù)據(jù)庫,數(shù)據(jù)庫建立好后再打開數(shù)據(jù)庫以對數(shù)據(jù)庫進行讀寫等操作[3]。
建立數(shù)據(jù)庫之前應加載數(shù)據(jù)庫卷,可以使用CeMountDBVol函數(shù)來實現(xiàn),其函數(shù)定義如下:
BOOL CeMountDBVolEx(PCEGUID pGuid,LPWSTR lpwszDBVol,CEVOLUMEOPTIONS*poptions,DWORD dwFlags);
參數(shù)pGuid用來標識數(shù)據(jù)庫文件的GUID,參數(shù)lpwszDBVol表示裝備數(shù)據(jù)庫卷的文件名稱,參數(shù)poptions設置新數(shù)據(jù)庫卷的行為和性能。如果此數(shù)據(jù)庫卷已經(jīng)被建立,此參數(shù)將被忽略參數(shù)poptions值為NULL,系統(tǒng)將設置默認的數(shù)據(jù)庫卷行為和性能,參數(shù)dwFlag表示數(shù)據(jù)庫卷被裝載的方式。
加載數(shù)據(jù)庫卷后,應該穿件數(shù)據(jù)庫,此時可以用CeCreateDatabaseEx來創(chuàng)建數(shù)據(jù)庫,定義如下:
CEOID CeCreateDatabaseWithProps(PCEGUID pGuid,CEDBASEINFOEX*pInfo,
DWORD cProps,CEPROPSPEC*prgProps);
參數(shù)pGuid指向已裝配的數(shù)據(jù)庫卷標識,參數(shù)pInfo指向CEDBASEINFOEX結(jié)構指針,用來藐視被創(chuàng)建的數(shù)據(jù)庫的名稱、類型、排序方式以及數(shù)據(jù)庫特征等信息。
在數(shù)據(jù)庫建立好之后,需要對數(shù)據(jù)庫進行操作,如添加記錄,刪除記錄,修改記錄,查找記錄。實現(xiàn)以上操作的方法定義如下:
(1)讀取記錄。
CEOID CEReadRecordPropsEx(HANDLE hDatabase,DWORD dwFlags,LPWORD lpcPropID,CEPROPID*prgPropID,LPBYTE lplpbuffer,LPDWORD lpcbBuffer,HANDLE hHeap);
參數(shù)dwFlags為讀取標識,參數(shù)lpcPropID表示參數(shù)rgPropID指向CEPROPID結(jié)構的數(shù)量。參數(shù)lplp-Buffer表示緩沖區(qū),用于存儲讀取到的記錄信息。
(2)寫記錄。
CEOID CeWriteRocordProps(HANDLE hDatabase,CEOID oidRecord,WORD cPropsID,CEPROPVAL*prg-PropVal);
參數(shù)oidRecord表示要寫入記錄的標識,如果參數(shù)是0,表示添加一條新紀錄,否則表示要更改記錄。參數(shù)cPropID表示要寫入記錄的字段個數(shù),參數(shù)prgProp-Val表示要寫入記錄的結(jié)構體。
(3)刪除記錄。
BOOL CeDeleteRecord(HANDLE hDatabase,CEOID oidRecord);
參數(shù) hDatabase表示要刪除的數(shù)據(jù)庫句柄,oidRecord表示要刪除的記錄ID。
在對EDB進行操作前需要先定義一個宏,即#define EDB,將其加載開發(fā)工程中的stdafx文件中,否則將無法使用EDB相關的函數(shù)。具體數(shù)據(jù)庫的創(chuàng)建思路與上文相同,本數(shù)據(jù)庫共由5個字段構成,分別是學生姓名,學號,性別,聯(lián)系電話,照片。
首先加載Windows CE系統(tǒng)學生信息數(shù)據(jù)庫所在的卷文件“\student.vol”,然后再打開數(shù)據(jù)庫,本數(shù)據(jù)庫有兩個排序字段,分別是姓名和學號,這樣在搜索數(shù)據(jù)庫時可以利用EDB自帶的API函數(shù)對這兩個字段的類內(nèi)容進行搜索。
通過對EDB數(shù)據(jù)庫進行分析,可以得到各個屬性的PropID,從而對學生信息進行讀取。核心代碼如下:
/* -------------------
【函數(shù)介紹】:讀取記錄;根據(jù)學生編號,修改記錄
【入口參數(shù)】:strNo:學生編號
pRecStudent:學生數(shù)據(jù)庫表結(jié)構
【出口參數(shù)】:(無)
【返回值】:TRUE:編輯成功;FALSE:編輯失敗
-------------------*/
ceOid=CeSeekDatabaseEx(tblStudent.m_hDB,CEDB_SEEK_VALUEFIRSTEQUAL,(DWORD)&seekPropVal,1,&dwIndex);
if(ceOid==0)
{TRACE(L"未查找到此記錄 ");
goto error;}
//寫入記錄
tmpCeOid=CeWriteRecordProps(tblStudent.m_hDB,ceOid,4,pProps);
if(tmpCeOid==0){
TRACE(L"寫入記錄失敗 ");
goto error;}
如果要從數(shù)據(jù)庫中刪除一個學生信息,只需要換的該學生信息的CEOID,然后通過調(diào)用CEDeleteRecord函數(shù)刪除。
ceOid=CeSeekDatabaseEx(tblStudent.m_hDB,CEDB_SEEK_VALUEFIRSTEQUAL,(DWORD)&seekPropVal,1,&dwIndex);
if(ceOid==0)
{TRACE(L"未查找到此記錄 ");
goto error;
}
//刪除當前記錄
if(!CeDeleteRecord(tblStudent.m_hDB,ceOid))
{TRACE(L"刪除記錄失敗 ");
goto error;
}
向數(shù)據(jù)庫添加一個學生信息,需要先填充屬性結(jié)構CEPROPVAL,然后調(diào)用 CEWriteRecordProps添加。該函數(shù)也可以用于修改某個已經(jīng)存在的記錄屬性值。
//首先填充CEPROPVAL屬性結(jié)構
CEPROPVAL pProps[4];
DWORD dwErrorCode=0;
DWORD dwWritten=0;
//定義內(nèi)存流句柄,用于寫入照片數(shù)據(jù)
HANDLE hStream=INVALID_HANDLE_VALUE;
//1,打開學生數(shù)據(jù)庫
if(!tblStudent.DB_Open_Student())
{
//打開學生數(shù)據(jù)庫失敗
return FALSE;
}
//給字段屬性賦值
ZeroMemory(&pProps[0],sizeof(CEPROPVAL)*4);
//學生學號
pProps[0].propid=PID_NO;
pProps[0].val.lpwstr=LPWSTR(pRecStudent→szNo);
pProps[0].wFlags=0;
……
//寫入記錄
ceOid=CeWriteRecordProps(tblStudent.m_hDB,0,4,pProps);
if(ceOid==0)
{
dwErrorCode=GetLastError();
//如果 dwErrorCode=183,表示學生編號重復
if(dwErrorCode==ERROR_ALREADY_EXISTS)
{
TRACE(L"學生編號重復 ");
}
else
{
TRACE(L"寫入記錄失敗,Error Code=%d ",dwErrorCode);
}
goto error;
}
EDB數(shù)據(jù)庫支持多種查找方式,比如指定位置查找,指定條件查找等,數(shù)據(jù)庫記錄的查找,通過CeSeek-DatabaseEx實現(xiàn)。CESeekDatabaseEx函數(shù)的 dwSeek-Type參數(shù)指定查找方式,常用的幾種查找方式如下:
直接查找指定位置的記錄(CEDB_SEEK_CEOID);
從開始位置查找(CEDB_SEEK_BEGINNING);
從最后位置查找(CEDB_SEEK_END);
從當前位置查找(CEDB_SEEK_CURRENT);
查找第一條屬性等于指定值的記錄(CEDB_SEEK_VALUEFIRSTEQUAL);
查找下一條屬性等于指定值的記錄(CEDB_SEEK_VALUESMALLER);
查找小于等于指定屬性值的記錄(CEDB_SEEK_VALUESMALLEROREQUAL);
查找大于指定屬性值的記錄(CEDB_SEEK_VALUEGREATER);
查找大于或者等于指定屬性值的記錄(CEDB_SEEK_VALUEGREATEROREQUAL)[4]。
開發(fā)數(shù)據(jù)庫的是一個復雜的過程,只有掌握較多的知識才可以開發(fā)出功能強大,性能穩(wěn)定的數(shù)據(jù)庫。在數(shù)據(jù)庫開發(fā)過程中,通常會存在多個結(jié)構體嵌套的情況,因而涉及到很多的結(jié)構體和指針變量,在開發(fā)中要特別注意指針和內(nèi)存的問題,要防止內(nèi)存泄露,指針要及時清空。
[1]汪兵.Windows CE嵌入式高級編程及其實例詳解[M].北京:中國水利水電出版社,2008.
[2]田東風.Windows CE應用程序設計[M].北京:機械工程出版社,2005.
[3]Microsoft.Microsoft MSDN mobile專區(qū)[EB/OL](2006 -07-18)[2013 -05 -16]http;//msdn.microsoft.com/en- us/windowsmobile/default.apsx,2006.
[4]沈建國.Windows CE 5.0 EDB數(shù)據(jù)庫的應用與開發(fā)[D].重慶:重慶郵電大學,2007.
[5]杜春雷.ARM體系結(jié)構與編程[M].北京:清華大學出版社,2008.