2024年1月6日发(作者:)

痛苦的历程:Ado访问PARADOX数据库

前面接手一个项目,需要用vc访问已经存在的paradox数据库。在接手这个项目前,对于paradox的理解少之又少,只知道有这么一种数据库,并不了解它的结构是什么。真正对它进行操作的时候,才发现是如此之难。有几次差点都放弃了,但最后一咬牙,总算坚持过来了。在这期间,我走了不少的弯路,也有了一些心得,下面写下来,做为一个总结,也为其他同道少走一点弯路起一点提示作用吧。

1、paradox数据库结构

paradox数据库是boland以前在delphi下利用bde进行操作的桌面数据库,目前已经很少使用,以致ado都不提供它的引擎了(也害得我吃了不少苦头)。paradox数据库本身以独立的表存在的,一个表就可以看成是一个库,或者也可以说是一个文件夹就是一个库,文件夹里的paradox数据表就是该库的各个表。paradox数据表的扩展名是db,此外还有一些其他的文件类型,作为数据表的辅助,但用ado对其进行处理时,使用*.db的文件就已经足够。

2、连接到paradox数据库

前面已经提到过,ado没有paradox数据库的引擎,要用ado访问paradox数据库,我试过三种方式来进行操作:用.4.0来替代paradox数据库引擎;用仿odbc的连接语句操作;建立odbc数据源,然后用ado来访问odbc。还有一种是用vc来封装bde api,在上可以找到相关的内容。对于最后一种方法,用他的例子却实效果不错,但真正移直到我的程序上时却费了很大劲,并且效果不好,主要是我对于bde操作方式太不理解了,花了好多时间,最终以放弃告终。

下面我就说明用前三种方式来进行连接到数据库的操作。其实这三种方式都没有太大的差别,只是连接语句不同而已。

(1)用.4.0引擎。这种方式访问时跟连接到其他数据库没什么差别:

_connectionptr m_pdb;

cstring connectsource;

(lprovider=.4.0;data source=%sshared;extended properties=paradox 5.x;persist

security info=false,strctrsroute);

try //检查数据库连接是否正常

{

m_instance(__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变量保存了连接信息,由此我们可以看到,所谓数据库的数据源,仅指是连接到paradox数据表放的位置data

source=%sshared,“shared”为一文件夹名,在该文件夹下面有paradox数据表,而不是具体指向哪一个数据表。同理,在下面的两种方式中,数据源也仅指向包含paradox数据表的文件夹。除了异常处理外在后面要详细说明外,其他的操作与其他ado操作没

有差别,在此不再冗续。

(2)用仿odbc连接操作。odbc还提供了paradox数据引擎,因此,我们可以通过odbc来访问paradox数据表。本方法就是把建立dsn的连接信息在ado的连接语句中完全地写出来。

(lcollatingsequence=ascii;dbq=sshared;

ldefaultdir=%sshared;driver={microsoft paradox driver

(*.db )};

ldriverid=538;fil=paradox 5.x;

lmaxbuffersize=2048;maxscanrows=8;pagetimeout=600;

lparadoxnetpath=sshared;paradoxnetstyle=4.x;paradoxusername=admin;

lsafetransactions=0;threads=3;uid=admin;usercommitsync=yes;,strctrsroute,strctrsroute,strctrsroute);

说明:上面所有信息均可在注册表中的odbc对应的键下面或都文件dsn中直接找到。我们现在操作的paradox数据表,一般是paradox 4.x或paradox 5.x。对于paradox 7.x好像也无能为力。好在我用的数据库的版本也没那么高,呵呵。其他操作同第一种方式。

(3)建立odbc数据源,用ado访问odbc数据源。这种方式是第二种的翻版,但需要动态地或手工添加odbc数据源。

cstring connectsource=provider=msdasql.1;persist security

info=false;data source=projdir;

其中data source=projdir,就指出了数据源为一个odbc的dsn。

为了实现动态地添加数据源,下面提供一个添加paradox 系统dsn的函数:

bool

{

//存放打开的注册表键

hkey hkey;

loaddbsource(cstring strsourcename, cstring

strsourcedb, cstring strdescription)

%%

dword dw;

//存放注册表api函数执行的返回值

long lreturn;

//存放要打开的子键

cstring strsubkey;

//检测是否安装了ms access odbc driver:

//获得 windows系统目录

wchar sysdir[max_path];

wchar drvname[]=l;

::getsystemdirectory(sysdir, max_path);

wcscat(sysdir,drvname);

cfilefind findfile;

if(!le(sysdir))

{

afxmessagebox(l没有安装paradox 5.x的odbc驱动程序,n无法加载该类数据源! ,mb_ok | mb_iconstop);

return false;

}

strsubkey= strsourcename;

//创建 odbc数据源在注册表中的子键

lreturn=::regcreatekeyex(hkey_local_machine,

(lpctstr)strsubkey, 0, null, reg_option_non_volatile,key_write,null,&hkey,&dw);

if(lreturn != error_success)

return false;

//设置数据源的各项参数

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,

::regsetvalueex(hkey,

::regsetvalueex(hkey,

ldefaultdir,

ldescription,

ldriver,

0l,

0l,

reg_sz,

reg_sz,

reg_sz,

(const

(const

(const

byte*)((lpcwstr)strdbq), 2*gth());

byte*)((lpcwstr)strdescription), 2*gth());

0l,

byte*)((lpcwstr)strdriver), 2*gth());

::regsetvalueex(hkey, ldriverid, 0l, reg_dword, (const byte*)(&dwdriverid), sizeof(dw));

::regsetvalueex(hkey,

::regsetvalueex(hkey,

lfil,

luid,

0l,

0l,

reg_sz,

reg_sz,

(const

(const

byte*)((lpcwstr)strfil),2*gth ());

byte*)((lpctstr)struid),2*gth ());

::regsetvalueex(hkey, lsafetransactions, 0l, reg_dword, (const

byte*)(&dwsafetransactions), sizeof(dw));

::regclosekey(hkey);

//创建 odbc数据源的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;

//设置该子键下的各项参数

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*gth() 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*gth());

::regsetvalueex(hkey, lcollatingsequence, 0l, reg_sz, (const

byte*)((lpcwstr)strcollseq), 2*gth());

::regsetvalueex(hkey, lparadoxnetpath, 0l, reg_sz, (const

byte*)((lpcwstr)strdbq), 2*gth());

::regsetvalueex(hkey, lparadoxnetstyle, 0l, reg_sz, (const

byte*)((lpcwstr)strparadoxnetstyle),

2*gth());

::regsetvalueex(hkey, lparadoxusername, 0l, reg_sz, (const

byte*)((lpcwstr)struid), 2*gth());

::regclosekey(hkey);

//设置odbc数据库引擎名称

lreturn=::regopenkeyex(hkey_local_machine,

c 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*gth());

return true;

}

说明:该函数是在网上一个写odbc注册表的例子上加工而成的,在此表示感谢!在建立系统dsn的时候,一个项是paradoxusername。在利用odbc管理器添加数据源的时候,会默认为当前用户的登录名。并且此项是必须的。为了减少去获得当前系统用户的麻烦,将它的值赋为:“amdin”,在实际的运行过程中,没有产生任何负面影响。

以上是连接到数据库的操作。所有上述的操作没有多大的差别。

3、对数据表操作,

本来当连接到数据库后,对于数据表的操作就是一件很容易的事了。但我却在这个环节上花费了大量的时间和精力,以致开发时间一加再加。对于表的操作我不想说太多,但这里面一个问题却不得不说。

我没有真正地却研究paradox底层原理是什么,但在实际操作时,却发现它对bde有很强的依赖性。由于我的计算机上曾经装过用bde开发的数据库产品,因此,所有操作一切正常。但当该系统拿到其他计算机上就出现了很多问题,其中最主要的就是出现“[odbc

paradox]外部数据表不是预期格式”的错误。后来经过多台计算机上总结,比较得出可能是bde引起的。后来在有问题的计算机上安装bde后,一切问题都没有了。

4、错误处理

本来,对ado进行操作时,进行错误处理是必要的步骤,但如何进行错误处理的方式也能影响程序的健壮与稳定。一般情况下,我们都是利用_com_error对象提供错误信息,在这个过程中,我发觉并不能得到错误的真正信息,而是一些模棱两可的信息如:3092,dispatch error等等,对于我们解决问题没有多少效果。错误的信息获得最好方法就是利用ado本身的error对象。它能捕捉所有connection,command和recodset的信息,下面这个函数也是从上得到的。在此对该函数作者表示感谢!

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;

(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;

}

要调用此函数,也只需在catch中调用即可,函数参数为_connectionptr型。

catch(_com_error e) //捕捉异常

{

logadoerrorimport(m_pdb);

}

5、其他

在我所接手的这个项目中,要对局域网内服务器上不同文件夹下的多个paradox数据表进行操作,也就是对多个数据库进行操作。现在的处理方式就是共享这些文件夹,并在本机建立网络映射。这样的操作,如果网络比较好的话还可以接受,当网络不太好的时候,操作起来就很困难,原因是对频繁对数据库进行连接操作占用了大量的时间。因此,我想到利用c/s模式,在服务器上安装处理这些数据库操作的服务,客户端把所有的请求发到服务器端,当服务器端处理好后直接传回数据。这样就减少了通过ado连接非本机数据库的时间。这仅是一个设想,在下一步的系统升级时希望能实现,也恳请计算机高手们提出意见和建议。