/////////////////////////////////////////////////////////////////////////////
// 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
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////