Click here to Skip to main content
15,891,431 members
Articles / Database Development / SQL Server

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 225K   3.5K   45  
/////////////////////////////////////////////////////////////////
//  BdeDatabase.cpp -- Implementation of 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, 



#include "stdafx.h"
#include "BdeDatabase.h"
#include "BdeException.h"

BOOL CBdeDatabase::m_bInitialized = FALSE;

///////////////////////////////////////////////////////////////////
// CBdeDatabase Construction/Destruction

CBdeDatabase::CBdeDatabase()
{
	m_hDb = NULL;
	m_hCursor = NULL;
  m_szTableName[0] = '\0';
	m_szDatabaseName[0] = '\0';
	m_szPrivateDir[0] = '\0';
	m_pEditRecordBuffer = NULL;
	m_nEditMode = 0;
	m_nTableType = -1;
}


CBdeDatabase::~CBdeDatabase()
{
	// perform any cleanup here
	CloseDatabase();
}





////////////////////////////////////////////////////////////////////
// CBdeDatabase Operations

/*BOOL CBdeDatabase::OpenDatabase(LPCTSTR szAlias, BOOL bReadOnly, BOOL bExclusive)
{


}	*/


// function to open a database given the full path and table name in a single string
// Table type is determined by file extension
BOOL CBdeDatabase::OpenDatabase(LPCTSTR szFullPath, 
	BOOL bReadOnly, BOOL bExclusive, LPCTSTR szPrivateDir)
{
	// split the file name into it's different parts
  char drive[_MAX_DRIVE];
  char dir[_MAX_DIR];
  char fname[_MAX_FNAME];
  char ext[_MAX_EXT];
  _splitpath(szFullPath, drive, dir, fname, ext );

	// use the file extension to determine the file type
	int nTableType;
	if (_stricmp(ext, ".txt") == 0) nTableType = TABLETYPE_TEXT;
	else if (_stricmp(ext, ".csv") == 0) nTableType = TABLETYPE_TEXT;
	else if (_stricmp(ext, ".dbf") == 0) nTableType = TABLETYPE_DBASE;
	else nTableType = TABLETYPE_PARADOX;

	CString strPath;
	strPath.Format("%s%s", drive, dir);

	return OpenDatabase(strPath, fname, nTableType, bReadOnly, bExclusive, szPrivateDir);
}


// function to open a database given a separate path and table name
BOOL CBdeDatabase::OpenDatabase(LPCTSTR szPath, LPCTSTR szTableName, int nTableType,
	BOOL bReadOnly, BOOL bExclusive, LPCTSTR szPrivateDir)
{
	if (!CheckInitialization()) return FALSE;

	// If database is active, then throw and exception
	if (m_hDb != NULL || m_hCursor != NULL)
	{
		throw new CBdeException(DBIERR_NONE, BDEEXERR_ALREADYOPEN,
			m_szTableName, m_szDatabaseName, "");
		return FALSE;
	}
	

	DBIResult dbiResult;
	CString s;
	DBIOpenMode eOpenMode;
	DBIShareMode eShareMode;

	// copy the string constants to the non-const member values
	strncpy(m_szTableName, szTableName, DBIMAXNAMELEN-1);
	strncpy(m_szDatabaseName, szPath, 254);
	if (szPrivateDir != NULL)
		strncpy(m_szPrivateDir, szPrivateDir, 254);
	else
		strncpy(m_szPrivateDir, "", 254);

	// Set read only and share mode values
	if (bReadOnly) eOpenMode = dbiREADONLY;
	else eOpenMode = dbiREADWRITE;

	if (bExclusive) eShareMode = dbiOPENEXCL;
	else eShareMode = dbiOPENSHARED;



	// Open the database
	dbiResult = DbiOpenDatabase(
    NULL,       	      // Database name - NULL for standard database
    NULL,          	      // Database type - NULL for standard database
    eOpenMode,  	      // Open mode - Read/Write or Read only

    eShareMode, 	      // Share mode - Shared or Exclusive
    NULL,          	      // Password - not needed for the STANDARD database
    NULL,          	      // Number of optional parameters
    NULL,          	      // Field Desc for optional parameters
    NULL,          	      // Values for the optional parameters
    &m_hDb);          	      // Handle to the database

	if (dbiResult != DBIERR_NONE)
	{
		s.Format("Failed to open database %s.", m_szDatabaseName);
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
		return FALSE;
	}

	return OpenDatabaseHelper(nTableType, eOpenMode, eShareMode, szPrivateDir);
}


// helper function to open the database, it actually opens the tables
// after a database has been opened.  It is here to support the different
// variations on opening a database
BOOL CBdeDatabase::OpenDatabaseHelper(int nTableType,
	DBIOpenMode eOpenMode, DBIShareMode eShareMode, LPCTSTR szPrivateDir)
{
	// before entering this function, the database must have been opened
	// but not the table
	ASSERT(m_hDb != NULL);
	ASSERT(m_hCursor == NULL);
		
	// local variables
	DBIResult dbiResult;
	CString s;
  CHAR szTableType[DBIMAXNAMELEN];
	pBYTE pRecBuf = NULL;
	CURProps CurProps; // cursor properties

	// save the table type
	m_nTableType = nTableType;

	// set the table directory
  dbiResult = DbiSetDirectory(
		m_hDb, // Handle to the database which is being modified
    m_szDatabaseName);	 // The new working directory

	if (dbiResult != DBIERR_NONE)
	{
		s.Format("Failed to open database %s.", m_szDatabaseName);
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
		goto l_Failure;
	}

	
	// set the private dir
	if (szPrivateDir != NULL)
	{
		dbiResult = DbiSetPrivateDir(m_szPrivateDir);
		if (dbiResult != DBIERR_NONE)
		{
			s.Format("Failed to set private directory %s.", m_szPrivateDir);
			throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
			goto l_Failure;
		}
	} // end of setting private dir


	// get the table type and open the table
	// Use paradox by default
	if (nTableType == TABLETYPE_TEXT)
		strcpy(szTableType, szASCII);
	else if (nTableType == TABLETYPE_DBASE)
		strcpy(szTableType, szDBASE);
	else strcpy(szTableType, szPARADOX);

	// Open the table
	dbiResult = DbiOpenTable(                                                    // Step 7
    m_hDb, 				// Handle to the standard database
    m_szTableName,     		// Name of the table
    szTableType,     		// Type of the table - only used for local tables
    NULL,          		// Index Name - Optional
    NULL,          		// IndexTagName - Optional. Only used by dBASE
    0,             		// IndexId - 0 = Primary.

    eOpenMode,  		// Open Mode - Read/Write or Read Only
    eShareMode, 		// Shared mode - SHARED or EXCL
    xltFIELD,      		// Translate mode - Almost always xltFIELD
    FALSE, 			// Unidirectional cursor movement.
    NULL,				// Optional Parameters.
    &m_hCursor);         		// Handle to the cursor

	if (dbiResult != DBIERR_NONE)
	{
		s.Format("Failed to open table %s in database %s.", 
			m_szTableName, m_szDatabaseName);
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
		goto l_Failure;
	}

	// get the cursor properties here
  dbiResult = DbiGetCursorProps(
		m_hCursor, // Handle to the cursor
    &CurProps);		// Properties of the cursor (table)

	if (dbiResult != DBIERR_NONE)
	{
		s.Format("Failed to get database cursor properties in table %s.", m_szTableName);
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
		goto l_Failure;
	}

	// allocate the memory for the record buffer
	pRecBuf = (pBYTE) malloc(CurProps.iRecBufSize * sizeof(BYTE)); 
	if (pRecBuf == NULL)
	{
		s.Format("Insufficient memory to open table %s.", m_szTableName);
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, s);
		goto l_Failure;
	}
	free(pRecBuf);
	
	if (CurProps.iFields > 0) MoveFirst();

	return TRUE;


	// goto here for cleanup
	l_Failure:;
	CloseDatabase();
	return FALSE;
} // end of OpenDatabase function





// function to initialize the database
BOOL CBdeDatabase::Initialize()
{
	// make sure not already initialized
	if (m_bInitialized) return TRUE;

	// initialize the BDE
	DBIResult dbiResult = DbiInit(NULL);

	// Check error code
	if (dbiResult == DBIERR_NONE)
	{
		m_bInitialized = TRUE;
		return TRUE;
	}

	Check(dbiResult, "Failed to initialize Borland Database Engine.");
	return FALSE;
}




// function to close the database
BOOL CBdeDatabase::CloseDatabase()
{
	DBIResult dbiResult;
	CString s;

	if (m_hDb == NULL) return TRUE; // database was not open to begin with

	// cancel any pending edits
	Cancel();

	// Close the cursor
  if (m_hCursor != NULL)
	{
		dbiResult = DbiCloseCursor(&m_hCursor);
		if (dbiResult != DBIERR_NONE)
		{
			s.Format("Failed to close table %s", m_szTableName);
			Check(dbiResult, s);
			return FALSE;
		}
	}
	m_hCursor = NULL;

	// close the database	
  dbiResult = DbiCloseDatabase(&m_hDb);
	if (dbiResult != DBIERR_NONE)
	{
		s.Format("Failed to close database %s", m_szDatabaseName);
		Check(dbiResult, s);
		return FALSE;
	}

	m_hDb = NULL;	
	m_szTableName[0] = '\0';
	m_szDatabaseName[0] = '\0';
	m_nTableType = -1;
	return TRUE;
}	 



// uninitialize the database, this should be last BDE call ever
void CBdeDatabase::Uninitialize()
{
	DbiExit();			
	m_bInitialized = FALSE;
}




/////////////////////////////////////////////////////////////////////
//  Functions to GetEnabling flags

BOOL CBdeDatabase::EnableFirst(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == TRUE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnableNext(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == TRUE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnablePrior(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == TRUE) return FALSE;
	return TRUE;
}


BOOL CBdeDatabase::EnableLast(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == TRUE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnableInsert(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == TRUE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnableEdit(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == TRUE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnablePost(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == FALSE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnableCancel(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == FALSE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnableAppend(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == TRUE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnableDelete(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	if (pBdeDb->GetEditMode() == TRUE) return FALSE;
	return TRUE;
}


BOOL CBdeDatabase::EnableOpen(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == TRUE) return FALSE;
	return TRUE;
}

BOOL CBdeDatabase::EnableClose(CBdeDatabase* pBdeDb)
{
	if (pBdeDb == NULL) return FALSE;
	if (CBdeDatabase::m_bInitialized == FALSE) return FALSE;
	if (pBdeDb->IsActive() == FALSE) return FALSE;
	return TRUE;
}

	  
/////////////////////////////////////////////////////////////////////
//  Validity checking functions

// function to display an error message from BDE error code
// Most of this is copied from BDE API Help
// this function is not used within the class because all errors are handled as exceptions
DBIResult CBdeDatabase::Check(DBIResult ErrorValue, LPCTSTR szMessage)
{
	char        dbi_status[DBIMAXMSGLEN * 5] = {'\0'};
	DBIMSG      dbi_string = {'\0'};
	DBIErrInfo  ErrInfo;
	CString s;

	if (ErrorValue == DBIERR_NONE) return ErrorValue;

	DbiGetErrorInfo(TRUE, &ErrInfo);

	if (ErrInfo.iError == ErrorValue)
	{
		wsprintf(dbi_status, "%s", ErrInfo.szErrCode);

		if (strcmp(ErrInfo.szContext1, ""))
			wsprintf(dbi_status, "%s\r\n    %s", dbi_status, ErrInfo.szContext1);

		if (strcmp(ErrInfo.szContext2, ""))
			wsprintf(dbi_status, "%s\r\n    %s", dbi_status, ErrInfo.szContext2);
		if (strcmp(ErrInfo.szContext3, ""))
			wsprintf(dbi_status, "%s\r\n    %s", dbi_status, ErrInfo.szContext3);
		if (strcmp(ErrInfo.szContext4, ""))
			wsprintf(dbi_status, "%s\r\n    %s", dbi_status, ErrInfo.szContext4);
	}
	else
	{
		 DbiGetErrorString(ErrorValue, dbi_string);
		 wsprintf(dbi_status, "%s", dbi_string);
	}

	if (szMessage != NULL)
	{
		s.Format("%s\n\nBorland Database Engine returned the following error:\n%s",
			szMessage, dbi_status);
	}
	else
	{
		s.Format("Borland Database Engine Error:\n%s",
			dbi_status);
	}

	AfxMessageBox(s, MB_OK | MB_ICONEXCLAMATION);
  return ErrorValue;
}




// function to make sure the BDE has been initialize before any function calls
BOOL CBdeDatabase::CheckInitialization(LPCTSTR szOperation /* = NULL */)
{
	if (m_bInitialized) return TRUE;

	// if this happens, it is not BDE's fault, it is the programmers!
	throw new CBdeException(DBIERR_NONE, BDEEXERR_NOTINITIALIZED,
		m_szTableName, m_szDatabaseName, szOperation);
	return FALSE;
}


// function throws an exception if the cursor position is not valid
// meaning a database has not been opened
BOOL CBdeDatabase::CheckValidCursor(LPCTSTR szOperation /* = NULL */)
{
	// check for initialization
	if (!CheckInitialization(szOperation)) return FALSE;

	// check the validity of the cursor
	if (m_hCursor != NULL && m_hDb != NULL) return TRUE;
	throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDCURSOR,
		m_szTableName, m_szDatabaseName, szOperation);
	return FALSE;
}

// function throws an exception if the database is NOT in edit mode
BOOL CBdeDatabase::CheckEditMode(LPCTSTR szOperation /* = NULL */)
{
	if (m_nEditMode != EDITMODE_NONE) return TRUE;
	throw new CBdeException(DBIERR_NONE, BDEEXERR_NOTINEDITMODE,
		m_szTableName, m_szDatabaseName, szOperation);
	return FALSE;
}


// function throws an exception if the database IS in edit mode
BOOL CBdeDatabase::CheckNotEditMode(LPCTSTR szOperation /* = NULL */)
{
	if (m_nEditMode == EDITMODE_NONE) return TRUE;
	throw new CBdeException(DBIERR_NONE, BDEEXERR_ALREADYINEDITMODE,
		m_szTableName, m_szDatabaseName, szOperation);
	return FALSE;
}




/////////////////////////////////////////////////////////////
//  Database Navigation Functions

void CBdeDatabase::MoveFirst()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to move to beginning of table.")) return;
	if (!CheckNotEditMode("Failed to move to beginning of table.")) return;

  DBIResult dbiResult = DbiSetToBegin(m_hCursor);
	if (dbiResult == DBIERR_NONE || dbiResult == DBIERR_BOF)
	{
		dbiResult = DbiGetNextRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
		return;
	}

	CString s;
	s.Format("Failed to move to the beginning of table %s.", m_szTableName);
	throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
	dbiResult = DbiGetNextRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
}


void CBdeDatabase::MoveNext()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to move to next record.")) return;
	if (!CheckNotEditMode("Failed to move to next record.")) return;

	DBIResult dbiResult = DbiGetNextRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
	if (dbiResult == DBIERR_NONE) return;
	if (dbiResult == DBIERR_EOF)
	{
		dbiResult = DbiGetPriorRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
		return;
	}
	CString s;
	s.Format("Failed to move to the next record in table %s.", m_szTableName);
	throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
}

void CBdeDatabase::MovePrior()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to move to previous record.")) return;
	if (!CheckNotEditMode("Failed to move to previous record.")) return;

	DBIResult dbiResult = DbiGetPriorRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
	if (dbiResult == DBIERR_NONE) return;
	if (dbiResult == DBIERR_BOF)
	{
		dbiResult = DbiGetNextRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
		return;
	}
	CString s;
	s.Format("Failed to move to the previous record in table %s.", m_szTableName);
	throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
}


void CBdeDatabase::MoveLast()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to move to end of table.")) return;
	if (!CheckNotEditMode("Failed to move to end of table.")) return;

  DBIResult dbiResult = DbiSetToEnd(m_hCursor);
	if (dbiResult == DBIERR_NONE || dbiResult == DBIERR_EOF)
	{
		dbiResult = DbiGetPriorRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
		return;	
	}
	CString s;
	s.Format("Failed to move to the end of table %s.", m_szTableName);
	throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
}


LONG CBdeDatabase::GetRecordCount()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to get number of records.")) return -1L;

	ASSERT(m_hCursor != NULL);
	UINT32 nRecNum;
	DBIResult dbiResult = DbiGetRecordCount(m_hCursor, &nRecNum);

	if (dbiResult == DBIERR_NONE) return (LONG)nRecNum;
	CString s;
	s.Format("Failed to determine the number of records in table %s.", m_szTableName);
	throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, s);
  return -1L;
}


BOOL CBdeDatabase::IsBOF()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor()) return FALSE;
	if (!CheckNotEditMode()) return FALSE;

	DBIResult dbiResult;
	BOOL bRetVal;

	// Check if we are at the end of the table.
	dbiResult = DbiGetPriorRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
	if (dbiResult == DBIERR_BOF) bRetVal = TRUE;
	else bRetVal = FALSE;

	// Put the cursor back to where it was before entering this function.
	DbiGetNextRecord(m_hCursor, dbiNOLOCK, NULL, NULL);

	return bRetVal;
}


BOOL CBdeDatabase::IsEOF()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor()) return FALSE;
	if (!CheckNotEditMode()) return FALSE;

	DBIResult dbiResult; 
	BOOL bRetVal;

	// Check if we are at the end of the table.
	dbiResult = DbiGetNextRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
	if (dbiResult == DBIERR_EOF) bRetVal = TRUE;
	else bRetVal = FALSE;

	// Put the cursor back to where it was before entering this function.
	DbiGetPriorRecord(m_hCursor, dbiNOLOCK, NULL, NULL);

	return bRetVal;
}








/////////////////////////////////////////////////////////////////////////////
//  Functions to read field values


CString CBdeDatabase::GetFieldAsString(UINT16 nFieldNumber, BOOL* pbBlank)
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to get field value.")) return "";
	if (!CheckNotEditMode("Failed to get field value.")) return "";

	DBIResult dbiResult = DBIERR_NONE;
	CString strValue;
	CString strError;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc = NULL; // field information
  pBYTE pRecBuf = NULL; // Record Buffer
	int nBdeExError = 0;

	
	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return "";
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return "";
	}


	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return "";
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return "";
	}

		
	// save the size and type of the field
	UINT16 nSize = pFldDesc[nFieldNumber-1].iLen;
	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;
	UINT16 nFieldSubType = pFldDesc[nFieldNumber-1].iSubType;
	UINT16 nUnits = pFldDesc[nFieldNumber-1].iUnits2;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;


	// allocate the record buffer for the table
  pRecBuf = (pBYTE) malloc(curProps.iRecBufSize * sizeof(BYTE));
	if (pRecBuf == NULL)
  {
		strError.Format("Insufficient memory to get field number %d.", nFieldNumber);
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
    return "";
  }

	// fill the record buffer with information
	dbiResult = DbiGetRecord(m_hCursor, dbiNOLOCK, pRecBuf, NULL);
	if (dbiResult != DBIERR_NONE)
	{
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
	  return "";
	}

	// allocate memory for the string buffer to recieve data
	pBYTE pDestBuf = (pBYTE)malloc(nSize*sizeof BYTE);
	memset(pDestBuf, 0, nSize*sizeof BYTE);


	if (pDestBuf == NULL)
	{
		// Throw memory exception here
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
		return "";
	}
	
	// get the actual field value

	switch (nFieldType)
	{
    case fldBYTES: // Bytes
    case fldZSTRING: // String
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)pDestBuf, pbBlank);
			strValue.Format("%s", (char*)pDestBuf);
			break;
		}
		case fldFLOAT:
		{
			double f;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&f, pbBlank);
			if (*pbBlank == FALSE)
			{
				if (nFieldSubType == fldstMONEY)
				{
					strValue.Format("$%.2lf", f);
				}
				else
				{
					strValue.Format("%g", f);
				}
			}
			break;
		}
		case fldBCD:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, 
				(pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
			{
				double lfFloat;
				DbiBcdToFloat((FMTBcd *)pDestBuf,	&lfFloat);
        strValue.Format("%.*lf", nUnits, lfFloat);
			}
			break;
		}

		case fldDATE:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, 
				(pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
			{
				// Here is a difficult situation...
				// dates are returned as 4 bytes, but in the 32-bit world
				// the DATE type is 8 bytes.  Typcasting directly to DATE
				// does not work.  We have to type cast to 32 bit int, then
				// to 64-bit int, and then to DATE
				INT32 n32 = (*(INT32 *)pDestBuf);
				strValue = FormatDate(n32);
			}
			break;
		}
		case fldTIME:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, 
				(pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
			{
				strValue = FormatTime(*(TIME *)pDestBuf);
			}
			break;
		}
		case fldTIMESTAMP:
		{
			// size of timestamp is 8
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, 
				(pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
			{
				TIMESTAMP dt = *(TIMESTAMP*)pDestBuf;
				strValue = FormatTimeStamp(dt);
			}
			break;
		}
		case fldINT16:
		{
			INT16 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				strValue.Format("%d", n);
			}
			break;
		}
		case fldUINT16:
		{
			UINT16 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				strValue.Format("%d", n);
			}
			break;
		}
		case fldINT32:
		{
			INT32 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				strValue.Format("%d", n);
			}
			break;
		}
		case fldUINT32:
		{
			UINT32 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				strValue.Format("%d", n);
			}
			break;
		}
		case fldBOOL:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
      {
        if (*(BYTE*)pDestBuf == 0) strValue = "FALSE";
        else strValue = "TRUE";
      }
			break;
		}
		case fldBLOB:	// NOTE:  This does not work on dBase files.
		{
			if (nFieldSubType == fldstMEMO)
			{
				UINT32 ulBlobSize; // Size of the BLOB

				// Open the blob
				dbiResult = DbiOpenBlob(m_hCursor, pRecBuf, nFieldNumber, dbiREADONLY);
				if (dbiResult != DBIERR_NONE) break;

        // Determine the size of the BLOB.
				dbiResult = DbiGetBlobSize(m_hCursor, pRecBuf, nFieldNumber, &ulBlobSize);
				if (dbiResult != DBIERR_NONE) break;

				// Get the BLOB from the table.
				dbiResult = DbiGetBlob(m_hCursor, pRecBuf, nFieldNumber, 0,
					ulBlobSize, (pBYTE)pDestBuf, &ulBlobSize);
				pDestBuf[ulBlobSize + 1] = '\0';
				strValue.Format("%s", pDestBuf);

				DbiFreeBlob(m_hCursor, pRecBuf, nFieldNumber);
				
				if (dbiResult != DBIERR_NONE) break;
			}
			else
			{
				// Handle non-memo formats here
				nBdeExError = BDEEXERR_UNSUPPORTEDBLOBTYPE;
			}
			break;
		}
		default:
		{
			nBdeExError = BDEEXERR_UNSUPPORTEDFIELDTYPE;
			break;
		}
	} // end of swtich

	// process the final error codes
	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to retrieve data for field %d.",
			nFieldNumber);
	  free(pRecBuf);
		free(pDestBuf);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, 
			m_szDatabaseName, strError);
		return "";
	} 

	free(pDestBuf);
  free(pRecBuf);
	return strValue;
} // end of GetFieldAsString



// function to get the field value as an integer
// throws an exception for incompatible data types
LONG CBdeDatabase::GetFieldAsInteger(UINT16 nFieldNumber, BOOL* pbBlank)
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to get field value.")) return 0L;
	if (!CheckNotEditMode("Failed to get field value.")) return 0L;

	DBIResult dbiResult = DBIERR_NONE;
	LONG nValue = 0L;
	CString strError;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
  pBYTE pRecBuf; // Record Buffer
	int nBdeExError = 0;

	
	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0L;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return 0L;
	}


	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return 0L;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0L;
	}

		
	// save the size and type of the field
	UINT16 nSize = pFldDesc[nFieldNumber-1].iLen;
	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;


	// allocate the record buffer for the table
  pRecBuf = (pBYTE) malloc(curProps.iRecBufSize * sizeof(BYTE));
	if (pRecBuf == NULL)
  {
		strError.Format("Insufficient memory to get field number %d.", nFieldNumber);
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
    return 0L;
  }

	// fill the record buffer with information
	dbiResult = DbiGetRecord(m_hCursor, dbiNOLOCK, pRecBuf, NULL);
	if (dbiResult != DBIERR_NONE)
	{
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
	  return 0L;
	}

	// allocate memory for the string buffer to recieve data
	pBYTE pDestBuf = (pBYTE)malloc(nSize*sizeof BYTE);
	memset(pDestBuf, 0, nSize*sizeof BYTE);


	if (pDestBuf == NULL)
	{
		// Throw memory exception here
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
		return 0L;
	}
	
	// get the actual field value

	switch (nFieldType)
	{
		case fldINT16:
		{
			INT16 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				nValue = (LONG)n;
			}
			break;
		}
		case fldUINT16:
		{
			UINT16 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				nValue = (LONG)n;
			}
			break;
		}
		case fldINT32:
		{
			INT32 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				nValue = (LONG)n;
			}
			break;
		}
		case fldUINT32:
		{
			UINT32 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				nValue = (LONG)n;
			}
			break;
		}
		case fldFLOAT: // need to handl floats for dBase files
		{
			double f;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&f, pbBlank);
			if (*pbBlank == FALSE)
			{
				nValue = (LONG)f;
			}
			break;
		}
		case fldBOOL:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
      {
        if (*(BYTE*)pDestBuf == 0)
        {
					nValue = 0L;
        }
        else
        {
					nValue = 1L;
        }
      }
			break;
		}
		default:
		{
			nBdeExError = BDEEXERR_FIELDNOTINTEGER;
		}
	} // end of swtich

	// process the final exceptions
	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to retrieve data for field %d.",
			nFieldNumber);
	  free(pRecBuf);
		free(pDestBuf);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, 
			m_szDatabaseName, strError);
		return 0L;
	} 

	free(pDestBuf);
  free(pRecBuf);
	return nValue;
} // end of GetFieldAsInteger



// function to get the field value as a float
// works on float, currency, BCD, and all integer types
double CBdeDatabase::GetFieldAsFloat(UINT16 nFieldNumber, BOOL* pbBlank)
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor()) return 0.0;
	if (!CheckNotEditMode()) return 0.0;

	DBIResult dbiResult = DBIERR_NONE;
	double fValue = 0.0;
	CString strError;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
  pBYTE pRecBuf; // Record Buffer
	int nBdeExError = 0;

	
	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0.0;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return 0.0;
	}

	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return 0.0;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0.0;
	}

		
	// save the size and type of the field
	UINT16 nSize = pFldDesc[nFieldNumber-1].iLen;
	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;


	// allocate the record buffer for the table
  pRecBuf = (pBYTE) malloc(curProps.iRecBufSize * sizeof(BYTE));
	if (pRecBuf == NULL)
  {
		strError.Format("Insufficient memory to get field number %d.", nFieldNumber);
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
    return 0.0;
  }

	// fill the record buffer with information
	dbiResult = DbiGetRecord(m_hCursor, dbiNOLOCK, pRecBuf, NULL);
	if (dbiResult != DBIERR_NONE)
	{
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
	  return 0.0;
	}

	// allocate memory for the string buffer to recieve data
	pBYTE pDestBuf = (pBYTE)malloc(nSize*sizeof BYTE);
	memset(pDestBuf, 0, nSize*sizeof BYTE);


	if (pDestBuf == NULL)
	{
		// Throw memory exception here
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
		return 0.0;
	}
	
	// get the actual field value
	switch (nFieldType)
	{
		case fldFLOAT:
		{
			double f;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&f, pbBlank);
			if (*pbBlank == FALSE)
			{
				fValue = f;
			}
			break;
		}
		case fldBCD:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, 
				(pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
			{
				double lfFloat;
				DbiBcdToFloat((FMTBcd *)pDestBuf,	&lfFloat);
				fValue = lfFloat;
			}
			break;
		}
		case fldINT16:
		{
			INT16 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				fValue = (double)n;
			}
			break;
		}
		case fldUINT16:
		{
			UINT16 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				fValue = (double)n;
			}
			break;
		}
		case fldINT32:
		{
			INT32 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				fValue = (double)n;
			}
			break;
		}
		case fldUINT32:
		{
			UINT32 n;
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)&n, pbBlank);
			if (*pbBlank == FALSE)
			{
				fValue = (double)n;
			}
			break;
		}
		default:
		{
			nBdeExError = BDEEXERR_FIELDNOTFLOAT;
		}
	} // end of swtich


	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to retrieve data for field %d.",
			nFieldNumber);
	  free(pRecBuf);
		free(pDestBuf);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, 
			m_szDatabaseName, strError);
		return 0.0;
	} 

	free(pDestBuf);
  free(pRecBuf);
	return fValue;
} // end of GetFieldAsFloat




COleDateTime CBdeDatabase::GetFieldAsDate(UINT16 nFieldNumber, BOOL* pbBlank)
{
	DBIResult dbiResult = DBIERR_NONE;
	int nBdeExError = 0;
	CString strError;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
  pBYTE pRecBuf; // Record Buffer
	COleDateTime dtRetVal = 0.0;
	dtRetVal.m_status = COleDateTime::null; 

	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to get field value.")) return dtRetVal;
	if (!CheckNotEditMode("Failed to get field value.")) return dtRetVal;

	
	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return dtRetVal;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return dtRetVal;
	}

	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return dtRetVal;
	}

	// get the field descriptors to get the type of the field
	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return dtRetVal;
	}

	// save the size and type of the field
	UINT16 nSize = pFldDesc[nFieldNumber-1].iLen;
	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;


	// allocate the record buffer for the table
  pRecBuf = (pBYTE) malloc(curProps.iRecBufSize * sizeof(BYTE));
	if (pRecBuf == NULL)
  {
		strError.Format("Insufficient memory to get field number %d.", nFieldNumber);
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
    return dtRetVal;
  }

	// fill the record buffer with information
	dbiResult = DbiGetRecord(m_hCursor, dbiNOLOCK, pRecBuf, NULL);
	if (dbiResult != DBIERR_NONE)
	{
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
	  return dtRetVal;
	}

	// allocate memory for the string buffer to recieve data
	pBYTE pDestBuf = (pBYTE)malloc(nSize*sizeof BYTE);
	memset(pDestBuf, 0, nSize*sizeof BYTE);


	if (pDestBuf == NULL)
	{
		// Throw memory exception here
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
		return dtRetVal;
	}
	
	// get the actual field value

	switch (nFieldType)
	{
		case fldDATE:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, 
				(pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
			{
				// Here is a difficult situation...
				// dates are returned as 4 bytes, but in the 32-bit world
				// the DATE type is 8 bytes.  Typcasting directly to DATE
				// does not work.  We have to type cast to 32 bit int, then
				// to 64-bit int, and then to DATE
				INT32 n32 = (*(INT32 *)pDestBuf);
				dtRetVal = DateToOleDateTime(n32);
			}
			break;
		}
		case fldTIME:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, 
				(pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
			{
				dtRetVal = TimeToOleDateTime((*(TIME *)pDestBuf));
			}
			break;
		}
		case fldTIMESTAMP:
		{
			// sie of timestamp is 8
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, 
				(pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
			{
				TIMESTAMP timestamp = *(TIMESTAMP*)pDestBuf;
				dtRetVal = TimeStampToOleDateTime(timestamp);
			}
			break;
		}
		default:
		{
			nBdeExError = BDEEXERR_FIELDNOTDATE;
		}
	} // end of swtich

	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to retrieve data for field %d.",
			nFieldNumber);
	  free(pRecBuf);
		free(pDestBuf);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, 
			m_szDatabaseName, strError);
		return dtRetVal;
	} 

	free(pDestBuf);
  free(pRecBuf);
	return dtRetVal;
} // end of GetFieldAsDate





// function to get the field value as an integer
// throws an exception for incompatible data types
BOOL CBdeDatabase::GetFieldAsBoolean(UINT16 nFieldNumber, BOOL* pbBlank)
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to get field value.")) return 0L;
	if (!CheckNotEditMode("Failed to get field value.")) return 0L;

	DBIResult dbiResult = DBIERR_NONE;
	BOOL bValue = FALSE;
	CString strError;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
  pBYTE pRecBuf; // Record Buffer
	int nBdeExError = 0;

	
	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
  }

	// make sure the field number is valid
	// TODO:  This should throw an exception
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}


	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return FALSE;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

		
	// save the size and type of the field
	UINT16 nSize = pFldDesc[nFieldNumber-1].iLen;
	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;


	// allocate the record buffer for the table
  pRecBuf = (pBYTE) malloc(curProps.iRecBufSize * sizeof(BYTE));
	if (pRecBuf == NULL)
  {
		strError.Format("Insufficient memory to get field number %d.", nFieldNumber);
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
    return FALSE;
  }

	// fill the record buffer with information
	dbiResult = DbiGetRecord(m_hCursor, dbiNOLOCK, pRecBuf, NULL);
	if (dbiResult != DBIERR_NONE)
	{
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
	  return FALSE;
	}

	// allocate memory for the string buffer to recieve data
	pBYTE pDestBuf = (pBYTE)malloc(nSize*sizeof BYTE);
	memset(pDestBuf, 0, nSize*sizeof BYTE);


	if (pDestBuf == NULL)
	{
		// Throw memory exception here
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}
	
	// get the actual field value

	switch (nFieldType)
	{
		case fldBOOL:
		{
			dbiResult = DbiGetField(m_hCursor, nFieldNumber, pRecBuf, (pBYTE)pDestBuf, pbBlank);
			if (*pbBlank == FALSE)
      {
        if (*(BYTE*)pDestBuf == 0) bValue = FALSE;
        else bValue = TRUE;
      }
			break;
		}
		default:
		{
			nBdeExError = BDEEXERR_FIELDNOTINTEGER;
		}
	} // end of swtich

	// process the final exceptions
	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to retrieve data for field %d.",
			nFieldNumber);
	  free(pRecBuf);
		free(pDestBuf);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, 
			m_szDatabaseName, strError);
		return FALSE;
	} 

	free(pDestBuf);
  free(pRecBuf);
	return bValue;
} // end of GetFieldAsBoolean




// returns the 1 based index of a field name
// returns 0 on error
int CBdeDatabase::GetFieldSize(int nFieldNumber)
{
	// Throw an exception if not in edit mode
	if (!CheckValidCursor("Failed to get field size.")) return 0;

	DBIResult dbiResult;
	CURProps CurProps;
	pFLDDesc pFldDesc;
	int nSize = -1;
	CString strError;

	dbiResult = DbiGetCursorProps(m_hCursor, &CurProps);

	if (dbiResult != DBIERR_NONE)
	{
		strError.Format("Failed to retrieve size of field %d.",
			nFieldNumber);
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return -1;
	}


	pFldDesc = (pFLDDesc)malloc(CurProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return -1;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);

	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return -1;
	}
		
	nSize = pFldDesc[nFieldNumber-1].iLen;
	free(pFldDesc);
	return nSize;
}
	

// returns the number of fields in the current table
// returns -1 on error
int CBdeDatabase::GetFieldCount()
{
	if (!CheckValidCursor("Failed to get field count.")) return -1;

	DBIResult dbiResult;
	CURProps CurProps;
	CString strError;
	CString strRetVal;

	dbiResult = DbiGetCursorProps(m_hCursor, &CurProps);

	if (dbiResult != DBIERR_NONE)
	{
		strError.Format("Failed to cursor properties in table %s.",
			m_szTableName);
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0;
	}

	return CurProps.iFields;
}


// function to get the filed type
// returns 0 on error
int CBdeDatabase::GetFieldType(int nFieldNumber)
{
	// Throw an exception if not in edit mode
	if (!CheckValidCursor("Failed to get field type.")) return FALSE;
	if (!CheckEditMode("Failed to get field type.")) return FALSE;

	DBIResult dbiResult;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
	CString strError;

	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return 0;
	}


	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return 0;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0;
	}

	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;

	free(pFldDesc); // clean up the field descriptor
	return (int)nFieldType;
}


// function to get the filed type
// returns 0 on error or not a blob
int CBdeDatabase::GetBlobType(int nFieldNumber)
{
	// Throw an exception if not in edit mode
	if (!CheckValidCursor("Failed to get field type.")) return FALSE;
	if (!CheckEditMode("Failed to get field type.")) return FALSE;

	DBIResult dbiResult;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
	CString strError;

	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return 0;
	}


	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return 0;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return 0;
	}

	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;
	UINT16 nFieldSubType = pFldDesc[nFieldNumber-1].iSubType;

	free(pFldDesc); // clean up the field descriptor

	if (nFieldType != fldBLOB) return 0;
	return (int)nFieldSubType;
}



// function to get the field name from it's number
CString CBdeDatabase::GetFieldName(int nFieldNumber)
{
	if (!CheckValidCursor("Failed to get field name.")) return "";

	DBIResult dbiResult;
	CURProps CurProps;
	pFLDDesc pFldDesc;
	CString strError;
	CString strRetVal;

	dbiResult = DbiGetCursorProps(m_hCursor, &CurProps);

	if (dbiResult != DBIERR_NONE)
	{
		strError.Format("Failed to cursor properties in table %s.",
			m_szTableName);
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return "";
	}

	// make sure the number is within the proper range
	if (nFieldNumber < 1 || nFieldNumber > CurProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, CurProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return "";
	}


	// allocate memory for the field descriptor
	pFldDesc = (pFLDDesc)malloc(CurProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return "";
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);

	if (dbiResult != DBIERR_NONE)
	{
		// Throw exception here
		strError.Format("Failed to get field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		free(pFldDesc);
		return "";
	}

	strRetVal.Format("%s", pFldDesc[nFieldNumber-1].szName);
	free(pFldDesc);
	return strRetVal;
}


// returns the 1 based index of a field name
// returns 0 on error
int CBdeDatabase::FieldNumberFromName(LPCTSTR szFieldName)
{
	if (!CheckValidCursor()) return 0;

	DBIResult dbiResult;
	CURProps CurProps;
	pFLDDesc pFldDesc;
	UINT16 nField;
	BOOL bFound = FALSE;
	CString strError;
	int nRetVal = 0;

	dbiResult = DbiGetCursorProps(m_hCursor, &CurProps);

	if (dbiResult != DBIERR_NONE)
	{
		strError.Format("Failed to cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return nRetVal;
	}


	pFldDesc = (pFLDDesc)malloc(CurProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return nRetVal;
	}


	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);

	if (dbiResult != DBIERR_NONE)
	{
		// TODO:  Throw exception here
		strError.Format("Failed to get field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		free(pFldDesc);
		return nRetVal;
	}

	for (nField = 0; nField < CurProps.iFields; nField++)
	{
		if (_stricmp(pFldDesc[nField].szName, szFieldName) == 0)
		{
			bFound = TRUE;
			nRetVal = pFldDesc[nField].iFldNum;
			free(pFldDesc);
			return nRetVal;
		}
	}
	free(pFldDesc);

	// did not find the name so throw an exception
		

	return nRetVal;
}



/////////////////////////////////////////////////////////////////////////
// Data type conversion functions

CString CBdeDatabase::FormatDate(INT32 Date)
{
	DBIResult   dbiResult;       // Return Value from IDAPI
	FMTDate     fmtDate;    // Date Format
	UINT16      uDay;       // Day portion of date
	UINT16      uMonth;     // Month portion of date
	INT16       iYear;      // Year portion of date
	CString strRetVal;
            
	// Get the formatting of the Date.
	dbiResult = DbiGetDateFormat(&fmtDate);
	if (dbiResult != DBIERR_NONE) return "";


	// Decode the date.
	dbiResult = DbiDateDecode(Date, &uMonth, &uDay, &iYear);
	if (dbiResult != DBIERR_NONE) return "";

	// Determine if date should be displayed year based.
	if (!(fmtDate.bFourDigitYear) && (fmtDate.bYearBiased))
	{
		iYear = (INT16)(iYear + 1900);
	}

	if (!(fmtDate.bFourDigitYear))
	{
		iYear = (INT16)(iYear - 1900);
	}

	// Make certain the seperator is not the
	//   escape character.
	if (!strcmp(fmtDate.szDateSeparator, "\\"))
	{
		strcpy(fmtDate.szDateSeparator, "/");
	}

	// Format the date.
	switch(fmtDate.iDateMode)
	{
			// MM/DD/YY - Month, Day, Year.
			case 0:
					strRetVal.Format("%0*d%s%0*d%s%0*d",
									1 + fmtDate.bMonthLeadingZero,
									uMonth,
									fmtDate.szDateSeparator,
									1 + fmtDate.bDayLeadingZero,
									uDay,
									fmtDate.szDateSeparator,
									2,
									iYear);
					break;
			// DD/MM/YY - Day, Month, Year
			case 1:
					strRetVal.Format("%0*d%s%0*d%s%0*d",
									1 + fmtDate.bDayLeadingZero,
									uDay,
									fmtDate.szDateSeparator,
									1 + fmtDate.bMonthLeadingZero,
									uMonth,
									fmtDate.szDateSeparator,
									2,
									iYear);
					break;
			// YY/MM/DD - Year, Month, Day
			case 2:
					strRetVal.Format("%0*d%s%0*d%s%0*d",
									2,
									iYear,
									fmtDate.szDateSeparator,
									1 + fmtDate.bMonthLeadingZero,
									uMonth,
									fmtDate.szDateSeparator,
									1 + fmtDate.bDayLeadingZero,
									uDay);
					break;
	}
	return strRetVal;
}


CString CBdeDatabase::FormatTime(TIME Time)
{
	DBIResult   dbiResult;       // Return value from IDAPI functions
	FMTTime     fmtTime;    // Time format
	UINT16      uHour;      // Hour portion of the time
	UINT16      uMinute;    // Minute portion of the time
	UINT16      uMilSec;    // Second portion (in ms) of the time
	UINT16      uIsAm = 0;      // Is Time AM?
	CHAR        szTemp[10]; // Temp buffer, used for AM, PM string
	CHAR szTime[48];
	CString strRetVal;

	// Get the formatting of the Time.
	dbiResult = DbiGetTimeFormat(&fmtTime);
	if (dbiResult != DBIERR_NONE) return strRetVal;

	// Decode the time.
	dbiResult = DbiTimeDecode(Time, &uHour, &uMinute, &uMilSec);
	if (dbiResult != DBIERR_NONE) return strRetVal;

	// Make certain the seperator is not the
	//   escape character.
	if (fmtTime.cTimeSeparator == '\\')
	{
		fmtTime.cTimeSeparator  = '/';
	}

	// Check if time should be displayed in 12 or 24 hour format.
	if (fmtTime.bTwelveHour)
	{
		// Temporary variable used to determine if the time is AM or PM.
		uIsAm = uHour;
		uHour = (UINT16)(uHour % 12);
		if (uHour == 0)
		{
			uHour = 12;
		}
		// If AM, set uIsAm to TRUE, else set uIsAm to 0.
		if (uIsAm == uHour)
		{
			uIsAm = 1;
		}
		else
		{
			uIsAm = 0;
		}
	}

	// Format the hour and minute of the time.
	wsprintf(szTime, "%2u%c%02u", uHour, fmtTime.cTimeSeparator, uMinute);

	// Determine if seconds are to be displayed.
	if (fmtTime.bSeconds)
	{
		wsprintf(szTemp, "%c%02u", fmtTime.cTimeSeparator, (uMilSec / 1000));
		strcat(szTime, szTemp);

		// Determine if milliseconds are to be displayed.
		if (fmtTime.bMilSeconds)
		{
			wsprintf(szTemp, "%c%03u", fmtTime.cTimeSeparator, (uMilSec % 1000));
			strcat(szTime, szTemp);   
		}
	}

	// Add a string to the time if the time is 12-hour.
	if (fmtTime.bTwelveHour)
	{
		strcat(szTime, " ");
		if (uIsAm)
		{
			strcat(szTime, fmtTime.szAmString);
		}
		else // otherwise it's PM
		{
			strcat(szTime, fmtTime.szPmString);
		}
	}

	strRetVal.Format("%s", szTime);
	return strRetVal;
}



CString CBdeDatabase::FormatTimeStamp(TIMESTAMP TimeStamp)
{
	DBIResult   rslt;       // Return value from IDAPI functions
	TIME        Time;       // Time portion of the TimeStamp
	CString strRetVal;
	INT32 nDate32;

	// Get the date and time components
	rslt = ::DbiTimeStampDecode(TimeStamp, &nDate32, &Time);

	// Format the TimeStamp
	strRetVal.Format("%s %s", FormatDate(nDate32), FormatTime(Time));

	return strRetVal;
}


// function to convert a TIMESTAMP to a COleDateTime
COleDateTime CBdeDatabase::TimeStampToOleDateTime(TIMESTAMP TimeStamp)
{
	DBIResult dbiResult;       // Return value from IDAPI functions
	TIME Time; // Time portion of the TimeStamp, actually a LONG
	INT32 n32; // date portion of the timestamp
	UINT16 hour, min, msec, day, month;
	INT16 year;
	COleDateTime dtRetVal = 0.0;
	dtRetVal.m_status = COleDateTime::null;

	// Get the date and time components
	dbiResult = ::DbiTimeStampDecode(TimeStamp, &n32, &Time);
	if (dbiResult != DBIERR_NONE) return dtRetVal;

	dbiResult = ::DbiTimeDecode(Time, &hour, &min, &msec);
	if (dbiResult != DBIERR_NONE) return dtRetVal;

	dbiResult = DbiDateDecode(n32, &month, &day, &year);
	if (dbiResult != DBIERR_NONE) return dtRetVal;

	dtRetVal.SetDateTime(year, month, day, hour, min, msec/1000);
	return dtRetVal;	
}





COleDateTime CBdeDatabase::TimeToOleDateTime(TIME time)
{
	DBIResult dbiResult;       // Return value from IDAPI functions
	UINT16 hour, min, msec;
	COleDateTime dtRetVal = 0.0;
	dtRetVal.m_status = COleDateTime::null;

	dbiResult = ::DbiTimeDecode(time, &hour, &min, &msec);
	if (dbiResult != DBIERR_NONE) return dtRetVal;

	dtRetVal.SetTime(hour, min, msec/1000);
	return dtRetVal;	
}


COleDateTime CBdeDatabase::DateToOleDateTime(INT32 Date)
{
	DBIResult dbiResult;       // Return value from IDAPI functions
	UINT16 day, month;
	INT16 year;
	COleDateTime dtRetVal = 0.0;
	dtRetVal.m_status = COleDateTime::null;

	dbiResult = DbiDateDecode(Date, &month, &day, &year);
	if (dbiResult != DBIERR_NONE) return dtRetVal;

	dtRetVal.SetDate(year, month, day);
	return dtRetVal;	
}



DBIResult CBdeDatabase::OleDateTimeToTimeStamp(COleDateTime dt, pTIMESTAMP pTimeStamp)
{
	DBIResult dbiResult;       // Return value from IDAPI functions
	TIME Time; // Time portion of the TimeStamp, actually a LONG
	INT32 n32; // date portion of the timestamp
	UINT16 hour, min, msec, day, month;
	INT16 year;

	// Get the date and time components
	year = (INT16)dt.GetYear();
	month = (UINT16)dt.GetMonth();
	day = (UINT16)dt.GetDay();
	hour = (UINT16)dt.GetHour();
	min = (UINT16)dt.GetMinute();
	msec = (UINT16)(dt.GetSecond()*1000);

	dbiResult = ::DbiTimeEncode(hour, min, msec, &Time);
	if (dbiResult != DBIERR_NONE) return dbiResult;

	dbiResult = DbiDateEncode(month, day, year, &n32);
	if (dbiResult != DBIERR_NONE) return dbiResult;

	dbiResult = ::DbiTimeStampEncode(n32, Time, pTimeStamp);
	if (dbiResult != DBIERR_NONE) return dbiResult;

	return DBIERR_NONE;	
}


INT32 CBdeDatabase::OleDateTimeToDate(COleDateTime dt)
{
	INT32 n32 = 0;
	UINT16 day, month;
	INT16 year;

	day = (UINT16)dt.GetDay();
	month = (UINT16)dt.GetMonth();
	year = (INT16)dt.GetYear();
		
	::DbiDateEncode(month, day, year, &n32);
	return n32;	
}


TIME CBdeDatabase::OleDateTimeToTime(COleDateTime dt)
{
	UINT16 hour, min, msec;
	TIME time = 0L;

	hour = (UINT16)dt.GetHour();
	min = (UINT16)dt.GetMinute();
	msec = (UINT16)(dt.GetSecond() * 1000);

	::DbiTimeEncode(hour, min, msec, &time);
	return time;	
}




/////////////////////////////////////////////////////////////////////////////////
//  Functions to set field values


// set field as a string, pass NULL or blank to set as a blank
BOOL CBdeDatabase::SetFieldAsString(INT16 nFieldNumber, LPCTSTR szValue, 
	BOOL bBlank /* = FALSE */)
{
	// Throw an exception if not in edit mode
	if (!CheckValidCursor("Failed to set field value.")) return FALSE;
	if (!CheckEditMode("Failed to set field value.")) return FALSE;

	DBIResult dbiResult;
	int nBdeExError = 0;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
	CString strError;

	// make sure edit mode allows editing
	// and m_pEditRecordBuffer is not NULL
	if (m_pEditRecordBuffer == NULL)
	{
		strError.Format("Failed to set field %d to value '%s'.", nFieldNumber, szValue);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_NOTINEDITMODE, m_szTableName,
			m_szDatabaseName, strError);
		return FALSE;
	}

	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}


	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return FALSE;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

	UINT16 nSize = pFldDesc[nFieldNumber-1].iLen;
	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;
	UINT16 nFieldSubType = pFldDesc[nFieldNumber-1].iSubType;
	UINT16 nPrecision = pFldDesc[nFieldNumber-1].iUnits2;
	if (m_nTableType == TABLETYPE_PARADOX) nPrecision = 32;
	UINT16 nDecimals = pFldDesc[nFieldNumber-1].iUnits2;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;

	// handle the situation for a blank
	if (bBlank)
	{
		dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, NULL);
		return TRUE;
	}

	// handle the situation for a NULL buffer
	if (szValue == NULL)
	{
		dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, NULL);
		return TRUE;
	}

	// if an empty string is passed, then set the field to blank
	if (strlen(szValue) == 0)
	{
		dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, NULL);
		return TRUE;
	}

	// allocate memory for the string buffer to recieve data
	pBYTE pSourceBuf = (pBYTE)malloc(nSize*sizeof BYTE);
	memset(pSourceBuf, 0, nSize*sizeof BYTE);

	// check for memory error
	if (pSourceBuf == NULL)
	{
		// Throw memory exception here
		free(pSourceBuf);
		strError.Format("Failed to set field %d to value '%s'.", nFieldNumber, szValue);
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}
	
	// set the field value based on it's type
	switch (nFieldType)
	{
    case fldBYTES: // Bytes
    case fldZSTRING: // String
		{
			strncpy((char*)pSourceBuf, szValue, nSize);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)pSourceBuf);
			break;
		}
		case fldFLOAT:
		{
			double f = atof(szValue);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&f);
			break;
		}
		case fldBCD:
		{
			double f = atof(szValue);
			DbiBcdFromFloat(&f, nPrecision, nDecimals, (FMTBcd *)pSourceBuf);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)pSourceBuf);
			break;
		}
		case fldINT16:
		{
			INT16 n16 = (INT16)atoi(szValue);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n16);
			break;
		}
		case fldUINT16:
		{
			UINT16 n16 = (UINT16)atoi(szValue);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n16);
			break;
		}
		case fldINT32:
		{
			INT32 n32 = (INT32)atoi(szValue);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n32);
			break;
		}
		case fldUINT32:
		{
			UINT32 n32 = (UINT32)atoi(szValue);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n32);
			break;
		}
		case fldBOOL:
		{
			BOOL bVal = TRUE;
			if (szValue[0] == 'F' || szValue[0] == 'f') bVal = FALSE;
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&bVal);
			break;
		}
		case fldDATE:
		{
			COleDateTime dtOle;
			INT32 n32;
			if (dtOle.ParseDateTime(szValue, VAR_DATEVALUEONLY))
			{
				n32 = OleDateTimeToDate(dtOle);
				dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n32);
			}
			else
			{
				// set an extended exception code here
				nBdeExError = BDEEXERR_INVALIDDATETIMEFORMAT;
			}
			break;
		}
		case fldTIME:
		{
			COleDateTime dtOle;
			TIME time;
			if (dtOle.ParseDateTime(szValue, VAR_TIMEVALUEONLY))
			{
				time = OleDateTimeToTime(dtOle);
				dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&time);
			}
			else
			{
				// set an extended exception code here
				nBdeExError = BDEEXERR_INVALIDDATETIMEFORMAT;
			}
			break;
		}
		case fldTIMESTAMP:
		{
			COleDateTime dtOle;
			TIMESTAMP timestamp;
			if (dtOle.ParseDateTime(szValue, 0))
			{
				dbiResult = OleDateTimeToTimeStamp(dtOle, &timestamp);
				if (dbiResult == DBIERR_NONE)
					dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&timestamp);
			}
			else
			{
				// set an extended exception code here
				nBdeExError = BDEEXERR_INVALIDDATETIMEFORMAT;
			}
			break;			
		}
		case fldBLOB:
		{
			if (nFieldSubType == fldstMEMO)
			{
				// Open the blob
				dbiResult = DbiOpenBlob(m_hCursor, m_pEditRecordBuffer, nFieldNumber, dbiREADWRITE);
				if (dbiResult != DBIERR_NONE) break;

				// Allocate memory for the blob
				pBYTE pBlobSourceBuf = (pBYTE)malloc(strlen(szValue)*sizeof BYTE + 1);

				// Check for NULL
				if (pBlobSourceBuf == NULL)
				{
					dbiResult = DBIERR_NOMEMORY;
					DbiFreeBlob(m_hCursor, m_pEditRecordBuffer, nFieldNumber);
					break;
				}

				// initialize the blob data
				memset(pBlobSourceBuf, 0, strlen(szValue)*sizeof BYTE + 1);

				// copy the string into the blob data
				strncpy((char*)pBlobSourceBuf, szValue, strlen(szValue));

				/// copy the blob data to the table
				dbiResult = DbiPutBlob(m_hCursor, m_pEditRecordBuffer, nFieldNumber, 0,
					strlen(szValue) + 1, (pBYTE)pBlobSourceBuf);

				// free the blob data				
				free(pBlobSourceBuf);
								
				// check for errors
				if (dbiResult != DBIERR_NONE)
				{
					DbiFreeBlob(m_hCursor, m_pEditRecordBuffer, nFieldNumber); 
				}
			}
			else
			{
				// TODO: Handle situations for non-memo blobs
				nBdeExError = BDEEXERR_UNSUPPORTEDBLOBTYPE;
			}
			break;
		}		
		default:
		{
			nBdeExError = BDEEXERR_UNSUPPORTEDFIELDTYPE;		
		}
	} // end of switch

	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to set field %d to value '%s'.", nFieldNumber, szValue);
		free(pSourceBuf);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	} 

	free(pSourceBuf);
	return TRUE;
} // end of SetFieldAsString




BOOL CBdeDatabase::SetFieldAsInteger(INT16 nFieldNumber, int nValue, 
	BOOL bBlank /* = FALSE */)
{
	// Throw an exception if not in edit mode
	if (!CheckValidCursor("Failed to set field value.")) return FALSE;
	if (!CheckEditMode("Failed to set field value.")) return FALSE;

	DBIResult dbiResult;
	int nBdeExError = 0;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
	CString strError;

	// make sure edit mode allows editing
	// and m_pEditRecordBuffer is not NULL
	if (m_pEditRecordBuffer == NULL)
	{
		strError.Format("Failed to set field %d to value %d.", nFieldNumber, nValue);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_NOTINEDITMODE, m_szTableName,
			m_szDatabaseName, strError);
		return FALSE;
	}

	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}


	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return FALSE;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;

	// handle the situation for a blank
	if (bBlank)
	{
		dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, NULL);
		return TRUE;
	}


	// set the field value based on it's type
	switch (nFieldType)
	{
		case fldINT16:
		{
			INT16 n16 = (INT16)nValue;
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n16);
			break;
		}
		case fldUINT16:
		{
			UINT16 n16 = (UINT16)nValue;
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n16);
			break;
		}
		case fldINT32:
		{
			INT32 n32 = (INT32)nValue;
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n32);
			break;
		}
		case fldUINT32:
		{
			UINT32 n32 = (UINT32)nValue;
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n32);
			break;
		}
		case fldFLOAT: // need to handle floats for dBase tables
		{
			double fValue = (double)nValue;
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, 
				(pBYTE)&fValue);
			break;
		}
		case fldBOOL:
		{
			BOOL bVal = (nValue == 0);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&bVal);
			break;
		}
		default:
		{
			// Handle non-integer types here
			nBdeExError = BDEEXERR_FIELDNOTINTEGER;
		}
	} // end of switch

	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to set field %d to value %d.", nFieldNumber, nValue);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	} 

	return TRUE;
} // end of SetFieldAsInteger




BOOL CBdeDatabase::SetFieldAsBoolean(INT16 nFieldNumber, BOOL bValue, 
	BOOL bBlank /* = FALSE */)
{
	// Throw an exception if not in edit mode
	if (!CheckValidCursor("Failed to set field value.")) return FALSE;
	if (!CheckEditMode("Failed to set field value.")) return FALSE;

	DBIResult dbiResult;
	int nBdeExError = 0;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
	CString strError;

	// make sure edit mode allows editing
	// and m_pEditRecordBuffer is not NULL
	if (m_pEditRecordBuffer == NULL)
	{
		strError.Format("Failed to set field %d value.", nFieldNumber);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_NOTINEDITMODE, m_szTableName,
			m_szDatabaseName, strError);
		return FALSE;
	}

	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return FALSE;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;

	// handle the situation for a blank
	if (bBlank)
	{
		dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, NULL);
		return TRUE;
	}


	// set the field value based on it's type
	switch (nFieldType)
	{
		case fldBOOL:
		{
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, 
				(pBYTE)&bValue);
			break;
		}
		default:
		{
			// Handle non-boolean types here
			nBdeExError = BDEEXERR_FIELDNOTBOOLEAN;
		}
	} // end of switch

	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to set field %d value.", nFieldNumber);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	} 

	return TRUE;
} // end of SetFieldAsBoolean





BOOL CBdeDatabase::SetFieldAsFloat(INT16 nFieldNumber, double fValue, 
	BOOL bBlank /* = FALSE */)
{
	// Throw an exception if not in edit mode
	if (!CheckValidCursor("Failed to set field value.")) return FALSE;
	if (!CheckEditMode("Failed to set field value.")) return FALSE;

	DBIResult dbiResult = DBIERR_NONE;
	int nBdeExError = 0;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
	CString strError;

	// make sure edit mode allows editing
	// and m_pEditRecordBuffer is not NULL
	if (m_pEditRecordBuffer == NULL)
	{
		strError.Format("Failed to set field %d to value %g.", nFieldNumber, fValue);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_NOTINEDITMODE, m_szTableName,
			m_szDatabaseName, strError);
		return FALSE;
	}

	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return FALSE;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

	UINT16 nSize = pFldDesc[nFieldNumber-1].iLen;
	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;
	UINT16 nPrecision = pFldDesc[nFieldNumber-1].iUnits1;
	if (m_nTableType == TABLETYPE_PARADOX) nPrecision = 32;
	UINT16 nDecimals = pFldDesc[nFieldNumber-1].iUnits2;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;

	// handle the situation for a blank
	if (bBlank)
	{
		dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, NULL);
		return TRUE;
	}


	// allocate memory for the string buffer to recieve data
	pBYTE pSourceBuf = (pBYTE)malloc(nSize*sizeof BYTE);
	memset(pSourceBuf, 0, nSize*sizeof BYTE);

	// check for memory error
	if (pSourceBuf == NULL)
	{
		// Throw memory exception here
		free(pSourceBuf);
		strError.Format("Failed to set field %d to value %g.", nFieldNumber, fValue);
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}
	
	// set the field value based on it's type
	switch (nFieldType)
	{
		case fldFLOAT:
		{
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, 
				(pBYTE)&fValue);
			break;
		}
		case fldBCD:
		{
			// handle precision and stuff here
			DbiBcdFromFloat(&fValue, nPrecision, nDecimals, (FMTBcd *)pSourceBuf);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)pSourceBuf);
			break;
		}
		default:
		{
			nBdeExError = BDEEXERR_FIELDNOTFLOAT;
		}
	} // end of switch

	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to set field %d to value %g.", nFieldNumber, fValue);
		free(pSourceBuf);
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	} 

	free(pSourceBuf);

	return TRUE;
} // end of SetFieldAsFloat






BOOL CBdeDatabase::SetFieldAsDate(INT16 nFieldNumber, COleDateTime dtValue, 
	BOOL bBlank /* = FALSE */)
{
	// Throw an exception if not in edit mode
	if (!CheckValidCursor("Failed to set field value.")) return FALSE;
	if (!CheckEditMode("Failed to set field value.")) return FALSE;

	DBIResult dbiResult = DBIERR_NONE;
	int nBdeExError = 0;
  CURProps curProps; // Table Properties
	pFLDDesc pFldDesc; // field information
	CString strError;

	// make sure edit mode allows editing
	// and m_pEditRecordBuffer is not NULL
	if (m_pEditRecordBuffer == NULL)
	{
		strError.Format("Failed to set field %d to value %s.", nFieldNumber, 
			dtValue.Format("%m/%d/%Y %H:%M:%S"));
		throw new CBdeException(DBIERR_NONE, BDEEXERR_NOTINEDITMODE, m_szTableName,
			m_szDatabaseName, strError);
		return FALSE;
	}

	// get the cursor properties for the table
  dbiResult = DbiGetCursorProps(m_hCursor, &curProps);
	if (dbiResult != DBIERR_NONE)
  {
		strError.Format("Failed to get cursor properties.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
  }

	// make sure the field number is valid
	if (nFieldNumber < 1 || nFieldNumber > curProps.iFields)
	{
		strError.Format("Invalid field index %d.  Valid numbers are between 1 and %d.", 
			nFieldNumber, curProps.iFields);
		throw new CBdeException(DBIERR_NONE, BDEEXERR_INVALIDFIELDINDEX,
			m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

	// allocate memory for the field descriptors
	pFldDesc = (pFLDDesc)malloc(curProps.iFields * sizeof(FLDDesc));
	if (pFldDesc == NULL)
	{
		// Throw exception for DBIERR_NOMEMORY
		throw new CBdeException(DBIERR_NOMEMORY);
		return FALSE;
	}

	dbiResult = DbiGetFieldDescs(m_hCursor, pFldDesc);
	if (dbiResult != DBIERR_NONE)
	{
		free(pFldDesc);
		strError.Format("Failed to retrieve field information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	}

	UINT16 nFieldType = pFldDesc[nFieldNumber-1].iFldType;

	free(pFldDesc); // clean up the field descriptor
	pFldDesc = NULL;

	// handle the situation for a blank	or invalid date time
	if (bBlank || dtValue.m_status != COleDateTime::valid)
	{
		dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, NULL);
		return TRUE;
	}


	// set the field value based on it's type
	switch (nFieldType)
	{
		case fldDATE:
		{
			INT32 n32;
			n32 = OleDateTimeToDate(dtValue);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&n32);
			break;
		}
		case fldTIME:
		{
			TIME time;
			time = OleDateTimeToTime(dtValue);
			dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&time);
			break;
		}
		case fldTIMESTAMP:
		{
			TIMESTAMP timestamp;
			dbiResult = OleDateTimeToTimeStamp(dtValue, &timestamp);
			if (dbiResult == DBIERR_NONE)
				dbiResult = DbiPutField(m_hCursor, nFieldNumber, m_pEditRecordBuffer, (pBYTE)&timestamp);
			break;			
		}		
		default:
		{
			// Handle non-date types here
			nBdeExError = BDEEXERR_FIELDNOTDATE;
		}
	} // end of switch

	if (dbiResult != DBIERR_NONE || nBdeExError != 0)
	{
		// Throw exception here
		strError.Format("Failed to set field %d to value %s.", nFieldNumber, 
			dtValue.Format("%m/%d/%Y %H:%M:%S"));
		throw new CBdeException(dbiResult, nBdeExError, m_szTableName, m_szDatabaseName, strError);
		return FALSE;
	} 

	return TRUE;
} // end of set field as date







//////////////////////////////////////////////////////////////////////////////////
//  Functions to edit or insert records
//
//  To edit or insert a record...
//	Call Edit or Insert or Append to set the table for editing
//	Create the record buffer
//  Fill the record buffer with the information
//  Call Post or Cancel to finalize or abort the changes
//


// function to begin editing the current record of the table
BOOL CBdeDatabase::Edit()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to edit record.")) return FALSE;
	if (!CheckNotEditMode("Failed to edit record.")) return FALSE;

	// make sure not already in edit mode
	if (m_pEditRecordBuffer != NULL)
	{
		throw new CBdeException(DBIERR_NONE, BDEEXERR_ALREADYINEDITMODE, 
			m_szTableName, m_szDatabaseName, "Failed to edit record.");
		return FALSE;
	}

	// prepare the record buffer for editing
	if (!PrepareRecordEdit(EDITMODE_EDITINPLACE)) return FALSE;

	// set the edit mode
	m_nEditMode = EDITMODE_EDITINPLACE;
	return TRUE;
}



// function to insert a record at the current position in the table
BOOL CBdeDatabase::Insert()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to add new record.")) return FALSE;
	if (!CheckNotEditMode("Failed to add new record.")) return FALSE;

	// make sure not already in edit mode
	if (m_pEditRecordBuffer != NULL)
	{
		throw new CBdeException(DBIERR_NONE, BDEEXERR_ALREADYINEDITMODE, 
			m_szTableName, m_szDatabaseName, "Failed to insert record.");
		return FALSE;
	}

	// prepare the record buffer for editing
	if (!PrepareRecordEdit(EDITMODE_INSERT)) return FALSE;

	// set the edit mode
	m_nEditMode = EDITMODE_INSERT;
	return TRUE;
}



// function to append a record to the end of the table
BOOL CBdeDatabase::Append()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to add new record.")) return FALSE;
	if (!CheckNotEditMode("Failed to add new record.")) return FALSE;

	// make sure not already in edit mode
	if (m_pEditRecordBuffer != NULL)
	{
		throw new CBdeException(DBIERR_NONE, BDEEXERR_ALREADYINEDITMODE, 
			m_szTableName, m_szDatabaseName, "Failed to add record.");
		return FALSE;
	}

	// prepare the record buffer for editing
	if (!PrepareRecordEdit(EDITMODE_APPEND)) return FALSE;

	// set the edit mode
	m_nEditMode = EDITMODE_APPEND;
	return TRUE;
}



// function to prepare for editing a record
// it allocates the record buffer
// it returns FALSE on failure, in which case no record buffer is allocated
BOOL CBdeDatabase::PrepareRecordEdit(int nEditMode)
{
	DBIResult dbiResult;
  CURProps CursorProps;       // Table descriptor
	

	// get the size of the tables record
  dbiResult = DbiGetCursorProps(m_hCursor, &CursorProps);
	if (dbiResult != DBIERR_NONE)
	{
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, 
			"Failed to get cursor properties.");
		return FALSE;
	}

	// allocate the buffer for the record
  m_pEditRecordBuffer = (pBYTE)malloc(CursorProps.iRecBufSize);
  if (m_pEditRecordBuffer == NULL)
  {
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, 
			"Failed to allocate record buffer.");
		return FALSE;
  }


	// if inserting or appending then initialize the record buffer
	// otherwise, fill the record buffer with current info
	if (nEditMode == EDITMODE_EDITINPLACE)
	{
		dbiResult = DbiGetRecord(m_hCursor, dbiNOLOCK, m_pEditRecordBuffer, NULL);
		if (dbiResult != DBIERR_NONE)
		{
			free(m_pEditRecordBuffer);
			m_pEditRecordBuffer = NULL;
			throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, 
				"Failed to initialize record buffer.");
			return FALSE;
		}
	}
	else
	{
		dbiResult = DbiInitRecord(m_hCursor, m_pEditRecordBuffer);
		if (dbiResult != DBIERR_NONE)
		{
			free(m_pEditRecordBuffer);
			m_pEditRecordBuffer = NULL;
			throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, 
				"Failed to initialize record buffer.");
			return FALSE;
		}
	}
	return TRUE;
}



// function to finalize edit, append, or insert operations and update the table
// if post fails, cancel must be called to clean up the record buffer
BOOL CBdeDatabase::Post()
{
	// Throw an exception if database is in NOT in Edit mode
	if (!CheckValidCursor("Failed to accept tables changes.")) return FALSE;
	if (!CheckEditMode("Failed to accept tables changes.")) return FALSE;


	DBIResult dbiResult;

	if (m_pEditRecordBuffer == NULL)
	{
		throw new CBdeException(DBIERR_NONE, BDEEXERR_NOTINEDITMODE, 
			m_szTableName, m_szDatabaseName, "Failed to accept changes.");
		return FALSE;
	}

	switch (m_nEditMode)
	{
		case EDITMODE_EDITINPLACE:
		{
			dbiResult = DbiModifyRecord(m_hCursor, m_pEditRecordBuffer, TRUE);
			if (dbiResult != DBIERR_NONE)
			{
				throw new CBdeException(dbiResult, m_szTableName, 
					m_szDatabaseName, "Failed to accept changes.");
				return FALSE;
			}
			break;
		}
		// Note the append may only be appropriate if there are no idexes
		case EDITMODE_APPEND:
		{
			dbiResult = DbiAppendRecord(m_hCursor, m_pEditRecordBuffer);
			if (dbiResult != DBIERR_NONE)
			{
				throw new CBdeException(dbiResult, m_szTableName, 
					m_szDatabaseName, "Failed to accept changes.");
				return FALSE;
			}
			break;
		}
		case EDITMODE_INSERT:
		{
			dbiResult = DbiInsertRecord(m_hCursor, dbiNOLOCK, m_pEditRecordBuffer);
			if (dbiResult != DBIERR_NONE)
			{
				throw new CBdeException(dbiResult, m_szTableName, 
					m_szDatabaseName, "Failed to accept changes.");
				return FALSE;
			}
			break;
		}
		default:
			ASSERT(FALSE);
	}
	
	// clean up the edit record buffer
	free(m_pEditRecordBuffer);
	m_pEditRecordBuffer = NULL;

	// reset the edit mode
	m_nEditMode = 0;
	
	return TRUE;
}


// function to cancel edit, append, or insert operations
BOOL CBdeDatabase::Cancel()
{
	if (!CheckInitialization()) return FALSE;

	// this function should be safe to call even if no edit buffer
	if (m_pEditRecordBuffer == NULL)
	{
		ASSERT(m_nEditMode == 0);
		return TRUE;
	}

	// clean up the edit record buffer
	free(m_pEditRecordBuffer);
	m_pEditRecordBuffer = NULL;

	// reset the edit mode
	m_nEditMode = 0;
	return TRUE;	
}



BOOL CBdeDatabase::DeleteRecord()
{
	// Throw an exception if database is in Edit mode
	if (!CheckValidCursor("Failed to delete record.")) return FALSE;
	if (!CheckNotEditMode("Failed to delete record.")) return FALSE;

	DBIResult dbiResult;
  CURProps CursorProps;       // Table descriptor
	pBYTE pRecBuf = NULL;
	CString strError;
	
	// get the size of the tables record
  dbiResult = DbiGetCursorProps(m_hCursor, &CursorProps);
	if (dbiResult != DBIERR_NONE)
	{
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, 
			"Failed to get cursor properties.");
		return FALSE;
	}

	// get the record buffer

 	// allocate the record buffer for the table
  pRecBuf = (pBYTE) malloc(CursorProps.iRecBufSize * sizeof(BYTE));
	if (pRecBuf == NULL)
  {
		strError.Format("Insufficient memory to get record buffer.");
		throw new CBdeException(DBIERR_NOMEMORY, m_szTableName, m_szDatabaseName, strError);
    return FALSE;
  }

	// fill the record buffer with information
	dbiResult = DbiGetRecord(m_hCursor, dbiNOLOCK, pRecBuf, NULL);
	if (dbiResult != DBIERR_NONE)
	{
		free(pRecBuf);
		strError.Format("Failed to get record information.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
	  return FALSE;
	}

	dbiResult = DbiDeleteRecord(m_hCursor, pRecBuf);
	if (dbiResult != DBIERR_NONE)
	{
		free(pRecBuf);
		strError.Format("Failed to delete record.");
		throw new CBdeException(dbiResult, m_szTableName, m_szDatabaseName, strError);
	  return FALSE;
	}

	// free up the record memory
	free(pRecBuf);

	// After deletion, the cursor is on a crack, so move it to a valid record
	// Move to the next record here, or if fails, move to previous
	dbiResult = DbiGetNextRecord(m_hCursor, dbiNOLOCK, NULL, NULL);
	if (dbiResult == DBIERR_EOF)
		dbiResult = DbiGetPriorRecord(m_hCursor, dbiNOLOCK, NULL, NULL);


	// if table is dBase, you may want to pack here

	return TRUE;
} // end of DeleteRecord function

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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