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连接非本机数据库的时间。这仅是一个设想,在下一步的系统升级时希望能实现,也恳请计算机高手们提出意见和建议。


发布评论