前面接手一個項目,需要用vc訪問已經(jīng)存在的paradox數(shù)據(jù)庫。在接手這個項目前,對于paradox的理解少之又少,只知道有這么一種數(shù)據(jù)庫,并不了解它的結(jié)構(gòu)是什么。真正對它進(jìn)行操作的時候,才發(fā)現(xiàn)是如此之難。有幾次差點(diǎn)都放棄了,但最后一咬牙,總算堅持過來了。在這期間,我走了不少的彎路,也有了一些心得,下面寫下來,做為一個總結(jié),也為其他同道少走一點(diǎn)彎路起一點(diǎn)提示作用吧。
1、paradox數(shù)據(jù)庫結(jié)構(gòu)
paradox數(shù)據(jù)庫是boland以前在delphi下利用bde進(jìn)行操作的桌面數(shù)據(jù)庫,目前已經(jīng)很少使用,以致ado都不提供它的引擎了(也害得我吃了不少苦頭)。paradox數(shù)據(jù)庫本身以獨(dú)立的表存在的,一個表就可以看成是一個庫,或者也可以說是一個文件夾就是一個庫,文件夾里的paradox數(shù)據(jù)表就是該庫的各個表。paradox數(shù)據(jù)表的擴(kuò)展名是db,此外還有一些其他的文件類型,作為數(shù)據(jù)表的輔助,但用ado對其進(jìn)行處理時,使用*.db的文件就已經(jīng)足夠。
2、連接到paradox數(shù)據(jù)庫
前面已經(jīng)提到過,ado沒有paradox數(shù)據(jù)庫的引擎,要用ado訪問paradox數(shù)據(jù)庫,我試過三種方式來進(jìn)行操作:用microsoft.jet.oledb.4.0來替代paradox數(shù)據(jù)庫引擎;用仿odbc的連接語句操作;建立odbc數(shù)據(jù)源,然后用ado來訪問odbc。還有一種是用vc來封裝bde api,在上可以找到相關(guān)的內(nèi)容。對于最后一種方法,用他的例子卻實效果不錯,但真正移直到我的程序上時卻費(fèi)了很大勁,并且效果不好,主要是我對于bde操作方式太不理解了,花了好多時間,最終以放棄告終。
下面我就說明用前三種方式來進(jìn)行連接到數(shù)據(jù)庫的操作。其實這三種方式都沒有太大的差別,只是連接語句不同而已。
(1)用microsoft.jet.oledb.4.0引擎。這種方式訪問時跟連接到其他數(shù)據(jù)庫沒什么差別:
_connectionptr m_pdb;
cstring connectsource;
connectsource.format(lprovider=microsoft.jet.oledb.4.0;data source=%s\shared;extended properties=paradox 5.x;persist security info=false,strctrsroute);
try //檢查數(shù)據(jù)庫連接是否正常
{
m_pdb.createinstance(__uuidof(connection));
m_pdb->connectiontimeout=10;
m_pdb->commandtimeout=20;
if(m_pdb->state!=adstateclosed)
{
m_pdb->close();
m_pdb->open((_bstr_t)connectsource,,,admodeunknown);
}
else
{
hr=m_pdb->open((_bstr_t)connectsource,,,admodeunknown);
}
}
catch(_com_error e) //捕捉異常
{
logadoerrorimport(m_pdb);
}
說明:connectsource變量保存了連接信息,由此我們可以看到,所謂數(shù)據(jù)庫的數(shù)據(jù)源,僅指是連接到paradox數(shù)據(jù)表放的位置data source=%s\shared,“shared”為一文件夾名,在該文件夾下面有paradox數(shù)據(jù)表,而不是具體指向哪一個數(shù)據(jù)表。同理,在下面的兩種方式中,數(shù)據(jù)源也僅指向包含paradox數(shù)據(jù)表的文件夾。除了異常處理外在后面要詳細(xì)說明外,其他的操作與其他ado操作沒有差別,在此不再冗續(xù)。
(2)用仿odbc連接操作。odbc還提供了paradox數(shù)據(jù)引擎,因此,我們可以通過odbc來訪問paradox數(shù)據(jù)表。本方法就是把建立dsn的連接信息在ado的連接語句中完全地寫出來。
connectsource.format(lcollatingsequence=ascii;dbq=%s\shared;
ldefaultdir=%s\shared;driver={microsoft paradox driver (*.db )};
ldriverid=538;fil=paradox 5.x;
lmaxbuffersize=2048;maxscanrows=8;pagetimeout=600;
lparadoxnetpath=%s\shared;paradoxnetstyle=4.x;paradoxusername=admin;
lsafetransactions=0;threads=3;uid=admin;usercommitsync=yes;,strctrsroute,strctrsroute,strctrsroute);
說明:上面所有信息均可在注冊表中的odbc對應(yīng)的鍵下面或都文件dsn中直接找到。我們現(xiàn)在操作的paradox數(shù)據(jù)表,一般是paradox 4.x或paradox 5.x。對于paradox 7.x好像也無能為力。好在我用的數(shù)據(jù)庫的版本也沒那么高,呵呵。其他操作同第一種方式。
(3)建立odbc數(shù)據(jù)源,用ado訪問odbc數(shù)據(jù)源。這種方式是第二種的翻版,但需要動態(tài)地或手工添加odbc數(shù)據(jù)源。
cstring connectsource=provider=msdasql.1;persist security info=false;data source=projdir;
其中data source=projdir,就指出了數(shù)據(jù)源為一個odbc的dsn。
為了實現(xiàn)動態(tài)地添加數(shù)據(jù)源,下面提供一個添加paradox 系統(tǒng)dsn的函數(shù):
bool loaddbsource(cstring strsourcename, cstring strsourcedb, cstring strdescription)
{
//存放打開的注冊表鍵
hkey hkey;
dword dw;
//存放注冊表api函數(shù)執(zhí)行的返回值
long lreturn;
//存放要打開的子鍵
cstring strsubkey;
//檢測是否安裝了ms access odbc driver:odbcjt32.dll
//獲得 windows系統(tǒng)目錄
wchar sysdir[max_path];
wchar drvname[]=l;
::getsystemdirectory(sysdir, max_path);
wcscat(sysdir,drvname);
cfilefind findfile;
if(!findfile.findfile(sysdir))
{
afxmessagebox(l沒有安裝paradox 5.x的odbc驅(qū)動程序odbcjt32.dll,n無法加載該類數(shù)據(jù)源! ,mb_ok | mb_iconstop);
return false;
}
strsubkey=lsoftware\odbc\odbc.ini\ strsourcename;
//創(chuàng)建 odbc數(shù)據(jù)源在注冊表中的子鍵
lreturn=::regcreatekeyex(hkey_local_machine, (lpctstr)strsubkey, 0, null, reg_option_non_volatile,key_write,null,&hkey,&dw);
if(lreturn != error_success)
return false;
//設(shè)置數(shù)據(jù)源的各項參數(shù)
cstring strdbq = strsourcedb;
cstring strdriver = sysdir;
dword dwdriverid = 538;
cstring strfil = paradox 5.x;;
//cstring strpwd = strsourcename;
dword dwsafetransactions = 0;
cstring struid =ladmin;
::regsetvalueex(hkey, ldefaultdir, 0l, reg_sz, (const byte*)((lpcwstr)strdbq), 2*strdbq.getlength());
::regsetvalueex(hkey, ldescription, 0l, reg_sz, (const byte*)((lpcwstr)strdescription), 2*strdescription.getlength());
::regsetvalueex(hkey, ldriver, 0l, reg_sz, (const byte*)((lpcwstr)strdriver), 2*strdriver.getlength());
::regsetvalueex(hkey, ldriverid, 0l, reg_dword, (const byte*)(&dwdriverid), sizeof(dw));
::regsetvalueex(hkey, lfil, 0l, reg_sz, (const byte*)((lpcwstr)strfil),2*strfil.getlength ());
::regsetvalueex(hkey, luid, 0l, reg_sz, (const byte*)((lpctstr)struid),2*struid.getlength ());
::regsetvalueex(hkey, lsafetransactions, 0l, reg_dword, (const byte*)(&dwsafetransactions), sizeof(dw));
::regclosekey(hkey);
//創(chuàng)建 odbc數(shù)據(jù)源的jet子鍵
strsubkey = ;
lreturn=::regcreatekeyex(hkey_local_machine, (lpcwstr)strsubkey, 0, null, reg_option_non_volatile, key_write, null, &hkey, &dw);
if(lreturn != error_success)
return false;
//設(shè)置該子鍵下的各項參數(shù)
cstring strimplict=;
cstring strusercommit=yes;
dword dwpagetimeout=5;
dword dwthreads=3;
dword dwmaxbuffersize=2048;
cstring strcollseq=lascii;
cstring strparadoxnetstyle=l4.x;
::regsetvalueex(hkey, limplicitcommitsync, 0l, reg_sz, (const byte*)((lpcwstr)strimplict), 2*strimplict.getlength() 1);
// ::regsetvalueex(hkey, lmaxbuffersize, 0l, reg_dword, (const byte*)(&dwmaxbuffersize), sizeof(dw));
::regsetvalueex(hkey, lpagetimeout, 0l, reg_dword, (const byte*)(&dwpagetimeout), sizeof(dw));
::regsetvalueex(hkey, lthreads, 0l, reg_dword, (const byte*)(&dwthreads), sizeof(dw));
::regsetvalueex(hkey, lusercommitsync, 0l, reg_sz, (const byte*)((lpcwstr)strusercommit), 2*strusercommit.getlength());
::regsetvalueex(hkey, lcollatingsequence, 0l, reg_sz, (const byte*)((lpcwstr)strcollseq), 2*strcollseq.getlength());
::regsetvalueex(hkey, lparadoxnetpath, 0l, reg_sz, (const byte*)((lpcwstr)strdbq), 2*strdbq.getlength());
::regsetvalueex(hkey, lparadoxnetstyle, 0l, reg_sz, (const byte*)((lpcwstr)strparadoxnetstyle), 2*strparadoxnetstyle.getlength());
::regsetvalueex(hkey, lparadoxusername, 0l, reg_sz, (const byte*)((lpcwstr)struid), 2*struid.getlength());
::regclosekey(hkey);
//設(shè)置odbc數(shù)據(jù)庫引擎名稱
lreturn=::regopenkeyex(hkey_local_machine, lsoftware\odbc\odbc.ini\odbc data sources, 0l, key_write, &hkey);
if(lreturn != error_success)
return false;
cstring strdbtype=lmicrosoft paradox driver (*.db );
::regsetvalueex(hkey, strsourcename, 0l, reg_sz, (const byte*)((lpctstr)strdbtype), 2*strdbtype.getlength());
return true;
}
說明:該函數(shù)是在網(wǎng)上一個寫odbc注冊表的例子上加工而成的,在此表示感謝!在建立系統(tǒng)dsn的時候,一個項是paradoxusername。在利用odbc管理器添加數(shù)據(jù)源的時候,會默認(rèn)為當(dāng)前用戶的登錄名。并且此項是必須的。為了減少去獲得當(dāng)前系統(tǒng)用戶的麻煩,將它的值賦為:“amdin”,在實際的運(yùn)行過程中,沒有產(chǎn)生任何負(fù)面影響。
以上是連接到數(shù)據(jù)庫的操作。所有上述的操作沒有多大的差別。
3、對數(shù)據(jù)表操作,
本來當(dāng)連接到數(shù)據(jù)庫后,對于數(shù)據(jù)表的操作就是一件很容易的事了。但我卻在這個環(huán)節(jié)上花費(fèi)了大量的時間和精力,以致開發(fā)時間一加再加。對于表的操作我不想說太多,但這里面一個問題卻不得不說。
我沒有真正地卻研究paradox底層原理是什么,但在實際操作時,卻發(fā)現(xiàn)它對bde有很強(qiáng)的依賴性。由于我的計算機(jī)上曾經(jīng)裝過用bde開發(fā)的數(shù)據(jù)庫產(chǎn)品,因此,所有操作一切正常。但當(dāng)該系統(tǒng)拿到其他計算機(jī)上就出現(xiàn)了很多問題,其中最主要的就是出現(xiàn)“[odbc paradox]外部數(shù)據(jù)表不是預(yù)期格式”的錯誤。后來經(jīng)過多臺計算機(jī)上總結(jié),比較得出可能是bde引起的。后來在有問題的計算機(jī)上安裝bde后,一切問題都沒有了。
4、錯誤處理
本來,對ado進(jìn)行操作時,進(jìn)行錯誤處理是必要的步驟,但如何進(jìn)行錯誤處理的方式也能影響程序的健壯與穩(wěn)定。一般情況下,我們都是利用_com_error對象提供錯誤信息,在這個過程中,我發(fā)覺并不能得到錯誤的真正信息,而是一些模棱兩可的信息如:3092,dispatch error等等,對于我們解決問題沒有多少效果。錯誤的信息獲得最好方法就是利用ado本身的error對象。它能捕捉所有connection,command和recodset的信息,下面這個函數(shù)也是從上得到的。在此對該函數(shù)作者表示感謝!
hresult logadoerrorimport(_connectionptr pconn)
{
errorsptr perrors;
errorptr perror;
cstring strtmp;
hresult hr = (hresult) 0l;
long ncount;
// dont have an un-handled exception in the handler that
// handles exceptions!
try
{
perrors = pconn->geterrors();
ncount = perrors->getcount();
for( long i = 0; (!failed(hr)) && (i < ncount); i )
{
trace( lt dumping ado error %d of %d, i 1, ncount );
hr = perrors->get_item((_variant_t)((long)i), &perror );
_bstr_t bstrsource ( perror->getsource() );
_bstr_t bstrdescription( perror->getdescription() );
_bstr_t bstrhelpfile ( perror->gethelpfile() );
_bstr_t bstrsqlstate ( perror->getsqlstate() );
trace( ln number = %ld, perror->getnumber() );
trace( ln source = %s, (lpctstr) bstrsource );
cstring strdes;
strdes.format(l%sn, (lpctstr) bstrdescription );
afxmessagebox(strdes,mb_ok | mb_iconerror);
trace( ln helpfile = %s, (lpctstr) bstrhelpfile );
trace( ln helpcontext = %ld, perror->gethelpcontext() );
trace( ln sqlstate = %s, (lpctstr) bstrsqlstate );
trace( ln helpcontext = %ld, perror->gethelpcontext() );
trace( ln nativeerror = %ld, perror->getnativeerror() );
}
}
catch( cexception *e )
{
trace( l*** unable to log exception *** );
e->delete();
}
catch(...)
{
trace( l*** unable to log exception *** );
}
perrors->release();
perror->release();
return hr;
}
要調(diào)用此函數(shù),也只需在catch中調(diào)用即可,函數(shù)參數(shù)為_connectionptr型。
catch(_com_error e) //捕捉異常
{
logadoerrorimport(m_pdb);
}
5、其他
在我所接手的這個項目中,要對局域網(wǎng)內(nèi)服務(wù)器上不同文件夾下的多個paradox數(shù)據(jù)表進(jìn)行操作,也就是對多個數(shù)據(jù)庫進(jìn)行操作?,F(xiàn)在的處理方式就是共享這些文件夾,并在本機(jī)建立網(wǎng)絡(luò)映射。這樣的操作,如果網(wǎng)絡(luò)比較好的話還可以接受,當(dāng)網(wǎng)絡(luò)不太好的時候,操作起來就很困難,原因是對頻繁對數(shù)據(jù)庫進(jìn)行連接操作占用了大量的時間。因此,我想到利用c/s模式,在服務(wù)器上安裝處理這些數(shù)據(jù)庫操作的服務(wù),客戶端把所有的請求發(fā)到服務(wù)器端,當(dāng)服務(wù)器端處理好后直接傳回數(shù)據(jù)。這樣就減少了通過ado連接非本機(jī)數(shù)據(jù)庫的時間。這僅是一個設(shè)想,在下一步的系統(tǒng)升級時希望能實現(xiàn),也懇請計算機(jī)高手們提出意見和建議。