从 Visual C++ 访问 Borland 数据库引擎 (BDE)





5.00/5 (5投票s)
1999年11月18日
6分钟阅读

242858

3516
Borland 数据库引擎 (BDE) 是 Borland (现为 Inprise) 提供的数据库引擎,用于访问 Paradox 和 dBase 数据库以及其他几种格式。它为 Borland 产品(如 Borland C++、Borland C++ Builder、Borland Delphi 和 Borland J Builder)提供数据库接口。Borland 提供库文件和头文件,以方便直接访问 BDE API。Borland 还提供了对每个 API 函数相当详尽的文档,通常包括 C 和 Pascal 的示例。Borland 编译器(如 Borland C++ 4.5 和 5.0)也包含使用直接 API 调用的示例程序。
Borland 的 MFC 等效项是 Object Windows Library (OWL)。OWL 为 BDE 提供了 C++ 接口。然而,随着 Borland 编译器在 Visual C++ 和 MFC 中的普及度下降,以及 Borland 对 OWL 的支持逐渐被 C++ Builder 等基于组件的编译器所取代,现在更需要一个基于 MFC 的类接口来访问 BDE API。
不幸的是,API 函数调用可能非常复杂,仅获取数据库字段值就需要多个 API 函数调用。任何函数都可能失败并返回一个必须始终检查的错误代码。因此,我投入时间开发了一个类包装器和相关的异常处理类来处理 BDE API 函数调用是非常值得的。本文将介绍这些类以及一个测试平台程序(如下所示),用于使用所有可用的 Paradox 数据类型读取和写入表中的数据。
此处提供的类广泛使用了 MFC 类 CString 和 COleDateTime 来读写表中的字符串和日期/时间信息。本文还提供了一个很好的用户定义的 CException 派生异常处理程序的开发和使用示例,并附带了增强的错误消息。
上面显示的是测试应用程序。在示例数据表中,每种类型都有一个字段。对话框中的某些类型有两个条目,以便测试 CBdeDatabase 类中可能的类型转换。一个字段接受字符串形式的字段值,另一个字段接受其原生类型的值(例如,您可以通过传递整数值或字符串值来设置整数字段)。
您为什么要从 Visual C++ 程序访问 Borland 数据库引擎?
- Jet 不支持版本 5.0 以上的 Paradox 表。如果您想访问这些表,直接使用 BDE 是最好的方法。
- 尽管我没有测试过此类或 BDE 的多线程功能,但 BDE 与 Jet 不同,据称是安全的,可以用于多线程。
- BDE 通过 dBase 文件进行访问,避免了 Jet 或 ADO 繁琐的 COM 开销,并且不使用那些可怕的 OLEVARIANT 类型!
- 通过 Microsoft 进行数据库访问似乎越来越复杂,而不是越来越容易,并且他们不断更改 API。BDE API 自始至终保持不变。
- 速度!虽然我没有进行正式测试,但这个直接访问 BDE 的类提供了比 Jet 甚至 Borland Delphi 数据库对象快得多的数据库访问速度。我从未见过比我使用此类的应用程序中的数据库访问速度更快。
如何设置项目以访问 BDE
1. 您的计算机上必须安装 BDE。市面上有各种版本的 BDE,所以请注意版本控制问题。2. BDE 路径必须包含在您计算机的 PATH 环境变量中。BDE 应用程序通常不会添加此项,所以您可能需要自己完成。
3. Borland 的网站提供了您必须链接到项目的 .lib 文件。这些文件也包含在此示例项目中。
4. Borland 的网站还提供了您必须包含在项目中的 Ms-idapi.h 头文件。这些文件也包含在此示例项目中。
5. 在您的项目中包含此处提供的文件。这些文件包括:
- BdeDatabase.h
- BDE API 类包装器的接口。
- BdeDatabase.cpp
- BDE API 类包装器的实现。
- BdeException.h
- BDE 异常处理程序的接口。
- BdeException.cppp
- BDE 异常处理程序的实现。
CBdeDatabase -- BDE 类包装器
此处提供的 CBdeDatabase 类允许您使用所有非 BLOB 类型以及 Paradox 表中的简单 Memo 字符串(出于某种原因,读取 dBase Memo 时会崩溃)来读写 dBase 和 Paradox 表中的数据。相关的功能,如获取字段名称和表导航,也得到了支持。本质上,每个要访问的表都会创建一个该类的实例。事件顺序非常简单。
- 在程序启动时调用 Initialize 来初始化 BDE。
- 要打开表连接,请调用 OpenDatabase 并提供表名和路径。
- 对表执行您的操作。
- 调用 CloseDatabase 断开与表的连接。
- 在程序退出前调用 Uninitialize。
这里的 CBdeDatabase 类仅提供了 BDE API 功能的一小部分,尽管对于许多项目来说,这部分是最重要的。
///////////////////////////////////////////////////////////////// // BdeDatabase.h -- Interface for the CBdeDatabase class // This class provides access to the Borland Database Engine // For this to work, the BDE directory must be in the computers PATH statement // Link with Idapi32m.lib, #ifndef __BDEDATABASE_H__ #define __BDEDATABASE_H__ //#include "Ms-idapi.h" // header file for BDE API calls // Actually, we are supposed to include Ms-idapi.h, but all it has is // the following three lines anyway. This allows me to keep idapi.h in the project directory #define __FLAT__ #define __WIN32__ #include "idapi.h" #define TABLETYPE_PARADOX 0 #define TABLETYPE_DBASE 1 #define TABLETYPE_TEXT 2 #define EDITMODE_NONE 0 #define EDITMODE_APPEND 1 #define EDITMODE_INSERT 2 #define EDITMODE_EDITINPLACE 3 class CBdeDatabase { // Construction public: CBdeDatabase(); ~CBdeDatabase(); // Attributes public: protected: hDBIDb m_hDb; // Handle to the Database hDBICur m_hCursor; // Handle to the cursor CHAR m_szTableName[DBIMAXNAMELEN]; CHAR m_szDatabaseName[255]; CHAR m_szPrivateDir[255]; pBYTE m_pEditRecordBuffer; UINT m_nEditMode; int m_nTableType; // Operations public: // functions to open and close databases BOOL OpenDatabase(LPCTSTR szPath, LPCTSTR szTableName, int nTableType = TABLETYPE_PARADOX, BOOL bReadOnly = FALSE, BOOL bExclusive = FALSE, LPCTSTR szPrivateDir = NULL); BOOL OpenDatabase(LPCTSTR szFullPath, BOOL bReadOnly = FALSE, BOOL bExclusive = FALSE, LPCTSTR szPrivateDir = NULL); BOOL CloseDatabase(); // Table navigation void MoveFirst(); void MoveNext(); void MovePrior(); void MoveLast(); LONG GetRecordCount(); BOOL IsBOF(); BOOL IsEOF(); // Functions to get field information int GetFieldCount(); CString GetFieldName(int nFieldNumber); int FieldNumberFromName(LPCTSTR szFieldName); int GetFieldSize(int nFieldNumber); int GetFieldType(int nFieldNumber); int GetBlobType(int nFieldNumber); // functions to get field values CString GetFieldAsString(UINT16 nFieldNumber, BOOL* pbIsBlank = NULL); LONG GetFieldAsInteger(UINT16 nFieldNumber, BOOL* pbBlank = NULL); double GetFieldAsFloat(UINT16 nFieldNumber, BOOL* pbIsBlank = NULL); COleDateTime GetFieldAsDate(UINT16 nFieldNumber, BOOL* pbBlank); BOOL GetFieldAsBoolean(UINT16 nFieldNumber, BOOL* pbBlank = NULL); // functions to set field values BOOL SetFieldAsString(INT16 nFieldNumber, LPCTSTR szValue, BOOL bBlank = FALSE); BOOL SetFieldAsInteger(INT16 nFieldNumber, int nValue, BOOL bBlank = FALSE); BOOL SetFieldAsDate(INT16 nFieldNumber, COleDateTime dtValue, BOOL bBlank = FALSE); BOOL SetFieldAsFloat(INT16 nFieldNumber, double fValue, BOOL bBlank = FALSE); BOOL SetFieldAsBoolean(INT16 nFieldNumber, int nValue, BOOL bBlank = FALSE); // functions for editing and posting operations BOOL Edit(); BOOL Insert(); // insert and append really do the same thing BOOL Append(); BOOL Post(); BOOL Cancel(); BOOL DeleteRecord(); protected: // Error checking routines BOOL CheckInitialization(LPCTSTR szOperation = NULL); BOOL CheckValidCursor(LPCTSTR szOperation = NULL); BOOL CheckEditMode(LPCTSTR szOperation = NULL); BOOL CheckNotEditMode(LPCTSTR szOperation = NULL); // Conversion routines CString FormatDate(INT32 Date); CString FormatTime(TIME Time); CString FormatTimeStamp (TIMESTAMP TimeStamp); COleDateTime TimeStampToOleDateTime(TIMESTAMP TimeStamp); COleDateTime DateToOleDateTime(INT32 Date); COleDateTime TimeToOleDateTime(TIME time); DBIResult OleDateTimeToTimeStamp(COleDateTime dt, pTIMESTAMP pTimeStamp); INT32 OleDateTimeToDate(COleDateTime dt); TIME OleDateTimeToTime(COleDateTime dt); BOOL OpenDatabaseHelper(int nTableType, DBIOpenMode eOpenMode, DBIShareMode eShareMode, LPCTSTR szPrivateDir); BOOL PrepareRecordEdit(int nEditMode); // Inlines public: inline BOOL IsActive() { return (m_hDb != NULL); } inline BOOL GetEditMode() { return (m_nEditMode != 0); } protected: // Statics public: static DBIResult Check(DBIResult ErrorValue, LPCTSTR szMessage = NULL); static BOOL Initialize(); static void Uninitialize(); // Functions for enabling buttons static BOOL EnableFirst(CBdeDatabase* pBdeDb); static BOOL EnableNext(CBdeDatabase* pBdeDb); static BOOL EnablePrior(CBdeDatabase* pBdeDb); static BOOL EnableLast(CBdeDatabase* pBdeDb); static BOOL EnableInsert(CBdeDatabase* pBdeDb); static BOOL EnableEdit(CBdeDatabase* pBdeDb); static BOOL EnablePost(CBdeDatabase* pBdeDb); static BOOL EnableCancel(CBdeDatabase* pBdeDb); static BOOL EnableAppend(CBdeDatabase* pBdeDb); static BOOL EnableDelete(CBdeDatabase* pBdeDb); static BOOL EnableOpen(CBdeDatabase* pBdeDb); static BOOL EnableClose(CBdeDatabase* pBdeDb); protected: static BOOL m_bInitialized; }; // end of class definition #endif // __BDEDATABASE_H__
CBdeException -- BDE 异常处理程序
为了提供我 BDE 类的结构化异常处理,我开发了 CBdeException。即使是像确定字段是否存在这样简单的任务,也需要一系列函数调用,每个函数都可能返回一个错误代码。许多 CBdeDatabase 成员函数还会分配内存,必须检查是否失败。对于每个 BDE API 函数调用,我都会检查返回值,并在出错时抛出异常。许多 API 以提供简短且无信息性的错误消息而闻名,BDE API 也不例外。因此,我已将 CBdeException 类的错误报告功能大大增强,提供了详细的错误消息、产生错误的表名和数据库名,以及 BDE 报告的错误字符串。
#define __BDEEXCEPION_H__ //#include "Ms-idapi.h" // header file for BDE API calls // Actually, we are supposed to include Ms-idapi.h, but all it has is // the following three lines anyway. This allows me to keep idapi.h in the project directory #define __FLAT__ #define __WIN32__ #include "idapi.h" // These are additional errors that may be generated in the // CBdeDatabase class #define BDEEXERR_FIELDNOTINTEGER 1 #define BDEEXERR_FIELDNOTFLOAT 2 #define BDEEXERR_FIELDNOTDATE 3 #define BDEEXERR_FIELDNOTSTRING 4 #define BDEEXERR_NOSUCHFIELD 5 #define BDEEXERR_NOTINEDITMODE 6 #define BDEEXERR_ALREADYINEDITMODE 7 #define BDEEXERR_INVALIDCURSOR 8 #define BDEEXERR_ALREADYOPEN 9 #define BDEEXERR_NOTINITIALIZED 10 #define BDEEXERR_INVALIDDATETIMEFORMAT 11 #define BDEEXERR_UNSUPPORTEDFIELDTYPE 12 #define BDEEXERR_UNSUPPORTEDBLOBTYPE 13 #define BDEEXERR_FIELDNOTBOOLEAN 14 #define BDEEXERR_INVALIDFIELDINDEX 15 #define BDEEXERR_INVALIDFIELDNAME 16 class CBdeException : public CException { DECLARE_DYNAMIC(CBdeException); // construction/destruction public: CBdeException(); CBdeException(DBIResult dbiResult); CBdeException(DBIResult dbiResult, CString strTable, CString strDatabaseName, LPCTSTR szAddInfo); CBdeException(DBIResult dbiResult, UINT nExtendedError, CString strTable, CString strDatabaseName, LPCTSTR szAddInfo); // Attributes public: protected: DBIResult m_dbiResult; UINT m_nExtendedError; CString m_strAddInfo; CString m_strTableName; CString m_strDatabaseName; // Operations public: virtual BOOL GetErrorMessage(LPTSTR lpszError, UINT nMaxError, PUINT pnHelpContext = NULL); CString GetErrorMessage(BOOL bVerbose = TRUE); virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0); static CString GetExtendedErrorMessage(int nError); protected: // inlines public: inline LPCTSTR GetTableName() { return m_strTableName; } inline LPCTSTR GetAddInfo() { return m_strAddInfo; } inline LPCTSTR GetDatabaseName() { return m_strDatabaseName; } }; // end of class definition #endif __BDEEXCEPION_H__
重新分发问题
如果您决定编写一个可以访问 BDE 的 MFC 应用程序,请注意一些重新分发问题。首先,BDE 目录必须在 PATH 环境变量中。大多数 BDE 应用程序(如 Delphi 编写的应用程序)不需要这样做,所以您可能需要自己设置。其次,如果您想分发 BDE,请首先参考 Borland 的重新分发协议。尽管重新分发是免费的,但他们过去曾有过一些不寻常但苛刻的条件。