分享
 
 
 

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

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

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

1、PARADOX数据库结构

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

2、连接到PARADOX数据库

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

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

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

_ConnectionPtr m_pDb;

CString connectsource;

connectsource.Format(L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s\\shared;Extended Properties=Paradox 5.x;Persist Security Info=False",strCTRSRoute);

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

{

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变量保存了连接信息,由此我们可以看到,所谓数据库的数据源,仅指是连接到PARADOX数据表放的位置Data Source=%s\\shared,“shared”为一文件夹名,在该文件夹下面有PARADOX数据表,而不是具体指向哪一个数据表。同理,在下面的两种方式中,数据源也仅指向包含PARADOX数据表的文件夹。除了异常处理外在后面要详细说明外,其他的操作与其他ADO操作没有差别,在此不再冗续。

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

connectsource.Format(L"CollatingSequence=ASCII;DBQ=%s\\shared;" L"DefaultDir=%s\\shared;Driver={Microsoft Paradox Driver (*.db )};" L"DriverId=538;FIL=Paradox 5.X;" L"MaxBufferSize=2048;MaxScanRows=8;PageTimeout=600;" L"ParadoxNetPath=%s\\shared;ParadoxNetStyle=4.x;ParadoxUserName=admin;" L"SafeTransactions=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 LoadDbSource(CString strSourceName, CString strSourceDb, CString strDescription)

{

//存放打开的注册表键

HKEY hKey;

DWORD dw;

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

LONG lReturn;

//存放要打开的子键

CString strSubKey;

//检测是否安装了MS Access ODBC driver:odbcjt32.dll

//获得 Windows系统目录

WCHAR sysDir[MAX_PATH];

WCHAR drvName[]=L"\odbcjt32.dll";

::GetSystemDirectory(sysDir, MAX_PATH);

wcscat(sysDir,drvName);

CFileFind findFile;

if(!findFile.FindFile(sysDir))

{

AfxMessageBox(L"没有安装Paradox 5.X的ODBC驱动程序odbcjt32.dll,\n无法加载该类数据源!" ,MB_OK | MB_ICONSTOP);

return false;

}

strSubKey=L"SOFTWARE\\ODBC\\ODBC.INI\\" + 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 =L"admin";

::RegSetValueEx(hKey, L"DefaultDir", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strDbq), 2*strDbq.GetLength());

::RegSetValueEx(hKey, L"Description", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strDescription), 2*strDescription.GetLength());

::RegSetValueEx(hKey, L"Driver", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strDriver), 2*strDriver.GetLength());

::RegSetValueEx(hKey, L"DriverId", 0L, REG_DWORD, (CONST BYTE*)(&dwDriverId), sizeof(dw));

::RegSetValueEx(hKey, L"FIL", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strFil),2*strFil.GetLength ());

::RegSetValueEx(hKey, L"UID", 0L, REG_SZ, (CONST BYTE*)((LPCTSTR)strUid),2*strUid.GetLength ());

::RegSetValueEx(hKey, L"SafeTransactions", 0L, REG_DWORD, (CONST BYTE*)(&dwSafeTransactions), sizeof(dw));

::RegCloseKey(hKey);

//创建 ODBC数据源的Jet子键

strSubKey += "\Engines\Paradox";

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=L"ASCII";

CString strParadoxNetStyle=L"4.x";

::RegSetValueEx(hKey, L"ImplicitCommitSync", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strImplict), 2*strImplict.GetLength()+1);

// ::RegSetValueEx(hKey, L"MaxBufferSize", 0L, REG_DWORD, (CONST BYTE*)(&dwMaxBufferSize), sizeof(dw));

::RegSetValueEx(hKey, L"PageTimeout", 0L, REG_DWORD, (CONST BYTE*)(&dwPageTimeout), sizeof(dw));

::RegSetValueEx(hKey, L"Threads", 0L, REG_DWORD, (CONST BYTE*)(&dwThreads), sizeof(dw));

::RegSetValueEx(hKey, L"UserCommitSync", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strUserCommit), 2*strUserCommit.GetLength());

::RegSetValueEx(hKey, L"CollatingSequence", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strCollSeq), 2*strCollSeq.GetLength());

::RegSetValueEx(hKey, L"ParadoxNetPath", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strDbq), 2*strDbq.GetLength());

::RegSetValueEx(hKey, L"ParadoxNetStyle", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strParadoxNetStyle), 2*strParadoxNetStyle.GetLength());

::RegSetValueEx(hKey, L"ParadoxUserName", 0L, REG_SZ, (CONST BYTE*)((LPCWSTR)strUid), 2*strUid.GetLength());

::RegCloseKey(hKey);

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

lReturn=::RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources", 0L, KEY_WRITE, &hKey);

if(lReturn != ERROR_SUCCESS)

return false;

CString strDbType=L"Microsoft Paradox Driver (*.db )";

::RegSetValueEx(hKey, strSourceName, 0L, REG_SZ, (CONST BYTE*)((LPCTSTR)strDbType), 2*strDbType.GetLength());

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的信息,下面这个函数也是从www.codeproject.com上得到的。在此对该函数作者表示感谢!

HRESULT LogAdoErrorImport(_ConnectionPtr pConn)

{

ErrorsPtr pErrors;

ErrorPtr pError;

CString strTmp;

HRESULT hr = (HRESULT) 0L;

long nCount;

// Don't 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( L"\t 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( L"\n Number = %ld", pError->GetNumber() );

TRACE( L"\n Source = %s", (LPCTSTR) bstrSource );

CString strDes;

strDes.Format(L"%s\n", (LPCTSTR) bstrDescription );

AfxMessageBox(strDes,MB_OK | MB_ICONERROR);

TRACE( L"\n HelpFile = %s", (LPCTSTR) bstrHelpFile );

TRACE( L"\n HelpContext = %ld", pError->GetHelpContext() );

TRACE( L"\n SQLState = %s", (LPCTSTR) bstrSQLState );

TRACE( L"\n HelpContext = %ld", pError->GetHelpContext() );

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有