// GZRsetPool.cpp: implementation of the CGZRsetPool class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "sp.h" #include "GZRsetPool.h" #include "TED.H" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CGZRsetPool::CGZRsetPool() { //IMPORTANT: to avoid memory fragmentation set this //value to something higher than the highest amount used //pool.SetSize(10); //used to track how many were allocated //in order to set the pool size adequately m_nTotalAllocated=0; //default is JET m_bSQLServer=false; //Added 08/03/2001 to hide errors on update m_bSupressErrors=false; m_bUseTransactions=false; //TEST TEST TEST pConnection=NULL; } CGZRsetPool::~CGZRsetPool() { //dump any allocated recordsets if any Empty(); } //return a free recordset //sets nRSID to the unique serial number of this rs //for identification when closing later GZRset * CGZRsetPool::GetRS(CString strCallerInfo) { GZRset* rs; int x; x=GetFreeRecordsetID(false); rs=(GZRset*)pool.GetAt(x); ASSERT(rs!=NULL); rs->Close(); rs->m_bExclusiveConnection=false; TRACE("\r\n*******************************\r\n" "[ %i ] - total recordsets in the pool.\r\n" "*******************************\r\n" ,m_nTotalAllocated); rs->m_bRSPRINT=false; //added Sept 30th 2002 //ensures on a change of use transactions //that previously allocated rs's are set correctly //when first accessed rs->m_bUseTransactions=m_bUseTransactions; rs->SetErrMsg(strCallerInfo); return rs; } //return a free recordset //sets nRSID to the unique serial number of this rs //for identification when closing later GZRset * CGZRsetPool::GetRSPrint(CString strCallerInfo) { GZRset* rs; int x; x=GetFreeRecordsetID(true); rs=(GZRset*)pool.GetAt(x); ASSERT(rs!=NULL); rs->Close(); rs->m_bExclusiveConnection=false; TRACE("\r\n*******************************\r\n" "[ %i ] - total recordsets in the pool.\r\n" "*******************************\r\n" ,m_nTotalAllocated); rs->m_bRSPRINT=true; rs->SetErrMsg(strCallerInfo); return rs; } //flag recordset nRSID as available bool CGZRsetPool::ReleaseRS(int* nID) { GZRset* rs; rs=(GZRset*)pool.GetAt(*nID); ASSERT(rs!=NULL); //close it up just in case, //safe to call this repeatedly, //it just returns if already closed rs->Close(); //Throw the RS back in the pool rs->m_bReserved=false; return true; } //empty the pool //(delete all recordset objects) void CGZRsetPool::Empty() { int x; GZRset* rs; if(m_nTotalAllocated>0) { for(x=0;xClose(); delete rs; } m_nTotalAllocated=0; } //added March 22 2001 on a whim pool.RemoveAll(); } //RETURNS the pool objarray index value of a free //recordset, also sets the recordset as in use int CGZRsetPool::GetFreeRecordsetID(bool bPrint) { int x; if(m_nTotalAllocated==0)//none made yet { rsGENERIC = new GZRset("Allocated First"); InitializeNewRS(rsGENERIC,bPrint); x=pool.Add((CObject*)rsGENERIC); //set this recordset's ID value rsGENERIC->m_nID=x; //Increment the recordsets allocated counter m_nTotalAllocated++; return x; } //there are some allocated, see if any are free for(int y=0; ym_bReserved==false)//is it free for use? { rsGENERIC->m_bReserved=true; return y; } } //There are none free so we have to add one now rsGENERIC = new GZRset("Allocated"); InitializeNewRS(rsGENERIC, bPrint); x=pool.Add((CObject*)rsGENERIC); //set this recordset's ID value rsGENERIC->m_nID=x; m_nTotalAllocated++; //arbitrary test value ASSERT(m_nTotalAllocated<50); return x; } bool CGZRsetPool::InitializeNewRS(GZRset *rs,bool bPrint) { ASSERT(rs!=NULL); //TEST TEST TEST ASSERT(pConnection!=NULL); rs->pTheConnection=pConnection; //set initial connection strings here rs->SetConnect(strConnectString); //Set standard connection string here rs->m_strLiveConnectString=m_strLiveConnectString; //Set exclusive connection string here rs->m_strLiveConnectStringExclusive=m_strLiveConnectStringExclusive; //Set DEAD connect string: point to the empty database file rs->m_strDeadConnectString=m_strDeadConnectString; //query to execute against the empty database file //to ensure the original connection gets closed //to the live database file rs->m_strCloseQuery=m_strCloseQuery; //Take it out of the pool rs->m_bReserved=true; //Added May21 2001 SQL server stuff rs->m_bSQLServer=m_bSQLServer; //Added 08/03/2001 rs->m_bSupressErrors=m_bSupressErrors; //added 09/30/2002 rs->m_bUseTransactions=m_bUseTransactions; //This is now the first database call as of //December 8 2000 rs->Close(); return true; } //Diagnostic routine void CGZRsetPool::ShowAllOpenRS() { int x; GZRset* rs; CString strMsg; if(m_nTotalAllocated>0) { strMsg.Format("Total recordsets allocated:%i\r\n",m_nTotalAllocated); for(x=0;xm_bInUse ? "** RS IS ACTIVE **":".. RS NOT ACTIVE .."; strMsg=strMsg+rs->GetRSInfo()+"\r\n"+rs->m_strLastQuery+"\r\n"; strMsg+="\r\n---------------------------------\r\n"; } CTED d; d.m_strText=strMsg; d.DoModal(); } else AfxMessageBox("No recordsets in use"); } void CGZRsetPool::StartConnection() { //create a pointer to a connection //_ConnectionPtr pConnection = NULL; try{ //instantiate it: pConnection.CreateInstance(__uuidof(Connection)); //get connect string _bstr_t strCnn(m_strLiveConnectString); //open the connection //this will THROW if the database is corrupted pConnection->Open (strCnn, "", "", adConnectUnspecified); DisplayProperties(); } catch (_com_error &e) { CString cstrErrMsg,strEx;HRESULT hr=e.Error(); int nReason=0; // get info from com error _bstr_t bstrSource(e.Source()); _bstr_t bstrDescription(e.Description()); CString str((BSTR)bstrDescription); //Decode error message from JET (hope it's in english) //because Microsoft doesn't pass through the original jet error //only the MDAC general error 80004005 if(str.Find("File Already in Use")!=-1) nReason=1;//locked exclusive / no rights\ if(str.Find("The Microsoft Jet database engine cannot open the file '(unknown)'")!=-1) nReason=1;//locked exclusive / no rights\ if(str.Find("Name Not Found")!=-1) nReason=2;//file not found if(str.Find("Unrecognized database format")!=-1) nReason=3;//corrupt or not a database. switch(nReason) { case 0://unknown error strEx=""; break; case 1://exclusive or no rights strEx="Common causes for this error:\r\n" "1) Insufficient rights to the database folder and file\r\n" "(check that you have full access rights to the database folder)\r\n" "\r\n" "2) Database file is already open in exclusive only mode by another user or program.\r\n" "(check if another program has the database open such as Access\r\n" "in exclusive only mode)\r\n\r\n"; break; case 2://file not found strEx="Common causes for this error:\r\n" "1) The wrong location has been specified for the database\r\n" "2) The database file has been deleted\r\n" "3) The database file has been renamed\r\n\r\n"; break; case 3://corrupt / not a database strEx="Common causes for this error:\r\n" "1) The database file is not actually a database file\r\n" "(someone renamed a non-database file to a database file name)\r\n" "\r\n" "2) The database file is damaged and needs to be repaired.\r\n" "(See the AyaNova manual reference section:\r\n" "\"Repairing the AyaNova Database\"\r\n" "which has complete instructions on repairing\r\n" "the database, the causes of database corruption\r\n" "and how to prevent the problem from happening\r\n" "again in future.)\r\n\r\n"; break; } cstrErrMsg.Format( "AyaNova could not open the database file:\r\n" "\r\n" "%s\\scdata.sc\r\n" "\r\n" "The database driver gave the following reason:\r\n" "%s\r\n" "\r\n" "%s" "This program will now close...",m_strDBPATH,str,strEx); TRACE("*************************************************\n"); TRACE("Exception thrown for classes generated by #import\n"); TRACE("\tCode = %08x\n", hr); TRACE("\tCode Meaning = %s\n", e.ErrorMessage()); TRACE("\tSource = %s\n", (LPCTSTR) bstrSource); TRACE("Description = %s\n", (LPCTSTR) bstrDescription); TRACE("*************************************************\n"); #ifdef AYQB cstrErrMsg.Format( "AyaNova could not open the database file:\r\n\r\n" "%sqbtrial.aya\r\n\r\n" "This database file does not appear to be a AyaNova QBI trial edition\r\n" "compatible database.\r\n\r\n" "AyaNova will now close...",m_strDBPATH); #endif AfxMessageBox(cstrErrMsg); PostQuitMessage(-1);//exit now before anything worse happens return ; } catch (...) { TRACE("*** Unhandled exception ***"); //AfxMessageBox(m_strErrMsg); PostQuitMessage(-1);//exit now before anything worse happens return ; } } //**************************************************** // compact the database through a jro object //********************************************** bool CGZRsetPool::Compact(bool bKeepOld) { bool status=true; CString src,dest,tempfile; CFileStatus fstat; CWaitCursor wait; //delete old one if exists tempfile.Format("%sscdata.tmp",m_strDBPATH); if(CFile::GetStatus(tempfile,fstat)==TRUE)//file exists { TRY { CFile::Remove( tempfile ); } CATCH( CFileException, e ) { status=false; CString cstrErrMsg; cstrErrMsg.Format("Error in Compact: %s cannot be removed",tempfile); AfxMessageBox(cstrErrMsg); } END_CATCH } if(status==true) { src.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%sscdata.sc;Jet OLEDB:Engine Type=5;", m_strDBPATH); dest.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%sscdata.tmp;Jet OLEDB:Engine Type=5;", m_strDBPATH); _bstr_t bstsrc(src); _bstr_t bstdest(dest); try { IJetEnginePtr jet(__uuidof(JetEngine)); jet->CompactDatabase(bstsrc, bstdest); } catch(_com_error &e) { status=false; CString cstrErrMsg; //get info from com error _bstr_t bstrSource(e.Source()); _bstr_t bstrDescription(e.Description()); TRACE( "*************************************************\n"); TRACE( "Exception thrown for classes generated by #import\n"); TRACE( "\tCode = %081x\n", e.Error); TRACE( "\tCode Meaning = %s\n",e.ErrorMessage()); TRACE( "\tSource = %s\n", (LPCTSTR) bstrSource); TRACE( "Description = %s\n", (LPCTSTR) bstrDescription); TRACE( "*************************************************\n"); cstrErrMsg.Format("Error in Compact: %s,\r\n%s",e.ErrorMessage(),(LPCTSTR) bstrDescription); AfxMessageBox(cstrErrMsg); } catch(...) { status=false; TRACE( "*** Unhandled exception ***" ); AfxMessageBox("Unhandled Error in Compact"); PostQuitMessage(-1);//exit now before anything worse happens } } //Added. indexing can leave a 100+mb old file //this way can delete it if(bKeepOld) { //copy mdb over old src.Format("%sscdata.sc",m_strDBPATH); dest.Format("%sscdata.old",m_strDBPATH); status=CopyFile(src.GetBuffer(100),dest.GetBuffer(100)); } if(status==true) { src.Format("%sscdata.tmp",m_strDBPATH); dest.Format("%sscdata.sc",m_strDBPATH); status=CopyFile(src.GetBuffer(100),dest.GetBuffer(100)); }//if status==true //delete gz1 tempfile.Format("%sscdata.tmp",m_strDBPATH); if(CFile::GetStatus(tempfile,fstat)==TRUE)//file exists { TRY { CFile::Remove( tempfile ); } CATCH( CFileException, e ) { status=false; CString cstrErrMsg; cstrErrMsg.Format("Error at end of Compact: %s cannot be removed\r\nOtherwise compact OK",tempfile); AfxMessageBox(cstrErrMsg); } END_CATCH } return status; } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //******************************************* //copy a file, overwrite dest if exists bool CGZRsetPool::CopyFile(char *src, char *dest) { CFile sourceFile; CFile destFile; CFileException ex; CString tempfile; // open the source file for reading if (!sourceFile.Open(src, CFile::modeRead | CFile::shareDenyWrite, &ex)) { // complain if an error happened // no need to delete the ex object TCHAR szError[1024]; ex.GetErrorMessage(szError, 1024); CString errormsg; errormsg.Format("Error copying file %s to %s\r\n The error was: %s",src,dest,szError); AfxMessageBox(errormsg); return false; } else { if (!destFile.Open(dest, CFile::modeWrite | CFile::shareExclusive | CFile::modeCreate, &ex)) { TCHAR szError[1024]; ex.GetErrorMessage(szError, 1024); CString errormsg; errormsg.Format("Error copying file %s to %s\r\n The error was: %s",src,dest,szError); AfxMessageBox(errormsg); sourceFile.Close(); return false; } BYTE buffer[4096]; DWORD dwRead; // Read in 4096-byte blocks, // remember how many bytes were actually read, // and try to write that many out. This loop ends // when there are no more bytes to read. do { dwRead = sourceFile.Read(buffer, 4096); destFile.Write(buffer, dwRead); } while (dwRead > 0); // Close both files destFile.Close(); sourceFile.Close(); } return true; } bool CGZRsetPool::GoExclusive() { try{ if(pConnection->GetState()==adStateOpen) pConnection->Close(); //get connect string _bstr_t strCnn(m_strLiveConnectStringExclusive); //open the connection pConnection->Open (strCnn, "", "", adConnectUnspecified); } catch (_com_error &e) { CString cstrErrMsg; // get info from com error _bstr_t bstrSource(e.Source()); _bstr_t bstrDescription(e.Description()); TRACE("*************************************************\n"); TRACE("Exception thrown for classes generated by #import\n"); TRACE("\tCode = %081x\n", e.Error); TRACE("\tCode Meaning = %s\n", e.ErrorMessage()); TRACE("\tSource = %s\n", (LPCTSTR) bstrSource); TRACE("Description = %s\n", (LPCTSTR) bstrDescription); TRACE("*************************************************\n"); cstrErrMsg.Format("At GoExclusive access (all other users out)\r\n %s,\r\n%s", e.ErrorMessage(), (LPCTSTR) bstrDescription); //cstrErrMsg.Format("%s: %s,\r\n%s",m_strErrMsg, e.ErrorMessage(), (LPCTSTR) bstrDescription); AfxMessageBox(cstrErrMsg); return false; } catch (...) { TRACE("*** Unhandled exception ***"); //AfxMessageBox(m_strErrMsg); PostQuitMessage(-1);//exit now before anything worse happens return false; } return true; } bool CGZRsetPool::GoShared() { try{ if(pConnection->GetState()==adStateOpen) pConnection->Close(); //get connect string _bstr_t strCnn(m_strLiveConnectString); //open the connection pConnection->Open (strCnn, "", "", adConnectUnspecified); } catch (_com_error &e) { CString cstrErrMsg; // get info from com error _bstr_t bstrSource(e.Source()); _bstr_t bstrDescription(e.Description()); TRACE("*************************************************\n"); TRACE("Exception thrown for classes generated by #import\n"); TRACE("\tCode = %081x\n", e.Error); TRACE("\tCode Meaning = %s\n", e.ErrorMessage()); TRACE("\tSource = %s\n", (LPCTSTR) bstrSource); TRACE("Description = %s\n", (LPCTSTR) bstrDescription); TRACE("*************************************************\n"); cstrErrMsg.Format("At GoShared()\r\n%s,\r\n%s\r\n", e.ErrorMessage(), (LPCTSTR) bstrDescription); //cstrErrMsg.Format("%s: %s,\r\n%s",m_strErrMsg, e.ErrorMessage(), (LPCTSTR) bstrDescription); AfxMessageBox(cstrErrMsg); return false; } catch (...) { TRACE("*** Unhandled exception ***"); //AfxMessageBox(m_strErrMsg); PostQuitMessage(-1);//exit now before anything worse happens return false; } return true; } void CGZRsetPool::Disconnect() { if(pConnection->GetState()==adStateOpen) pConnection->Close(); } //DIAGNOSIS Class for messing about with //connection and other properties //not actually used by the program void CGZRsetPool::DisplayProperties() { PropertiesPtr pPrpLoop = NULL; _variant_t vtIndex; _variant_t propValue; vtIndex.vt = VT_I2; _variant_t vtOne; vtOne.vt=VT_I4; vtOne.lVal=1; CString strName; pPrpLoop=pConnection->GetProperties(); //pConnection->GetProperties()->GetItem()->Value=(_variant_t)1; for (int intProperties = 0; intProperties < (int)pPrpLoop-> GetCount(); intProperties++) { vtIndex.iVal = intProperties; propValue = pPrpLoop->GetItem(vtIndex)->Value; strName=(LPCSTR) pPrpLoop->GetItem(vtIndex)->GetName(); if(strName=="Jet OLEDB:Transaction Commit Mode") { pPrpLoop->GetItem(vtIndex)->Value=vtOne; break; } } /* #ifdef _DEBUG propValue = pPrpLoop->GetItem(vtIndex)->Value; ASSERT(propValue.iVal==1); #endif */ } //Get datbase properties for tech support //displayed under help->About->Support info... void CGZRsetPool::GetDBProps() { try{ if(pConnection->GetState()!=adStateOpen) return; //SET All version variables m_strADOVersion = (LPCSTR) pConnection->Version; m_strDBMSName = (LPCSTR) (_bstr_t) pConnection->Properties->GetItem("DBMS Name")->Value; m_strDBMSVersion= (LPCSTR) (_bstr_t) pConnection-> Properties->GetItem("DBMS Version")->Value; m_strOLEDbVersion=(LPCSTR) (_bstr_t) pConnection->Properties->GetItem("OLE DB Version")->Value; m_strProviderName=(LPCSTR) (_bstr_t) pConnection->Properties->GetItem("Provider Name")->Value; m_strProviderVersion=(LPCSTR) (_bstr_t) pConnection->Properties->GetItem("Provider Version")->Value; /* m_strDriverVersion=(LPCSTR) (_bstr_t) pConnection->Properties->GetItem("Driver Name")->Value; m_strDriverName=(LPCSTR) (_bstr_t) pConnection->Properties->GetItem("Driver Version")->Value; m_strDriverODBCVersion=(LPCSTR) (_bstr_t) pConnection->Properties->GetItem("Driver ODBC Version")->Value; */ } catch (_com_error &e) { CString cstrErrMsg; // get info from com error _bstr_t bstrSource(e.Source()); _bstr_t bstrDescription(e.Description()); TRACE("*************************************************\n"); TRACE("Exception thrown for classes generated by #import\n"); TRACE("\tCode = %081x\n", e.Error); TRACE("\tCode Meaning = %s\n", e.ErrorMessage()); TRACE("\tSource = %s\n", (LPCTSTR) bstrSource); TRACE("Description = %s\n", (LPCTSTR) bstrDescription); TRACE("*************************************************\n"); cstrErrMsg.Format("Error: Can't fetch database driver information.\r\n\r\nExact error is:\r\n%s", (LPCTSTR) bstrDescription); return ; } catch (...) { TRACE("*** Unhandled exception ***"); //AfxMessageBox(m_strErrMsg); return ; } } //Added 08/03/2001 to selective turn off error //warnings in gzrset to accomodate updates //so users don't freak out void CGZRsetPool::SupressErrors(bool bNoErrors) { m_bSupressErrors=bNoErrors; int x; GZRset* rs; if(m_nTotalAllocated>0) { for(x=0;xm_bSupressErrors=m_bSupressErrors; } } }