Click here to Skip to main content
15,861,168 members
Articles / Database Development / SQL Server
Article

Accessing the Borland Database Engine (BDE) from Visual C++

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
17 Nov 19996 min read 222.4K   3.5K   45   20
  • Download demo project - 223 Kb
  • Download source - 16.2 Kb
  • Sample Image

    The Borland Database Engine (BDE) is the database engine provided by Borland (now Inprise) for access to Paradox and dBase databases s well as a few other formats. It provides the database interface for Borland products such as Borland C++, Borland C++ Builder, Borland Delphi, and Borland J Builder. Borland provides library files and header files to facilitate direct access to the BDE API. Borland also provides fairly extensive documentation of each API function, usually including samples in C and Pascal. Borland compilers, such as Borland C++ 4.5 and 5.0 also include example programs using direct API calls.

    Borlands's equivalent of MFC is the Object Windows Library (OWL). OWL provides a C++ interface for the BDE. However, with the decline in popularity of Borland compilers in favor of Visual C++ and MFC, and the almost total disappearance of Borland's support for OWL in favor of component based compilers such as C++ Builder, an MFC based class interface to the BDE API is now much more desireable.

    Unfortunately, the API function calls can be quite complex and multiple API function calls are required just to get a database field value. Any function may fail returning an error code that must always be checked. Therefore, it was well worth my time to develop a class wrapper and associated exception handling class for the BDE API function calls. This article presents these classes with a test-bed program (shown below) for reading and writing data to and from a table using each of the available Paradox data types.

    The classes provided here make extensive use of the MFC classes CString and COleDateTime for reading and writing string and date/time information to and from tables. This article also provides a nice example of the development and use of user-defined CException-derived exception handlers, with enhanced error messages.

    Shown above is the test application. In the example data table, there is one field for each type. Some types have two entries in the dialog above in order to test the type conversions possible in the CBdeDatabase class. One field accepts a field value as a string, and the other accepts a field value in it's native type (e.g., you can set an integer field by passing an integer value, or a string value).

    Why would you want to access the Borland Database Engine from Visual C++ programs?

    1. Paradox tables past version 5.0 are not supported by Jet. If you want to access these tables, direct use of the BDE is the best way.
    2. Although I have not tested this class or the BDE with multi-threading, the BDE, unlike Jet, is supposedly safe for multi-threading.
    3. BDE provides access to dBase files with the cumbersome COM overhead of Jet or ADO, and does not use those horrid OLEVARIANT types!
    4. Database access through Microsoft seems to be getting more complicated rather than easier and they keep changing the APIs. The BDE API has stayed the same since the beginning.
    5. Speed! Although I haven't done formal testing, this class for direct access to the BDE provides much faster database access than Jet or even the Borland Delphi database objects. I have never seen any database access as fast as the applications in which I used this class.

    How to set up a project to access the BDE

    1. You must have the BDE installed on your computer. There are various versions of the BDE out there so pay attention to version control issues.

    2. The BDE path must be included in your PATH environment variable for the computer. BDE applications don't normally add this, so you will probably have to do it yourself.

    3. Borland's web site provides the .lib file file that you must link with your project. These are also included with this example project.

    4. Borland's web site also provides the Ms-idapi.h header file that you must include in your project. These are also included with this example project.

    5. Includes the files provided here in your project. These include

    BdeDatabase.h
    Interface for the BDE API class wrapper.
    BdeDatabase.cpp
    Implementation of the BDE API class wrapper.
    BdeException.h
    Interface for the BDE exception handler.
    BdeException.cppp
    Implementation of the BDE exception handler.

    CBdeDatabase -- The BDE Class Wrapper

    The CBdeDatabase class provided here allows you to read and write data to and from dBase and Paradox tables using all non-BLOB types, and simple memo strings for BLOBs in Paradox tables (it crashes when reading dBase memos for some reason). Related functionality such as getting field names, and table navigation is also supported.

    Essentially, one instance of the class is create for each table you want to access. The order of events is quite simple.

    1. Call Initialize at program startup to initialize the BDE.
    2. To open a connection to a table, call OpenDatabase providing the table name and path.
    3. Perform your operations on the table.
    4. Call CloseDatabase to disconnect from the table.
    5. Call Uninitialize before program exit.

    The CBdeDatabase class here provides only a small fraction of the functionality of the BDE API, although for many projects, it is the most important.

    /////////////////////////////////////////////////////////////////
    //  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 -- The BDE Exception Handler

    To provide structured exception handling for my BDE class, I developed CBdeException. Even a simple task such as determining if a field exists requires a series of function calls, each of which could return an error code. Many CBdeDatabase member functions allocate memory as well which must be checked for failure. With each BDE API function call, I check the return code, and throw an exception on error.

    Many API's are notorious for providing terse and uninformative error messages, and the BDE API is no exception. For that reason, I have enhanced the error reporting the CBdeException class extensively, providing a detailed error message, the table and database name that produced the error, and finally the error string reported by the 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__

    Redistribution Issues

    If you decide to write an MFC application with access to the BDE, be aware of a few redistribution issues. First, you must have the BDE directory in the PATH environment variable. Most BDE applications, such as those written in Delphi, do not require this, so you will probably have to do it yourself. Second, if you want to distribute the BDE, refer to the Borland redistribution agreement first. Although redistribution is free, they have had some unusual caveats in the past.

    Where to go for More Information

    This CBdeDatabase class provides a nice foundation for additional database functions such as creating tables or performing queries. If you want to do additional development, you will definately need a Borland development product. Borland C++ 5.0 has a large selection of example BDE applications in C accessing the BDE API directly. Also, Borland provides a good on-line help file and written documentation of all of the API functions with C source examples of each in many cases. Even so, the C examples are usually not sufficient to figure out how to make some API calls without the more complete examples provided with the Borland development products.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    President Starpoint Software Inc.
    United States United States
    Bob Pittenger is founder and President of Starpoint Software Inc. He holds a B.A. degree from Miami University, M.S. and Ph.D. degrees from Purdue University, and an MBA from Xavier University. He has been programming since 1993, starting with Windows application development in C++/MFC and moving to C# and .NET around 2005 and is a .NET Microsoft Certified Professional Developer.

    Bob is the author of two books:
    Billionaire: How the Ultra-Rich Built Their Fortunes Through Good and Evil and What You Can Learn from Them
    and
    Wealthonomics: The Most Important Economic and Financial Concepts that Can Make You Rich Fast.
    Visit http://www.billionairebook.net for more information.

    Comments and Discussions

     
    GeneralDbiInit(NULL); is failing, what could be the issue Pin
    snaramsetti23-Sep-09 4:52
    snaramsetti23-Sep-09 4:52 
    GeneralRe: DbiInit(NULL); is failing, what could be the issue Pin
    RobertAWells6-Nov-23 5:34
    RobertAWells6-Nov-23 5:34 
    General"Trying to load multiple IDAPIxx.DLL" error resolution Pin
    dc_200031-Jul-07 21:40
    dc_200031-Jul-07 21:40 
    Generalthank u very much for ur code Pin
    ahmad_jafari17-Aug-06 2:47
    ahmad_jafari17-Aug-06 2:47 
    Smile | :) ur code help me so. i used it easy and with no problem.thank u very much.
    GeneralOpen where password <> NULL Pin
    BASH_0526-Nov-04 6:32
    BASH_0526-Nov-04 6:32 
    GeneralRe: Open where password <> NULL Pin
    RobertZ018-Feb-05 1:05
    RobertZ018-Feb-05 1:05 
    GeneralProblem In Data Type Pin
    mahrooian20-Oct-04 1:53
    sussmahrooian20-Oct-04 1:53 
    GeneralIdapi runtime-link Pin
    dezmont15-May-04 16:17
    dezmont15-May-04 16:17 
    QuestionHow To Execute an SQL Statement and Get The Resultset Pin
    Vikrant Vikrant4-Aug-03 2:56
    Vikrant Vikrant4-Aug-03 2:56 
    GeneralError in GetFieldAsString function Pin
    Anonymous9-Jan-03 5:08
    Anonymous9-Jan-03 5:08 
    GeneralWhen I open another BDE app,the demo doesn't work! Pin
    6-Feb-02 15:35
    suss6-Feb-02 15:35 
    GeneralBDE is work Pin
    6-Feb-02 15:31
    suss6-Feb-02 15:31 
    GeneralRe: BDE is work Pin
    RobertAWells6-Nov-23 5:35
    RobertAWells6-Nov-23 5:35 
    GeneralIsEof is worknig wrong for me... Pin
    23-Jan-01 4:10
    suss23-Jan-01 4:10 
    GeneralRe: IsEof is worknig wrong for me... Pin
    10-Apr-01 18:31
    suss10-Apr-01 18:31 
    GeneralRe: IsEof is worknig wrong for me... Pin
    10-Apr-01 18:31
    suss10-Apr-01 18:31 
    GeneralRe: IsEof is worknig wrong for me... Pin
    10-Apr-01 18:31
    suss10-Apr-01 18:31 
    GeneralReading the wrong config file Pin
    Abel Berenstein10-Jun-00 9:32
    sussAbel Berenstein10-Jun-00 9:32 
    GeneralSource is here Pin
    Simon Capewell8-Mar-00 4:13
    Simon Capewell8-Mar-00 4:13 
    GeneralSource Not Found! Pin
    Chun Byung Jun15-Feb-00 19:56
    sussChun Byung Jun15-Feb-00 19:56 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.