1181 lines
26 KiB
C++
Executable File
1181 lines
26 KiB
C++
Executable File
#include "StdAfx.h"
|
||
|
||
#include "errno.h"
|
||
#include "iserrors.h"
|
||
|
||
#include "connect.h"
|
||
#include "server.h"
|
||
#include "tracing.h"
|
||
|
||
#ifndef NO_MFC
|
||
#ifdef _DEBUG
|
||
#define new DEBUG_NEW
|
||
#undef THIS_FILE
|
||
static char THIS_FILE[] = __FILE__;
|
||
#endif
|
||
#endif
|
||
|
||
// @doc INTERNAL
|
||
|
||
// @rdesc Ritorna il valore corrispondente alla chiave appartenente
|
||
// ad una sezione del file di configurazione
|
||
|
||
static CString GetIniString(LPCSTR sec, // @parm Sezione
|
||
LPCSTR key, // @parm Chiave
|
||
LPCSTR def) // @parm Valore di default
|
||
{
|
||
// Nome del file .ini
|
||
static CString m_strIniFile;
|
||
|
||
// Lunghezza massima di un nome di directory
|
||
// o di un valore del file .ini
|
||
const DWORD dwSize = _MAX_DIR + 1;
|
||
|
||
// Costruisce il nome del file .ini se necessario
|
||
if (m_strIniFile.IsEmpty())
|
||
{
|
||
GetCurrentDirectory(dwSize, m_strIniFile.GetBuffer(dwSize));
|
||
m_strIniFile.ReleaseBuffer();
|
||
m_strIniFile += "\\prawin.ini";
|
||
}
|
||
|
||
// Legge il valore della chiave nella sezione specificata
|
||
CString tmp;
|
||
char* buf = tmp.GetBuffer(dwSize);
|
||
GetPrivateProfileString(sec, key, def, buf, dwSize, m_strIniFile);
|
||
tmp.ReleaseBuffer();
|
||
return tmp;
|
||
}
|
||
|
||
// @doc INTERNAL
|
||
|
||
// @class TFileInfo | Informazioni generali su di un file
|
||
//
|
||
// @base public | CObject
|
||
|
||
class TFileInfo : public CObject
|
||
// @author:(INTERNAL) Guido
|
||
|
||
// @access:(INTERNAL) Private Member
|
||
{
|
||
// @cmember:(INTERNAL) Nome del file
|
||
CString m_strName;
|
||
// @cmember:(INTERNAL) Nome del data source
|
||
CString m_strDataSource;
|
||
// @cmember:(INTERNAL) Descrizione del file
|
||
CString m_strDescription;
|
||
|
||
public:
|
||
// @cmember Ritorna il nome del file
|
||
const CString& GetName() const
|
||
{ return m_strName; }
|
||
|
||
// @cmember Ritorna il nome del data source
|
||
const CString& GetDataSource() const
|
||
{ return m_strDataSource; }
|
||
|
||
// @cmember Ritorna la descrizione del file
|
||
const CString& GetDescription() const
|
||
{ return m_strDescription; }
|
||
|
||
// @cmember Setta il nome del file
|
||
void SetName(LPCSTR sName)
|
||
{ m_strName = sName; }
|
||
|
||
// @cmember Setta il nome del data source
|
||
void SetDataSource(LPCSTR sDataSource)
|
||
{ m_strDataSource = sDataSource; }
|
||
|
||
// @cmember Setta la descrizione
|
||
void SetDescription(LPCSTR sDescription)
|
||
{ m_strDescription = sDescription; }
|
||
|
||
// @cmember Costruttore
|
||
TFileInfo() { }
|
||
// @cmember Distruttore
|
||
virtual ~TFileInfo() { }
|
||
};
|
||
|
||
// @doc INTERNAL
|
||
|
||
// @class TDataSources | Informazioni generali sui file
|
||
//
|
||
// @base public | CObject
|
||
|
||
class TDataSources : public CObject
|
||
// @author:(INTERNAL) Guido
|
||
|
||
// @access:(INTERNAL) Private Member
|
||
{
|
||
// @cmember:(INTERNAL) Motore di database DAO
|
||
CdbDBEngine m_dbEngine;
|
||
// @cmember:(INTERNAL) Nome del database principale
|
||
CString m_strDatabase;
|
||
// @cmember:(INTERNAL) Stringa di connsessione ODBC
|
||
CString m_strConnect;
|
||
// @cmember:(INTERNAL) Si tratta di una vera connessione ODBC
|
||
BOOL m_bODBC;
|
||
// @cmember:(INTERNAL) Elenco delle informazioni sui file
|
||
CMap<CString,LPCSTR,TFileInfo*,TFileInfo*> m_FileInfo;
|
||
// @cmember:(INTERNAL) Elenco dei database aperti
|
||
CMap<CString,LPCSTR,CdbDatabase*,CdbDatabase*> m_Databases;
|
||
|
||
protected:
|
||
// @cmember Carica tutte le descrizioni dei file
|
||
BOOL FillFileInfo();
|
||
// @cmember Ritorna le informazioni su di un file
|
||
const TFileInfo& GetFileInfo(int nLogicNumber) const;
|
||
|
||
// @cmember Costruisce il nome del database di un datasource
|
||
CString BuildDatabaseName(LPCSTR sName, int nFirm) const;
|
||
// @cmember Ritorna il database corrispondente ad un datasource
|
||
CdbDatabase* GetDatabase(LPCSTR sName, int nFirm);
|
||
// @cmember Ritorna il nome della tabella di un file
|
||
CString GetTableName(int nFirm, int nLogicNumber) const;
|
||
|
||
public:
|
||
// @cmember Ritorna TRUE se si tratta di una connessione ODBC
|
||
BOOL IsODBC() const { return m_bODBC; }
|
||
// @cmember Verifica l'esistenza di un datasource
|
||
BOOL TestDataSource(LPCSTR sName, int nFirm) const;
|
||
// @cmember Ritorna database e nome di un file
|
||
CdbDatabase* GetDataSource(int nFirm, int nLogicNum,
|
||
CString& strTableName);
|
||
// @cmember Toglie un riferimento al database
|
||
void ReleaseDataSource(int nFirm, int nLogicNum);
|
||
|
||
// @cmember Verifica la corretta inizializzazione
|
||
BOOL IsOk() const
|
||
{ return !m_FileInfo.IsEmpty(); }
|
||
|
||
// @cmember Costruttore
|
||
TDataSources();
|
||
// @cmember Distruttore
|
||
virtual ~TDataSources();
|
||
};
|
||
|
||
|
||
// @mfunc Costruisce il nome del database associato ad un
|
||
// determinato data source se <p sName> vale FIRM
|
||
// lo trasforma nel codice ditta corrispondente
|
||
// a <p nFirm>, es: 00883
|
||
CString TDataSources::BuildDatabaseName(
|
||
LPCSTR sName, // @parm Nome del data source
|
||
int nFirm) const // @parm Codice della ditta
|
||
{
|
||
CString strDataSource = m_strDatabase;
|
||
if (IsODBC())
|
||
{
|
||
// In ODBC esiste un unico database ad un livello
|
||
}
|
||
else
|
||
{
|
||
CString strName(sName);
|
||
if (strName == "FIRM")
|
||
strName.Format("%05dA", nFirm);
|
||
if (!strName.IsEmpty())
|
||
{
|
||
strDataSource += '\\';
|
||
strDataSource += strName;
|
||
}
|
||
}
|
||
return strDataSource;
|
||
}
|
||
|
||
// @mfunc Verifica l'esistenza di un data source
|
||
BOOL TDataSources::TestDataSource(
|
||
LPCSTR sName, // @parm Nome del data source
|
||
int nFirm) const // @parm Codice della ditta
|
||
{
|
||
CString strDataSource = BuildDatabaseName(sName, nFirm);
|
||
BOOL bOk = TRUE;
|
||
try
|
||
{
|
||
CdbDBEngine dbEngine;
|
||
dbEngine.OpenDatabase(strDataSource,
|
||
FALSE, FALSE,
|
||
m_strConnect);
|
||
}
|
||
catch(...)
|
||
{
|
||
bOk = FALSE;
|
||
}
|
||
return bOk;
|
||
}
|
||
|
||
// @mfunc Apre il database corrispondenete ad un data source
|
||
CdbDatabase* TDataSources::GetDatabase(
|
||
LPCSTR sName, // @parm Nome del data source
|
||
int nFirm) // @parm Codice della ditta
|
||
{
|
||
CString strDataSource = BuildDatabaseName(sName, nFirm);
|
||
CdbDatabase* pDatabase;
|
||
BOOL ok = m_Databases.Lookup(strDataSource, pDatabase);
|
||
if (!ok)
|
||
{
|
||
pDatabase = new CdbDatabase;
|
||
try
|
||
{
|
||
*pDatabase = m_dbEngine.OpenDatabase(strDataSource,
|
||
FALSE, FALSE,
|
||
m_strConnect);
|
||
m_Databases.SetAt(strDataSource, pDatabase);
|
||
}
|
||
catch(...)
|
||
{
|
||
delete pDatabase;
|
||
pDatabase = NULL;
|
||
}
|
||
}
|
||
|
||
return pDatabase;
|
||
}
|
||
|
||
// @mfunc Ritorna le informazioni sul file <p nLogicNumber>
|
||
const TFileInfo& TDataSources::GetFileInfo(int nLogicNumber) const
|
||
{
|
||
char codice[16]; itoa(nLogicNumber, codice, 10);
|
||
TFileInfo* fi;
|
||
BOOL ok = m_FileInfo.Lookup(codice, fi);
|
||
ASSERT(ok);
|
||
return *fi;
|
||
}
|
||
|
||
// @mfunc Ritorna il nome della tabella SQL contenete i dati
|
||
// del file ISAM <p nLogicNumber>
|
||
CString TDataSources::GetTableName(int nFirm, int nLogicNumber) const
|
||
{
|
||
const TFileInfo& fi = GetFileInfo(nLogicNumber);
|
||
CString strName;
|
||
if (IsODBC())
|
||
{
|
||
strName = fi.GetDataSource();
|
||
if (strName == "FIRM")
|
||
strName.Format("%05dA", nFirm);
|
||
strName += '_';
|
||
strName += fi.GetName();
|
||
}
|
||
else
|
||
strName = fi.GetName();
|
||
|
||
return strName;
|
||
}
|
||
|
||
// @mfunc Carica le informazioni relative a tutti i file ISAM
|
||
BOOL TDataSources::FillFileInfo()
|
||
{
|
||
CdbDatabase* pDatabase = GetDatabase("", 0);
|
||
if (pDatabase)
|
||
{
|
||
try
|
||
{
|
||
CdbRecordset rstDir = pDatabase->OpenRecordset("DIR", dbOpenDynaset);
|
||
|
||
COleVariant oleVariant;
|
||
for (rstDir.MoveFirst(); !rstDir.GetEOF(); rstDir.MoveNext())
|
||
{
|
||
oleVariant = rstDir.GetField("CODICE");
|
||
|
||
TFileInfo* fi = new TFileInfo;
|
||
m_FileInfo.SetAt((LPCSTR)oleVariant.pbVal, fi);
|
||
|
||
oleVariant = rstDir.GetField("NOME");
|
||
fi->SetName((LPCSTR)oleVariant.pbVal);
|
||
|
||
oleVariant = rstDir.GetField("SORGENTE");
|
||
fi->SetDataSource((LPCSTR)oleVariant.pbVal);
|
||
|
||
oleVariant = rstDir.GetField("DESCR");
|
||
fi->SetDescription((LPCSTR)oleVariant.pbVal);
|
||
}
|
||
}
|
||
catch(...)
|
||
{
|
||
pDatabase = NULL;
|
||
}
|
||
}
|
||
return pDatabase != NULL;
|
||
}
|
||
|
||
// @mfunc Ritorna il database ed il nome
|
||
CdbDatabase* TDataSources::GetDataSource(int nFirm, int nLogicNumber,
|
||
CString& strTableName)
|
||
{
|
||
const TFileInfo& fi = GetFileInfo(nLogicNumber);
|
||
const CString& strDataSource = fi.GetDataSource();
|
||
strTableName = GetTableName(nFirm, nLogicNumber);
|
||
return GetDatabase(strDataSource, nFirm);
|
||
}
|
||
|
||
// @mfunc Chiude un database inutilizzato
|
||
// Per il momemnto non si sa come implementarla
|
||
void TDataSources::ReleaseDataSource(int nFirm, int nLogicNumber)
|
||
{
|
||
// TODO:
|
||
}
|
||
|
||
// @mfunc Costruttore del'insieme dei data source
|
||
TDataSources::TDataSources()
|
||
{
|
||
const char* const odbc = "ODBC";
|
||
|
||
m_dbEngine.SetDefaultUser(GetIniString(odbc, "User", odbc));
|
||
m_dbEngine.SetDefaultPassword(GetIniString(odbc, "Pwd", odbc));
|
||
|
||
m_strDatabase = GetIniString(odbc, "Database", odbc);
|
||
m_strConnect = GetIniString(odbc, "Connect", odbc);
|
||
|
||
m_bODBC = m_strConnect.Mid(0, 4) == odbc;
|
||
|
||
FillFileInfo();
|
||
}
|
||
|
||
// @mfunc Distruttore del'insiemde dei data source
|
||
TDataSources::~TDataSources()
|
||
{
|
||
POSITION pos; // Posizione corrente nella lista
|
||
CString strKey; // Chiave dell'elemento corrente della lista
|
||
|
||
// Vuota la lista dei database
|
||
for (pos = m_Databases.GetStartPosition(); pos; )
|
||
{
|
||
CdbDatabase* pDatabase; // Database corrente
|
||
m_Databases.GetNextAssoc(pos, strKey, pDatabase);
|
||
pDatabase->Close();
|
||
delete pDatabase;
|
||
}
|
||
m_Databases.RemoveAll();
|
||
|
||
// Vuota la lista delle informazioni sui file
|
||
for (pos = m_FileInfo.GetStartPosition(); pos; )
|
||
{
|
||
TFileInfo* pFileInfo; // File corrente
|
||
m_FileInfo.GetNextAssoc(pos, strKey, pFileInfo);
|
||
delete pFileInfo;
|
||
}
|
||
m_FileInfo.RemoveAll();
|
||
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// TTableDef
|
||
|
||
class TTableDef : public CObject
|
||
{
|
||
CdbDatabase* m_db;
|
||
CString m_strTableName;
|
||
|
||
CdbRecordset m_Dynaset;
|
||
|
||
clock_t m_tModifyTime;
|
||
int m_nReferences;
|
||
|
||
CMap<CString,LPCSTR,DWORD,DWORD> m_Locks;
|
||
DWORD m_dwExclusiveLocker;
|
||
|
||
protected:
|
||
friend class TTableDefPool;
|
||
int AddReference() { return ++m_nReferences; }
|
||
int RemoveReference() { return --m_nReferences; }
|
||
void ZeroReferences() { m_nReferences = 0; }
|
||
|
||
public:
|
||
BOOL IsChangedSince(clock_t t) const
|
||
{ return t <= m_tModifyTime; }
|
||
|
||
CdbDatabase& GetDatabase() { return *m_db; }
|
||
const CString& GetTableName() { return m_strTableName; }
|
||
|
||
CdbTableDef GetTableDef();
|
||
CdbRecordset& GetDynaset();
|
||
|
||
int Lock(const char* strKey, DWORD dwConnection);
|
||
int Unlock(const char* strKey, DWORD dwConnection);
|
||
|
||
int ExclusiveLock(DWORD dwConnection);
|
||
BOOL IsLocked() const { return m_dwExclusiveLocker != 0; }
|
||
int ExclusiveUnlock(DWORD dwConnection);
|
||
|
||
TTableDef(CdbDatabase* db, LPCSTR strTableName);
|
||
virtual ~TTableDef();
|
||
};
|
||
|
||
CdbTableDef TTableDef::GetTableDef()
|
||
{
|
||
return (*m_db)[m_strTableName];
|
||
}
|
||
|
||
CdbRecordset& TTableDef::GetDynaset()
|
||
{
|
||
if (!m_Dynaset.Exists())
|
||
{
|
||
// Genera la query basata sulla chiave primaria = 0
|
||
CString strQuery;
|
||
strQuery = "SELECT * FROM ";
|
||
strQuery += m_strTableName;
|
||
strQuery = " ORDER BY ";
|
||
|
||
CdbTableDef dbTable = (*m_db)[m_strTableName];
|
||
CdbIndex dbIndex = dbTable.Indexes[0L];
|
||
CdbFields& idxFields = dbIndex.Fields;
|
||
for (int f = 0; f < idxFields.GetCount(); f++)
|
||
{
|
||
if (f) strQuery += ",";
|
||
strQuery += idxFields[f].GetName();
|
||
}
|
||
strQuery += ";";
|
||
m_Dynaset = m_db->OpenRecordset(strQuery, dbOpenDynaset);
|
||
}
|
||
return m_Dynaset;
|
||
}
|
||
|
||
int TTableDef::Lock(const char* strKey, DWORD dwConnection)
|
||
{
|
||
DWORD dwLocker;
|
||
if (!m_Locks.Lookup(strKey, dwLocker))
|
||
{
|
||
m_Locks[strKey] = dwConnection;
|
||
dwLocker = dwConnection;
|
||
}
|
||
return dwLocker == dwConnection ? NOERR : _islocked;
|
||
}
|
||
|
||
int TTableDef::Unlock(LPCSTR strKey, DWORD dwConnection)
|
||
{
|
||
int err = NOERR;
|
||
|
||
if (strKey)
|
||
{
|
||
DWORD dwLocker;
|
||
if (m_Locks.Lookup(strKey, dwLocker))
|
||
{
|
||
if (dwLocker == dwConnection)
|
||
m_Locks.RemoveKey(strKey);
|
||
else
|
||
err = _islocked;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (POSITION pos = m_Locks.GetStartPosition(); pos; )
|
||
{
|
||
CString strKey;
|
||
DWORD dwLocker;
|
||
m_Locks.GetNextAssoc(pos, strKey, dwLocker);
|
||
if (dwLocker == dwConnection)
|
||
m_Locks.RemoveKey(strKey);
|
||
}
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
int TTableDef::ExclusiveLock(DWORD dwConnection)
|
||
{
|
||
if (m_dwExclusiveLocker == dwConnection)
|
||
return NOERR;
|
||
if (m_dwExclusiveLocker != 0 || m_nReferences > 1)
|
||
return _islocked;
|
||
m_dwExclusiveLocker = dwConnection;
|
||
return NOERR;
|
||
}
|
||
|
||
int TTableDef::ExclusiveUnlock(DWORD dwConnection)
|
||
{
|
||
if (m_dwExclusiveLocker == dwConnection)
|
||
m_dwExclusiveLocker = 0;
|
||
return m_dwExclusiveLocker == 0 ? NOERR : _islocked;
|
||
}
|
||
|
||
TTableDef::TTableDef(CdbDatabase* db, LPCSTR strTableName)
|
||
: m_db(db), m_strTableName(strTableName),
|
||
m_nReferences(0), m_dwExclusiveLocker(0)
|
||
{
|
||
m_tModifyTime = clock();
|
||
}
|
||
|
||
TTableDef::~TTableDef()
|
||
{
|
||
ASSERT(m_nReferences == 0);
|
||
if (m_Dynaset.Exists())
|
||
m_Dynaset.Close();
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// TTableDefPool
|
||
|
||
class TTableDefPool : public CObject
|
||
{
|
||
CMap<DWORD,DWORD,TTableDef*, TTableDef*> m_Tables;
|
||
|
||
protected:
|
||
enum { FIRM_MULTIPLIER = 10000 };
|
||
DWORD LogToIndex(int nFirm, int nLogicNumber) const;
|
||
|
||
public:
|
||
TTableDef* Find(int nFirm, int nLogicNumber) const;
|
||
|
||
void Add(int nFirm, int nLogicNumber, TTableDef* pTable);
|
||
|
||
void AddReference(int nFirm, int nLogicNumber);
|
||
|
||
TTableDef& TableDef(int nFirm, int nLogicNumber) const;
|
||
|
||
int RemoveReference(int nFirm, int nLogicNumber);
|
||
|
||
BOOL FindFirm(int nFirm) const;
|
||
|
||
TTableDefPool();
|
||
virtual ~TTableDefPool();
|
||
};
|
||
|
||
DWORD TTableDefPool::LogToIndex(int nFirm, int nLogicNumber) const
|
||
{
|
||
DWORD dwIndex = (nFirm * FIRM_MULTIPLIER) + nLogicNumber;
|
||
return dwIndex;
|
||
}
|
||
|
||
TTableDef* TTableDefPool::Find(int nFirm, int nLogicNumber) const
|
||
{
|
||
const DWORD dwIndex = LogToIndex(nFirm, nLogicNumber);
|
||
TTableDef* pTable;
|
||
BOOL bOk = m_Tables.Lookup(dwIndex, pTable);
|
||
return bOk ? pTable : NULL;
|
||
}
|
||
|
||
TTableDef& TTableDefPool::TableDef(int nFirm, int nLogicNumber) const
|
||
{
|
||
return *Find(nFirm, nLogicNumber);
|
||
}
|
||
|
||
|
||
void TTableDefPool::Add(int nFirm, int nLogicNumber, TTableDef* pTable)
|
||
{
|
||
ASSERT(Find(nFirm, nLogicNumber) == NULL);
|
||
const DWORD dwIndex = LogToIndex(nFirm, nLogicNumber);
|
||
m_Tables.SetAt(dwIndex, pTable);
|
||
}
|
||
|
||
void TTableDefPool::AddReference(int nFirm, int nLogicNumber)
|
||
{
|
||
TTableDef* pTable = Find(nFirm, nLogicNumber);
|
||
pTable->AddReference();
|
||
}
|
||
|
||
// @mfunc Decrementa il numero di riferimenti al file <p nLogicNumber>
|
||
// della ditta <p nFirm>. Se questo numero e' zero allora
|
||
// rimuove la definizione dalla lista
|
||
int TTableDefPool::RemoveReference(int nFirm, int nLogicNumber)
|
||
{
|
||
const DWORD dwIndex = LogToIndex(nFirm, nLogicNumber);
|
||
int nRef = 0;
|
||
TTableDef* pTable;
|
||
if (m_Tables.Lookup(dwIndex, pTable))
|
||
{
|
||
nRef = pTable->RemoveReference();
|
||
if (nRef == 0)
|
||
{
|
||
delete pTable;
|
||
m_Tables.RemoveKey(dwIndex);
|
||
}
|
||
}
|
||
return nRef;
|
||
}
|
||
|
||
// @mfunc Verifica l'esistenza di almeno un file appartenente
|
||
// alla ditta <p nFirm>
|
||
BOOL TTableDefPool::FindFirm(int nFirm) const
|
||
{
|
||
POSITION pos;
|
||
for (pos = m_Tables.GetStartPosition(); pos;)
|
||
{
|
||
DWORD dwIndex;
|
||
TTableDef* pTable;
|
||
m_Tables.GetNextAssoc(pos, dwIndex, pTable);
|
||
if (int(dwIndex / FIRM_MULTIPLIER) == nFirm)
|
||
break;
|
||
}
|
||
return pos != NULL;
|
||
}
|
||
|
||
TTableDefPool::TTableDefPool()
|
||
{
|
||
}
|
||
|
||
// @mfunc Vuota la lista delle definizioni delle tabelle SQL
|
||
TTableDefPool::~TTableDefPool()
|
||
{
|
||
for (POSITION pos = m_Tables.GetStartPosition(); pos;)
|
||
{
|
||
DWORD dwIndex;
|
||
TTableDef* pTable;
|
||
m_Tables.GetNextAssoc(pos, dwIndex, pTable);
|
||
// Azzera i riferimenti prima di distruggere
|
||
pTable->ZeroReferences();
|
||
delete pTable;
|
||
}
|
||
m_Tables.RemoveAll();
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// TSnapshot
|
||
|
||
class TSnapshot : public CObject
|
||
{
|
||
CdbDatabase* m_db;
|
||
CString m_strQuery;
|
||
CdbRecordset* m_Snapshot;
|
||
long m_nPosition;
|
||
|
||
BOOL m_bReusable;
|
||
|
||
clock_t m_tCreationTime;
|
||
clock_t m_tAccessTime;
|
||
|
||
public:
|
||
clock_t GetCreationTime() const { return m_tCreationTime; }
|
||
clock_t GetAccessTime() const { return m_tAccessTime; }
|
||
|
||
BOOL IsEqual(const CdbDatabase* pDatabase, LPCSTR sQuery) const;
|
||
|
||
BOOL IsOpen() const;
|
||
CdbRecordset& GetSnapshot();
|
||
void Update();
|
||
void Close();
|
||
|
||
void MakeReusable();
|
||
BOOL Reusable() const { return m_bReusable; }
|
||
void Reuse();
|
||
|
||
TSnapshot(CdbDatabase* db, LPCSTR strQuery);
|
||
virtual ~TSnapshot();
|
||
};
|
||
|
||
BOOL TSnapshot::IsOpen() const
|
||
{
|
||
return m_Snapshot != NULL;
|
||
}
|
||
|
||
CdbRecordset& TSnapshot::GetSnapshot()
|
||
{
|
||
if (!IsOpen())
|
||
{
|
||
m_tCreationTime = clock();
|
||
m_Snapshot = new CdbRecordset;
|
||
try
|
||
{
|
||
*m_Snapshot = m_db->OpenRecordset(m_strQuery, dbOpenSnapshot);
|
||
}
|
||
catch(...)
|
||
{
|
||
TRACE("Can't open snapshot!");
|
||
ASSERT(0);
|
||
}
|
||
if (m_nPosition > 0)
|
||
{
|
||
try
|
||
{
|
||
m_Snapshot->Move(m_nPosition);
|
||
}
|
||
catch(...)
|
||
{
|
||
TRACE("Can't restore snapshot position!");
|
||
}
|
||
}
|
||
}
|
||
m_tAccessTime = clock();
|
||
return *m_Snapshot;
|
||
}
|
||
|
||
void TSnapshot::Update()
|
||
{
|
||
if (IsOpen())
|
||
{
|
||
m_tCreationTime = clock();
|
||
m_Snapshot->Requery();
|
||
}
|
||
}
|
||
|
||
void TSnapshot::MakeReusable()
|
||
{
|
||
ASSERT(!m_bReusable);
|
||
m_bReusable = TRUE;
|
||
}
|
||
|
||
void TSnapshot::Reuse()
|
||
{
|
||
ASSERT(m_bReusable);
|
||
m_bReusable = FALSE;
|
||
}
|
||
|
||
void TSnapshot::Close()
|
||
{
|
||
if (IsOpen())
|
||
{
|
||
m_nPosition = m_Snapshot->GetAbsolutePosition();
|
||
m_Snapshot->Close();
|
||
delete m_Snapshot;
|
||
m_Snapshot = NULL;
|
||
m_tCreationTime = 0;
|
||
}
|
||
}
|
||
|
||
BOOL TSnapshot::IsEqual(const CdbDatabase* pDatabase,
|
||
LPCSTR sQuery) const
|
||
{
|
||
return m_db == pDatabase && m_strQuery == sQuery;
|
||
}
|
||
|
||
|
||
TSnapshot::TSnapshot(CdbDatabase* db, LPCSTR strQuery)
|
||
: m_db(db), m_Snapshot(NULL), m_strQuery(strQuery),
|
||
m_tAccessTime(0), m_tCreationTime(0), m_nPosition(-1),
|
||
m_bReusable(FALSE)
|
||
{
|
||
}
|
||
|
||
TSnapshot::~TSnapshot()
|
||
{
|
||
Close();
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// TSnapshotPool
|
||
|
||
class TSnapshotPool : public CObject
|
||
{
|
||
CMap<DWORD,DWORD,TSnapshot*,TSnapshot*> m_Snapshots;
|
||
DWORD m_dwNextId;
|
||
|
||
protected:
|
||
DWORD RecycleSnapshot(CdbDatabase* pDatabase, LPCSTR sQuery);
|
||
|
||
public:
|
||
DWORD AddSnapshot(CdbDatabase* pDatabase, LPCSTR sQuery);
|
||
|
||
TSnapshot& operator[](DWORD nHandle)
|
||
{ return *m_Snapshots[nHandle]; }
|
||
|
||
void RemoveSnapshot(DWORD nHandle);
|
||
|
||
TSnapshotPool();
|
||
virtual ~TSnapshotPool();
|
||
};
|
||
|
||
|
||
TSnapshotPool::TSnapshotPool()
|
||
: m_dwNextId(0)
|
||
{
|
||
}
|
||
|
||
// @mfunc Data <p sQuery> cerca uno snapshot inutilizzato che
|
||
// abbia la stessa query: se lo trova lo riutilizza
|
||
DWORD TSnapshotPool::RecycleSnapshot(CdbDatabase* pDatabase,
|
||
LPCSTR sQuery)
|
||
{
|
||
for (POSITION pos = m_Snapshots.GetStartPosition(); pos; )
|
||
{
|
||
DWORD dwId;
|
||
TSnapshot* pSnapshot;
|
||
m_Snapshots.GetNextAssoc(pos, dwId, pSnapshot);
|
||
if (pSnapshot->Reusable() &&
|
||
pSnapshot->IsEqual(pDatabase, sQuery))
|
||
{
|
||
pSnapshot->Reuse();
|
||
return dwId;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
DWORD TSnapshotPool::AddSnapshot(CdbDatabase* pDatabase,
|
||
LPCSTR sQuery)
|
||
{
|
||
// Tenta di riciclare uno Snapshot precedente gia' aperto
|
||
DWORD dwId = RecycleSnapshot(pDatabase, sQuery);
|
||
if (dwId)
|
||
return dwId;
|
||
|
||
TSnapshot* pNew = new TSnapshot(pDatabase, sQuery);
|
||
|
||
const int MAX_OPEN = 32;
|
||
if (m_Snapshots.GetCount() > MAX_OPEN)
|
||
{
|
||
TSnapshot* pOld = NULL;
|
||
DWORD dwOldId = 0;
|
||
|
||
int nReallyOpen = 0;
|
||
for (POSITION pos = m_Snapshots.GetStartPosition(); pos; )
|
||
{
|
||
DWORD dwId;
|
||
TSnapshot* pSnapshot;
|
||
m_Snapshots.GetNextAssoc(pos, dwId, pSnapshot);
|
||
if (pSnapshot->IsOpen())
|
||
{
|
||
nReallyOpen++;
|
||
if (pOld == NULL ||
|
||
pSnapshot->GetAccessTime() < pOld->GetAccessTime())
|
||
{
|
||
pOld = pSnapshot;
|
||
dwOldId = dwId;
|
||
}
|
||
}
|
||
}
|
||
if (nReallyOpen > MAX_OPEN);
|
||
{
|
||
pOld->Close();
|
||
if (pOld->Reusable())
|
||
RemoveSnapshot(dwOldId);
|
||
}
|
||
}
|
||
|
||
m_dwNextId++;
|
||
m_Snapshots.SetAt(m_dwNextId, pNew);
|
||
|
||
return m_dwNextId;
|
||
}
|
||
|
||
void TSnapshotPool::RemoveSnapshot(DWORD nHandle)
|
||
{
|
||
TSnapshot* pSnapshot = m_Snapshots[nHandle];
|
||
if (pSnapshot->IsOpen())
|
||
pSnapshot->MakeReusable();
|
||
else
|
||
{
|
||
delete pSnapshot;
|
||
m_Snapshots.RemoveKey(nHandle);
|
||
}
|
||
}
|
||
|
||
TSnapshotPool::~TSnapshotPool()
|
||
{
|
||
for (POSITION pos = m_Snapshots.GetStartPosition(); pos; )
|
||
{
|
||
DWORD dwId;
|
||
TSnapshot* pSnapshot;
|
||
m_Snapshots.GetNextAssoc(pos, dwId, pSnapshot);
|
||
delete pSnapshot;
|
||
}
|
||
m_Snapshots.RemoveAll();
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// TPrassiServer
|
||
|
||
TConnection* TPrassiServer::OnCreateConnection(DWORD id)
|
||
{
|
||
Trace(0, "Connecting %lu", id);
|
||
return new TPrassiConnection(this, id);
|
||
}
|
||
|
||
BOOL TPrassiServer::OnRemoveConnection(DWORD id)
|
||
{
|
||
Trace(0, "Disconnecting %lu", id);
|
||
return BASE_SERVER::OnRemoveConnection(id);
|
||
}
|
||
|
||
BOOL TPrassiServer::TestFirm(int nFirm) const
|
||
{
|
||
BOOL bOk = m_pTables->FindFirm(nFirm);
|
||
if (!bOk)
|
||
bOk = m_pDataSources->TestDataSource("FIRM", nFirm);
|
||
return bOk;
|
||
}
|
||
|
||
|
||
BOOL TPrassiServer::OpenTableDef(int nFirm, int nLogicNumber)
|
||
{
|
||
TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
if (pTable == NULL)
|
||
{
|
||
CString strTableName;
|
||
CdbDatabase* db =
|
||
m_pDataSources->GetDataSource(nFirm,
|
||
nLogicNumber,
|
||
strTableName);
|
||
if (db == NULL)
|
||
return FALSE;
|
||
try
|
||
{
|
||
(*db)[strTableName].Exists();
|
||
}
|
||
catch(...)
|
||
{
|
||
m_pDataSources->ReleaseDataSource(nFirm, nLogicNumber);
|
||
return FALSE;
|
||
}
|
||
|
||
pTable = new TTableDef(db, strTableName);
|
||
m_pTables->Add(nFirm, nLogicNumber, pTable);
|
||
}
|
||
|
||
if (pTable != NULL)
|
||
{
|
||
if (pTable->IsLocked())
|
||
return FALSE;
|
||
m_pTables->AddReference(nFirm, nLogicNumber);
|
||
}
|
||
|
||
return pTable != NULL;
|
||
}
|
||
|
||
CdbTableDef TPrassiServer::GetTableDef(int nFirm, int nLogicNumber)
|
||
{
|
||
TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
return pTable->GetTableDef();
|
||
}
|
||
|
||
CdbRecordset& TPrassiServer::GetDynaset(int nFirm, int nLogicNumber)
|
||
{
|
||
TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
return pTable->GetDynaset();
|
||
}
|
||
|
||
void TPrassiServer::CloseTableDef(int nFirm, int nLogicNumber)
|
||
{
|
||
int nRef = m_pTables->RemoveReference(nFirm, nLogicNumber);
|
||
if (nRef == 0)
|
||
m_pDataSources->ReleaseDataSource(nFirm, nLogicNumber);
|
||
}
|
||
|
||
DWORD TPrassiServer::OpenSnapshot(int nFirm, int nLogicNumber,
|
||
LPCSTR sWhere, LPCSTR sOrder)
|
||
{
|
||
TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
|
||
CString strQuery;
|
||
if (sOrder || sWhere)
|
||
{
|
||
strQuery = "SELECT * FROM ";
|
||
strQuery += pTable->GetTableName();
|
||
if (sWhere)
|
||
{
|
||
strQuery += " WHERE ";
|
||
strQuery += sWhere;
|
||
}
|
||
if (sOrder)
|
||
{
|
||
strQuery += " ORDER BY ";
|
||
int nKey = atoi(sOrder);
|
||
if (nKey > 0)
|
||
{
|
||
CdbTableDef dbTable = pTable->GetTableDef();
|
||
CdbIndex dbIndex = dbTable.Indexes[nKey-1];
|
||
CdbFields& idxFields = dbIndex.Fields;
|
||
for (int f = 0; f < idxFields.GetCount(); f++)
|
||
{
|
||
if (f) strQuery += ",";
|
||
strQuery += idxFields[f].GetName();
|
||
}
|
||
}
|
||
else
|
||
strQuery += sOrder;
|
||
}
|
||
strQuery += ";";
|
||
}
|
||
else
|
||
strQuery = pTable->GetTableName();
|
||
|
||
CdbDatabase* pDatabase = &pTable->GetDatabase();
|
||
DWORD dwHandle = m_pSnapshots->AddSnapshot(pDatabase, strQuery);
|
||
return dwHandle;
|
||
}
|
||
|
||
CdbRecordset& TPrassiServer::GetSnapshot(DWORD nHandle)
|
||
{
|
||
return (*m_pSnapshots)[nHandle].GetSnapshot();
|
||
}
|
||
|
||
BOOL TPrassiServer::UpdateSnapshot(DWORD nHandle,
|
||
int nFirm, int nLogicNumber)
|
||
{
|
||
TSnapshot& snap = (*m_pSnapshots)[nHandle];
|
||
const clock_t tCreationTime = snap.GetCreationTime();
|
||
const TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
ASSERT(pTable);
|
||
BOOL bChanged = pTable->IsChangedSince(tCreationTime);
|
||
if (bChanged)
|
||
snap.Update();
|
||
return bChanged;
|
||
}
|
||
|
||
void TPrassiServer::CloseSnapshot(DWORD nHandle)
|
||
{
|
||
m_pSnapshots->RemoveSnapshot(nHandle);
|
||
}
|
||
|
||
int TPrassiServer::Lock(int nFirm, int nLogicNumber,
|
||
LPCSTR sKey, DWORD dwLocker)
|
||
{
|
||
TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
ASSERT(pTable);
|
||
return pTable->Lock(sKey, dwLocker);
|
||
}
|
||
|
||
int TPrassiServer::Unlock(int nFirm, int nLogicNumber,
|
||
LPCSTR sKey, DWORD dwLocker)
|
||
{
|
||
TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
ASSERT(pTable);
|
||
return pTable->Unlock(sKey, dwLocker);
|
||
}
|
||
|
||
int TPrassiServer::ExclusiveLock(int nFirm, int nLogicNumber, DWORD dwLocker)
|
||
{
|
||
TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
ASSERT(pTable);
|
||
return pTable->ExclusiveLock(dwLocker);
|
||
}
|
||
|
||
int TPrassiServer::ExclusiveUnlock(int nFirm, int nLogicNumber, DWORD dwLocker)
|
||
{
|
||
TTableDef* pTable = m_pTables->Find(nFirm, nLogicNumber);
|
||
ASSERT(pTable);
|
||
return pTable->ExclusiveUnlock(dwLocker);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// Dongle Management
|
||
///////////////////////////////////////////////////////////
|
||
|
||
#include "hlapi_c.h"
|
||
|
||
WORD TPrassiServer::m_DongleNumber;
|
||
WORD TPrassiServer::m_DongleModules[DONGLE_WORDS];
|
||
|
||
WORD TPrassiServer::DongleLogin()
|
||
{
|
||
unsigned char* REFKEY = (unsigned char*)"CAMPOKEY";
|
||
unsigned char* VERKEY = (unsigned char*)"<EFBFBD>pو<EFBFBD>c<EFBFBD><";
|
||
const int status = HL_LOGIN(26952, LOCAL_DEVICE, REFKEY, VERKEY);
|
||
|
||
Trace(-1, "Hardlock login %s: %d",
|
||
status == STATUS_OK ? "OK" : "Failed", status);
|
||
|
||
m_DongleNumber = 0xFFFF;
|
||
|
||
if (status == STATUS_OK)
|
||
{
|
||
Word Val[4] = { 0, 0, 0, 0 };
|
||
for (int i = 0; i < 4; i++)
|
||
HL_READ(i, &Val[i]);
|
||
HL_CODE(Val, 1);
|
||
if (Val[0] == 0xFAE8)
|
||
m_DongleNumber = Val[1];
|
||
else
|
||
m_DongleNumber = 0;
|
||
}
|
||
|
||
Trace(-1, "Hardlock serial number is %s: %d",
|
||
(m_DongleNumber == 0xFFFF) ? "Bad" : "OK", (int)m_DongleNumber);
|
||
|
||
switch(m_DongleNumber)
|
||
{
|
||
case 0x0000:
|
||
memset(m_DongleModules, 0xFF, sizeof(m_DongleModules));
|
||
break;
|
||
case 0xFFFF:
|
||
memset(m_DongleModules, 0x00, sizeof(m_DongleModules));
|
||
break;
|
||
default:
|
||
{
|
||
int i;
|
||
for (i = 0; i < DONGLE_WORDS; i++)
|
||
{
|
||
HL_READ(48+i, &m_DongleModules[i]);
|
||
if (i % 4 == 3)
|
||
HL_CODE(&m_DongleModules[i-3], 1);
|
||
}
|
||
for (i = 0; i < DONGLE_WORDS; i++)
|
||
m_DongleModules[i] ^= m_DongleNumber;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return m_DongleNumber;
|
||
}
|
||
|
||
void TPrassiServer::DongleLogout()
|
||
{
|
||
HL_LOGOUT();
|
||
m_DongleNumber = 0xFFFF;
|
||
memset(m_DongleModules, 0x00, sizeof(m_DongleModules));
|
||
}
|
||
|
||
BOOL TPrassiServer::IsOk() const
|
||
{
|
||
BOOL ok = BASE_SERVER::IsOk();
|
||
if (ok)
|
||
ok &= m_pDataSources->IsOk();
|
||
return ok;
|
||
}
|
||
|
||
TPrassiServer::TPrassiServer()
|
||
: BASE_SERVER("PRASSI")
|
||
{
|
||
m_pDataSources = new TDataSources;
|
||
m_pTables = new TTableDefPool;
|
||
m_pSnapshots = new TSnapshotPool;
|
||
}
|
||
|
||
TPrassiServer::~TPrassiServer()
|
||
{
|
||
delete m_pSnapshots;
|
||
delete m_pTables;
|
||
delete m_pDataSources;
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// Start/Get/Stop server
|
||
|
||
static TPrassiServer* pServer = NULL;
|
||
|
||
BOOL StopServer()
|
||
{
|
||
BOOL ok = pServer != NULL;
|
||
if (ok)
|
||
{
|
||
TPrassiServer::DongleLogout();
|
||
delete pServer;
|
||
pServer = NULL;
|
||
}
|
||
return ok;
|
||
}
|
||
|
||
BOOL StartServer()
|
||
{
|
||
BOOL ok = pServer == NULL;
|
||
if (ok)
|
||
{
|
||
ok = TPrassiServer::DongleLogin() != 0xFFFF;
|
||
if (ok)
|
||
{
|
||
pServer = new TPrassiServer;
|
||
if (!pServer->IsOk())
|
||
{
|
||
StopServer();
|
||
AfxMessageBox("ODBC initialization failure.",
|
||
MB_OK | MB_ICONERROR);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AfxMessageBox("Error connecting to the dongle.",
|
||
MB_OK | MB_ICONERROR);
|
||
}
|
||
ok = pServer != NULL;
|
||
}
|
||
return ok;
|
||
}
|
||
|
||
TPrassiServer& GetServer()
|
||
{
|
||
if (pServer == NULL);
|
||
StartServer();
|
||
ASSERT(pServer);
|
||
return *pServer;
|
||
}
|
||
|
||
|