Click here to Skip to main content
15,896,201 members
Articles / Desktop Programming / Win32

Creation and memory mapping existing DBF files as alternative of data serialization during work with modified CListCtrl classes in virtual mode on dialogs of MDI application

Rate me:
Please Sign up or sign in to vote.
4.64/5 (6 votes)
22 Jul 2009CPOL11 min read 47.2K   1.5K   26  
The demonstration of reading, writing and creation of standard DBF files with random access in memory instead of serialization of typical MFC application for descendants of CListCtrl classes in Virtual Mode.
/////////////////////////////////////////////////////////////////////////////
// MainDoc.cpp : implementation of the CMainDoc class
/////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "Main.h"
#include "MainDoc.h"
#include "Dbf\Dbf.h"

#ifdef _DEBUG
	#define new DEBUG_NEW
#undef THIS_FILE
	static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMainDoc
/////////////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNCREATE(CMainDoc, CDocument)

BEGIN_MESSAGE_MAP(CMainDoc, CDocument)
	//{{AFX_MSG_MAP(CMainDoc)
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMainDoc construction
/////////////////////////////////////////////////////////////////////////////
CMainDoc::CMainDoc() {
	//*** Main application pointer
	m_pMainApp = reinterpret_cast<CMainApp *>(AfxGetApp());

	if(!m_pMainApp) {
		_M("CMainDoc: Empty object of the CMainApp class!");
		return;
	}

	//*** Table Id
	m_eTable = m_pMainApp->m_eTable;

	if(!m_eTable) {
		_M("CMainDoc: No objects is in the application!");
		return;
	}

	//*** Meta table structure
	m_MetaTable = m_pMainApp->m_aMetaTable[m_eTable];

	//*** Current table pointer
	//m_pTable = NULL;

	//*** Handle of dbf file
	m_hDbfFile = NULL;

	//*** Hendle file mapping
	m_hDbfMap = NULL;

	//*** Maps view of dbf file for its first part (where are header)
	m_pDbfView1 = NULL;

	//*** Maps view of dbf file for its second part (where are data)
	m_pDbfView2 = NULL;

	//*** Dbf header structure
	m_pDbfHdr = NULL;

	//*** Number of records in file
	m_nRecCount = 0;

	//*** Length of one data record (including delete flag)
	m_nRecSize = 0;

	//*** Number of fields in dbf file
	m_nFldCount = 0;

	//*** Dynamic array of field names
	m_aacFldName = NULL;

	//*** Dynamic array of field types
	m_acFldType = NULL;

	//*** Dynamic array of offsets
	m_anOff = NULL;

	//*** Dynamic array of lengths
	m_acLen = NULL;
	
	//*** Dynamic array of decimal places
	m_acDec = NULL;
}  // CMainDoc

/////////////////////////////////////////////////////////////////////////////
// CMainDoc destruction
/////////////////////////////////////////////////////////////////////////////
CMainDoc::~CMainDoc() {
}  // ~CMainDoc
/*
/////////////////////////////////////////////////////////////////////////////
// OnNewDocument
/////////////////////////////////////////////////////////////////////////////
BOOL CMainDoc::OnNewDocument() {
	if(!CDocument::OnNewDocument()) {
		_M("CMainDoc: Failed to call CDocument::OnNewDocument function!");
		return FALSE;
	}
	
	//*** Changes window name
	//SetTitle(m_MetaTable.szTblName);

	return TRUE;
}  // OnNewDocument
*/
/////////////////////////////////////////////////////////////////////////////
// CreateDocumentFile
/////////////////////////////////////////////////////////////////////////////
BOOL CMainDoc::CreateDocumentFile(TCHAR *szDbfName) {
	//*** The dbf file structure
	/*
	typedef struct {
		DBF_HEADER DbfHdr;
		DBF_FIELD aDbfField[FLDCOUNT];  // FLDCOUNT = (DbfHdr.wFirstRec-296)/32 for VFP
		BYTE cHdrEnd;  // Header record terminator = 0x0D (13)
		BYTE acDbcFile[263];  // A data range for associated *.dbc file, contains 0x00
		DBF_RECORD aDbfRec[RECCOUNT];  // RECCOUNT = DbfHdr.nRecCount
		BYTE cFileEnd;  // File record terminator = 0x1A (26)
	} DBF_FILE;
	*/
	CFileStatus FileStatus;

	//*** If file szDbfName does exist simply return
	if(CFile::GetStatus(szDbfName, FileStatus))
			return TRUE;

	//*** Number of records in file
	ULONG nRecCount = m_MetaTable.nRowCount;

	//*** Data table fields count
	UINT nFldCount = m_MetaTable.nColCount;

	SYSTEMTIME SysTime = {0};

	//*** Gets system date and time
	GetSystemTime(&SysTime);

	//*** Dbf header structure

	DBF_HEADER DbfHdr = {0};

	DbfHdr.cType = VFPTYPE;  // DBF type
	DbfHdr.cYear = SysTime.wYear%BASEYEAR;  // Year of last update
	DbfHdr.cMonth = SysTime.wMonth;  // Month of last update
	DbfHdr.cDay = SysTime.wDay;  // Day of last update
	DbfHdr.nRecCount = nRecCount;  // Number of records in file
	//DbfHdr.wFirstRec = 0;	// Position of first data record
	//DbfHdr.wRecSize = 0;  // Length of one data record, including delete flag
	//DbfHdr.cFlag = 0;	 // Table Flags (Only Visual Foxpro)
	DbfHdr.cCodePage = CP1252;	// Windows ANSI

	//*** Dbf field structure

	DBF_FIELD DbfField = {0};

	//*** Gets handle of dbf file
	HANDLE hDbfFile = ::CreateFile(
			szDbfName,  // Pointer to name of the dbf file
			GENERIC_WRITE,  // Access (read-write) mode
			0,  // Share mode
			NULL,  // Pointer to security attributes
			CREATE_ALWAYS,  // How to create
			FILE_ATTRIBUTE_NORMAL,  // File attributes
			NULL  // HANDLE hTemplateFile - Handle to file with attributes to copy
	);

	//*** Message buffer
	TCHAR szStr[MAXITEMTEXT];

	//*** Checks file creation
	if(hDbfFile == INVALID_HANDLE_VALUE) {
		swprintf(
				szStr,
				_T("CMainApp: Failed to create new file: '%s'!"),
				szDbfName
		);

		_M(szStr);
		::CloseHandle(hDbfFile);
		return FALSE;
	}

	//*** Number of written bytes
	DWORD dwBytes = 0;

	//*** Writes bytes into file
	WriteFile(hDbfFile, &DbfHdr, sizeof(DbfHdr), &dwBytes, NULL);

	//*** Dinamic array of field length
	BYTE *acFldLen = new BYTE[nFldCount];

	//*** Dinamic array of field types
	BYTE *acFldType = new BYTE[nFldCount];

	UINT nOffset = 1;  // Skips delete byte

	//*** Writes array of DBF_FIELD aDbfField[FLDCOUNT] structures
	for(int i = 0; i < nFldCount; i++) {
		META_DATA MetaData = m_MetaTable.aMetaData[i];
		BYTE *acName = DbfField.acName;
		TCHAR *szFldName = MetaData.szFldName;

		//*** Field name with a maximum of 10 characters, a rest is padded 
		// with 0x00
		//for(int j = 0; j < 11; j++)
				//acName[j] = szFldName[j];
		
		//*** Simply copies
		while(*acName++ = *szFldName++);

		acFldType[i] = MetaData.szFldType[0];  // Field type
		DbfField.cType = acFldType[i];  // Field type
		DbfField.nOffset = nOffset;  // Displacement of field in record
		acFldLen[i] = MetaData.nFldLen;  // Length of field (in bytes)
		DbfField.cLen = acFldLen[i];  // Length of field (in bytes)
		DbfField.cDec = MetaData.nDecLen;  // Number of decimal places

		nOffset += acFldLen[i];

		//*** Writes bytes into file
		WriteFile(hDbfFile, &DbfField, sizeof(DbfField), &dwBytes, NULL);
	}

	//*** Length of one data record, including delete flag
	DbfHdr.wRecSize = nOffset;  // ARE NOT WRITTEN YET!

	//*** Header record terminator
	BYTE cHdrEnd = HEADEREND;  // = 0x0D (13)
	
	//*** Writes bytes into file
	WriteFile(hDbfFile, &cHdrEnd, sizeof(cHdrEnd), &dwBytes, NULL);

	//*** A data range for associated *.dbc file, contains 0x00
	BYTE acDbcFile[263] = {0};

	//*** Writes bytes into file
	WriteFile(hDbfFile, &acDbcFile, sizeof(acDbcFile), &dwBytes, NULL);

	//*** Gets current file pointer
	DWORD nCurOffset = SetFilePointer(hDbfFile, 0, NULL, FILE_CURRENT);

	//*** Position of first data record
	DbfHdr.wFirstRec = nCurOffset;  // ARE NOT WRITTEN YET!

	//*** The delete flag
	BYTE cDelete = 32;  // = 0x20 (" ")

	//*** Line table cell index
	UINT ji = 0;

	//*** Writes array of DBF_RECORD aDbfRec[RECCOUNT] structures
	for(ULONG j = 0; j < nRecCount; j++) {
		//*** Writes bytes into file
		WriteFile(hDbfFile, &cDelete, sizeof(cDelete), &dwBytes, NULL);

		//*** Writes array of BYTE's strings
		for(i = 0; i < nFldCount; i++) {
			ji = j*nFldCount + i;  // Line table cell index
			TCHAR *acRowText = m_MetaTable.apRowText[ji];

			BYTE cFldLen = acFldLen[i];
			BYTE cFldType = acFldType[i];

			//*** Dinamic array of field data
			BYTE *acFldData = new BYTE[cFldLen + 2];  // +2 for sake date format

			//*** Copies field data (into BYTEs from TCHARs)
			for(int k = 0; k < cFldLen; k++)
					acFldData[k] = acRowText[k];

			//*** Formates our date string (DD.MM.YYYY) into dbf style (YYYYMMDD)
			if(cFldType == 68) {  // = 0x44 ("D") - Date
				if(cFldLen != 8) {
					swprintf(
							szStr, 
							_T("CMainApp: Length of date format is %d. Must be 8!"), 
							cFldLen
					);

					_M(szStr);
					return FALSE;
				}

				if(cFldLen == 8) {  // Date length for dbf date format
					//*** Our static date has 10 characters
					acFldData[8] = acRowText[8];
					acFldData[9] = acRowText[9];
					
					//*** Date format is d1d2.m1m2.y1y2y3y4 . Must be y1y2y3y4m1m2d1d2
					//* Offset:           0 12 3 45 6 7 8 9            0 1 2 3 4 5 6 7
					acFldData[2] = acFldData[8];  // Writes Y3
					acFldData[5] = acFldData[4];  // Writes M2
					acFldData[4] = acFldData[3];  // Writes M1
					acFldData[3] = acFldData[9];  // Writes Y4
					acFldData[8] = acFldData[0];  // Saves D1
					acFldData[9] = acFldData[1];  // Saves D2
					acFldData[0] = acFldData[6];  // Writes Y1
					acFldData[1] = acFldData[7];  // Writes Y2
					acFldData[6] = acFldData[8];  // Writes D1
					acFldData[7] = acFldData[9];  // Writes D2
				} 
				//*** Else do nothing
			}

			//*** Writes bytes into file
			WriteFile(hDbfFile, acFldData, cFldLen, &dwBytes, NULL);
		}
	}

	//*** File record terminator
	BYTE cFileEnd = DBFEND;  // = 0x1A (26)

	//*** Writes bytes into file
	WriteFile(hDbfFile, &cFileEnd, sizeof(cFileEnd), &dwBytes, NULL);

	//*** Calculates file pointer to DbfHdr.wFirstRec
	ULONG nPos = (ULONG) &DbfHdr.wFirstRec - (ULONG) &DbfHdr.cType;  // = 8
	WORD wFirstRec = DbfHdr.wFirstRec;
	WORD wRecSize = DbfHdr.wRecSize;

	//*** Sets file pointer in DbfHdr.wRecSize position
	SetFilePointer(hDbfFile, nPos, NULL, FILE_BEGIN);

	//*** Writes NOT WRITTEN YET bytes into file
	WriteFile(hDbfFile, &wFirstRec, sizeof(wFirstRec), &dwBytes, NULL);

	//*** Writes next NOT WRITTEN YET bytes into file
	WriteFile(hDbfFile, &wRecSize, sizeof(wRecSize), &dwBytes, NULL);

	::CloseHandle(hDbfFile);
	
	return TRUE;
}  // CreateDocumentFile

/////////////////////////////////////////////////////////////////////////////
// OpenDocumentFile
/////////////////////////////////////////////////////////////////////////////
BOOL CMainDoc::OnOpenDocument(LPCTSTR szFileName) {  // szFileName isn't using
	TCHAR *szDbfName = m_MetaTable.szDbfName;

	CFileStatus FileStatus;

	//*** If file szDbfName does not exist creates its
	if(!CFile::GetStatus(szDbfName, FileStatus)) {
		//*** Creates current document (dbf) file on physical disk
		if(!CreateDocumentFile(szDbfName)) {
			//_M("CMainDoc: Failed to create a document file!");
			return FALSE;
		}
	}

	//*** Gets handle of dbf file
	m_hDbfFile = ::CreateFile(
			szDbfName,  // Name of dbf file
			GENERIC_READ | GENERIC_WRITE,  // Access (read-write) mode
			FILE_SHARE_READ | FILE_SHARE_WRITE,  // Share mode
			NULL,  // Pointer to security attributes
			OPEN_EXISTING,  // How to create
			FILE_ATTRIBUTE_NORMAL,  // File attributes
			NULL  // HANDLE hTemplateFile - Handle to file with attributes to copy
	);

	if(m_hDbfFile == INVALID_HANDLE_VALUE) {
		_M("CMainDoc: Failed to call ::CreateFile function!");
		return FALSE;
	}

	//*** The message buffer
	TCHAR szStr[MAXITEMTEXT];

	//*** Gets file size in bytes
	ULONG nDbfSize = ::GetFileSize(
		m_hDbfFile,  // Handle of file to get size of
		NULL  // Pointer to high-order word for file size
	);

	//*** Checks file size
	if(nDbfSize == 0) {
		swprintf(
				szStr,
				_T("CMainDoc: File '%s' is empty!"),
				szDbfName
		);

		_M(szStr);
		::CloseHandle(m_hDbfFile);
		return FALSE;
	}

	//*** Creates file mapping
	m_hDbfMap = ::CreateFileMapping(
			m_hDbfFile,  // Handle to file to map
			NULL,  // Optional security attributes
			PAGE_READWRITE,  // Protection for mapping object
			0,  // High-order 32 bits of object size
			0,  // Low-order 32 bits of object size
			NULL  // Name of file-mapping object
	);

	if(!m_hDbfMap) {
		_M("CMainDoc: Failed to call ::CreateFileMapping function!");
		return FALSE;
	}

	//*** Maps view of dbf file for its first part (where are header)
	m_pDbfView1 = reinterpret_cast<DBF_FILE1 *>(::MapViewOfFile(
			m_hDbfMap,  // File-mapping object to map into address space
			FILE_MAP_WRITE,  // Access mode
			0,  // High-order 32 bits of file offset
			0,  // Low-order 32 bits of file offset
			0  // Number of bytes to map (if it is zero, the entire file is mapped)
	));

	if(!m_pDbfView1) {
		_M("CMainDoc: Failed to call ::MapViewOfFile function!");
		return FALSE;
	}

	//*** Dbf header structure
	m_pDbfHdr = &m_pDbfView1->DbfHdr;

	//*** Checks dbf file type
	if(m_pDbfHdr->cType != VFPTYPE) {  // = 0x30 (48)
		swprintf(
				szStr,
				_T("CMainDoc: Dbf type: %d doesn't equal to VFP type: %d!"),
				m_pDbfHdr->cType,
				VFPTYPE
		);

		_M(szStr);
		return FALSE;
	}
	/*
	//*** Shows date of last update
	
	swprintf(
			szStr, 
			_T("Date of last update is %0.2d.%0.2d.%d"), 
			m_pDbfHdr->cDay, 
			m_pDbfHdr->cMonth, 
			BASEYEAR + m_pDbfHdr->cYear
	);

	_M(szStr);
	*/
	//*** Number of records in file
	m_nRecCount = m_pDbfHdr->nRecCount;

	//*** Length of one data record (including delete flag)
	m_nRecSize = m_pDbfHdr->wRecSize;

	//*** Checks record size
	if((m_nRecSize == 0 && m_nRecCount != 0) ||
		 (m_nRecSize != 0 && m_nRecCount == 0)) {
		swprintf(
				szStr,
				_T("CMainDoc: Not matches record size (%d) and record count (%d)!"),
				m_nRecSize,
				m_nRecCount
		);

		_M(szStr);
		return FALSE;
	}

	//*** Calculates size of all records
	ULONG nDataSize = nDbfSize - 1 - m_pDbfHdr->wFirstRec;

	//*** Checks record parameters
	if(nDataSize != m_nRecCount*m_nRecSize) {
		swprintf(
				szStr,
				_T("CMainDoc: Data size (%d) doesn't equal record count (%d) * record size (%d)!"),
				nDataSize,
				m_nRecCount,
				m_nRecSize
		);

		_M(szStr);
		return FALSE;
	}

	//*** Number of fields in file (for Visual FoxPro only)
	m_nFldCount = (m_pDbfHdr->wFirstRec - 296)/32;

	//*** Checks field count
	if(m_nFldCount > m_nRecSize - 1) {
		_M("CMainDoc: Field count is very large!");
		return FALSE;
	}

	//*** Dbf field structure
	DBF_FIELD *aDbfField = m_pDbfView1->aDbfField;

	//*** Maps view of dbf file for its first part (where are data)
	m_pDbfView2 = reinterpret_cast<DBF_FILE2 *>(
			&m_pDbfView1->aDbfField[m_nFldCount].acName[0]
	);

	BYTE cHdrEnd = 0;

	//*** Checks dbf reading
	try {
		cHdrEnd = m_pDbfView2->cHdrEnd;
  } catch(...) {
		_M("CMainDoc: Dbf file has wrong structure!");
		return FALSE;
	}

	//*** Checks dbf header record terminator
	if(cHdrEnd != HEADEREND) {  // = 0x0D (13)
		swprintf(
				szStr, 
				_T("CMainDoc: Header record terminator: %d doesn't equal to: %d!"),
				m_pDbfView2->cHdrEnd,
				HEADEREND
		);

		_M(szStr);
		return FALSE;
	}

	//*** Delete flag  // = " " or "*"
	//_M(pDbfFile2->aDbfRec[0]);

	//*** Dynamic array of field names
	m_aacFldName = new BYTE *[m_nFldCount];

	//*** Dynamic array of field types
	m_acFldType = new BYTE[m_nFldCount];

	//*** Dynamic array of offsets
	m_anOff = new UINT[m_nFldCount];
	
	//*** Dynamic array of lengths
	m_acLen = new BYTE[m_nFldCount];
	
	//*** Dynamic array of decimal places
	m_acDec = new BYTE[m_nFldCount];

	//*** Initializes arrays of length, offsets and etc. of dbf fields
	for(int i = 0; i < m_nFldCount; i++) {
		//*** Field name with a maximum of 10 characters with null terminator
		m_aacFldName[i] = new BYTE[11];

		//*** Copies field name
		for(int j = 0; j < 11; j++)
				m_aacFldName[i][j] = aDbfField[i].acName[j];

		//*** Field type
		m_acFldType[i] = aDbfField[i].cType;

		//*** Field lenght (in bytes)
		m_acLen[i] = aDbfField[i].cLen;

		//*** Number of decimal places (in bytes)
		m_anOff[i] = aDbfField[i].nOffset;

		//*** Number of decimal places (in bytes)
		m_acDec[i] = aDbfField[i].cDec;
	}

	//*** Testing for all field of j-th record
	/*
	//*** j-th record
	ULONG j = 11;
	
	//*** Line displacement of i-th field of j-th record
	ULONG ji = 0;
	
	for(i = 0; i < m_nFldCount; i++) {
		ji = j*m_nRecSize + m_anOff[i];

		//*** The copy of (j, i) field value of m_anLen[i]-th length
		// As it hasn't null terminator
		CString sFldVal((LPCSTR) &m_pDbfMap2->aDbfRec[ji], m_anLen[i]);

		sFldVal.TrimLeft();
		sFldVal.TrimRight();

		swprintf(
				szStr, 
				_T("%s : %c : %d : %d.%d :: '%s'"),
				(CString) aszFldName[i],  // As it has null terminator
				acFldType[i],
				m_anOff[i],
				m_anLen[i],
				m_anDec[i],
				sFldVal
		);

		_M(szStr);
	}
	*/
	BYTE cFileEnd = 0;

	//*** Checks dbf reading
	try {
		cFileEnd = m_pDbfView2->aDbfRec[m_nRecCount * m_nRecSize];
  } catch(...) {
		_M("CMainDoc: Dbf file has wrong structure!");
		return FALSE;
	}

	//*** Checks dbf file record terminator
	if(cFileEnd != DBFEND) {  // = 0x1A (26)
		swprintf(
				szStr, 
	 			_T("CMainDoc: Header record terminator: %d doesn't equal to: %d!"),
				m_pDbfView2->aDbfRec[m_nRecCount * m_nRecSize], 
				DBFEND
		);

		_M(szStr);
		return FALSE;
	}

	//*** Current table
	CListCtrlEx *pTable = m_pMainApp->m_apTable[m_eTable];
	
	if(!pTable) {
		_M("CMainDoc: Empty a CListCtrlEx object!");
		return FALSE;
	}

	//*** Sets the table rows count in the virtual mode (LVS_OWNERDATA)
	//*** Send messages LVN_GETDISPINFOW & HDM_LAYOUT
	//*** Calls the CListCtrlEx::DrawItem
	pTable->SetItemCount(m_nRecCount);

	//*** Shows the vertical scroll bar always
	//pTable->ShowScrollBar(SB_VERT);

	//*** Saves the current document
	m_pMainApp->m_apDoc[m_eTable] = this;

	return TRUE;
}  // OnOpenDocument
/*
/////////////////////////////////////////////////////////////////////////////
// Serialize : CMainDoc serialization
/////////////////////////////////////////////////////////////////////////////
void CMainDoc::Serialize(CArchive &Ar) {
	if(Ar.IsStoring()) {
	} else {
	}
}  // Serialize
*/
/////////////////////////////////////////////////////////////////////////////
// OnDestroy
/////////////////////////////////////////////////////////////////////////////
void CMainDoc::OnDestroy() {
	::UnmapViewOfFile(m_pDbfView1);
	::CloseHandle(m_hDbfMap);
	::CloseHandle(m_hDbfFile);
}  // OnDestroy

/////////////////////////////////////////////////////////////////////////////
// CMainDoc diagnostics
/////////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
	/////////////////////////////////////////////////////////////////////////////
	// AssertValid
	/////////////////////////////////////////////////////////////////////////////
	void CMainDoc::AssertValid() const {
		CDocument::AssertValid();
	}  // AssertValid

	/////////////////////////////////////////////////////////////////////////////
	// Dump
	/////////////////////////////////////////////////////////////////////////////
	void CMainDoc::Dump(CDumpContext &dc) const {
		CDocument::Dump(dc);
	}  // Dump
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions