65.9K
CodeProject 正在变化。 阅读更多。
Home

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

1999年11月18日

6分钟阅读

viewsIcon

242858

downloadIcon

3516

  • 下载演示项目 - 223 Kb
  • 下载源代码 - 16.2 Kb
  • Sample Image

    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 数据库引擎?

    1. Jet 不支持版本 5.0 以上的 Paradox 表。如果您想访问这些表,直接使用 BDE 是最好的方法。
    2. 尽管我没有测试过此类或 BDE 的多线程功能,但 BDE 与 Jet 不同,据称是安全的,可以用于多线程。
    3. BDE 通过 dBase 文件进行访问,避免了 Jet 或 ADO 繁琐的 COM 开销,并且不使用那些可怕的 OLEVARIANT 类型!
    4. 通过 Microsoft 进行数据库访问似乎越来越复杂,而不是越来越容易,并且他们不断更改 API。BDE API 自始至终保持不变。
    5. 速度!虽然我没有进行正式测试,但这个直接访问 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 表中的数据。相关的功能,如获取字段名称和表导航,也得到了支持。

    本质上,每个要访问的表都会创建一个该类的实例。事件顺序非常简单。

    1. 在程序启动时调用 Initialize 来初始化 BDE。
    2. 要打开表连接,请调用 OpenDatabase 并提供表名和路径。
    3. 对表执行您的操作。
    4. 调用 CloseDatabase 断开与表的连接。
    5. 在程序退出前调用 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 的重新分发协议。尽管重新分发是免费的,但他们过去曾有过一些不寻常但苛刻的条件。

    更多信息来源

    这个 CBdeDatabase 类为创建表或执行查询等其他数据库功能提供了一个很好的基础。如果您想进行其他开发,您肯定需要一个 Borland 开发产品。Borland C++ 5.0 提供了大量 C 语言的 BDE 应用程序示例,可以直接访问 BDE API。此外,Borland 还提供了优秀的在线帮助文件和所有 API 函数的书面文档,在许多情况下附带 C 语言源代码示例。即便如此,C 语言示例通常不足以在不参考 Borland 开发产品提供的更完整的示例的情况下弄清楚如何进行某些 API 调用。
    © . All rights reserved.