Commit 1cfc4e40 authored by captainwong's avatar captainwong

Initial commit

parents
// FileOper.h: interface for the CFileOper class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_FILEOPER_H__25D4B209_DAC6_4D93_98B2_692621E5508F__INCLUDED_)
#define AFX_FILEOPER_H__25D4B209_DAC6_4D93_98B2_692621E5508F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <stdio.h>
#include <Shlobj.h>
static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData)
{
switch(uMsg)
{
case BFFM_INITIALIZED: //初始化消息
::SendMessage(hwnd,BFFM_SETSELECTION,TRUE,lpData);
break;
case BFFM_SELCHANGED: //选择路径变化,
{
TCHAR curr[MAX_PATH];
SHGetPathFromIDList((LPCITEMIDLIST)lParam,curr);
::SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)curr);
}
break;
default:
break;
}
return 0;
}
class CFileOper
{
public:
// 弹出文件夹选择对话框,并返回选定的文件夹路径
static BOOL BrowseGetPath(CString &path, HWND hWnd){
TCHAR szPath[MAX_PATH] = {0};
BROWSEINFO bi;
memset(&bi, 0, sizeof(bi));
bi.hwndOwner = hWnd;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
bi.lpszTitle = _T("select folder");
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LONG)(LPCTSTR)path;
LPITEMIDLIST idl = SHBrowseForFolder(&bi);
if(idl == NULL)
return FALSE;
SHGetPathFromIDList(idl, szPath);
path.Format(_T("%s"), szPath);
return TRUE;
}
// 删除该路径下所有文件与文件夹
static BOOL DeleteFolderAndAllSubFolder_Files(LPCTSTR folderPath){
system("echo delete old folder and sub files...\n");
//CString cmd = _T("");
char cmd[64] = {0};
#if defined(_UNICODE) || defined(UNICODE)
char *path = Utf16ToAnsi(folderPath);
sprintf_s(cmd, "rd /s /q \"%s\"", path);
SAFEDELETEARR(path);
#else
sprintf_s(cmd, "rd /s /q \"%s\"", folderPath);
#endif
BOOL ret = system(cmd) == 0;
sprintf_s(cmd, "echo delete %s\n", ret ? "success" : "failed");
system(cmd);
return ret;
}
// 获取文件大小
static long GetFileSize(const CString& path){
FILE *p = NULL;
#if defined(_UNICODE) || defined(UNICODE)
char *cpath = Utf16ToAnsi(path);
fopen_s(&p, cpath, "r");
if(p == NULL)
{
SAFEDELETEARR(cpath);
return 0;
}
SAFEDELETEARR(cpath);
#else
fopen_s(&p, cpath, "r");
if(p == NULL)
return 0;
#endif
fseek(p, 0, SEEK_END);
long len = ftell(p);
fclose(p);
return len;
}
// 获取路径中最右的文件夹名称,如D:\A\B,返回B
static CString GetCurFolderFromFullPath(CString strpath){
int pos = -1;
pos = strpath.ReverseFind(_T('\\'));
if(pos != -1)
return strpath.Right(strpath.GetLength() - pos - 1);
return _T("");
}
// 复制文件夹到目的,并弹出文件夹复制对话框
static BOOL CopyFolder(CString dstFolder, CString srcFolder, HWND hWnd){
TCHAR szDst[MAX_PATH], szSrc[MAX_PATH];
ZeroMemory(szDst, MAX_PATH);
ZeroMemory(szSrc, MAX_PATH);
lstrcpy(szDst, dstFolder);
lstrcpy(szSrc, srcFolder);
SHFILEOPSTRUCT lpFile;
lpFile.hwnd = hWnd;
lpFile.wFunc = FO_COPY;
lpFile.pFrom = szSrc;
lpFile.pTo = szDst;
lpFile.fFlags = FOF_ALLOWUNDO;
lpFile.fAnyOperationsAborted = FALSE;
lpFile.hNameMappings = NULL;
lpFile.lpszProgressTitle = NULL;
int ret = SHFileOperation(&lpFile);
return ret == 0 && lpFile.fAnyOperationsAborted == FALSE;
}
// 判断指定路径是否存在(文件夹)
static BOOL IsFolderExists(LPCTSTR lpszFolderPath){
return FindFirstFileExists(lpszFolderPath, FILE_ATTRIBUTE_DIRECTORY);
}
// 判断指定路径是否存在(文件或文件夹)
static BOOL PathExists(LPCTSTR lpszPath){
return FindFirstFileExists(lpszPath, FALSE);
}
// 更改文件名后缀名
static CString ChangeFileExt(const CString& srcFilePath, const CString& dstExt){
CString dstFilePath = srcFilePath;
int pos = srcFilePath.ReverseFind(_T('.'));
if(pos != -1){
dstFilePath = srcFilePath.Left(pos + 1);
dstFilePath += dstExt;
}
return dstFilePath;
}
// 从文件路径中获取文件标题,如D:\AAA.TXT或者AAA.TXT,返回AAA
static CString GetFileTitle(const CString& fileName){
CString title = _T("");
int pos = -1;
pos = fileName.FindOneOf(_T("\\"));
CString name = fileName;
if(pos > 0)
name = GetFileNameFromPathName(fileName);
pos = name.ReverseFind(_T('.'));
if(pos != -1)
title = name.Left(pos);
return title;
}
// 从文件路径中获取文件后缀名,如D:\AAA.TXT,返回TXT
static CString GetFileExt(const CString& filePath){
CString ext = _T("");
int pos = filePath.ReverseFind(_T('.'));
if(pos != -1){
ext = filePath.Right(filePath.GetLength() - pos - 1);
ext.MakeUpper();
}
return ext;
}
// 从文件路径中获取文件名,如D:\AAA.TXT,返回AAA.TXT
static CString GetFileNameFromPathName(const CString& filePathName){
CString fileName = _T("");
int pos = filePathName.ReverseFind(_T('\\'));
if(pos != -1) {
fileName = filePathName.Right(filePathName.GetLength() - pos - 1);
}
return fileName;
}
// 寻找目录(该路径不能以 \ 结尾)下所有指定后缀名(不支持*.*)的文件,并将结果保存在CStringList中,
static int FindFilesInFolder(LPCTSTR lpszFolder, LPCTSTR lpszFilter, CStringList &dstList){
if(lstrlen(lpszFolder) == 0)
return 0;
int nNums = 0;
CFileFind filefind;
CString path, filter;
filter.Format(_T("%s\\%s"), lpszFolder, lpszFilter);
BOOL bFound = filefind.FindFile(filter);
while(bFound){
bFound = filefind.FindNextFile();
if(filefind.IsDots())
continue;
if(!filefind.IsDirectory()){
path = filefind.GetFilePath();
dstList.AddTail(path);
nNums++;
}
}
filefind.Close();
return nNums;
}
// 将目录下符合 *n.* 的文件(n代表数字)重命名为 *00n.*的形式,将n格式化为3位数
static BOOL RenameFile03d(LPCTSTR lpszFilePath){
CString fileName = lpszFilePath;
CString tittle, num, ext, newFileName;
int pos;
pos = fileName.ReverseFind(_T('.'));
if(pos == -1) return FALSE;
tittle = fileName.Left(pos);
ext = fileName.Right(fileName.GetLength() - pos);
int i = 0, posNum = 0, numnums = 0;
TCHAR ch;
bool bNumCharFound = false;
for(i = tittle.GetLength()-1, posNum = i+1; i > 0; i--){
ch = tittle.GetAt(i);
if(ch > _T('0') && ch < _T('9')){
if(posNum != i+1)
break;
posNum = i;
numnums++;
bNumCharFound = true;
}
}
if(!bNumCharFound) return FALSE;
num = tittle.Right(numnums);
tittle = tittle.Left(posNum);
int nums = _ttoi(num.GetBuffer(num.GetLength()));
num.ReleaseBuffer();
num.Format(_T("%03d"), nums);
tittle += num;
newFileName.Format(_T("%s%s"), tittle, ext);
LPCTSTR szOldName = fileName.GetBuffer(fileName.GetLength());
fileName.ReleaseBuffer();
LPCTSTR szNewName = newFileName.GetBuffer(newFileName.GetLength());
newFileName.ReleaseBuffer();
MoveFile(szOldName, szNewName);
TRACE(_T("movefile %s to %s \n"), fileName, newFileName);
return TRUE;
}
// 删除所有该目录路径下指定后缀名的文件
static DWORD DeleteFilesInFolder(LPCTSTR lpszFolder, LPCTSTR lpszFilter){
DWORD dwDeletedFileNums = 0;
CFileFind find;
BOOL bfoundone = FALSE;
CString filter = _T("");
filter.Format(_T("%s\\%s"), lpszFolder, lpszFilter);
BOOL bFound = find.FindFile(filter);
TRACE(_T("CFileOper::DeleteFilesInFolder find path %s\n"), filter);
CString filepath = _T("");
while(bFound){
bFound = find.FindNextFile();
if(find.IsDots())
continue;
if(!find.IsDirectory()){
filepath = find.GetFilePath();
if(DeleteFile(filepath.GetBuffer(filepath.GetLength()))){
CString trace;trace.Format(_T("CFileOper::DeleteFilesInFolder delete file %s \n"),
find.GetFilePath());
TRACE(trace);
bfoundone = TRUE;
dwDeletedFileNums++;
}
}
}
if(!bfoundone)
TRACE(_T("CFileOper::DeleteFilesInFolder no file deleted\n"));
find.Close();
return dwDeletedFileNums;
}
private:
CFileOper();
virtual ~CFileOper();
protected:
static BOOL FindFirstFileExists(LPCTSTR lspzPath, DWORD dwFilter){
TCHAR realPath[MAX_PATH] = {0};
if(_tcsclen(lspzPath) > MAX_PATH)
GetShortPathName(lspzPath, realPath, MAX_PATH);
else
_tcscpy_s(realPath, lspzPath);
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile(realPath, &fd);
BOOL bFilter = (FALSE == dwFilter) ? TRUE : fd.dwFileAttributes & dwFilter;
BOOL RetValue = ((hFind != INVALID_HANDLE_VALUE) && bFilter) ? TRUE : FALSE;
FindClose(hFind);
return RetValue;
}
};
#endif // !defined(AFX_FILEOPER_H__25D4B209_DAC6_4D93_98B2_692621E5508F__INCLUDED_)
#ifndef ___IOCP_H_INCLUDED___
#define ___IOCP_H_INCLUDED___
#include <windows.h>
#include "socket_client.h"
///////////////////////////////////////////////////////////////////////////////////////////////
// A template class (wrapper) to handle basic operations
// related to IO Completion Ports (IOCP).
template<class T>
class IOCPSimple {
private:
HANDLE m_hIocp;
DWORD m_dwTimeout;
protected:
// Useful method to queue a socket (handler in fact) in the IOCP's
// queue, without requesting any 'physical' I/O operations.
BOOL PostQueuedCompletionStatus( ClientSocket<T> *sock, MYOVERLAPPED *pMov ) {
return ::PostQueuedCompletionStatus( this->m_hIocp, 1,
(DWORD) sock, (OVERLAPPED*) pMov );
};
public:
~IOCPSimple() {
// Destroy the IOCP handler
::CloseHandle( this->m_hIocp );
};
IOCPSimple( DWORD dwTimeout = 0 ) {
// This will be used as a timeout value to wait
// for completion events from IOCP (in milliseconds).
// See 'GetQueuedCompletionStatus(...)' for more
// details. If is set to 0 == means infinite.
m_dwTimeout = dwTimeout;
// Create the IOCP handler
this->m_hIocp = ::CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );
if ( this->m_hIocp == NULL ) {
throw "CreateIoCompletionPort() failed to create IO Completion port.";
}
};
// Method associates Socket handler with the handler
// of the IOCP. It is important to do this association
// first of all, once Socket is accepted and is valid.
// In fact, this method registers socket with the IOCP,
// so IOCP will monitor every I/O operation happening
// with the socket.
void AssociateSocket( ClientSocket<T> *sock ) {
if ( sock != NULL ) {
// As a key of this association, the pointer to the
// "sock" is set. The pointer to the "sock" will be
// returned with "GetQueuedCompletionStatus" call
// whenever a I/O operation completes, so we always know
// which is the relevant socket.
::CreateIoCompletionPort( (HANDLE) sock->GetSocket(),
this->m_hIocp, (DWORD) sock, 0 );
}
};
// Queue in the IOCP's queue with the status 'pending'.
// This is a fictive status (not real). Method is useful in
// cases when packets/data buffers to send via socket are yet
// not ready (e.g. due some internal, time consuming, calculations)
// and rather than keeping socket somewhere, in separate structures,
// place it in the queue with status pending. It will be returned
// shortly with the "GetQueuedCompletionStatus" call anyway.
BOOL SetPendingMode( ClientSocket<T> *sock ) {
BOOL ret = false;
if ( sock != NULL ) {
// Get a free instance of the structure
// from the pool.
MYOVERLAPPED *mov = Overlapped::Get();
if ( mov != NULL ) {
mov->OperationType = _PENDING;
mov->nSession = sock->GetSession();
ret = PostQueuedCompletionStatus( sock, mov );
if ( !ret ) Overlapped::Release( mov );
}
}
return ret;
};
// Queue in the IOCP's queue with the status 'close'.
// This is a fictive status (not real). Useful only
// when close socket cases should be treated/handled
// within the routine, handling I/O completion statuses,
// and just to not spread the code across different
// modules/classes.
BOOL SetCloseMode( ClientSocket<T> *sock, MYOVERLAPPED *pMov = NULL ) {
BOOL ret = false;
if ( sock != NULL ) {
// Get a free instance of the structure
// from the pool.
MYOVERLAPPED *mov = Overlapped::Get();
if ( mov != NULL ) {
mov->OperationType = _CLOSE;
mov->nSession = sock->GetSession();
if (pMov != NULL) {
mov->DataBuf.buf = pMov->DataBuf.buf;
mov->DataBuf.len = pMov->DataBuf.len;
}
ret = PostQueuedCompletionStatus( sock, mov );
if ( !ret ) Overlapped::Release( mov );
}
}
return ret;
};
// Queue in the IOCP's queue with the status 'accept'.
// This is a fictive status (not real). When a new
// client connection is accepted by the server socket,
// method is used to acknowledge the routine, handling
// I/O completion statuses, of this. Same reason as per
// 'close' status, just to have a unique piece of code
// handling all the possible statuses.
BOOL SetAcceptMode( ClientSocket<T> *sock ) {
BOOL ret = false;
if ( sock != NULL ) {
// Get a free instance of the structure
// from the pool.
MYOVERLAPPED *mov = Overlapped::Get();
if ( mov != NULL ) {
mov->OperationType = _ACCEPT;
mov->nSession = sock->GetSession();
ret = PostQueuedCompletionStatus( sock, mov );
if ( !ret ) Overlapped::Release( mov );
}
}
return ret;
};
// This method is just a wrapper. For more details,
// see MSDN for "GetQueuedCompletionStatus". The only
// difference is, method is adjusted to handle
// 'ClientSocket<T>' (as registered with 'AssociateSocket(...)')
// rather than pointer to the 'DWORD'.
BOOL GetQueuedCompletionStatus( LPDWORD pdwBytesTransferred,
ClientSocket<T> **sock, MYOVERLAPPED **lpOverlapped ) {
return ::GetQueuedCompletionStatus( this->m_hIocp, pdwBytesTransferred,
(LPDWORD) sock, (LPOVERLAPPED*) lpOverlapped, m_dwTimeout );
};
};
#endif
\ No newline at end of file
#ifndef ___LOG_H_INCLUDED___
#define ___LOG_H_INCLUDED___
#include "INCLUDE/sync_simple.h"
#include "D:/global/log.h"
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A simple class to log messages to the console
// considering multithreading
class Log {
private:
static QMutex m_qMutex;
//static wchar_t m_buff[1024];
public:
static void LogMessage(const char* str);
static void LogMessage(const wchar_t *str);
static void LogMessage(const wchar_t *str, long num);
static void LogMessage(const wchar_t *str, long num1, long num2);
static void LogMessage(const wchar_t *str, long num, const wchar_t *str1);
static void LogMessage(const wchar_t *str, const wchar_t *str1);
static void LogMessage(const wchar_t *str, const wchar_t *str1, const wchar_t *str2);
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- IMPLEMENTATION -------------------------------------------------
QMutex Log::m_qMutex;
void Log::LogMessage(const wchar_t *str, long num1, long num2) {
m_qMutex.Lock();
//wsprintf(m_buff, str, num1, num2);
CLog::WriteLogW(str, num1, num2);
m_qMutex.Unlock();
}
void Log::LogMessage(const wchar_t *str, long num, const wchar_t *str1) {
m_qMutex.Lock();
CLog::WriteLogW(str, num, str1);
m_qMutex.Unlock();
}
void Log::LogMessage(const wchar_t *str, const wchar_t *str1, const wchar_t *str2) {
m_qMutex.Lock();
CLog::WriteLogW(str, str1, str2);
m_qMutex.Unlock();
}
void Log::LogMessage(const wchar_t *str, const wchar_t *str1) {
m_qMutex.Lock();
CLog::WriteLogW(str, str1);
m_qMutex.Unlock();
}
void Log::LogMessage(const wchar_t *str, long num) {
m_qMutex.Lock();
CLog::WriteLogW(str, num);
m_qMutex.Unlock();
}
void Log::LogMessage(const wchar_t *str) {
m_qMutex.Lock();
CLog::WriteLogW(str);
m_qMutex.Unlock();
}
void Log::LogMessage(const char *str) {
m_qMutex.Lock();
CLog::WriteLogA(str);
m_qMutex.Unlock();
}
#endif
\ No newline at end of file
#ifndef ___MEM_MANAGER_H_INCLUDED___
#define ___MEM_MANAGER_H_INCLUDED___
#pragma warning(disable:4786)
#include <vector>
#include <queue>
#include <set>
#include "sync_simple.h"
using namespace std;
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A priority queue that allows pre-setting capacity of the container.
template<class T>
class mpriority_queue : public priority_queue<T, vector<T>, less<typename vector<T>::value_type> > {
public:
void reserve(size_type _N) { c.reserve(_N); };
size_type capacity() const { return c.capacity(); };
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A template class allowing re-using of the memory blocks.
template<class T>
class QueuedBlocks {
private:
QMutex m_qMutex;
set< T* > m_quBlocks;
vector< T* > m_allBlocks;
public:
QueuedBlocks(int nInitSize = 1): m_qMutex(), m_quBlocks(), m_allBlocks() {
int i;
int nSize = (nInitSize <= 0)?1:nInitSize;
// allocate and push the blocks to the queue
for (i = 0; i < nSize; i++) {
T *t = new T();
if (t != NULL) {
t->Clear();
m_quBlocks.insert( t );
m_allBlocks.push_back( t );
}
}
};
// get a free block from the queue, if one cannot be found
// then NULL is returned
T* GetFromQueue() {
T* t = NULL;
m_qMutex.Lock();
if (!m_quBlocks.empty()) {
set<T*>::iterator itPos = m_quBlocks.begin();
t = *itPos;
m_quBlocks.erase( t );
}
m_qMutex.Unlock();
return t;
};
// get a free block from the queue, if one cannot be found
// then a new one is created
T* Get() {
T* t = GetFromQueue();
if (t == NULL) {
t = new T();
if (t != NULL) {
t->Clear();
m_qMutex.Lock();
m_allBlocks.push_back( t );
m_qMutex.Unlock();
}
}
return t;
};
// Release the used block, place it
// back to the queue. For performance reason,
// we assume that the block was previously taken
// from the queue.
void Release(T* t) {
if (t != NULL) {
t->Clear();
m_qMutex.Lock();
m_quBlocks.insert( t );
m_qMutex.Unlock();
}
};
// Return all the blocks ever allocated.
vector<T*> *GetBlocks() { return &m_allBlocks; };
~QueuedBlocks() {
m_qMutex.Lock();
m_quBlocks.clear();
vector<T*>::iterator itPos = m_allBlocks.begin();
for (; itPos < m_allBlocks.end(); itPos++) {
T* t = *itPos;
t->Clear();
delete t;
}
m_allBlocks.clear();
m_qMutex.Unlock();
};
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A template class used for providing free blocks as well
// as releasing unnecessary ones. Uses "QueuedBlocks" which
// allows reusing of the blocks.
template<class T>
class StaticBlocks {
private:
static QueuedBlocks<T> *blocks;
public:
static void Init(int nSize = 1) {
if (blocks == NULL) blocks = new QueuedBlocks<T>(nSize);
};
static void Clear() {
if (blocks != NULL) {
delete blocks; blocks = NULL;
}
}
static T *Get() {
if (blocks == NULL) return NULL;
return blocks->Get();
};
static void Release(T *b) {
if (blocks != NULL) blocks->Release(b);
};
};
#endif
#ifndef ___SERVER_SERVICE_H_INCLUDED___
#define ___SERVER_SERVICE_H_INCLUDED___
//#include "INCLUDE/log.h"
//#include "D:/global/log.h"
#include "threading.h"
#include "socket_server.h"
#include "iocp.h"
#include <vector>
using namespace std;
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A template class designed for verifying client sockets
// against time-out cases. Time-out case = if no I/O actions
// happen with a socket during a configured number of
// seconds. This implies that the attachment of the
// client socket must implement "GetTimeElapsed()" and "ResetTime(...)"
// methods.
template<class T>
class CTimeOutChecker: public IRunnable {
private:
unsigned int m_nTimeOutValue;
vector<ClientSocket<T> *> *m_arrSocketPool;
IOCPSimple<T> *m_hIocp;
protected:
// method checks sockets to detect time-out cases
virtual void run() {
ClientSocket<T>* pSocket = NULL;
unsigned int i = 0;
long lngTimeElapsed;
OutputDebugString( L"Time out checker service started.\n" );
vector<ClientSocket<T> *>::iterator itPos = m_arrSocketPool->begin();
while ( !CThread::currentThread().isInterrupted() ) {
if ( itPos >= m_arrSocketPool->end() ) {
itPos = m_arrSocketPool->begin();
i = 0;
}
pSocket = *itPos;
pSocket->Lock();
// check if client socket object is
// assigned a socket handler.
if ( pSocket->IsBusy() ) {
// call "GetTimeElapsed()" of the socket's
// attachment.
lngTimeElapsed = pSocket->GetAttachment()->GetTimeElapsed();
// check elapsed time, since last registered action,
// with the configured time-out.
if ( lngTimeElapsed > static_cast<long>(this->m_nTimeOutValue)) {
// clear the time and push the socket to
// the IOCP with status _CLOSE. Socket
// will be closed shortly.
pSocket->GetAttachment()->ResetTime( true );
m_hIocp->SetCloseMode( pSocket );
}
}
pSocket->UnLock();
// sleep 2 milliseconds after every 2 iterations
if ( ( i % 2 ) == 0 ) ::Sleep( 1 );
itPos++;
i++;
}
OutputDebugString( L"Time out checker service stopped.\n" );
};
public:
// Constructor of the template class. Requires:
// - a pointer to a collection of pointers to client sockets.
// This is the collection of sockets to be checked against
// time-out cases.
// - a pointer to the IO completion port object responsible
// for checking I/O events against passed collection of
// client sockets.
// - value of the time-out, in seconds.
CTimeOutChecker( vector<ClientSocket<T> *> *arrSocketPool,
IOCPSimple<T> *hIocp, unsigned int nTimeOutValue ) {
m_arrSocketPool = arrSocketPool;
m_hIocp = hIocp;
// If time-out is less than 5 seconds, drop it to 5 seconds.
// It is expected that configured time-out value to be >= 5!
if ( nTimeOutValue > 5 ) this->m_nTimeOutValue = nTimeOutValue;
else this->m_nTimeOutValue = 5;
};
// Nothing to destruct as inside the template class
// we keep/use just pointers obtained from external
// sources.
~CTimeOutChecker() {};
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A template interface showing how the client socket event
// handler should look like.
template<class T>
class ISockEvent {
public:
// Client socket ("pSocket") is about to be closed.
virtual void OnClose( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
// Client socket ("pSocket") was just accepted by
// the server socket (new connection with a client
// is created).
virtual void OnAccept( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
// Client socket ("pSocket") was returned from the IOCP
// queue with status _PENDING. For more details see
// "IOCPSimple<T>::SetPendingMode(...)". This method
// is invoked only if there was a call to
// "IOCPSimple<T>::SetPendingMode(...)", performed by a
// user code, internally "SetPendingMode(...)"
// is never called.
virtual void OnPending( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
// Client socket ("pSocket") was returned from IOCP
// queue with status _READ. This means that overlapped
// reading operation, started previously with
// "ClientSocket<T>::ReadFromSocket(...)", was
// successfully finished.
// - "pOverlap->DataBuf" structure points to the data
// buffer and buffer's size that were passed to the
// "ClientSocket<T>::ReadFromSocket(...)".
// - "dwBytesTransferred" will indicate how many
// bytes were read.
virtual void OnReadFinalized( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
DWORD dwBytesTransferred,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
// Client socket ("pSocket") was returned from IOCP
// queue with status _WRITE. This means that overlapped
// writting operation, started previously with
// "ClientSocket<T>::WriteToSocket(...)", was
// successfully finished.
// - "pOverlap->DataBuf" structure points to the data
// buffer and buffer's size that were passed to the
// "ClientSocket<T>::WriteToSocket(...)".
// - "dwBytesTransferred" will indicate how many
// bytes were written.
virtual void OnWriteFinalized( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
DWORD dwBytesTransferred,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Class is used internally by "ServerService<T>" as a
// Task entity to be submitted to "ServerService<T>::m_ThPool".
template<class T>
class CSockEventTask: public IRunnable {
private:
MYOVERLAPPED *m_pOverlap;
ClientSocket<T> *m_pSocket;
ISockEvent<T> *m_pSEvent;
ServerSocket<T> *m_pServerSocket;
IOCPSimple<T> *m_pHIocp;
DWORD m_dwBytesTransferred;
QueuedBlocks<CSockEventTask<T> > *m_pSockEventTaskPool;
protected:
virtual void run() {
if ( m_pSEvent == NULL ) return;
m_pSocket->Lock();
// check if client socket object is assigned a socket
// handler and session assigned to operation is equal
// to the session of the socket.
if ( m_pSocket->IsBusy() && ( m_pOverlap->nSession == m_pSocket->GetSession() ) ) {
switch ( m_pOverlap->OperationType ) {
// invoke the relevant event handler
case _CLOSE:
{
struct sockaddr_in addr;
m_pSocket->GetAddrIn(addr);
char out[128] = {0};
sprintf_s(out, "CloseSocket request %d. IP:%s\n",
m_pSocket->GetSocket(), inet_ntoa(addr.sin_addr));
OutputDebugStringA(out);
m_pSEvent->OnClose( m_pSocket, m_pOverlap, m_pServerSocket, m_pHIocp );
// Make sure (double check) the socket
// is closed.
m_pServerSocket->Release( m_pSocket );
break;
}
case _ACCEPT:
m_pSEvent->OnAccept( m_pSocket, m_pOverlap, m_pServerSocket, m_pHIocp );
break;
case _READ:
m_pSEvent->OnReadFinalized( m_pSocket, m_pOverlap, m_dwBytesTransferred,
m_pServerSocket, m_pHIocp );
break;
case _PENDING:
m_pSEvent->OnPending( m_pSocket, m_pOverlap, m_pServerSocket, m_pHIocp );
break;
case _WRITE:
m_pSEvent->OnWriteFinalized( m_pSocket, m_pOverlap, m_dwBytesTransferred,
m_pServerSocket, m_pHIocp );
break;
default:
OutputDebugString( L"Oops...\n" );
}
}
m_pSocket->UnLock();
// Place the structure back to the pool.
Overlapped::Release( m_pOverlap );
m_pSockEventTaskPool->Release( this );
};
public:
CSockEventTask() { Clear(); };
// Set everything required to the Task to be
// able to invoke the relevant event handler.
void Set( QueuedBlocks<CSockEventTask<T> > *pSockEventTaskPool,
ISockEvent<T> *pSEvent,
ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
DWORD dwBytesTransferred,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) {
m_pSockEventTaskPool = pSockEventTaskPool;
m_pSEvent = pSEvent;
m_pOverlap = pOverlap;
m_dwBytesTransferred = dwBytesTransferred;
m_pSocket = pSocket;
m_pServerSocket = pServerSocket;
m_pHIocp = pHIocp;
};
// Method is used by 'QueuedBlocks' from
// "mem_manager.h".
void Clear() {
m_dwBytesTransferred = 0;
m_pSockEventTaskPool = NULL;
m_pSEvent = NULL;
m_pOverlap = NULL;
m_pSocket = NULL;
m_pServerSocket = NULL;
m_pHIocp = NULL;
};
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Well, finally, here is the template class joining all
// the stuff together. Considering the Aspect Oriented paradigm,
// this template class may be seen as an Aspect. The "individualizations"
// of this aspect are "ISockEvent<T>" and "T" itself. "T" is nothing
// else but attachment of the client socket (see ClientSocket<T> template
// class for more details). Implementing "ISockEvent<T>" and "T" will
// define the individual behaviour of this aspect.
// It is a composition of the IOCP, server socket, time-out checker
// and thread pool. Class implements the business logic that makes all
// these entities working together.
template<class T>
class ServerService: public IRunnable {
private:
ServerSocket<T> m_ServerSocket;
IOCPSimple<T> m_hIocp;
ISockEvent<T> *m_pSEvent;
CTimeOutChecker<T> *m_TChecker;
// thread pool which will execute the tasks
CSimpleThreadPool *m_ThPool;
// a pool of the CSockEventTask<T> objects
QueuedBlocks<CSockEventTask<T> > m_SockEventTaskPool;
protected:
// This method will be executed by a thread
// of the task pool.
virtual void run() {
int nRet;
DWORD dwBytesTransferred;
MYOVERLAPPED *pOverlap = NULL;
ClientSocket<T> *pSocket = NULL;
OutputDebugString( L"Socket service thread started.\n" );
while ( !CThread::currentThread().isInterrupted() ) {
::Sleep( 1 );
// check for an incoming connection
pSocket = m_ServerSocket.Accept();
if ( pSocket != NULL ) {
struct sockaddr_in addr;
pSocket->GetAddrIn(addr);
wchar_t tmp[64] = { 0 };
AnsiToUtf16Array(inet_ntoa(addr.sin_addr), tmp, 64);
TRACE( L"Socket created at %d. IP:%s\n",
pSocket->GetSocket(), tmp);
//delete tmp;
// associate socket with IOCP
m_hIocp.AssociateSocket( pSocket );
pSocket->GetAttachment()->ResetTime( false );
// push the socket to the IOCP queue with
// status _ACCEPT
if ( !( m_hIocp.SetAcceptMode( pSocket ) ) ) {
m_ServerSocket.Release( pSocket );
}
}
dwBytesTransferred = 0;
pSocket = NULL;
// check the IOCP queue for:
// - a completed I/O operation against a socket or
// - a queued socket with a fake status like _ACCEPT,
// _CLOSE or _PENDING
//
// When an overlapped I/O completes, an I/O completion packet
// arrives at the IOCP and GetQueuedCompletionStatus returns.
nRet = m_hIocp.GetQueuedCompletionStatus( &dwBytesTransferred,
&pSocket, &pOverlap );
// something is wrong
if ( nRet == 0 ) {
if ( ::GetLastError() == WAIT_TIMEOUT ) continue;
if ( pOverlap != NULL ) dwBytesTransferred = 0;
else continue;
}
if ( pSocket == NULL ) {
Overlapped::Release( pOverlap );
continue;
}
// not good if zero bytes were transferred
if ( dwBytesTransferred == 0 ) {
// clear the time and push the socket to
// the IOCP with status _CLOSE. Socket
// will be closed shortly.
if ( pSocket->IsBusy() && ( pOverlap->nSession == pSocket->GetSession() ) ) {
pSocket->GetAttachment()->ResetTime( true );
if ( !( m_hIocp.SetCloseMode( pSocket, pOverlap ) ) ) {
m_ServerSocket.Release( pSocket );
}
}
Overlapped::Release( pOverlap );
}
else {
// obtain a free instance of the event task
// which will be submitted to the threat pool
CSockEventTask<T> *pCSockEv = m_SockEventTaskPool.Get();
if ( pCSockEv != NULL ) {
pCSockEv->Set( &m_SockEventTaskPool, m_pSEvent,
pSocket, pOverlap, dwBytesTransferred,
&m_ServerSocket, &m_hIocp
);
// _CLOSE operation type will have a lower
// priority that other operation types.
int priority = ( pOverlap->OperationType == _CLOSE )?1:2;
// submit the task to the thread pool
m_ThPool->submit( pCSockEv, priority );
}
else {
// no free resources, so close the socket
m_ServerSocket.Release( pSocket );
Overlapped::Release( pOverlap );
}
}
}
OutputDebugString( L"Socket service thread stopped.\n" );
};
public:
// Constructor or the class.
// pSEvent - pointer to an instance implementing
// ISockEvent<T>. This instance will be used
// as a client socket event handler.
// nPort - port number to bind server socket to.
// nMaxClients - the maximum number of accepted (concurrent)
// client connections. To be passed to
// the server socket and also will be used
// as the initial size for the pool of the
// CSockEventTask<T> objects.
// nNoThreads - indicative (the real one is computed,
// see below) number of the threads
// to be created by the thread pool.
// timeout - the value of the time-out, in seconds.
// Will be passed to the time-out checker.
// If time-out is zero, time-out checker
// will not be created.
// blnBindLocal - see ServerSocket<T> for more details.
// If "true" then server socket is bind
// to localhost ("127.0.0.1").
// If "false" then server socket is bind
// to INADDR_ANY ("0.0.0.0").
ServerService( ISockEvent<T> *pSEvent, unsigned int nPort, unsigned int nMaxClients,
unsigned int nNoThreads, unsigned int timeout,
bool blnBindLocal = true ):
m_ServerSocket( nPort, nMaxClients, true, blnBindLocal ),
m_hIocp( 200 ), m_SockEventTaskPool( nMaxClients ) {
unsigned int i = 0;
SYSTEM_INFO si;
OutputDebugString( L"Creating \"socket_listener\" ... " );
if ( nMaxClients < 1 ) {
throw "illegal value for \"max_connections\" supplied (should be > 0).";
}
if ( nNoThreads < 1 ) {
throw "illegal value for \"working_threads\" supplied (should be > 0).";
}
if ( pSEvent == NULL ) {
throw "NULL pointer set for socket event handler.";
}
m_pSEvent = pSEvent;
// Query system info in order to check
// how many CPU's are there installed in
// the system.
::GetSystemInfo( &si );
// Compute the real number of the threads
// to be created by the thread pool.
i = nNoThreads + si.dwNumberOfProcessors;
// If configured time-out is zero, then don't
// create time-out checker object. Client sockets
// will not be checked against time-out cases.
if ( timeout > 0 ) {
m_TChecker = new CTimeOutChecker<T>( m_ServerSocket.GetPool(), &m_hIocp, timeout );
// Increase the number of the threads,
// +1 for the time-out checker.
i++;
} else m_TChecker = NULL;
// Create the thread pool which will handle
// the tasks. "nMaxClients" is used as the
// initial capacity of the priority queue
// of (associated with) the thread pool.
m_ThPool = new CSimpleThreadPool( i, 2 * nMaxClients );
// Submit this instance (as a task) to the
// thread pool, as many times as CPU's are installed
// in the system. The "run()" method will be executed
// by pool's threads.
for (i = 0; i < si.dwNumberOfProcessors; i++) m_ThPool->submit( this, 1 );
// If time-out checker was created, then
// submit it to the thread pool (as a task).
if ( m_TChecker != NULL ) m_ThPool->submit( m_TChecker );
OutputDebugString( L"DONE\n" );
};
virtual ~ServerService() {
// Stop all the pool's threads.
m_ThPool->shutdown();
// If time-out checker was created, then
// delete it.
if (m_TChecker != NULL) delete m_TChecker;
// Delete the thread pool.
delete m_ThPool;
OutputDebugString( L"Killing \"socket_listener\" ... DONE\n" );
};
// Start the threat pool (== all the
// threads in the pool).
void start() { m_ThPool->startAll(); };
};
#endif
#ifndef ___SERVER_SERVICE_H_INCLUDED___
#define ___SERVER_SERVICE_H_INCLUDED___
//#include "INCLUDE/log.h"
#include "D:/global/log.h"
#include "threading.h"
#include "socket_server.h"
#include "iocp.h"
#include <vector>
using namespace std;
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A template class designed for verifying client sockets
// against time-out cases. Time-out case = if no I/O actions
// happen with a socket during a configured number of
// seconds. This implies that the attachment of the
// client socket must implement "GetTimeElapsed()" and "ResetTime(...)"
// methods.
template<class T>
class CTimeOutChecker: public IRunnable {
private:
unsigned int m_nTimeOutValue;
vector<ClientSocket<T> *> *m_arrSocketPool;
IOCPSimple<T> *m_hIocp;
protected:
// method checks sockets to detect time-out cases
virtual void run() {
ClientSocket<T>* pSocket = NULL;
unsigned int i = 0;
long lngTimeElapsed;
CLog::WriteLog( L"Time out checker service started.\n" );
vector<ClientSocket<T> *>::iterator itPos = m_arrSocketPool->begin();
while ( !CThread::currentThread().isInterrupted() ) {
if ( itPos >= m_arrSocketPool->end() ) {
itPos = m_arrSocketPool->begin();
i = 0;
}
pSocket = *itPos;
pSocket->Lock();
// check if client socket object is
// assigned a socket handler.
if ( pSocket->IsBusy() ) {
// call "GetTimeElapsed()" of the socket's
// attachment.
lngTimeElapsed = pSocket->GetAttachment()->GetTimeElapsed();
// check elapsed time, since last registered action,
// with the configured time-out.
if ( lngTimeElapsed > this->m_nTimeOutValue ) {
// clear the time and push the socket to
// the IOCP with status _CLOSE. Socket
// will be closed shortly.
pSocket->GetAttachment()->ResetTime( true );
m_hIocp->SetCloseMode( pSocket );
}
}
pSocket->UnLock();
// sleep 2 milliseconds after every 2 iterations
if ( ( i % 2 ) == 0 ) ::Sleep( 2 );
itPos++;
i++;
}
CLog::WriteLog( L"Time out checker service stopped.\n" );
};
public:
// Constructor of the template class. Requires:
// - a pointer to a collection of pointers to client sockets.
// This is the collection of sockets to be checked against
// time-out cases.
// - a pointer to the IO completion port object responsible
// for checking I/O events against passed collection of
// client sockets.
// - value of the time-out, in seconds.
CTimeOutChecker( vector<ClientSocket<T> *> *arrSocketPool,
IOCPSimple<T> *hIocp, unsigned int nTimeOutValue ) {
m_arrSocketPool = arrSocketPool;
m_hIocp = hIocp;
// If time-out is less than 5 seconds, drop it to 5 seconds.
// It is expected that configured time-out value to be >= 5!
if ( nTimeOutValue > 5 ) this->m_nTimeOutValue = nTimeOutValue;
else this->m_nTimeOutValue = 5;
};
// Nothing to destruct as inside the template class
// we keep/use just pointers obtained from external
// sources.
~CTimeOutChecker() {};
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A template interface showing how the client socket event
// handler should look like.
template<class T>
class ISockEvent {
public:
// Client socket ("pSocket") is about to be closed.
virtual void OnClose( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
// Client socket ("pSocket") was just accepted by
// the server socket (new connection with a client
// is created).
virtual void OnAccept( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
// Client socket ("pSocket") was returned from the IOCP
// queue with status _PENDING. For more details see
// "IOCPSimple<T>::SetPendingMode(...)". This method
// is invoked only if there was a call to
// "IOCPSimple<T>::SetPendingMode(...)", performed by a
// user code, internally "SetPendingMode(...)"
// is never called.
virtual void OnPending( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
// Client socket ("pSocket") was returned from IOCP
// queue with status _READ. This means that overlapped
// reading operation, started previously with
// "ClientSocket<T>::ReadFromSocket(...)", was
// successfully finished.
// - "pOverlap->DataBuf" structure points to the data
// buffer and buffer's size that were passed to the
// "ClientSocket<T>::ReadFromSocket(...)".
// - "dwBytesTransferred" will indicate how many
// bytes were read.
virtual void OnReadFinalized( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
DWORD dwBytesTransferred,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
// Client socket ("pSocket") was returned from IOCP
// queue with status _WRITE. This means that overlapped
// writting operation, started previously with
// "ClientSocket<T>::WriteToSocket(...)", was
// successfully finished.
// - "pOverlap->DataBuf" structure points to the data
// buffer and buffer's size that were passed to the
// "ClientSocket<T>::WriteToSocket(...)".
// - "dwBytesTransferred" will indicate how many
// bytes were written.
virtual void OnWriteFinalized( ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
DWORD dwBytesTransferred,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) = 0;
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Class is used internally by "ServerService<T>" as a
// Task entity to be submitted to "ServerService<T>::m_ThPool".
template<class T>
class CSockEventTask: public IRunnable {
private:
MYOVERLAPPED *m_pOverlap;
ClientSocket<T> *m_pSocket;
ISockEvent<T> *m_pSEvent;
ServerSocket<T> *m_pServerSocket;
IOCPSimple<T> *m_pHIocp;
DWORD m_dwBytesTransferred;
QueuedBlocks<CSockEventTask<T> > *m_pSockEventTaskPool;
protected:
virtual void run() {
if ( m_pSEvent == NULL ) return;
m_pSocket->Lock();
// check if client socket object is assigned a socket
// handler and session assigned to operation is equal
// to the session of the socket.
if ( m_pSocket->IsBusy() && ( m_pOverlap->nSession == m_pSocket->GetSession() ) ) {
switch ( m_pOverlap->OperationType ) {
// invoke the relevant event handler
case _CLOSE:
{
struct sockaddr_in addr;
m_pSocket->GetAddrIn(addr);
CLog::WriteLogA( "CloseSocket request %d. IP:%s\n",
m_pSocket->GetSocket(), inet_ntoa(addr.sin_addr));
m_pSEvent->OnClose( m_pSocket, m_pOverlap, m_pServerSocket, m_pHIocp );
// Make sure (double check) the socket
// is closed.
m_pServerSocket->Release( m_pSocket );
break;
}
case _ACCEPT:
m_pSEvent->OnAccept( m_pSocket, m_pOverlap, m_pServerSocket, m_pHIocp );
break;
case _READ:
m_pSEvent->OnReadFinalized( m_pSocket, m_pOverlap, m_dwBytesTransferred,
m_pServerSocket, m_pHIocp );
break;
case _PENDING:
m_pSEvent->OnPending( m_pSocket, m_pOverlap, m_pServerSocket, m_pHIocp );
break;
case _WRITE:
m_pSEvent->OnWriteFinalized( m_pSocket, m_pOverlap, m_dwBytesTransferred,
m_pServerSocket, m_pHIocp );
break;
default:
CLog::WriteLog( L"Oops...\n" );
}
}
m_pSocket->UnLock();
// Place the structure back to the pool.
Overlapped::Release( m_pOverlap );
m_pSockEventTaskPool->Release( this );
};
public:
CSockEventTask() { Clear(); };
// Set everything required to the Task to be
// able to invoke the relevant event handler.
void Set( QueuedBlocks<CSockEventTask<T> > *pSockEventTaskPool,
ISockEvent<T> *pSEvent,
ClientSocket<T> *pSocket,
MYOVERLAPPED *pOverlap,
DWORD dwBytesTransferred,
ServerSocket<T> *pServerSocket,
IOCPSimple<T> *pHIocp
) {
m_pSockEventTaskPool = pSockEventTaskPool;
m_pSEvent = pSEvent;
m_pOverlap = pOverlap;
m_dwBytesTransferred = dwBytesTransferred;
m_pSocket = pSocket;
m_pServerSocket = pServerSocket;
m_pHIocp = pHIocp;
};
// Method is used by 'QueuedBlocks' from
// "mem_manager.h".
void Clear() {
m_dwBytesTransferred = 0;
m_pSockEventTaskPool = NULL;
m_pSEvent = NULL;
m_pOverlap = NULL;
m_pSocket = NULL;
m_pServerSocket = NULL;
m_pHIocp = NULL;
};
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Well, finally, here is the template class joining all
// the stuff together. Considering the Aspect Oriented paradigm,
// this template class may be seen as an Aspect. The "individualizations"
// of this aspect are "ISockEvent<T>" and "T" itself. "T" is nothing
// else but attachment of the client socket (see ClientSocket<T> template
// class for more details). Implementing "ISockEvent<T>" and "T" will
// define the individual behaviour of this aspect.
// It is a composition of the IOCP, server socket, time-out checker
// and thread pool. Class implements the business logic that makes all
// these entities working together.
template<class T>
class ServerService: public IRunnable {
private:
ServerSocket<T> m_ServerSocket;
IOCPSimple<T> m_hIocp;
ISockEvent<T> *m_pSEvent;
CTimeOutChecker<T> *m_TChecker;
// thread pool which will execute the tasks
CSimpleThreadPool *m_ThPool;
// a pool of the CSockEventTask<T> objects
QueuedBlocks<CSockEventTask<T> > m_SockEventTaskPool;
protected:
// This method will be executed by a thread
// of the task pool.
virtual void run() {
int nRet;
DWORD dwBytesTransferred;
MYOVERLAPPED *pOverlap = NULL;
ClientSocket<T> *pSocket = NULL;
CLog::WriteLog( L"Socket service thread started.\n" );
while ( !CThread::currentThread().isInterrupted() ) {
::Sleep( 10 );
// check for an incoming connection
pSocket = m_ServerSocket.Accept();
if ( pSocket != NULL ) {
struct sockaddr_in addr;
pSocket->GetAddrIn(addr);
wchar_t * tmp = AnsiToUtf16(inet_ntoa(addr.sin_addr));
CLog::WriteLog( L"Socket created at %d. IP:%s\n",
pSocket->GetSocket(), tmp);
delete tmp;
// associate socket with IOCP
m_hIocp.AssociateSocket( pSocket );
pSocket->GetAttachment()->ResetTime( false );
// push the socket to the IOCP queue with
// status _ACCEPT
if ( !( m_hIocp.SetAcceptMode( pSocket ) ) ) {
m_ServerSocket.Release( pSocket );
}
}
dwBytesTransferred = 0;
pSocket = NULL;
// check the IOCP queue for:
// - a completed I/O operation against a socket or
// - a queued socket with a fake status like _ACCEPT,
// _CLOSE or _PENDING
//
// When an overlapped I/O completes, an I/O completion packet
// arrives at the IOCP and GetQueuedCompletionStatus returns.
nRet = m_hIocp.GetQueuedCompletionStatus( &dwBytesTransferred,
&pSocket, &pOverlap );
// something is wrong
if ( nRet == 0 ) {
if ( ::GetLastError() == WAIT_TIMEOUT ) continue;
if ( pOverlap != NULL ) dwBytesTransferred = 0;
else continue;
}
if ( pSocket == NULL ) {
Overlapped::Release( pOverlap );
continue;
}
// not good if zero bytes were transferred
if ( dwBytesTransferred == 0 ) {
// clear the time and push the socket to
// the IOCP with status _CLOSE. Socket
// will be closed shortly.
if ( pSocket->IsBusy() && ( pOverlap->nSession == pSocket->GetSession() ) ) {
pSocket->GetAttachment()->ResetTime( true );
if ( !( m_hIocp.SetCloseMode( pSocket, pOverlap ) ) ) {
m_ServerSocket.Release( pSocket );
}
}
Overlapped::Release( pOverlap );
}
else {
// obtain a free instance of the event task
// which will be submitted to the threat pool
CSockEventTask<T> *pCSockEv = m_SockEventTaskPool.Get();
if ( pCSockEv != NULL ) {
pCSockEv->Set( &m_SockEventTaskPool, m_pSEvent,
pSocket, pOverlap, dwBytesTransferred,
&m_ServerSocket, &m_hIocp
);
// _CLOSE operation type will have a lower
// priority that other operation types.
int priority = ( pOverlap->OperationType == _CLOSE )?1:2;
// submit the task to the thread pool
m_ThPool->submit( pCSockEv, priority );
}
else {
// no free resources, so close the socket
m_ServerSocket.Release( pSocket );
Overlapped::Release( pOverlap );
}
}
}
CLog::WriteLog( L"Socket service thread stopped.\n" );
};
public:
// Constructor or the class.
// pSEvent - pointer to an instance implementing
// ISockEvent<T>. This instance will be used
// as a client socket event handler.
// nPort - port number to bind server socket to.
// nMaxClients - the maximum number of accepted (concurrent)
// client connections. To be passed to
// the server socket and also will be used
// as the initial size for the pool of the
// CSockEventTask<T> objects.
// nNoThreads - indicative (the real one is computed,
// see below) number of the threads
// to be created by the thread pool.
// timeout - the value of the time-out, in seconds.
// Will be passed to the time-out checker.
// If time-out is zero, time-out checker
// will not be created.
// blnBindLocal - see ServerSocket<T> for more details.
// If "true" then server socket is bind
// to localhost ("127.0.0.1").
// If "false" then server socket is bind
// to INADDR_ANY ("0.0.0.0").
ServerService( ISockEvent<T> *pSEvent, unsigned int nPort, unsigned int nMaxClients,
unsigned int nNoThreads, unsigned int timeout,
bool blnBindLocal = true ):
m_ServerSocket( nPort, nMaxClients, true, blnBindLocal ),
m_hIocp( 200 ), m_SockEventTaskPool( nMaxClients ) {
unsigned int i = 0;
SYSTEM_INFO si;
CLog::WriteLog( L"Creating \"socket_listener\" ... " );
if ( nMaxClients < 1 ) {
throw "illegal value for \"max_connections\" supplied (should be > 0).";
}
if ( nNoThreads < 1 ) {
throw "illegal value for \"working_threads\" supplied (should be > 0).";
}
if ( pSEvent == NULL ) {
throw "NULL pointer set for socket event handler.";
}
m_pSEvent = pSEvent;
// Query system info in order to check
// how many CPU's are there installed in
// the system.
::GetSystemInfo( &si );
// Compute the real number of the threads
// to be created by the thread pool.
i = nNoThreads + si.dwNumberOfProcessors;
// If configured time-out is zero, then don't
// create time-out checker object. Client sockets
// will not be checked against time-out cases.
if ( timeout > 0 ) {
m_TChecker = new CTimeOutChecker<T>( m_ServerSocket.GetPool(), &m_hIocp, timeout );
// Increase the number of the threads,
// +1 for the time-out checker.
i++;
} else m_TChecker = NULL;
// Create the thread pool which will handle
// the tasks. "nMaxClients" is used as the
// initial capacity of the priority queue
// of (associated with) the thread pool.
m_ThPool = new CSimpleThreadPool( i, 2 * nMaxClients );
// Submit this instance (as a task) to the
// thread pool, as many times as CPU's are installed
// in the system. The "run()" method will be executed
// by pool's threads.
for (i = 0; i < si.dwNumberOfProcessors; i++) m_ThPool->submit( this, 1 );
// If time-out checker was created, then
// submit it to the thread pool (as a task).
if ( m_TChecker != NULL ) m_ThPool->submit( m_TChecker );
CLog::WriteLog( L"DONE\n" );
};
virtual ~ServerService() {
// Stop all the pool's threads.
m_ThPool->shutdown();
// If time-out checker was created, then
// delete it.
if (m_TChecker != NULL) delete m_TChecker;
// Delete the thread pool.
delete m_ThPool;
CLog::WriteLog( L"Killing \"socket_listener\" ... DONE\n" );
};
// Start the threat pool (== all the
// threads in the pool).
void start() { m_ThPool->startAll(); };
};
#endif
#ifndef ___SOCKET_CLIENT_H_INCLUDED___
#define ___SOCKET_CLIENT_H_INCLUDED___
#include <winsock2.h>
#include "mem_manager.h"
#define RECV_BUFFER_EMPTY -2
enum IO_SOCK_MODES {_CLOSE, _ACCEPT, _READ, _WRITE, _PENDING};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Overlapped structure used with this implementation of the IOCP.
// Only the "Overlapped" member is used in context with the
// standard IOCP API, anything else is just to help this particular
// implementation.
typedef struct {
// It is very important "Overlapped" to be the very first
// member of the structure because pointer pointing to
// "this" is == to the address of the first member.
// This makes usage of the MYOVERLAPPED equivalent
// to the OVERLAPPED in context of the standard IOCP API (just a trick).
OVERLAPPED Overlapped;
// A structure that points to the data packet and size of the data
// packet associated with current overlapped operation.
WSABUF DataBuf;
// Operation type associated with current overlapped operation.
IO_SOCK_MODES OperationType;
// Internally used. If nSession != ClientSocket::m_nSession
// that means that socket was closed (recently) and
// operation retrieved from IOCP queue or TaskPool queue
// is not relevant, to the current session of the socket.
volatile unsigned int nSession;
// Method is used by 'QueuedBlocks' from
// "mem_manager.h".
void Clear() {
memset(this, 0, sizeof(MYOVERLAPPED));
};
} MYOVERLAPPED;
// a pool of the MYOVERLAPPED structures
typedef StaticBlocks<MYOVERLAPPED> Overlapped;
QueuedBlocks<MYOVERLAPPED> *Overlapped::blocks = NULL;
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A class wrapping basic client socket's operations
template<class T>
class ClientSocket {
//friend class MyServerService;
private:
SOCKET m_ClientSock;
volatile unsigned int m_nSession;
QMutex m_qMutex;
struct sockaddr_in m_psForeignAddIn;
volatile bool m_blnIsBusy;
T m_objAttachment;
protected:
public:
ClientSocket(): m_objAttachment(), m_qMutex() {
this->m_nSession = 1;
this->m_ClientSock = 0;
this->m_blnIsBusy = false;
};
~ClientSocket() {
if (this->m_blnIsBusy) this->CloseSocket();
};
// Is the object assigned a socket.
bool IsBusy() { return this->m_blnIsBusy; };
// Method is used by 'QueuedBlocks' from
// "mem_manager.h".
void Clear() { CloseSocket(); };
// Returns the socket associated with the object.
SOCKET GetSocket() { return this->m_ClientSock; };
void GetAddrIn(struct sockaddr_in& addrin) {memcpy(&addrin, &m_psForeignAddIn,sizeof(struct sockaddr_in));}
// Returns the session of the current client socket.
// Technically, this is the counter of how may times
// current instance was pooled, but really, this is used
// to check if session of operation (MYOVERLAPPED)
// is == with the session of the socket.
unsigned int GetSession() { return this->m_nSession; };
// Internally used by this implementation. Don't call it.
void Lock() { m_qMutex.Lock(); };
// Internally used by this implementation. Don't call it.
void UnLock() { m_qMutex.Unlock(); };
// Associate the object with a socket.
int Associate(SOCKET sSocket, struct sockaddr_in *psForeignAddIn) {
int nRet = SOCKET_ERROR;
if (sSocket < 0) return nRet;
if (this->m_blnIsBusy) return nRet;
this->m_blnIsBusy = true;
this->m_ClientSock = sSocket;
memcpy(&m_psForeignAddIn, psForeignAddIn, sizeof(struct sockaddr_in));
m_objAttachment.Clear();
return (int) this->m_ClientSock;
};
// Returns the attachment of the object.
// Use the attachment to associate the socket with any other
// additional information required.
T* GetAttachment() { return &m_objAttachment; };
// Write data to the socket,
// uses overlapped style.
int WriteToSocket(char *pBuffer, DWORD buffSize) {
int nRet = SOCKET_ERROR ;
DWORD dwSendBytes = 0;
DWORD dwFlags = 0;
if ( this->m_blnIsBusy ) {
// Get a free instance of the structure
// from the pool.
MYOVERLAPPED *mov = Overlapped::Get();
if ( mov != NULL ) {
mov->DataBuf.buf = pBuffer;
mov->DataBuf.len = buffSize;
mov->OperationType = _WRITE;
mov->nSession = GetSession();
nRet = WSASend(this->m_ClientSock, &mov->DataBuf, 1,
&dwSendBytes, dwFlags, (OVERLAPPED *) mov, NULL);
if (nRet == SOCKET_ERROR) {
if ((WSAGetLastError()) == WSA_IO_PENDING) nRet = 0;
}
}
}
return nRet;
};
// Read data from the socket,
// uses overlapped style.
int ReadFromSocket(char *pBuffer, DWORD buffSize) {
int nRet = SOCKET_ERROR ;
DWORD dwRecvBytes;
DWORD dwFlags = 0;
if ( this->m_blnIsBusy ) {
if (buffSize > 0) {
// Get a free instance of the structure
// from the pool.
MYOVERLAPPED *mov = Overlapped::Get();
if ( mov != NULL ) {
mov->DataBuf.buf = pBuffer;
mov->DataBuf.len = buffSize;
mov->OperationType = _READ;
mov->nSession = GetSession();
nRet = WSARecv(this->m_ClientSock, &mov->DataBuf, 1,
&dwRecvBytes, &dwFlags, (OVERLAPPED *) mov, NULL);
if (nRet == SOCKET_ERROR) {
if ((WSAGetLastError()) == WSA_IO_PENDING) nRet = 0;
}
}
}
else nRet = RECV_BUFFER_EMPTY;
}
return nRet;
};
// Closes the socket associated with this instance
// of the object and performs clean-up.
void CloseSocket() {
if ( this->m_blnIsBusy ) {
this->m_blnIsBusy = false;
if ((this->m_nSession++) > 0x0FFFD) this->m_nSession = 1;
shutdown(this->m_ClientSock, 2);
closesocket(this->m_ClientSock);
this->m_ClientSock = 0;
memset(&m_psForeignAddIn, 0 , sizeof(struct sockaddr_in));
m_objAttachment.Clear();
}
};
};
#endif
\ No newline at end of file
#ifndef ___SOCKET_SERVER_H_INCLUDED___
#define ___SOCKET_SERVER_H_INCLUDED___
#include <winsock2.h>
#include "mem_manager.h"
#include "socket_client.h"
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A class for handling basic server socket operations.
template<class T>
class ServerSocket {
private:
// Server socket.
SOCKET m_ServSock;
// Pool of client sockets.
QueuedBlocks<ClientSocket<T> > m_SockPool;
protected:
public:
// Set the size of the socket pool to the maximum number of accepted
// client connections.
ServerSocket(unsigned int nPort, unsigned int nMaxClients,
bool blnCreateAsync = false, bool blnBindLocal = true): m_SockPool(nMaxClients) {
int nRet;
unsigned long lngMode = 1;
struct sockaddr_in sAddrIn;
// Create the server socket, set the necessary
// parameters for making it IOCP compatible.
this->m_ServSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (this->m_ServSock <= 0) {
throw "server socket creation failed.";
}
// Make server socket Synchronous (blocking)
// or Asynchronous (non-blocking)
if (blnCreateAsync) {
nRet = ioctlsocket(this->m_ServSock, FIONBIO, (u_long FAR*) &lngMode);
if (nRet < 0) {
closesocket(this->m_ServSock);
throw "server socket creation failed.";
}
}
// Fill the structure for binding operation
sAddrIn.sin_family = AF_INET;
sAddrIn.sin_port = htons(nPort);
// Bind to the localhost ("127.0.0.1") to accept connections only from
// localhost or
// "0.0.0.0" (INADDR_ANY) to accept connections from any IP address.
if (blnBindLocal) sAddrIn.sin_addr.s_addr = inet_addr("127.0.0.1");
else sAddrIn.sin_addr.s_addr = htonl(INADDR_ANY);
// Bind the structure to the created server socket.
nRet = bind(this->m_ServSock, (struct sockaddr *) &sAddrIn, sizeof(sAddrIn));
if (nRet < 0) {
closesocket(this->m_ServSock);
throw "server socket failed to bind on given port.";
}
// Set server socket in listen mode and set the listen queue to 20.
nRet = listen(this->m_ServSock, 20);
if (nRet < 0) {
closesocket(this->m_ServSock);
throw "server scket failed to listen.";
}
};
// Returns a pointer to the ClientSocket instance, bind
// to the accepted client socket (incoming connection).
ClientSocket<T>* Accept() {
SOCKET sAccept;
struct sockaddr_in sForeignAddIn;
int nLength = sizeof(struct sockaddr_in);
ClientSocket<T> *ret = NULL;
// Check if there is something in the listen queue.
sAccept = WSAAccept(this->m_ServSock, (struct sockaddr *) &sForeignAddIn,
&nLength, NULL, NULL);
// Check for errors.
if (sAccept != INVALID_SOCKET) {
// Get a pointer to the free ClientSocket
// instance from the pool.
ret = m_SockPool.GetFromQueue();
if (ret == NULL) {
// There are no free instances in the pool, maximum
// number of accepted client connections is reached.
::shutdown(sAccept,2);
::closesocket(sAccept);
}
else {
// Everything is fine, so associate the instance
// with the client socket.
ret->Lock();
ret->Associate(sAccept, &sForeignAddIn);
ret->UnLock();
}
}
return ret;
};
// Release the client socket, will try closing connection and
// will place it back to the pool.
void Release(ClientSocket<T> *sock) {
if (sock != NULL) {
// check if client socket object is
// assigned a socket handler. If yes,
// close it.
if (sock->IsBusy()) sock->CloseSocket();
// Place it back to the pool.
m_SockPool.Release(sock);
}
};
vector<ClientSocket<T> *> *GetPool() {
return m_SockPool.GetBlocks();
};
~ServerSocket() {
shutdown(this->m_ServSock, 2);
closesocket(this->m_ServSock);
};
};
#endif
\ No newline at end of file
#ifndef ___SYNC_SIMPLE_H_INCLUDED___
#define ___SYNC_SIMPLE_H_INCLUDED___
#include <windows.h>
#pragma comment(lib, "kernel32.lib")
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Implementation of the critical section
class QMutex {
private:
CRITICAL_SECTION m_Cs;
public:
QMutex() { ::InitializeCriticalSection(&this->m_Cs); }
~QMutex() { ::DeleteCriticalSection(&this->m_Cs); }
void Lock() { ::EnterCriticalSection(&this->m_Cs); }
BOOL TryLock() { return (BOOL) ::TryEnterCriticalSection(&this->m_Cs); }
void Unlock() { ::LeaveCriticalSection(&this->m_Cs); }
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Implementation of the semaphore
class QSemaphore {
private:
HANDLE m_hSemaphore;
long m_lMaximumCount;
public:
QSemaphore(long lMaximumCount) {
this->m_hSemaphore = ::CreateSemaphore(NULL, lMaximumCount, lMaximumCount, NULL);
if (this->m_hSemaphore == NULL) throw "Call to CreateSemaphore() failed. Could not create semaphore.";
this->m_lMaximumCount = lMaximumCount;
};
~QSemaphore() { ::CloseHandle(this->m_hSemaphore); };
long GetMaximumCount() const { return this->m_lMaximumCount; };
void Inc() { ::WaitForSingleObject(this->m_hSemaphore, INFINITE); };
void Dec() { ::ReleaseSemaphore(this->m_hSemaphore, 1, NULL); };
void Dec(long lCount) { ::ReleaseSemaphore(this->m_hSemaphore, lCount, NULL); };
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Implementation of the read-write mutex.
// Multiple threads can have read access at the same time.
// Write access is exclusive for only one thread.
class ReadWriteMutex {
private:
QMutex m_qMutex;
QSemaphore m_qSemaphore;
public:
ReadWriteMutex(long lMaximumReaders): m_qSemaphore(lMaximumReaders) {};
void lockRead() { m_qSemaphore.Inc(); };
void unlockRead() { m_qSemaphore.Dec(); };
void lockWrite() {
m_qMutex.Lock();
for (int i = 0; i < maxReaders(); ++i) m_qSemaphore.Inc();
m_qMutex.Unlock();
};
void unlockWrite() { m_qSemaphore.Dec(m_qSemaphore.GetMaximumCount()); };
int maxReaders() const { return m_qSemaphore.GetMaximumCount(); };
};
#endif
\ No newline at end of file
#ifndef ___THREADING_H_INCLUDED___
#define ___THREADING_H_INCLUDED___
#include <windows.h>
#include "mem_manager.h"
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
class CThread;
class CSimpleThreadPool;
///////////////////////////////////////////////////////////////////////////////////////////////
// The core interface of this threading implementation.
class IRunnable {
friend class CThread;
friend class CSimpleThreadPool;
protected:
virtual void run() = 0;
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// structure holds Thread Local Storage descriptor
struct TLSDescriptor {
DWORD descriptor;
TLSDescriptor() {
descriptor = TlsAlloc();
if (descriptor == -1) throw "TlsAlloc() failed to create descriptor";
};
~TLSDescriptor() { TlsFree(descriptor); };
};
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Very basic thread class, implementing basic threading API
class CThread: public IRunnable {
private:
static TLSDescriptor m_TLSDesc;
volatile bool m_bIsInterrupted;
volatile bool m_bIsRunning;
int m_nThreadPriority;
IRunnable *m_RunObj;
QMutex m_qMutex;
// See ::CreateThread(...) within the start() method. This is
// the thread's API function to be executed. Method executes
// run() method of the CThread instance passed as parameter.
static DWORD WINAPI StartThreadST(LPVOID PARAM) {
CThread *_this = (CThread *) PARAM;
if (_this != NULL) {
_this->m_qMutex.Lock();
// Set the pointer to the instance of the passed CThread
// in the current Thread's Local Storage. Also see
// currentThread() method.
TlsSetValue(CThread::m_TLSDesc.descriptor, (LPVOID) _this);
_this->run();
_this->m_bIsRunning = false;
_this->m_qMutex.Unlock();
}
return 0;
};
protected:
// It is not possible to instantiate CThread objects directly. Possible only by
// specifying a IRunnable object to execute its run() method.
CThread(int nPriority = THREAD_PRIORITY_NORMAL): m_qMutex() {
this->m_bIsInterrupted = false;
this->m_bIsRunning = false;
this->m_nThreadPriority = nPriority;
this->m_RunObj = NULL;
};
// this implementation of the run() will execute the passed IRunnable
// object (if not null). Inheriting class is responsible for using this
// method or overriding it with a different one.
virtual void run() {
if (this->m_RunObj != NULL) this->m_RunObj->run();
};
public:
CThread(IRunnable *RunTask, int nPriority = THREAD_PRIORITY_NORMAL): m_qMutex() {
this->m_bIsInterrupted = false;
this->m_bIsRunning = false;
this->m_nThreadPriority = nPriority;
if (this != RunTask) this->m_RunObj = RunTask;
else throw "Self referencing not allowed.";
};
virtual ~CThread() {
this->interrupt();
// wait until thread ends
this->join();
};
// Method returns the instance of a CThread responsible
// for the context of the current thread.
static CThread& currentThread() {
CThread *thr = (CThread *) TlsGetValue(CThread::m_TLSDesc.descriptor);
if (thr == NULL) throw "Call is not within a CThread context.";
return *thr;
};
// Method signals thread to stop execution.
void interrupt() { this->m_bIsInterrupted = true; };
// Check if thread was signaled to stop.
bool isInterrupted() { return this->m_bIsInterrupted; };
// Method will wait for thread's termination.
void join() {
this->m_qMutex.Lock();
this->m_qMutex.Unlock();
};
// Method starts the Thread. If thread is already started/running, method
// will simply return.
void start() {
HANDLE hThread;
LPTHREAD_START_ROUTINE pStartRoutine = &CThread::StartThreadST;
if (this->m_qMutex.TryLock()) {
if (!this->m_bIsRunning) {
this->m_bIsRunning = true;
this->m_bIsInterrupted = false;
hThread = ::CreateThread(NULL, 0, pStartRoutine, (PVOID) this, 0, NULL);
if (hThread == NULL) {
this->m_bIsRunning = false;
this->m_qMutex.Unlock();
throw "Failed to call CreateThread(). Thread not started.";
}
::SetThreadPriority(hThread, this->m_nThreadPriority);
::CloseHandle(hThread);
}
this->m_qMutex.Unlock();
}
};
};
TLSDescriptor CThread::m_TLSDesc;
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// Helper class to submit tasks to the CSimpleThreadPool
class CPriorityTask {
private:
int m_nPriority;
IRunnable *m_pRunObj;
public:
CPriorityTask(const CPriorityTask &t) {
m_pRunObj = t.m_pRunObj;
m_nPriority = t.m_nPriority;
};
CPriorityTask() {
m_pRunObj = NULL;
m_nPriority = 0;
};
CPriorityTask(IRunnable *pRunObj, int nPriority = 0) {
m_pRunObj = pRunObj;
m_nPriority = nPriority;
};
int getPriority() const { return m_nPriority; };
IRunnable *getTask() const { return m_pRunObj; };
~CPriorityTask() {};
CPriorityTask& operator=(const CPriorityTask &t) {
m_nPriority = t.m_nPriority;
m_pRunObj = t.m_pRunObj;
return *this;
};
};
//Overload the < operator.
bool operator< (const CPriorityTask& pt1, const CPriorityTask& pt2) {
return pt1.getPriority() < pt2.getPriority();
}
//Overload the > operator.
bool operator> (const CPriorityTask& pt1, const CPriorityTask& pt2) {
return pt1.getPriority() > pt2.getPriority();
}
///////////////////////////////////////////////////////////////////////////////////////////////
//---------------------------- CLASS ----------------------------------------------------------
// A class containing a collection of CThreadTask's.
// Every CThreadTask will execute same CSimpleThreadPool::run() method.
class CSimpleThreadPool: public IRunnable {
private:
QMutex m_qMutex;
vector<CThread*> m_arrThreadTasks;
mpriority_queue<CPriorityTask> m_PQueue;
// Method will return a task from the queue,
// if there are no tasks in the queue, method will return NULL.
IRunnable *get() {
IRunnable *ret = NULL;
m_qMutex.Lock();
if (!m_PQueue.empty()) {
CPriorityTask t = m_PQueue.top();
m_PQueue.pop();
ret = t.getTask();
}
m_qMutex.Unlock();
return ret;
};
public:
// How many threads are in the collection.
int threads() const { m_arrThreadTasks.size(); };
// Method starts pool's threads.
void startAll() {
for (unsigned int i = 0; i < m_arrThreadTasks.size(); i++) {
m_arrThreadTasks[i]->start();
}
};
// Constructor creates the thread pool and sets capacity for the task queue.
CSimpleThreadPool(unsigned int nThreadsCount, unsigned int nQueueCapacity = 16):
m_qMutex(), m_PQueue() {
unsigned int i;
CThread *thTask = NULL;
if (nThreadsCount <= 0) throw "Invalid number of threads supplied.";
if (nQueueCapacity <= 0) throw "Invalid capacity supplied.";
// Set initial capacity of the tasks Queue.
m_PQueue.reserve(nQueueCapacity);
// Initialize thread pool.
for (i = 0; i < nThreadsCount; i++) {
thTask = new CThread(this);
if (thTask != NULL) m_arrThreadTasks.push_back(thTask);
}
};
// Submit a new task to the pool
void submit(IRunnable *pRunObj, int nPriority = 0) {
if (this == pRunObj) throw "Self referencing not allowed.";
m_qMutex.Lock();
m_PQueue.push(CPriorityTask(pRunObj, nPriority));
m_qMutex.Unlock();
};
// Method will execute task's run() method within its CThread context.
virtual void run() {
IRunnable *task;
while (!CThread::currentThread().isInterrupted()) {
// Get a task from the queue.
task = get();
// Execute the task.
if (task != NULL) task->run();
::Sleep(2);
}
};
virtual ~CSimpleThreadPool() {
vector<CThread*>::iterator itPos = m_arrThreadTasks.begin();
for (; itPos < m_arrThreadTasks.end(); itPos++) delete *itPos;
m_arrThreadTasks.clear();
while (!m_PQueue.empty()) { m_PQueue.pop(); }
};
// Method signals threads to stop and waits for termination
void shutdown() {
for (unsigned int i = 0; i < m_arrThreadTasks.size(); i++) {
m_arrThreadTasks[i]->interrupt();
m_arrThreadTasks[i]->join();
}
};
};
#endif
\ No newline at end of file
// LocalLock.h: interface for the CLocalLock class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_LOCALLOCK_H__FD10F2F9_1DF2_46A6_8261_1FA9E2AB061C__INCLUDED_)
#define AFX_LOCALLOCK_H__FD10F2F9_1DF2_46A6_8261_1FA9E2AB061C__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CLocalLock
{
public:
CLocalLock(LPCRITICAL_SECTION lpCriticalSection)
:m_lpCriticalSection(lpCriticalSection)
{
EnterCriticalSection(m_lpCriticalSection);
}
~CLocalLock()
{
LeaveCriticalSection(m_lpCriticalSection);
}
private:
LPCRITICAL_SECTION m_lpCriticalSection;
};
class CLock
{
public:
CLock()
{
InitializeCriticalSection(&m_cs);
}
~CLock()
{
DeleteCriticalSection(&m_cs);
}
void Lock()
{
EnterCriticalSection(&m_cs);
}
void UnLock()
{
LeaveCriticalSection(&m_cs);
}
private:
CRITICAL_SECTION m_cs;
};
#endif // !defined(AFX_LOCALLOCK_H__FD10F2F9_1DF2_46A6_8261_1FA9E2AB061C__INCLUDED_)
// Log.h: interface for the CLog class.
//
//////////////////////////////////////////////////////////////////////
/*
日志类CLog
简述:单例的日志输出类,使用时会自动构造,但因为要保证日志类最后一个析构,需要手动释放
功能:写日志,输出TRACE内容到dbgview,console。
使用:
使用函数
static void SetOutputLogFileName(LPCTSTR szFileName);
static void SetOutputConsole(BOOL bFlag);
static void SetOutputDbgView(BOOL bFlag);
static void SetOutputLogFile(BOOL bFlag);
来设置输出日志的方式
使用TRACE或使用函数static void WriteLog(const char* format, ...);写日志,可接受可变参数
释放:在程序退出时(ExitInstance()函数的最后)调用CLog::Release()释放引用计数
*/
#if !defined(AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_)
#define AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
//#include "dbghlpapi.h"
#ifdef TRACE
#undef TRACE
#define TRACE CLog::WriteLog
#endif
#define IMPLEMENT_CLASS_LOG_STATIC_MEMBER \
CRITICAL_SECTION CLog::m_cs;\
CLock CLog::m_lockForSingleton;\
char CLog::m_ansiBuf[MAX_OUTPUT_LEN];\
wchar_t CLog::m_utf16Buf[MAX_OUTPUT_LEN];
/*
#define TRACEFUNCNAME \
CString str = _T("");\
INIT_SYM(TRUE);\
str.Format("File %s, line %d, funcname %s\n", __FILE__, __LINE__, __FUNCNAME__);\
OutputDebugString(str);\
UNINIT_SYM();\
*/
#define MAX_OUTPUT_LEN 4096
#define MAX_FILE_LEN 1024 * 1024 * 10
static TCHAR g_szFileName[1024] = {0};
class CLog
{
private:
double exe_time;
LARGE_INTEGER freq;
LARGE_INTEGER start_t, stop_t;
BOOL m_bOutputLogFile;
BOOL m_bOutputDbgView;
BOOL m_bOutputConsole;
BOOL m_bConsoleOpened;
BOOL m_bDbgviewOpened;
BOOL m_bLogFileOpened;
BOOL m_bRunning;
FILE *m_pLogFile;
PROCESS_INFORMATION pi;
static CRITICAL_SECTION m_cs;
static CLock m_lockForSingleton;
static char m_ansiBuf[MAX_OUTPUT_LEN];
static wchar_t m_utf16Buf[MAX_OUTPUT_LEN];
public:
static CString GetPrivateLogPath(){
static CString strPrivateMapFolder = _T("");
static BOOL b = TRUE;
if(b){
strPrivateMapFolder.Format(_T("%s\\Log"), GetModuleFilePath());
b = FALSE;
}
return strPrivateMapFolder;
}
static CLog* GetInstance(){
m_lockForSingleton.Lock();
static CLog log;
m_lockForSingleton.UnLock();
return &log;
}
~CLog(){
m_bRunning = FALSE;
//WaitTillThreadExited(m_handle);
if(m_bConsoleOpened)
OpenConsole(FALSE);
if(m_bDbgviewOpened)
OpenDbgview(FALSE);
if(m_bLogFileOpened)
CloseLogFile();
DeleteCriticalSection(&m_cs);
#ifdef _DEBUG
TCHAR buf[1024], out[1024];
wsprintf(buf, _T("%s\n"), _T("CLog::~CLog() destruction"));
FormatBuf(buf, out, 1024);
OutputDebugString(out);
#endif
}
static void SetOutputLogFileName(LPCTSTR szLastName){
lstrcpy(g_szFileName, szLastName);
}
static void SetOutputConsole(BOOL bFlag){
CLog* plog = CLog::GetInstance();
plog->m_bOutputConsole = bFlag;
plog->OpenConsole(bFlag);
}
static void SetOutputDbgView(BOOL bFlag){
CLog* plog = CLog::GetInstance();
plog->m_bOutputDbgView = bFlag;
//plog->OpenDbgview(bFlag);
}
static void SetOutputLogFile(BOOL bFlag){
CLog* plog = CLog::GetInstance();
plog->m_bOutputLogFile = bFlag;
if(plog->m_bOutputLogFile && lstrlen(g_szFileName) == 0)
plog->CreateFileName();
}
static void WriteLog(const TCHAR* format, ...) {
try{
CLog* plog = CLog::GetInstance();
if(plog == NULL)
return;
CLocalLock lock(&m_cs);
if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) {
static TCHAR buf[MAX_OUTPUT_LEN], output[MAX_OUTPUT_LEN], *p;
p = buf;
va_list args;
va_start(args, format);
p += _vsntprintf_s(p, sizeof (buf) - 1, sizeof (buf) - 1, format, args);
va_end(args);
while ( (p > buf) && _istspace(*(p-1)) )
*--p = _T('\0');
*p++ = _T('\r');
*p++ = _T('\n');
*p = _T('\0');
plog->FormatBuf(buf, output);
plog->Output(output);
}
}
catch(...){
AfxMessageBox(_T("CLog::WriteLog"));
ASSERT(0);
}
}
static void WriteLogW(const wchar_t* format, ...) {
try{
CLog* plog = CLog::GetInstance();
if(plog == NULL)
return;
CLocalLock lock(&m_cs);
if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) {
static wchar_t buf[MAX_OUTPUT_LEN], *p;
p = buf;
va_list args;
va_start(args, format);
p += _vsnwprintf_s(p, sizeof (buf) - 1, sizeof (buf) - 1, format, args);
va_end(args);
while ( (p > buf) && iswspace(*(p-1)) )
*--p = '\0';
*p++ = '\r';
*p++ = '\n';
*p = '\0';
#if defined(_UNICODE) || defined(UNICODE)
wchar_t output[MAX_OUTPUT_LEN];
plog->FormatBuf(buf, output);
plog->Output(output);
#else
char output[MAX_OUTPUT_LEN];
if(Utf16ToAnsiUseCharArray(buf, m_ansiBuf, MAX_OUTPUT_LEN)){
WORD out_len = plog->FormatBuf(m_ansiBuf, output);
plog->Output(output, out_len);
}else{
char *ansiOut = Utf16ToAnsi(buf);
WORD out_len = plog->FormatBuf(ansiOut, output);
plog->Output(output, out_len);
SAFEDELETEARR(ansiOut);
}
#endif
}
}
catch(...){
AfxMessageBox(_T("CLog::WriteLog"));
ASSERT(0);
}
}
static void WriteLogA(const char* format, ...) {
try{
CLog* plog = CLog::GetInstance();
if(plog == NULL)
return;
CLocalLock lock(&m_cs);
if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) {
static char buf[MAX_OUTPUT_LEN], *p;
p = buf;
va_list args;
va_start(args, format);
p += _vsnprintf_s(p, sizeof (buf) - 1, sizeof (buf) - 1, format, args);
va_end(args);
while ( (p > buf) && isspace(*(p-1)) )
*--p = '\0';
*p++ = '\r';
*p++ = '\n';
*p = '\0';
#if defined(_UNICODE) || defined(UNICODE)
wchar_t output[MAX_OUTPUT_LEN];
if(AnsiToUtf16Array(buf, m_utf16Buf, MAX_OUTPUT_LEN))
{
plog->FormatBuf(m_utf16Buf, output);
plog->Output(output);
}
else
{
wchar_t *wBuf = AnsiToUtf16(buf);
plog->FormatBuf(wBuf, output);
plog->Output(output);
SAFEDELETEARR(wBuf);
}
#else
char output[MAX_OUTPUT_LEN];
WORD out_len = plog->FormatBuf(buf, output);
plog->Output(output, out_len);
#endif
}
}
catch(...){
AfxMessageBox(_T("CLog::WriteLog"));
ASSERT(0);
}
}
protected:
CLog(): m_pLogFile(NULL), m_bOutputLogFile(FALSE), m_bOutputDbgView(FALSE),
m_bOutputConsole(FALSE), m_bConsoleOpened(FALSE), m_bDbgviewOpened(FALSE),
m_bLogFileOpened(FALSE), m_bRunning(TRUE){
QueryPerformanceFrequency(&freq);
//OutputDebugString(_T("The frequency of your pc is 0x%X.\n"), freq.QuadPart);
QueryPerformanceCounter(&start_t);
memset(g_szFileName, 0, sizeof(g_szFileName));
TCHAR out[128] = {0};
wsprintf(out, _T("CLog construction addr: %p\n"), this);
OutputDebugString(out);
memset(&pi, 0, sizeof(pi));
InitializeCriticalSection(&m_cs);
}
LPCTSTR GetAppRunTime() {
static TCHAR szTime[128];
memset(szTime, 0, sizeof szTime);
QueryPerformanceCounter(&stop_t);
exe_time = 1e3*(stop_t.QuadPart-start_t.QuadPart)/freq.QuadPart;
WORD day, hour, min, sec;
sec = static_cast<unsigned short>(static_cast<unsigned long>(exe_time / 1000) % 60);
min = static_cast<unsigned short>(static_cast<unsigned long>(exe_time / 1000 / 60) % 60);
hour = static_cast<unsigned short>((exe_time / 1000 / 60) / 60);
day = static_cast<unsigned short>(exe_time / 1000.0 / 60.0 / 60.0 / 24);
double ms = exe_time - (int)(exe_time) + ((int)(exe_time) % 1000);
wsprintf(szTime, _T("%dday%02d:%02d:%02ds.%3.6fms"), day, hour, min, sec, ms);
return szTime;
}
void Output(const TCHAR *out) {
if(m_bOutputLogFile){
OutputFile(out);
}
#ifdef _DEBUG
OutputDebugString(out);
#else
if(m_bOutputDbgView){
OutputDebugString(out);
}
#endif
if(m_bOutputConsole){
_tprintf(out);
}
}
WORD FormatBuf(const TCHAR *oldBuf, TCHAR *newBuf, WORD max_new_buff_len = MAX_OUTPUT_LEN){
static SYSTEMTIME st;
static TCHAR sztime[128];
memset(sztime, 0, sizeof sztime);
GetLocalTime(&st);
wsprintf(sztime, _T("HB %04d-%02d-%02d-w%d %02d:%02d:%02d.%03d ---- "),
st.wYear, st.wMonth, st.wDay, (st.wDayOfWeek != 0) ? st.wDayOfWeek : 7,
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
lstrcpy(newBuf, sztime);
if(lstrlen(sztime) + lstrlen(oldBuf) < max_new_buff_len){
lstrcat(newBuf, oldBuf);
}
return static_cast<unsigned short>(lstrlen(newBuf));
}
void OutputFile(const TCHAR *buf){
if(m_pLogFile == NULL)
m_pLogFile = OpenLogFile();
if(m_pLogFile != NULL){
fseek(m_pLogFile, 0, SEEK_END);
long filelen = ftell(m_pLogFile);
if(filelen >= MAX_FILE_LEN){
fclose(m_pLogFile);
CreateFileName();
m_pLogFile = OpenLogFile();
}
}
if(m_pLogFile != NULL){
fseek(m_pLogFile, 0, SEEK_END);
#if defined(_UNICODE) || defined(UNICODE)
if(Utf16ToAnsiUseCharArray(buf, m_ansiBuf, MAX_OUTPUT_LEN)){
fwrite(m_ansiBuf, 1, strlen(m_ansiBuf), m_pLogFile);
}else{
char *ansiOut = Utf16ToAnsi(buf);
fwrite(ansiOut, 1, strlen(ansiOut), m_pLogFile);
SAFEDELETEARR(ansiOut);
}
#else
fwrite(buf, 1, buf_len, m_pLogFile);
#endif
fflush(m_pLogFile);
}
}
BOOL OpenDbgview(BOOL bOpen){
static STARTUPINFO si;
static LPCTSTR lpszAppName = _T("dbgview.exe");
if(bOpen){
if(!m_bDbgviewOpened){
memset(&si,0,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
if(CreateProcess(lpszAppName,NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)) {
m_bDbgviewOpened = TRUE;
}
}
}else if(m_bDbgviewOpened){
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
m_bDbgviewOpened = FALSE;
}
return bOpen ? m_bDbgviewOpened : !m_bDbgviewOpened;
}
FILE* OpenLogFile(){
FILE *pfile = NULL;
_tfopen_s(&pfile, g_szFileName, _T("r"));
if(pfile == NULL){
_tfopen_s(&pfile, g_szFileName, _T("wb"));
if(pfile == NULL){
AfxMessageBox(_T("Create log file failed."));
return NULL;
}
}else{
fclose(pfile);
_tfopen_s(&pfile, g_szFileName, _T("ab"));
if(pfile == NULL){
AfxMessageBox(_T("Open log file failed."));
return NULL;
}
}
if(pfile != NULL)
m_bLogFileOpened = TRUE;
return pfile;
}
void CreateFileName(){
SYSTEMTIME st;
GetLocalTime(&st);
CString path = GetPrivateLogPath();
CreateDirectory(path, NULL);
wsprintf(g_szFileName, _T("%s\\%04d-%02d-%02d-w%d-%02d-%02d-%02d"), path,
st.wYear, st.wMonth, st.wDay, (st.wDayOfWeek != 0) ? st.wDayOfWeek : 7,
st.wHour, st.wMinute, st.wSecond);
lstrcat(g_szFileName, _T(".log"));
}
void CloseLogFile(){
if(m_pLogFile != NULL){
fflush(m_pLogFile);
fclose(m_pLogFile);
m_pLogFile = NULL;
}
}
BOOL OpenConsole(BOOL bOpen){
static FILE *pConsole = NULL;
if(bOpen){
if(!m_bConsoleOpened){
if(AllocConsole()){
SetConsoleTitle(_T("output"));
/*COORD coord;
coord.X = 120;
coord.Y = 60;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
FillConsoleOutputAttribute(hStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
120, coord, NULL);
SetConsoleScreenBufferSize(hStdOut, coord);
SMALL_RECT se;
se.Left = 0;
se.Top = 0;
se.Right = 100;
se.Bottom = 100;
SetConsoleWindowInfo(hStdOut, FALSE, &se);
*/
_tfreopen_s(&pConsole, _T("CONOUT$"), _T("w"), stdout);
m_bConsoleOpened = TRUE;
}
}
return m_bConsoleOpened;
}else{
if(m_bConsoleOpened){
fclose(pConsole);
if(FreeConsole()){
m_bConsoleOpened = FALSE;
}
}
return !m_bConsoleOpened;
}
}
LPCTSTR GetLastTimeLogFileName(){
return g_szFileName;
}
};
//CRITICAL_SECTION CLog::m_cs;
//int CLog::m_nRef = 0;
//char CLog::m_ansiBuf[MAX_OUTPUT_LEN] = {0};
//CLock CLog::m_lockForSingleton;
#endif // !defined(AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_)
// Log.h: interface for the CLog class.
//
//////////////////////////////////////////////////////////////////////
/*
日志类CLog
简述:单例的日志输出类,使用时会自动构造,但因为要保证日志类最后一个析构,需要手动释放
功能:写日志,输出TRACE内容到dbgview,console。
使用:
使用函数
static void SetOutputLogFileName(LPCTSTR szFileName);
static void SetOutputConsole(BOOL bFlag);
static void SetOutputDbgView(BOOL bFlag);
static void SetOutputLogFile(BOOL bFlag);
来设置输出日志的方式
使用TRACE或使用函数static void WriteLog(const char* format, ...);写日志,可接受可变参数
释放:在程序退出时(ExitInstance()函数的最后)调用CLog::Release()释放引用计数
*/
#if !defined(AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_)
#define AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
//#include "dbghlpapi.h"
#ifdef TRACE
#undef TRACE
#define TRACE CLog::WriteLog
#endif
/*
#define TRACEFUNCNAME \
CString str = _T("");\
INIT_SYM(TRUE);\
str.Format("File %s, line %d, funcname %s\n", __FILE__, __LINE__, __FUNCNAME__);\
OutputDebugString(str);\
UNINIT_SYM();\
*/
#define MAX_OUTPUT_LEN 4096
#define MAX_FILE_LEN 1024 * 1024 * 10
static TCHAR g_szFileName[1024] = {0};
class CLog
{
private:
double exe_time;
LARGE_INTEGER freq;
LARGE_INTEGER start_t, stop_t;
BOOL m_bOutputLogFile;
BOOL m_bOutputDbgView;
BOOL m_bOutputConsole;
BOOL m_bConsoleOpened, m_bDbgviewOpened, m_bLogFileOpened;
BOOL m_bRunning;
FILE *m_pLogFile;
PROCESS_INFORMATION pi;
static CRITICAL_SECTION m_cs;
static CLock m_lockForSingleton;
static char m_ansiBuf[MAX_OUTPUT_LEN];
public:
static CString GetPrivateLogPath(){
static CString strPrivateMapFolder = _T("");
static BOOL b = TRUE;
if(b){
strPrivateMapFolder.Format(_T("%s\\Log"), GetModuleFilePath());
b = FALSE;
}
return strPrivateMapFolder;
}
static CLog* GetInstance(){
m_lockForSingleton.Lock();
static CLog log;
m_lockForSingleton.UnLock();
return &log;
}
~CLog(){
m_bRunning = FALSE;
//WaitTillThreadExited(m_handle);
if(m_bConsoleOpened)
OpenConsole(FALSE);
if(m_bDbgviewOpened)
OpenDbgview(FALSE);
if(m_bLogFileOpened)
CloseLogFile();
DeleteCriticalSection(&m_cs);
#ifdef _DEBUG
TCHAR buf[1024], out[1024];
wsprintf(buf, _T("%s\n"), _T("CLog::~CLog() destruction"));
FormatBuf(buf, out, 1024);
OutputDebugString(out);
#endif
}
static void SetOutputLogFileName(LPCTSTR szLastName){
lstrcpy(g_szFileName, szLastName);
}
static void SetOutputConsole(BOOL bFlag){
CLog* plog = CLog::GetInstance();
plog->m_bOutputConsole = bFlag;
plog->OpenConsole(bFlag);
}
static void SetOutputDbgView(BOOL bFlag){
CLog* plog = CLog::GetInstance();
plog->m_bOutputDbgView = bFlag;
//plog->OpenDbgview(bFlag);
}
static void SetOutputLogFile(BOOL bFlag){
CLog* plog = CLog::GetInstance();
plog->m_bOutputLogFile = bFlag;
if(plog->m_bOutputLogFile && lstrlen(g_szFileName) == 0)
plog->CreateFileName();
}
static void WriteLog(const TCHAR* format, ...) {
try{
CLog* plog = CLog::GetInstance();
if(plog == NULL)
return;
CLocalLock lock(&m_cs);
if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) {
static TCHAR buf[MAX_OUTPUT_LEN], output[MAX_OUTPUT_LEN], *p;
p = buf;
va_list args;
va_start(args, format);
p += _vsntprintf(p, sizeof (buf) - 1, format, args);
va_end(args);
while ( (p > buf) && isspace(*(p-1)) )
*--p = _T('\0');
*p++ = _T('\r');
*p++ = _T('\n');
*p = _T('\0');
WORD out_len = plog->FormatBuf(buf, output);
plog->Output(output, out_len);
}
}
catch(...){
AfxMessageBox(_T("CLog::WriteLog"));
ASSERT(0);
}
}
static void WriteLogW(const wchar_t* format, ...) {
try{
CLog* plog = CLog::GetInstance();
if(plog == NULL)
return;
CLocalLock lock(&m_cs);
if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) {
static wchar_t buf[MAX_OUTPUT_LEN], *p;
p = buf;
va_list args;
va_start(args, format);
p += _vsnwprintf(p, sizeof (buf) - 1, format, args);
va_end(args);
while ( (p > buf) && iswspace(*(p-1)) )
*--p = '\0';
*p++ = '\r';
*p++ = '\n';
*p = '\0';
#if defined(_UNICODE) || defined(UNICODE)
wchar_t output[MAX_OUTPUT_LEN];
WORD out_len = plog->FormatBuf(buf, output);
plog->Output(output, out_len);
#else
char *aBuf = UnicodeToAnsi(buf);
char output[MAX_OUTPUT_LEN];
WORD out_len = plog->FormatBuf(aBuf, output);
plog->Output(output, out_len);
SAFEDELETEARR(aBuf);
#endif
}
}
catch(...){
AfxMessageBox(_T("CLog::WriteLog"));
ASSERT(0);
}
}
static void WriteLogA(const char* format, ...) {
try{
CLog* plog = CLog::GetInstance();
if(plog == NULL)
return;
CLocalLock lock(&m_cs);
if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) {
static char buf[MAX_OUTPUT_LEN], *p;
p = buf;
va_list args;
va_start(args, format);
p += _vsnprintf(p, sizeof (buf) - 1, format, args);
va_end(args);
while ( (p > buf) && isspace(*(p-1)) )
*--p = '\0';
*p++ = '\r';
*p++ = '\n';
*p = '\0';
#if defined(_UNICODE) || defined(UNICODE)
wchar_t *wBuf = AnsiToUtf16(buf);
wchar_t output[MAX_OUTPUT_LEN];
WORD out_len = plog->FormatBuf(wBuf, output);
plog->Output(output, out_len);
SAFEDELETEARR(wBuf);
#else
char output[MAX_OUTPUT_LEN];
WORD out_len = plog->FormatBuf(buf, output);
plog->Output(output, out_len);
#endif
}
}
catch(...){
AfxMessageBox(_T("CLog::WriteLog"));
ASSERT(0);
}
}
protected:
CLog(): m_pLogFile(NULL), m_bOutputLogFile(FALSE), m_bOutputDbgView(FALSE),
m_bOutputConsole(FALSE), m_bConsoleOpened(FALSE), m_bDbgviewOpened(FALSE),
m_bLogFileOpened(FALSE), m_bRunning(TRUE){
QueryPerformanceFrequency(&freq);
//OutputDebugString(_T("The frequency of your pc is 0x%X.\n"), freq.QuadPart);
QueryPerformanceCounter(&start_t);
memset(g_szFileName, 0, sizeof(g_szFileName));
TCHAR out[128] = {0};
wsprintf(out, _T("CLog construction addr: %p\n"), this);
OutputDebugString(out);
memset(&pi, 0, sizeof(pi));
InitializeCriticalSection(&m_cs);
}
LPCTSTR GetAppRunTime() {
static TCHAR szTime[128];
memset(szTime, 0, sizeof szTime);
QueryPerformanceCounter(&stop_t);
exe_time = 1e3*(stop_t.QuadPart-start_t.QuadPart)/freq.QuadPart;
WORD day, hour, min, sec;
sec = static_cast<unsigned short>(static_cast<unsigned long>(exe_time / 1000) % 60);
min = static_cast<unsigned short>(static_cast<unsigned long>(exe_time / 1000 / 60) % 60);
hour = static_cast<unsigned short>((exe_time / 1000 / 60) / 60);
day = static_cast<unsigned short>(exe_time / 1000.0 / 60.0 / 60.0 / 24);
double ms = exe_time - (int)(exe_time) + ((int)(exe_time) % 1000);
wsprintf(szTime, _T("%dday%02d:%02d:%02ds.%3.6fms"), day, hour, min, sec, ms);
return szTime;
}
void Output(const TCHAR *out, WORD out_len) {
if(m_bOutputLogFile){
OutputFile(out, out_len);
}
#ifdef _DEBUG
OutputDebugString(out);
#else
if(m_bOutputDbgView){
OutputDebugString(out);
}
#endif
if(m_bOutputConsole){
_tprintf(out);
}
}
WORD FormatBuf(const TCHAR *oldBuf, TCHAR *newBuf, WORD max_new_buff_len = MAX_OUTPUT_LEN){
static SYSTEMTIME st;
static TCHAR sztime[128];
memset(sztime, 0, sizeof sztime);
GetLocalTime(&st);
wsprintf(sztime, _T("HB %04d-%02d-%02d-w%d %02d:%02d:%02d.%03d ---- "),
st.wYear, st.wMonth, st.wDay, (st.wDayOfWeek != 0) ? st.wDayOfWeek : 7,
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
lstrcpy(newBuf, sztime);
if(lstrlen(sztime) + lstrlen(oldBuf) < max_new_buff_len){
lstrcat(newBuf, oldBuf);
}
return static_cast<unsigned short>(lstrlen(newBuf));
}
void OutputFile(const TCHAR *buf, WORD buf_len){
if(m_pLogFile == NULL)
m_pLogFile = OpenLogFile();
if(m_pLogFile != NULL){
fseek(m_pLogFile, 0, SEEK_END);
long filelen = ftell(m_pLogFile);
if(filelen >= MAX_FILE_LEN){
fclose(m_pLogFile);
CreateFileName();
m_pLogFile = OpenLogFile();
}
}
if(m_pLogFile != NULL){
fseek(m_pLogFile, 0, SEEK_END);
#if defined(_UNICODE) || defined(UNICODE)
if(UnicodeToAnsiUseCharArray(buf, m_ansiBuf, MAX_OUTPUT_LEN)){
fwrite(m_ansiBuf, 1, strlen(m_ansiBuf), m_pLogFile);
}else{
char *ansiOut = UnicodeToAnsi(buf);
fwrite(ansiOut, 1, strlen(ansiOut), m_pLogFile);
SAFEDELETEARR(ansiOut);
}
#else
fwrite(buf, 1, buf_len, m_pLogFile);
#endif
fflush(m_pLogFile);
}
}
BOOL OpenDbgview(BOOL bOpen){
static STARTUPINFO si;
static LPCTSTR lpszAppName = _T("dbgview.exe");
if(bOpen){
if(!m_bDbgviewOpened){
memset(&si,0,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
if(CreateProcess(lpszAppName,NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)) {
m_bDbgviewOpened = TRUE;
}
}
}else if(m_bDbgviewOpened){
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
m_bDbgviewOpened = FALSE;
}
return bOpen ? m_bDbgviewOpened : !m_bDbgviewOpened;
}
FILE* OpenLogFile(){
FILE *pfile = NULL;
pfile = _tfopen(g_szFileName, _T("r"));
if(pfile == NULL){
pfile = _tfopen(g_szFileName, _T("wb"));
if(pfile == NULL){
AfxMessageBox(_T("Create log file failed."));
return NULL;
}
}else{
fclose(pfile);
pfile = _tfopen(g_szFileName, _T("ab"));
if(pfile == NULL){
AfxMessageBox(_T("Open log file failed."));
return NULL;
}
}
if(pfile != NULL)
m_bLogFileOpened = TRUE;
return pfile;
}
void CreateFileName(){
SYSTEMTIME st;
GetLocalTime(&st);
CString path = GetPrivateLogPath();
CreateDirectory(path, NULL);
wsprintf(g_szFileName, _T("%s\\%04d-%02d-%02d-w%d-%02d-%02d-%02d"), path,
st.wYear, st.wMonth, st.wDay, (st.wDayOfWeek != 0) ? st.wDayOfWeek : 7,
st.wHour, st.wMinute, st.wSecond);
lstrcat(g_szFileName, _T(".log"));
}
void CloseLogFile(){
if(m_pLogFile != NULL){
fflush(m_pLogFile);
fclose(m_pLogFile);
m_pLogFile = NULL;
}
}
BOOL OpenConsole(BOOL bOpen){
static FILE *pConsole = NULL;
if(bOpen){
if(!m_bConsoleOpened){
if(AllocConsole()){
SetConsoleTitle(_T("output"));
/*COORD coord;
coord.X = 120;
coord.Y = 60;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
FillConsoleOutputAttribute(hStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
120, coord, NULL);
SetConsoleScreenBufferSize(hStdOut, coord);
SMALL_RECT se;
se.Left = 0;
se.Top = 0;
se.Right = 100;
se.Bottom = 100;
SetConsoleWindowInfo(hStdOut, FALSE, &se);
*/
pConsole = _tfreopen(_T("CONOUT$"), _T("w"), stdout);
m_bConsoleOpened = TRUE;
}
}
return m_bConsoleOpened;
}else{
if(m_bConsoleOpened){
fclose(pConsole);
if(FreeConsole()){
m_bConsoleOpened = FALSE;
}
}
return !m_bConsoleOpened;
}
}
LPCTSTR GetLastTimeLogFileName(){
return g_szFileName;
}
};
CRITICAL_SECTION CLog::m_cs;
//int CLog::m_nRef = 0;
char CLog::m_ansiBuf[MAX_OUTPUT_LEN] = {0};
CLock CLog::m_lockForSingleton;
#endif // !defined(AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_)
#pragma comment(lib, "USER32" )
#include <crtdbg.h>
#include <tchar.h>
#pragma comment(lib, "version.lib")
#define MTVERIFY(a) (a)
#if 0
#define SHOWERROR(a) PrintError(a,__FILE__,__LINE__,GetLastError())
#ifdef DEBUG
#define MTASSERT(a) _ASSERTE(a)
#define MTVERIFY(a) if (!(a)) PrintError(#a,__FILE__,__LINE__,GetLastError())
#else /*DEBUG*/
#define MTASSERT(a)
#define MTVERIFY(a) (a)
#endif /*DEBUG*/
__inline void PrintError(LPCTSTR linedesc, LPCTSTR filename, int lineno, DWORD errnum)
{
LPTSTR lpBuffer;
TCHAR errbuf[256];
#ifdef _WINDOWS_
TCHAR modulename[MAX_PATH];
#else //_WINDOWS_
DWORD numread;
#endif //_WINDOWS_
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errnum,
LANG_NEUTRAL,
(LPTSTR)&lpBuffer,
0,
NULL
);
_stprintf(errbuf, _T("\nThe following call failed at line %d in %s:\n\n %s\n\nReason: %s\n"),
lineno, filename, linedesc, lpBuffer);
#ifndef _WINDOWS_
WriteFile(GetStdHandle(STD_ERROR_HANDLE), errbuf, lstrlen(errbuf), &numread, FALSE );
Sleep(3000);
#else //_WINDOWS_
GetModuleFileName(NULL, modulename, MAX_PATH);
MessageBox(NULL, errbuf, modulename, MB_ICONWARNING|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND);
OutputDebugString(errbuf);
#endif //_WINDOWS_
LocalFree(lpBuffer);
//exit(EXIT_FAILURE);
}
#endif
__inline void WaitTillThreadActive(HANDLE &handle)
{
if(handle != INVALID_HANDLE_VALUE)
{
BOOL rc = FALSE;
DWORD ec = 0;
for(;;)
{
rc = GetExitCodeThread(handle, &ec);
if(rc && (ec == STILL_ACTIVE))
break;
Sleep(500);
}
}
}
// obsolete
/*
__inline void WaitTillThreadExited(HANDLE &handle, DWORD dwMilliseconds = 3000)
{
if(handle != INVALID_HANDLE_VALUE)
{
BOOL rc = FALSE;
DWORD ec = 0;
DWORD dwStart = GetTickCount();
for(;;)
{
if(GetTickCount() - dwStart >= dwMilliseconds)
{
::TerminateThread(handle, 0xffffffff);
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
break;
}
if(handle == INVALID_HANDLE_VALUE)
break;
rc = GetExitCodeThread(handle, &ec);
if(rc && (ec != STILL_ACTIVE))
{
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
break;
}
Sleep(500);
}
}
}
*/
__forceinline void WaitTillThreadExited(HANDLE &handle, DWORD dwMilliseconds = 5000)
{
if(handle == INVALID_HANDLE_VALUE)
return;
DWORD ret = ::WaitForSingleObject(handle, dwMilliseconds);
if(ret != WAIT_OBJECT_0)
{ //::TerminateThread(handle, 0xffffffff);
}
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
}
__inline LPTSTR GetModuleFilePath()
{
static TCHAR szPath[MAX_PATH] = {'\0'};
if(lstrlen(szPath) != 0)
return szPath;
TCHAR szExe[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), szExe, MAX_PATH);
int len = lstrlen(szExe);
for(int i = len; i > 0; i--)
{
if(szExe[i] == '\\')
{
szExe[i] = '\0';
lstrcpy(szPath, szExe);
break;
}
}
return szPath;
}
/////////*********************自定义宏********************////////////////////////
//安全删除普通堆内存
#define SAFEDELETEP(p) {if(p){delete (p); (p)=NULL;}}
#define SAFEDELETEARR(pArr) {if((pArr)){delete[] (pArr); (pArr)=NULL;}}
//安全删除对话框类堆内存
#define SAFEDELETEDLG(pDlg) {if((pDlg)){(pDlg)->DestroyWindow(); delete (pDlg); (pDlg)=NULL;}}
//关闭核心对象句柄
#define CLOSEHANDLE(h){\
if(h != INVALID_HANDLE_VALUE)\
{ \
::CloseHandle(h); \
h = INVALID_HANDLE_VALUE;\
} \
}
#define CLOSESOCKET(s){\
if(s != INVALID_SOCKET)\
{ \
::closesocket(s); \
s = INVALID_SOCKET;\
} \
}
#ifdef _DEBUG
#define VERIFYPTR(p, cb) {MTVERIFY(!IsBadWritePtr(p, cb));}
#else
#define VERIFYPTR(p, cb)
#endif
/**************UNICODE-ANSI mutually transform**************************/
// need be manuly delete
__forceinline LPSTR Utf16ToAnsi(const wchar_t * const wSrc)
{
char* pElementText;
int iTextLen;
iTextLen = WideCharToMultiByte( CP_ACP, 0, wSrc, -1, NULL,0, NULL, NULL );
pElementText = new char[iTextLen + 1];
memset( ( void* )pElementText, 0, sizeof( char ) * ( iTextLen + 1 ) );
::WideCharToMultiByte( CP_ACP,0,wSrc,-1,pElementText,iTextLen,NULL,NULL);
return pElementText;
}
__forceinline BOOL Utf16ToAnsiUseCharArray(const wchar_t * const wSrc,
char *ansiArr, WORD ansiArrLen)
{
int iTextLen = WideCharToMultiByte( CP_ACP, 0, wSrc, -1, NULL,0, NULL, NULL );
if(iTextLen < ansiArrLen){
memset( ( void* )ansiArr, 0, sizeof( char ) * ( iTextLen + 1 ) );
::WideCharToMultiByte(CP_ACP,0,wSrc,-1,ansiArr,iTextLen,NULL,NULL);
return TRUE;
}
return FALSE;
}
__forceinline wchar_t* AnsiToUtf16(PCSTR ansiSrc)
{
wchar_t *pWide;
int iUnicodeLen = ::MultiByteToWideChar( CP_ACP,
0, ansiSrc, -1, NULL, 0);
pWide = new wchar_t[iUnicodeLen + 1];
memset(pWide, 0, (iUnicodeLen+1) * sizeof(wchar_t) );
::MultiByteToWideChar( CP_ACP, 0, ansiSrc, -1, (LPWSTR)pWide, iUnicodeLen );
return pWide;
}
__forceinline BOOL AnsiToUtf16Array(PCSTR ansiSrc, wchar_t* warr, int warr_len)
{
int iUnicodeLen = ::MultiByteToWideChar( CP_ACP,
0, ansiSrc, -1, NULL, 0);
if(warr_len >= iUnicodeLen){
memset(warr, 0, (iUnicodeLen+1) * sizeof(wchar_t) );
::MultiByteToWideChar( CP_ACP, 0, ansiSrc, -1, (LPWSTR)warr, iUnicodeLen );
return TRUE;
}
return FALSE;
}
__forceinline wchar_t* Utf8ToUtf16(PCSTR ansiSrc)
{
wchar_t *pWide;
int iUnicodeLen = ::MultiByteToWideChar( CP_UTF8,
0, ansiSrc, -1, NULL, 0);
pWide = new wchar_t[iUnicodeLen + 1];
memset(pWide, 0, (iUnicodeLen+1) * sizeof(wchar_t) );
::MultiByteToWideChar( CP_UTF8, 0, ansiSrc, -1, (LPWSTR)pWide, iUnicodeLen );
return pWide;
}
/*
__forceinline CString GetFileVersion()
{
int iVerInfoSize;
char *pBuf;
CString asVer= _T("");
VS_FIXEDFILEINFO *pVsInfo;
unsigned int iFileInfoSize = sizeof(VS_FIXEDFILEINFO);
TCHAR fileName[MAX_PATH];
GetModuleFileName(NULL, fileName, MAX_PATH);
iVerInfoSize = GetFileVersionInfoSize(fileName, NULL);
if(iVerInfoSize != 0){
pBuf = new char[iVerInfoSize];
if(GetFileVersionInfo(fileName, 0, iVerInfoSize, pBuf)){
if(VerQueryValue(pBuf, _T("//"), (void**)&pVsInfo, &iFileInfoSize)){
asVer.Format(_T("%d.%d.%d.%d"),
HIWORD(pVsInfo->dwFileVersionMS),
LOWORD(pVsInfo->dwFileVersionMS),
HIWORD(pVsInfo->dwFileVersionLS),
LOWORD(pVsInfo->dwFileVersionLS));
}
}
delete pBuf;
}
return asVer;
}
*/
\ No newline at end of file
#pragma comment( lib, "USER32" )
#include <crtdbg.h>
#include <tchar.h>
#define MTVERIFY(a) (a)
#if 0
#define SHOWERROR(a) PrintError(a,__FILE__,__LINE__,GetLastError())
#ifdef DEBUG
#define MTASSERT(a) _ASSERTE(a)
#define MTVERIFY(a) if (!(a)) PrintError(#a,__FILE__,__LINE__,GetLastError())
#else /*DEBUG*/
#define MTASSERT(a)
#define MTVERIFY(a) (a)
#endif /*DEBUG*/
__inline void PrintError(LPCTSTR linedesc, LPCTSTR filename, int lineno, DWORD errnum)
{
LPTSTR lpBuffer;
TCHAR errbuf[256];
#ifdef _WINDOWS_
TCHAR modulename[MAX_PATH];
#else //_WINDOWS_
DWORD numread;
#endif //_WINDOWS_
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errnum,
LANG_NEUTRAL,
(LPTSTR)&lpBuffer,
0,
NULL
);
_stprintf(errbuf, _T("\nThe following call failed at line %d in %s:\n\n %s\n\nReason: %s\n"),
lineno, filename, linedesc, lpBuffer);
#ifndef _WINDOWS_
WriteFile(GetStdHandle(STD_ERROR_HANDLE), errbuf, lstrlen(errbuf), &numread, FALSE );
Sleep(3000);
#else //_WINDOWS_
GetModuleFileName(NULL, modulename, MAX_PATH);
MessageBox(NULL, errbuf, modulename, MB_ICONWARNING|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND);
OutputDebugString(errbuf);
#endif //_WINDOWS_
LocalFree(lpBuffer);
//exit(EXIT_FAILURE);
}
#endif
__inline void ExitError(LPCTSTR error)
{
//SHOWERROR(error);
ExitProcess(0);
}
__inline void WaitTillThreadActive(HANDLE &handle)
{
if(handle != INVALID_HANDLE_VALUE)
{
BOOL rc = FALSE;
DWORD ec = 0;
for(;;)
{
rc = GetExitCodeThread(handle, &ec);
if(rc && (ec == STILL_ACTIVE))
break;
Sleep(500);
}
}
}
// obsolete
/*
__inline void WaitTillThreadExited(HANDLE &handle, DWORD dwMilliseconds = 3000)
{
if(handle != INVALID_HANDLE_VALUE)
{
BOOL rc = FALSE;
DWORD ec = 0;
DWORD dwStart = GetTickCount();
for(;;)
{
if(GetTickCount() - dwStart >= dwMilliseconds)
{
::TerminateThread(handle, 0xffffffff);
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
break;
}
if(handle == INVALID_HANDLE_VALUE)
break;
rc = GetExitCodeThread(handle, &ec);
if(rc && (ec != STILL_ACTIVE))
{
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
break;
}
Sleep(500);
}
}
}
*/
__forceinline void WaitTillThreadExited(HANDLE &handle, DWORD dwMilliseconds = 5000)
{
if(handle == INVALID_HANDLE_VALUE)
return;
DWORD ret = ::WaitForSingleObject(handle, dwMilliseconds);
if(ret != WAIT_OBJECT_0)
{ ::TerminateThread(handle, 0xffffffff); }
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
}
__inline LPTSTR GetModuleFilePath()
{
static TCHAR szPath[MAX_PATH] = {'\0'};
if(lstrlen(szPath) != 0)
return szPath;
TCHAR szExe[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), szExe, MAX_PATH);
int len = lstrlen(szExe);
for(int i = len; i > 0; i--)
{
if(szExe[i] == '\\')
{
szExe[i] = '\0';
lstrcpy(szPath, szExe);
break;
}
}
return szPath;
}
/////////*********************自定义宏********************////////////////////////
//安全删除普通堆内存
#define SAFEDELETEP(p) {if(p){delete (p); (p)=NULL;}}
#define SAFEDELETEARR(pArr) {if((pArr)){delete[] (pArr); (pArr)=NULL;}}
//安全删除对话框类堆内存
#define SAFEDELETEDLG(pDlg) {if((pDlg)){(pDlg)->DestroyWindow(); delete (pDlg); (pDlg)=NULL;}}
//关闭核心对象句柄
#define CLOSEHANDLE(h){\
if(h != INVALID_HANDLE_VALUE)\
{ \
::CloseHandle(h); \
h = INVALID_HANDLE_VALUE;\
} \
}
#define CLOSESOCKET(s){\
if(s != INVALID_SOCKET)\
{ \
::closesocket(s); \
s = INVALID_SOCKET;\
} \
}
#ifdef _DEBUG
#define VERIFYPTR(p, cb) {MTVERIFY(!IsBadWritePtr(p, cb));}
#else
#define VERIFYPTR(p, cb)
#endif
/**************UNICODE-ANSI mutually transform**************************/
// need be manuly delete
__forceinline LPSTR UnicodeToAnsi(const wchar_t * const wSrc)
{
char* pElementText;
int iTextLen;
iTextLen = WideCharToMultiByte( CP_ACP, 0, wSrc, -1, NULL,0, NULL, NULL );
pElementText = new char[iTextLen + 1];
memset( ( void* )pElementText, 0, sizeof( char ) * ( iTextLen + 1 ) );
::WideCharToMultiByte( CP_ACP,0,wSrc,-1,pElementText,iTextLen,NULL,NULL);
return pElementText;
}
__forceinline BOOL UnicodeToAnsiUseCharArray(const wchar_t * const wSrc, char *ansiArr, WORD ansiArrLen)
{
int iTextLen = WideCharToMultiByte( CP_ACP, 0, wSrc, -1, NULL,0, NULL, NULL );
if(iTextLen < ansiArrLen)
{
memset( ( void* )ansiArr, 0, sizeof( char ) * ( iTextLen + 1 ) );
::WideCharToMultiByte(CP_ACP,0,wSrc,-1,ansiArr,iTextLen,NULL,NULL);
return TRUE;
}
return FALSE;
}
__forceinline wchar_t* AnsiToUtf16(PCSTR ansiSrc)
{
wchar_t *pWide;
int iUnicodeLen = ::MultiByteToWideChar( CP_ACP,
0, ansiSrc, -1, NULL, 0);
pWide = new wchar_t[iUnicodeLen + 1];
memset(pWide, 0, (iUnicodeLen+1) * sizeof(wchar_t) );
::MultiByteToWideChar( CP_ACP, 0, ansiSrc, -1, (LPWSTR)pWide, iUnicodeLen );
return pWide;
}
__forceinline wchar_t* Utf8ToUtf16(PCSTR ansiSrc)
{
wchar_t *pWide;
int iUnicodeLen = ::MultiByteToWideChar( CP_UTF8,
0, ansiSrc, -1, NULL, 0);
pWide = new wchar_t[iUnicodeLen + 1];
memset(pWide, 0, (iUnicodeLen+1) * sizeof(wchar_t) );
::MultiByteToWideChar( CP_UTF8, 0, ansiSrc, -1, (LPWSTR)pWide, iUnicodeLen );
return pWide;
}
// PointerList.cpp: implementation of the CPointerList class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "PointerList.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CPointerList::CPointerList()
{
m_pHead = m_pTail = NULL;
m_nCount = 0;
}
CPointerList::~CPointerList()
{
RemoveAll();
}
void CPointerList::AddTail(void *element)
{
PELEMENT pTmp = new ELEMENT;
pTmp->element = element;
if(m_pHead == NULL)
{
m_pHead = m_pTail = pTmp;
}
else
{
pTmp->pPrev = m_pTail;
m_pTail->pNext = pTmp;
m_pTail = pTmp;
}
m_nCount++;
int * p = (int*)&m_pHead;
int *p2 = &m_nCount;
}
void CPointerList::AddHead(void *element)
{
PELEMENT pTmp = new ELEMENT;
pTmp->element = element;
if(m_pHead == NULL)
{
m_pHead = m_pTail = pTmp;
}
else
{
pTmp->pNext = m_pHead;
m_pHead->pPrev = pTmp;
m_pHead = pTmp;
}
m_nCount++;
}
void CPointerList::RemoveAt(int index)
{
if(m_nCount > 0 && index >= 0 && index < m_nCount)
{
PELEMENT pCur = GetElementAt(index);
if(pCur)
{
PELEMENT pPrev = pCur->pPrev;
PELEMENT pNext = pCur->pNext;
delete pCur;
pCur = NULL;
if(pPrev)
pPrev->pNext = pNext;
else
m_pHead = pNext;
if(pNext)
pNext->pPrev = pPrev;
else
m_pTail = pPrev;
m_nCount--;
}
}
}
void CPointerList::RemoveHead()
{
if(m_nCount > 0)
{
PELEMENT pTmp = m_pHead->pNext;
delete m_pHead;
if(pTmp)
pTmp->pPrev = NULL;
m_pHead = pTmp;
m_nCount--;
}
}
void CPointerList::RemoveTail()
{
if(m_nCount > 0)
{
PELEMENT pTmp = m_pTail->pPrev;
delete m_pTail;
if(pTmp)
pTmp->pNext = NULL;
m_pTail = pTmp;
m_nCount--;
}
}
void CPointerList::RemoveAll()
{
if(m_nCount > 0)
{
PELEMENT pTmp = m_pHead;
while(pTmp)
{
m_pHead = pTmp;
pTmp = pTmp->pNext;
delete m_pHead;
}
m_pHead = m_pTail = NULL;
m_nCount = 0;
}
}
void CPointerList::InsertAt(int index, void *element)
{
PELEMENT pCur = GetElementAt(index);
if(pCur)
{
PELEMENT pNew = new ELEMENT;
pNew->element = element;
PELEMENT pPrev = pCur->pPrev;
if(pPrev)
{
pNew->pPrev = pPrev;
pPrev->pNext = pNew;
}
else
{
m_pHead = pNew;
}
pNew->pNext = pCur;
pCur->pPrev = pNew;
m_nCount++;
}
else
{
AddHead(element);
}
}
int CPointerList::GetCount()
{
return m_nCount;
}
void *CPointerList::GetHead()
{
return m_pHead->element;
}
void *CPointerList::GetTail()
{
return m_pTail->element;
}
void *CPointerList::GetNext(void *element)
{
PELEMENT pTmp = FindElement(element);
if(pTmp)
{
pTmp = pTmp->pNext;
}
return pTmp ? pTmp->element : NULL;
}
void *CPointerList::GetPrev(void *element)
{
PELEMENT pTmp = FindElement(element);
if(pTmp)
{
pTmp = pTmp->pPrev;
}
return pTmp ? pTmp->element : NULL;
}
int CPointerList::Find(void *element)
{
bool find = false;
int index = 0;
if(m_nCount > 0)
{
PELEMENT pTmp = m_pHead;
while(pTmp)
{
if(pTmp->element == element)
{
find = true;
break;
}
pTmp = pTmp->pNext;
index++;
}
}
return find ? index : -1;
}
PELEMENT CPointerList::GetElementAt(int index)
{
PELEMENT pTmp = NULL;
if(m_nCount > 0 && index >= 0 && index < m_nCount)
{
pTmp = m_pHead;
int i = 0;
while(i < index)
{
pTmp = pTmp->pNext;
i++;
}
}
return pTmp;
}
void* CPointerList::GetAt(int index)
{
PELEMENT pTmp = NULL;
if(m_nCount > 0 && index >= 0 && index < m_nCount)
{
pTmp = m_pHead;
int i = 0;
while(i < index && pTmp)
{
pTmp = pTmp->pNext;
i++;
}
if(i == index && pTmp)
return pTmp->element;
}
return NULL;
}
PELEMENT CPointerList::FindElement(void *element)
{
bool find = false;
PELEMENT pTmp = NULL;
if(m_nCount > 0)
{
pTmp = m_pHead;
while(pTmp)
{
if(pTmp->element == element)
{
find = true;
break;
}
pTmp = pTmp->pNext;
}
}
return find ? pTmp : NULL;
}
// PointerList.h: interface for the CPointerList class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_POINTERLIST_H__3FB7FEA8_DAE8_468B_9494_92CA39593E1A__INCLUDED_)
#define AFX_POINTERLIST_H__3FB7FEA8_DAE8_468B_9494_92CA39593E1A__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
//自定义存取指针的链表,不需要手动回收指针(删除节点时自动删除指针所指向的内存)
class CPointerList
{
typedef struct _ELEMENT
{
void *element;
_ELEMENT *pNext;
_ELEMENT *pPrev;
_ELEMENT() : element(NULL), pNext(NULL), pPrev(NULL) { }
}ELEMENT, *PELEMENT;
public:
CPointerList():m_pHead(NULL), m_pTail(NULL), m_nCount(0) { }
virtual ~CPointerList() {RemoveAll();}
public:
void* GetAt(int index){
PELEMENT pTmp = NULL;
if(m_nCount > 0 && index >= 0 && index < m_nCount){
pTmp = m_pHead;
int i = 0;
while(i < index && pTmp){
pTmp = pTmp->pNext;
i++;
}
if(i == index && pTmp)
return pTmp->element;
}
return NULL;
}
int Find(void *element){
bool find = false;
int index = 0;
if(m_nCount > 0){
PELEMENT pTmp = m_pHead;
while(pTmp){
if(pTmp->element == element){
find = true;
break;
}
pTmp = pTmp->pNext;
index++;
}
}
return find ? index : -1;
}
void AddTail(void *element){
PELEMENT pTmp = new ELEMENT;
pTmp->element = element;
if(m_pHead == NULL){
m_pHead = m_pTail = pTmp;
}else{
pTmp->pPrev = m_pTail;
m_pTail->pNext = pTmp;
m_pTail = pTmp;
}
m_nCount++;
}
void AddHead(void *element){
PELEMENT pTmp = new ELEMENT;
pTmp->element = element;
if(m_pHead == NULL){
m_pHead = m_pTail = pTmp;
}else{
pTmp->pNext = m_pHead;
m_pHead->pPrev = pTmp;
m_pHead = pTmp;
}
m_nCount++;
}
void RemoveAt(int index){
if(m_nCount > 0 && index >= 0 && index < m_nCount){
PELEMENT pCur = GetElementAt(index);
if(pCur){
PELEMENT pPrev = pCur->pPrev;
PELEMENT pNext = pCur->pNext;
delete pCur;
pCur = NULL;
if(pPrev)
pPrev->pNext = pNext;
else
m_pHead = pNext;
if(pNext)
pNext->pPrev = pPrev;
else
m_pTail = pPrev;
m_nCount--;
}
}
}
void RemoveHead(){
if(m_nCount > 0){
PELEMENT pTmp = m_pHead->pNext;
delete m_pHead;
if(pTmp)
pTmp->pPrev = NULL;
m_pHead = pTmp;
m_nCount--;
}
}
void RemoveTail(){
if(m_nCount > 0){
PELEMENT pTmp = m_pTail->pPrev;
delete m_pTail;
if(pTmp)
pTmp->pNext = NULL;
m_pTail = pTmp;
m_nCount--;
}
}
void RemoveAll(){
if(m_nCount > 0){
PELEMENT pTmp = m_pHead;
while(pTmp){
m_pHead = pTmp;
pTmp = pTmp->pNext;
delete m_pHead;
}
m_pHead = m_pTail = NULL;
m_nCount = 0;
}
}
void InsertAt(int index, void *element){
PELEMENT pCur = GetElementAt(index);
if(pCur){
PELEMENT pNew = new ELEMENT;
pNew->element = element;
PELEMENT pPrev = pCur->pPrev;
if(pPrev){
pNew->pPrev = pPrev;
pPrev->pNext = pNew;
}else{
m_pHead = pNew;
}
pNew->pNext = pCur;
pCur->pPrev = pNew;
m_nCount++;
}else{
AddHead(element);
}
}
int GetCount() const{
return m_nCount;
}
void *GetHead(){
return m_pHead ? m_pHead->element : NULL;
}
void *GetTail(){
return m_pTail ? m_pTail->element : NULL;
}
void *GetNext(void *element){
PELEMENT pTmp = FindElement(element);
if(pTmp)
pTmp = pTmp->pNext;
return pTmp ? pTmp->element : NULL;
}
void *GetPrev(void *element){
PELEMENT pTmp = FindElement(element);
if(pTmp)
pTmp = pTmp->pPrev;
return pTmp ? pTmp->element : NULL;
}
private:
PELEMENT m_pHead;
PELEMENT m_pTail;
int m_nCount;
protected:
PELEMENT GetElementAt(int index){
PELEMENT pTmp = NULL;
if(m_nCount > 0 && index >= 0 && index < m_nCount){
pTmp = m_pHead;
int i = 0;
while(i < index){
pTmp = pTmp->pNext;
i++;
}
}
return pTmp;
}
PELEMENT FindElement(void *element){
bool find = false;
PELEMENT pTmp = NULL;
if(m_nCount > 0){
pTmp = m_pHead;
while(pTmp){
if(pTmp->element == element){
find = true;
break;
}
pTmp = pTmp->pNext;
}
}
return find ? pTmp : NULL;
}
};
#endif // !defined(AFX_POINTERLIST_H__3FB7FEA8_DAE8_468B_9494_92CA39593E1A__INCLUDED_)
// VsVer.h
#pragma once
/***************************************/
// 为了代码在vc6和vs2010下都能通过编译,定义以下宏
#if _MSC_VER == 1200 // vc6的cl版本号
#define _ultoa_s _ultoa
#define _itoa_s _itoa
#define _ltoa_s _ltoa
#define _gcvt_s(buffer, sizeInBytes, value, digits) _gcvt(value, digits, buffer)
#define sprintf_s sprintf
#define fopen_s(p,f,m) {*p = fopen(f,m);}
#define _tfopen_s(p,f,m) {*p = _tfopen(f,m);}
#define _tfreopen_s(p,t,m,s) {*p = _tfreopen(t,m,s);}
#define _tcscpy_s _tcscpy
#define strcpy_s(dst, ct, src) strcpy(dst, src)
#define _vsntprintf_s(buff, size, count, format, argptr) _vsntprintf(buff, count, format, argptr)
#define _vsnwprintf_s(buff, size, count, format, argptr) _vsnwprintf(buff, count, format, argptr)
#define _vsnprintf_s(buff, size, count, format, argptr) _vsnprintf(buff, count, format, argptr)
//#define _snprintf_s(buff, size, count, format, argptr) _snprintf(buff, count, format, argptr)
//#define _snprintf_s(buff, count, format, argptr) _snprintf(buff, format, argptr)
#define _stprintf_s(buff, format, arg1, arg2) _stprintf(buff, format, arg1, arg2)
#define strcat_s(dst, len, src) strcat(dst, src)
#define _countof sizeof
#endif
/***************************************/
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
tinyxml2 @ cf33e37d
Subproject commit cf33e37d25346d108ef00b3bfa447b9a8f69382f
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment