소스 검색

逻辑大处理;

scbc.sat2 5 년 전
부모
커밋
da1ca2ec64

+ 0 - 10
SATService/SATService.sln

@@ -5,8 +5,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SATService", "SATService\SA
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SATHTTP", "..\SATHTTP\SATHTTP\SATHTTP.vcproj", "{E3798CCE-0392-42B7-A1D0-0D726BA23CA4}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RunPython", "..\RunPython\RunPython\RunPython.vcproj", "{92FC4676-AAFC-4568-93EE-849029E5381D}"
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -31,14 +29,6 @@ Global
 		{E3798CCE-0392-42B7-A1D0-0D726BA23CA4}.DeubgExe|Win32.Build.0 = DebugEXE|Win32
 		{E3798CCE-0392-42B7-A1D0-0D726BA23CA4}.Release|Win32.ActiveCfg = Release|Win32
 		{E3798CCE-0392-42B7-A1D0-0D726BA23CA4}.Release|Win32.Build.0 = Release|Win32
-		{92FC4676-AAFC-4568-93EE-849029E5381D}.Debug|Win32.ActiveCfg = Debug|Win32
-		{92FC4676-AAFC-4568-93EE-849029E5381D}.Debug|Win32.Build.0 = Debug|Win32
-		{92FC4676-AAFC-4568-93EE-849029E5381D}.DebugEXE|Win32.ActiveCfg = Debug|Win32
-		{92FC4676-AAFC-4568-93EE-849029E5381D}.DebugEXE|Win32.Build.0 = Debug|Win32
-		{92FC4676-AAFC-4568-93EE-849029E5381D}.DeubgExe|Win32.ActiveCfg = DeubgExe|Win32
-		{92FC4676-AAFC-4568-93EE-849029E5381D}.DeubgExe|Win32.Build.0 = DeubgExe|Win32
-		{92FC4676-AAFC-4568-93EE-849029E5381D}.Release|Win32.ActiveCfg = Release|Win32
-		{92FC4676-AAFC-4568-93EE-849029E5381D}.Release|Win32.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 84 - 0
SATService/SATService/CritSection.h

@@ -0,0 +1,84 @@
+#ifndef __CRITSECTION_20160221__
+#define __CRITSECTION_20160221__
+
+// ÁÙ½çÖµ;
+class ThreadSection
+{
+public:
+	ThreadSection(){
+		HRESULT hr = Init();
+		(hr);
+	}
+
+	~ThreadSection(){
+		DeleteCriticalSection(&_CriticalSection);
+	}
+
+	bool Lock()
+	{
+		bool result = false;
+		__try
+		{
+			EnterCriticalSection(&_CriticalSection);
+			result = true;
+		}
+		__except (STATUS_NO_MEMORY == GetExceptionCode())
+		{
+		}
+		return result;
+	}
+
+	bool Unlock()
+	{
+		bool result = false;
+		__try
+		{
+			LeaveCriticalSection(&_CriticalSection);
+			result = true;
+		}
+		__except (STATUS_NO_MEMORY == GetExceptionCode())
+		{
+		}
+		return result;
+	}
+
+private:
+	HRESULT Init() throw()
+	{
+		HRESULT hRes = E_FAIL;
+		__try
+		{
+			InitializeCriticalSection(&_CriticalSection);
+			hRes = S_OK;
+		}
+		__except (STATUS_NO_MEMORY == GetExceptionCode())
+		{
+			hRes = E_OUTOFMEMORY;
+		}
+		return hRes;
+	}
+
+	ThreadSection(const ThreadSection & tSection);
+	ThreadSection &operator=(const ThreadSection & tSection);
+	CRITICAL_SECTION _CriticalSection;
+};
+
+
+class AutoThreadSection
+{
+public:
+	AutoThreadSection(IN ThreadSection* pSection){
+		_pSection = pSection;
+		_pSection->Lock();
+	}
+
+	~AutoThreadSection(){
+		_pSection->Unlock();
+	}
+private:
+	AutoThreadSection(const AutoThreadSection & tSection);
+	AutoThreadSection &operator=(const AutoThreadSection & tSection);
+	ThreadSection * _pSection;
+};
+
+#endif //__CRITSECTION_20160221__

+ 119 - 14
SATService/SATService/Global.cpp

@@ -2,15 +2,18 @@
 
 #include "stdafx.h"
 #include "Global.h"
-
+#include <io.h>
+#include <direct.h>
 #include <tlhelp32.h>
 #include <shlwapi.h>
 #include <strsafe.h>
 #include <WinVer.h>		
 #pragma comment(lib,"version.lib")
+#include "CritSection.h"
 
 namespace Global
 {
+	STSATConfig g_stSATConfig;
 	//////////////////////////////////////////////////////////////////////////
 	// 全局变量;
 	TCHAR g_szIniFile[MAX_PATH] = {0};
@@ -30,6 +33,7 @@ namespace Global
 
 	CHAR g_szKeyPath[MAX_PATH];					// 加密狗USB位置;
 
+	ThreadSection g_critSection;
 	//////////////////////////////////////////////////////////////////////////
 	static const int BEGIN_YEAR = 1901;
 	static const int NUMBER_YEAR = 199;
@@ -150,7 +154,7 @@ namespace Global
 		if (szPath != NULL && szIniName != NULL)
 			StringCchPrintf(szIniPath, MAX_PATH, _T("%s%s"), szPath, szIniName);
 		else
-			StringCchPrintf(szIniPath, MAX_PATH, _T("%sServiceInfo.ini"), g_szModulePath);
+			StringCchPrintf(szIniPath, MAX_PATH, _T("%sSATService.ini"), g_szModulePath);
 
 		HANDLE hFile = CreateFile(szIniPath, 0/*GENERIC_READ*/, 0, NULL, OPEN_EXISTING, 0, NULL);
 
@@ -160,20 +164,28 @@ namespace Global
 		}
 
 		// 获取服务器端信息;
-		GetPrivateProfileString(_T("DatabaseInfo"), _T("dbSource"), _T(""), g_szDBSource, MAX_PATH, szIniPath);
-		g_dwDBServerPort = GetPrivateProfileInt(_T("DatabaseInfo"), _T("dbServerPort"), 0, szIniPath);
-		GetPrivateProfileString(_T("DatabaseInfo"), _T("dbAccount"), _T(""), g_szDBAccount, MAX_PATH, szIniPath);
-		GetPrivateProfileString(_T("DatabaseInfo"), _T("dbPassWord"), _T(""), g_szDBPassWord, MAX_PATH, szIniPath);
-		GetPrivateProfileString(_T("DatabaseInfo"), _T("dbName"), _T(""), g_szDBName, MAX_PATH, szIniPath);
+		GetPrivateProfileString(_T("SATService"), _T("ScriptDir"), _T(""), g_stSATConfig.szScriptDir, MAX_PATH, szIniPath);
+		g_stSATConfig.nAdbTimeout = GetPrivateProfileInt(_T("SATService"), _T("AdbTimeout"), 120, szIniPath);
+		GetPrivateProfileString(_T("SATService"), _T("ExecuteServer"), _T(""), g_stSATConfig.szExecuteServer, MAX_PATH, szIniPath);
+		GetPrivateProfileString(_T("SATService"), _T("ExecuteSyscode"), _T(""), g_stSATConfig.szExecuteSysCode, MAX_PATH, szIniPath);
 
-		//g_dwCSPort = GetPrivateProfileInt(_T("NetWorkInfo"), _T("TCPChatPort"), 5678, szIniPath);
+		GetPrivateProfileString(_T("SATService"), _T("ApkServer"), _T(""), g_stSATConfig.szApkServer, MAX_PATH, szIniPath);
+		GetPrivateProfileString(_T("SATService"), _T("ApkSyscode"), _T(""), g_stSATConfig.szApkSysCode, MAX_PATH, szIniPath);
 
-		if (g_dwDBServerPort != 0)
-			StringCchPrintf(g_szConnectString, MAX_PATH, _T("driver={SQL Server};Server=%s,%d;database=%s;uid=%s;pwd=%s"),
-			g_szDBSource, g_dwDBServerPort, g_szDBName, g_szDBAccount, g_szDBPassWord);
-		else
-			StringCchPrintf(g_szConnectString, MAX_PATH, _T("driver={SQL Server};Server=%s;database=%s;uid=%s;pwd=%s"),
-			g_szDBSource, g_szDBName, g_szDBAccount, g_szDBPassWord);
+		GetPrivateProfileString(_T("SATService"), _T("CaseServer"), _T(""), g_stSATConfig.szCaseServer, MAX_PATH, szIniPath);
+		GetPrivateProfileString(_T("SATService"), _T("CaseSyscode"), _T(""), g_stSATConfig.szCaseSysCode, MAX_PATH, szIniPath);
+
+		GetPrivateProfileString(_T("SATService"), _T("TaskServer"), _T(""), g_stSATConfig.szTaskServer, MAX_PATH, szIniPath);
+		GetPrivateProfileString(_T("SATService"), _T("TaskSyscode"), _T(""), g_stSATConfig.szTaskSysCode, MAX_PATH, szIniPath);
+
+		GetPrivateProfileString(_T("SATService"), _T("ResultServer"), _T(""), g_stSATConfig.szResultServer, MAX_PATH, szIniPath);
+
+		GetPrivateProfileString(_T("SATService"), _T("ResultExcelDir"), _T(""), g_stSATConfig.szResultExcelDir, MAX_PATH, szIniPath);
+		
+		GetPrivateProfileString(_T("LinuxDevice"), _T("device"), _T(""), g_stSATConfig.stLinuxDevice.szDevice, MAX_PATH, szIniPath);
+		GetPrivateProfileString(_T("LinuxDevice"), _T("manu"), _T(""), g_stSATConfig.stLinuxDevice.szManu, MAX_PATH, szIniPath);
+		GetPrivateProfileString(_T("LinuxDevice"), _T("hardwareversion"), _T(""), g_stSATConfig.stLinuxDevice.szHardwareVersion, MAX_PATH, szIniPath);
+		GetPrivateProfileString(_T("LinuxDevice"), _T("model"), _T(""), g_stSATConfig.stLinuxDevice.szModel, MAX_PATH, szIniPath);
 
 		return 0;
 	}
@@ -978,4 +990,97 @@ namespace Global
 #endif
 	}
 
+	bool MKDIR(LPCTSTR dir) 
+	{
+		//////////////////////////////////////////////////////////////////////////
+		// 创建目录;
+		int nleft = 0;
+		int nIndex = -1;
+		string strdir = dir;
+		strdir = strdir.substr(0, strdir.find_last_of(_T("\\")));
+
+		if (strdir.at(strdir.size() - 1) != _T('\\'))
+			strdir.append(_T("\\"));
+
+		// 共享路径和硬盘盘符;
+		if (_tcscmp(strdir.substr(0, 2).c_str(), _T("\\\\")) == 0)
+			nleft = strdir.find_first_of(_T("\\"), 2) + 1;	// 去除共享主机名;
+		else if (strdir.at(2) == _T('\\'))
+			nleft = 3;
+
+		do
+		{
+			nIndex = strdir.substr(nleft, -1).find_first_of(_T("\\"));
+
+			if (nIndex != string::npos)
+			{
+				if (_mkdir(strdir.substr(0, nIndex + nleft).c_str()) == -1 && (errno != EEXIST))
+				{
+					//WriteTextLog(_T("创建目录失败:%s,错误码:%d"), strdir.substr(0, nIndex + nleft).c_str(), errno);
+					return false;
+				}
+
+				nleft += nIndex + 1;
+			}
+		} while (nIndex != -1);
+
+		return true;
+	};
+
+	void WritePythonLog(LPCTSTR file, const TCHAR* msg)
+	{
+		AutoThreadSection aSection(&g_critSection);
+		// 将日志内容输入到文件中;
+		// 获取今年年份;
+		//__time64_t gmt = time(NULL); // 获取当前日历时间(1900-01-01开始的Unix时间戳);
+		//struct tm gmtm = { 0 };
+		//localtime_s(&gmtm, &gmt); // 时间戳转成本地时间;
+
+		int nErr = -1;
+		// 打开或创建文件;
+		FILE* fp = NULL;
+		
+#ifndef UNICODE
+		if (_access(file, 0) != -1)
+#else
+		if (_taccess(file, 0) != -1)
+#endif
+		{ // 存在;
+			if (0 == _tfopen_s(&fp, file, _T("a+")))
+			{
+				// 移动到末尾;
+				fseek(fp, 0, SEEK_END);
+			}
+			else
+			{
+				//WriteTextLog(_T("打开文件失败:%s,%d"), file, GetLastError());
+				return;
+			}
+		}
+		else
+		{ // 不存在;
+			MKDIR(file);
+			if (0 !=  _tfopen_s(&fp, file, _T("w+")) )
+			{
+				//WriteTextLog(_T("创建文件失败:%s,%d"), file, GetLastError());
+				return;
+			}
+		}
+
+		if (fp == NULL)
+		{
+			//WriteTextLog(_T("文件句柄空:%s,%d"), file, GetLastError());
+			return;
+		}
+
+		// 格式化前设置语言区域;
+		TCHAR* old_locale = _tcsdup(_tsetlocale(LC_CTYPE, NULL));
+		_tsetlocale(LC_CTYPE, _T("chs")); //设定中文;
+		//_ftprintf(fp, _T("%04d-%02d-%02d %02d:%02d:%02d %s\n"), gmtm.tm_year + 1990, gmtm.tm_mon + 1, gmtm.tm_mday, gmtm.tm_hour, gmtm.tm_min, gmtm.tm_sec, msg);
+		_ftprintf(fp, _T("%s"), msg);
+		// 关闭文件,释放资源并设置回原语言区域;
+		fclose(fp);
+		_tsetlocale(LC_CTYPE, old_locale);
+		free(old_locale); //还原区域设定;
+	}
 };

+ 36 - 1
SATService/SATService/Global.h

@@ -33,6 +33,38 @@ typedef wstring TString;
 
 namespace Global
 {
+	typedef struct __ST_SAT_CFG__ 
+	{
+		int nAdbTimeout;
+		TCHAR szScriptDir[MAX_PATH];		// 脚本路径;
+		TCHAR szApkServer[MAX_PATH];
+		TCHAR szApkSysCode[MAX_PATH];
+
+		TCHAR szCaseServer[MAX_PATH];
+		TCHAR szCaseSysCode[MAX_PATH];
+
+		TCHAR szTaskServer[MAX_PATH];
+		TCHAR szTaskSysCode[MAX_PATH];
+
+		TCHAR szResultServer[MAX_PATH];
+		//TCHAR szResultSysCode[MAX_PATH];
+
+		TCHAR szExecuteServer[MAX_PATH];
+		TCHAR szExecuteSysCode[MAX_PATH];
+
+		TCHAR szResultExcelDir[MAX_PATH];
+		typedef struct  __ST_LD__
+		{
+			TCHAR szDevice[MAX_PATH];
+			TCHAR szManu[MAX_PATH];
+			TCHAR szHardwareVersion[MAX_PATH];
+			TCHAR szModel[MAX_PATH];
+		}STLinuxDevice, *pSTLinuxDevice;
+
+		STLinuxDevice stLinuxDevice;
+	}STSATConfig, *pSTSATConfig;
+
+	extern STSATConfig g_stSATConfig;
 	//////////////////////////////////////////////////////////////////////////
 	// 全局变量;
 	//////////////////////////////////////////////////////////////////////////
@@ -103,7 +135,10 @@ namespace Global
 	extern void DeleteDirectory(CString strDir);
 	//
 	extern void GetHostName();
-
+	// 创建目录;
+	bool MKDIR(LPCTSTR dir);
+	// 写脚本日志;
+	void WritePythonLog(LPCTSTR file, const TCHAR* msg);
 };
 
 #endif

+ 969 - 0
SATService/SATService/IOCPModel.cpp

@@ -0,0 +1,969 @@
+#include "StdAfx.h"
+#include "IOCPModel.h"
+//#include "MainDlg.h"
+
+// 每一个处理器上产生多少个线程(为了最大限度的提升服务器性能,详见配套文档)
+#define WORKER_THREADS_PER_PROCESSOR 2
+// 同时投递的Accept请求的数量(这个要根据实际的情况灵活设置)
+#define MAX_POST_ACCEPT              10
+// 传递给Worker线程的退出信号
+#define EXIT_CODE                    NULL
+
+
+// 释放指针和句柄资源的宏
+
+// 释放指针宏
+#define RELEASE(x)                      {if(x != NULL ){delete x;x=NULL;}}
+// 释放句柄宏
+#define RELEASE_HANDLE(x)               {if(x != NULL && x!=INVALID_HANDLE_VALUE){ CloseHandle(x);x = NULL;}}
+// 释放Socket宏
+#define RELEASE_SOCKET(x)               {if(x !=INVALID_SOCKET) { closesocket(x);x=INVALID_SOCKET;}}
+
+
+
+CIOCPModel::CIOCPModel(void):
+							m_nThreads(0),
+							m_hShutdownEvent(NULL),
+							m_hIOCompletionPort(NULL),
+							m_phWorkerThreads(NULL),
+							m_strIP(DEFAULT_IP),
+							m_nPort(DEFAULT_PORT),
+							m_pMain(NULL),
+							m_lpfnAcceptEx( NULL ),
+							m_pListenContext( NULL )
+{
+}
+
+
+CIOCPModel::~CIOCPModel(void)
+{
+	// 确保资源彻底释放
+	this->Stop();
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////
+// 工作者线程:  为IOCP请求服务的工作者线程
+//         也就是每当完成端口上出现了完成数据包,就将之取出来进行处理的线程
+///////////////////////////////////////////////////////////////////
+
+DWORD WINAPI CIOCPModel::_WorkerThread(LPVOID lpParam)
+{    
+	THREADPARAMS_WORKER* pParam = (THREADPARAMS_WORKER*)lpParam;
+	CIOCPModel* pIOCPModel = (CIOCPModel*)pParam->pIOCPModel;
+	int nThreadNo = (int)pParam->nThreadNo;
+
+	pIOCPModel->_ShowMessage(_T("工作者线程启动,ID: %d."),nThreadNo);
+
+	OVERLAPPED           *pOverlapped = NULL;
+	PER_SOCKET_CONTEXT   *pSocketContext = NULL;
+	DWORD                dwBytesTransfered = 0;
+
+	// 循环处理请求,知道接收到Shutdown信息为止
+	while (WAIT_OBJECT_0 != WaitForSingleObject(pIOCPModel->m_hShutdownEvent, 0))
+	{
+		BOOL bReturn = GetQueuedCompletionStatus(
+			pIOCPModel->m_hIOCompletionPort,
+			&dwBytesTransfered,
+			(PULONG_PTR)&pSocketContext,
+			&pOverlapped,
+			INFINITE);
+
+		// 如果收到的是退出标志,则直接退出
+		if ( EXIT_CODE==(DWORD)pSocketContext )
+		{
+			break;
+		}
+
+		// 判断是否出现了错误
+		if( !bReturn )  
+		{  
+			DWORD dwErr = GetLastError();
+
+			// 显示一下提示信息
+			if( !pIOCPModel->HandleError( pSocketContext,dwErr ) )
+			{
+				break;
+			}
+
+			continue;  
+		}  
+		else  
+		{  	
+			// 读取传入的参数
+			PER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_CONTEXT, m_Overlapped);  
+
+			// 判断是否有客户端断开了
+			if((0 == dwBytesTransfered) && ( RECV_POSTED==pIoContext->m_OpType || SEND_POSTED==pIoContext->m_OpType))  
+			{  
+				pIOCPModel->_ShowMessage( _T("客户端 %s:%d 断开连接."),
+					inet_ntoa(pSocketContext->m_ClientAddr.sin_addr), 
+					ntohs(pSocketContext->m_ClientAddr.sin_port) );
+
+				// 释放掉对应的资源
+				pIOCPModel->_RemoveContext( pSocketContext );
+
+ 				continue;  
+			}  
+			else
+			{
+				switch( pIoContext->m_OpType )  
+				{  
+					 // Accept  
+				case ACCEPT_POSTED:
+					{ 
+						// 为了增加代码可读性,这里用专门的_DoAccept函数进行处理连入请求
+						pIOCPModel->_DoAccpet( pSocketContext, pIoContext );		
+					}
+					break;
+
+					// RECV
+				case RECV_POSTED:
+					{
+						// 为了增加代码可读性,这里用专门的_DoRecv函数进行处理接收请求
+						pIOCPModel->_DoRecv( pSocketContext,pIoContext );
+					}
+					break;
+
+					// SEND
+					// 这里略过不写了,要不代码太多了,不容易理解,Send操作相对来讲简单一些
+				case SEND_POSTED:
+					{
+
+					}
+					break;
+				default:
+					// 不应该执行到这里
+					TRACE(_T("_WorkThread中的 pIoContext->m_OpType 参数异常.\n"));
+					break;
+				} //switch
+			}//if
+		}//if
+
+	}//while
+
+	TRACE(_T("工作者线程 %d 号退出.\n"),nThreadNo);
+
+	// 释放线程参数
+	RELEASE(lpParam);	
+
+	return 0;
+}
+
+
+
+//====================================================================================
+//
+//				    系统初始化和终止
+//
+//====================================================================================
+
+
+
+
+////////////////////////////////////////////////////////////////////
+// 初始化WinSock 2.2
+bool CIOCPModel::LoadSocketLib()
+{    
+	WSADATA wsaData;
+	int nResult;
+	nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+	// 错误(一般都不可能出现)
+	if (NO_ERROR != nResult)
+	{
+		this->_ShowMessage(_T("初始化WinSock 2.2失败!\n"));
+		return false; 
+	}
+
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////
+//	启动服务器
+bool CIOCPModel::Start(unsigned int port)
+{
+	// 初始化线程互斥量
+	InitializeCriticalSection(&m_csContextList);
+
+	// 建立系统退出的事件通知
+	m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+	// 初始化IOCP
+	if (false == _InitializeIOCP())
+	{
+		this->_ShowMessage(_T("初始化IOCP失败!\n"));
+		return false;
+	}
+	else
+	{
+		this->_ShowMessage(_T("\nIOCP初始化完毕\n."));
+	}
+
+	// 初始化Socket
+	if( false==_InitializeListenSocket(port) )
+	{
+		this->_ShowMessage(_T("Listen Socket初始化失败!\n"));
+		this->_DeInitialize();
+		return false;
+	}
+	else
+	{
+		this->_ShowMessage(_T("Listen Socket初始化完毕."));
+	}
+
+	this->_ShowMessage(_T("系统准备就绪,等候连接....\n"));
+
+	return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//	开始发送系统退出消息,退出完成端口和线程资源
+void CIOCPModel::Stop()
+{
+	if( m_pListenContext!=NULL && m_pListenContext->m_Socket!=INVALID_SOCKET )
+	{
+		// 激活关闭消息通知
+		SetEvent(m_hShutdownEvent);
+
+		for (int i = 0; i < m_nThreads; i++)
+		{
+			// 通知所有的完成端口操作退出
+			PostQueuedCompletionStatus(m_hIOCompletionPort, 0, (DWORD)EXIT_CODE, NULL);
+		}
+
+		// 等待所有的客户端资源退出
+		WaitForMultipleObjects(m_nThreads, m_phWorkerThreads, TRUE, INFINITE);
+
+		// 清除客户端列表信息
+		this->_ClearContextList();
+
+		// 释放其他资源
+		this->_DeInitialize();
+
+		this->_ShowMessage(_T("停止监听\n"));
+	}	
+}
+
+
+////////////////////////////////
+// 初始化完成端口
+bool CIOCPModel::_InitializeIOCP()
+{
+	// 建立第一个完成端口
+	m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );
+
+	if ( NULL == m_hIOCompletionPort)
+	{
+		this->_ShowMessage(_T("建立完成端口失败!错误代码: %d!\n"), WSAGetLastError());
+		return false;
+	}
+
+	// 根据本机中的处理器数量,建立对应的线程数
+	m_nThreads = WORKER_THREADS_PER_PROCESSOR * _GetNoOfProcessors();
+	
+	// 为工作者线程初始化句柄
+	m_phWorkerThreads = new HANDLE[m_nThreads];
+	
+	// 根据计算出来的数量建立工作者线程
+	DWORD nThreadID;
+	for (int i = 0; i < m_nThreads; i++)
+	{
+		THREADPARAMS_WORKER* pThreadParams = new THREADPARAMS_WORKER;
+		pThreadParams->pIOCPModel = this;
+		pThreadParams->nThreadNo  = i+1;
+		m_phWorkerThreads[i] = ::CreateThread(0, 0, _WorkerThread, (void *)pThreadParams, 0, &nThreadID);
+	}
+
+	TRACE(" 建立 _WorkerThread %d 个.\n", m_nThreads );
+
+	return true;
+}
+
+
+/////////////////////////////////////////////////////////////////
+// 初始化Socket
+bool CIOCPModel::_InitializeListenSocket(unsigned int port)
+{
+	// AcceptEx 和 GetAcceptExSockaddrs 的GUID,用于导出函数指针
+	GUID GuidAcceptEx = WSAID_ACCEPTEX;  
+	GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS; 
+
+	// 服务器地址信息,用于绑定Socket
+	struct sockaddr_in ServerAddress;
+
+	// 生成用于监听的Socket的信息
+	m_pListenContext = new PER_SOCKET_CONTEXT;
+
+	// 需要使用重叠IO,必须得使用WSASocket来建立Socket,才可以支持重叠IO操作
+	m_pListenContext->m_Socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
+	if (INVALID_SOCKET == m_pListenContext->m_Socket) 
+	{
+		this->_ShowMessage(_T("初始化Socket失败,错误代码: %d.\n"), WSAGetLastError());
+		return false;
+	}
+	else
+	{
+		TRACE("WSASocket() 完成.\n");
+	}
+
+	// 将Listen Socket绑定至完成端口中
+	if( NULL== CreateIoCompletionPort( (HANDLE)m_pListenContext->m_Socket, m_hIOCompletionPort,(DWORD)m_pListenContext, 0))  
+	{  
+		this->_ShowMessage(_T("绑定 Listen Socket至完成端口失败!错误代码: %d/n"), WSAGetLastError());  
+		RELEASE_SOCKET( m_pListenContext->m_Socket );
+		return false;
+	}
+	else
+	{
+		TRACE("Listen Socket绑定完成端口 完成.\n");
+	}
+
+	// 填充地址信息
+	ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress));
+	ServerAddress.sin_family = AF_INET;
+	// 这里可以绑定任何可用的IP地址,或者绑定一个指定的IP地址 
+	ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);                      
+	//ServerAddress.sin_addr.s_addr = inet_addr(m_strIP.GetString());         
+	ServerAddress.sin_port = htons(port);                          
+
+	// 绑定地址和端口
+	if (SOCKET_ERROR == ::bind(m_pListenContext->m_Socket, (struct sockaddr *) &ServerAddress, sizeof(ServerAddress))) 
+	{
+		this->_ShowMessage(_T("bind()函数执行错误! 错误代码: %d/n"), WSAGetLastError());  
+		return false;
+	}
+	else
+	{
+		TRACE("bind() 完成.\n");
+	}
+
+	// 开始进行监听
+	if (SOCKET_ERROR == listen(m_pListenContext->m_Socket,SOMAXCONN))
+	{
+		this->_ShowMessage(_T("Listen()函数执行出现错误; 错误代码: %d/n"), WSAGetLastError());  
+		return false;
+	}
+	else
+	{
+		TRACE("Listen() 完成.\n");
+	}
+
+	// 使用AcceptEx函数,因为这个是属于WinSock2规范之外的微软另外提供的扩展函数
+	// 所以需要额外获取一下函数的指针,
+	// 获取AcceptEx函数指针
+	DWORD dwBytes = 0;  
+	if(SOCKET_ERROR == WSAIoctl(
+		m_pListenContext->m_Socket, 
+		SIO_GET_EXTENSION_FUNCTION_POINTER, 
+		&GuidAcceptEx, 
+		sizeof(GuidAcceptEx), 
+		&m_lpfnAcceptEx, 
+		sizeof(m_lpfnAcceptEx), 
+		&dwBytes, 
+		NULL, 
+		NULL))  
+	{  
+		this->_ShowMessage(_T("WSAIoctl 未能获取AcceptEx函数指针。错误代码: %d\n"), WSAGetLastError()); 
+		this->_DeInitialize();
+		return false;  
+	}  
+
+	// 获取GetAcceptExSockAddrs函数指针,也是同理
+	if(SOCKET_ERROR == WSAIoctl(
+		m_pListenContext->m_Socket, 
+		SIO_GET_EXTENSION_FUNCTION_POINTER, 
+		&GuidGetAcceptExSockAddrs,
+		sizeof(GuidGetAcceptExSockAddrs), 
+		&m_lpfnGetAcceptExSockAddrs, 
+		sizeof(m_lpfnGetAcceptExSockAddrs),   
+		&dwBytes, 
+		NULL, 
+		NULL))  
+	{  
+		this->_ShowMessage(_T("WSAIoctl 未能获取GuidGetAcceptExSockAddrs函数指针。错误代码: %d\n"), WSAGetLastError());  
+		this->_DeInitialize();
+		return false; 
+	}  
+
+
+	// 为AcceptEx 准备参数,然后投递AcceptEx I/O请求
+	for( int i=0;i<MAX_POST_ACCEPT;i++ )
+	{
+		// 新建一个IO_CONTEXT
+		PER_IO_CONTEXT* pAcceptIoContext = m_pListenContext->GetNewIoContext();
+
+		if( false==this->_PostAccept( pAcceptIoContext ) )
+		{
+			m_pListenContext->RemoveContext(pAcceptIoContext);
+			return false;
+		}
+	}
+
+	this->_ShowMessage( _T("投递 %d 个AcceptEx请求完毕"),MAX_POST_ACCEPT );
+
+	return true;
+}
+
+////////////////////////////////////////////////////////////
+//	最后释放掉所有资源
+void CIOCPModel::_DeInitialize()
+{
+	// 删除客户端列表的互斥量
+	DeleteCriticalSection(&m_csContextList);
+
+	// 关闭系统退出事件句柄
+	RELEASE_HANDLE(m_hShutdownEvent);
+
+	// 释放工作者线程句柄指针
+	for( int i=0;i<m_nThreads;i++ )
+	{
+		RELEASE_HANDLE(m_phWorkerThreads[i]);
+	}
+	
+	RELEASE(m_phWorkerThreads);
+
+	// 关闭IOCP句柄
+	RELEASE_HANDLE(m_hIOCompletionPort);
+
+	// 关闭监听Socket
+	RELEASE(m_pListenContext);
+
+	this->_ShowMessage(_T("释放资源完毕.\n"));
+}
+
+
+//====================================================================================
+//
+//				    投递完成端口请求
+//
+//====================================================================================
+
+
+//////////////////////////////////////////////////////////////////
+// 投递Accept请求
+bool CIOCPModel::_PostAccept( PER_IO_CONTEXT* pAcceptIoContext )
+{
+	ASSERT( INVALID_SOCKET!=m_pListenContext->m_Socket );
+
+	// 准备参数
+	DWORD dwBytes = 0;  
+	pAcceptIoContext->m_OpType = ACCEPT_POSTED;  
+	WSABUF *p_wbuf   = &pAcceptIoContext->m_wsaBuf;
+	OVERLAPPED *p_ol = &pAcceptIoContext->m_Overlapped;
+	
+	// 为以后新连入的客户端先准备好Socket( 这个是与传统accept最大的区别 ) 
+	pAcceptIoContext->m_sockAccept  = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);  
+	if( INVALID_SOCKET==pAcceptIoContext->m_sockAccept )  
+	{  
+		_ShowMessage(_T("创建用于Accept的Socket失败!错误代码: %d"), WSAGetLastError()); 
+		return false;  
+	} 
+
+	// 投递AcceptEx
+	if(FALSE == m_lpfnAcceptEx( m_pListenContext->m_Socket, pAcceptIoContext->m_sockAccept, p_wbuf->buf, p_wbuf->len - ((sizeof(SOCKADDR_IN)+16)*2),   
+								sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, &dwBytes, p_ol))  
+	{  
+		if(WSA_IO_PENDING != WSAGetLastError())  
+		{  
+			_ShowMessage(_T("投递 AcceptEx 请求失败,错误代码: %d"), WSAGetLastError());  
+			return false;  
+		}  
+	} 
+
+	return true;
+}
+
+////////////////////////////////////////////////////////////
+// 在有客户端连入的时候,进行处理
+// 流程有点复杂,你要是看不懂的话,就看配套的文档吧....
+// 如果能理解这里的话,完成端口的机制你就消化了一大半了
+
+// 总之你要知道,传入的是ListenSocket的Context,我们需要复制一份出来给新连入的Socket用
+// 原来的Context还是要在上面继续投递下一个Accept请求
+//
+bool CIOCPModel::_DoAccpet( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext )
+{
+	SOCKADDR_IN* ClientAddr = NULL;
+	SOCKADDR_IN* LocalAddr = NULL;  
+	int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);  
+
+	///////////////////////////////////////////////////////////////////////////
+	// 1. 首先取得连入客户端的地址信息
+	// 这个 m_lpfnGetAcceptExSockAddrs 不得了啊~~~~~~
+	// 不但可以取得客户端和本地端的地址信息,还能顺便取出客户端发来的第一组数据
+	this->m_lpfnGetAcceptExSockAddrs(pIoContext->m_wsaBuf.buf, pIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN)+16)*2),  
+		sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);  
+
+	_RecvProcess(pSocketContext, pIoContext);
+	this->_ShowMessage( _T("客户端 %s:%d 连入."), inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port) );
+	//this->_ShowMessage( _T("客户额 %s:%d 信息:%s."),inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port),pIoContext->m_wsaBuf.buf );
+	//if ( Global::g_bEnableLog )
+	//{
+	//	// 解析Json字符串;
+	//	Global::TLog tlog;
+	//	cJSON *pJson = cJSON_Parse(Global::DeCode_URLUNICODE(pIoContext->m_wsaBuf.buf).c_str());
+	//	if ( pJson )
+	//	{
+	//		tlog.report_type = cJSON_GetObjectItem(pJson, _T("ReportType")) ? cJSON_GetObjectItem(pJson, _T("ReportType"))->valuestring : "";
+	//		tlog.report_data = cJSON_GetObjectItem(pJson, _T("prinMsg")) ? cJSON_GetObjectItem(pJson, _T("prinMsg"))->valuestring : "";
+	//		if ( _tcscmp(tlog.report_type.c_str(), _T("printLog")) == 0 )
+	//		{
+	//			Global::WritePythonLog(tlog.report_data.c_str());
+	//			Global::g_time = time(NULL);
+	//			Global::g_lastTime = COleDateTime::GetCurrentTime();
+	//		}
+
+	//		cJSON_Delete(pJson);
+	//		pJson = NULL;
+	//	}
+	//}
+
+	//////////////////////////////////////////////////////////////////////////////////////////////////////
+	// 2. 这里需要注意,这里传入的这个是ListenSocket上的Context,这个Context我们还需要用于监听下一个连接
+	// 所以我还得要将ListenSocket上的Context复制出来一份为新连入的Socket新建一个SocketContext
+
+	PER_SOCKET_CONTEXT* pNewSocketContext = new PER_SOCKET_CONTEXT;
+	pNewSocketContext->m_Socket           = pIoContext->m_sockAccept;
+	memcpy(&(pNewSocketContext->m_ClientAddr), ClientAddr, sizeof(SOCKADDR_IN));
+
+	// 参数设置完毕,将这个Socket和完成端口绑定(这也是一个关键步骤)
+	if( false==this->_AssociateWithIOCP( pNewSocketContext ) )
+	{
+		RELEASE( pNewSocketContext );
+		return false;
+	}  
+
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////
+	// 3. 继续,建立其下的IoContext,用于在这个Socket上投递第一个Recv数据请求
+	PER_IO_CONTEXT* pNewIoContext = pNewSocketContext->GetNewIoContext();
+	pNewIoContext->m_OpType       = RECV_POSTED;
+	pNewIoContext->m_sockAccept   = pNewSocketContext->m_Socket;
+	// 如果Buffer需要保留,就自己拷贝一份出来
+	//memcpy( pNewIoContext->m_szBuffer,pIoContext->m_szBuffer,MAX_BUFFER_LEN );
+
+	// 绑定完毕之后,就可以开始在这个Socket上投递完成请求了
+	if( false==this->_PostRecv( pNewIoContext) )
+	{
+		pNewSocketContext->RemoveContext( pNewIoContext );
+		return false;
+	}
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////
+	// 4. 如果投递成功,那么就把这个有效的客户端信息,加入到ContextList中去(需要统一管理,方便释放资源)
+	this->_AddToContextList( pNewSocketContext );
+
+	////////////////////////////////////////////////////////////////////////////////////////////////
+	// 5. 使用完毕之后,把Listen Socket的那个IoContext重置,然后准备投递新的AcceptEx
+	pIoContext->ResetBuffer();
+	return this->_PostAccept( pIoContext ); 	
+}
+
+////////////////////////////////////////////////////////////////////
+// 投递接收数据请求
+bool CIOCPModel::_PostRecv( PER_IO_CONTEXT* pIoContext )
+{
+	// 初始化变量
+	DWORD dwFlags = 0;
+	DWORD dwBytes = 0;
+	WSABUF *p_wbuf   = &pIoContext->m_wsaBuf;
+	OVERLAPPED *p_ol = &pIoContext->m_Overlapped;
+
+	pIoContext->ResetBuffer();
+	pIoContext->m_OpType = RECV_POSTED;
+
+	// 初始化完成后,,投递WSARecv请求
+	int nBytesRecv = WSARecv( pIoContext->m_sockAccept, p_wbuf, 1, &dwBytes, &dwFlags, p_ol, NULL );
+
+	// 如果返回值错误,并且错误的代码并非是Pending的话,那就说明这个重叠请求失败了
+	if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
+	{
+		this->_ShowMessage(_T("投递第一个WSARecv失败!"));
+		return false;
+	}
+	return true;
+}
+
+/////////////////////////////////////////////////////////////////
+// 在有接收的数据到达的时候,进行处理
+bool CIOCPModel::_DoRecv( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext )
+{
+	// 先把上一次的数据显示出现,然后就重置状态,发出下一个Recv请求
+	SOCKADDR_IN* ClientAddr = &pSocketContext->m_ClientAddr;
+	//this->_ShowMessage( _T("收到  %s:%d 信息:%s"),inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port), pIoContext->m_wsaBuf.buf );
+	
+	_RecvProcess(pSocketContext, pIoContext);
+	//if ( Global::g_bEnableLog )
+	//{
+	//	// 解析Json字符串;
+	//	Global::TLog tlog;
+	//	cJSON *pJson = cJSON_Parse(Global::DeCode_URLUNICODE(pIoContext->m_wsaBuf.buf).c_str());
+	//	if ( pJson )
+	//	{
+	//		tlog.report_type = cJSON_GetObjectItem(pJson, _T("ReportType")) ? cJSON_GetObjectItem(pJson, _T("ReportType"))->valuestring : "";
+	//		tlog.report_data = cJSON_GetObjectItem(pJson, _T("prinMsg")) ? cJSON_GetObjectItem(pJson, _T("prinMsg"))->valuestring : "";
+	//		if ( _tcscmp(tlog.report_type.c_str(), _T("printLog")) == 0 )
+	//		{
+	//			Global::WritePythonLog(tlog.report_data.c_str());
+	//			Global::g_time = time(NULL);
+	//			Global::g_lastTime = COleDateTime::GetCurrentTime();
+	//		}
+	//		cJSON_Delete(pJson);
+	//		pJson = NULL;
+	//	}
+	//	
+	//}
+
+	// 然后开始投递下一个WSARecv请求
+	return _PostRecv( pIoContext );
+}
+
+
+
+/////////////////////////////////////////////////////
+// 将句柄(Socket)绑定到完成端口中
+bool CIOCPModel::_AssociateWithIOCP( PER_SOCKET_CONTEXT *pContext )
+{
+	// 将用于和客户端通信的SOCKET绑定到完成端口中
+	HANDLE hTemp = CreateIoCompletionPort((HANDLE)pContext->m_Socket, m_hIOCompletionPort, (DWORD)pContext, 0);
+
+	if (NULL == hTemp)
+	{
+		this->_ShowMessage(_T("执行CreateIoCompletionPort()出现错误.错误代码:%d"),GetLastError());
+		return false;
+	}
+
+	return true;
+}
+
+
+
+
+//====================================================================================
+//
+//				    ContextList 相关操作
+//
+//====================================================================================
+
+
+//////////////////////////////////////////////////////////////
+// 将客户端的相关信息存储到数组中
+void CIOCPModel::_AddToContextList( PER_SOCKET_CONTEXT *pHandleData )
+{
+	EnterCriticalSection(&m_csContextList);
+
+	m_arrayClientContext.Add(pHandleData);	
+	
+	LeaveCriticalSection(&m_csContextList);
+}
+
+////////////////////////////////////////////////////////////////
+//	移除某个特定的Context
+void CIOCPModel::_RemoveContext( PER_SOCKET_CONTEXT *pSocketContext )
+{
+	EnterCriticalSection(&m_csContextList);
+
+	for( int i=0;i<m_arrayClientContext.GetCount();i++ )
+	{
+		if( pSocketContext==m_arrayClientContext.GetAt(i) )
+		{
+			RELEASE( pSocketContext );			
+			m_arrayClientContext.RemoveAt(i);			
+			break;
+		}
+	}
+
+	LeaveCriticalSection(&m_csContextList);
+}
+
+////////////////////////////////////////////////////////////////
+// 清空客户端信息
+void CIOCPModel::_ClearContextList()
+{
+	EnterCriticalSection(&m_csContextList);
+
+	for( int i=0;i<m_arrayClientContext.GetCount();i++ )
+	{
+		delete m_arrayClientContext.GetAt(i);
+	}
+
+	m_arrayClientContext.RemoveAll();
+
+	LeaveCriticalSection(&m_csContextList);
+}
+
+
+
+//====================================================================================
+//
+//				       其他辅助函数定义
+//
+//====================================================================================
+
+
+
+////////////////////////////////////////////////////////////////////
+// 获得本机的IP地址
+CString CIOCPModel::GetLocalIP()
+{
+	// 获得本机主机名
+	char hostname[MAX_PATH] = {0};
+	gethostname(hostname,MAX_PATH);                
+	struct hostent FAR* lpHostEnt = gethostbyname(hostname);
+	if(lpHostEnt == NULL)
+	{
+		return DEFAULT_IP;
+	}
+
+	// 取得IP地址列表中的第一个为返回的IP(因为一台主机可能会绑定多个IP)
+	LPSTR lpAddr = lpHostEnt->h_addr_list[0];      
+
+	// 将IP地址转化成字符串形式
+	struct in_addr inAddr;
+	memmove(&inAddr,lpAddr,4);
+	m_strIP = CString( inet_ntoa(inAddr) );        
+
+	return m_strIP;
+}
+
+///////////////////////////////////////////////////////////////////
+// 获得本机中处理器的数量
+int CIOCPModel::_GetNoOfProcessors()
+{
+	SYSTEM_INFO si;
+
+	GetSystemInfo(&si);
+
+	return si.dwNumberOfProcessors;
+}
+
+/////////////////////////////////////////////////////////////////////
+// 在主界面中显示提示信息
+void CIOCPModel::_ShowMessage(const CString szFormat,...) const
+{
+	// 根据传入的参数格式化字符串
+	CString   strMessage;
+	va_list   arglist;
+
+	// 处理变长参数
+	va_start(arglist, szFormat);
+	strMessage.FormatV(szFormat,arglist);
+	va_end(arglist);
+
+#if 0
+	// 在主界面中显示
+	CMainDlg* pMain = (CMainDlg*)m_pMain;
+	if( m_pMain!=NULL )
+	{
+		pMain->AddInformation(strMessage);
+		TRACE( strMessage+_T("\n") );
+	}
+#else
+//	Global::WriteTextLog(strMessage);
+#endif
+}
+
+#define PAK_LEN sizeof(ProHeader)
+void CIOCPModel::_RecvProcess(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext)
+{
+	// 小于包头;
+	ProHeader* phead = NULL;
+	if (pSocketContext->lastData.size() == 0)
+	{
+		// 不足包头;
+		if (PAK_LEN > pIoContext->m_Overlapped.InternalHigh)
+		{
+			OutputDebugString("A:不足包头;\n");
+			pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf, pIoContext->m_Overlapped.InternalHigh);
+		}
+		else
+		{
+			phead = (ProHeader*)pIoContext->m_wsaBuf.buf;
+
+			// 完整的包;
+			if (phead->len == pIoContext->m_Overlapped.InternalHigh)
+			{
+				OutputDebugString("A:完整的包;\n");
+				_DeviceProc(pIoContext, (Package*)pIoContext->m_wsaBuf.buf);
+			}
+			// 小包;
+			else if (phead->len > pIoContext->m_Overlapped.InternalHigh)
+			{
+				OutputDebugString("A:小包;\n");
+				pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf, pIoContext->m_Overlapped.InternalHigh);
+			}
+			// 超包;
+			else if (phead->len < pIoContext->m_Overlapped.InternalHigh)
+			{
+				OutputDebugString("A:超包;\n");
+				pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf + phead->len, pIoContext->m_Overlapped.InternalHigh - phead->len);
+				_DeviceProc(pIoContext, (Package*)pIoContext->m_wsaBuf.buf);
+			}
+		}
+	}
+	else
+	{
+		int lastlen = pIoContext->m_Overlapped.InternalHigh;
+		if (pSocketContext->lastData.size() >= PAK_LEN)
+		{
+			phead = (ProHeader*)pSocketContext->lastData.data();
+			if (phead->len <= pSocketContext->lastData.size() + pIoContext->m_Overlapped.InternalHigh)
+			{
+				if ( phead->len <= pSocketContext->lastData.size() )
+				{
+					OutputDebugString("C:超包;\n");
+					// 完整包;
+					_DeviceProc(pIoContext, (Package*)pSocketContext->lastData.substr(0, phead->len).data());
+
+					pSocketContext->lastData = pSocketContext->lastData.substr(phead->len);
+					pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf, pIoContext->m_Overlapped.InternalHigh);
+				}
+				else
+				{
+					OutputDebugString("D:超包;\n");
+					lastlen = pSocketContext->lastData.size() + pIoContext->m_Overlapped.InternalHigh - phead->len;
+					pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf, pIoContext->m_Overlapped.InternalHigh - lastlen);
+					// 完整包;
+					_DeviceProc(pIoContext, (Package*)pSocketContext->lastData.data());
+					// 剩余包;
+					pSocketContext->lastData.clear();
+					if (lastlen)
+						pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf + pIoContext->m_Overlapped.InternalHigh - lastlen, lastlen);
+				}
+			}
+			else
+			{
+				OutputDebugString("C:仍不足一个包;\n");
+				pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf, pIoContext->m_Overlapped.InternalHigh);
+			}
+		}
+		else
+		{
+			// 包头剩余长度;
+			int diflen = PAK_LEN - pSocketContext->lastData.size();
+			// 仍不足一个包头;
+			if ( diflen > pIoContext->m_Overlapped.InternalHigh )
+			{
+				OutputDebugString("B:仍不足一个包头;\n");
+				pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf, pIoContext->m_Overlapped.InternalHigh);
+			}
+			else
+			{
+				// 拼成完整包头;
+				pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf, diflen);
+				phead = (ProHeader*)pSocketContext->lastData.data();
+
+				// 完整包;
+				if ( phead->len == PAK_LEN + pIoContext->m_Overlapped.InternalHigh - diflen )
+				{
+					OutputDebugString("B:完整包;\n");
+					pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf + diflen, pIoContext->m_Overlapped.InternalHigh - diflen);
+					_DeviceProc(pIoContext, (Package*)pSocketContext->lastData.data());
+					pSocketContext->lastData.clear();
+				}
+				// 小包;
+				else if ( phead->len > PAK_LEN + pIoContext->m_Overlapped.InternalHigh - diflen)
+				{
+					OutputDebugString("B:小包;\n");
+					pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf + diflen, pIoContext->m_Overlapped.InternalHigh - diflen);
+				}
+				// 超包;
+				else if (phead->len < PAK_LEN + pIoContext->m_Overlapped.InternalHigh - diflen)
+				{
+					OutputDebugString("B:超包;\n");
+					// 组完成包;
+					pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf + diflen, phead->len - PAK_LEN);
+					_DeviceProc(pIoContext, (Package*)pSocketContext->lastData.data());
+					pSocketContext->lastData.clear();
+					int last = pIoContext->m_Overlapped.InternalHigh - diflen - phead->len + PAK_LEN;
+					if (last)
+					{						
+						pSocketContext->lastData.append(pIoContext->m_wsaBuf.buf + pIoContext->m_Overlapped.InternalHigh - last, last);
+					}
+				}
+			}
+		}
+	}
+}
+
+void CIOCPModel::_DeviceProc(PER_IO_CONTEXT* pIoContext, Package* pak)
+{
+	if ( pak->header.version != 0xAA )
+	{
+		return;
+	}
+
+	cJSON* pJson = cJSON_Parse((const char*)&pak->buf);
+	if ( pJson == NULL )
+	{
+		return;
+	}
+
+	RequesJson reqj;
+	reqj.device_id = cJSON_GetObjectItem(pJson, "device_id") ? cJSON_GetObjectItem(pJson, "device_id")->valueint : 0;
+	reqj.device_name = cJSON_GetObjectItem(pJson, "device_name") ? cJSON_GetObjectItem(pJson, "device_name")->valuestring : "";
+	reqj.device_cmd = cJSON_GetObjectItem(pJson, "device_cmd") ? cJSON_GetObjectItem(pJson, "device_cmd")->valuestring : "";
+	reqj.device_timeout = cJSON_GetObjectItem(pJson, "device_timeout") ? cJSON_GetObjectItem(pJson, "device_timeout")->valueint : 300;
+
+	if ( pJson )
+	{
+		cJSON_Delete(pJson);
+		pJson = NULL;
+	}
+
+}
+
+/////////////////////////////////////////////////////////////////////
+// 判断客户端Socket是否已经断开,否则在一个无效的Socket上投递WSARecv操作会出现异常
+// 使用的方法是尝试向这个socket发送数据,判断这个socket调用的返回值
+// 因为如果客户端网络异常断开(例如客户端崩溃或者拔掉网线等)的时候,服务器端是无法收到客户端断开的通知的
+
+bool CIOCPModel::_IsSocketAlive(SOCKET s)
+{
+	int nByteSent=send(s,"",0,0);
+	if (-1 == nByteSent) return false;
+	return true;
+}
+
+///////////////////////////////////////////////////////////////////
+// 显示并处理完成端口上的错误
+bool CIOCPModel::HandleError( PER_SOCKET_CONTEXT *pContext,const DWORD& dwErr )
+{
+	// 如果是超时了,就再继续等吧  
+	if(WAIT_TIMEOUT == dwErr)  
+	{  	
+		// 确认客户端是否还活着...
+		if( !_IsSocketAlive( pContext->m_Socket) )
+		{
+			this->_ShowMessage( _T("检测到客户端异常退出!") );
+			this->_RemoveContext( pContext );
+			return true;
+		}
+		else
+		{
+			this->_ShowMessage( _T("网络操作超时!重试中...") );
+			return true;
+		}
+	}  
+
+	// 可能是客户端异常退出了
+	else if( ERROR_NETNAME_DELETED==dwErr )
+	{
+		this->_ShowMessage( _T("检测到客户端异常退出!") );
+		this->_RemoveContext( pContext );
+		return true;
+	}
+
+	else
+	{
+		this->_ShowMessage( _T("完成端口操作出现错误,线程退出。错误代码:%d"),dwErr );
+		return false;
+	}
+}
+
+
+
+

+ 295 - 0
SATService/SATService/IOCPModel.h

@@ -0,0 +1,295 @@
+#pragma once
+
+// winsock 2 的头文件和库
+#include <winsock2.h>
+#include <MSWSock.h>
+//#include <ws2tcpip.h>
+#pragma comment(lib,"ws2_32.lib")
+
+// 缓冲区长度 (1024*8)
+// 之所以为什么设置8K,也是一个江湖上的经验值
+// 如果确实客户端发来的每组数据都比较少,那么就设置得小一些,省内存
+#define MAX_BUFFER_LEN        8192  
+// 默认端口
+#define DEFAULT_PORT          12345    
+// 默认IP地址
+#define DEFAULT_IP            _T("127.0.0.1")
+
+#pragma pack(push)
+#pragma pack(1)
+
+typedef struct _PRO_HEADER_
+{
+	byte	version;
+	unsigned int len; // header长度+buf长度;
+}ProHeader;
+
+// 请求包
+typedef struct _PACKAGE_
+{
+	ProHeader header;
+	byte	buf[4]; // json格式;
+}Package;
+#pragma pack(pop)
+
+typedef struct _REQUEST_JSON_
+{
+	int			device_id;
+	std::string		device_name;
+	std::string device_cmd;
+	int	device_timeout;
+}RequesJson;
+
+typedef struct _RESPONSE_JSON_
+{
+	int			device_id;
+	std::string		device_name;
+	std::string device_cmd_result;	// 命令执行结果;
+}ResponseJson;
+
+
+//////////////////////////////////////////////////////////////////
+// 在完成端口上投递的I/O操作的类型
+typedef enum _OPERATION_TYPE  
+{  
+	ACCEPT_POSTED,                     // 标志投递的Accept操作
+	SEND_POSTED,                       // 标志投递的是发送操作
+	RECV_POSTED,                       // 标志投递的是接收操作
+	NULL_POSTED                        // 用于初始化,无意义
+
+}OPERATION_TYPE;
+
+//====================================================================================
+//
+//				单IO数据结构体定义(用于每一个重叠操作的参数)
+//
+//====================================================================================
+
+typedef struct _PER_IO_CONTEXT
+{
+	OVERLAPPED     m_Overlapped;                               // 每一个重叠网络操作的重叠结构(针对每一个Socket的每一个操作,都要有一个)              
+	SOCKET         m_sockAccept;                               // 这个网络操作所使用的Socket
+	WSABUF         m_wsaBuf;                                   // WSA类型的缓冲区,用于给重叠操作传参数的
+	char           m_szBuffer[MAX_BUFFER_LEN];                 // 这个是WSABUF里具体存字符的缓冲区
+	OPERATION_TYPE m_OpType;                                   // 标识网络操作的类型(对应上面的枚举)
+
+	// 初始化
+	_PER_IO_CONTEXT()
+	{
+		ZeroMemory(&m_Overlapped, sizeof(m_Overlapped));  
+		ZeroMemory( m_szBuffer,MAX_BUFFER_LEN );
+		m_sockAccept = INVALID_SOCKET;
+		m_wsaBuf.buf = m_szBuffer;
+		m_wsaBuf.len = MAX_BUFFER_LEN;
+		m_OpType     = NULL_POSTED;
+	}
+	// 释放掉Socket
+	~_PER_IO_CONTEXT()
+	{
+		if( m_sockAccept!=INVALID_SOCKET )
+		{
+			closesocket(m_sockAccept);
+			m_sockAccept = INVALID_SOCKET;
+		}
+	}
+	// 重置缓冲区内容
+	void ResetBuffer()
+	{
+		ZeroMemory( m_szBuffer,MAX_BUFFER_LEN );
+	}
+
+} PER_IO_CONTEXT, *PPER_IO_CONTEXT;
+
+
+//====================================================================================
+//
+//				单句柄数据结构体定义(用于每一个完成端口,也就是每一个Socket的参数)
+//
+//====================================================================================
+
+typedef struct _PER_SOCKET_CONTEXT
+{  
+	SOCKET      m_Socket;                                  // 每一个客户端连接的Socket
+	SOCKADDR_IN m_ClientAddr;                              // 客户端的地址
+	CArray<_PER_IO_CONTEXT*> m_arrayIoContext;             // 客户端网络操作的上下文数据,
+	                                                       // 也就是说对于每一个客户端Socket,是可以在上面同时投递多个IO请求的
+	std::string lastData;
+
+	// 初始化
+	_PER_SOCKET_CONTEXT()
+	{
+		m_Socket = INVALID_SOCKET;
+		memset(&m_ClientAddr, 0, sizeof(m_ClientAddr)); 
+	}
+
+	// 释放资源
+	~_PER_SOCKET_CONTEXT()
+	{
+		if( m_Socket!=INVALID_SOCKET )
+		{
+			closesocket( m_Socket );
+		    m_Socket = INVALID_SOCKET;
+		}
+		// 释放掉所有的IO上下文数据
+		for( int i=0;i<m_arrayIoContext.GetCount();i++ )
+		{
+			delete m_arrayIoContext.GetAt(i);
+		}
+		m_arrayIoContext.RemoveAll();
+	}
+
+	// 获取一个新的IoContext
+	_PER_IO_CONTEXT* GetNewIoContext()
+	{
+		_PER_IO_CONTEXT* p = new _PER_IO_CONTEXT;
+
+		m_arrayIoContext.Add( p );
+
+		return p;
+	}
+
+	// 从数组中移除一个指定的IoContext
+	void RemoveContext( _PER_IO_CONTEXT* pContext )
+	{
+		ASSERT( pContext!=NULL );
+
+		for( int i=0;i<m_arrayIoContext.GetCount();i++ )
+		{
+			if( pContext==m_arrayIoContext.GetAt(i) )
+			{
+				delete pContext;
+				pContext = NULL;
+				m_arrayIoContext.RemoveAt(i);				
+				break;
+			}
+		}
+	}
+
+} PER_SOCKET_CONTEXT, *PPER_SOCKET_CONTEXT;
+
+
+
+
+//====================================================================================
+//
+//				CIOCPModel类定义
+//
+//====================================================================================
+
+// 工作者线程的线程参数
+class CIOCPModel;
+typedef struct _tagThreadParams_WORKER
+{
+	CIOCPModel* pIOCPModel;                                   // 类指针,用于调用类中的函数
+	int         nThreadNo;                                    // 线程编号
+
+} THREADPARAMS_WORKER,*PTHREADPARAM_WORKER; 
+
+// CIOCPModel类
+class CIOCPModel
+{
+public:
+	CIOCPModel(void);
+	~CIOCPModel(void);
+
+public:
+
+	// 启动服务器
+	bool Start(unsigned int port);
+
+	//	停止服务器
+	void Stop();
+
+	// 加载Socket库
+	bool LoadSocketLib();
+
+	// 卸载Socket库,彻底完事
+	void UnloadSocketLib() { WSACleanup(); }
+
+	// 获得本机的IP地址
+	CString GetLocalIP();
+
+	// 设置监听端口
+	void SetPort( const int& nPort ) { m_nPort=nPort; }
+
+	// 设置主界面的指针,用于调用显示信息到界面中
+	void SetMainDlg( CDialog* p ) { m_pMain=p; }
+
+protected:
+
+	// 初始化IOCP
+	bool _InitializeIOCP();
+
+	// 初始化Socket
+	bool _InitializeListenSocket(unsigned int port);
+
+	// 最后释放资源
+	void _DeInitialize();
+
+	// 投递Accept请求
+	bool _PostAccept( PER_IO_CONTEXT* pAcceptIoContext ); 
+
+	// 投递接收数据请求
+	bool _PostRecv( PER_IO_CONTEXT* pIoContext );
+
+	// 在有客户端连入的时候,进行处理
+	bool _DoAccpet( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext );
+
+	// 在有接收的数据到达的时候,进行处理
+	bool _DoRecv( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext );
+
+	// 将客户端的相关信息存储到数组中
+	void _AddToContextList( PER_SOCKET_CONTEXT *pSocketContext );
+
+	// 将客户端的信息从数组中移除
+	void _RemoveContext( PER_SOCKET_CONTEXT *pSocketContext );
+
+	// 清空客户端信息
+	void _ClearContextList();
+
+	// 将句柄绑定到完成端口中
+	bool _AssociateWithIOCP( PER_SOCKET_CONTEXT *pContext);
+
+	// 处理完成端口上的错误
+	bool HandleError( PER_SOCKET_CONTEXT *pContext,const DWORD& dwErr );
+
+	// 线程函数,为IOCP请求服务的工作者线程
+	static DWORD WINAPI _WorkerThread(LPVOID lpParam);
+
+	// 获得本机的处理器数量
+	int _GetNoOfProcessors();
+
+	// 判断客户端Socket是否已经断开
+	bool _IsSocketAlive(SOCKET s);
+
+	// 在主界面中显示信息
+	void _ShowMessage( const CString szFormat,...) const;
+
+	void _RecvProcess(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext);
+	void _DeviceProc(PER_IO_CONTEXT* pIoContext, Package *pak);
+private:
+
+	HANDLE                       m_hShutdownEvent;              // 用来通知线程系统退出的事件,为了能够更好的退出线程
+
+	HANDLE                       m_hIOCompletionPort;           // 完成端口的句柄
+
+	HANDLE*                      m_phWorkerThreads;             // 工作者线程的句柄指针
+
+	int		                     m_nThreads;                    // 生成的线程数量
+
+	CString                      m_strIP;                       // 服务器端的IP地址
+	int                          m_nPort;                       // 服务器端的监听端口
+
+	CDialog*                     m_pMain;                       // 主界面的界面指针,用于在主界面中显示消息
+
+	CRITICAL_SECTION             m_csContextList;               // 用于Worker线程同步的互斥量
+
+	CArray<PER_SOCKET_CONTEXT*>  m_arrayClientContext;          // 客户端Socket的Context信息        
+
+	PER_SOCKET_CONTEXT*          m_pListenContext;              // 用于监听的Socket的Context信息
+
+	LPFN_ACCEPTEX                m_lpfnAcceptEx;                // AcceptEx 和 GetAcceptExSockaddrs 的函数指针,用于调用这两个扩展函数
+	LPFN_GETACCEPTEXSOCKADDRS    m_lpfnGetAcceptExSockAddrs; 
+
+};
+

+ 135 - 0
SATService/SATService/SATExecutor.cpp

@@ -0,0 +1,135 @@
+#include "StdAfx.h"
+#include "SATExecutor.h"
+
+
+CSATExecutor::CSATExecutor(void)
+{
+	m_hEventHearbeat = NULL;
+	m_hThreadHearbeat = NULL;
+	m_hEventExcuteScript = NULL;
+	m_hThreadExcuteScript = NULL;
+}
+
+CSATExecutor::~CSATExecutor(void)
+{
+}
+
+bool CSATExecutor::IsTaskExist(SATParameters::STTask &task)
+{
+	bool found = false;
+	std::vector<SATParameters::STTask>::iterator it = m_vtTask.begin();
+	for ( ; it != m_vtTask.end(); it++ )
+	{
+		if ( it->nTaskId == task.nTaskId )
+		{
+			found = true;
+			break;
+		}
+	}
+
+	return found;
+}
+
+void CSATExecutor::StartWork()
+{
+	m_hEventHearbeat = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if ( m_hEventHearbeat == NULL )
+	{
+		_tprintf_s(_T("创建事件失败\n"));
+		return;
+	}
+
+	m_hEventExcuteScript = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if ( m_hEventExcuteScript == NULL )
+	{
+		_tprintf_s(_T("创建事件失败2\n"));
+		return;
+	}
+
+	m_hThreadHearbeat = CreateThread(NULL, 0, HearbeatThread, this, 0, NULL);
+	if ( m_hThreadHearbeat == NULL)
+	{
+		SetEvent(m_hEventHearbeat);
+		SetEvent(m_hEventExcuteScript);
+		CloseHandle(m_hEventHearbeat);
+		CloseHandle(m_hEventExcuteScript);
+		m_hEventHearbeat = NULL;
+		m_hEventExcuteScript = NULL;
+		_tprintf_s(_T("创建线程失败\n"));
+		return;
+	}
+
+	m_hThreadExcuteScript = CreateThread(NULL, 0, ExecuteScriptThread, this, 0, NULL);
+	if ( m_hThreadExcuteScript == NULL )
+	{
+		SetEvent(m_hEventHearbeat);
+		SetEvent(m_hEventExcuteScript);
+		CloseHandle(m_hEventHearbeat);
+		CloseHandle(m_hEventExcuteScript);
+		m_hEventHearbeat = NULL;
+		m_hEventExcuteScript = NULL;
+		WaitForSingleObject(m_hThreadHearbeat,INFINITE);
+		if (m_hThreadHearbeat)
+			CloseHandle(m_hThreadHearbeat);
+		m_hThreadHearbeat = NULL;
+		_tprintf_s(_T("创建线程失败2\n"));
+		return;
+	}
+}
+
+void CSATExecutor::EndofWork()
+{
+
+}
+
+DWORD CSATExecutor::HearbeatThread(LPVOID lpVoid)
+{
+	CSATExecutor *that = (CSATExecutor*)lpVoid;
+	if ( !that )
+		return 0;
+	
+	do 
+	{
+		// 没有登录成功,不查询;
+		if ( !that->m_bLogin )
+			continue;
+		
+		SATParameters::STHeartbeatReq stHeartbeatReq;
+		SATParameters::STHeartbeatResp stHeartbeatResp;
+		std::string url = Global::g_stSATConfig.szExecuteServer;
+		url.append("/ajaxInteractiveManage!executeHeartbeat.action");
+		
+		stHeartbeatReq.strUserName = that->m_stLoginReq.strUserName;
+		stHeartbeatReq.strRunnerMac = that->m_stLoginReq.strMAC;
+		stHeartbeatReq.devicelist.assign(that->m_vtDevice.begin(), that->m_vtDevice.end());
+		if ( Heartbeat(url, stHeartbeatReq, stHeartbeatResp) )
+		{
+			std::vector<SATParameters::STTask>::iterator it = stHeartbeatResp.vtTask.begin();
+			for ( ; it != stHeartbeatResp.vtTask.end(); it++ )
+			{
+				if (!that->IsTaskExist(*it))
+				{
+					that->m_vtTask.push_back(*it);
+				}
+			}
+		}
+		
+	} while ( WaitForSingleObject(that->m_hEventHearbeat, 5000) == WAIT_TIMEOUT );
+
+	return 0;
+}
+
+DWORD CSATExecutor::ExecuteScriptThread(LPVOID lpVoid)
+{
+	CSATExecutor *that = (CSATExecutor*)lpVoid;
+	if ( !that )
+		return 0;
+
+	do 
+	{
+		std::vector<SATParameters::STTask>::iterator it = that->m_vtTask.begin();
+
+	} while ( WaitForSingleObject(that->m_hEventExcuteScript, 10000) == WAIT_TIMEOUT );
+
+	return 0;
+}

+ 59 - 0
SATService/SATService/SATExecutor.h

@@ -0,0 +1,59 @@
+#ifndef __SAT_EXECUTOR__
+#define __SAT_EXECUTOR__
+
+#pragma once
+
+class CSATExecutor
+{
+	CSATExecutor(void);
+public:
+	// 单例对象模式;
+	static CSATExecutor* GetInstance()
+	{
+		static CSATExecutor* pInstance = NULL;
+		if ( pInstance == NULL )
+		{
+			pInstance = new CSATExecutor;
+		}
+
+		return pInstance;
+	}
+
+	~CSATExecutor(void);
+
+protected:
+	// 线程控制句柄;
+	HANDLE  m_hEventHearbeat;
+	// 心跳线程句柄;
+	HANDLE	m_hThreadHearbeat;
+	// 线程控制句柄;
+	HANDLE  m_hEventExcuteScript;
+	// 执行线程句柄;
+	HANDLE	m_hThreadExcuteScript;
+
+	// 是否登录成功;
+	BOOL	m_bLogin;
+
+	std::vector<SATParameters::STTask> m_vtTask;
+	std::vector<SATParameters::STDevice> m_vtDevice;
+	SATParameters::STLoginReq	m_stLoginReq;
+	SATParameters::STLoginResp	m_stLoginResp;
+
+
+	bool IsTaskExist(SATParameters::STTask &task);
+
+	// 其他接口;
+public:
+	// 工作开始函数;	
+	void StartWork();
+	// 工作结束函数;
+	void EndofWork();
+
+	// 心跳线程;
+	static DWORD WINAPI HearbeatThread(LPVOID lpVoid);
+	// 执行线程;
+	static DWORD WINAPI ExecuteScriptThread(LPVOID lpVoid);
+};
+
+
+#endif //__SAT_EXECUTOR__

+ 10 - 0
SATService/SATService/SATQuerier.cpp

@@ -0,0 +1,10 @@
+#include "StdAfx.h"
+#include "SATQuerier.h"
+
+CSATQuerier::CSATQuerier(void)
+{
+}
+
+CSATQuerier::~CSATQuerier(void)
+{
+}

+ 46 - 0
SATService/SATService/SATQuerier.h

@@ -0,0 +1,46 @@
+#ifndef __SAT_QUERIER__
+#define __SAT_QUERIER__
+
+#pragma once
+
+class CSATQuerier
+{
+	CSATQuerier(void);
+public:
+	// 单例对象模式;
+	static CSATQuerier* GetInstance()
+	{
+		static CSATQuerier* pInstance = NULL;
+		if ( pInstance == NULL )
+		{
+			pInstance = new CSATQuerier;
+		}
+
+		return pInstance;
+	}
+
+	~CSATQuerier(void);
+
+protected:
+	// 线程控制句柄;
+	HANDLE  m_hEvent1;
+	// 检查服务线程句柄;
+	HANDLE	m_hCheckServices;
+	// 线程控制句柄;
+	HANDLE  m_hEvent2;
+	// 备份数据库线程句柄;
+	HANDLE	m_hBackupDatabase;
+
+	// 其他接口;
+public:
+	// 工作开始函数;	
+	void StartWork();
+	// 工作结束函数;
+	void EndofWork();
+
+	// 检测服务在线线程;
+	static DWORD WINAPI CheckSvcStatusThread(LPVOID lpVoid);
+	static DWORD WINAPI BackupDatabaseThread(LPVOID lpVoid);
+};
+
+#endif // __SAT_QUERIER__

+ 1 - 1
SATService/SATService/SATService.cpp

@@ -54,7 +54,7 @@ int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
 	{
 		// TODO: 在此处为应用程序的行为编写代码。
 #ifdef _DEBUG
-		//return test();
+		return test();
 #endif
 		Global::GetIniInfo();
 

+ 128 - 20
SATService/SATService/SATService.vcproj

@@ -42,6 +42,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
+				AdditionalIncludeDirectories="../cJSON;C:\Python27\Include"
 				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
@@ -63,6 +64,7 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="SATHTTP.lib RunPython.lib"
 				LinkIncremental="2"
+				AdditionalLibraryDirectories="C:\Python27\libs"
 				UACExecutionLevel="2"
 				GenerateDebugInformation="true"
 				SubSystem="1"
@@ -118,6 +120,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="2"
 				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="../cJSON;C:\Python27\Include"
 				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
 				RuntimeLibrary="2"
 				EnableFunctionLevelLinking="true"
@@ -138,6 +141,7 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="SATHTTP.lib RunPython.lib"
 				LinkIncremental="1"
+				AdditionalLibraryDirectories="C:\Python27\libs"
 				UACExecutionLevel="2"
 				GenerateDebugInformation="true"
 				SubSystem="1"
@@ -180,10 +184,6 @@
 				RelativePath=".\Global.cpp"
 				>
 			</File>
-			<File
-				RelativePath=".\MainProcess.cpp"
-				>
-			</File>
 			<File
 				RelativePath=".\SATService.cpp"
 				>
@@ -208,10 +208,6 @@
 					/>
 				</FileConfiguration>
 			</File>
-			<File
-				RelativePath=".\test.cpp"
-				>
-			</File>
 			<File
 				RelativePath=".\WindowService.cpp"
 				>
@@ -223,11 +219,11 @@
 			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
 			>
 			<File
-				RelativePath=".\Global.h"
+				RelativePath=".\CritSection.h"
 				>
 			</File>
 			<File
-				RelativePath=".\MainProcess.h"
+				RelativePath=".\Global.h"
 				>
 			</File>
 			<File
@@ -238,6 +234,40 @@
 				RelativePath=".\Resource.h"
 				>
 			</File>
+			<File
+				RelativePath=".\SATService.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\targetver.h"
+				>
+			</File>
+			<File
+				RelativePath=".\WindowService.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="×ÊÔ´Îļþ"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+			<File
+				RelativePath=".\main.ico"
+				>
+			</File>
+			<File
+				RelativePath=".\SATService.rc"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="lib"
+			>
 			<File
 				RelativePath=".\RunPython.h"
 				>
@@ -247,37 +277,115 @@
 				>
 			</File>
 			<File
-				RelativePath=".\SATService.h"
+				RelativePath=".\test.cpp"
 				>
 			</File>
+		</Filter>
+		<Filter
+			Name="tinyxml"
+			>
 			<File
-				RelativePath=".\stdafx.h"
+				RelativePath=".\stdint.h"
 				>
 			</File>
 			<File
-				RelativePath=".\targetver.h"
+				RelativePath=".\tinyxml2.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="0"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="0"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\tinyxml2.h"
 				>
 			</File>
+		</Filter>
+		<Filter
+			Name="cJson"
+			>
 			<File
-				RelativePath=".\WindowService.h"
+				RelativePath="..\cJson\cJSON.c"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="0"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="0"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\cJson\cJSON.h"
 				>
 			</File>
 		</Filter>
 		<Filter
-			Name="×ÊÔ´Îļþ"
-			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
-			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			Name="TCP"
 			>
 			<File
-				RelativePath=".\icon1.ico"
+				RelativePath=".\IOCPModel.cpp"
 				>
 			</File>
 			<File
-				RelativePath=".\main.ico"
+				RelativePath=".\IOCPModel.h"
 				>
 			</File>
+		</Filter>
+		<Filter
+			Name="Work"
+			>
 			<File
-				RelativePath=".\SATService.rc"
+				RelativePath=".\MainProcess.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\MainProcess.h"
+				>
+			</File>
+			<File
+				RelativePath=".\SATExecutor.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\SATExecutor.h"
+				>
+			</File>
+			<File
+				RelativePath=".\SATQuerier.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\SATQuerier.h"
+				>
+			</File>
+			<File
+				RelativePath=".\ScriptExecutor.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\ScriptExecutor.h"
 				>
 			</File>
 		</Filter>

+ 714 - 0
SATService/SATService/ScriptExecutor.cpp

@@ -0,0 +1,714 @@
+#include "StdAfx.h"
+#include "ScriptExecutor.h"
+#include <fstream>
+#include <io.h>
+#include <fcntl.h>
+#include "Python.h"
+
+CScriptExecutor::CScriptExecutor(void)
+{
+	m_hWorkThread = NULL;
+	m_hWorkEvent = NULL;
+	m_hLogThread = NULL;
+	m_hLogEvent = NULL;
+	m_bStatus = FALSE;
+	m_nRunType = 0;
+	m_hStdOutRead = NULL;
+	m_hStdOutWrite = NULL;
+	m_hStdErrorWrite = NULL;
+	m_hOldStdOutWrite = NULL;
+
+	memset(m_szScriptPath, 0, MAX_PATH);
+	memset(m_szLogPath, 0, MAX_PATH);
+	memset(m_szExtraSentence, 0, MAX_PATH);
+
+	memset(&m_si, 0, sizeof(m_si));
+	memset(&m_pi, 0, sizeof(m_pi));
+	m_si.cb = sizeof(m_si);
+
+	m_bKillProcess = FALSE;
+	m_bStopLogExport = FALSE;
+	m_dwThreadId = 0;
+	m_dwSubprocessId = 0;
+	m_bRuned = FALSE;
+}
+
+CScriptExecutor::~CScriptExecutor(void)
+{
+}
+
+DWORD CScriptExecutor::_WorkerThread(LPVOID lpParam)
+{
+	CScriptExecutor* that = (CScriptExecutor*)lpParam;
+	if ( !that )
+	{
+		printf("Error:参数失效\n");
+		return -1;
+	}
+
+	if ( that->m_nRunType == EMBEDDED )
+	{
+		that->RedirectProcessStdout();
+		ResumeThread(that->m_hLogThread);
+		that->RunEmbeddedScript();
+	}
+	else if (that->m_nRunType == SUBPROCESS)
+	{
+		that->RunScripProcess();
+	}
+
+	CloseHandle(that->m_hWorkThread);
+	that->m_hWorkThread = NULL;
+
+	// 结束日志线程;
+	that->m_bStopLogExport = TRUE;
+
+	return 0;
+}
+
+DWORD CScriptExecutor::_LogExportThread(LPVOID lpParam)
+{
+	CScriptExecutor* that = (CScriptExecutor*)lpParam;
+	if (!that)
+	{
+		printf("Error:参数失效\n");
+		return -1;
+	}
+
+	if (that->m_nRunType == SUBPROCESS)
+	{
+		DWORD dwRead;
+		CHAR chBuf[BUFSIZE] = {0};
+		BOOL bSuccess = FALSE;
+		
+		do
+		{
+			bSuccess = ReadFile(that->m_hStdOutRead, chBuf, BUFSIZE, &dwRead, NULL);
+			if (!bSuccess || dwRead == 0 || !_tcslen(chBuf)) 
+				continue;
+
+			Global::WritePythonLog(that->m_szLogPath, chBuf);
+			memset(chBuf, 0, BUFSIZE);
+		} while (!that->m_bStopLogExport);
+	}
+	else if ( that->m_nRunType == EMBEDDED)
+	{
+		DWORD dwRead;
+		CHAR chBuf[BUFSIZE] = {0};
+		BOOL bSuccess = FALSE;
+
+		do
+		{	
+			bSuccess = ReadFile(that->m_hStdOutRead, chBuf, BUFSIZE, &dwRead, NULL);
+			if (!bSuccess || dwRead == 0 || !_tcslen(chBuf)) 
+				continue;
+
+			Global::WritePythonLog(that->m_szLogPath, chBuf);
+			memset(chBuf, 0, BUFSIZE);
+		} while (!that->m_bStopLogExport);
+	}
+
+	// 关闭线程句柄;
+	CloseHandle(that->m_hLogThread);
+	that->m_hLogThread = NULL;
+
+	// 结束重定向;
+	that->EndSubprocessStdOut();
+
+	return 0;
+}
+
+int CScriptExecutor::RedirectSubprocessStdout(LPSTARTUPINFO si /*=NULL*/)
+{
+#ifdef _DEBUG
+	OutputDebugString("RedirectSubprocessStdout\n");
+#endif
+
+	// Python脚本中,必须使用sys.__stdout__()
+	if (m_nRunType == SUBPROCESS)
+	{
+		SECURITY_ATTRIBUTES sa;
+		sa.bInheritHandle = TRUE;
+		sa.lpSecurityDescriptor = NULL;
+		sa.nLength = sizeof(sa);
+
+		// 创建stdout的管道;
+		if (!CreatePipe(&m_hStdOutRead, &m_hStdOutWrite, &sa, 0))
+		{
+			OutputDebugString("Error:创建stdout管道失败\n");
+			return -1;
+		}
+
+		// 创建stderr的管道,由于stderr一般就是stdout,直接复制句柄;
+		if (!DuplicateHandle(GetCurrentProcess(), m_hStdOutWrite, GetCurrentProcess(), &m_hStdErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
+		{
+			OutputDebugString("创建stderr管道失败\n");
+			return -2;
+		}
+
+		si->dwFlags |= STARTF_USESTDHANDLES;
+		// 将子进程的stdout输出到句柄hStdOutWrite;
+		si->hStdOutput = m_hStdOutWrite;
+		// 将子进程的stderr输出到句柄hStdErrWrite;
+		si->hStdError = m_hStdErrorWrite;
+
+		return 0;
+	}
+
+	// 异常类型;
+	OutputDebugString("Error:异常类型\n");
+
+	return -5;
+}
+
+int CScriptExecutor::RedirectProcessStdout()
+{
+#ifdef _DEBUG
+	OutputDebugString("RedirectProcessStdout\n");
+#endif
+	if (m_nRunType == EMBEDDED)
+	{
+		// 创建stdout的管道;
+		if (!CreatePipe(&m_hStdOutRead, &m_hStdOutWrite, NULL, 0))
+		{
+			printf("Error:创建stdout管道失败\n");
+			return -1;
+		}
+
+		// 将 C 运行时文件描述符与现有操作系统文件句柄关联;
+		int wpfd = _open_osfhandle((intptr_t)m_hStdOutWrite, _O_TEXT);
+		// 为stdout分配新文件描述符;
+		if (_dup2(wpfd, _fileno(stdout)) != 0)
+		{
+			printf("Error:dup2调用失败");
+			return -2;
+		}
+		_close(wpfd);
+
+		// 备份进程原始标准输出;
+		m_hOldStdOutWrite = GetStdHandle(STD_OUTPUT_HANDLE);
+		// 设备进程标准输出,重定向到管道中;
+		if (!SetStdHandle(STD_OUTPUT_HANDLE, m_hStdOutWrite))
+		{
+			printf("Error:重定向失败\n");
+			return -3;
+		}
+
+		// 设置标准流不使用缓冲,即时写入;
+		setvbuf(stdout, NULL, _IONBF, 0);
+
+		return 0;
+	}
+
+	// 异常类型;
+	printf("Error:异常类型\n");
+
+	return -5;
+}
+
+void CScriptExecutor::CatchPythonException()
+{
+	if (!Py_IsInitialized())
+	{
+		printf("Error:未初始化Python环境\n");
+		return;
+	}
+
+	if (!PyErr_Occurred())
+	{
+		printf("Error:没有异常产生\n");
+		return;
+	}
+
+	// 捕获异常;
+	const char* pszErrMsg = NULL;
+	TCHAR* pszTraceback = NULL;
+
+	PyObject* pPyType = NULL;
+	PyObject* pPyValue = NULL;
+	PyObject* pPyTraceback = NULL;
+	// 非控制台,使用PyErr_Fetch捕获异常;
+	PyErr_Fetch(&pPyType, &pPyValue, &pPyTraceback);
+	PyErr_NormalizeException(&pPyType, &pPyValue, &pPyTraceback); // 可有可无,不影响获取异常;
+	if (pPyValue)
+	{
+		PyObject* pPyStr = PyObject_Str(pPyValue);
+		if (PyString_Check(pPyStr))
+		{
+			pszErrMsg = PyString_AsString(pPyStr);
+			if (pszErrMsg)
+				printf("Error Info=>%s\n", pszErrMsg);
+
+			if (pPyTraceback)
+			{
+				PyObject* pPyTraceModule = PyImport_ImportModule("traceback");
+				if (!pPyTraceModule)
+				{
+					printf("Error:导入traceback模块失败\n");
+					return;
+				}
+#if 1 // 细分出所有换行符;
+				PyObject* pPyModuleDict = PyModule_GetDict(pPyTraceModule);
+				if (pPyModuleDict)
+				{
+					PyObject* pPyFunc = PyDict_GetItemString(pPyModuleDict, "format_exception");
+					if (!pPyFunc || !PyCallable_Check(pPyFunc))
+					{
+						printf("Error:加载format_exception失败\n");
+						return;
+					}
+
+					PyObject* pErroList = PyObject_CallFunctionObjArgs(pPyFunc, pPyType, pPyValue, pPyTraceback, NULL);
+					if (pErroList)
+					{
+						int nSize = PyList_Size(pErroList);
+						for (int i = 0; i < nSize; i++)
+						{
+							pszErrMsg = PyString_AsString(PyList_GetItem(pErroList, i));
+							printf("%s", pszErrMsg);
+						}
+					}
+					Py_DECREF(pErroList);
+				}
+
+				Py_XDECREF(pPyTraceModule);
+#else // 不细分Item;
+				PyObject* pPyFunc = PyObject_GetAttrString(pPyTraceModule, "format_exception");
+				if (!pPyFunc || !PyCallable_Check(pPyFunc))
+				{
+					printf("Error:加载format_exception失败\n");
+					return;
+				}
+
+				PyObject* pPyResult = PyObject_CallFunctionObjArgs(pPyFunc, pPyType, pPyValue, pPyTraceback, NULL);
+				pPyStr = PyObject_Str(pPyResult);
+				pszErrMsg = PyString_AsString(pPyStr);
+				if (pszErrMsg)
+					printf("%s\n", pszErrMsg);
+
+				Py_DECREF(pPyResult);
+				Py_XDECREF(pPyTraceModule);
+#endif
+			}
+		}
+	}
+
+	Py_XDECREF(pPyType);
+	Py_XDECREF(pPyValue);
+	Py_XDECREF(pPyTraceback);
+}
+
+int CScriptExecutor::RunEmbeddedScript()
+{
+	// 参数有效性判断;
+	if (!PathFileExists(m_szScriptPath))
+	{
+		printf("Error:脚本文件不存在\n");
+		return -1;
+	}
+
+	// 初始化Python环境;
+	if (!Py_IsInitialized())
+		Py_Initialize();
+
+	if (!Py_IsInitialized())
+	{
+		printf("Error:初始化Python环境失败\n");
+		return -2;
+	}
+
+	// 解析脚本路径和脚本名称;
+	std::string scriptdir;
+	TCHAR szDrive[_MAX_DRIVE] = { 0 };
+	TCHAR szDir[_MAX_DIR] = { 0 };
+	TCHAR szExt[_MAX_EXT] = { 0 };
+	TCHAR szFilename[_MAX_FNAME] = { 0 };
+	TCHAR szScriptDir[MAX_PATH] = { 0 };
+	_tsplitpath_s(m_szScriptPath, szDrive, szDir, szFilename, szExt);
+	_stprintf_s(szScriptDir, _T("%s%s"), szDrive, szDir);
+	// 缓存一份路径;
+	scriptdir = szScriptDir;
+	// 将'\'转换成'/', Python才设置运行目录成功;
+	int len = _tcslen(szScriptDir);
+	for (int i = 0; i < len; i++)
+	{
+		if (szScriptDir[i] == '\\')
+			szScriptDir[i] = '/';
+	}
+	//szScriptDir[len-1] = '\0';
+
+	TCHAR szExecuteDir[MAX_PATH] = { 0 };
+	_stprintf_s(szExecuteDir, _T("sys.path.append(\"%s\")"), szScriptDir);
+	// 添加系统模块,并指定当前脚本路径为运行路径;
+	PyRun_SimpleString("import sys");
+	PyRun_SimpleString(szExecuteDir);
+	// 运行额外的语句,一般用于传递命令行参数;
+	if (_tcslen(m_szExtraSentence))
+		PyRun_SimpleString(m_szExtraSentence);
+
+	// 注意:脚本名称尽量不要与系统目录其他py文件相同;
+	// 导入脚本,以脚本名称为模块名加载;
+	PyObject* pModuleObj = PyImport_ImportModule(szFilename);
+	if (!pModuleObj)
+	{
+		printf("Error:加载脚本模块失败\n");
+		Py_Finalize();
+		return -3;
+	}
+
+	// 获取脚本的主函数名称(规定所有脚本的主函数名为main)
+	// 加载函数:注意,如果目录下有同名的脚本文件,可能会加载失败;
+	PyObject* pFunction = PyObject_GetAttrString(pModuleObj, "main");
+	if (!pFunction || !PyCallable_Check(pFunction))
+	{
+		printf("Error:加载函数失败\n");
+		Py_Finalize();
+		return -4;
+	}
+
+	m_bRuned = TRUE; 
+	// 执行main函数;
+	PyObject* pResult = PyObject_CallObject(pFunction, NULL);
+	if (!pResult)
+	{
+		printf("Error:执行函数失败\n");
+		CatchPythonException();
+		Py_Finalize();
+		
+		return -5;
+	}
+
+	Py_DECREF(pResult);
+
+	Py_Finalize();
+
+	return 0;
+}
+
+int CScriptExecutor::RunScripProcess()
+{
+	if (!PathFileExists(m_szScriptPath))
+	{
+		printf("Error:脚本路径无效\n");
+		return -1;
+	}
+
+	// 初始化参数;
+	::memset(&m_si, 0, sizeof(m_si));
+	::memset(&m_pi, 0, sizeof(m_pi));
+	m_si.cb = sizeof(m_si);
+	GetStartupInfo(&m_si); 
+
+	// 强制stdion, stdout和stderr完全无缓冲:python -u
+	TCHAR szCommandLine[MAX_PATH] = { 0 };
+	if (_tcslen(m_szExtraSentence))
+		_stprintf_s(szCommandLine, _T("python -W ignore -u %s %s"), m_szScriptPath, m_szExtraSentence);
+	else
+		_stprintf_s(szCommandLine, _T("python -W ignore -u %s"), m_szScriptPath);
+
+	// 重定向输出;
+	RedirectSubprocessStdout(&m_si);
+	// 恢复日志线程;
+	ResumeThread(m_hLogThread);
+
+	// 启动子进程;
+	if (!CreateProcess(
+		NULL,					// No module name (use command line)
+		szCommandLine,			// Command line
+		NULL,					// Process handle not inheritable
+		NULL,					// Thread handle not inheritable
+		TRUE,					// Set handle inheritance to TRUE
+		0,						// No creation flags
+		NULL,					// Use parent's environment block
+		NULL,					// Use parent's starting directory 
+		&m_si,					// Pointer to STARTUPINFO structure
+		&m_pi)					// Pointer to PROCESS_INFORMATION structure
+		)
+	{
+		printf("Error:创建子进程失败 (%d).\n", GetLastError());
+		return -3;
+	}
+
+	m_bRuned = TRUE;
+	m_dwSubprocessId = m_pi.dwProcessId;
+	// 等待进程完成退出.
+	WaitForSingleObject(m_pi.hProcess, INFINITE);
+
+	Sleep(5000);
+	// 结束日志线程;
+	m_bStopLogExport = TRUE;
+
+	// 关闭进程句柄. 
+	CloseHandle(m_pi.hProcess);
+	CloseHandle(m_pi.hThread);
+
+	// 重置;
+	memset(&m_si, 0, sizeof(m_si));
+	memset(&m_pi, 0, sizeof(m_pi));
+	m_si.cb = sizeof(m_si);
+
+	return 0;
+}
+
+bool CScriptExecutor::StartWorkThread()
+{
+	// 创建线程;
+	m_hWorkThread = CreateThread(NULL, 0, _WorkerThread, this, 0, &m_dwThreadId);
+	if (!m_hWorkThread)
+	{
+		printf("Error:创建线程失败\n");
+		return false;
+	}
+
+	return true;
+}
+
+bool CScriptExecutor::StartLogThread()
+{
+	printf("StartLogThread\n");
+	// 创建线程事件:手动控制,无信号状态;
+	m_hLogEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (!m_hLogEvent)
+	{
+		printf("Error:无法为线程创建控制事件\n");
+		return false;
+	}
+
+	// 创建线程;
+	m_hLogThread = CreateThread(NULL, 0, _LogExportThread, this, CREATE_SUSPENDED, NULL);
+	if (!m_hLogThread)
+	{
+		printf("Error:创建线程失败\n");
+		CloseHandle(m_hLogEvent);
+		m_hLogEvent = NULL;
+
+		return false;
+	}
+
+	return true;
+}
+
+void CScriptExecutor::EndWorkThread()
+{
+	// 等待线程结束;
+	if (m_hWorkThread)
+	{
+		// 尝试5次结束行为;
+		for (int i = 0; i < 5; i++)
+		{
+			if (TerminateThread(m_hWorkThread, 0))
+				break;
+		}
+
+		CloseHandle(m_hWorkThread);
+		m_hWorkThread = NULL;
+	}
+}
+
+void CScriptExecutor::EndLogThread()
+{
+	// 设置事件有信号;
+	if (m_hLogEvent)
+		SetEvent(m_hLogEvent);
+
+	// 等待线程结束;
+	if (m_hLogThread)
+	{
+		WaitForSingleObject(m_hLogThread, INFINITE);
+		CloseHandle(m_hLogThread);
+		m_hLogThread = NULL;
+	}
+
+	// 关闭句柄;
+	if (m_hLogEvent)
+		CloseHandle(m_hLogEvent);
+	m_hLogEvent = NULL;
+}
+
+void CScriptExecutor::EndThread(HANDLE hThread, HANDLE hEvent)
+{
+	// 设置事件有信号;
+	if (hEvent)
+		SetEvent(hEvent);
+
+	// 等待线程结束;
+	if (hThread)
+	{
+		WaitForSingleObject(hThread, INFINITE);
+		CloseHandle(hThread);
+		hThread = NULL;
+	}
+
+	// 关闭句柄;
+	if (hEvent)
+		CloseHandle(hEvent);
+	hEvent = NULL;
+}
+
+BOOL CScriptExecutor::EndSubprocess()
+{
+	BOOL ret = false;
+	if (m_pi.hProcess)
+	{
+		// 尝试5次结束;
+		for (int i = 0; i < 5; i++)
+		{
+			if ( (ret = TerminateProcess(m_pi.hProcess, 0)) )
+				break;
+		}
+	}
+
+	return ret;
+}
+
+void CScriptExecutor::EndProcessStdOut()
+{
+	OutputDebugString("--------------EndProcessStdOut----------------\n");
+#if 0 // 恢复失败,代码有问题
+	// 恢复标准输出(嵌入进程脚本);
+	if ( m_nRunType == EMBEDDED && m_hOldStdOutWrite)
+	{
+		int fn = _fileno(stdout);
+		// 恢复进程默认标准输出;
+		int fd = _open_osfhandle((intptr_t)m_hOldStdOutWrite, _O_RDWR);
+		if ( _dup2(fd, _fileno(stdout)) == 0)
+		{
+			_close(fd);
+			// 设置标准输出;
+			SetStdHandle(STD_OUTPUT_HANDLE, m_hOldStdOutWrite);
+			m_hOldStdOutWrite = NULL;
+			// 设置标准流不使用缓冲,即时写入;
+			setvbuf(stdout, NULL, _IONBF, 0);
+		}
+		else
+		{
+			printf("Error:_dup2分配文件描述符失败\n");
+		}			
+	}
+#endif
+
+	// 关闭重定向句柄;
+	if (m_hStdErrorWrite)
+		CloseHandle(m_hStdErrorWrite);
+	m_hStdErrorWrite = NULL;
+
+	// 只有子进程方式才关闭句柄;
+	if ( m_hStdOutWrite)
+		CloseHandle(m_hStdOutWrite);
+	m_hStdOutWrite = NULL;
+
+	if (m_hStdOutRead)
+		CloseHandle(m_hStdOutRead);
+	m_hStdOutRead = NULL;	
+}
+
+void CScriptExecutor::EndSubprocessStdOut()
+{
+	OutputDebugString("--------------EndSubprocessStdOut----------------\n");
+	// 关闭重定向句柄;
+	if (m_hStdErrorWrite)
+		CloseHandle(m_hStdErrorWrite);
+	m_hStdErrorWrite = NULL;
+
+	// 只有子进程方式才关闭句柄;
+	if ( m_hStdOutWrite)
+		CloseHandle(m_hStdOutWrite);
+	m_hStdOutWrite = NULL;
+
+	if (m_hStdOutRead)
+		CloseHandle(m_hStdOutRead);
+	m_hStdOutRead = NULL;	
+}
+
+void CScriptExecutor::InitScript(std::string strScript, std::string strLogPath, std::string strScriptCmd, int nRunType /*= PY_RUN_TYPE::EMBEDDED*/)
+{
+	// 判断脚本是否存在;
+	if (!PathFileExists(strScript.c_str()))
+	{
+		printf("Error:脚本文件不存在\n");
+		return;
+	}
+
+	// 判断日志文件路径是否可创建;
+	if (!PathFileExists(strLogPath.c_str()))
+	{
+		// 创建路径;
+		if (!Global::MKDIR(strLogPath.c_str()))
+		{
+			printf("Error:创建目录失败\n");
+			return;
+		}
+
+		// 创建文件;
+		std::ofstream flog(strLogPath.c_str());
+		if ( flog.bad() )
+		{
+			printf("Error:创建文件失败\n");
+			return;
+		}
+		flog.close();
+	}
+
+	// 赋值参数;
+	m_bRuned = FALSE;
+	m_nRunType = nRunType;
+	m_dwThreadId = m_dwSubprocessId = 0;
+	_tcscpy_s(m_szScriptPath, strScript.c_str());
+	_tcscpy_s(m_szLogPath, strLogPath.c_str());
+	_tcscpy_s(m_szExtraSentence, strScriptCmd.c_str());
+}
+
+bool CScriptExecutor::StartScript()
+{
+	if (StartWorkThread())
+	{
+		// 开启日志导出线程;
+		if (!StartLogThread())
+		{
+			printf("Error:日志导出线程创建出错\n");
+		}
+
+		Sleep(100);
+
+		return true;
+	}
+
+	// 异常类型;
+	printf("Error:异常类型脚本\n");
+
+	return false;
+}
+
+void CScriptExecutor::StopScript()
+{
+	// 如果是子进程运行脚本,停止线程时kill进程;
+	if (m_nRunType == SUBPROCESS)
+		EndSubprocess();
+
+	// 结束线程;
+	EndWorkThread();
+}
+
+bool CScriptExecutor::IsScriptOver()
+{
+	int i = 10;
+	while (!m_bRuned)
+	{
+		Sleep(300);
+		if ( --i == 0 )
+			break;
+	}
+
+	if (m_nRunType == EMBEDDED)
+	{
+		if ( m_hWorkThread == NULL )
+			return true;
+	}
+	else if (m_nRunType == SUBPROCESS)
+	{
+		if ( m_pi.hProcess == NULL && m_hWorkThread == NULL )
+			return true;
+	}
+
+	return false;
+}

+ 104 - 0
SATService/SATService/ScriptExecutor.h

@@ -0,0 +1,104 @@
+#ifndef __SCRIPT_EXECUTOR__
+#define __SCRIPT_EXECUTOR__
+
+#pragma once
+
+#define BUFSIZE 4096
+enum PY_RUN_TYPE
+{
+	EMBEDDED,			// 嵌入到本进程运行;
+	SUBPROCESS			// 以子进程方式运行;
+};
+
+class CScriptExecutor
+{
+public:
+	CScriptExecutor(void);
+	~CScriptExecutor(void);
+
+protected:
+	HANDLE			m_hWorkThread;						// 事件句柄;
+	HANDLE			m_hWorkEvent;						// 线程事件;
+	HANDLE			m_hLogThread;						// 事件句柄;
+	HANDLE			m_hLogEvent;						// 线程事件;
+	BOOL			m_bStatus;							// 线程状态;
+	TCHAR			m_szScriptPath[MAX_PATH];			// 脚本路径;
+	TCHAR			m_szLogPath[MAX_PATH];				// 日志路径;	
+	TCHAR			m_szExtraSentence[MAX_PATH];		// 脚本命令行参数或额外要执行的语句;
+	int				m_nRunType;
+	// 重定向标准输出和标准错误;
+	HANDLE			m_hStdOutRead;
+	HANDLE			m_hStdOutWrite;
+	HANDLE			m_hStdErrorWrite;
+	HANDLE			m_hOldStdOutWrite;					// 用于本进程标准输出备份;
+	// 子进程信息;
+	STARTUPINFO			m_si;
+	PROCESS_INFORMATION	m_pi;
+	// 进程结束标志;
+	BOOL			m_bKillProcess;
+	// 停止日志导出线程;
+	BOOL			m_bStopLogExport;
+	// 执行器执行时间;
+	CTime			m_tStartTime;
+	// 执行器结束时间;
+	CTime			m_tEndTime;
+	// 子进程ID;
+	DWORD			m_dwSubprocessId;
+	// 线程ID;
+	DWORD			m_dwThreadId;
+	// 脚本是否成功运行;
+	BOOL			m_bRuned;
+protected:
+	// 工作者线程函数;
+	static DWORD WINAPI _WorkerThread(LPVOID lpParam);
+	// 日志导出线程;
+	static DWORD WINAPI _LogExportThread(LPVOID lpParam);
+	// 重定向本进程标准输出;
+	int RedirectProcessStdout();
+	// 重定子进程向标准输出;
+	int RedirectSubprocessStdout(LPSTARTUPINFO si = NULL);	
+	// 获取Python异常;
+	void CatchPythonException();
+	// 运行嵌入的脚本;
+	int RunEmbeddedScript();
+	// 运行脚本进程;
+	int RunScripProcess();
+	// 开启线程;
+	bool StartWorkThread();
+	bool StartLogThread();
+	// 结束线程;
+	void EndWorkThread();
+	void EndLogThread();
+	void EndThread(HANDLE hThread, HANDLE hEvent);
+	// 结束子进程;
+	BOOL EndSubprocess();
+	// 从管道读取日志;
+	void ReadFromPipe();
+	// 结束重定向句柄,恢复默认;
+	void EndProcessStdOut();
+	void EndSubprocessStdOut();
+public:
+	// 初始脚本运行参数;
+	void InitScript(
+		std::string strScript, 
+		std::string strLogPath, 
+		std::string strScriptCmd, 
+		int nRunType = EMBEDDED);
+	// 运行脚本;
+	bool StartScript();
+	// 停止脚本;
+	void StopScript();
+	// 脚本是否结束;
+	bool IsScriptOver();
+	// 运行类型;
+	int GetRunType() const { return m_nRunType;}
+	// 获取脚本Id;
+	DWORD GetScriptId() const {
+		if ( m_nRunType == SUBPROCESS )
+			return m_dwSubprocessId;
+		else
+			return m_dwThreadId;
+	}
+};
+
+#endif // __SCRIPT_EXECUTOR__

+ 2 - 1
SATService/SATService/stdafx.h

@@ -28,9 +28,10 @@
 #include <iostream>
 #include <string>
 #include <vector>
+#include <Shlwapi.h>
 #include "SATHTTP.h"
 #include "RunPython.h"
 #include "WindowService.h"
 #include "Global.h"
-
+#include "cJSON.h"
 // TODO: 在此处引用程序需要的其他头文件

+ 259 - 0
SATService/SATService/stdint.h

@@ -0,0 +1,259 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006-2013 Alexander Chemeris
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+// 
+//   3. Neither the name of the product nor the names of its contributors may
+//      be used to endorse or promote products derived from this software
+//      without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#if _MSC_VER >= 2000 // [
+#include <stdint.h>
+#else // ] _MSC_VER >= 1600 [
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+#  include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#     define _W64 __w64
+#  else
+#     define _W64
+#  endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+   typedef signed char       int8_t;
+   typedef signed short      int16_t;
+   typedef signed int        int32_t;
+   typedef unsigned char     uint8_t;
+   typedef unsigned short    uint16_t;
+   typedef unsigned int      uint32_t;
+#else
+   typedef signed __int8     int8_t;
+   typedef signed __int16    int16_t;
+   typedef signed __int32    int32_t;
+   typedef unsigned __int8   uint8_t;
+   typedef unsigned __int16  uint16_t;
+   typedef unsigned __int32  uint32_t;
+#endif
+typedef signed __int64       int64_t;
+typedef unsigned __int64     uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t    int_least8_t;
+typedef int16_t   int_least16_t;
+typedef int32_t   int_least32_t;
+typedef int64_t   int_least64_t;
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+typedef uint64_t  uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t    int_fast8_t;
+typedef int16_t   int_fast16_t;
+typedef int32_t   int_fast32_t;
+typedef int64_t   int_fast64_t;
+typedef uint8_t   uint_fast8_t;
+typedef uint16_t  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+typedef uint64_t  uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+   typedef signed __int64    intptr_t;
+   typedef unsigned __int64  uintptr_t;
+#else // _WIN64 ][
+   typedef _W64 signed int   intptr_t;
+   typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t   intmax_t;
+typedef uint64_t  uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN     ((int8_t)_I8_MIN)
+#define INT8_MAX     _I8_MAX
+#define INT16_MIN    ((int16_t)_I16_MIN)
+#define INT16_MAX    _I16_MAX
+#define INT32_MIN    ((int32_t)_I32_MIN)
+#define INT32_MAX    _I32_MAX
+#define INT64_MIN    ((int64_t)_I64_MIN)
+#define INT64_MAX    _I64_MAX
+#define UINT8_MAX    _UI8_MAX
+#define UINT16_MAX   _UI16_MAX
+#define UINT32_MAX   _UI32_MAX
+#define UINT64_MAX   _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN    INT8_MIN
+#define INT_LEAST8_MAX    INT8_MAX
+#define INT_LEAST16_MIN   INT16_MIN
+#define INT_LEAST16_MAX   INT16_MAX
+#define INT_LEAST32_MIN   INT32_MIN
+#define INT_LEAST32_MAX   INT32_MAX
+#define INT_LEAST64_MIN   INT64_MIN
+#define INT_LEAST64_MAX   INT64_MAX
+#define UINT_LEAST8_MAX   UINT8_MAX
+#define UINT_LEAST16_MAX  UINT16_MAX
+#define UINT_LEAST32_MAX  UINT32_MAX
+#define UINT_LEAST64_MAX  UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN    INT8_MIN
+#define INT_FAST8_MAX    INT8_MAX
+#define INT_FAST16_MIN   INT16_MIN
+#define INT_FAST16_MAX   INT16_MAX
+#define INT_FAST32_MIN   INT32_MIN
+#define INT_FAST32_MAX   INT32_MAX
+#define INT_FAST64_MIN   INT64_MIN
+#define INT_FAST64_MAX   INT64_MAX
+#define UINT_FAST8_MAX   UINT8_MAX
+#define UINT_FAST16_MAX  UINT16_MAX
+#define UINT_FAST32_MAX  UINT32_MAX
+#define UINT_FAST64_MAX  UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+#  define INTPTR_MIN   INT64_MIN
+#  define INTPTR_MAX   INT64_MAX
+#  define UINTPTR_MAX  UINT64_MAX
+#else // _WIN64 ][
+#  define INTPTR_MIN   INT32_MIN
+#  define INTPTR_MAX   INT32_MAX
+#  define UINTPTR_MAX  UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN   INT64_MIN
+#define INTMAX_MAX   INT64_MAX
+#define UINTMAX_MAX  UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+#  define PTRDIFF_MIN  _I64_MIN
+#  define PTRDIFF_MAX  _I64_MAX
+#else  // _WIN64 ][
+#  define PTRDIFF_MIN  _I32_MIN
+#  define PTRDIFF_MAX  _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN  INT_MIN
+#define SIG_ATOMIC_MAX  INT_MAX
+
+#ifndef SIZE_MAX // [
+#  ifdef _WIN64 // [
+#     define SIZE_MAX  _UI64_MAX
+#  else // _WIN64 ][
+#     define SIZE_MAX  _UI32_MAX
+#  endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+#  define WCHAR_MIN  0
+#endif  // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+#  define WCHAR_MAX  _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN  0
+#define WINT_MAX  _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C //   [
+#  define INTMAX_C   INT64_C
+#endif // INTMAX_C    ]
+#ifndef UINTMAX_C //  [
+#  define UINTMAX_C  UINT64_C
+#endif // UINTMAX_C   ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#endif // _MSC_VER >= 1600 ]
+
+#endif // _MSC_STDINT_H_ ]

+ 49 - 38
SATService/SATService/test.cpp

@@ -107,45 +107,48 @@ int test()
 		stLoginResp.strRunnerToken.c_str()
 		);		
 
-	while(true)
+	// ##########################更新设备信息##############################;
+	SATParameters::STUpdateDeviceReq stUpdateDeviceReq;
+	SATParameters::STUpdateDeviceResp stUpdateDeviceResp;
+	__if_not_exists(url)
+	{			
+		std::string url = host + "/btc_execute_se/ajaxInteractiveManage!updateDeviceMessage.action";
+	}
+	__if_exists(url)
 	{
-		// ##########################更新设备信息##############################;
-		SATParameters::STUpdateDeviceReq stUpdateDeviceReq;
-		SATParameters::STUpdateDeviceResp stUpdateDeviceResp;
-		__if_not_exists(url)
-		{			
-			std::string url = host + "/btc_execute_se/ajaxInteractiveManage!updateDeviceMessage.action";
-		}
-		__if_exists(url)
-		{
-			url = host + "/btc_execute_se/ajaxInteractiveManage!updateDeviceMessage.action";
-		}
+		url = host + "/btc_execute_se/ajaxInteractiveManage!updateDeviceMessage.action";
+	}
 
-		stUpdateDeviceReq.strStatus = "0";
-		stUpdateDeviceReq.strUserName = "superAdmin";		
-		stUpdateDeviceReq.strIP = "10.118.158.175";
-		stUpdateDeviceReq.strStorage = "228092536KB";
-		stUpdateDeviceReq.strRunnerName = "SAT-Admin";
-		stUpdateDeviceReq.strMAC = "40:16:7e:23:10:53";
-		stUpdateDeviceReq.strReportType = "1";
-		stUpdateDeviceReq.strStartTime = "2019-12-16 19:15:30";
-		stUpdateDeviceReq.strMemory = "8938544KB";
-		stUpdateDeviceReq.strEndTime = "";
-		stUpdateDeviceReq.strCPU = "24.7%";
-		stUpdateDeviceReq.devicelist.push_back(stDevice);
-
-		if ( UpdateDeviceMessage(url, stUpdateDeviceReq, stUpdateDeviceResp) )
-		{
-			printf("更新设备信息成功:code=%s, msg=%s, str=%s\n",
-				stLoginResp.strCode.c_str(),
-				stLoginResp.strMessage.c_str(),
-				stLoginResp.strStr.c_str());
-		}
-		else
-		{
-			printf("更新设备信息失败!\n");
-		}
+	stUpdateDeviceReq.strStatus = "0";
+	stUpdateDeviceReq.strUserName = "superAdmin";		
+	stUpdateDeviceReq.strIP = "10.118.158.175";
+	stUpdateDeviceReq.strStorage = "228092536KB";
+	stUpdateDeviceReq.strRunnerName = "SAT-Admin";
+	stUpdateDeviceReq.strMAC = "40:16:7e:23:10:53";
+	stUpdateDeviceReq.strReportType = "1";
+	stUpdateDeviceReq.strStartTime = "2019-12-16 19:15:30";
+	stUpdateDeviceReq.strMemory = "8938544KB";
+	stUpdateDeviceReq.strEndTime = "";
+	stUpdateDeviceReq.strCPU = "24.7%";
+	stUpdateDeviceReq.devicelist.push_back(stDevice);
+
+	if ( UpdateDeviceMessage(url, stUpdateDeviceReq, stUpdateDeviceResp) )
+	{
+		printf("更新设备信息成功:code=%s, msg=%s, str=%s\n",
+			stLoginResp.strCode.c_str(),
+			stLoginResp.strMessage.c_str(),
+			stLoginResp.strStr.c_str());
+	}
+	else
+	{
+		printf("更新设备信息失败!\n");
+	}
+
+	//system("pause");
+	//return 1;
 
+	while(true)
+	{
 		// ##########################心跳包机制##############################;
 		SATParameters::STHeartbeatReq stHeartbeatReq;
 		SATParameters::STHeartbeatResp stHeartbeatResp;
@@ -313,8 +316,16 @@ int test()
 							DWORD dwScriptId = RunPyScript(stScriptUrlResp.strScripFile.c_str(), "", "D:\\SAT\\log.txt", TRUE);
 							if ( dwScriptId != -1 )
 							{
+								ULONGLONG ulTickCount = GetTickCount64();
 								while( !IsPyScriptOver(dwScriptId, TRUE) )
-									Sleep(100);
+								{
+									Sleep(1000);
+									//if ( GetTickCount64() - ulTickCount > 1800000)
+									if ( GetTickCount64() - ulTickCount > 1200000)
+									{
+										break;
+									}
+								}
 							}
 							CleanPyScript(dwScriptId);
 						}
@@ -327,7 +338,7 @@ int test()
 			}
 		}
 
-		Sleep(3000);
+		Sleep(30000);
 	}
 
 	

+ 2837 - 0
SATService/SATService/tinyxml2.cpp

@@ -0,0 +1,2837 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include "tinyxml2.h"
+
+#include <new>		// yes, this one new style header, is in the Android SDK.
+#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
+#   include <stddef.h>
+#   include <stdarg.h>
+#else
+#   include <cstddef>
+#   include <cstdarg>
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+	// Microsoft Visual Studio, version 2005 and higher. Not WinCE.
+	/*int _snprintf_s(
+	   char *buffer,
+	   size_t sizeOfBuffer,
+	   size_t count,
+	   const char *format [,
+		  argument] ...
+	);*/
+	static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
+	{
+		va_list va;
+		va_start( va, format );
+		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+		va_end( va );
+		return result;
+	}
+
+	static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
+	{
+		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+		return result;
+	}
+
+	#define TIXML_VSCPRINTF	_vscprintf
+	#define TIXML_SSCANF	sscanf_s
+#elif defined _MSC_VER
+	// Microsoft Visual Studio 2003 and earlier or WinCE
+	#define TIXML_SNPRINTF	_snprintf
+	#define TIXML_VSNPRINTF _vsnprintf
+	#define TIXML_SSCANF	sscanf
+	#if (_MSC_VER < 1400 ) && (!defined WINCE)
+		// Microsoft Visual Studio 2003 and not WinCE.
+		#define TIXML_VSCPRINTF   _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
+	#else
+		// Microsoft Visual Studio 2003 and earlier or WinCE.
+		static inline int TIXML_VSCPRINTF( const char* format, va_list va )
+		{
+			int len = 512;
+			for (;;) {
+				len = len*2;
+				char* str = new char[len]();
+				const int required = _vsnprintf(str, len, format, va);
+				delete[] str;
+				if ( required != -1 ) {
+					TIXMLASSERT( required >= 0 );
+					len = required;
+					break;
+				}
+			}
+			TIXMLASSERT( len >= 0 );
+			return len;
+		}
+	#endif
+#else
+	// GCC version 3 and higher
+	//#warning( "Using sn* functions." )
+	#define TIXML_SNPRINTF	snprintf
+	#define TIXML_VSNPRINTF	vsnprintf
+	static inline int TIXML_VSCPRINTF( const char* format, va_list va )
+	{
+		int len = vsnprintf( 0, 0, format, va );
+		TIXMLASSERT( len >= 0 );
+		return len;
+	}
+	#define TIXML_SSCANF   sscanf
+#endif
+
+
+static const char LINE_FEED				= (char)0x0a;			// all line endings are normalized to LF
+static const char LF = LINE_FEED;
+static const char CARRIAGE_RETURN		= (char)0x0d;			// CR gets filtered out
+static const char CR = CARRIAGE_RETURN;
+static const char SINGLE_QUOTE			= '\'';
+static const char DOUBLE_QUOTE			= '\"';
+
+// Bunch of unicode info at:
+//		http://www.unicode.org/faq/utf_bom.html
+//	ef bb bf (Microsoft "lead bytes") - designates UTF-8
+
+static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+namespace tinyxml2
+{
+
+struct Entity {
+    const char* pattern;
+    int length;
+    char value;
+};
+
+static const int NUM_ENTITIES = 5;
+static const Entity entities[NUM_ENTITIES] = {
+    { "quot", 4,	DOUBLE_QUOTE },
+    { "amp", 3,		'&'  },
+    { "apos", 4,	SINGLE_QUOTE },
+    { "lt",	2, 		'<'	 },
+    { "gt",	2,		'>'	 }
+};
+
+
+StrPair::~StrPair()
+{
+    Reset();
+}
+
+
+void StrPair::TransferTo( StrPair* other )
+{
+    if ( this == other ) {
+        return;
+    }
+    // This in effect implements the assignment operator by "moving"
+    // ownership (as in auto_ptr).
+
+    TIXMLASSERT( other != 0 );
+    TIXMLASSERT( other->_flags == 0 );
+    TIXMLASSERT( other->_start == 0 );
+    TIXMLASSERT( other->_end == 0 );
+
+    other->Reset();
+
+    other->_flags = _flags;
+    other->_start = _start;
+    other->_end = _end;
+
+    _flags = 0;
+    _start = 0;
+    _end = 0;
+}
+
+
+void StrPair::Reset()
+{
+    if ( _flags & NEEDS_DELETE ) {
+        delete [] _start;
+    }
+    _flags = 0;
+    _start = 0;
+    _end = 0;
+}
+
+
+void StrPair::SetStr( const char* str, int flags )
+{
+    TIXMLASSERT( str );
+    Reset();
+    size_t len = strlen( str );
+    TIXMLASSERT( _start == 0 );
+    _start = new char[ len+1 ];
+    memcpy( _start, str, len+1 );
+    _end = _start + len;
+    _flags = flags | NEEDS_DELETE;
+}
+
+
+char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )
+{
+    TIXMLASSERT( p );
+    TIXMLASSERT( endTag && *endTag );
+	TIXMLASSERT(curLineNumPtr);
+
+    char* start = p;
+    char  endChar = *endTag;
+    size_t length = strlen( endTag );
+
+    // Inner loop of text parsing.
+    while ( *p ) {
+        if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
+            Set( start, p, strFlags );
+            return p + length;
+        } else if (*p == '\n') {
+            ++(*curLineNumPtr);
+        }
+        ++p;
+        TIXMLASSERT( p );
+    }
+    return 0;
+}
+
+
+char* StrPair::ParseName( char* p )
+{
+    if ( !p || !(*p) ) {
+        return 0;
+    }
+    if ( !XMLUtil::IsNameStartChar( *p ) ) {
+        return 0;
+    }
+
+    char* const start = p;
+    ++p;
+    while ( *p && XMLUtil::IsNameChar( *p ) ) {
+        ++p;
+    }
+
+    Set( start, p, 0 );
+    return p;
+}
+
+
+void StrPair::CollapseWhitespace()
+{
+    // Adjusting _start would cause undefined behavior on delete[]
+    TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
+    // Trim leading space.
+    _start = XMLUtil::SkipWhiteSpace( _start, 0 );
+
+    if ( *_start ) {
+        const char* p = _start;	// the read pointer
+        char* q = _start;	// the write pointer
+
+        while( *p ) {
+            if ( XMLUtil::IsWhiteSpace( *p )) {
+                p = XMLUtil::SkipWhiteSpace( p, 0 );
+                if ( *p == 0 ) {
+                    break;    // don't write to q; this trims the trailing space.
+                }
+                *q = ' ';
+                ++q;
+            }
+            *q = *p;
+            ++q;
+            ++p;
+        }
+        *q = 0;
+    }
+}
+
+
+const char* StrPair::GetStr()
+{
+    TIXMLASSERT( _start );
+    TIXMLASSERT( _end );
+    if ( _flags & NEEDS_FLUSH ) {
+        *_end = 0;
+        _flags ^= NEEDS_FLUSH;
+
+        if ( _flags ) {
+            const char* p = _start;	// the read pointer
+            char* q = _start;	// the write pointer
+
+            while( p < _end ) {
+                if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
+                    // CR-LF pair becomes LF
+                    // CR alone becomes LF
+                    // LF-CR becomes LF
+                    if ( *(p+1) == LF ) {
+                        p += 2;
+                    }
+                    else {
+                        ++p;
+                    }
+                    *q = LF;
+                    ++q;
+                }
+                else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
+                    if ( *(p+1) == CR ) {
+                        p += 2;
+                    }
+                    else {
+                        ++p;
+                    }
+                    *q = LF;
+                    ++q;
+                }
+                else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
+                    // Entities handled by tinyXML2:
+                    // - special entities in the entity table [in/out]
+                    // - numeric character reference [in]
+                    //   &#20013; or &#x4e2d;
+
+                    if ( *(p+1) == '#' ) {
+                        const int buflen = 10;
+                        char buf[buflen] = { 0 };
+                        int len = 0;
+                        char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
+                        if ( adjusted == 0 ) {
+                            *q = *p;
+                            ++p;
+                            ++q;
+                        }
+                        else {
+                            TIXMLASSERT( 0 <= len && len <= buflen );
+                            TIXMLASSERT( q + len <= adjusted );
+                            p = adjusted;
+                            memcpy( q, buf, len );
+                            q += len;
+                        }
+                    }
+                    else {
+                        bool entityFound = false;
+                        for( int i = 0; i < NUM_ENTITIES; ++i ) {
+                            const Entity& entity = entities[i];
+                            if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
+                                    && *( p + entity.length + 1 ) == ';' ) {
+                                // Found an entity - convert.
+                                *q = entity.value;
+                                ++q;
+                                p += entity.length + 2;
+                                entityFound = true;
+                                break;
+                            }
+                        }
+                        if ( !entityFound ) {
+                            // fixme: treat as error?
+                            ++p;
+                            ++q;
+                        }
+                    }
+                }
+                else {
+                    *q = *p;
+                    ++p;
+                    ++q;
+                }
+            }
+            *q = 0;
+        }
+        // The loop below has plenty going on, and this
+        // is a less useful mode. Break it out.
+        if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
+            CollapseWhitespace();
+        }
+        _flags = (_flags & NEEDS_DELETE);
+    }
+    TIXMLASSERT( _start );
+    return _start;
+}
+
+
+
+
+// --------- XMLUtil ----------- //
+
+const char* XMLUtil::writeBoolTrue  = "true";
+const char* XMLUtil::writeBoolFalse = "false";
+
+void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
+{
+	static const char* defTrue  = "true";
+	static const char* defFalse = "false";
+
+	writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
+	writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
+}
+
+
+const char* XMLUtil::ReadBOM( const char* p, bool* bom )
+{
+    TIXMLASSERT( p );
+    TIXMLASSERT( bom );
+    *bom = false;
+    const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
+    // Check for BOM:
+    if (    *(pu+0) == TIXML_UTF_LEAD_0
+            && *(pu+1) == TIXML_UTF_LEAD_1
+            && *(pu+2) == TIXML_UTF_LEAD_2 ) {
+        *bom = true;
+        p += 3;
+    }
+    TIXMLASSERT( p );
+    return p;
+}
+
+
+void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
+{
+    const unsigned long BYTE_MASK = 0xBF;
+    const unsigned long BYTE_MARK = 0x80;
+    const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+    if (input < 0x80) {
+        *length = 1;
+    }
+    else if ( input < 0x800 ) {
+        *length = 2;
+    }
+    else if ( input < 0x10000 ) {
+        *length = 3;
+    }
+    else if ( input < 0x200000 ) {
+        *length = 4;
+    }
+    else {
+        *length = 0;    // This code won't convert this correctly anyway.
+        return;
+    }
+
+    output += *length;
+
+    // Scary scary fall throughs are annotated with carefully designed comments
+    // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
+    switch (*length) {
+        case 4:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+            //fall through
+        case 3:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+            //fall through
+        case 2:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+            //fall through
+        case 1:
+            --output;
+            *output = (char)(input | FIRST_BYTE_MARK[*length]);
+            break;
+        default:
+            TIXMLASSERT( false );
+    }
+}
+
+
+const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
+{
+    // Presume an entity, and pull it out.
+    *length = 0;
+
+    if ( *(p+1) == '#' && *(p+2) ) {
+        unsigned long ucs = 0;
+        TIXMLASSERT( sizeof( ucs ) >= 4 );
+        ptrdiff_t delta = 0;
+        unsigned mult = 1;
+        static const char SEMICOLON = ';';
+
+        if ( *(p+2) == 'x' ) {
+            // Hexadecimal.
+            const char* q = p+3;
+            if ( !(*q) ) {
+                return 0;
+            }
+
+            q = strchr( q, SEMICOLON );
+
+            if ( !q ) {
+                return 0;
+            }
+            TIXMLASSERT( *q == SEMICOLON );
+
+            delta = q-p;
+            --q;
+
+            while ( *q != 'x' ) {
+                unsigned int digit = 0;
+
+                if ( *q >= '0' && *q <= '9' ) {
+                    digit = *q - '0';
+                }
+                else if ( *q >= 'a' && *q <= 'f' ) {
+                    digit = *q - 'a' + 10;
+                }
+                else if ( *q >= 'A' && *q <= 'F' ) {
+                    digit = *q - 'A' + 10;
+                }
+                else {
+                    return 0;
+                }
+                TIXMLASSERT( digit < 16 );
+                TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
+                const unsigned int digitScaled = mult * digit;
+                TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
+                ucs += digitScaled;
+                TIXMLASSERT( mult <= UINT_MAX / 16 );
+                mult *= 16;
+                --q;
+            }
+        }
+        else {
+            // Decimal.
+            const char* q = p+2;
+            if ( !(*q) ) {
+                return 0;
+            }
+
+            q = strchr( q, SEMICOLON );
+
+            if ( !q ) {
+                return 0;
+            }
+            TIXMLASSERT( *q == SEMICOLON );
+
+            delta = q-p;
+            --q;
+
+            while ( *q != '#' ) {
+                if ( *q >= '0' && *q <= '9' ) {
+                    const unsigned int digit = *q - '0';
+                    TIXMLASSERT( digit < 10 );
+                    TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
+                    const unsigned int digitScaled = mult * digit;
+                    TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
+                    ucs += digitScaled;
+                }
+                else {
+                    return 0;
+                }
+                TIXMLASSERT( mult <= UINT_MAX / 10 );
+                mult *= 10;
+                --q;
+            }
+        }
+        // convert the UCS to UTF-8
+        ConvertUTF32ToUTF8( ucs, value, length );
+        return p + delta + 1;
+    }
+    return p+1;
+}
+
+
+void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
+}
+
+
+void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
+}
+
+
+void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
+}
+
+/*
+	ToStr() of a number is a very tricky topic.
+	https://github.com/leethomason/tinyxml2/issues/106
+*/
+void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
+}
+
+
+void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
+}
+
+
+void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize)
+{
+	// horrible syntax trick to make the compiler happy about %lld
+	TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
+}
+
+
+bool XMLUtil::ToInt( const char* str, int* value )
+{
+    if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
+{
+    if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+bool XMLUtil::ToBool( const char* str, bool* value )
+{
+    int ival = 0;
+    if ( ToInt( str, &ival )) {
+        *value = (ival==0) ? false : true;
+        return true;
+    }
+    if ( StringEqual( str, "true" ) ) {
+        *value = true;
+        return true;
+    }
+    else if ( StringEqual( str, "false" ) ) {
+        *value = false;
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToFloat( const char* str, float* value )
+{
+    if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToDouble( const char* str, double* value )
+{
+    if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToInt64(const char* str, int64_t* value)
+{
+	long long v = 0;	// horrible syntax trick to make the compiler happy about %lld
+	if (TIXML_SSCANF(str, "%lld", &v) == 1) {
+		*value = (int64_t)v;
+		return true;
+	}
+	return false;
+}
+
+
+char* XMLDocument::Identify( char* p, XMLNode** node )
+{
+    TIXMLASSERT( node );
+    TIXMLASSERT( p );
+    char* const start = p;
+    int const startLine = _parseCurLineNum;
+    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
+    if( !*p ) {
+        *node = 0;
+        TIXMLASSERT( p );
+        return p;
+    }
+
+    // These strings define the matching patterns:
+    static const char* xmlHeader		= { "<?" };
+    static const char* commentHeader	= { "<!--" };
+    static const char* cdataHeader		= { "<![CDATA[" };
+    static const char* dtdHeader		= { "<!" };
+    static const char* elementHeader	= { "<" };	// and a header for everything else; check last.
+
+    static const int xmlHeaderLen		= 2;
+    static const int commentHeaderLen	= 4;
+    static const int cdataHeaderLen		= 9;
+    static const int dtdHeaderLen		= 2;
+    static const int elementHeaderLen	= 1;
+
+    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );		// use same memory pool
+    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );	// use same memory pool
+    XMLNode* returnNode = 0;
+    if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += xmlHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += commentHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
+        XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
+        returnNode = text;
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += cdataHeaderLen;
+        text->SetCData( true );
+    }
+    else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += dtdHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
+        returnNode =  CreateUnlinkedNode<XMLElement>( _elementPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += elementHeaderLen;
+    }
+    else {
+        returnNode = CreateUnlinkedNode<XMLText>( _textPool );
+        returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
+        p = start;	// Back it up, all the text counts.
+        _parseCurLineNum = startLine;
+    }
+
+    TIXMLASSERT( returnNode );
+    TIXMLASSERT( p );
+    *node = returnNode;
+    return p;
+}
+
+
+bool XMLDocument::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    if ( visitor->VisitEnter( *this ) ) {
+        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+            if ( !node->Accept( visitor ) ) {
+                break;
+            }
+        }
+    }
+    return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLNode ----------- //
+
+XMLNode::XMLNode( XMLDocument* doc ) :
+    _document( doc ),
+    _parent( 0 ),
+    _value(),
+    _parseLineNum( 0 ),
+    _firstChild( 0 ), _lastChild( 0 ),
+    _prev( 0 ), _next( 0 ),
+	_userData( 0 ),
+    _memPool( 0 )
+{
+}
+
+
+XMLNode::~XMLNode()
+{
+    DeleteChildren();
+    if ( _parent ) {
+        _parent->Unlink( this );
+    }
+}
+
+const char* XMLNode::Value() const
+{
+    // Edge case: XMLDocuments don't have a Value. Return null.
+    if ( this->ToDocument() )
+        return 0;
+    return _value.GetStr();
+}
+
+void XMLNode::SetValue( const char* str, bool staticMem )
+{
+    if ( staticMem ) {
+        _value.SetInternedStr( str );
+    }
+    else {
+        _value.SetStr( str );
+    }
+}
+
+XMLNode* XMLNode::DeepClone(XMLDocument* target) const
+{
+	XMLNode* clone = this->ShallowClone(target);
+	if (!clone) return 0;
+
+	for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
+		XMLNode* childClone = child->DeepClone(target);
+		TIXMLASSERT(childClone);
+		clone->InsertEndChild(childClone);
+	}
+	return clone;
+}
+
+void XMLNode::DeleteChildren()
+{
+    while( _firstChild ) {
+        TIXMLASSERT( _lastChild );
+        DeleteChild( _firstChild );
+    }
+    _firstChild = _lastChild = 0;
+}
+
+
+void XMLNode::Unlink( XMLNode* child )
+{
+    TIXMLASSERT( child );
+    TIXMLASSERT( child->_document == _document );
+    TIXMLASSERT( child->_parent == this );
+    if ( child == _firstChild ) {
+        _firstChild = _firstChild->_next;
+    }
+    if ( child == _lastChild ) {
+        _lastChild = _lastChild->_prev;
+    }
+
+    if ( child->_prev ) {
+        child->_prev->_next = child->_next;
+    }
+    if ( child->_next ) {
+        child->_next->_prev = child->_prev;
+    }
+	child->_next = 0;
+	child->_prev = 0;
+	child->_parent = 0;
+}
+
+
+void XMLNode::DeleteChild( XMLNode* node )
+{
+    TIXMLASSERT( node );
+    TIXMLASSERT( node->_document == _document );
+    TIXMLASSERT( node->_parent == this );
+    Unlink( node );
+	TIXMLASSERT(node->_prev == 0);
+	TIXMLASSERT(node->_next == 0);
+	TIXMLASSERT(node->_parent == 0);
+    DeleteNode( node );
+}
+
+
+XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+    InsertChildPreamble( addThis );
+
+    if ( _lastChild ) {
+        TIXMLASSERT( _firstChild );
+        TIXMLASSERT( _lastChild->_next == 0 );
+        _lastChild->_next = addThis;
+        addThis->_prev = _lastChild;
+        _lastChild = addThis;
+
+        addThis->_next = 0;
+    }
+    else {
+        TIXMLASSERT( _firstChild == 0 );
+        _firstChild = _lastChild = addThis;
+
+        addThis->_prev = 0;
+        addThis->_next = 0;
+    }
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+    InsertChildPreamble( addThis );
+
+    if ( _firstChild ) {
+        TIXMLASSERT( _lastChild );
+        TIXMLASSERT( _firstChild->_prev == 0 );
+
+        _firstChild->_prev = addThis;
+        addThis->_next = _firstChild;
+        _firstChild = addThis;
+
+        addThis->_prev = 0;
+    }
+    else {
+        TIXMLASSERT( _lastChild == 0 );
+        _firstChild = _lastChild = addThis;
+
+        addThis->_prev = 0;
+        addThis->_next = 0;
+    }
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+
+    TIXMLASSERT( afterThis );
+
+    if ( afterThis->_parent != this ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+    if ( afterThis == addThis ) {
+        // Current state: BeforeThis -> AddThis -> OneAfterAddThis
+        // Now AddThis must disappear from it's location and then
+        // reappear between BeforeThis and OneAfterAddThis.
+        // So just leave it where it is.
+        return addThis;
+    }
+
+    if ( afterThis->_next == 0 ) {
+        // The last node or the only node.
+        return InsertEndChild( addThis );
+    }
+    InsertChildPreamble( addThis );
+    addThis->_prev = afterThis;
+    addThis->_next = afterThis->_next;
+    afterThis->_next->_prev = addThis;
+    afterThis->_next = addThis;
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+
+
+const XMLElement* XMLNode::FirstChildElement( const char* name ) const
+{
+    for( const XMLNode* node = _firstChild; node; node = node->_next ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::LastChildElement( const char* name ) const
+{
+    for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
+{
+    for( const XMLNode* node = _next; node; node = node->_next ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
+{
+    for( const XMLNode* node = _prev; node; node = node->_prev ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
+{
+    // This is a recursive method, but thinking about it "at the current level"
+    // it is a pretty simple flat list:
+    //		<foo/>
+    //		<!-- comment -->
+    //
+    // With a special case:
+    //		<foo>
+    //		</foo>
+    //		<!-- comment -->
+    //
+    // Where the closing element (/foo) *must* be the next thing after the opening
+    // element, and the names must match. BUT the tricky bit is that the closing
+    // element will be read by the child.
+    //
+    // 'endTag' is the end tag for this node, it is returned by a call to a child.
+    // 'parentEnd' is the end tag for the parent, which is filled in and returned.
+
+	XMLDocument::DepthTracker tracker(_document);
+	if (_document->Error())
+		return 0;
+
+	while( p && *p ) {
+        XMLNode* node = 0;
+
+        p = _document->Identify( p, &node );
+        TIXMLASSERT( p );
+        if ( node == 0 ) {
+            break;
+        }
+
+        int initialLineNum = node->_parseLineNum;
+
+        StrPair endTag;
+        p = node->ParseDeep( p, &endTag, curLineNumPtr );
+        if ( !p ) {
+            DeleteNode( node );
+            if ( !_document->Error() ) {
+                _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
+            }
+            break;
+        }
+
+        XMLDeclaration* decl = node->ToDeclaration();
+        if ( decl ) {
+            // Declarations are only allowed at document level
+            //
+            // Multiple declarations are allowed but all declarations
+            // must occur before anything else. 
+            //
+            // Optimized due to a security test case. If the first node is 
+            // a declaration, and the last node is a declaration, then only 
+            // declarations have so far been addded.
+            bool wellLocated = false;
+
+            if (ToDocument()) {
+                if (FirstChild()) {
+                    wellLocated =
+                        FirstChild() &&
+                        FirstChild()->ToDeclaration() &&
+                        LastChild() &&
+                        LastChild()->ToDeclaration();
+                }
+                else {
+                    wellLocated = true;
+                }
+            }
+            if ( !wellLocated ) {
+                _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
+                DeleteNode( node );
+                break;
+            }
+        }
+
+        XMLElement* ele = node->ToElement();
+        if ( ele ) {
+            // We read the end tag. Return it to the parent.
+            if ( ele->ClosingType() == XMLElement::CLOSING ) {
+                if ( parentEndTag ) {
+                    ele->_value.TransferTo( parentEndTag );
+                }
+                node->_memPool->SetTracked();   // created and then immediately deleted.
+                DeleteNode( node );
+                return p;
+            }
+
+            // Handle an end tag returned to this level.
+            // And handle a bunch of annoying errors.
+            bool mismatch = false;
+            if ( endTag.Empty() ) {
+                if ( ele->ClosingType() == XMLElement::OPEN ) {
+                    mismatch = true;
+                }
+            }
+            else {
+                if ( ele->ClosingType() != XMLElement::OPEN ) {
+                    mismatch = true;
+                }
+                else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
+                    mismatch = true;
+                }
+            }
+            if ( mismatch ) {
+                _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
+                DeleteNode( node );
+                break;
+            }
+        }
+        InsertEndChild( node );
+    }
+    return 0;
+}
+
+/*static*/ void XMLNode::DeleteNode( XMLNode* node )
+{
+    if ( node == 0 ) {
+        return;
+    }
+	TIXMLASSERT(node->_document);
+	if (!node->ToDocument()) {
+		node->_document->MarkInUse(node);
+	}
+
+    MemPool* pool = node->_memPool;
+    node->~XMLNode();
+    pool->Free( node );
+}
+
+void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
+{
+    TIXMLASSERT( insertThis );
+    TIXMLASSERT( insertThis->_document == _document );
+
+	if (insertThis->_parent) {
+        insertThis->_parent->Unlink( insertThis );
+	}
+	else {
+		insertThis->_document->MarkInUse(insertThis);
+        insertThis->_memPool->SetTracked();
+	}
+}
+
+const XMLElement* XMLNode::ToElementWithName( const char* name ) const
+{
+    const XMLElement* element = this->ToElement();
+    if ( element == 0 ) {
+        return 0;
+    }
+    if ( name == 0 ) {
+        return element;
+    }
+    if ( XMLUtil::StringEqual( element->Name(), name ) ) {
+       return element;
+    }
+    return 0;
+}
+
+// --------- XMLText ---------- //
+char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    if ( this->CData() ) {
+        p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+        if ( !p ) {
+            _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
+        }
+        return p;
+    }
+    else {
+        int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
+        if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
+            flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
+        }
+
+        p = _value.ParseText( p, "<", flags, curLineNumPtr );
+        if ( p && *p ) {
+            return p-1;
+        }
+        if ( !p ) {
+            _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
+        }
+    }
+    return 0;
+}
+
+
+XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLText* text = doc->NewText( Value() );	// fixme: this will always allocate memory. Intern?
+    text->SetCData( this->CData() );
+    return text;
+}
+
+
+bool XMLText::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLText* text = compare->ToText();
+    return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
+}
+
+
+bool XMLText::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+
+// --------- XMLComment ---------- //
+
+XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLComment::~XMLComment()
+{
+}
+
+
+char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Comment parses as text.
+    p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
+    if ( p == 0 ) {
+        _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
+    }
+    return p;
+}
+
+
+XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLComment* comment = doc->NewComment( Value() );	// fixme: this will always allocate memory. Intern?
+    return comment;
+}
+
+
+bool XMLComment::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLComment* comment = compare->ToComment();
+    return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
+}
+
+
+bool XMLComment::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+
+// --------- XMLDeclaration ---------- //
+
+XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLDeclaration::~XMLDeclaration()
+{
+    //printf( "~XMLDeclaration\n" );
+}
+
+
+char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Declaration parses as text.
+    p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+    if ( p == 0 ) {
+        _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
+    }
+    return p;
+}
+
+
+XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLDeclaration* dec = doc->NewDeclaration( Value() );	// fixme: this will always allocate memory. Intern?
+    return dec;
+}
+
+
+bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLDeclaration* declaration = compare->ToDeclaration();
+    return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
+}
+
+
+
+bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+// --------- XMLUnknown ---------- //
+
+XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLUnknown::~XMLUnknown()
+{
+}
+
+
+char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Unknown parses as text.
+    p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+    if ( !p ) {
+        _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
+    }
+    return p;
+}
+
+
+XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLUnknown* text = doc->NewUnknown( Value() );	// fixme: this will always allocate memory. Intern?
+    return text;
+}
+
+
+bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLUnknown* unknown = compare->ToUnknown();
+    return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
+}
+
+
+bool XMLUnknown::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+// --------- XMLAttribute ---------- //
+
+const char* XMLAttribute::Name() const
+{
+    return _name.GetStr();
+}
+
+const char* XMLAttribute::Value() const
+{
+    return _value.GetStr();
+}
+
+char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
+{
+    // Parse using the name rules: bug fix, was using ParseText before
+    p = _name.ParseName( p );
+    if ( !p || !*p ) {
+        return 0;
+    }
+
+    // Skip white space before =
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+    if ( *p != '=' ) {
+        return 0;
+    }
+
+    ++p;	// move up to opening quote
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+    if ( *p != '\"' && *p != '\'' ) {
+        return 0;
+    }
+
+    char endTag[2] = { *p, 0 };
+    ++p;	// move past opening quote
+
+    p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
+    return p;
+}
+
+
+void XMLAttribute::SetName( const char* n )
+{
+    _name.SetStr( n );
+}
+
+
+XMLError XMLAttribute::QueryIntValue( int* value ) const
+{
+    if ( XMLUtil::ToInt( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
+{
+    if ( XMLUtil::ToUnsigned( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
+{
+	if (XMLUtil::ToInt64(Value(), value)) {
+		return XML_SUCCESS;
+	}
+	return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryBoolValue( bool* value ) const
+{
+    if ( XMLUtil::ToBool( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryFloatValue( float* value ) const
+{
+    if ( XMLUtil::ToFloat( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryDoubleValue( double* value ) const
+{
+    if ( XMLUtil::ToDouble( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+void XMLAttribute::SetAttribute( const char* v )
+{
+    _value.SetStr( v );
+}
+
+
+void XMLAttribute::SetAttribute( int v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute( unsigned v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute(int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	_value.SetStr(buf);
+}
+
+
+
+void XMLAttribute::SetAttribute( bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( double v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( float v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+// --------- XMLElement ---------- //
+XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
+    _closingType( OPEN ),
+    _rootAttribute( 0 )
+{
+}
+
+
+XMLElement::~XMLElement()
+{
+    while( _rootAttribute ) {
+        XMLAttribute* next = _rootAttribute->_next;
+        DeleteAttribute( _rootAttribute );
+        _rootAttribute = next;
+    }
+}
+
+
+const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
+{
+    for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
+        if ( XMLUtil::StringEqual( a->Name(), name ) ) {
+            return a;
+        }
+    }
+    return 0;
+}
+
+
+const char* XMLElement::Attribute( const char* name, const char* value ) const
+{
+    const XMLAttribute* a = FindAttribute( name );
+    if ( !a ) {
+        return 0;
+    }
+    if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
+        return a->Value();
+    }
+    return 0;
+}
+
+int XMLElement::IntAttribute(const char* name, int defaultValue) const
+{
+	int i = defaultValue;
+	QueryIntAttribute(name, &i);
+	return i;
+}
+
+unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
+{
+	unsigned i = defaultValue;
+	QueryUnsignedAttribute(name, &i);
+	return i;
+}
+
+int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
+{
+	int64_t i = defaultValue;
+	QueryInt64Attribute(name, &i);
+	return i;
+}
+
+bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
+{
+	bool b = defaultValue;
+	QueryBoolAttribute(name, &b);
+	return b;
+}
+
+double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
+{
+	double d = defaultValue;
+	QueryDoubleAttribute(name, &d);
+	return d;
+}
+
+float XMLElement::FloatAttribute(const char* name, float defaultValue) const
+{
+	float f = defaultValue;
+	QueryFloatAttribute(name, &f);
+	return f;
+}
+
+const char* XMLElement::GetText() const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        return FirstChild()->Value();
+    }
+    return "";
+}
+
+
+void	XMLElement::SetText( const char* inText )
+{
+	if ( FirstChild() && FirstChild()->ToText() )
+		FirstChild()->SetValue( inText );
+	else {
+		XMLText*	theText = GetDocument()->NewText( inText );
+		InsertFirstChild( theText );
+	}
+}
+
+
+void XMLElement::SetText( int v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( unsigned v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText(int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	SetText(buf);
+}
+
+
+void XMLElement::SetText( bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( float v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( double v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+XMLError XMLElement::QueryIntText( int* ival ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToInt( t, ival ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToUnsigned( t, uval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryInt64Text(int64_t* ival) const
+{
+	if (FirstChild() && FirstChild()->ToText()) {
+		const char* t = FirstChild()->Value();
+		if (XMLUtil::ToInt64(t, ival)) {
+			return XML_SUCCESS;
+		}
+		return XML_CAN_NOT_CONVERT_TEXT;
+	}
+	return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryBoolText( bool* bval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToBool( t, bval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryDoubleText( double* dval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToDouble( t, dval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryFloatText( float* fval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToFloat( t, fval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+int XMLElement::IntText(int defaultValue) const
+{
+	int i = defaultValue;
+	QueryIntText(&i);
+	return i;
+}
+
+unsigned XMLElement::UnsignedText(unsigned defaultValue) const
+{
+	unsigned i = defaultValue;
+	QueryUnsignedText(&i);
+	return i;
+}
+
+int64_t XMLElement::Int64Text(int64_t defaultValue) const
+{
+	int64_t i = defaultValue;
+	QueryInt64Text(&i);
+	return i;
+}
+
+bool XMLElement::BoolText(bool defaultValue) const
+{
+	bool b = defaultValue;
+	QueryBoolText(&b);
+	return b;
+}
+
+double XMLElement::DoubleText(double defaultValue) const
+{
+	double d = defaultValue;
+	QueryDoubleText(&d);
+	return d;
+}
+
+float XMLElement::FloatText(float defaultValue) const
+{
+	float f = defaultValue;
+	QueryFloatText(&f);
+	return f;
+}
+
+
+XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
+{
+    XMLAttribute* last = 0;
+    XMLAttribute* attrib = 0;
+    for( attrib = _rootAttribute;
+            attrib;
+            last = attrib, attrib = attrib->_next ) {
+        if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
+            break;
+        }
+    }
+    if ( !attrib ) {
+        attrib = CreateAttribute();
+        TIXMLASSERT( attrib );
+        if ( last ) {
+            TIXMLASSERT( last->_next == 0 );
+            last->_next = attrib;
+        }
+        else {
+            TIXMLASSERT( _rootAttribute == 0 );
+            _rootAttribute = attrib;
+        }
+        attrib->SetName( name );
+    }
+    return attrib;
+}
+
+
+void XMLElement::DeleteAttribute( const char* name )
+{
+    XMLAttribute* prev = 0;
+    for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
+        if ( XMLUtil::StringEqual( name, a->Name() ) ) {
+            if ( prev ) {
+                prev->_next = a->_next;
+            }
+            else {
+                _rootAttribute = a->_next;
+            }
+            DeleteAttribute( a );
+            break;
+        }
+        prev = a;
+    }
+}
+
+
+char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
+{
+    XMLAttribute* prevAttribute = 0;
+
+    // Read the attributes.
+    while( p ) {
+        p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+        if ( !(*p) ) {
+            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
+            return 0;
+        }
+
+        // attribute.
+        if (XMLUtil::IsNameStartChar( *p ) ) {
+            XMLAttribute* attrib = CreateAttribute();
+            TIXMLASSERT( attrib );
+            attrib->_parseLineNum = _document->_parseCurLineNum;
+
+            int attrLineNum = attrib->_parseLineNum;
+
+            p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
+            if ( !p || Attribute( attrib->Name() ) ) {
+                DeleteAttribute( attrib );
+                _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
+                return 0;
+            }
+            // There is a minor bug here: if the attribute in the source xml
+            // document is duplicated, it will not be detected and the
+            // attribute will be doubly added. However, tracking the 'prevAttribute'
+            // avoids re-scanning the attribute list. Preferring performance for
+            // now, may reconsider in the future.
+            if ( prevAttribute ) {
+                TIXMLASSERT( prevAttribute->_next == 0 );
+                prevAttribute->_next = attrib;
+            }
+            else {
+                TIXMLASSERT( _rootAttribute == 0 );
+                _rootAttribute = attrib;
+            }
+            prevAttribute = attrib;
+        }
+        // end of the tag
+        else if ( *p == '>' ) {
+            ++p;
+            break;
+        }
+        // end of the tag
+        else if ( *p == '/' && *(p+1) == '>' ) {
+            _closingType = CLOSED;
+            return p+2;	// done; sealed element.
+        }
+        else {
+            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
+            return 0;
+        }
+    }
+    return p;
+}
+
+void XMLElement::DeleteAttribute( XMLAttribute* attribute )
+{
+    if ( attribute == 0 ) {
+        return;
+    }
+    MemPool* pool = attribute->_memPool;
+    attribute->~XMLAttribute();
+    pool->Free( attribute );
+}
+
+XMLAttribute* XMLElement::CreateAttribute()
+{
+    TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
+    XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
+    TIXMLASSERT( attrib );
+    attrib->_memPool = &_document->_attributePool;
+    attrib->_memPool->SetTracked();
+    return attrib;
+}
+
+//
+//	<ele></ele>
+//	<ele>foo<b>bar</b></ele>
+//
+char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
+{
+    // Read the element name.
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+
+    // The closing element is the </element> form. It is
+    // parsed just like a regular element then deleted from
+    // the DOM.
+    if ( *p == '/' ) {
+        _closingType = CLOSING;
+        ++p;
+    }
+
+    p = _value.ParseName( p );
+    if ( _value.Empty() ) {
+        return 0;
+    }
+
+    p = ParseAttributes( p, curLineNumPtr );
+    if ( !p || !*p || _closingType != OPEN ) {
+        return p;
+    }
+
+    p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
+    return p;
+}
+
+
+
+XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLElement* element = doc->NewElement( Value() );					// fixme: this will always allocate memory. Intern?
+    for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
+        element->SetAttribute( a->Name(), a->Value() );					// fixme: this will always allocate memory. Intern?
+    }
+    return element;
+}
+
+
+bool XMLElement::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLElement* other = compare->ToElement();
+    if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
+
+        const XMLAttribute* a=FirstAttribute();
+        const XMLAttribute* b=other->FirstAttribute();
+
+        while ( a && b ) {
+            if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
+                return false;
+            }
+            a = a->Next();
+            b = b->Next();
+        }
+        if ( a || b ) {
+            // different count
+            return false;
+        }
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLElement::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
+        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+            if ( !node->Accept( visitor ) ) {
+                break;
+            }
+        }
+    }
+    return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLDocument ----------- //
+
+// Warning: List must match 'enum XMLError'
+const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
+    "XML_SUCCESS",
+    "XML_NO_ATTRIBUTE",
+    "XML_WRONG_ATTRIBUTE_TYPE",
+    "XML_ERROR_FILE_NOT_FOUND",
+    "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
+    "XML_ERROR_FILE_READ_ERROR",
+    "XML_ERROR_PARSING_ELEMENT",
+    "XML_ERROR_PARSING_ATTRIBUTE",
+    "XML_ERROR_PARSING_TEXT",
+    "XML_ERROR_PARSING_CDATA",
+    "XML_ERROR_PARSING_COMMENT",
+    "XML_ERROR_PARSING_DECLARATION",
+    "XML_ERROR_PARSING_UNKNOWN",
+    "XML_ERROR_EMPTY_DOCUMENT",
+    "XML_ERROR_MISMATCHED_ELEMENT",
+    "XML_ERROR_PARSING",
+    "XML_CAN_NOT_CONVERT_TEXT",
+    "XML_NO_TEXT_NODE",
+	"XML_ELEMENT_DEPTH_EXCEEDED"
+};
+
+
+XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
+    XMLNode( 0 ),
+    _writeBOM( false ),
+    _processEntities( processEntities ),
+    _errorID(XML_SUCCESS),
+    _whitespaceMode( whitespaceMode ),
+    _errorStr(),
+    _errorLineNum( 0 ),
+    _charBuffer( 0 ),
+    _parseCurLineNum( 0 ),
+	_parsingDepth(0),
+    _unlinked(),
+    _elementPool(),
+    _attributePool(),
+    _textPool(),
+    _commentPool()
+{
+    // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
+    _document = this;
+}
+
+
+XMLDocument::~XMLDocument()
+{
+    Clear();
+}
+
+
+void XMLDocument::MarkInUse(XMLNode* node)
+{
+	TIXMLASSERT(node);
+	TIXMLASSERT(node->_parent == 0);
+
+	for (int i = 0; i < _unlinked.Size(); ++i) {
+		if (node == _unlinked[i]) {
+			_unlinked.SwapRemove(i);
+			break;
+		}
+	}
+}
+
+void XMLDocument::Clear()
+{
+    DeleteChildren();
+	while( _unlinked.Size()) {
+		DeleteNode(_unlinked[0]);	// Will remove from _unlinked as part of delete.
+	}
+
+#ifdef TINYXML2_DEBUG
+    const bool hadError = Error();
+#endif
+    ClearError();
+
+    delete [] _charBuffer;
+    _charBuffer = 0;
+	_parsingDepth = 0;
+
+#if 0
+    _textPool.Trace( "text" );
+    _elementPool.Trace( "element" );
+    _commentPool.Trace( "comment" );
+    _attributePool.Trace( "attribute" );
+#endif
+
+#ifdef TINYXML2_DEBUG
+    if ( !hadError ) {
+        TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );
+        TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
+        TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );
+        TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );
+    }
+#endif
+}
+
+
+void XMLDocument::DeepCopy(XMLDocument* target) const
+{
+	TIXMLASSERT(target);
+    if (target == this) {
+        return; // technically success - a no-op.
+    }
+
+	target->Clear();
+	for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
+		target->InsertEndChild(node->DeepClone(target));
+	}
+}
+
+XMLElement* XMLDocument::NewElement( const char* name )
+{
+    XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
+    ele->SetName( name );
+    return ele;
+}
+
+
+XMLComment* XMLDocument::NewComment( const char* str )
+{
+    XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
+    comment->SetValue( str );
+    return comment;
+}
+
+
+XMLText* XMLDocument::NewText( const char* str )
+{
+    XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
+    text->SetValue( str );
+    return text;
+}
+
+
+XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
+{
+    XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
+    dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
+    return dec;
+}
+
+
+XMLUnknown* XMLDocument::NewUnknown( const char* str )
+{
+    XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
+    unk->SetValue( str );
+    return unk;
+}
+
+static FILE* callfopen( const char* filepath, const char* mode )
+{
+    TIXMLASSERT( filepath );
+    TIXMLASSERT( mode );
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+    FILE* fp = 0;
+    errno_t err = fopen_s( &fp, filepath, mode );
+    if ( err ) {
+        return 0;
+    }
+#else
+    FILE* fp = fopen( filepath, mode );
+#endif
+    return fp;
+}
+
+void XMLDocument::DeleteNode( XMLNode* node )	{
+    TIXMLASSERT( node );
+    TIXMLASSERT(node->_document == this );
+    if (node->_parent) {
+        node->_parent->DeleteChild( node );
+    }
+    else {
+        // Isn't in the tree.
+        // Use the parent delete.
+        // Also, we need to mark it tracked: we 'know'
+        // it was never used.
+        node->_memPool->SetTracked();
+        // Call the static XMLNode version:
+        XMLNode::DeleteNode(node);
+    }
+}
+
+
+XMLError XMLDocument::LoadFile( const char* filename )
+{
+    if ( !filename ) {
+        TIXMLASSERT( false );
+        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
+        return _errorID;
+    }
+
+    Clear();
+    FILE* fp = callfopen( filename, "rb" );
+    if ( !fp ) {
+        SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
+        return _errorID;
+    }
+    LoadFile( fp );
+    fclose( fp );
+    return _errorID;
+}
+
+// This is likely overengineered template art to have a check that unsigned long value incremented
+// by one still fits into size_t. If size_t type is larger than unsigned long type
+// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
+// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
+// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
+// types sizes relate to each other.
+template
+<bool = (sizeof(unsigned long) >= sizeof(size_t))>
+struct LongFitsIntoSizeTMinusOne {
+    static bool Fits( unsigned long value )
+    {
+        return value < (size_t)-1;
+    }
+};
+
+template <>
+struct LongFitsIntoSizeTMinusOne<false> {
+    static bool Fits( unsigned long )
+    {
+        return true;
+    }
+};
+
+XMLError XMLDocument::LoadFile( FILE* fp )
+{
+    Clear();
+
+    fseek( fp, 0, SEEK_SET );
+    if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+        return _errorID;
+    }
+
+    fseek( fp, 0, SEEK_END );
+    const long filelength = ftell( fp );
+    fseek( fp, 0, SEEK_SET );
+    if ( filelength == -1L ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+        return _errorID;
+    }
+    TIXMLASSERT( filelength >= 0 );
+
+    if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
+        // Cannot handle files which won't fit in buffer together with null terminator
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+        return _errorID;
+    }
+
+    if ( filelength == 0 ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+        return _errorID;
+    }
+
+    const size_t size = filelength;
+    TIXMLASSERT( _charBuffer == 0 );
+    _charBuffer = new char[size+1];
+    size_t read = fread( _charBuffer, 1, size, fp );
+    if ( read != size ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+        return _errorID;
+    }
+
+    _charBuffer[size] = 0;
+
+    Parse();
+    return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( const char* filename, bool compact )
+{
+    if ( !filename ) {
+        TIXMLASSERT( false );
+        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
+        return _errorID;
+    }
+
+    FILE* fp = callfopen( filename, "w" );
+    if ( !fp ) {
+        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
+        return _errorID;
+    }
+    SaveFile(fp, compact);
+    fclose( fp );
+    return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
+{
+    // Clear any error from the last save, otherwise it will get reported
+    // for *this* call.
+    ClearError();
+    XMLPrinter stream( fp, compact );
+    Print( &stream );
+    return _errorID;
+}
+
+
+XMLError XMLDocument::Parse( const char* p, size_t len )
+{
+    Clear();
+
+    if ( len == 0 || !p || !*p ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+        return _errorID;
+    }
+    if ( len == (size_t)(-1) ) {
+        len = strlen( p );
+    }
+    TIXMLASSERT( _charBuffer == 0 );
+    _charBuffer = new char[ len+1 ];
+    memcpy( _charBuffer, p, len );
+    _charBuffer[len] = 0;
+
+    Parse();
+    if ( Error() ) {
+        // clean up now essentially dangling memory.
+        // and the parse fail can put objects in the
+        // pools that are dead and inaccessible.
+        DeleteChildren();
+        _elementPool.Clear();
+        _attributePool.Clear();
+        _textPool.Clear();
+        _commentPool.Clear();
+    }
+    return _errorID;
+}
+
+
+void XMLDocument::Print( XMLPrinter* streamer ) const
+{
+    if ( streamer ) {
+        Accept( streamer );
+    }
+    else {
+        XMLPrinter stdoutStreamer( stdout );
+        Accept( &stdoutStreamer );
+    }
+}
+
+
+void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
+{
+    TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
+    _errorID = error;
+    _errorLineNum = lineNum;
+	_errorStr.Reset();
+
+    size_t BUFFER_SIZE = 1000;
+    char* buffer = new char[BUFFER_SIZE];
+
+    TIXMLASSERT(sizeof(error) <= sizeof(int));
+    TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
+
+	if (format) {
+		size_t len = strlen(buffer);
+		TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
+		len = strlen(buffer);
+
+		va_list va;
+		va_start(va, format);
+		TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
+		va_end(va);
+	}
+	_errorStr.SetStr(buffer);
+	delete[] buffer;
+}
+
+
+/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
+{
+	TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
+    const char* errorName = _errorNames[errorID];
+    TIXMLASSERT( errorName && errorName[0] );
+    return errorName;
+}
+
+const char* XMLDocument::ErrorStr() const
+{
+	return _errorStr.Empty() ? "" : _errorStr.GetStr();
+}
+
+
+void XMLDocument::PrintError() const
+{
+    printf("%s\n", ErrorStr());
+}
+
+const char* XMLDocument::ErrorName() const
+{
+    return ErrorIDToName(_errorID);
+}
+
+void XMLDocument::Parse()
+{
+    TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
+    TIXMLASSERT( _charBuffer );
+    _parseCurLineNum = 1;
+    _parseLineNum = 1;
+    char* p = _charBuffer;
+    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
+    p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
+    if ( !*p ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+        return;
+    }
+    ParseDeep(p, 0, &_parseCurLineNum );
+}
+
+void XMLDocument::PushDepth()
+{
+	_parsingDepth++;
+	if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
+		SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
+	}
+}
+
+void XMLDocument::PopDepth()
+{
+	TIXMLASSERT(_parsingDepth > 0);
+	--_parsingDepth;
+}
+
+XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
+    _elementJustOpened( false ),
+    _stack(),
+    _firstElement( true ),
+    _fp( file ),
+    _depth( depth ),
+    _textDepth( -1 ),
+    _processEntities( true ),
+    _compactMode( compact ),
+    _buffer()
+{
+    for( int i=0; i<ENTITY_RANGE; ++i ) {
+        _entityFlag[i] = false;
+        _restrictedEntityFlag[i] = false;
+    }
+    for( int i=0; i<NUM_ENTITIES; ++i ) {
+        const char entityValue = entities[i].value;
+        const unsigned char flagIndex = (unsigned char)entityValue;
+        TIXMLASSERT( flagIndex < ENTITY_RANGE );
+        _entityFlag[flagIndex] = true;
+    }
+    _restrictedEntityFlag[(unsigned char)'&'] = true;
+    _restrictedEntityFlag[(unsigned char)'<'] = true;
+    _restrictedEntityFlag[(unsigned char)'>'] = true;	// not required, but consistency is nice
+    _buffer.Push( 0 );
+}
+
+
+void XMLPrinter::Print( const char* format, ... )
+{
+    va_list     va;
+    va_start( va, format );
+
+    if ( _fp ) {
+        vfprintf( _fp, format, va );
+    }
+    else {
+        const int len = TIXML_VSCPRINTF( format, va );
+        // Close out and re-start the va-args
+        va_end( va );
+        TIXMLASSERT( len >= 0 );
+        va_start( va, format );
+        TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
+        char* p = _buffer.PushArr( len ) - 1;	// back up over the null terminator.
+		TIXML_VSNPRINTF( p, len+1, format, va );
+    }
+    va_end( va );
+}
+
+
+void XMLPrinter::Write( const char* data, size_t size )
+{
+    if ( _fp ) {
+        fwrite ( data , sizeof(char), size, _fp);
+    }
+    else {
+        char* p = _buffer.PushArr( static_cast<int>(size) ) - 1;   // back up over the null terminator.
+        memcpy( p, data, size );
+        p[size] = 0;
+    }
+}
+
+
+void XMLPrinter::Putc( char ch )
+{
+    if ( _fp ) {
+        fputc ( ch, _fp);
+    }
+    else {
+        char* p = _buffer.PushArr( sizeof(char) ) - 1;   // back up over the null terminator.
+        p[0] = ch;
+        p[1] = 0;
+    }
+}
+
+
+void XMLPrinter::PrintSpace( int depth )
+{
+    for( int i=0; i<depth; ++i ) {
+        Write( "    " );
+    }
+}
+
+
+void XMLPrinter::PrintString( const char* p, bool restricted )
+{
+    // Look for runs of bytes between entities to print.
+    const char* q = p;
+
+    if ( _processEntities ) {
+        const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
+        while ( *q ) {
+            TIXMLASSERT( p <= q );
+            // Remember, char is sometimes signed. (How many times has that bitten me?)
+            if ( *q > 0 && *q < ENTITY_RANGE ) {
+                // Check for entities. If one is found, flush
+                // the stream up until the entity, write the
+                // entity, and keep looking.
+                if ( flag[(unsigned char)(*q)] ) {
+                    while ( p < q ) {
+                        const size_t delta = q - p;
+                        const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
+                        Write( p, toPrint );
+                        p += toPrint;
+                    }
+                    bool entityPatternPrinted = false;
+                    for( int i=0; i<NUM_ENTITIES; ++i ) {
+                        if ( entities[i].value == *q ) {
+                            Putc( '&' );
+                            Write( entities[i].pattern, entities[i].length );
+                            Putc( ';' );
+                            entityPatternPrinted = true;
+                            break;
+                        }
+                    }
+                    if ( !entityPatternPrinted ) {
+                        // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
+                        TIXMLASSERT( false );
+                    }
+                    ++p;
+                }
+            }
+            ++q;
+            TIXMLASSERT( p <= q );
+        }
+        // Flush the remaining string. This will be the entire
+        // string if an entity wasn't found.
+        if ( p < q ) {
+            const size_t delta = q - p;
+            const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
+            Write( p, toPrint );
+        }
+    }
+    else {
+        Write( p );
+    }
+}
+
+
+void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
+{
+    if ( writeBOM ) {
+        static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
+        Write( reinterpret_cast< const char* >( bom ) );
+    }
+    if ( writeDec ) {
+        PushDeclaration( "xml version=\"1.0\"" );
+    }
+}
+
+
+void XMLPrinter::OpenElement( const char* name, bool compactMode )
+{
+    SealElementIfJustOpened();
+    _stack.Push( name );
+
+    if ( _textDepth < 0 && !_firstElement && !compactMode ) {
+        Putc( '\n' );
+    }
+    if ( !compactMode ) {
+        PrintSpace( _depth );
+    }
+
+    Write ( "<" );
+    Write ( name );
+
+    _elementJustOpened = true;
+    _firstElement = false;
+    ++_depth;
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, const char* value )
+{
+    TIXMLASSERT( _elementJustOpened );
+    Putc ( ' ' );
+    Write( name );
+    Write( "=\"" );
+    PrintString( value, false );
+    Putc ( '\"' );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, int v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, unsigned v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute(const char* name, int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	PushAttribute(name, buf);
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, double v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::CloseElement( bool compactMode )
+{
+    --_depth;
+    const char* name = _stack.Pop();
+
+    if ( _elementJustOpened ) {
+        Write( "/>" );
+    }
+    else {
+        if ( _textDepth < 0 && !compactMode) {
+            Putc( '\n' );
+            PrintSpace( _depth );
+        }
+        Write ( "</" );
+        Write ( name );
+        Write ( ">" );
+    }
+
+    if ( _textDepth == _depth ) {
+        _textDepth = -1;
+    }
+    if ( _depth == 0 && !compactMode) {
+        Putc( '\n' );
+    }
+    _elementJustOpened = false;
+}
+
+
+void XMLPrinter::SealElementIfJustOpened()
+{
+    if ( !_elementJustOpened ) {
+        return;
+    }
+    _elementJustOpened = false;
+    Putc( '>' );
+}
+
+
+void XMLPrinter::PushText( const char* text, bool cdata )
+{
+    _textDepth = _depth-1;
+
+    SealElementIfJustOpened();
+    if ( cdata ) {
+        Write( "<![CDATA[" );
+        Write( text );
+        Write( "]]>" );
+    }
+    else {
+        PrintString( text, true );
+    }
+}
+
+void XMLPrinter::PushText( int64_t value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+void XMLPrinter::PushText( int value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( unsigned value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( bool value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( float value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( double value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushComment( const char* comment )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Putc( '\n' );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+
+    Write( "<!--" );
+    Write( comment );
+    Write( "-->" );
+}
+
+
+void XMLPrinter::PushDeclaration( const char* value )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Putc( '\n' );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+
+    Write( "<?" );
+    Write( value );
+    Write( "?>" );
+}
+
+
+void XMLPrinter::PushUnknown( const char* value )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Putc( '\n' );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+
+    Write( "<!" );
+    Write( value );
+    Putc( '>' );
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLDocument& doc )
+{
+    _processEntities = doc.ProcessEntities();
+    if ( doc.HasBOM() ) {
+        PushHeader( true, false );
+    }
+    return true;
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
+{
+    const XMLElement* parentElem = 0;
+    if ( element.Parent() ) {
+        parentElem = element.Parent()->ToElement();
+    }
+    const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
+    OpenElement( element.Name(), compactMode );
+    while ( attribute ) {
+        PushAttribute( attribute->Name(), attribute->Value() );
+        attribute = attribute->Next();
+    }
+    return true;
+}
+
+
+bool XMLPrinter::VisitExit( const XMLElement& element )
+{
+    CloseElement( CompactMode(element) );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLText& text )
+{
+    PushText( text.Value(), text.CData() );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLComment& comment )
+{
+    PushComment( comment.Value() );
+    return true;
+}
+
+bool XMLPrinter::Visit( const XMLDeclaration& declaration )
+{
+    PushDeclaration( declaration.Value() );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLUnknown& unknown )
+{
+    PushUnknown( unknown.Value() );
+    return true;
+}
+
+}   // namespace tinyxml2

+ 2310 - 0
SATService/SATService/tinyxml2.h

@@ -0,0 +1,2310 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#ifndef TINYXML2_INCLUDED
+#define TINYXML2_INCLUDED
+
+#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
+#   include <ctype.h>
+#   include <limits.h>
+#   include <stdio.h>
+#   include <stdlib.h>
+#   include <string.h>
+#	if defined(__PS3__)
+#		include <stddef.h>
+#	endif
+#else
+#   include <cctype>
+#   include <climits>
+#   include <cstdio>
+#   include <cstdlib>
+#   include <cstring>
+#endif
+
+#include "stdint.h"
+
+/*
+   TODO: intern strings instead of allocation.
+*/
+/*
+	gcc:
+        g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe
+
+    Formatting, Artistic Style:
+        AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h
+*/
+
+#if defined( _DEBUG ) || defined (__DEBUG__)
+#   ifndef TINYXML2_DEBUG
+#       define TINYXML2_DEBUG
+#   endif
+#endif
+
+#ifdef _MSC_VER
+#   pragma warning(push)
+#   pragma warning(disable: 4251)
+#endif
+
+#ifdef _WIN32
+#   ifdef TINYXML2_EXPORT
+#       define TINYXML2_LIB __declspec(dllexport)
+#   elif defined(TINYXML2_IMPORT)
+#       define TINYXML2_LIB __declspec(dllimport)
+#   else
+#       define TINYXML2_LIB
+#   endif
+#elif __GNUC__ >= 4
+#   define TINYXML2_LIB __attribute__((visibility("default")))
+#else
+#   define TINYXML2_LIB
+#endif
+
+
+#if defined(TINYXML2_DEBUG)
+#   if defined(_MSC_VER)
+#       // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like
+#       define TIXMLASSERT( x )           if ( !((void)0,(x))) { __debugbreak(); }
+#   elif defined (ANDROID_NDK)
+#       include <android/log.h>
+#       define TIXMLASSERT( x )           if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); }
+#   else
+#       include <assert.h>
+#       define TIXMLASSERT                assert
+#   endif
+#else
+#   define TIXMLASSERT( x )               {}
+#endif
+
+
+/* Versioning, past 1.0.14:
+	http://semver.org/
+*/
+static const int TIXML2_MAJOR_VERSION = 7;
+static const int TIXML2_MINOR_VERSION = 0;
+static const int TIXML2_PATCH_VERSION = 1;
+
+#define TINYXML2_MAJOR_VERSION 7
+#define TINYXML2_MINOR_VERSION 0
+#define TINYXML2_PATCH_VERSION 1
+
+// A fixed element depth limit is problematic. There needs to be a
+// limit to avoid a stack overflow. However, that limit varies per
+// system, and the capacity of the stack. On the other hand, it's a trivial
+// attack that can result from ill, malicious, or even correctly formed XML,
+// so there needs to be a limit in place.
+static const int TINYXML2_MAX_ELEMENT_DEPTH = 100;
+
+namespace tinyxml2
+{
+class XMLDocument;
+class XMLElement;
+class XMLAttribute;
+class XMLComment;
+class XMLText;
+class XMLDeclaration;
+class XMLUnknown;
+class XMLPrinter;
+
+/*
+	A class that wraps strings. Normally stores the start and end
+	pointers into the XML file itself, and will apply normalization
+	and entity translation if actually read. Can also store (and memory
+	manage) a traditional char[]
+
+    Isn't clear why TINYXML2_LIB is needed; but seems to fix #719
+*/
+class TINYXML2_LIB StrPair
+{
+public:
+    enum {
+        NEEDS_ENTITY_PROCESSING			= 0x01,
+        NEEDS_NEWLINE_NORMALIZATION		= 0x02,
+        NEEDS_WHITESPACE_COLLAPSING     = 0x04,
+
+        TEXT_ELEMENT		            = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+        TEXT_ELEMENT_LEAVE_ENTITIES		= NEEDS_NEWLINE_NORMALIZATION,
+        ATTRIBUTE_NAME		            = 0,
+        ATTRIBUTE_VALUE		            = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+        ATTRIBUTE_VALUE_LEAVE_ENTITIES  = NEEDS_NEWLINE_NORMALIZATION,
+        COMMENT							= NEEDS_NEWLINE_NORMALIZATION
+    };
+
+    StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
+    ~StrPair();
+
+    void Set( char* start, char* end, int flags ) {
+        TIXMLASSERT( start );
+        TIXMLASSERT( end );
+        Reset();
+        _start  = start;
+        _end    = end;
+        _flags  = flags | NEEDS_FLUSH;
+    }
+
+    const char* GetStr();
+
+    bool Empty() const {
+        return _start == _end;
+    }
+
+    void SetInternedStr( const char* str ) {
+        Reset();
+        _start = const_cast<char*>(str);
+    }
+
+    void SetStr( const char* str, int flags=0 );
+
+    char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr );
+    char* ParseName( char* in );
+
+    void TransferTo( StrPair* other );
+	void Reset();
+
+private:
+    void CollapseWhitespace();
+
+    enum {
+        NEEDS_FLUSH = 0x100,
+        NEEDS_DELETE = 0x200
+    };
+
+    int     _flags;
+    char*   _start;
+    char*   _end;
+
+    StrPair( const StrPair& other );	// not supported
+    void operator=( const StrPair& other );	// not supported, use TransferTo()
+};
+
+
+/*
+	A dynamic array of Plain Old Data. Doesn't support constructors, etc.
+	Has a small initial memory pool, so that low or no usage will not
+	cause a call to new/delete
+*/
+template <class T, int INITIAL_SIZE>
+class DynArray
+{
+public:
+    DynArray() :
+        _mem( _pool ),
+        _allocated( INITIAL_SIZE ),
+        _size( 0 )
+    {
+    }
+
+    ~DynArray() {
+        if ( _mem != _pool ) {
+            delete [] _mem;
+        }
+    }
+
+    void Clear() {
+        _size = 0;
+    }
+
+    void Push( T t ) {
+        TIXMLASSERT( _size < INT_MAX );
+        EnsureCapacity( _size+1 );
+        _mem[_size] = t;
+        ++_size;
+    }
+
+    T* PushArr( int count ) {
+        TIXMLASSERT( count >= 0 );
+        TIXMLASSERT( _size <= INT_MAX - count );
+        EnsureCapacity( _size+count );
+        T* ret = &_mem[_size];
+        _size += count;
+        return ret;
+    }
+
+    T Pop() {
+        TIXMLASSERT( _size > 0 );
+        --_size;
+        return _mem[_size];
+    }
+
+    void PopArr( int count ) {
+        TIXMLASSERT( _size >= count );
+        _size -= count;
+    }
+
+    bool Empty() const					{
+        return _size == 0;
+    }
+
+    T& operator[](int i)				{
+        TIXMLASSERT( i>= 0 && i < _size );
+        return _mem[i];
+    }
+
+    const T& operator[](int i) const	{
+        TIXMLASSERT( i>= 0 && i < _size );
+        return _mem[i];
+    }
+
+    const T& PeekTop() const            {
+        TIXMLASSERT( _size > 0 );
+        return _mem[ _size - 1];
+    }
+
+    int Size() const					{
+        TIXMLASSERT( _size >= 0 );
+        return _size;
+    }
+
+    int Capacity() const				{
+        TIXMLASSERT( _allocated >= INITIAL_SIZE );
+        return _allocated;
+    }
+
+	void SwapRemove(int i) {
+		TIXMLASSERT(i >= 0 && i < _size);
+		TIXMLASSERT(_size > 0);
+		_mem[i] = _mem[_size - 1];
+		--_size;
+	}
+
+    const T* Mem() const				{
+        TIXMLASSERT( _mem );
+        return _mem;
+    }
+
+    T* Mem() {
+        TIXMLASSERT( _mem );
+        return _mem;
+    }
+
+private:
+    DynArray( const DynArray& ); // not supported
+    void operator=( const DynArray& ); // not supported
+
+    void EnsureCapacity( int cap ) {
+        TIXMLASSERT( cap > 0 );
+        if ( cap > _allocated ) {
+            TIXMLASSERT( cap <= INT_MAX / 2 );
+            int newAllocated = cap * 2;
+            T* newMem = new T[newAllocated];
+            TIXMLASSERT( newAllocated >= _size );
+            memcpy( newMem, _mem, sizeof(T)*_size );	// warning: not using constructors, only works for PODs
+            if ( _mem != _pool ) {
+                delete [] _mem;
+            }
+            _mem = newMem;
+            _allocated = newAllocated;
+        }
+    }
+
+    T*  _mem;
+    T   _pool[INITIAL_SIZE];
+    int _allocated;		// objects allocated
+    int _size;			// number objects in use
+};
+
+
+/*
+	Parent virtual class of a pool for fast allocation
+	and deallocation of objects.
+*/
+class MemPool
+{
+public:
+    MemPool() {}
+    virtual ~MemPool() {}
+
+    virtual int ItemSize() const = 0;
+    virtual void* Alloc() = 0;
+    virtual void Free( void* ) = 0;
+    virtual void SetTracked() = 0;
+};
+
+
+/*
+	Template child class to create pools of the correct type.
+*/
+template< int ITEM_SIZE >
+class MemPoolT : public MemPool
+{
+public:
+    MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0)	{}
+    ~MemPoolT() {
+        MemPoolT< ITEM_SIZE >::Clear();
+    }
+
+    void Clear() {
+        // Delete the blocks.
+        while( !_blockPtrs.Empty()) {
+            Block* lastBlock = _blockPtrs.Pop();
+            delete lastBlock;
+        }
+        _root = 0;
+        _currentAllocs = 0;
+        _nAllocs = 0;
+        _maxAllocs = 0;
+        _nUntracked = 0;
+    }
+
+    virtual int ItemSize() const	{
+        return ITEM_SIZE;
+    }
+    int CurrentAllocs() const		{
+        return _currentAllocs;
+    }
+
+    virtual void* Alloc() {
+        if ( !_root ) {
+            // Need a new block.
+            Block* block = new Block();
+            _blockPtrs.Push( block );
+
+            Item* blockItems = block->items;
+            for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {
+                blockItems[i].next = &(blockItems[i + 1]);
+            }
+            blockItems[ITEMS_PER_BLOCK - 1].next = 0;
+            _root = blockItems;
+        }
+        Item* const result = _root;
+        TIXMLASSERT( result != 0 );
+        _root = _root->next;
+
+        ++_currentAllocs;
+        if ( _currentAllocs > _maxAllocs ) {
+            _maxAllocs = _currentAllocs;
+        }
+        ++_nAllocs;
+        ++_nUntracked;
+        return result;
+    }
+
+    virtual void Free( void* mem ) {
+        if ( !mem ) {
+            return;
+        }
+        --_currentAllocs;
+        Item* item = static_cast<Item*>( mem );
+#ifdef TINYXML2_DEBUG
+        memset( item, 0xfe, sizeof( *item ) );
+#endif
+        item->next = _root;
+        _root = item;
+    }
+    void Trace( const char* name ) {
+        printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
+                name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs,
+                ITEM_SIZE, _nAllocs, _blockPtrs.Size() );
+    }
+
+    void SetTracked() {
+        --_nUntracked;
+    }
+
+    int Untracked() const {
+        return _nUntracked;
+    }
+
+	// This number is perf sensitive. 4k seems like a good tradeoff on my machine.
+	// The test file is large, 170k.
+	// Release:		VS2010 gcc(no opt)
+	//		1k:		4000
+	//		2k:		4000
+	//		4k:		3900	21000
+	//		16k:	5200
+	//		32k:	4300
+	//		64k:	4000	21000
+    // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK
+    // in private part if ITEMS_PER_BLOCK is private
+    enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE };
+
+private:
+    MemPoolT( const MemPoolT& ); // not supported
+    void operator=( const MemPoolT& ); // not supported
+
+    union Item {
+        Item*   next;
+        char    itemData[ITEM_SIZE];
+    };
+    struct Block {
+        Item items[ITEMS_PER_BLOCK];
+    };
+    DynArray< Block*, 10 > _blockPtrs;
+    Item* _root;
+
+    int _currentAllocs;
+    int _nAllocs;
+    int _maxAllocs;
+    int _nUntracked;
+};
+
+
+
+/**
+	Implements the interface to the "Visitor pattern" (see the Accept() method.)
+	If you call the Accept() method, it requires being passed a XMLVisitor
+	class to handle callbacks. For nodes that contain other nodes (Document, Element)
+	you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs
+	are simply called with Visit().
+
+	If you return 'true' from a Visit method, recursive parsing will continue. If you return
+	false, <b>no children of this node or its siblings</b> will be visited.
+
+	All flavors of Visit methods have a default implementation that returns 'true' (continue
+	visiting). You need to only override methods that are interesting to you.
+
+	Generally Accept() is called on the XMLDocument, although all nodes support visiting.
+
+	You should never change the document from a callback.
+
+	@sa XMLNode::Accept()
+*/
+class TINYXML2_LIB XMLVisitor
+{
+public:
+    virtual ~XMLVisitor() {}
+
+    /// Visit a document.
+    virtual bool VisitEnter( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+    /// Visit a document.
+    virtual bool VisitExit( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+
+    /// Visit an element.
+    virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ )	{
+        return true;
+    }
+    /// Visit an element.
+    virtual bool VisitExit( const XMLElement& /*element*/ )			{
+        return true;
+    }
+
+    /// Visit a declaration.
+    virtual bool Visit( const XMLDeclaration& /*declaration*/ )		{
+        return true;
+    }
+    /// Visit a text node.
+    virtual bool Visit( const XMLText& /*text*/ )					{
+        return true;
+    }
+    /// Visit a comment node.
+    virtual bool Visit( const XMLComment& /*comment*/ )				{
+        return true;
+    }
+    /// Visit an unknown node.
+    virtual bool Visit( const XMLUnknown& /*unknown*/ )				{
+        return true;
+    }
+};
+
+// WARNING: must match XMLDocument::_errorNames[]
+enum XMLError {
+    XML_SUCCESS = 0,
+    XML_NO_ATTRIBUTE,
+    XML_WRONG_ATTRIBUTE_TYPE,
+    XML_ERROR_FILE_NOT_FOUND,
+    XML_ERROR_FILE_COULD_NOT_BE_OPENED,
+    XML_ERROR_FILE_READ_ERROR,
+    XML_ERROR_PARSING_ELEMENT,
+    XML_ERROR_PARSING_ATTRIBUTE,
+    XML_ERROR_PARSING_TEXT,
+    XML_ERROR_PARSING_CDATA,
+    XML_ERROR_PARSING_COMMENT,
+    XML_ERROR_PARSING_DECLARATION,
+    XML_ERROR_PARSING_UNKNOWN,
+    XML_ERROR_EMPTY_DOCUMENT,
+    XML_ERROR_MISMATCHED_ELEMENT,
+    XML_ERROR_PARSING,
+    XML_CAN_NOT_CONVERT_TEXT,
+    XML_NO_TEXT_NODE,
+	XML_ELEMENT_DEPTH_EXCEEDED,
+
+	XML_ERROR_COUNT
+};
+
+
+/*
+	Utility functionality.
+*/
+class TINYXML2_LIB XMLUtil
+{
+public:
+    static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr )	{
+        TIXMLASSERT( p );
+
+        while( IsWhiteSpace(*p) ) {
+            if (curLineNumPtr && *p == '\n') {
+                ++(*curLineNumPtr);
+            }
+            ++p;
+        }
+        TIXMLASSERT( p );
+        return p;
+    }
+    static char* SkipWhiteSpace( char* p, int* curLineNumPtr )				{
+        return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p), curLineNumPtr ) );
+    }
+
+    // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't
+    // correct, but simple, and usually works.
+    static bool IsWhiteSpace( char p )					{
+        return !IsUTF8Continuation(p) && isspace( static_cast<unsigned char>(p) );
+    }
+
+    inline static bool IsNameStartChar( unsigned char ch ) {
+        if ( ch >= 128 ) {
+            // This is a heuristic guess in attempt to not implement Unicode-aware isalpha()
+            return true;
+        }
+        if ( isalpha( ch ) ) {
+            return true;
+        }
+        return ch == ':' || ch == '_';
+    }
+
+    inline static bool IsNameChar( unsigned char ch ) {
+        return IsNameStartChar( ch )
+               || isdigit( ch )
+               || ch == '.'
+               || ch == '-';
+    }
+
+    inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX )  {
+        if ( p == q ) {
+            return true;
+        }
+        TIXMLASSERT( p );
+        TIXMLASSERT( q );
+        TIXMLASSERT( nChar >= 0 );
+        return strncmp( p, q, nChar ) == 0;
+    }
+
+    inline static bool IsUTF8Continuation( char p ) {
+        return ( p & 0x80 ) != 0;
+    }
+
+    static const char* ReadBOM( const char* p, bool* hasBOM );
+    // p is the starting location,
+    // the UTF-8 value of the entity will be placed in value, and length filled in.
+    static const char* GetCharacterRef( const char* p, char* value, int* length );
+    static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+    // converts primitive types to strings
+    static void ToStr( int v, char* buffer, int bufferSize );
+    static void ToStr( unsigned v, char* buffer, int bufferSize );
+    static void ToStr( bool v, char* buffer, int bufferSize );
+    static void ToStr( float v, char* buffer, int bufferSize );
+    static void ToStr( double v, char* buffer, int bufferSize );
+	static void ToStr(int64_t v, char* buffer, int bufferSize);
+
+    // converts strings to primitive types
+    static bool	ToInt( const char* str, int* value );
+    static bool ToUnsigned( const char* str, unsigned* value );
+    static bool	ToBool( const char* str, bool* value );
+    static bool	ToFloat( const char* str, float* value );
+    static bool ToDouble( const char* str, double* value );
+	static bool ToInt64(const char* str, int64_t* value);
+
+	// Changes what is serialized for a boolean value.
+	// Default to "true" and "false". Shouldn't be changed
+	// unless you have a special testing or compatibility need.
+	// Be careful: static, global, & not thread safe.
+	// Be sure to set static const memory as parameters.
+	static void SetBoolSerialization(const char* writeTrue, const char* writeFalse);
+
+private:
+	static const char* writeBoolTrue;
+	static const char* writeBoolFalse;
+};
+
+
+/** XMLNode is a base class for every object that is in the
+	XML Document Object Model (DOM), except XMLAttributes.
+	Nodes have siblings, a parent, and children which can
+	be navigated. A node is always in a XMLDocument.
+	The type of a XMLNode can be queried, and it can
+	be cast to its more defined type.
+
+	A XMLDocument allocates memory for all its Nodes.
+	When the XMLDocument gets deleted, all its Nodes
+	will also be deleted.
+
+	@verbatim
+	A Document can contain:	Element	(container or leaf)
+							Comment (leaf)
+							Unknown (leaf)
+							Declaration( leaf )
+
+	An Element can contain:	Element (container or leaf)
+							Text	(leaf)
+							Attributes (not on tree)
+							Comment (leaf)
+							Unknown (leaf)
+
+	@endverbatim
+*/
+class TINYXML2_LIB XMLNode
+{
+    friend class XMLDocument;
+    friend class XMLElement;
+public:
+
+    /// Get the XMLDocument that owns this XMLNode.
+    const XMLDocument* GetDocument() const	{
+        TIXMLASSERT( _document );
+        return _document;
+    }
+    /// Get the XMLDocument that owns this XMLNode.
+    XMLDocument* GetDocument()				{
+        TIXMLASSERT( _document );
+        return _document;
+    }
+
+    /// Safely cast to an Element, or null.
+    virtual XMLElement*		ToElement()		{
+        return 0;
+    }
+    /// Safely cast to Text, or null.
+    virtual XMLText*		ToText()		{
+        return 0;
+    }
+    /// Safely cast to a Comment, or null.
+    virtual XMLComment*		ToComment()		{
+        return 0;
+    }
+    /// Safely cast to a Document, or null.
+    virtual XMLDocument*	ToDocument()	{
+        return 0;
+    }
+    /// Safely cast to a Declaration, or null.
+    virtual XMLDeclaration*	ToDeclaration()	{
+        return 0;
+    }
+    /// Safely cast to an Unknown, or null.
+    virtual XMLUnknown*		ToUnknown()		{
+        return 0;
+    }
+
+    virtual const XMLElement*		ToElement() const		{
+        return 0;
+    }
+    virtual const XMLText*			ToText() const			{
+        return 0;
+    }
+    virtual const XMLComment*		ToComment() const		{
+        return 0;
+    }
+    virtual const XMLDocument*		ToDocument() const		{
+        return 0;
+    }
+    virtual const XMLDeclaration*	ToDeclaration() const	{
+        return 0;
+    }
+    virtual const XMLUnknown*		ToUnknown() const		{
+        return 0;
+    }
+
+    /** The meaning of 'value' changes for the specific type.
+    	@verbatim
+    	Document:	empty (NULL is returned, not an empty string)
+    	Element:	name of the element
+    	Comment:	the comment text
+    	Unknown:	the tag contents
+    	Text:		the text string
+    	@endverbatim
+    */
+    const char* Value() const;
+
+    /** Set the Value of an XML node.
+    	@sa Value()
+    */
+    void SetValue( const char* val, bool staticMem=false );
+
+    /// Gets the line number the node is in, if the document was parsed from a file.
+    int GetLineNum() const { return _parseLineNum; }
+
+    /// Get the parent of this node on the DOM.
+    const XMLNode*	Parent() const			{
+        return _parent;
+    }
+
+    XMLNode* Parent()						{
+        return _parent;
+    }
+
+    /// Returns true if this node has no children.
+    bool NoChildren() const					{
+        return !_firstChild;
+    }
+
+    /// Get the first child node, or null if none exists.
+    const XMLNode*  FirstChild() const		{
+        return _firstChild;
+    }
+
+    XMLNode*		FirstChild()			{
+        return _firstChild;
+    }
+
+    /** Get the first child element, or optionally the first child
+        element with the specified name.
+    */
+    const XMLElement* FirstChildElement( const char* name = 0 ) const;
+
+    XMLElement* FirstChildElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( name ));
+    }
+
+    /// Get the last child node, or null if none exists.
+    const XMLNode*	LastChild() const						{
+        return _lastChild;
+    }
+
+    XMLNode*		LastChild()								{
+        return _lastChild;
+    }
+
+    /** Get the last child element or optionally the last child
+        element with the specified name.
+    */
+    const XMLElement* LastChildElement( const char* name = 0 ) const;
+
+    XMLElement* LastChildElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(name) );
+    }
+
+    /// Get the previous (left) sibling node of this node.
+    const XMLNode*	PreviousSibling() const					{
+        return _prev;
+    }
+
+    XMLNode*	PreviousSibling()							{
+        return _prev;
+    }
+
+    /// Get the previous (left) sibling element of this node, with an optionally supplied name.
+    const XMLElement*	PreviousSiblingElement( const char* name = 0 ) const ;
+
+    XMLElement*	PreviousSiblingElement( const char* name = 0 ) {
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( name ) );
+    }
+
+    /// Get the next (right) sibling node of this node.
+    const XMLNode*	NextSibling() const						{
+        return _next;
+    }
+
+    XMLNode*	NextSibling()								{
+        return _next;
+    }
+
+    /// Get the next (right) sibling element of this node, with an optionally supplied name.
+    const XMLElement*	NextSiblingElement( const char* name = 0 ) const;
+
+    XMLElement*	NextSiblingElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement( name ) );
+    }
+
+    /**
+    	Add a child node as the last (right) child.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertEndChild( XMLNode* addThis );
+
+    XMLNode* LinkEndChild( XMLNode* addThis )	{
+        return InsertEndChild( addThis );
+    }
+    /**
+    	Add a child node as the first (left) child.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertFirstChild( XMLNode* addThis );
+    /**
+    	Add a node after the specified child node.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the afterThis node
+		is not a child of this node, or if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
+
+    /**
+    	Delete all the children of this node.
+    */
+    void DeleteChildren();
+
+    /**
+    	Delete a child of this node.
+    */
+    void DeleteChild( XMLNode* node );
+
+    /**
+    	Make a copy of this node, but not its children.
+    	You may pass in a Document pointer that will be
+    	the owner of the new Node. If the 'document' is
+    	null, then the node returned will be allocated
+    	from the current Document. (this->GetDocument())
+
+    	Note: if called on a XMLDocument, this will return null.
+    */
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;
+
+	/**
+		Make a copy of this node and all its children.
+
+		If the 'target' is null, then the nodes will
+		be allocated in the current document. If 'target'
+        is specified, the memory will be allocated is the
+        specified XMLDocument.
+
+		NOTE: This is probably not the correct tool to
+		copy a document, since XMLDocuments can have multiple
+		top level XMLNodes. You probably want to use
+        XMLDocument::DeepCopy()
+	*/
+	XMLNode* DeepClone( XMLDocument* target ) const;
+
+    /**
+    	Test if 2 nodes are the same, but don't test children.
+    	The 2 nodes do not need to be in the same Document.
+
+    	Note: if called on a XMLDocument, this will return false.
+    */
+    virtual bool ShallowEqual( const XMLNode* compare ) const = 0;
+
+    /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the
+    	XML tree will be conditionally visited and the host will be called back
+    	via the XMLVisitor interface.
+
+    	This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse
+    	the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this
+    	interface versus any other.)
+
+    	The interface has been based on ideas from:
+
+    	- http://www.saxproject.org/
+    	- http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+    	Which are both good references for "visiting".
+
+    	An example of using Accept():
+    	@verbatim
+    	XMLPrinter printer;
+    	tinyxmlDoc.Accept( &printer );
+    	const char* xmlcstr = printer.CStr();
+    	@endverbatim
+    */
+    virtual bool Accept( XMLVisitor* visitor ) const = 0;
+
+	/**
+		Set user data into the XMLNode. TinyXML-2 in
+		no way processes or interprets user data.
+		It is initially 0.
+	*/
+	void SetUserData(void* userData)	{ _userData = userData; }
+
+	/**
+		Get user data set into the XMLNode. TinyXML-2 in
+		no way processes or interprets user data.
+		It is initially 0.
+	*/
+	void* GetUserData() const			{ return _userData; }
+
+protected:
+    explicit XMLNode( XMLDocument* );
+    virtual ~XMLNode();
+
+    virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);
+
+    XMLDocument*	_document;
+    XMLNode*		_parent;
+    mutable StrPair	_value;
+    int             _parseLineNum;
+
+    XMLNode*		_firstChild;
+    XMLNode*		_lastChild;
+
+    XMLNode*		_prev;
+    XMLNode*		_next;
+
+	void*			_userData;
+
+private:
+    MemPool*		_memPool;
+    void Unlink( XMLNode* child );
+    static void DeleteNode( XMLNode* node );
+    void InsertChildPreamble( XMLNode* insertThis ) const;
+    const XMLElement* ToElementWithName( const char* name ) const;
+
+    XMLNode( const XMLNode& );	// not supported
+    XMLNode& operator=( const XMLNode& );	// not supported
+};
+
+
+/** XML text.
+
+	Note that a text node can have child element nodes, for example:
+	@verbatim
+	<root>This is <b>bold</b></root>
+	@endverbatim
+
+	A text node can have 2 ways to output the next. "normal" output
+	and CDATA. It will default to the mode it was parsed from the XML file and
+	you generally want to leave it alone, but you can change the output mode with
+	SetCData() and query it with CData().
+*/
+class TINYXML2_LIB XMLText : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLText* ToText()			{
+        return this;
+    }
+    virtual const XMLText* ToText() const	{
+        return this;
+    }
+
+    /// Declare whether this should be CDATA or standard text.
+    void SetCData( bool isCData )			{
+        _isCData = isCData;
+    }
+    /// Returns true if this is a CDATA text element.
+    bool CData() const						{
+        return _isCData;
+    }
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    explicit XMLText( XMLDocument* doc )	: XMLNode( doc ), _isCData( false )	{}
+    virtual ~XMLText()												{}
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    bool _isCData;
+
+    XMLText( const XMLText& );	// not supported
+    XMLText& operator=( const XMLText& );	// not supported
+};
+
+
+/** An XML Comment. */
+class TINYXML2_LIB XMLComment : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLComment*	ToComment()					{
+        return this;
+    }
+    virtual const XMLComment* ToComment() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    explicit XMLComment( XMLDocument* doc );
+    virtual ~XMLComment();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);
+
+private:
+    XMLComment( const XMLComment& );	// not supported
+    XMLComment& operator=( const XMLComment& );	// not supported
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+	@verbatim
+		<?xml version="1.0" standalone="yes"?>
+	@endverbatim
+
+	TinyXML-2 will happily read or write files without a declaration,
+	however.
+
+	The text of the declaration isn't interpreted. It is parsed
+	and written as a string.
+*/
+class TINYXML2_LIB XMLDeclaration : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLDeclaration*	ToDeclaration()					{
+        return this;
+    }
+    virtual const XMLDeclaration* ToDeclaration() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    explicit XMLDeclaration( XMLDocument* doc );
+    virtual ~XMLDeclaration();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLDeclaration( const XMLDeclaration& );	// not supported
+    XMLDeclaration& operator=( const XMLDeclaration& );	// not supported
+};
+
+
+/** Any tag that TinyXML-2 doesn't recognize is saved as an
+	unknown. It is a tag of text, but should not be modified.
+	It will be written back to the XML, unchanged, when the file
+	is saved.
+
+	DTD tags get thrown into XMLUnknowns.
+*/
+class TINYXML2_LIB XMLUnknown : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLUnknown*	ToUnknown()					{
+        return this;
+    }
+    virtual const XMLUnknown* ToUnknown() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    explicit XMLUnknown( XMLDocument* doc );
+    virtual ~XMLUnknown();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLUnknown( const XMLUnknown& );	// not supported
+    XMLUnknown& operator=( const XMLUnknown& );	// not supported
+};
+
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+	number of attributes, each with a unique name.
+
+	@note The attributes are not XMLNodes. You may only query the
+	Next() attribute in a list.
+*/
+class TINYXML2_LIB XMLAttribute
+{
+    friend class XMLElement;
+public:
+    /// The name of the attribute.
+    const char* Name() const;
+
+    /// The value of the attribute.
+    const char* Value() const;
+
+    /// Gets the line number the attribute is in, if the document was parsed from a file.
+    int GetLineNum() const { return _parseLineNum; }
+
+    /// The next attribute in the list.
+    const XMLAttribute* Next() const {
+        return _next;
+    }
+
+    /** IntValue interprets the attribute as an integer, and returns the value.
+        If the value isn't an integer, 0 will be returned. There is no error checking;
+    	use QueryIntValue() if you need error checking.
+    */
+	int	IntValue() const {
+		int i = 0;
+		QueryIntValue(&i);
+		return i;
+	}
+
+	int64_t Int64Value() const {
+		int64_t i = 0;
+		QueryInt64Value(&i);
+		return i;
+	}
+
+    /// Query as an unsigned integer. See IntValue()
+    unsigned UnsignedValue() const			{
+        unsigned i=0;
+        QueryUnsignedValue( &i );
+        return i;
+    }
+    /// Query as a boolean. See IntValue()
+    bool	 BoolValue() const				{
+        bool b=false;
+        QueryBoolValue( &b );
+        return b;
+    }
+    /// Query as a double. See IntValue()
+    double 	 DoubleValue() const			{
+        double d=0;
+        QueryDoubleValue( &d );
+        return d;
+    }
+    /// Query as a float. See IntValue()
+    float	 FloatValue() const				{
+        float f=0;
+        QueryFloatValue( &f );
+        return f;
+    }
+
+    /** QueryIntValue interprets the attribute as an integer, and returns the value
+    	in the provided parameter. The function will return XML_SUCCESS on success,
+    	and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
+    */
+    XMLError QueryIntValue( int* value ) const;
+    /// See QueryIntValue
+    XMLError QueryUnsignedValue( unsigned int* value ) const;
+	/// See QueryIntValue
+	XMLError QueryInt64Value(int64_t* value) const;
+	/// See QueryIntValue
+    XMLError QueryBoolValue( bool* value ) const;
+    /// See QueryIntValue
+    XMLError QueryDoubleValue( double* value ) const;
+    /// See QueryIntValue
+    XMLError QueryFloatValue( float* value ) const;
+
+    /// Set the attribute to a string value.
+    void SetAttribute( const char* value );
+    /// Set the attribute to value.
+    void SetAttribute( int value );
+    /// Set the attribute to value.
+    void SetAttribute( unsigned value );
+	/// Set the attribute to value.
+	void SetAttribute(int64_t value);
+	/// Set the attribute to value.
+    void SetAttribute( bool value );
+    /// Set the attribute to value.
+    void SetAttribute( double value );
+    /// Set the attribute to value.
+    void SetAttribute( float value );
+
+private:
+    enum { BUF_SIZE = 200 };
+
+    XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {}
+    virtual ~XMLAttribute()	{}
+
+    XMLAttribute( const XMLAttribute& );	// not supported
+    void operator=( const XMLAttribute& );	// not supported
+    void SetName( const char* name );
+
+    char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr );
+
+    mutable StrPair _name;
+    mutable StrPair _value;
+    int             _parseLineNum;
+    XMLAttribute*   _next;
+    MemPool*        _memPool;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+	and can contain other elements, text, comments, and unknowns.
+	Elements also contain an arbitrary number of attributes.
+*/
+class TINYXML2_LIB XMLElement : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    /// Get the name of an element (which is the Value() of the node.)
+    const char* Name() const		{
+        return Value();
+    }
+    /// Set the name of the element.
+    void SetName( const char* str, bool staticMem=false )	{
+        SetValue( str, staticMem );
+    }
+
+    virtual XMLElement* ToElement()				{
+        return this;
+    }
+    virtual const XMLElement* ToElement() const {
+        return this;
+    }
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    /** Given an attribute name, Attribute() returns the value
+    	for the attribute of that name, or null if none
+    	exists. For example:
+
+    	@verbatim
+    	const char* value = ele->Attribute( "foo" );
+    	@endverbatim
+
+    	The 'value' parameter is normally null. However, if specified,
+    	the attribute will only be returned if the 'name' and 'value'
+    	match. This allow you to write code:
+
+    	@verbatim
+    	if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
+    	@endverbatim
+
+    	rather than:
+    	@verbatim
+    	if ( ele->Attribute( "foo" ) ) {
+    		if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
+    	}
+    	@endverbatim
+    */
+    const char* Attribute( const char* name, const char* value=0 ) const;
+
+    /** Given an attribute name, IntAttribute() returns the value
+    	of the attribute interpreted as an integer. The default
+        value will be returned if the attribute isn't present,
+        or if there is an error. (For a method with error
+    	checking, see QueryIntAttribute()).
+    */
+	int IntAttribute(const char* name, int defaultValue = 0) const;
+    /// See IntAttribute()
+	unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const;
+	/// See IntAttribute()
+	int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const;
+	/// See IntAttribute()
+	bool BoolAttribute(const char* name, bool defaultValue = false) const;
+    /// See IntAttribute()
+	double DoubleAttribute(const char* name, double defaultValue = 0) const;
+    /// See IntAttribute()
+	float FloatAttribute(const char* name, float defaultValue = 0) const;
+
+    /** Given an attribute name, QueryIntAttribute() returns
+    	XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+    	can't be performed, or XML_NO_ATTRIBUTE if the attribute
+    	doesn't exist. If successful, the result of the conversion
+    	will be written to 'value'. If not successful, nothing will
+    	be written to 'value'. This allows you to provide default
+    	value:
+
+    	@verbatim
+    	int value = 10;
+    	QueryIntAttribute( "foo", &value );		// if "foo" isn't found, value will still be 10
+    	@endverbatim
+    */
+    XMLError QueryIntAttribute( const char* name, int* value ) const				{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryIntValue( value );
+    }
+
+	/// See QueryIntAttribute()
+    XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const	{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryUnsignedValue( value );
+    }
+
+	/// See QueryIntAttribute()
+	XMLError QueryInt64Attribute(const char* name, int64_t* value) const {
+		const XMLAttribute* a = FindAttribute(name);
+		if (!a) {
+			return XML_NO_ATTRIBUTE;
+		}
+		return a->QueryInt64Value(value);
+	}
+
+	/// See QueryIntAttribute()
+    XMLError QueryBoolAttribute( const char* name, bool* value ) const				{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryBoolValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryDoubleAttribute( const char* name, double* value ) const			{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryDoubleValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryFloatAttribute( const char* name, float* value ) const			{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryFloatValue( value );
+    }
+
+	/// See QueryIntAttribute()
+	XMLError QueryStringAttribute(const char* name, const char** value) const {
+		const XMLAttribute* a = FindAttribute(name);
+		if (!a) {
+			return XML_NO_ATTRIBUTE;
+		}
+		*value = a->Value();
+		return XML_SUCCESS;
+	}
+
+
+
+    /** Given an attribute name, QueryAttribute() returns
+    	XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+    	can't be performed, or XML_NO_ATTRIBUTE if the attribute
+    	doesn't exist. It is overloaded for the primitive types,
+		and is a generally more convenient replacement of
+		QueryIntAttribute() and related functions.
+
+		If successful, the result of the conversion
+    	will be written to 'value'. If not successful, nothing will
+    	be written to 'value'. This allows you to provide default
+    	value:
+
+    	@verbatim
+    	int value = 10;
+    	QueryAttribute( "foo", &value );		// if "foo" isn't found, value will still be 10
+    	@endverbatim
+    */
+	XMLError QueryAttribute( const char* name, int* value ) const {
+		return QueryIntAttribute( name, value );
+	}
+
+	XMLError QueryAttribute( const char* name, unsigned int* value ) const {
+		return QueryUnsignedAttribute( name, value );
+	}
+
+	XMLError QueryAttribute(const char* name, int64_t* value) const {
+		return QueryInt64Attribute(name, value);
+	}
+
+	XMLError QueryAttribute( const char* name, bool* value ) const {
+		return QueryBoolAttribute( name, value );
+	}
+
+	XMLError QueryAttribute( const char* name, double* value ) const {
+		return QueryDoubleAttribute( name, value );
+	}
+
+	XMLError QueryAttribute( const char* name, float* value ) const {
+		return QueryFloatAttribute( name, value );
+	}
+
+	/// Sets the named attribute to value.
+    void SetAttribute( const char* name, const char* value )	{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, int value )			{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, unsigned value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+
+	/// Sets the named attribute to value.
+	void SetAttribute(const char* name, int64_t value) {
+		XMLAttribute* a = FindOrCreateAttribute(name);
+		a->SetAttribute(value);
+	}
+
+	/// Sets the named attribute to value.
+    void SetAttribute( const char* name, bool value )			{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, double value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, float value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+
+    /**
+    	Delete an attribute.
+    */
+    void DeleteAttribute( const char* name );
+
+    /// Return the first attribute in the list.
+    const XMLAttribute* FirstAttribute() const {
+        return _rootAttribute;
+    }
+    /// Query a specific attribute in the list.
+    const XMLAttribute* FindAttribute( const char* name ) const;
+
+    /** Convenience function for easy access to the text inside an element. Although easy
+    	and concise, GetText() is limited compared to getting the XMLText child
+    	and accessing it directly.
+
+    	If the first child of 'this' is a XMLText, the GetText()
+    	returns the character string of the Text node, else null is returned.
+
+    	This is a convenient method for getting the text of simple contained text:
+    	@verbatim
+    	<foo>This is text</foo>
+    		const char* str = fooElement->GetText();
+    	@endverbatim
+
+    	'str' will be a pointer to "This is text".
+
+    	Note that this function can be misleading. If the element foo was created from
+    	this XML:
+    	@verbatim
+    		<foo><b>This is text</b></foo>
+    	@endverbatim
+
+    	then the value of str would be null. The first child node isn't a text node, it is
+    	another element. From this XML:
+    	@verbatim
+    		<foo>This is <b>text</b></foo>
+    	@endverbatim
+    	GetText() will return "This is ".
+    */
+    const char* GetText() const;
+
+    /** Convenience function for easy access to the text inside an element. Although easy
+    	and concise, SetText() is limited compared to creating an XMLText child
+    	and mutating it directly.
+
+    	If the first child of 'this' is a XMLText, SetText() sets its value to
+		the given string, otherwise it will create a first child that is an XMLText.
+
+    	This is a convenient method for setting the text of simple contained text:
+    	@verbatim
+    	<foo>This is text</foo>
+    		fooElement->SetText( "Hullaballoo!" );
+     	<foo>Hullaballoo!</foo>
+		@endverbatim
+
+    	Note that this function can be misleading. If the element foo was created from
+    	this XML:
+    	@verbatim
+    		<foo><b>This is text</b></foo>
+    	@endverbatim
+
+    	then it will not change "This is text", but rather prefix it with a text element:
+    	@verbatim
+    		<foo>Hullaballoo!<b>This is text</b></foo>
+    	@endverbatim
+
+		For this XML:
+    	@verbatim
+    		<foo />
+    	@endverbatim
+    	SetText() will generate
+    	@verbatim
+    		<foo>Hullaballoo!</foo>
+    	@endverbatim
+    */
+	void SetText( const char* inText );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( int value );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( unsigned value );
+	/// Convenience method for setting text inside an element. See SetText() for important limitations.
+	void SetText(int64_t value);
+	/// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( bool value );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( double value );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( float value );
+
+    /**
+    	Convenience method to query the value of a child text node. This is probably best
+    	shown by example. Given you have a document is this form:
+    	@verbatim
+    		<point>
+    			<x>1</x>
+    			<y>1.4</y>
+    		</point>
+    	@endverbatim
+
+    	The QueryIntText() and similar functions provide a safe and easier way to get to the
+    	"value" of x and y.
+
+    	@verbatim
+    		int x = 0;
+    		float y = 0;	// types of x and y are contrived for example
+    		const XMLElement* xElement = pointElement->FirstChildElement( "x" );
+    		const XMLElement* yElement = pointElement->FirstChildElement( "y" );
+    		xElement->QueryIntText( &x );
+    		yElement->QueryFloatText( &y );
+    	@endverbatim
+
+    	@returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted
+    			 to the requested type, and XML_NO_TEXT_NODE if there is no child text to query.
+
+    */
+    XMLError QueryIntText( int* ival ) const;
+    /// See QueryIntText()
+    XMLError QueryUnsignedText( unsigned* uval ) const;
+	/// See QueryIntText()
+	XMLError QueryInt64Text(int64_t* uval) const;
+	/// See QueryIntText()
+    XMLError QueryBoolText( bool* bval ) const;
+    /// See QueryIntText()
+    XMLError QueryDoubleText( double* dval ) const;
+    /// See QueryIntText()
+    XMLError QueryFloatText( float* fval ) const;
+
+	int IntText(int defaultValue = 0) const;
+
+	/// See QueryIntText()
+	unsigned UnsignedText(unsigned defaultValue = 0) const;
+	/// See QueryIntText()
+	int64_t Int64Text(int64_t defaultValue = 0) const;
+	/// See QueryIntText()
+	bool BoolText(bool defaultValue = false) const;
+	/// See QueryIntText()
+	double DoubleText(double defaultValue = 0) const;
+	/// See QueryIntText()
+	float FloatText(float defaultValue = 0) const;
+
+    // internal:
+    enum ElementClosingType {
+        OPEN,		// <foo>
+        CLOSED,		// <foo/>
+        CLOSING		// </foo>
+    };
+    ElementClosingType ClosingType() const {
+        return _closingType;
+    }
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLElement( XMLDocument* doc );
+    virtual ~XMLElement();
+    XMLElement( const XMLElement& );	// not supported
+    void operator=( const XMLElement& );	// not supported
+
+    XMLAttribute* FindOrCreateAttribute( const char* name );
+    char* ParseAttributes( char* p, int* curLineNumPtr );
+    static void DeleteAttribute( XMLAttribute* attribute );
+    XMLAttribute* CreateAttribute();
+
+    enum { BUF_SIZE = 200 };
+    ElementClosingType _closingType;
+    // The attribute list is ordered; there is no 'lastAttribute'
+    // because the list needs to be scanned for dupes before adding
+    // a new attribute.
+    XMLAttribute* _rootAttribute;
+};
+
+
+enum Whitespace {
+    PRESERVE_WHITESPACE,
+    COLLAPSE_WHITESPACE
+};
+
+
+/** A Document binds together all the functionality.
+	It can be saved, loaded, and printed to the screen.
+	All Nodes are connected and allocated to a Document.
+	If the Document is deleted, all its Nodes are also deleted.
+*/
+class TINYXML2_LIB XMLDocument : public XMLNode
+{
+    friend class XMLElement;
+    // Gives access to SetError and Push/PopDepth, but over-access for everything else.
+    // Wishing C++ had "internal" scope.
+    friend class XMLNode;
+    friend class XMLText;
+    friend class XMLComment;
+    friend class XMLDeclaration;
+    friend class XMLUnknown;
+public:
+    /// constructor
+    XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE );
+    ~XMLDocument();
+
+    virtual XMLDocument* ToDocument()				{
+        TIXMLASSERT( this == _document );
+        return this;
+    }
+    virtual const XMLDocument* ToDocument() const	{
+        TIXMLASSERT( this == _document );
+        return this;
+    }
+
+    /**
+    	Parse an XML file from a character string.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+
+    	You may optionally pass in the 'nBytes', which is
+    	the number of bytes which will be parsed. If not
+    	specified, TinyXML-2 will assume 'xml' points to a
+    	null terminated string.
+    */
+    XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) );
+
+    /**
+    	Load an XML file from disk.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError LoadFile( const char* filename );
+
+    /**
+    	Load an XML file from disk. You are responsible
+    	for providing and closing the FILE*.
+
+        NOTE: The file should be opened as binary ("rb")
+        not text in order for TinyXML-2 to correctly
+        do newline normalization.
+
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError LoadFile( FILE* );
+
+    /**
+    	Save the XML file to disk.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError SaveFile( const char* filename, bool compact = false );
+
+    /**
+    	Save the XML file to disk. You are responsible
+    	for providing and closing the FILE*.
+
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError SaveFile( FILE* fp, bool compact = false );
+
+    bool ProcessEntities() const		{
+        return _processEntities;
+    }
+    Whitespace WhitespaceMode() const	{
+        return _whitespaceMode;
+    }
+
+    /**
+    	Returns true if this document has a leading Byte Order Mark of UTF8.
+    */
+    bool HasBOM() const {
+        return _writeBOM;
+    }
+    /** Sets whether to write the BOM when writing the file.
+    */
+    void SetBOM( bool useBOM ) {
+        _writeBOM = useBOM;
+    }
+
+    /** Return the root element of DOM. Equivalent to FirstChildElement().
+        To get the first node, use FirstChild().
+    */
+    XMLElement* RootElement()				{
+        return FirstChildElement();
+    }
+    const XMLElement* RootElement() const	{
+        return FirstChildElement();
+    }
+
+    /** Print the Document. If the Printer is not provided, it will
+        print to stdout. If you provide Printer, this can print to a file:
+    	@verbatim
+    	XMLPrinter printer( fp );
+    	doc.Print( &printer );
+    	@endverbatim
+
+    	Or you can use a printer to print to memory:
+    	@verbatim
+    	XMLPrinter printer;
+    	doc.Print( &printer );
+    	// printer.CStr() has a const char* to the XML
+    	@endverbatim
+    */
+    void Print( XMLPrinter* streamer=0 ) const;
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    /**
+    	Create a new Element associated with
+    	this Document. The memory for the Element
+    	is managed by the Document.
+    */
+    XMLElement* NewElement( const char* name );
+    /**
+    	Create a new Comment associated with
+    	this Document. The memory for the Comment
+    	is managed by the Document.
+    */
+    XMLComment* NewComment( const char* comment );
+    /**
+    	Create a new Text associated with
+    	this Document. The memory for the Text
+    	is managed by the Document.
+    */
+    XMLText* NewText( const char* text );
+    /**
+    	Create a new Declaration associated with
+    	this Document. The memory for the object
+    	is managed by the Document.
+
+    	If the 'text' param is null, the standard
+    	declaration is used.:
+    	@verbatim
+    		<?xml version="1.0" encoding="UTF-8"?>
+    	@endverbatim
+    */
+    XMLDeclaration* NewDeclaration( const char* text=0 );
+    /**
+    	Create a new Unknown associated with
+    	this Document. The memory for the object
+    	is managed by the Document.
+    */
+    XMLUnknown* NewUnknown( const char* text );
+
+    /**
+    	Delete a node associated with this document.
+    	It will be unlinked from the DOM.
+    */
+    void DeleteNode( XMLNode* node );
+
+    void ClearError() {
+        SetError(XML_SUCCESS, 0, 0);
+    }
+
+    /// Return true if there was an error parsing the document.
+    bool Error() const {
+        return _errorID != XML_SUCCESS;
+    }
+    /// Return the errorID.
+    XMLError  ErrorID() const {
+        return _errorID;
+    }
+	const char* ErrorName() const;
+    static const char* ErrorIDToName(XMLError errorID);
+
+    /** Returns a "long form" error description. A hopefully helpful
+        diagnostic with location, line number, and/or additional info.
+    */
+	const char* ErrorStr() const;
+
+    /// A (trivial) utility function that prints the ErrorStr() to stdout.
+    void PrintError() const;
+
+    /// Return the line where the error occurred, or zero if unknown.
+    int ErrorLineNum() const
+    {
+        return _errorLineNum;
+    }
+
+    /// Clear the document, resetting it to the initial state.
+    void Clear();
+
+	/**
+		Copies this document to a target document.
+		The target will be completely cleared before the copy.
+		If you want to copy a sub-tree, see XMLNode::DeepClone().
+
+		NOTE: that the 'target' must be non-null.
+	*/
+	void DeepCopy(XMLDocument* target) const;
+
+	// internal
+    char* Identify( char* p, XMLNode** node );
+
+	// internal
+	void MarkInUse(XMLNode*);
+
+    virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const	{
+        return 0;
+    }
+    virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const	{
+        return false;
+    }
+
+private:
+    XMLDocument( const XMLDocument& );	// not supported
+    void operator=( const XMLDocument& );	// not supported
+
+    bool			_writeBOM;
+    bool			_processEntities;
+    XMLError		_errorID;
+    Whitespace		_whitespaceMode;
+    mutable StrPair	_errorStr;
+    int             _errorLineNum;
+    char*			_charBuffer;
+    int				_parseCurLineNum;
+	int				_parsingDepth;
+	// Memory tracking does add some overhead.
+	// However, the code assumes that you don't
+	// have a bunch of unlinked nodes around.
+	// Therefore it takes less memory to track
+	// in the document vs. a linked list in the XMLNode,
+	// and the performance is the same.
+	DynArray<XMLNode*, 10> _unlinked;
+
+    MemPoolT< sizeof(XMLElement) >	 _elementPool;
+    MemPoolT< sizeof(XMLAttribute) > _attributePool;
+    MemPoolT< sizeof(XMLText) >		 _textPool;
+    MemPoolT< sizeof(XMLComment) >	 _commentPool;
+
+	static const char* _errorNames[XML_ERROR_COUNT];
+
+    void Parse();
+
+    void SetError( XMLError error, int lineNum, const char* format, ... );
+
+	// Something of an obvious security hole, once it was discovered.
+	// Either an ill-formed XML or an excessively deep one can overflow
+	// the stack. Track stack depth, and error out if needed.
+	class DepthTracker {
+	public:
+		explicit DepthTracker(XMLDocument * document) {
+			this->_document = document;
+			document->PushDepth();
+		}
+		~DepthTracker() {
+			_document->PopDepth();
+		}
+	private:
+		XMLDocument * _document;
+	};
+	void PushDepth();
+	void PopDepth();
+
+    template<class NodeType, int PoolElementSize>
+    NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool );
+};
+
+template<class NodeType, int PoolElementSize>
+inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool )
+{
+    TIXMLASSERT( sizeof( NodeType ) == PoolElementSize );
+    TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() );
+    NodeType* returnNode = new (pool.Alloc()) NodeType( this );
+    TIXMLASSERT( returnNode );
+    returnNode->_memPool = &pool;
+
+	_unlinked.Push(returnNode);
+    return returnNode;
+}
+
+/**
+	A XMLHandle is a class that wraps a node pointer with null checks; this is
+	an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2
+	DOM structure. It is a separate utility class.
+
+	Take an example:
+	@verbatim
+	<Document>
+		<Element attributeA = "valueA">
+			<Child attributeB = "value1" />
+			<Child attributeB = "value2" />
+		</Element>
+	</Document>
+	@endverbatim
+
+	Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+	easy to write a *lot* of code that looks like:
+
+	@verbatim
+	XMLElement* root = document.FirstChildElement( "Document" );
+	if ( root )
+	{
+		XMLElement* element = root->FirstChildElement( "Element" );
+		if ( element )
+		{
+			XMLElement* child = element->FirstChildElement( "Child" );
+			if ( child )
+			{
+				XMLElement* child2 = child->NextSiblingElement( "Child" );
+				if ( child2 )
+				{
+					// Finally do something useful.
+	@endverbatim
+
+	And that doesn't even cover "else" cases. XMLHandle addresses the verbosity
+	of such code. A XMLHandle checks for null pointers so it is perfectly safe
+	and correct to use:
+
+	@verbatim
+	XMLHandle docHandle( &document );
+	XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement();
+	if ( child2 )
+	{
+		// do something useful
+	@endverbatim
+
+	Which is MUCH more concise and useful.
+
+	It is also safe to copy handles - internally they are nothing more than node pointers.
+	@verbatim
+	XMLHandle handleCopy = handle;
+	@endverbatim
+
+	See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects.
+*/
+class TINYXML2_LIB XMLHandle
+{
+public:
+    /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+    explicit XMLHandle( XMLNode* node ) : _node( node ) {
+    }
+    /// Create a handle from a node.
+    explicit XMLHandle( XMLNode& node ) : _node( &node ) {
+    }
+    /// Copy constructor
+    XMLHandle( const XMLHandle& ref ) : _node( ref._node ) {
+    }
+    /// Assignment
+    XMLHandle& operator=( const XMLHandle& ref )							{
+        _node = ref._node;
+        return *this;
+    }
+
+    /// Get the first child of this handle.
+    XMLHandle FirstChild() 													{
+        return XMLHandle( _node ? _node->FirstChild() : 0 );
+    }
+    /// Get the first child element of this handle.
+    XMLHandle FirstChildElement( const char* name = 0 )						{
+        return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 );
+    }
+    /// Get the last child of this handle.
+    XMLHandle LastChild()													{
+        return XMLHandle( _node ? _node->LastChild() : 0 );
+    }
+    /// Get the last child element of this handle.
+    XMLHandle LastChildElement( const char* name = 0 )						{
+        return XMLHandle( _node ? _node->LastChildElement( name ) : 0 );
+    }
+    /// Get the previous sibling of this handle.
+    XMLHandle PreviousSibling()												{
+        return XMLHandle( _node ? _node->PreviousSibling() : 0 );
+    }
+    /// Get the previous sibling element of this handle.
+    XMLHandle PreviousSiblingElement( const char* name = 0 )				{
+        return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 );
+    }
+    /// Get the next sibling of this handle.
+    XMLHandle NextSibling()													{
+        return XMLHandle( _node ? _node->NextSibling() : 0 );
+    }
+    /// Get the next sibling element of this handle.
+    XMLHandle NextSiblingElement( const char* name = 0 )					{
+        return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 );
+    }
+
+    /// Safe cast to XMLNode. This can return null.
+    XMLNode* ToNode()							{
+        return _node;
+    }
+    /// Safe cast to XMLElement. This can return null.
+    XMLElement* ToElement() 					{
+        return ( _node ? _node->ToElement() : 0 );
+    }
+    /// Safe cast to XMLText. This can return null.
+    XMLText* ToText() 							{
+        return ( _node ? _node->ToText() : 0 );
+    }
+    /// Safe cast to XMLUnknown. This can return null.
+    XMLUnknown* ToUnknown() 					{
+        return ( _node ? _node->ToUnknown() : 0 );
+    }
+    /// Safe cast to XMLDeclaration. This can return null.
+    XMLDeclaration* ToDeclaration() 			{
+        return ( _node ? _node->ToDeclaration() : 0 );
+    }
+
+private:
+    XMLNode* _node;
+};
+
+
+/**
+	A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the
+	same in all regards, except for the 'const' qualifiers. See XMLHandle for API.
+*/
+class TINYXML2_LIB XMLConstHandle
+{
+public:
+    explicit XMLConstHandle( const XMLNode* node ) : _node( node ) {
+    }
+    explicit XMLConstHandle( const XMLNode& node ) : _node( &node ) {
+    }
+    XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) {
+    }
+
+    XMLConstHandle& operator=( const XMLConstHandle& ref )							{
+        _node = ref._node;
+        return *this;
+    }
+
+    const XMLConstHandle FirstChild() const											{
+        return XMLConstHandle( _node ? _node->FirstChild() : 0 );
+    }
+    const XMLConstHandle FirstChildElement( const char* name = 0 ) const				{
+        return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 );
+    }
+    const XMLConstHandle LastChild()	const										{
+        return XMLConstHandle( _node ? _node->LastChild() : 0 );
+    }
+    const XMLConstHandle LastChildElement( const char* name = 0 ) const				{
+        return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 );
+    }
+    const XMLConstHandle PreviousSibling() const									{
+        return XMLConstHandle( _node ? _node->PreviousSibling() : 0 );
+    }
+    const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const		{
+        return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 );
+    }
+    const XMLConstHandle NextSibling() const										{
+        return XMLConstHandle( _node ? _node->NextSibling() : 0 );
+    }
+    const XMLConstHandle NextSiblingElement( const char* name = 0 ) const			{
+        return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 );
+    }
+
+
+    const XMLNode* ToNode() const				{
+        return _node;
+    }
+    const XMLElement* ToElement() const			{
+        return ( _node ? _node->ToElement() : 0 );
+    }
+    const XMLText* ToText() const				{
+        return ( _node ? _node->ToText() : 0 );
+    }
+    const XMLUnknown* ToUnknown() const			{
+        return ( _node ? _node->ToUnknown() : 0 );
+    }
+    const XMLDeclaration* ToDeclaration() const	{
+        return ( _node ? _node->ToDeclaration() : 0 );
+    }
+
+private:
+    const XMLNode* _node;
+};
+
+
+/**
+	Printing functionality. The XMLPrinter gives you more
+	options than the XMLDocument::Print() method.
+
+	It can:
+	-# Print to memory.
+	-# Print to a file you provide.
+	-# Print XML without a XMLDocument.
+
+	Print to Memory
+
+	@verbatim
+	XMLPrinter printer;
+	doc.Print( &printer );
+	SomeFunction( printer.CStr() );
+	@endverbatim
+
+	Print to a File
+
+	You provide the file pointer.
+	@verbatim
+	XMLPrinter printer( fp );
+	doc.Print( &printer );
+	@endverbatim
+
+	Print without a XMLDocument
+
+	When loading, an XML parser is very useful. However, sometimes
+	when saving, it just gets in the way. The code is often set up
+	for streaming, and constructing the DOM is just overhead.
+
+	The Printer supports the streaming case. The following code
+	prints out a trivially simple XML file without ever creating
+	an XML document.
+
+	@verbatim
+	XMLPrinter printer( fp );
+	printer.OpenElement( "foo" );
+	printer.PushAttribute( "foo", "bar" );
+	printer.CloseElement();
+	@endverbatim
+*/
+class TINYXML2_LIB XMLPrinter : public XMLVisitor
+{
+public:
+    /** Construct the printer. If the FILE* is specified,
+    	this will print to the FILE. Else it will print
+    	to memory, and the result is available in CStr().
+    	If 'compact' is set to true, then output is created
+    	with only required whitespace and newlines.
+    */
+    XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 );
+    virtual ~XMLPrinter()	{}
+
+    /** If streaming, write the BOM and declaration. */
+    void PushHeader( bool writeBOM, bool writeDeclaration );
+    /** If streaming, start writing an element.
+        The element must be closed with CloseElement()
+    */
+    void OpenElement( const char* name, bool compactMode=false );
+    /// If streaming, add an attribute to an open element.
+    void PushAttribute( const char* name, const char* value );
+    void PushAttribute( const char* name, int value );
+    void PushAttribute( const char* name, unsigned value );
+	void PushAttribute(const char* name, int64_t value);
+	void PushAttribute( const char* name, bool value );
+    void PushAttribute( const char* name, double value );
+    /// If streaming, close the Element.
+    virtual void CloseElement( bool compactMode=false );
+
+    /// Add a text node.
+    void PushText( const char* text, bool cdata=false );
+    /// Add a text node from an integer.
+    void PushText( int value );
+    /// Add a text node from an unsigned.
+    void PushText( unsigned value );
+	/// Add a text node from an unsigned.
+	void PushText(int64_t value);
+	/// Add a text node from a bool.
+    void PushText( bool value );
+    /// Add a text node from a float.
+    void PushText( float value );
+    /// Add a text node from a double.
+    void PushText( double value );
+
+    /// Add a comment
+    void PushComment( const char* comment );
+
+    void PushDeclaration( const char* value );
+    void PushUnknown( const char* value );
+
+    virtual bool VisitEnter( const XMLDocument& /*doc*/ );
+    virtual bool VisitExit( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+
+    virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute );
+    virtual bool VisitExit( const XMLElement& element );
+
+    virtual bool Visit( const XMLText& text );
+    virtual bool Visit( const XMLComment& comment );
+    virtual bool Visit( const XMLDeclaration& declaration );
+    virtual bool Visit( const XMLUnknown& unknown );
+
+    /**
+    	If in print to memory mode, return a pointer to
+    	the XML file in memory.
+    */
+    const char* CStr() const {
+        return _buffer.Mem();
+    }
+    /**
+    	If in print to memory mode, return the size
+    	of the XML file in memory. (Note the size returned
+    	includes the terminating null.)
+    */
+    int CStrSize() const {
+        return _buffer.Size();
+    }
+    /**
+    	If in print to memory mode, reset the buffer to the
+    	beginning.
+    */
+    void ClearBuffer() {
+        _buffer.Clear();
+        _buffer.Push(0);
+		_firstElement = true;
+    }
+
+protected:
+	virtual bool CompactMode( const XMLElement& )	{ return _compactMode; }
+
+	/** Prints out the space before an element. You may override to change
+	    the space and tabs used. A PrintSpace() override should call Print().
+	*/
+    virtual void PrintSpace( int depth );
+    void Print( const char* format, ... );
+    void Write( const char* data, size_t size );
+    inline void Write( const char* data )           { Write( data, strlen( data ) ); }
+    void Putc( char ch );
+
+    void SealElementIfJustOpened();
+    bool _elementJustOpened;
+    DynArray< const char*, 10 > _stack;
+
+private:
+    void PrintString( const char*, bool restrictedEntitySet );	// prints out, after detecting entities.
+
+    bool _firstElement;
+    FILE* _fp;
+    int _depth;
+    int _textDepth;
+    bool _processEntities;
+	bool _compactMode;
+
+    enum {
+        ENTITY_RANGE = 64,
+        BUF_SIZE = 200
+    };
+    bool _entityFlag[ENTITY_RANGE];
+    bool _restrictedEntityFlag[ENTITY_RANGE];
+
+    DynArray< char, 20 > _buffer;
+
+    // Prohibit cloning, intentionally not implemented
+    XMLPrinter( const XMLPrinter& );
+    XMLPrinter& operator=( const XMLPrinter& );
+};
+
+
+}	// tinyxml2
+
+#if defined(_MSC_VER)
+#   pragma warning(pop)
+#endif
+
+#endif // TINYXML2_INCLUDED

+ 132 - 0
SATService/cJson/JsonUtils.cpp

@@ -0,0 +1,132 @@
+#include "JsonUtils.h"
+
+
+namespace JsonUtil {
+
+	cJSON * obj2json(const JsonType &obj) {
+
+		// 创建json根节点;
+		cJSON * pJsonRoot = NULL;
+		JsonList jsonList;
+		JsonMap jsonMap;
+		JsonVector jsonvec;
+
+		switch (obj.type()) {
+		case JsonType::VoidType:
+			pJsonRoot = cJSON_CreateNull();
+			break;
+		case JsonType::NumberType:
+			pJsonRoot = cJSON_CreateNumber(obj.toNumber());
+			break;
+		case JsonType::BooleanType:
+			pJsonRoot = cJSON_CreateBool(obj.toBoolean());
+			break;
+		case JsonType::StringType:
+			pJsonRoot = cJSON_CreateString(obj.toString().c_str());
+			break;
+		case JsonType::ListType:
+			jsonList.clear();
+			jsonList = obj.toList();
+			pJsonRoot = cJSON_CreateArray();
+			for (JsonList::const_iterator iter = jsonList.begin();
+				iter != jsonList.end(); ++iter) {
+				cJSON_AddItemToArray(pJsonRoot, obj2json(*iter));
+			}
+			break;
+		case JsonType::MapType:
+			jsonMap.clear();
+			jsonMap = obj.toMap();
+			pJsonRoot = cJSON_CreateObject();
+			for (JsonMap::const_iterator iter = jsonMap.begin();
+				iter != jsonMap.end(); ++iter) {
+				cJSON_AddItemToObject(pJsonRoot, iter->first.toString().c_str(), obj2json(iter->second));
+			}
+			break;
+		case JsonType::VectorType:
+			jsonvec.clear();
+			jsonvec = obj.toVector();
+			pJsonRoot = cJSON_CreateArray();
+			for (JsonVector::const_iterator it = jsonvec.begin(); it != jsonvec.end(); ++it) {
+				cJSON_AddItemToArray(pJsonRoot, obj2json(*it));
+			}
+			break;
+		default:
+			break;
+		}
+		return pJsonRoot;
+	}
+
+	JsonType json2obj(const cJSON *pJsonRoot) {
+
+		// 创建json根节点;
+		cJSON *pChild = NULL;
+		JsonList jsonList;
+		JsonMap jsonMap;
+
+		// 解析类型;
+		if (pJsonRoot) {
+			switch (pJsonRoot->type) {
+			case cJSON_NULL:
+				return JsonType();
+				break;
+			case cJSON_False:
+				return JsonType(false);
+				break;
+			case cJSON_True:
+				return JsonType(true);
+				break;
+			case cJSON_Number:
+				return JsonType(pJsonRoot->valuedouble);
+				break;
+			case cJSON_String:
+				return JsonType(pJsonRoot->valuestring);
+				break;
+			case cJSON_Array:
+				jsonList.clear();
+				pChild = pJsonRoot->child;
+				while (pChild) {
+					jsonList.push_back(json2obj(pChild));
+					pChild = pChild->next;
+				}
+				return JsonType(jsonList);
+				break;
+			case cJSON_Object:
+				jsonMap.clear();
+				pChild = pJsonRoot->child;
+				while (pChild) {
+					jsonMap.insert(JsonMap::value_type(JsonType(pChild->string), json2obj(pChild)));
+					pChild = pChild->next;
+				}
+				return JsonType(jsonMap);
+				break;
+			default:
+				break;
+			}
+		}
+		return JsonType();
+	}
+
+	string objectToString(const JsonType &obj) {
+		cJSON *pJson = obj2json(obj);
+		string str;
+		if (pJson) {
+			const char *pCh = cJSON_PrintUnformatted(pJson);
+			if (pCh) {
+				str = pCh;
+				free((void*)pCh);
+			}
+			cJSON_Delete(pJson);
+		}
+		return str;
+	}
+
+	JsonType stringToObject(const string &json) {
+		cJSON *pJson = cJSON_Parse(json.c_str());
+		JsonType obj = json2obj(pJson);
+		if (pJson) {
+			cJSON_Delete(pJson);
+		}
+		return obj;
+	}
+
+}

+ 15 - 0
SATService/cJson/JsonUtils.h

@@ -0,0 +1,15 @@
+#ifndef JSONUTIL_H
+#define JSONUTIL_H
+
+#include "cJSON.h"
+#include "json_type.h"
+
+namespace JsonUtil {
+
+    string objectToString(const JsonType &obj);
+
+    JsonType stringToObject(const string &json);
+
+}
+
+#endif // JSONUTIL_H

+ 132 - 0
SATService/cJson/Json_Type.h

@@ -0,0 +1,132 @@
+#ifndef JSONTYPE_H
+#define JSONTYPE_H
+
+#include <string>
+#include <list>
+#include <map>
+#include <vector>
+
+using std::string;
+using std::list;
+using std::vector;
+using std::map;
+
+class JsonType;
+
+typedef list<JsonType> JsonList;
+typedef vector<JsonType> JsonVector;
+typedef map<JsonType, JsonType> JsonMap;
+
+class JsonType {
+public:
+	JsonType() : m_type(JsonType::VoidType) {}
+
+	JsonType(int num) : m_type(JsonType::NumberType), num_vale(num) {}
+
+	JsonType(double fvalue) : m_type(JsonType::FloatType), float_value(fvalue) {}
+
+	JsonType(bool bl) : m_type(JsonType::BooleanType), bl_value(bl) {}
+
+	JsonType(const char *str) : m_type(JsonType::StringType), str_value(str) {}
+
+	JsonType(const char *str, int len) : m_type(JsonType::StringType), str_value(str, len) {}
+
+	JsonType(const string &str) : m_type(JsonType::StringType), str_value(str) {}
+
+	JsonType(const JsonList &list) : m_type(JsonType::ListType), json_list(list) {}
+
+	JsonType(const JsonVector &vec) : m_type(JsonType::VectorType), json_vec(vec) {}
+
+	JsonType(const JsonMap &map) : m_type(JsonType::MapType), json_map(map) {}
+
+	virtual ~JsonType() {}
+
+	bool operator<(const JsonType &val) const {
+		if (this->type() < val.type()) {
+			return true;
+		}
+		else if (this->type() == val.type()) {
+			switch (this->type()) {
+			case JsonType::VoidType:
+				return false;
+				break;
+			case JsonType::NumberType:
+				return this->toNumber() < val.toNumber();
+				break;
+			case JsonType::FloatType:
+				return this->toFloat() < val.toFloat();
+				break;
+			case JsonType::BooleanType:
+				return this->toBoolean() < val.toBoolean();
+				break;
+			case JsonType::StringType:
+				return this->toString() < val.toString();
+				break;
+			case JsonType::ListType:
+				return this->toList() < val.toList();
+				break;
+			case JsonType::VectorType:
+				return this->toVector() < val.toVector();
+				break;
+			case JsonType::MapType:
+				return this->toMap() < val.toMap();
+				break;
+			default:
+				break;
+			}
+		}
+		return false;
+	}
+
+public:
+	enum Type {
+		VoidType, NumberType, FloatType, BooleanType, StringType, ListType, VectorType, MapType
+	};
+
+private:
+	Type m_type;
+
+	int		 num_vale;
+	double   float_value;
+	bool     bl_value;
+	string   str_value;
+	JsonList json_list;
+	JsonVector json_vec;
+	JsonMap  json_map;
+
+public:
+	Type type() const {
+		return m_type;
+	}
+
+	int toNumber() const {
+		return num_vale;
+	}
+
+	double toFloat() const {
+		return float_value;
+	}
+
+	bool toBoolean() const {
+		return bl_value;
+	}
+
+	string toString() const {
+		return str_value;
+	}
+
+	JsonList toList() const {
+		return json_list;
+	}
+
+	JsonMap toMap() const {
+		return json_map;
+	}
+
+	JsonVector toVector() const {
+		return json_vec;
+	}
+};
+
+
+#endif // JSONTYPE_H

+ 2973 - 0
SATService/cJson/cJSON.c

@@ -0,0 +1,2973 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+typedef struct {
+    const unsigned char *json;
+    size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+    return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) {
+    if (!cJSON_IsString(item)) {
+        return NULL;
+    }
+
+    return item->valuestring;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 12)
+    #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+    static char version[15];
+    sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+    return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+    if ((string1 == NULL) || (string2 == NULL))
+    {
+        return 1;
+    }
+
+    if (string1 == string2)
+    {
+        return 0;
+    }
+
+    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+    {
+        if (*string1 == '\0')
+        {
+            return 0;
+        }
+    }
+
+    return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+    void *(CJSON_CDECL *allocate)(size_t size);
+    void (CJSON_CDECL *deallocate)(void *pointer);
+    void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dillimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+    return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+    free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+    return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+    size_t length = 0;
+    unsigned char *copy = NULL;
+
+    if (string == NULL)
+    {
+        return NULL;
+    }
+
+    length = strlen((const char*)string) + sizeof("");
+    copy = (unsigned char*)hooks->allocate(length);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    memcpy(copy, string, length);
+
+    return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+    if (hooks == NULL)
+    {
+        /* Reset hooks */
+        global_hooks.allocate = malloc;
+        global_hooks.deallocate = free;
+        global_hooks.reallocate = realloc;
+        return;
+    }
+
+    global_hooks.allocate = malloc;
+    if (hooks->malloc_fn != NULL)
+    {
+        global_hooks.allocate = hooks->malloc_fn;
+    }
+
+    global_hooks.deallocate = free;
+    if (hooks->free_fn != NULL)
+    {
+        global_hooks.deallocate = hooks->free_fn;
+    }
+
+    /* use realloc only if both free and malloc are used */
+    global_hooks.reallocate = NULL;
+    if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+    {
+        global_hooks.reallocate = realloc;
+    }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+    if (node)
+    {
+        memset(node, '\0', sizeof(cJSON));
+    }
+
+    return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+    cJSON *next = NULL;
+    while (item != NULL)
+    {
+        next = item->next;
+        if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+        {
+            cJSON_Delete(item->child);
+        }
+        if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+        {
+            global_hooks.deallocate(item->valuestring);
+        }
+        if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+        {
+            global_hooks.deallocate(item->string);
+        }
+        global_hooks.deallocate(item);
+        item = next;
+    }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+    struct lconv *lconv = localeconv();
+    return (unsigned char) lconv->decimal_point[0];
+#else
+    return '.';
+#endif
+}
+
+typedef struct
+{
+    const unsigned char *content;
+    size_t length;
+    size_t offset;
+    size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+    internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+    double number = 0;
+    unsigned char *after_end = NULL;
+    unsigned char number_c_string[64];
+    unsigned char decimal_point = get_decimal_point();
+    size_t i = 0;
+
+    if ((input_buffer == NULL) || (input_buffer->content == NULL))
+    {
+        return false;
+    }
+
+    /* copy the number into a temporary buffer and replace '.' with the decimal point
+     * of the current locale (for strtod)
+     * This also takes care of '\0' not necessarily being available for marking the end of the input */
+    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+    {
+        switch (buffer_at_offset(input_buffer)[i])
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case '+':
+            case '-':
+            case 'e':
+            case 'E':
+                number_c_string[i] = buffer_at_offset(input_buffer)[i];
+                break;
+
+            case '.':
+                number_c_string[i] = decimal_point;
+                break;
+
+            default:
+                goto loop_end;
+        }
+    }
+loop_end:
+    number_c_string[i] = '\0';
+
+    number = strtod((const char*)number_c_string, (char**)&after_end);
+    if (number_c_string == after_end)
+    {
+        return false; /* parse_error */
+    }
+
+    item->valuedouble = number;
+
+    /* use saturation in case of overflow */
+    if (number >= INT_MAX)
+    {
+        item->valueint = INT_MAX;
+    }
+    else if (number <= (double)INT_MIN)
+    {
+        item->valueint = INT_MIN;
+    }
+    else
+    {
+        item->valueint = (int)number;
+    }
+
+    item->type = cJSON_Number;
+
+    input_buffer->offset += (size_t)(after_end - number_c_string);
+    return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+    if (number >= INT_MAX)
+    {
+        object->valueint = INT_MAX;
+    }
+    else if (number <= (double)INT_MIN)
+    {
+        object->valueint = INT_MIN;
+    }
+    else
+    {
+        object->valueint = (int)number;
+    }
+
+    return object->valuedouble = number;
+}
+
+typedef struct
+{
+    unsigned char *buffer;
+    size_t length;
+    size_t offset;
+    size_t depth; /* current nesting depth (for formatted printing) */
+    cJSON_bool noalloc;
+    cJSON_bool format; /* is this print a formatted print */
+    internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+    unsigned char *newbuffer = NULL;
+    size_t newsize = 0;
+
+    if ((p == NULL) || (p->buffer == NULL))
+    {
+        return NULL;
+    }
+
+    if ((p->length > 0) && (p->offset >= p->length))
+    {
+        /* make sure that offset is valid */
+        return NULL;
+    }
+
+    if (needed > INT_MAX)
+    {
+        /* sizes bigger than INT_MAX are currently not supported */
+        return NULL;
+    }
+
+    needed += p->offset + 1;
+    if (needed <= p->length)
+    {
+        return p->buffer + p->offset;
+    }
+
+    if (p->noalloc) {
+        return NULL;
+    }
+
+    /* calculate new buffer size */
+    if (needed > (INT_MAX / 2))
+    {
+        /* overflow of int, use INT_MAX if possible */
+        if (needed <= INT_MAX)
+        {
+            newsize = INT_MAX;
+        }
+        else
+        {
+            return NULL;
+        }
+    }
+    else
+    {
+        newsize = needed * 2;
+    }
+
+    if (p->hooks.reallocate != NULL)
+    {
+        /* reallocate with realloc if available */
+        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+        if (newbuffer == NULL)
+        {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+    }
+    else
+    {
+        /* otherwise reallocate manually */
+        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+        if (!newbuffer)
+        {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+        if (newbuffer)
+        {
+            memcpy(newbuffer, p->buffer, p->offset + 1);
+        }
+        p->hooks.deallocate(p->buffer);
+    }
+    p->length = newsize;
+    p->buffer = newbuffer;
+
+    return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+    const unsigned char *buffer_pointer = NULL;
+    if ((buffer == NULL) || (buffer->buffer == NULL))
+    {
+        return;
+    }
+    buffer_pointer = buffer->buffer + buffer->offset;
+
+    buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    double d = item->valuedouble;
+    int length = 0;
+    size_t i = 0;
+    unsigned char number_buffer[26]; /* temporary buffer to print the number into */
+    unsigned char decimal_point = get_decimal_point();
+    double test;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* This checks for NaN and Infinity */
+    if ((d * 0) != 0)
+    {
+        length = sprintf((char*)number_buffer, "null");
+    }
+    else
+    {
+        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+        length = sprintf((char*)number_buffer, "%1.15g", d);
+
+        /* Check whether the original double can be recovered */
+        if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d))
+        {
+            /* If not, print with 17 decimal places of precision */
+            length = sprintf((char*)number_buffer, "%1.17g", d);
+        }
+    }
+
+    /* sprintf failed or buffer overrun occurred */
+    if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+    {
+        return false;
+    }
+
+    /* reserve appropriate space in the output */
+    output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    /* copy the printed number to the output and replace locale
+     * dependent decimal point with '.' */
+    for (i = 0; i < ((size_t)length); i++)
+    {
+        if (number_buffer[i] == decimal_point)
+        {
+            output_pointer[i] = '.';
+            continue;
+        }
+
+        output_pointer[i] = number_buffer[i];
+    }
+    output_pointer[i] = '\0';
+
+    output_buffer->offset += (size_t)length;
+
+    return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+    unsigned int h = 0;
+    size_t i = 0;
+
+    for (i = 0; i < 4; i++)
+    {
+        /* parse digit */
+        if ((input[i] >= '0') && (input[i] <= '9'))
+        {
+            h += (unsigned int) input[i] - '0';
+        }
+        else if ((input[i] >= 'A') && (input[i] <= 'F'))
+        {
+            h += (unsigned int) 10 + input[i] - 'A';
+        }
+        else if ((input[i] >= 'a') && (input[i] <= 'f'))
+        {
+            h += (unsigned int) 10 + input[i] - 'a';
+        }
+        else /* invalid */
+        {
+            return 0;
+        }
+
+        if (i < 3)
+        {
+            /* shift left to make place for the next nibble */
+            h = h << 4;
+        }
+    }
+
+    return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+    long unsigned int codepoint = 0;
+    unsigned int first_code = 0;
+    const unsigned char *first_sequence = input_pointer;
+    unsigned char utf8_length = 0;
+    unsigned char utf8_position = 0;
+    unsigned char sequence_length = 0;
+    unsigned char first_byte_mark = 0;
+
+    if ((input_end - first_sequence) < 6)
+    {
+        /* input ends unexpectedly */
+        goto fail;
+    }
+
+    /* get the first utf16 sequence */
+    first_code = parse_hex4(first_sequence + 2);
+
+    /* check that the code is valid */
+    if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+    {
+        goto fail;
+    }
+
+    /* UTF16 surrogate pair */
+    if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+    {
+        const unsigned char *second_sequence = first_sequence + 6;
+        unsigned int second_code = 0;
+        sequence_length = 12; /* \uXXXX\uXXXX */
+
+        if ((input_end - second_sequence) < 6)
+        {
+            /* input ends unexpectedly */
+            goto fail;
+        }
+
+        if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+        {
+            /* missing second half of the surrogate pair */
+            goto fail;
+        }
+
+        /* get the second utf16 sequence */
+        second_code = parse_hex4(second_sequence + 2);
+        /* check that the code is valid */
+        if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+        {
+            /* invalid second half of the surrogate pair */
+            goto fail;
+        }
+
+
+        /* calculate the unicode codepoint from the surrogate pair */
+        codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+    }
+    else
+    {
+        sequence_length = 6; /* \uXXXX */
+        codepoint = first_code;
+    }
+
+    /* encode as UTF-8
+     * takes at maximum 4 bytes to encode:
+     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+    if (codepoint < 0x80)
+    {
+        /* normal ascii, encoding 0xxxxxxx */
+        utf8_length = 1;
+    }
+    else if (codepoint < 0x800)
+    {
+        /* two bytes, encoding 110xxxxx 10xxxxxx */
+        utf8_length = 2;
+        first_byte_mark = 0xC0; /* 11000000 */
+    }
+    else if (codepoint < 0x10000)
+    {
+        /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 3;
+        first_byte_mark = 0xE0; /* 11100000 */
+    }
+    else if (codepoint <= 0x10FFFF)
+    {
+        /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 4;
+        first_byte_mark = 0xF0; /* 11110000 */
+    }
+    else
+    {
+        /* invalid unicode codepoint */
+        goto fail;
+    }
+
+    /* encode as utf8 */
+    for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+    {
+        /* 10xxxxxx */
+        (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+        codepoint >>= 6;
+    }
+    /* encode first byte */
+    if (utf8_length > 1)
+    {
+        (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+    }
+    else
+    {
+        (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+    }
+
+    *output_pointer += utf8_length;
+
+    return sequence_length;
+
+fail:
+    return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+    const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+    const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+    unsigned char *output_pointer = NULL;
+    unsigned char *output = NULL;
+
+    /* not a string */
+    if (buffer_at_offset(input_buffer)[0] != '\"')
+    {
+        goto fail;
+    }
+
+    {
+        /* calculate approximate size of the output (overestimate) */
+        size_t allocation_length = 0;
+        size_t skipped_bytes = 0;
+        while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+        {
+            /* is escape sequence */
+            if (input_end[0] == '\\')
+            {
+                if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+                {
+                    /* prevent buffer overflow when last input character is a backslash */
+                    goto fail;
+                }
+                skipped_bytes++;
+                input_end++;
+            }
+            input_end++;
+        }
+        if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+        {
+            goto fail; /* string ended unexpectedly */
+        }
+
+        /* This is at most how much we need for the output */
+        allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+        output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+        if (output == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+    }
+
+    output_pointer = output;
+    /* loop through the string literal */
+    while (input_pointer < input_end)
+    {
+        if (*input_pointer != '\\')
+        {
+            *output_pointer++ = *input_pointer++;
+        }
+        /* escape sequence */
+        else
+        {
+            unsigned char sequence_length = 2;
+            if ((input_end - input_pointer) < 1)
+            {
+                goto fail;
+            }
+
+            switch (input_pointer[1])
+            {
+                case 'b':
+                    *output_pointer++ = '\b';
+                    break;
+                case 'f':
+                    *output_pointer++ = '\f';
+                    break;
+                case 'n':
+                    *output_pointer++ = '\n';
+                    break;
+                case 'r':
+                    *output_pointer++ = '\r';
+                    break;
+                case 't':
+                    *output_pointer++ = '\t';
+                    break;
+                case '\"':
+                case '\\':
+                case '/':
+                    *output_pointer++ = input_pointer[1];
+                    break;
+
+                /* UTF-16 literal */
+                case 'u':
+                    sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+                    if (sequence_length == 0)
+                    {
+                        /* failed to convert UTF16-literal to UTF-8 */
+                        goto fail;
+                    }
+                    break;
+
+                default:
+                    goto fail;
+            }
+            input_pointer += sequence_length;
+        }
+    }
+
+    /* zero terminate the output */
+    *output_pointer = '\0';
+
+    item->type = cJSON_String;
+    item->valuestring = (char*)output;
+
+    input_buffer->offset = (size_t) (input_end - input_buffer->content);
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if (output != NULL)
+    {
+        input_buffer->hooks.deallocate(output);
+    }
+
+    if (input_pointer != NULL)
+    {
+        input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+    }
+
+    return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+    const unsigned char *input_pointer = NULL;
+    unsigned char *output = NULL;
+    unsigned char *output_pointer = NULL;
+    size_t output_length = 0;
+    /* numbers of additional characters needed for escaping */
+    size_t escape_characters = 0;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* empty string */
+    if (input == NULL)
+    {
+        output = ensure(output_buffer, sizeof("\"\""));
+        if (output == NULL)
+        {
+            return false;
+        }
+        strcpy((char*)output, "\"\"");
+
+        return true;
+    }
+
+    /* set "flag" to 1 if something needs to be escaped */
+    for (input_pointer = input; *input_pointer; input_pointer++)
+    {
+        switch (*input_pointer)
+        {
+            case '\"':
+            case '\\':
+            case '\b':
+            case '\f':
+            case '\n':
+            case '\r':
+            case '\t':
+                /* one character escape sequence */
+                escape_characters++;
+                break;
+            default:
+                if (*input_pointer < 32)
+                {
+                    /* UTF-16 escape sequence uXXXX */
+                    escape_characters += 5;
+                }
+                break;
+        }
+    }
+    output_length = (size_t)(input_pointer - input) + escape_characters;
+
+    output = ensure(output_buffer, output_length + sizeof("\"\""));
+    if (output == NULL)
+    {
+        return false;
+    }
+
+    /* no characters have to be escaped */
+    if (escape_characters == 0)
+    {
+        output[0] = '\"';
+        memcpy(output + 1, input, output_length);
+        output[output_length + 1] = '\"';
+        output[output_length + 2] = '\0';
+
+        return true;
+    }
+
+    output[0] = '\"';
+    output_pointer = output + 1;
+    /* copy the string */
+    for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+    {
+        if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+        {
+            /* normal character, copy */
+            *output_pointer = *input_pointer;
+        }
+        else
+        {
+            /* character needs to be escaped */
+            *output_pointer++ = '\\';
+            switch (*input_pointer)
+            {
+                case '\\':
+                    *output_pointer = '\\';
+                    break;
+                case '\"':
+                    *output_pointer = '\"';
+                    break;
+                case '\b':
+                    *output_pointer = 'b';
+                    break;
+                case '\f':
+                    *output_pointer = 'f';
+                    break;
+                case '\n':
+                    *output_pointer = 'n';
+                    break;
+                case '\r':
+                    *output_pointer = 'r';
+                    break;
+                case '\t':
+                    *output_pointer = 't';
+                    break;
+                default:
+                    /* escape and print as unicode codepoint */
+                    sprintf((char*)output_pointer, "u%04x", *input_pointer);
+                    output_pointer += 4;
+                    break;
+            }
+        }
+    }
+    output[output_length + 1] = '\"';
+    output[output_length + 2] = '\0';
+
+    return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+    return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL))
+    {
+        return NULL;
+    }
+
+    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+    {
+       buffer->offset++;
+    }
+
+    if (buffer->offset == buffer->length)
+    {
+        buffer->offset--;
+    }
+
+    return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+    {
+        return NULL;
+    }
+
+    if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+    {
+        buffer->offset += 3;
+    }
+
+    return buffer;
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+    parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+    cJSON *item = NULL;
+
+    /* reset error position */
+    global_error.json = NULL;
+    global_error.position = 0;
+
+    if (value == NULL)
+    {
+        goto fail;
+    }
+
+    buffer.content = (const unsigned char*)value;
+    buffer.length = strlen((const char*)value) + sizeof("");
+    buffer.offset = 0;
+    buffer.hooks = global_hooks;
+
+    item = cJSON_New_Item(&global_hooks);
+    if (item == NULL) /* memory fail */
+    {
+        goto fail;
+    }
+
+    if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+    {
+        /* parse failure. ep is set. */
+        goto fail;
+    }
+
+    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+    if (require_null_terminated)
+    {
+        buffer_skip_whitespace(&buffer);
+        if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+        {
+            goto fail;
+        }
+    }
+    if (return_parse_end)
+    {
+        *return_parse_end = (const char*)buffer_at_offset(&buffer);
+    }
+
+    return item;
+
+fail:
+    if (item != NULL)
+    {
+        cJSON_Delete(item);
+    }
+
+    if (value != NULL)
+    {
+        error local_error;
+        local_error.json = (const unsigned char*)value;
+        local_error.position = 0;
+
+        if (buffer.offset < buffer.length)
+        {
+            local_error.position = buffer.offset;
+        }
+        else if (buffer.length > 0)
+        {
+            local_error.position = buffer.length - 1;
+        }
+
+        if (return_parse_end != NULL)
+        {
+            *return_parse_end = (const char*)local_error.json + local_error.position;
+        }
+
+        global_error = local_error;
+    }
+
+    return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+    return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+#define cjson_min(a, b) ((a < b) ? a : b)
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+    static const size_t default_buffer_size = 256;
+    printbuffer buffer[1];
+    unsigned char *printed = NULL;
+
+    memset(buffer, 0, sizeof(buffer));
+
+    /* create buffer */
+    buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+    buffer->length = default_buffer_size;
+    buffer->format = format;
+    buffer->hooks = *hooks;
+    if (buffer->buffer == NULL)
+    {
+        goto fail;
+    }
+
+    /* print the value */
+    if (!print_value(item, buffer))
+    {
+        goto fail;
+    }
+    update_offset(buffer);
+
+    /* check if reallocate is available */
+    if (hooks->reallocate != NULL)
+    {
+        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+        if (printed == NULL) {
+            goto fail;
+        }
+        buffer->buffer = NULL;
+    }
+    else /* otherwise copy the JSON over to a new buffer */
+    {
+        printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+        if (printed == NULL)
+        {
+            goto fail;
+        }
+        memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+        printed[buffer->offset] = '\0'; /* just to be sure */
+
+        /* free the buffer */
+        hooks->deallocate(buffer->buffer);
+    }
+
+    return printed;
+
+fail:
+    if (buffer->buffer != NULL)
+    {
+        hooks->deallocate(buffer->buffer);
+    }
+
+    if (printed != NULL)
+    {
+        hooks->deallocate(printed);
+    }
+
+    return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+    return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+    return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if (prebuffer < 0)
+    {
+        return NULL;
+    }
+
+    p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+    if (!p.buffer)
+    {
+        return NULL;
+    }
+
+    p.length = (size_t)prebuffer;
+    p.offset = 0;
+    p.noalloc = false;
+    p.format = fmt;
+    p.hooks = global_hooks;
+
+    if (!print_value(item, &p))
+    {
+        global_hooks.deallocate(p.buffer);
+        return NULL;
+    }
+
+    return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if ((len < 0) || (buf == NULL))
+    {
+        return false;
+    }
+
+    p.buffer = (unsigned char*)buf;
+    p.length = (size_t)len;
+    p.offset = 0;
+    p.noalloc = true;
+    p.format = fmt;
+    p.hooks = global_hooks;
+
+    return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+    if ((input_buffer == NULL) || (input_buffer->content == NULL))
+    {
+        return false; /* no input */
+    }
+
+    /* parse the different types of values */
+    /* null */
+    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+    {
+        item->type = cJSON_NULL;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* false */
+    if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+    {
+        item->type = cJSON_False;
+        input_buffer->offset += 5;
+        return true;
+    }
+    /* true */
+    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+    {
+        item->type = cJSON_True;
+        item->valueint = 1;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* string */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+    {
+        return parse_string(item, input_buffer);
+    }
+    /* number */
+    if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+    {
+        return parse_number(item, input_buffer);
+    }
+    /* array */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+    {
+        return parse_array(item, input_buffer);
+    }
+    /* object */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+    {
+        return parse_object(item, input_buffer);
+    }
+
+    return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output = NULL;
+
+    if ((item == NULL) || (output_buffer == NULL))
+    {
+        return false;
+    }
+
+    switch ((item->type) & 0xFF)
+    {
+        case cJSON_NULL:
+            output = ensure(output_buffer, 5);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "null");
+            return true;
+
+        case cJSON_False:
+            output = ensure(output_buffer, 6);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "false");
+            return true;
+
+        case cJSON_True:
+            output = ensure(output_buffer, 5);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "true");
+            return true;
+
+        case cJSON_Number:
+            return print_number(item, output_buffer);
+
+        case cJSON_Raw:
+        {
+            size_t raw_length = 0;
+            if (item->valuestring == NULL)
+            {
+                return false;
+            }
+
+            raw_length = strlen(item->valuestring) + sizeof("");
+            output = ensure(output_buffer, raw_length);
+            if (output == NULL)
+            {
+                return false;
+            }
+            memcpy(output, item->valuestring, raw_length);
+            return true;
+        }
+
+        case cJSON_String:
+            return print_string(item, output_buffer);
+
+        case cJSON_Array:
+            return print_array(item, output_buffer);
+
+        case cJSON_Object:
+            return print_object(item, output_buffer);
+
+        default:
+            return false;
+    }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+    cJSON *head = NULL; /* head of the linked list */
+    cJSON *current_item = NULL;
+
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if (buffer_at_offset(input_buffer)[0] != '[')
+    {
+        /* not an array */
+        goto fail;
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+    {
+        /* empty array */
+        goto success;
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do
+    {
+        /* allocate next item */
+        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if (new_item == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if (head == NULL)
+        {
+            /* start the linked list */
+            current_item = head = new_item;
+        }
+        else
+        {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse next value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_value(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    }
+    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+    {
+        goto fail; /* expected end of array */
+    }
+
+success:
+    input_buffer->depth--;
+
+    item->type = cJSON_Array;
+    item->child = head;
+
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if (head != NULL)
+    {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    size_t length = 0;
+    cJSON *current_element = item->child;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* Compose the output array. */
+    /* opening square bracket */
+    output_pointer = ensure(output_buffer, 1);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    *output_pointer = '[';
+    output_buffer->offset++;
+    output_buffer->depth++;
+
+    while (current_element != NULL)
+    {
+        if (!print_value(current_element, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+        if (current_element->next)
+        {
+            length = (size_t) (output_buffer->format ? 2 : 1);
+            output_pointer = ensure(output_buffer, length + 1);
+            if (output_pointer == NULL)
+            {
+                return false;
+            }
+            *output_pointer++ = ',';
+            if(output_buffer->format)
+            {
+                *output_pointer++ = ' ';
+            }
+            *output_pointer = '\0';
+            output_buffer->offset += length;
+        }
+        current_element = current_element->next;
+    }
+
+    output_pointer = ensure(output_buffer, 2);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+    *output_pointer++ = ']';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+    cJSON *head = NULL; /* linked list head */
+    cJSON *current_item = NULL;
+
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+    {
+        goto fail; /* not an object */
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+    {
+        goto success; /* empty object */
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do
+    {
+        /* allocate next item */
+        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if (new_item == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if (head == NULL)
+        {
+            /* start the linked list */
+            current_item = head = new_item;
+        }
+        else
+        {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse the name of the child */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_string(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse name */
+        }
+        buffer_skip_whitespace(input_buffer);
+
+        /* swap valuestring and string, because we parsed the name */
+        current_item->string = current_item->valuestring;
+        current_item->valuestring = NULL;
+
+        if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+        {
+            goto fail; /* invalid object */
+        }
+
+        /* parse the value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_value(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    }
+    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+    {
+        goto fail; /* expected end of object */
+    }
+
+success:
+    input_buffer->depth--;
+
+    item->type = cJSON_Object;
+    item->child = head;
+
+    input_buffer->offset++;
+    return true;
+
+fail:
+    if (head != NULL)
+    {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    size_t length = 0;
+    cJSON *current_item = item->child;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* Compose the output: */
+    length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+    output_pointer = ensure(output_buffer, length + 1);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    *output_pointer++ = '{';
+    output_buffer->depth++;
+    if (output_buffer->format)
+    {
+        *output_pointer++ = '\n';
+    }
+    output_buffer->offset += length;
+
+    while (current_item)
+    {
+        if (output_buffer->format)
+        {
+            size_t i;
+            output_pointer = ensure(output_buffer, output_buffer->depth);
+            if (output_pointer == NULL)
+            {
+                return false;
+            }
+            for (i = 0; i < output_buffer->depth; i++)
+            {
+                *output_pointer++ = '\t';
+            }
+            output_buffer->offset += output_buffer->depth;
+        }
+
+        /* print key */
+        if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        length = (size_t) (output_buffer->format ? 2 : 1);
+        output_pointer = ensure(output_buffer, length);
+        if (output_pointer == NULL)
+        {
+            return false;
+        }
+        *output_pointer++ = ':';
+        if (output_buffer->format)
+        {
+            *output_pointer++ = '\t';
+        }
+        output_buffer->offset += length;
+
+        /* print value */
+        if (!print_value(current_item, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        /* print comma if not last */
+        length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+        output_pointer = ensure(output_buffer, length + 1);
+        if (output_pointer == NULL)
+        {
+            return false;
+        }
+        if (current_item->next)
+        {
+            *output_pointer++ = ',';
+        }
+
+        if (output_buffer->format)
+        {
+            *output_pointer++ = '\n';
+        }
+        *output_pointer = '\0';
+        output_buffer->offset += length;
+
+        current_item = current_item->next;
+    }
+
+    output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+    if (output_buffer->format)
+    {
+        size_t i;
+        for (i = 0; i < (output_buffer->depth - 1); i++)
+        {
+            *output_pointer++ = '\t';
+        }
+    }
+    *output_pointer++ = '}';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+    cJSON *child = NULL;
+    size_t size = 0;
+
+    if (array == NULL)
+    {
+        return 0;
+    }
+
+    child = array->child;
+
+    while(child != NULL)
+    {
+        size++;
+        child = child->next;
+    }
+
+    /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+    return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+    cJSON *current_child = NULL;
+
+    if (array == NULL)
+    {
+        return NULL;
+    }
+
+    current_child = array->child;
+    while ((current_child != NULL) && (index > 0))
+    {
+        index--;
+        current_child = current_child->next;
+    }
+
+    return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+    if (index < 0)
+    {
+        return NULL;
+    }
+
+    return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+    cJSON *current_element = NULL;
+
+    if ((object == NULL) || (name == NULL))
+    {
+        return NULL;
+    }
+
+    current_element = object->child;
+    if (case_sensitive)
+    {
+        while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+        {
+            current_element = current_element->next;
+        }
+    }
+    else
+    {
+        while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+        {
+            current_element = current_element->next;
+        }
+    }
+
+    if ((current_element == NULL) || (current_element->string == NULL)) {
+        return NULL;
+    }
+
+    return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+    return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+    return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+    return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+    prev->next = item;
+    item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+    cJSON *reference = NULL;
+    if (item == NULL)
+    {
+        return NULL;
+    }
+
+    reference = cJSON_New_Item(hooks);
+    if (reference == NULL)
+    {
+        return NULL;
+    }
+
+    memcpy(reference, item, sizeof(cJSON));
+    reference->string = NULL;
+    reference->type |= cJSON_IsReference;
+    reference->next = reference->prev = NULL;
+    return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+    cJSON *child = NULL;
+
+    if ((item == NULL) || (array == NULL))
+    {
+        return false;
+    }
+
+    child = array->child;
+
+    if (child == NULL)
+    {
+        /* list is empty, start new one */
+        array->child = item;
+    }
+    else
+    {
+        /* append to the end */
+        while (child->next)
+        {
+            child = child->next;
+        }
+        suffix_object(child, item);
+    }
+
+    return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+    add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+    return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+    char *new_key = NULL;
+    int new_type = cJSON_Invalid;
+
+    if ((object == NULL) || (string == NULL) || (item == NULL))
+    {
+        return false;
+    }
+
+    if (constant_key)
+    {
+        new_key = (char*)cast_away_const(string);
+        new_type = item->type | cJSON_StringIsConst;
+    }
+    else
+    {
+        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+        if (new_key == NULL)
+        {
+            return false;
+        }
+
+        new_type = item->type & ~cJSON_StringIsConst;
+    }
+
+    if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+    {
+        hooks->deallocate(item->string);
+    }
+
+    item->string = new_key;
+    item->type = new_type;
+
+    return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+    add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+    add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+    if (array == NULL)
+    {
+        return;
+    }
+
+    add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+    if ((object == NULL) || (string == NULL))
+    {
+        return;
+    }
+
+    add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+    cJSON *null = cJSON_CreateNull();
+    if (add_item_to_object(object, name, null, &global_hooks, false))
+    {
+        return null;
+    }
+
+    cJSON_Delete(null);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+    cJSON *true_item = cJSON_CreateTrue();
+    if (add_item_to_object(object, name, true_item, &global_hooks, false))
+    {
+        return true_item;
+    }
+
+    cJSON_Delete(true_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+    cJSON *false_item = cJSON_CreateFalse();
+    if (add_item_to_object(object, name, false_item, &global_hooks, false))
+    {
+        return false_item;
+    }
+
+    cJSON_Delete(false_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+    cJSON *bool_item = cJSON_CreateBool(boolean);
+    if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+    {
+        return bool_item;
+    }
+
+    cJSON_Delete(bool_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+    cJSON *number_item = cJSON_CreateNumber(number);
+    if (add_item_to_object(object, name, number_item, &global_hooks, false))
+    {
+        return number_item;
+    }
+
+    cJSON_Delete(number_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+    cJSON *string_item = cJSON_CreateString(string);
+    if (add_item_to_object(object, name, string_item, &global_hooks, false))
+    {
+        return string_item;
+    }
+
+    cJSON_Delete(string_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+    cJSON *raw_item = cJSON_CreateRaw(raw);
+    if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+    {
+        return raw_item;
+    }
+
+    cJSON_Delete(raw_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+    cJSON *object_item = cJSON_CreateObject();
+    if (add_item_to_object(object, name, object_item, &global_hooks, false))
+    {
+        return object_item;
+    }
+
+    cJSON_Delete(object_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+    cJSON *array = cJSON_CreateArray();
+    if (add_item_to_object(object, name, array, &global_hooks, false))
+    {
+        return array;
+    }
+
+    cJSON_Delete(array);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+    if ((parent == NULL) || (item == NULL))
+    {
+        return NULL;
+    }
+
+    if (item->prev != NULL)
+    {
+        /* not the first element */
+        item->prev->next = item->next;
+    }
+    if (item->next != NULL)
+    {
+        /* not the last element */
+        item->next->prev = item->prev;
+    }
+
+    if (item == parent->child)
+    {
+        /* first element */
+        parent->child = item->next;
+    }
+    /* make sure the detached item doesn't point anywhere anymore */
+    item->prev = NULL;
+    item->next = NULL;
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+    if (which < 0)
+    {
+        return NULL;
+    }
+
+    return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+    cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+    cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+    cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+    cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+    cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    cJSON *after_inserted = NULL;
+
+    if (which < 0)
+    {
+        return;
+    }
+
+    after_inserted = get_array_item(array, (size_t)which);
+    if (after_inserted == NULL)
+    {
+        add_item_to_array(array, newitem);
+        return;
+    }
+
+    newitem->next = after_inserted;
+    newitem->prev = after_inserted->prev;
+    after_inserted->prev = newitem;
+    if (after_inserted == array->child)
+    {
+        array->child = newitem;
+    }
+    else
+    {
+        newitem->prev->next = newitem;
+    }
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+    if ((parent == NULL) || (replacement == NULL) || (item == NULL))
+    {
+        return false;
+    }
+
+    if (replacement == item)
+    {
+        return true;
+    }
+
+    replacement->next = item->next;
+    replacement->prev = item->prev;
+
+    if (replacement->next != NULL)
+    {
+        replacement->next->prev = replacement;
+    }
+    if (replacement->prev != NULL)
+    {
+        replacement->prev->next = replacement;
+    }
+    if (parent->child == item)
+    {
+        parent->child = replacement;
+    }
+
+    item->next = NULL;
+    item->prev = NULL;
+    cJSON_Delete(item);
+
+    return true;
+}
+
+CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    if (which < 0)
+    {
+        return;
+    }
+
+    cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+    if ((replacement == NULL) || (string == NULL))
+    {
+        return false;
+    }
+
+    /* replace the name in the replacement */
+    if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+    {
+        cJSON_free(replacement->string);
+    }
+    replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+    replacement->type &= ~cJSON_StringIsConst;
+
+    cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+
+    return true;
+}
+
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+    replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+    replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_NULL;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_True;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = b ? cJSON_True : cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_Number;
+        item->valuedouble = num;
+
+        /* use saturation in case of overflow */
+        if (num >= INT_MAX)
+        {
+            item->valueint = INT_MAX;
+        }
+        else if (num <= (double)INT_MIN)
+        {
+            item->valueint = INT_MIN;
+        }
+        else
+        {
+            item->valueint = (int)num;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_String;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+        if(!item->valuestring)
+        {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL)
+    {
+        item->type = cJSON_String | cJSON_IsReference;
+        item->valuestring = (char*)cast_away_const(string);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL) {
+        item->type = cJSON_Object | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL) {
+        item->type = cJSON_Array | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_Raw;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+        if(!item->valuestring)
+        {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type=cJSON_Array;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_Object;
+    }
+
+    return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+    for(i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber(numbers[i]);
+        if (!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber((double)numbers[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0;a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber(numbers[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (strings == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for (i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateString(strings[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p,n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+    cJSON *newitem = NULL;
+    cJSON *child = NULL;
+    cJSON *next = NULL;
+    cJSON *newchild = NULL;
+
+    /* Bail on bad ptr */
+    if (!item)
+    {
+        goto fail;
+    }
+    /* Create new item */
+    newitem = cJSON_New_Item(&global_hooks);
+    if (!newitem)
+    {
+        goto fail;
+    }
+    /* Copy over all vars */
+    newitem->type = item->type & (~cJSON_IsReference);
+    newitem->valueint = item->valueint;
+    newitem->valuedouble = item->valuedouble;
+    if (item->valuestring)
+    {
+        newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+        if (!newitem->valuestring)
+        {
+            goto fail;
+        }
+    }
+    if (item->string)
+    {
+        newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+        if (!newitem->string)
+        {
+            goto fail;
+        }
+    }
+    /* If non-recursive, then we're done! */
+    if (!recurse)
+    {
+        return newitem;
+    }
+    /* Walk the ->next chain for the child. */
+    child = item->child;
+    while (child != NULL)
+    {
+        newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
+        if (!newchild)
+        {
+            goto fail;
+        }
+        if (next != NULL)
+        {
+            /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+            next->next = newchild;
+            newchild->prev = next;
+            next = newchild;
+        }
+        else
+        {
+            /* Set newitem->child and move to it */
+            newitem->child = newchild;
+            next = newchild;
+        }
+        child = child->next;
+    }
+
+    return newitem;
+
+fail:
+    if (newitem != NULL)
+    {
+        cJSON_Delete(newitem);
+    }
+
+    return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+    *input += static_strlen("//");
+
+    for (; (*input)[0] != '\0'; ++(*input))
+    {
+        if ((*input)[0] == '\n') {
+            *input += static_strlen("\n");
+            return;
+        }
+    }
+}
+
+static void skip_multiline_comment(char **input)
+{
+    *input += static_strlen("/*");
+
+    for (; (*input)[0] != '\0'; ++(*input))
+    {
+        if (((*input)[0] == '*') && ((*input)[1] == '/'))
+        {
+            *input += static_strlen("*/");
+            return;
+        }
+    }
+}
+
+static void minify_string(char **input, char **output) {
+    (*output)[0] = (*input)[0];
+    *input += static_strlen("\"");
+    *output += static_strlen("\"");
+
+
+    for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+        (*output)[0] = (*input)[0];
+
+        if ((*input)[0] == '\"') {
+            (*output)[0] = '\"';
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+            return;
+        } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+            (*output)[1] = (*input)[1];
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+        }
+    }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+    char *into = json;
+
+    if (json == NULL)
+    {
+        return;
+    }
+
+    while (json[0] != '\0')
+    {
+        switch (json[0])
+        {
+            case ' ':
+            case '\t':
+            case '\r':
+            case '\n':
+                json++;
+                break;
+
+            case '/':
+                if (json[1] == '/')
+                {
+                    skip_oneline_comment(&json);
+                }
+                else if (json[1] == '*')
+                {
+                    skip_multiline_comment(&json);
+                } else {
+                    json++;
+                }
+                break;
+
+            case '\"':
+                minify_string(&json, (char**)&into);
+                break;
+
+            default:
+                into[0] = json[0];
+                json++;
+                into++;
+        }
+    }
+
+    /* and null-terminate. */
+    *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
+    {
+        return false;
+    }
+
+    /* check if type is valid */
+    switch (a->type & 0xFF)
+    {
+        case cJSON_False:
+        case cJSON_True:
+        case cJSON_NULL:
+        case cJSON_Number:
+        case cJSON_String:
+        case cJSON_Raw:
+        case cJSON_Array:
+        case cJSON_Object:
+            break;
+
+        default:
+            return false;
+    }
+
+    /* identical objects are equal */
+    if (a == b)
+    {
+        return true;
+    }
+
+    switch (a->type & 0xFF)
+    {
+        /* in these cases and equal type is enough */
+        case cJSON_False:
+        case cJSON_True:
+        case cJSON_NULL:
+            return true;
+
+        case cJSON_Number:
+            if (a->valuedouble == b->valuedouble)
+            {
+                return true;
+            }
+            return false;
+
+        case cJSON_String:
+        case cJSON_Raw:
+            if ((a->valuestring == NULL) || (b->valuestring == NULL))
+            {
+                return false;
+            }
+            if (strcmp(a->valuestring, b->valuestring) == 0)
+            {
+                return true;
+            }
+
+            return false;
+
+        case cJSON_Array:
+        {
+            cJSON *a_element = a->child;
+            cJSON *b_element = b->child;
+
+            for (; (a_element != NULL) && (b_element != NULL);)
+            {
+                if (!cJSON_Compare(a_element, b_element, case_sensitive))
+                {
+                    return false;
+                }
+
+                a_element = a_element->next;
+                b_element = b_element->next;
+            }
+
+            /* one of the arrays is longer than the other */
+            if (a_element != b_element) {
+                return false;
+            }
+
+            return true;
+        }
+
+        case cJSON_Object:
+        {
+            cJSON *a_element = NULL;
+            cJSON *b_element = NULL;
+            cJSON_ArrayForEach(a_element, a)
+            {
+                /* TODO This has O(n^2) runtime, which is horrible! */
+                b_element = get_object_item(b, a_element->string, case_sensitive);
+                if (b_element == NULL)
+                {
+                    return false;
+                }
+
+                if (!cJSON_Compare(a_element, b_element, case_sensitive))
+                {
+                    return false;
+                }
+            }
+
+            /* doing this twice, once on a and b to prevent true comparison if a subset of b
+             * TODO: Do this the proper way, this is just a fix for now */
+            cJSON_ArrayForEach(b_element, b)
+            {
+                a_element = get_object_item(a, b_element->string, case_sensitive);
+                if (a_element == NULL)
+                {
+                    return false;
+                }
+
+                if (!cJSON_Compare(b_element, a_element, case_sensitive))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        default:
+            return false;
+    }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+    return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+    global_hooks.deallocate(object);
+}

+ 285 - 0
SATService/cJson/cJSON.h

@@ -0,0 +1,285 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type)   type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 12
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False  (1 << 0)
+#define cJSON_True   (1 << 1)
+#define cJSON_NULL   (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array  (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw    (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+    struct cJSON *next;
+    struct cJSON *prev;
+    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+    struct cJSON *child;
+
+    /* The type of the item, as above. */
+    int type;
+
+    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
+    char *valuestring;
+    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+    int valueint;
+    /* The item's number, if type==cJSON_Number */
+    double valuedouble;
+
+    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+    char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+      /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+      void *(CJSON_CDECL *malloc_fn)(size_t sz);
+      void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check if the item is a string and return its valuestring */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/arrray that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detatch items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+need to be released. With recurse!=0, it will duplicate any children connected to the item.
+The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 1457 - 0
SATService/cJson/cJSON_Utils.c

@@ -0,0 +1,1457 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUCC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUCC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON_Utils.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+static unsigned char* cJSONUtils_strdup(const unsigned char* const string)
+{
+    size_t length = 0;
+    unsigned char *copy = NULL;
+
+    length = strlen((const char*)string) + sizeof("");
+    copy = (unsigned char*) cJSON_malloc(length);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    memcpy(copy, string, length);
+
+    return copy;
+}
+
+/* string comparison which doesn't consider NULL pointers equal */
+static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive)
+{
+    if ((string1 == NULL) || (string2 == NULL))
+    {
+        return 1;
+    }
+
+    if (string1 == string2)
+    {
+        return 0;
+    }
+
+    if (case_sensitive)
+    {
+        return strcmp((const char*)string1, (const char*)string2);
+    }
+
+    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+    {
+        if (*string1 == '\0')
+        {
+            return 0;
+        }
+    }
+
+    return tolower(*string1) - tolower(*string2);
+}
+
+/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
+static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive)
+{
+    if ((name == NULL) || (pointer == NULL))
+    {
+        return false;
+    }
+
+    for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */
+    {
+        if (*pointer == '~')
+        {
+            /* check for escaped '~' (~0) and '/' (~1) */
+            if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/')))
+            {
+                /* invalid escape sequence or wrong character in *name */
+                return false;
+            }
+            else
+            {
+                pointer++;
+            }
+        }
+        else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer)))
+        {
+            return false;
+        }
+    }
+    if (((*pointer != 0) && (*pointer != '/')) != (*name != 0))
+    {
+        /* one string has ended, the other not */
+        return false;;
+    }
+
+    return true;
+}
+
+/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */
+static size_t pointer_encoded_length(const unsigned char *string)
+{
+    size_t length;
+    for (length = 0; *string != '\0'; (void)string++, length++)
+    {
+        /* character needs to be escaped? */
+        if ((*string == '~') || (*string == '/'))
+        {
+            length++;
+        }
+    }
+
+    return length;
+}
+
+/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */
+static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source)
+{
+    for (; source[0] != '\0'; (void)source++, destination++)
+    {
+        if (source[0] == '/')
+        {
+            destination[1] = '1';
+            destination++;
+        }
+        else if (source[0] == '~')
+        {
+            destination[0] = '~';
+            destination[1] = '1';
+            destination++;
+        }
+        else
+        {
+            destination[0] = source[0];
+        }
+    }
+
+    destination[0] = '\0';
+}
+
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target)
+{
+    size_t child_index = 0;
+    cJSON *current_child = 0;
+
+    if ((object == NULL) || (target == NULL))
+    {
+        return NULL;
+    }
+
+    if (object == target)
+    {
+        /* found */
+        return (char*)cJSONUtils_strdup((const unsigned char*)"");
+    }
+
+    /* recursively search all children of the object or array */
+    for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++)
+    {
+        unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target);
+        /* found the target? */
+        if (target_pointer != NULL)
+        {
+            if (cJSON_IsArray(object))
+            {
+                /* reserve enough memory for a 64 bit integer + '/' and '\0' */
+                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/"));
+                /* check if conversion to unsigned long is valid
+                 * This should be eliminated at compile time by dead code elimination
+                 * if size_t is an alias of unsigned long, or if it is bigger */
+                if (child_index > ULONG_MAX)
+                {
+                    cJSON_free(target_pointer);
+                    return NULL;
+                }
+                sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */
+                cJSON_free(target_pointer);
+
+                return (char*)full_pointer;
+            }
+
+            if (cJSON_IsObject(object))
+            {
+                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2);
+                full_pointer[0] = '/';
+                encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string);
+                strcat((char*)full_pointer, (char*)target_pointer);
+                cJSON_free(target_pointer);
+
+                return (char*)full_pointer;
+            }
+
+            /* reached leaf of the tree, found nothing */
+            cJSON_free(target_pointer);
+            return NULL;
+        }
+    }
+
+    /* not found */
+    return NULL;
+}
+
+/* non broken version of cJSON_GetArrayItem */
+static cJSON *get_array_item(const cJSON *array, size_t item)
+{
+    cJSON *child = array ? array->child : NULL;
+    while ((child != NULL) && (item > 0))
+    {
+        item--;
+        child = child->next;
+    }
+
+    return child;
+}
+
+static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index)
+{
+    size_t parsed_index = 0;
+    size_t position = 0;
+
+    if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/')))
+    {
+        /* leading zeroes are not permitted */
+        return 0;
+    }
+
+    for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)
+    {
+        parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');
+
+    }
+
+    if ((pointer[position] != '\0') && (pointer[position] != '/'))
+    {
+        return 0;
+    }
+
+    *index = parsed_index;
+
+    return 1;
+}
+
+static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive)
+{
+    cJSON *current_element = object;
+
+    if (pointer == NULL)
+    {
+        return NULL;
+    }
+
+    /* follow path of the pointer */
+    while ((pointer[0] == '/') && (current_element != NULL))
+    {
+        pointer++;
+        if (cJSON_IsArray(current_element))
+        {
+            size_t index = 0;
+            if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
+            {
+                return NULL;
+            }
+
+            current_element = get_array_item(current_element, index);
+        }
+        else if (cJSON_IsObject(current_element))
+        {
+            current_element = current_element->child;
+            /* GetObjectItem. */
+            while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive))
+            {
+                current_element = current_element->next;
+            }
+        }
+        else
+        {
+            return NULL;
+        }
+
+        /* skip to the next path token or end of string */
+        while ((pointer[0] != '\0') && (pointer[0] != '/'))
+        {
+            pointer++;
+        }
+    }
+
+    return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer)
+{
+    return get_item_from_pointer(object, pointer, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer)
+{
+    return get_item_from_pointer(object, pointer, true);
+}
+
+/* JSON Patch implementation. */
+static void decode_pointer_inplace(unsigned char *string)
+{
+    unsigned char *decoded_string = string;
+
+    if (string == NULL) {
+        return;
+    }
+
+    for (; *string; (void)decoded_string++, string++)
+    {
+        if (string[0] == '~')
+        {
+            if (string[1] == '0')
+            {
+                decoded_string[0] = '~';
+            }
+            else if (string[1] == '1')
+            {
+                decoded_string[1] = '/';
+            }
+            else
+            {
+                /* invalid escape sequence */
+                return;
+            }
+
+            string++;
+        }
+    }
+
+    decoded_string[0] = '\0';
+}
+
+/* non-broken cJSON_DetachItemFromArray */
+static cJSON *detach_item_from_array(cJSON *array, size_t which)
+{
+    cJSON *c = array->child;
+    while (c && (which > 0))
+    {
+        c = c->next;
+        which--;
+    }
+    if (!c)
+    {
+        /* item doesn't exist */
+        return NULL;
+    }
+    if (c->prev)
+    {
+        /* not the first element */
+        c->prev->next = c->next;
+    }
+    if (c->next)
+    {
+        c->next->prev = c->prev;
+    }
+    if (c==array->child)
+    {
+        array->child = c->next;
+    }
+    /* make sure the detached item doesn't point anywhere anymore */
+    c->prev = c->next = NULL;
+
+    return c;
+}
+
+/* detach an item at the given path */
+static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive)
+{
+    unsigned char *parent_pointer = NULL;
+    unsigned char *child_pointer = NULL;
+    cJSON *parent = NULL;
+    cJSON *detached_item = NULL;
+
+    /* copy path and split it in parent and child */
+    parent_pointer = cJSONUtils_strdup(path);
+    if (parent_pointer == NULL) {
+        goto cleanup;
+    }
+
+    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */
+    if (child_pointer == NULL)
+    {
+        goto cleanup;
+    }
+    /* split strings */
+    child_pointer[0] = '\0';
+    child_pointer++;
+
+    parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
+    decode_pointer_inplace(child_pointer);
+
+    if (cJSON_IsArray(parent))
+    {
+        size_t index = 0;
+        if (!decode_array_index_from_pointer(child_pointer, &index))
+        {
+            goto cleanup;
+        }
+        detached_item = detach_item_from_array(parent, index);
+    }
+    else if (cJSON_IsObject(parent))
+    {
+        detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer);
+    }
+    else
+    {
+        /* Couldn't find object to remove child from. */
+        goto cleanup;
+    }
+
+cleanup:
+    if (parent_pointer != NULL)
+    {
+        cJSON_free(parent_pointer);
+    }
+
+    return detached_item;
+}
+
+/* sort lists using mergesort */
+static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive)
+{
+    cJSON *first = list;
+    cJSON *second = list;
+    cJSON *current_item = list;
+    cJSON *result = list;
+    cJSON *result_tail = NULL;
+
+    if ((list == NULL) || (list->next == NULL))
+    {
+        /* One entry is sorted already. */
+        return result;
+    }
+
+    while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0))
+    {
+        /* Test for list sorted. */
+        current_item = current_item->next;
+    }
+    if ((current_item == NULL) || (current_item->next == NULL))
+    {
+        /* Leave sorted lists unmodified. */
+        return result;
+    }
+
+    /* reset pointer to the beginning */
+    current_item = list;
+    while (current_item != NULL)
+    {
+        /* Walk two pointers to find the middle. */
+        second = second->next;
+        current_item = current_item->next;
+        /* advances current_item two steps at a time */
+        if (current_item != NULL)
+        {
+            current_item = current_item->next;
+        }
+    }
+    if ((second != NULL) && (second->prev != NULL))
+    {
+        /* Split the lists */
+        second->prev->next = NULL;
+        second->prev = NULL;
+    }
+
+    /* Recursively sort the sub-lists. */
+    first = sort_list(first, case_sensitive);
+    second = sort_list(second, case_sensitive);
+    result = NULL;
+
+    /* Merge the sub-lists */
+    while ((first != NULL) && (second != NULL))
+    {
+        cJSON *smaller = NULL;
+        if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, case_sensitive) < 0)
+        {
+            smaller = first;
+        }
+        else
+        {
+            smaller = second;
+        }
+
+        if (result == NULL)
+        {
+            /* start merged list with the smaller element */
+            result_tail = smaller;
+            result = smaller;
+        }
+        else
+        {
+            /* add smaller element to the list */
+            result_tail->next = smaller;
+            smaller->prev = result_tail;
+            result_tail = smaller;
+        }
+
+        if (first == smaller)
+        {
+            first = first->next;
+        }
+        else
+        {
+            second = second->next;
+        }
+    }
+
+    if (first != NULL)
+    {
+        /* Append rest of first list. */
+        if (result == NULL)
+        {
+            return first;
+        }
+        result_tail->next = first;
+        first->prev = result_tail;
+    }
+    if (second != NULL)
+    {
+        /* Append rest of second list */
+        if (result == NULL)
+        {
+            return second;
+        }
+        result_tail->next = second;
+        second->prev = result_tail;
+    }
+
+    return result;
+}
+
+static void sort_object(cJSON * const object, const cJSON_bool case_sensitive)
+{
+    if (object == NULL)
+    {
+        return;
+    }
+    object->child = sort_list(object->child, case_sensitive);
+}
+
+static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive)
+{
+    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
+    {
+        /* mismatched type. */
+        return false;
+    }
+    switch (a->type & 0xFF)
+    {
+        case cJSON_Number:
+            /* numeric mismatch. */
+            if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+
+        case cJSON_String:
+            /* string mismatch. */
+            if (strcmp(a->valuestring, b->valuestring) != 0)
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+
+        case cJSON_Array:
+            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
+            {
+                cJSON_bool identical = compare_json(a, b, case_sensitive);
+                if (!identical)
+                {
+                    return false;
+                }
+            }
+
+            /* array size mismatch? (one of both children is not NULL) */
+            if ((a != NULL) || (b != NULL))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+
+        case cJSON_Object:
+            sort_object(a, case_sensitive);
+            sort_object(b, case_sensitive);
+            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
+            {
+                cJSON_bool identical = false;
+                /* compare object keys */
+                if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive))
+                {
+                    /* missing member */
+                    return false;
+                }
+                identical = compare_json(a, b, case_sensitive);
+                if (!identical)
+                {
+                    return false;
+                }
+            }
+
+            /* object length mismatch (one of both children is not null) */
+            if ((a != NULL) || (b != NULL))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+
+        default:
+            break;
+    }
+
+    /* null, true or false */
+    return true;
+}
+
+/* non broken version of cJSON_InsertItemInArray */
+static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
+{
+    cJSON *child = array->child;
+    while (child && (which > 0))
+    {
+        child = child->next;
+        which--;
+    }
+    if (which > 0)
+    {
+        /* item is after the end of the array */
+        return 0;
+    }
+    if (child == NULL)
+    {
+        cJSON_AddItemToArray(array, newitem);
+        return 1;
+    }
+
+    /* insert into the linked list */
+    newitem->next = child;
+    newitem->prev = child->prev;
+    child->prev = newitem;
+
+    /* was it at the beginning */
+    if (child == array->child)
+    {
+        array->child = newitem;
+    }
+    else
+    {
+        newitem->prev->next = newitem;
+    }
+
+    return 1;
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive)
+{
+    if (case_sensitive)
+    {
+        return cJSON_GetObjectItemCaseSensitive(object, name);
+    }
+
+    return cJSON_GetObjectItem(object, name);
+}
+
+enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };
+
+static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive)
+{
+    cJSON *operation = get_object_item(patch, "op", case_sensitive);
+    if (!cJSON_IsString(operation))
+    {
+        return INVALID;
+    }
+
+    if (strcmp(operation->valuestring, "add") == 0)
+    {
+        return ADD;
+    }
+
+    if (strcmp(operation->valuestring, "remove") == 0)
+    {
+        return REMOVE;
+    }
+
+    if (strcmp(operation->valuestring, "replace") == 0)
+    {
+        return REPLACE;
+    }
+
+    if (strcmp(operation->valuestring, "move") == 0)
+    {
+        return MOVE;
+    }
+
+    if (strcmp(operation->valuestring, "copy") == 0)
+    {
+        return COPY;
+    }
+
+    if (strcmp(operation->valuestring, "test") == 0)
+    {
+        return TEST;
+    }
+
+    return INVALID;
+}
+
+/* overwrite and existing item with another one and free resources on the way */
+static void overwrite_item(cJSON * const root, const cJSON replacement)
+{
+    if (root == NULL)
+    {
+        return;
+    }
+
+    if (root->string != NULL)
+    {
+        cJSON_free(root->string);
+    }
+    if (root->valuestring != NULL)
+    {
+        cJSON_free(root->valuestring);
+    }
+    if (root->child != NULL)
+    {
+        cJSON_Delete(root->child);
+    }
+
+    memcpy(root, &replacement, sizeof(cJSON));
+}
+
+static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive)
+{
+    cJSON *path = NULL;
+    cJSON *value = NULL;
+    cJSON *parent = NULL;
+    enum patch_operation opcode = INVALID;
+    unsigned char *parent_pointer = NULL;
+    unsigned char *child_pointer = NULL;
+    int status = 0;
+
+    path = get_object_item(patch, "path", case_sensitive);
+    if (!cJSON_IsString(path))
+    {
+        /* malformed patch. */
+        status = 2;
+        goto cleanup;
+    }
+
+    opcode = decode_patch_operation(patch, case_sensitive);
+    if (opcode == INVALID)
+    {
+        status = 3;
+        goto cleanup;
+    }
+    else if (opcode == TEST)
+    {
+        /* compare value: {...} with the given path */
+        status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive);
+        goto cleanup;
+    }
+
+    /* special case for replacing the root */
+    if (path->valuestring[0] == '\0')
+    {
+        if (opcode == REMOVE)
+        {
+            static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
+
+            overwrite_item(object, invalid);
+
+            status = 0;
+            goto cleanup;
+        }
+
+        if ((opcode == REPLACE) || (opcode == ADD))
+        {
+            value = get_object_item(patch, "value", case_sensitive);
+            if (value == NULL)
+            {
+                /* missing "value" for add/replace. */
+                status = 7;
+                goto cleanup;
+            }
+
+            value = cJSON_Duplicate(value, 1);
+            if (value == NULL)
+            {
+                /* out of memory for add/replace. */
+                status = 8;
+                goto cleanup;
+            }
+
+            overwrite_item(object, *value);
+
+            /* delete the duplicated value */
+            cJSON_free(value);
+            value = NULL;
+
+            /* the string "value" isn't needed */
+            if (object->string != NULL)
+            {
+                cJSON_free(object->string);
+                object->string = NULL;
+            }
+
+            status = 0;
+            goto cleanup;
+        }
+    }
+
+    if ((opcode == REMOVE) || (opcode == REPLACE))
+    {
+        /* Get rid of old. */
+        cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive);
+        if (old_item == NULL)
+        {
+            status = 13;
+            goto cleanup;
+        }
+        cJSON_Delete(old_item);
+        if (opcode == REMOVE)
+        {
+            /* For Remove, this job is done. */
+            status = 0;
+            goto cleanup;
+        }
+    }
+
+    /* Copy/Move uses "from". */
+    if ((opcode == MOVE) || (opcode == COPY))
+    {
+        cJSON *from = get_object_item(patch, "from", case_sensitive);
+        if (from == NULL)
+        {
+            /* missing "from" for copy/move. */
+            status = 4;
+            goto cleanup;
+        }
+
+        if (opcode == MOVE)
+        {
+            value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive);
+        }
+        if (opcode == COPY)
+        {
+            value = get_item_from_pointer(object, from->valuestring, case_sensitive);
+        }
+        if (value == NULL)
+        {
+            /* missing "from" for copy/move. */
+            status = 5;
+            goto cleanup;
+        }
+        if (opcode == COPY)
+        {
+            value = cJSON_Duplicate(value, 1);
+        }
+        if (value == NULL)
+        {
+            /* out of memory for copy/move. */
+            status = 6;
+            goto cleanup;
+        }
+    }
+    else /* Add/Replace uses "value". */
+    {
+        value = get_object_item(patch, "value", case_sensitive);
+        if (value == NULL)
+        {
+            /* missing "value" for add/replace. */
+            status = 7;
+            goto cleanup;
+        }
+        value = cJSON_Duplicate(value, 1);
+        if (value == NULL)
+        {
+            /* out of memory for add/replace. */
+            status = 8;
+            goto cleanup;
+        }
+    }
+
+    /* Now, just add "value" to "path". */
+
+    /* split pointer in parent and child */
+    parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
+    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
+    if (child_pointer != NULL)
+    {
+        child_pointer[0] = '\0';
+        child_pointer++;
+    }
+    parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
+    decode_pointer_inplace(child_pointer);
+
+    /* add, remove, replace, move, copy, test. */
+    if ((parent == NULL) || (child_pointer == NULL))
+    {
+        /* Couldn't find object to add to. */
+        status = 9;
+        goto cleanup;
+    }
+    else if (cJSON_IsArray(parent))
+    {
+        if (strcmp((char*)child_pointer, "-") == 0)
+        {
+            cJSON_AddItemToArray(parent, value);
+            value = NULL;
+        }
+        else
+        {
+            size_t index = 0;
+            if (!decode_array_index_from_pointer(child_pointer, &index))
+            {
+                status = 11;
+                goto cleanup;
+            }
+
+            if (!insert_item_in_array(parent, index, value))
+            {
+                status = 10;
+                goto cleanup;
+            }
+            value = NULL;
+        }
+    }
+    else if (cJSON_IsObject(parent))
+    {
+        if (case_sensitive)
+        {
+            cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
+        }
+        else
+        {
+            cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
+        }
+        cJSON_AddItemToObject(parent, (char*)child_pointer, value);
+        value = NULL;
+    }
+    else /* parent is not an object */
+    {
+        /* Couldn't find object to add to. */
+        status = 9;
+        goto cleanup;
+    }
+
+cleanup:
+    if (value != NULL)
+    {
+        cJSON_Delete(value);
+    }
+    if (parent_pointer != NULL)
+    {
+        cJSON_free(parent_pointer);
+    }
+
+    return status;
+}
+
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
+{
+    const cJSON *current_patch = NULL;
+    int status = 0;
+
+    if (!cJSON_IsArray(patches))
+    {
+        /* malformed patches. */
+        return 1;
+    }
+
+    if (patches != NULL)
+    {
+        current_patch = patches->child;
+    }
+
+    while (current_patch != NULL)
+    {
+        status = apply_patch(object, current_patch, false);
+        if (status != 0)
+        {
+            return status;
+        }
+        current_patch = current_patch->next;
+    }
+
+    return 0;
+}
+
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)
+{
+    const cJSON *current_patch = NULL;
+    int status = 0;
+
+    if (!cJSON_IsArray(patches))
+    {
+        /* malformed patches. */
+        return 1;
+    }
+
+    if (patches != NULL)
+    {
+        current_patch = patches->child;
+    }
+
+    while (current_patch != NULL)
+    {
+        status = apply_patch(object, current_patch, true);
+        if (status != 0)
+        {
+            return status;
+        }
+        current_patch = current_patch->next;
+    }
+
+    return 0;
+}
+
+static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)
+{
+    cJSON *patch = NULL;
+
+    if ((patches == NULL) || (operation == NULL) || (path == NULL))
+    {
+        return;
+    }
+
+    patch = cJSON_CreateObject();
+    if (patch == NULL)
+    {
+        return;
+    }
+    cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));
+
+    if (suffix == NULL)
+    {
+        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
+    }
+    else
+    {
+        size_t suffix_length = pointer_encoded_length(suffix);
+        size_t path_length = strlen((const char*)path);
+        unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));
+
+        sprintf((char*)full_path, "%s/", (const char*)path);
+        encode_string_as_pointer(full_path + path_length + 1, suffix);
+
+        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
+        cJSON_free(full_path);
+    }
+
+    if (value != NULL)
+    {
+        cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
+    }
+    cJSON_AddItemToArray(patches, patch);
+}
+
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
+{
+    compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
+}
+
+static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
+{
+    if ((from == NULL) || (to == NULL))
+    {
+        return;
+    }
+
+    if ((from->type & 0xFF) != (to->type & 0xFF))
+    {
+        compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
+        return;
+    }
+
+    switch (from->type & 0xFF)
+    {
+        case cJSON_Number:
+            if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
+            {
+                compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
+            }
+            return;
+
+        case cJSON_String:
+            if (strcmp(from->valuestring, to->valuestring) != 0)
+            {
+                compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
+            }
+            return;
+
+        case cJSON_Array:
+        {
+            size_t index = 0;
+            cJSON *from_child = from->child;
+            cJSON *to_child = to->child;
+            unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */
+
+            /* generate patches for all array elements that exist in both "from" and "to" */
+            for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
+            {
+                /* check if conversion to unsigned long is valid
+                 * This should be eliminated at compile time by dead code elimination
+                 * if size_t is an alias of unsigned long, or if it is bigger */
+                if (index > ULONG_MAX)
+                {
+                    cJSON_free(new_path);
+                    return;
+                }
+                sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
+                create_patches(patches, new_path, from_child, to_child, case_sensitive);
+            }
+
+            /* remove leftover elements from 'from' that are not in 'to' */
+            for (; (from_child != NULL); (void)(from_child = from_child->next))
+            {
+                /* check if conversion to unsigned long is valid
+                 * This should be eliminated at compile time by dead code elimination
+                 * if size_t is an alias of unsigned long, or if it is bigger */
+                if (index > ULONG_MAX)
+                {
+                    cJSON_free(new_path);
+                    return;
+                }
+                sprintf((char*)new_path, "%lu", (unsigned long)index);
+                compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
+            }
+            /* add new elements in 'to' that were not in 'from' */
+            for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
+            {
+                compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
+            }
+            cJSON_free(new_path);
+            return;
+        }
+
+        case cJSON_Object:
+        {
+            cJSON *from_child = NULL;
+            cJSON *to_child = NULL;
+            sort_object(from, case_sensitive);
+            sort_object(to, case_sensitive);
+
+            from_child = from->child;
+            to_child = to->child;
+            /* for all object values in the object with more of them */
+            while ((from_child != NULL) || (to_child != NULL))
+            {
+                int diff;
+                if (from_child == NULL)
+                {
+                    diff = 1;
+                }
+                else if (to_child == NULL)
+                {
+                    diff = -1;
+                }
+                else
+                {
+                    diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive);
+                }
+
+                if (diff == 0)
+                {
+                    /* both object keys are the same */
+                    size_t path_length = strlen((const char*)path);
+                    size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
+                    unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));
+
+                    sprintf((char*)new_path, "%s/", path);
+                    encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
+
+                    /* create a patch for the element */
+                    create_patches(patches, new_path, from_child, to_child, case_sensitive);
+                    cJSON_free(new_path);
+
+                    from_child = from_child->next;
+                    to_child = to_child->next;
+                }
+                else if (diff < 0)
+                {
+                    /* object element doesn't exist in 'to' --> remove it */
+                    compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
+
+                    from_child = from_child->next;
+                }
+                else
+                {
+                    /* object element doesn't exist in 'from' --> add it */
+                    compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
+
+                    to_child = to_child->next;
+                }
+            }
+            return;
+        }
+
+        default:
+            break;
+    }
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
+{
+    cJSON *patches = NULL;
+
+    if ((from == NULL) || (to == NULL))
+    {
+        return NULL;
+    }
+
+    patches = cJSON_CreateArray();
+    create_patches(patches, (const unsigned char*)"", from, to, false);
+
+    return patches;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
+{
+    cJSON *patches = NULL;
+
+    if ((from == NULL) || (to == NULL))
+    {
+        return NULL;
+    }
+
+    patches = cJSON_CreateArray();
+    create_patches(patches, (const unsigned char*)"", from, to, true);
+
+    return patches;
+}
+
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
+{
+    sort_object(object, false);
+}
+
+CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object)
+{
+    sort_object(object, true);
+}
+
+static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)
+{
+    cJSON *patch_child = NULL;
+
+    if (!cJSON_IsObject(patch))
+    {
+        /* scalar value, array or NULL, just duplicate */
+        cJSON_Delete(target);
+        return cJSON_Duplicate(patch, 1);
+    }
+
+    if (!cJSON_IsObject(target))
+    {
+        cJSON_Delete(target);
+        target = cJSON_CreateObject();
+    }
+
+    patch_child = patch->child;
+    while (patch_child != NULL)
+    {
+        if (cJSON_IsNull(patch_child))
+        {
+            /* NULL is the indicator to remove a value, see RFC7396 */
+            if (case_sensitive)
+            {
+                cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string);
+            }
+            else
+            {
+                cJSON_DeleteItemFromObject(target, patch_child->string);
+            }
+        }
+        else
+        {
+            cJSON *replace_me = NULL;
+            cJSON *replacement = NULL;
+
+            if (case_sensitive)
+            {
+                replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string);
+            }
+            else
+            {
+                replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
+            }
+
+            replacement = merge_patch(replace_me, patch_child, case_sensitive);
+            if (replacement == NULL)
+            {
+                return NULL;
+            }
+
+            cJSON_AddItemToObject(target, patch_child->string, replacement);
+        }
+        patch_child = patch_child->next;
+    }
+    return target;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
+{
+    return merge_patch(target, patch, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)
+{
+    return merge_patch(target, patch, true);
+}
+
+static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
+{
+    cJSON *from_child = NULL;
+    cJSON *to_child = NULL;
+    cJSON *patch = NULL;
+    if (to == NULL)
+    {
+        /* patch to delete everything */
+        return cJSON_CreateNull();
+    }
+    if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
+    {
+        return cJSON_Duplicate(to, 1);
+    }
+
+    sort_object(from, case_sensitive);
+    sort_object(to, case_sensitive);
+
+    from_child = from->child;
+    to_child = to->child;
+    patch = cJSON_CreateObject();
+    while (from_child || to_child)
+    {
+        int diff;
+        if (from_child != NULL)
+        {
+            if (to_child != NULL)
+            {
+                diff = strcmp(from_child->string, to_child->string);
+            }
+            else
+            {
+                diff = -1;
+            }
+        }
+        else
+        {
+            diff = 1;
+        }
+
+        if (diff < 0)
+        {
+            /* from has a value that to doesn't have -> remove */
+            cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());
+
+            from_child = from_child->next;
+        }
+        else if (diff > 0)
+        {
+            /* to has a value that from doesn't have -> add to patch */
+            cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));
+
+            to_child = to_child->next;
+        }
+        else
+        {
+            /* object key exists in both objects */
+            if (!compare_json(from_child, to_child, case_sensitive))
+            {
+                /* not identical --> generate a patch */
+                cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
+            }
+
+            /* next key in the object */
+            from_child = from_child->next;
+            to_child = to_child->next;
+        }
+    }
+    if (patch->child == NULL)
+    {
+        /* no patch generated */
+        cJSON_Delete(patch);
+        return NULL;
+    }
+
+    return patch;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
+{
+    return generate_merge_patch(from, to, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)
+{
+    return generate_merge_patch(from, to, true);
+}

+ 88 - 0
SATService/cJson/cJSON_Utils.h

@@ -0,0 +1,88 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON_Utils__h
+#define cJSON_Utils__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "cJSON.h"
+
+/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
+
+/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
+/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
+/* Utility for generating patch array entries. */
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
+/* Returns 0 for success. */
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
+
+/*
+// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
+//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
+//{
+//    cJSON *modme = cJSON_Duplicate(*object, 1);
+//    int error = cJSONUtils_ApplyPatches(modme, patches);
+//    if (!error)
+//    {
+//        cJSON_Delete(*object);
+//        *object = modme;
+//    }
+//    else
+//    {
+//        cJSON_Delete(modme);
+//    }
+//
+//    return error;
+//}
+// Code not added to library since this strategy is a LOT slower.
+*/
+
+/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
+/* target will be modified by patch. return value is new ptr for target. */
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
+/* generates a patch to move from -> to */
+/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
+
+/* Given a root object and a target object, construct a pointer from one to the other. */
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
+
+/* Sorts the members of the object into alphabetical order. */
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
+CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 1491 - 0
SATService/filehelper/filehelper.cpp

@@ -0,0 +1,1491 @@
+#include "StdAfx.h"
+#include "filehelper.h"
+#include <shlwapi.h>
+
+// 排序和乱序使用stl;
+#include <cstdlib>
+#include <ctime>
+#include <algorithm>
+// _mkdir头文件;
+#include <direct.h>
+
+int myrandom(int i) { return std::rand() % i; }
+
+filehelper::filehelper(void)
+{
+	
+}
+
+filehelper::~filehelper(void)
+{
+
+}
+
+/************************************************************************/
+/*  函数:[2/21/2017 Jeff];
+/*  描述:;
+/*  参数:;
+/*  	[IN] :;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void filehelper::random(IN STR_VEC& vtContent, IN BOOL bSort /* = TRUE */)
+{
+	if (bSort)
+	{//排序;
+		std::sort(vtContent.begin(), vtContent.end());
+	}
+	else
+	{//乱序;
+		//设置随即数生成器的种子;
+		std::srand(unsigned(std::time(0)));
+		std::random_shuffle(vtContent.begin(), vtContent.end(), myrandom);
+	}
+}
+
+/************************************************************************/
+/*  函数:[2/21/2017 Jeff];
+/*  描述:;
+/*  参数:;
+/*  	[IN] :;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void filehelper::split(OUT vector<TString>& vtSplit, IN const TString str, IN const TString strSplit)
+{
+	if (str.size() == 0 || strSplit.size() == 0)
+		return;
+
+	INT nIndex = 0;
+	vtSplit.clear();
+	TString strtmp = str;
+	TString strtmp2;
+	do 
+	{
+		if (TString::npos != (nIndex = strtmp.find_first_of(strSplit)))
+		{
+			strtmp2 = strtmp.substr(0, nIndex);
+			if (strtmp2.size())vtSplit.push_back(strtmp2);
+			strtmp = strtmp.substr(nIndex + strSplit.size());
+		}
+	} while (strtmp.find_first_of(strSplit) != TString::npos);
+
+	if (strtmp.size())
+		vtSplit.push_back(strtmp);
+}
+
+/************************************************************************/
+/*  函数:[2/19/2017 Jeff];
+/*  描述:保留指定的扩展名文件;
+/*  参数:;
+/*  	[IN] keepExt:要保留的扩展名;
+/*  	[OUT] vtfiles:要提取保留扩展名的文件数组;
+/*  	[IN/OUT] :;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void filehelper::keepdownbyext(IN const TString &keepExt, IN STR_VEC &vtfiles)
+{
+	if (keepExt.find(_T("*.*")) != TString::npos) return;
+	if (vtfiles.size() == 0) return;
+
+	// 获取复制扩展名;
+	int nIndex = 0;
+	TString strtmp;
+	TString strRemainExt(keepExt);
+	strRemainExt.append(_T("|"));
+	STR_VEC vtRemainExt;
+
+	// 将所有扩展名解析到数组里;
+	do
+	{
+		nIndex = strRemainExt.find(_T('|'));
+		if (nIndex != TString::npos)
+		{
+			strtmp = strRemainExt.substr(0, nIndex);
+			strRemainExt = strRemainExt.substr(nIndex + 1);
+
+			if (strtmp.compare(_T("*.*")))
+				vtRemainExt.push_back(strtmp);
+		}
+	} while (strRemainExt.find(_T('|')) != TString::npos);
+
+	STR_VEC vtresult;
+	// 过滤非复制扩展名的文件,同时去掉缩略图,保留指定复制扩展名的文件;
+	for (STR_VEC::iterator itExt = vtRemainExt.begin(); itExt != vtRemainExt.end(); itExt++)
+	{
+		for (STR_VEC::iterator it = vtfiles.begin(); it != vtfiles.end();)
+		{
+			if (match(itExt->substr(1), it->c_str())) 
+			{
+				vtresult.push_back(*it);
+				it = vtfiles.erase(it);
+			}
+			else
+				it++;
+		}
+	}
+
+	vtfiles.clear();
+	for ( STR_VEC::iterator it = vtresult.begin(); it != vtresult.end(); it++ )
+	{
+		vtfiles.push_back(*it);
+	}
+}
+
+void filehelper::keepdownbyext(IN const TString &keepExt, IN const STR_VEC &vtfiles, OUT STR_VEC &vtresult)
+{
+	if (keepExt.find(_T("*.*")) != TString::npos) return;
+	if (vtfiles.size() == 0) return;
+
+	// 获取复制扩展名;
+	int nIndex = 0;
+	TString strtmp;
+	TString strRemainExt(keepExt);
+	strRemainExt.append(_T("|"));
+	STR_VEC vtRemainExt;
+
+	// 将所有扩展名解析到数组里;
+	do
+	{
+		nIndex = strRemainExt.find(_T('|'));
+		if (nIndex != TString::npos)
+		{
+			strtmp = strRemainExt.substr(0, nIndex);
+			strRemainExt = strRemainExt.substr(nIndex + 1);
+
+			if (strtmp.compare(_T("*.*")))
+				vtRemainExt.push_back(strtmp);
+		}
+	} while (strRemainExt.find(_T('|')) != TString::npos);
+
+#if 0
+	// 过滤非复制扩展名的文件,同时去掉缩略图,保留指定复制扩展名的文件;
+	for (STR_VEC::iterator itExt = vtRemainExt.begin(); itExt != vtRemainExt.end(); itExt++)
+	{
+		for (STR_VEC::const_iterator it = vtfiles.begin(); it != vtfiles.end(); it++)
+		{
+			if (match(itExt->substr(1), it->c_str())) 
+			{
+				vtresult.push_back(*it);
+			}
+		}
+	}
+#else
+	STR_VEC vt_files;
+	for (STR_VEC::const_iterator it = vtfiles.begin(); it != vtfiles.end(); it++ )
+	{// 不能直接用vtresult来复制,因为如果vtresult不为空的情况下,会有异常出现;
+		vt_files.push_back(*it);
+	}
+
+	for (STR_VEC::iterator itExt = vtRemainExt.begin(); itExt != vtRemainExt.end(); itExt++)
+	{
+		for (STR_VEC::iterator it = vt_files.begin(); it != vt_files.end(); )
+		{
+			if (match(itExt->substr(1), it->c_str()))
+			{
+				vtresult.push_back(*it);
+				it = vt_files.erase(it);
+			}
+			else
+				it++;
+		}
+	}
+#endif
+}
+
+/************************************************************************/
+/*  函数:keepoutext[2/19/2017 Jeff];
+/*  描述:移除指定扩展名的文件;
+/*  参数:;
+/*  	[IN] :;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void filehelper::keepoutbyext(IN const TString &removeext, IN STR_VEC &vtfiles)
+{
+	if (removeext.find(_T("*.*")) != TString::npos) return;
+	if (vtfiles.size() == 0) return;
+
+	// 获取复制扩展名;
+	int nIndex = 0;
+	TString strtmp;
+	TString strRemoveExt(removeext);
+	strRemoveExt.append(_T("|"));
+	STR_VEC vtRemoveExt;
+
+	// 将所有扩展名解析到数组里;
+	do
+	{
+		nIndex = strRemoveExt.find(_T('|'));
+		if (nIndex != TString::npos)
+		{
+			strtmp = strRemoveExt.substr(0, nIndex);
+			strRemoveExt = strRemoveExt.substr(nIndex + 1);
+
+			if (strtmp.compare(_T("*.*")))
+				vtRemoveExt.push_back(strtmp);
+		}
+	} while (strRemoveExt.find(_T('|')) != TString::npos);
+
+	// 过滤非复制扩展名的文件,同时去掉缩略图,保留指定复制扩展名的文件;
+	for (STR_VEC::iterator itExt = vtRemoveExt.begin(); itExt != vtRemoveExt.end(); itExt++)
+	{
+		for (STR_VEC::iterator it = vtfiles.begin(); it != vtfiles.end();)
+		{
+			if (match(itExt->substr(1), it->c_str())) 
+			{
+				it = vtfiles.erase(it);
+			}
+			else
+				it++;
+		}
+	}
+}
+
+void filehelper::keepoutbyext(IN const TString &removeext, IN const STR_VEC &vtfiles, OUT STR_VEC &vtresult)
+{
+	if (removeext.find(_T("*.*")) != TString::npos) return;
+	if (vtfiles.size() == 0) return;
+
+	// 获取复制扩展名;
+	int nIndex = 0;
+	TString strtmp;
+	TString strRemoveExt(removeext);
+	strRemoveExt.append(_T("|"));
+	STR_VEC vtRemoveExt;
+
+	// 将所有扩展名解析到数组里;
+	do
+	{
+		nIndex = strRemoveExt.find(_T('|'));
+		if (nIndex != TString::npos)
+		{
+			strtmp = strRemoveExt.substr(0, nIndex);
+			strRemoveExt = strRemoveExt.substr(nIndex + 1);
+
+			if (strtmp.compare(_T("*.*")))
+				vtRemoveExt.push_back(strtmp);
+		}
+	} while (strRemoveExt.find(_T('|')) != TString::npos);
+
+	// 复制到新容器中;
+	STR_VEC vt_files;
+	for (STR_VEC::const_iterator it = vtfiles.begin(); it != vtfiles.end(); it++ )
+	{// 不能直接用vtresult来复制,因为如果vtresult不为空的情况下,会有异常出现;
+		vt_files.push_back(*it);
+	}
+
+	// 过滤非复制扩展名的文件,同时去掉缩略图,保留指定复制扩展名的文件;
+	for (STR_VEC::iterator itExt = vtRemoveExt.begin(); itExt != vtRemoveExt.end(); itExt++)
+	{
+		for (STR_VEC::iterator it = vt_files.begin(); it != vt_files.end();)
+		{
+			if (match(itExt->substr(1), it->c_str()))
+			{
+				vtresult.push_back(*it);
+				it = vt_files.erase(it);
+			}
+			else
+				it++;
+		}
+	}
+}
+
+void filehelper::keepdowbyname(IN const STR_VEC &vtnames)
+{
+	if ( m_pvtfiles == NULL || m_pvtnames == &vtnames )
+		return;
+
+	INT nIndex = 0;
+	STR_VEC vt_result_files;
+	if ( m_pvtnames == NULL)
+	{
+		STR_VEC::const_iterator it_name = vtnames.begin();
+		for ( ; it_name != vtnames.end(); it_name++ )
+		{
+			STR_VEC::iterator it_file = m_pvtfiles->begin();
+			for ( ; it_file != m_pvtfiles->end(); )
+			{
+				nIndex = it_file->find_last_of(_T('\\'));
+				if (TString::npos != nIndex)
+				{
+					if (_tcsicmp(it_file->substr(nIndex + 1).c_str(), it_name->c_str()) == 0)
+					{
+						vt_result_files.push_back(*it_file);
+						it_file = m_pvtfiles->erase(it_file);
+						continue;
+					}
+				}
+				it_file++;
+			}
+		}
+
+		m_pvtfiles->clear();
+		for ( STR_VEC::iterator it = vt_result_files.begin(); it != vt_result_files.end(); it++ )
+		{
+			m_pvtfiles->push_back(*it);
+		}
+	}
+	else 
+	{
+		if ( m_pvtnames->size() != m_pvtfiles->size() )
+			return;
+
+		STR_VEC::iterator it_name;
+		STR_VEC::iterator it_file;
+		STR_VEC vt_result_names;
+		for ( STR_VEC::const_iterator it = vtnames.begin(); it != vtnames.end(); it++ )
+		{
+			it_name = m_pvtnames->begin();
+			it_file = m_pvtfiles->begin();
+			for ( ; it_file != m_pvtfiles->end(), it_name != m_pvtnames->end();)
+			{
+				if ( _tcsicmp(it_name->c_str(), it->c_str()) == 0 )
+				{
+					vt_result_files.push_back(*it_file);
+					vt_result_names.push_back(*it_name);
+					it_file = m_pvtfiles->erase(it_file);
+					it_name = m_pvtnames->erase(it_name);
+					continue;
+				}
+
+				it_file++;
+				it_name++;
+			}
+		}
+
+		m_pvtfiles->clear();
+		m_pvtnames->clear();
+		it_name = vt_result_names.begin();
+		it_file = vt_result_files.begin();
+		for ( ; it_file != vt_result_files.end(), it_name != vt_result_names.end(); it_file++, it_name++ )
+		{
+			m_pvtfiles->push_back(*it_file);
+			m_pvtnames->push_back(*it_name);
+		}
+	}
+}
+
+void filehelper::keepdowbyname(IN const STR_VEC &vtnames, IN STR_VEC &vtfiles)
+{
+	if ( vtnames.size() == 0 )
+		return;
+
+	INT nIndex = 0;
+	STR_VEC vt_result_files;
+	STR_VEC::const_iterator it_name = vtnames.begin();
+	for ( ; it_name != vtnames.end(); it_name++ )
+	{
+		STR_VEC::iterator it_file = vtfiles.begin();
+		for ( ; it_file != vtfiles.end(); )
+		{
+			nIndex = it_file->find_last_of(_T('\\'));
+			if (TString::npos != nIndex)
+			{
+				if (_tcsicmp(it_file->substr(nIndex + 1).c_str(), it_name->c_str()) == 0)
+				{
+					vt_result_files.push_back(*it_file);
+					it_file = vtfiles.erase(it_file);
+					continue;
+				}
+			
+			}
+			it_file++;
+		}
+	}
+
+	vtfiles.clear();
+	for ( STR_VEC::iterator it = vt_result_files.begin(); it != vt_result_files.end(); it++ )
+	{
+		vtfiles.push_back(*it);
+	}
+}
+
+void filehelper::keepdowbyname(IN const STR_VEC &vtnames, IN const STR_VEC &vtfiles, OUT STR_VEC &vt_result)
+{
+	if ( vtnames.size() == 0 )
+		return;
+
+	INT nIndex = 0;
+	STR_VEC::const_iterator it_name = vtnames.begin();
+	for ( ; it_name != vtnames.end(); it_name++ )
+	{
+		STR_VEC::const_iterator it_file = vtfiles.begin();
+		for ( ; it_file != vtfiles.end(); it_file++)
+		{
+			nIndex = it_file->find_last_of(_T('\\'));
+			if (TString::npos != nIndex)
+			{
+				if (_tcsicmp(it_file->substr(nIndex + 1).c_str(), it_name->c_str()) == 0)
+				{
+					vt_result.push_back(*it_file);
+				}
+			}
+		}
+	}
+}
+
+void filehelper::keepdowbyname_noext(IN const STR_VEC &vtnames)
+{
+	if ( m_pvtfiles == NULL || m_pvtnames == &vtnames )
+		return;
+
+	INT nIndex = 0;
+	STR_VEC vt_result_files;
+	if ( m_pvtnames == NULL)
+	{
+		INT nIndex2 = 0;
+		STR_VEC::const_iterator it_name = vtnames.begin();
+		for ( ; it_name != vtnames.end(); it_name++ )
+		{
+			STR_VEC::iterator it_file = m_pvtfiles->begin();
+			for ( ; it_file != m_pvtfiles->end(); )
+			{
+				nIndex = it_file->find_last_of(_T('\\'));
+				nIndex2 = it_file->find_last_of(_T('.'));
+				if (TString::npos != nIndex && TString::npos != nIndex2 && nIndex > nIndex2 )
+				{
+					if ( _tcsicmp(it_file->substr(nIndex+1,nIndex2 - nIndex - 1).c_str(), it_name->c_str()) == 0)
+					{
+						vt_result_files.push_back(*it_file);
+						it_file = m_pvtfiles->erase(it_file);
+						continue;
+					}
+				}
+				it_file++;
+			}
+		}
+
+		m_pvtfiles->clear();
+		for ( STR_VEC::iterator it = vt_result_files.begin(); it != vt_result_files.end(); it++ )
+		{
+			m_pvtfiles->push_back(*it);
+		}
+	}
+	else 
+	{
+		if ( m_pvtnames->size() != m_pvtfiles->size() )
+			return;
+
+		STR_VEC::iterator it_name;
+		STR_VEC::iterator it_file;
+		STR_VEC vt_result_names;
+		for ( STR_VEC::const_iterator it = vtnames.begin(); it != vtnames.end(); it++ )
+		{
+			it_name = m_pvtnames->begin();
+			it_file = m_pvtfiles->begin();
+			for ( ; it_file != m_pvtfiles->end(), it_name != m_pvtnames->end();)
+			{
+				if ( TString::npos != ( nIndex = it_name->find_last_of(_T('.'))))
+				{
+					if ( _tcsicmp(it_name->substr(0, nIndex).c_str(), it->c_str()) == 0 )
+					{
+						vt_result_files.push_back(*it_file);
+						vt_result_names.push_back(*it_name);
+						it_file = m_pvtfiles->erase(it_file);
+						it_name = m_pvtnames->erase(it_name);
+						continue;
+					}
+				}
+				it_file++;
+				it_name++;
+			}
+		}
+
+		m_pvtfiles->clear();
+		m_pvtnames->clear();
+		it_name = vt_result_names.begin();
+		it_file = vt_result_files.begin();
+		for ( ; it_file != vt_result_files.end(), it_name != vt_result_names.end(); it_file++, it_name++ )
+		{
+			m_pvtfiles->push_back(*it_file);
+			m_pvtnames->push_back(*it_name);
+		}
+	}
+}
+
+void filehelper::keepdowbyname_noext(IN const STR_VEC &vtnames, IN STR_VEC &vtfiles)
+{
+	if ( vtnames.size() == 0 )
+		return;
+
+	INT nIndex = 0;
+	TString strName;
+	STR_VEC vt_result_files;
+	STR_VEC::const_iterator it_name = vtnames.begin();
+	for ( ; it_name != vtnames.end(); it_name++ )
+	{
+		STR_VEC::iterator it_file = vtfiles.begin();
+		for ( ; it_file != vtfiles.end(); )
+		{
+			nIndex = it_file->find_last_of(_T('\\'));
+			if (TString::npos != nIndex)
+			{
+				strName = it_file->substr(nIndex+1);
+				if ( TString::npos != ( nIndex = strName.find_last_of(_T('.'))))
+				{
+					if ( _tcsicmp(strName.substr(0,nIndex).c_str(), it_name->c_str()) == 0)
+					{
+						vt_result_files.push_back(*it_file);
+						it_file = vtfiles.erase(it_file);
+						continue;
+					}
+				}
+			}
+			it_file++;
+		}
+	}
+
+	vtfiles.clear();
+	for ( STR_VEC::iterator it = vt_result_files.begin(); it != vt_result_files.end(); it++ )
+	{
+		vtfiles.push_back(*it);
+	}
+}
+
+void filehelper::keepdowbyname_noext(IN const STR_VEC &vtnames, IN const STR_VEC &vtfiles, OUT STR_VEC &vt_result)
+{
+	if ( vtnames.size() == 0 )
+		return;
+
+	INT nIndex = 0;
+	TString strName;
+	STR_VEC::const_iterator it_name = vtnames.begin();
+	for ( ; it_name != vtnames.end(); it_name++ )
+	{
+		STR_VEC::const_iterator it_file = vtfiles.begin();
+		for ( ; it_file != vtfiles.end(); it_file++)
+		{
+			nIndex = it_file->find_last_of(_T('\\'));
+			if (TString::npos != nIndex)
+			{
+				strName = it_file->substr(nIndex+1);
+				if ( TString::npos != ( nIndex = strName.find_last_of(_T('.'))))
+				{
+					if ( _tcsicmp(strName.substr(0,nIndex).c_str(), it_name->c_str()) == 0)
+					{
+						vt_result.push_back(*it_file);
+					}
+				}
+			}
+		}
+	}
+}
+
+void filehelper::subgroupExt(IN const TString &ext1, IN STR_VEC &vt1, IN const TString &ext2, IN STR_VEC &vt2)
+{
+	// 		if (ext1.find(_T("*.*")) != TString::npos ) return;
+	// 		if ( !iscorrectext(ext1) || vt1.size() == 0 ) return;
+	// 
+	// 		// 获取复制扩展名;
+	// 		int nIndex = 0;
+	// 		TString strtmp;
+	// 		TString strExt1(ext1);
+	// 		strExt1.append(_T("|"));
+	// 		STR_VEC vtExt1;
+	// 
+	// 		// 将所有扩展名解析到数组里;
+	// 		do 
+	// 		{
+	// 			nIndex = strExt1.find(_T('|'));
+	// 			if ( nIndex != TString::npos )
+	// 			{
+	// 				strtmp = strExt1.substr(0,nIndex);
+	// 				strExt1 = strExt1.substr(nIndex+1);
+	// 
+	// 				if(strtmp.compare(_T("*.*")))
+	// 					vtExt1.push_back(strtmp);
+	// 			}
+	// 		} while ( strExt1.find(_T('|')) != TString::npos );
+}
+
+/************************************************************************/
+/*  函数:filterbyname[2/21/2017 Jeff];
+/*  描述:根据有后缀的文件名(如:001.jpg)来过虑文件;;
+/*  参数:;
+/*  	[IN] vtfiles:要被过滤的文件路径数组;
+/*  	[IN] vtnames:要过滤的文件名,每个文件名都有后缀,如:001.jpg;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void filehelper::filterbyname(IN STR_VEC &vtfiles, IN const STR_VEC &vtnames)
+{// 再按名称过滤;
+	INT nIndex = 0;
+	TString strName;
+	for ( STR_VEC::const_iterator it_name = vtnames.begin(); it_name != vtnames.end(); it_name++ )
+	{
+		for ( STR_VEC::iterator it_file = vtfiles.begin(); it_file != vtfiles.end(); )
+		{
+			nIndex = it_file->find_last_of(_T('\\'));
+			if (TString::npos != nIndex)
+			{
+				strName = it_file->substr(nIndex+1);
+				if ( _tcsicmp(strName.c_str(), it_name->c_str()) == 0 )
+				{
+					it_file = vtfiles.erase(it_file);
+					continue;
+				}				
+			}
+			it_file++;
+		}
+	}
+}
+
+void filehelper::filterbyname(IN const STR_VEC &vtnames)
+{
+	if ( m_pvtfiles == NULL || m_pvtnames == &vtnames )
+		return;
+
+	INT nIndex = 0;
+	TString strName;
+	if ( m_pvtnames == NULL)
+	{
+		STR_VEC::const_iterator it_name = vtnames.begin();
+		for ( ; it_name != vtnames.end(); it_name++ )
+		{
+			STR_VEC::iterator it_file = m_pvtfiles->begin();
+			for ( ; it_file != m_pvtfiles->end(); )
+			{
+				nIndex = it_file->find_last_of(_T('\\'));
+				if (TString::npos != nIndex)
+				{
+					strName = it_file->substr(nIndex+1);
+					if ( _tcsicmp(strName.c_str(), it_name->c_str()) == 0)
+					{
+						it_file = m_pvtfiles->erase(it_file);
+						continue;
+					}
+				}
+				it_file++;
+			}
+		}
+	}
+	else 
+	{
+		if ( m_pvtnames->size() != m_pvtfiles->size() )
+			return;
+		for ( STR_VEC::const_iterator it = vtnames.begin(); it != vtnames.end(); it++ )
+		{
+			STR_VEC::iterator it_name = m_pvtnames->begin();
+			STR_VEC::iterator it_file = m_pvtfiles->begin();
+			for ( ; it_file != m_pvtfiles->end(), it_name != m_pvtnames->end();)
+			{
+				if ( _tcsicmp(it_name->c_str(), it->c_str()) == 0 )
+				{
+					it_file = m_pvtfiles->erase(it_file);
+					it_name = m_pvtnames->erase(it_name);
+					continue;
+				}
+
+				it_file++;
+				it_name++;
+			}
+		}
+	}
+}
+
+// 根据无后缀的文件名(如:001)来过虑文件;
+void filehelper::filterbyname_noext(IN STR_VEC &vtfiles, IN const STR_VEC &vtnames)
+{// 再按名称过滤;
+	INT nIndex = 0;
+	INT nIndex2 = 0;
+	TString strName;
+	for ( STR_VEC::iterator it_file = vtfiles.begin(); it_file != vtfiles.end(); )
+	{
+		nIndex = it_file->find_last_of(_T('\\'));
+		nIndex2 = it_file->find_last_of(_T('.'));
+		for ( STR_VEC::const_iterator it_name = vtnames.begin(); it_name != vtnames.end(); it_name++ )
+		{
+			if (TString::npos != nIndex && TString::npos != nIndex2 && nIndex2 > nIndex)
+			{
+				if ( _tcsicmp(it_file->substr(nIndex+1,nIndex2-nIndex-1).c_str(), it_name->c_str()) == 0)
+				{
+					it_file = vtfiles.erase(it_file);
+					continue;
+				}
+			}
+			it_file++;
+		}
+	}
+}
+
+void filehelper::filterbyname_noext(IN const STR_VEC &vtnames)
+{
+	if ( m_pvtfiles == NULL || m_pvtnames == &vtnames )
+		return;
+
+	INT nIndex = 0;
+	TString strName;
+	if ( m_pvtnames == NULL )
+	{
+		INT nIndex2 = 0;
+		STR_VEC::iterator it_file = m_pvtfiles->begin();
+		for ( ; it_file != m_pvtfiles->end(); )
+		{
+			nIndex = it_file->find_last_of(_T('\\'));
+			nIndex2 = it_file->find_last_of(_T('.'));
+			for ( STR_VEC::const_iterator it_name = vtnames.begin(); it_name != vtnames.end(); it_name++ )
+			{
+				if (TString::npos != nIndex && TString::npos != nIndex2 && nIndex2 > nIndex)
+				{
+					if ( _tcsicmp(it_file->substr(nIndex+1,nIndex2-nIndex-1).c_str(), it_name->c_str()) == 0 )
+					{
+						it_file = m_pvtfiles->erase(it_file);
+						continue;
+					}
+				}
+				it_file++;
+			}
+		}
+	}
+	else
+	{
+		if (m_pvtnames->size() != m_pvtfiles->size())
+			return;
+
+		for ( STR_VEC::const_iterator it = vtnames.begin(); it != vtnames.end(); it++ )
+		{
+			STR_VEC::iterator it_name = m_pvtnames->begin();
+			STR_VEC::iterator it_file = m_pvtfiles->begin();
+			for ( ; it_file != m_pvtfiles->end(), it_name != m_pvtnames->end();)
+			{
+				if ( TString::npos != (nIndex = it_name->find_last_of(_T('.'))) )
+				{
+					if ( _tcsicmp(it_name->substr(0, nIndex).c_str(), it->c_str()) == 0 )
+					{
+						it_file = m_pvtfiles->erase(it_file);
+						it_name = m_pvtnames->erase(it_name);
+						continue;
+					}
+
+					it_file++;
+					it_name++;
+				}
+			}
+		}
+	}
+
+}
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::getall(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN OUT STR_VEC *pvtfiles, IN OUT STR_VEC *pvtnames, IN OUT STR_VEC *pvtfolders)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (!pvtfiles || !pvtnames || !pvtfolders ) return FALSE;
+
+	m_pvtfiles = pvtfiles;
+	m_pvtnames = pvtnames;
+	m_pvtfolders = pvtfolders;
+
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+
+	findall(lpfolder);
+	if ( _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtfiles);
+		keepdownbyext(lpfindext, *pvtnames);
+	}
+
+	return TRUE;
+}
+
+BOOL filehelper::getsubfolder(IN LPCTSTR lpfolder, IN STR_VEC *pvtfolders)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfolders == NULL) return FALSE;
+	m_pvtfolders = pvtfolders;
+
+	findsubfolder(lpfolder);
+
+	return TRUE;
+}
+
+BOOL filehelper::getallsubfolder(IN LPCTSTR lpfolder, IN STR_VEC *pvtfolders)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfolders == NULL) return FALSE;
+	m_pvtfolders = pvtfolders;
+
+	findallsubfolder(lpfolder);
+
+	return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::getfiles_findin_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtfiles)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfiles == NULL) return FALSE;
+	m_pvtfiles = pvtfiles;
+
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	findfiles_findin_subfolder(lpfolder);
+	if ( _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	return TRUE;
+}
+
+BOOL filehelper::getfiles_findout_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtfiles)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfiles == NULL) return FALSE;
+	m_pvtfiles = pvtfiles;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	findfiles_findout_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	return TRUE;
+}
+
+BOOL filehelper::getfiles_within_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtfiles, IN STR_VEC *pvtfolders)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfiles == NULL) return FALSE;
+	m_pvtfiles = pvtfiles;
+	m_pvtfolders = pvtfolders;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	findfiles_within_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::getfiles_bynames_findin_subfolder( IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC &vtnames, IN STR_VEC *pvtfiles )
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfiles == NULL) return FALSE;
+	m_pvtfiles = pvtfiles;
+
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	findfiles_findin_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	// 再按名称过滤;
+	filterbyname(*pvtfiles, vtnames);
+
+	return TRUE;
+}
+
+BOOL filehelper::getfiles_bynames_findout_subfolder( IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC &vtnames, IN OUT STR_VEC *pvtfiles )
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfiles == NULL) return FALSE;
+	m_pvtfiles = pvtfiles;
+
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+
+	findfiles_findout_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	// 再按名称过滤;
+	filterbyname(*pvtfiles, vtnames);
+
+	return TRUE;
+}
+
+BOOL filehelper::getfiles_bynames_within_subfolder( IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC &vtnames, IN STR_VEC *pvtfiles, IN STR_VEC *pvtfolders )
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfiles == NULL) return FALSE;
+	m_pvtfiles = pvtfiles;
+	m_pvtfolders = pvtfolders;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+
+	findfiles_within_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	// 再按名称过滤;
+	filterbyname(*pvtfiles, vtnames);
+
+	return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::getnames_findin_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtnames)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtnames == NULL) return FALSE;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	m_pvtnames = pvtnames;
+	findnames_findin_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *m_pvtnames);
+	}
+
+	return TRUE;
+}
+
+BOOL filehelper::getnames_findout_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtnames)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtnames == NULL) return FALSE;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	m_pvtnames = pvtnames;
+	findnames_findout_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *m_pvtnames);
+	}
+
+	return TRUE;
+}
+
+BOOL filehelper::getnames_within_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtnames, IN STR_VEC *pvtfolders)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtnames == NULL || pvtfolders == NULL ) 
+		return FALSE;
+
+	m_pvtfolders = pvtfolders;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	m_pvtnames = pvtnames;
+	m_pvtfolders = pvtfolders;
+	findnames_within_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *m_pvtnames);
+	}
+
+	return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::getfilesnames_findin_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtfiles, IN STR_VEC *pvtnames)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtnames || !pvtnames) return FALSE;
+	m_pvtfiles = pvtfiles;
+	m_pvtnames = pvtnames;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	findfilesnames_findin_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtnames);
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	return TRUE;
+}
+
+BOOL filehelper::getfilesnames_findout_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtfiles, IN STR_VEC *pvtnames)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtnames || !pvtnames) return FALSE;
+	m_pvtfiles = pvtfiles;
+	m_pvtnames = pvtnames;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	findfilesnames_findout_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtnames);
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	return TRUE;
+}
+
+BOOL filehelper::getfilesnames_within_subfolder(IN LPCTSTR lpfolder, IN LPCTSTR lpfindext, IN STR_VEC *pvtfiles, IN STR_VEC *pvtnames, IN STR_VEC *pvtfolders)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtnames || !pvtnames) return FALSE;
+	m_pvtfiles = pvtfiles;
+	m_pvtnames = pvtnames;
+	m_pvtfolders = pvtfolders;
+#if	USE_IMGEXT
+	// 判断扩展名有效性;
+	if (!iscorrectext(lpfindext)) return FALSE;
+#endif
+	findfilesnames_within_subfolder(lpfolder);
+	if (lpfindext && _tcsstr(lpfindext, _T("*.*")) == NULL )
+	{// 需要保留指定后缀;
+		keepdownbyext(lpfindext, *pvtnames);
+		keepdownbyext(lpfindext, *pvtfiles);
+	}
+
+	return TRUE;
+}
+
+#if 1
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::comparename_2file(IN LPCTSTR file1, IN LPCTSTR file2)
+{
+	if (!file1 || !file2) return FALSE;
+
+	if (!PathFileExists(file1) || !PathFileExists(file2))
+		return FALSE;
+
+	TString strfile1 = file1;
+	TString strfile2 = file2;
+
+	strfile1 = getfilename(strfile1);
+	strfile2 = getfilename(strfile2);
+
+	return !strfile1.compare(strfile2);
+}
+
+BOOL filehelper::comparename_findin_names(IN LPCTSTR name)
+{
+	if (!m_pvtnames || !name) return FALSE;
+	for (STR_VEC::iterator it = m_pvtnames->begin(); it != m_pvtnames->end(); it++)
+	{
+		if ( _tcsicmp(name, it->c_str()) == 0)
+		{
+			// 打印日志,或提示用户同名文件路径在哪;
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+BOOL filehelper::comparename_findin_names(IN LPCTSTR name, IN STR_VEC &vtnames)
+{
+	if (!name) return FALSE;
+	for (STR_VEC::iterator it = vtnames.begin(); it != vtnames.end(); it++)
+	{
+		if ( _tcsicmp(name, it->c_str()) == 0)
+		{
+			// 打印日志,或提示用户同名文件路径在哪;
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+BOOL filehelper::comparename_findin_files(IN LPCTSTR name, IN STR_VEC &vtfiles)
+{
+	if (!name) return FALSE;
+
+	int nIndex = 0;
+	for (STR_VEC::iterator it = vtfiles.begin(); it != vtfiles.end(); it++)
+	{
+		if ((nIndex = it->find_last_of(_T('\\'))) != TString::npos)
+		{
+			if (_tcsicmp(name, it->substr(nIndex+1).c_str()) == 0)
+			{
+				// 打印日志,或提示用户同名文件路径在哪;
+				return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+BOOL filehelper::comparename_findin_folder(IN LPCTSTR name, IN LPCTSTR folder, IN LPCTSTR findext, IN BOOL bsubfolder)
+{
+#if	USE_IMGEXT
+	if (!name || !iscorrectext(findext)) return FALSE;
+#else
+	if ( !name ) return FALSE;
+#endif
+	STR_VEC vtnames;
+	if (bsubfolder)
+	{
+		if (!getnames_findin_subfolder(folder, findext, &vtnames))
+			return FALSE;
+	}
+	else
+	{
+		if (!getnames_findout_subfolder(folder, findext, &vtnames))
+			return FALSE;
+	}
+
+	return comparename_findin_names(name, vtnames);
+}
+
+INT filehelper::comparenames_findin_names(IN STR_VEC vtSrcnames, IN STR_VEC vtDesnames, OUT STR_VEC *pvtIsonym /* = NULL */)
+{
+	if (vtSrcnames.size() == 0 || vtDesnames.size() == 0) return 0;
+
+	INT nCount = 0;
+	for (STR_VEC::iterator it1 = vtSrcnames.begin(); it1 != vtSrcnames.end(); it1++)
+	{
+		for (STR_VEC::iterator it2 = vtDesnames.begin(); it2 != vtDesnames.end(); it2++)
+		{
+			if (_tcsicmp(it1->c_str(), it2->c_str()) == 0)
+			{
+				nCount++;
+				if (pvtIsonym)
+					pvtIsonym->push_back(*it1);
+
+				break;
+			}
+		}
+	}
+
+	return nCount;
+}
+
+INT filehelper::comparenames_findin_files(IN STR_VEC vtnames, IN STR_VEC vtfiles, IN LPCTSTR findext, OUT STR_VEC *pvtIsonym /* = NULL */)
+{
+	if (vtnames.size() == 0 || vtfiles.size() == 0) return 0;
+#if	USE_IMGEXT
+	if (!iscorrectext(findext)) return 0;
+#endif
+	INT nCount = 0;
+	for (STR_VEC::iterator it = vtnames.begin(); it != vtnames.end(); it++)
+	{
+		if (comparename_findin_files(it->c_str(), vtfiles))
+		{
+			if (pvtIsonym)
+				pvtIsonym->push_back(*it);
+			nCount++;
+		}
+	}
+
+	return nCount;
+}
+
+INT filehelper::comparenames_findin_folder(IN STR_VEC vtnames, IN LPCTSTR folder, IN BOOL bsubfolder, IN LPCTSTR findext, OUT STR_VEC *pvtIsonym /* = NULL */)
+{
+	if (vtnames.size() == 0 || !PathFileExists(folder)) return 0;
+#if	USE_IMGEXT
+	if (!iscorrectext(findext)) return 0;
+#endif
+	STR_VEC vtnames2;
+	if (bsubfolder)
+	{
+		getnames_findin_subfolder(folder, findext, &vtnames2);
+	}
+	else
+	{
+		getnames_findout_subfolder(folder, findext, &vtnames2);
+	}
+
+	return comparenames_findin_names(vtnames, vtnames2, pvtIsonym);
+}
+
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::comparefile_findin_names(IN LPCTSTR file, IN STR_VEC& vtnames)
+{
+	if (vtnames.size() == 0) return FALSE;
+	if (!file || !PathFileExists(file)) return FALSE;
+
+	TString strfile = file;
+	TString name = getfilename(strfile);
+
+	return comparename_findin_names(name.c_str(), vtnames);
+}
+
+BOOL filehelper::comparefile_findin_files(IN LPCTSTR file, IN STR_VEC& vtfiles, IN LPCTSTR findext)
+{
+	if (!file || !PathFileExists(file)) return FALSE;
+#if	USE_IMGEXT
+	if (!iscorrectext(findext)) return FALSE;
+#endif
+	TString strfile = file;
+	TString name = getfilename(strfile);
+
+	// 过滤掉不需要的扩展名;
+	keepdownbyext(findext, vtfiles);
+
+	return comparename_findin_files(name.c_str(), vtfiles);
+}
+
+BOOL filehelper::comparefile_findin_folder(IN LPCTSTR file, IN LPCTSTR folder, IN BOOL bsubfolder, IN LPCTSTR findext)
+{
+	if (!file || !PathFileExists(file) || !folder || !PathFileExists(folder)) return FALSE;
+#if	USE_IMGEXT
+	if (!iscorrectext(findext)) return FALSE;
+#endif
+	TString strfile = file;
+	TString name = getfilename(strfile);
+
+	STR_VEC vtnames;
+	if (bsubfolder)
+	{
+		getnames_findin_subfolder(folder, findext, &vtnames);
+	}
+	else
+	{
+		getnames_findout_subfolder(folder, findext, &vtnames);
+	}
+
+	return comparename_findin_names(name.c_str(), vtnames);
+}
+
+INT filehelper::comparefiles_findin_files(IN STR_VEC &vtfiles1, IN STR_VEC &vtfiles2, IN LPCTSTR findext, OUT STR_VEC *pvtIsonym /* = NULL */)
+{
+	if (vtfiles1.size() == 0 || vtfiles2.size() == 0) return 0;
+#if	USE_IMGEXT
+	if (!iscorrectext(findext)) return 0;
+#endif
+	// 过滤掉不需要的扩展名;
+	keepdownbyext(findext, vtfiles1);
+
+	// 过滤掉不需要的扩展名;
+	keepdownbyext(findext, vtfiles2);
+
+	INT nCount = 0;
+	TString name;
+	for (STR_VEC::iterator it = vtfiles1.begin(); it != vtfiles1.end(); it++)
+	{
+		name = getfilename(*it);
+		if (comparename_findin_files(name.c_str(), vtfiles2))
+		{
+			if (pvtIsonym)
+				pvtIsonym->push_back(name);
+			nCount++;
+		}
+	}
+
+	return nCount;
+}
+
+INT filehelper::comparefiles_findin_folder(IN STR_VEC &vtfiles, IN LPCTSTR folder, IN BOOL bsubfolder, IN LPCTSTR findext, OUT STR_VEC *pvtIsonym /* = NULL */)
+{
+	if (vtfiles.size() == 0 || !folder || !PathFileExists(folder)) return 0;
+#if	USE_IMGEXT
+	if (!iscorrectext(findext)) return 0;
+#endif
+	STR_VEC vtnames;
+	if (bsubfolder)
+	{
+		getnames_findin_subfolder(folder, findext, &vtnames);
+	}
+	else
+	{
+		getnames_findout_subfolder(folder, findext, &vtnames);
+	}
+
+	return comparenames_findin_files(vtnames, vtfiles, findext, pvtIsonym);
+}
+
+INT filehelper::comparefolderself(IN LPCTSTR folder, IN LPCTSTR findext, OUT STR_VEC *pvtIsonym /* = NULL */)
+{
+	OutputDebugString(_T("\n"));
+#if	USE_IMGEXT
+	if (!folder || !PathFileExists(folder) || !iscorrectext(findext)) return 0;
+#else
+	if (!folder || !PathFileExists(folder) ) return 0;
+#endif
+
+	STR_VEC vtsubfolders;
+	vtsubfolders.push_back(folder);	// 包含当前目录;
+	getsubfolder(folder, &vtsubfolders);
+
+	int n = vtsubfolders.size();
+	STR_VEC *pvtfiles = new STR_VEC[n];
+
+	for (STR_VEC::iterator it = vtsubfolders.begin(); it != vtsubfolders.end(); it++)
+	{
+		getfiles_findin_subfolder(it->c_str(), findext, &pvtfiles[--n]);
+	}
+
+	STR_VEC vtnames;
+	n = vtsubfolders.size();
+	TString name;
+	for (int i = 0; i < n; i++)
+	{
+		for (STR_VEC::iterator it = pvtfiles[i].begin(); it != pvtfiles[i].end(); it++)
+		{
+			name = getfilename(*it);
+			if (comparename_findin_names(name.c_str(), vtnames))
+			{
+				// 打印日志,提示客户重名的相片;
+				OutputDebugString(it->c_str());
+				OutputDebugString(_T("\n"));
+				pvtIsonym->push_back(it->c_str());
+			}
+			else
+			{
+				vtnames.push_back(name);
+			}
+		}
+	}
+
+	if (pvtfiles)
+		delete []pvtfiles;
+	pvtfiles = NULL;
+
+	return 0;
+}
+
+INT filehelper::comparefolder_findin_folder(IN LPCTSTR folder1, IN LPCTSTR folder2, IN BOOL bsubfolder, IN LPCTSTR findext, OUT STR_VEC *pvtIsonym /* = NULL */)
+{
+	return 0;
+}
+
+INT filehelper::copyfolder(IN LPCTSTR from, IN LPCTSTR to)
+{
+	if (to == NULL || from == NULL)
+		return -1;
+
+	// 创建目录;
+	int nleft = 0;
+	int nIndex = -1;
+	TString strTo = to;
+	if (strTo.at(strTo.size()-1) != _T('\\'))
+		strTo.append(_T("\\"));
+	// 共享路径和硬盘盘符;
+	if (_tcscmp(strTo.substr(0, 2).c_str(), _T("\\\\")) == 0)
+		nleft = 2;
+	else if (strTo.at(2) == _T('\\'))
+		nleft = 3;
+
+	do 
+	{
+		nIndex = strTo.substr(nleft, -1).find_first_of(_T("\\"));
+		if (nIndex != TString::npos)
+		{
+			if (_tmkdir(strTo.substr(0, nIndex + nleft).c_str()) == -1 && errno != EEXIST)				
+				break;
+			nleft += nIndex + 1;
+		}		
+	} while (nIndex!=-1);
+	
+
+	return 0;
+}
+#endif
+
+

+ 322 - 0
SATService/filehelper/filehelper.h

@@ -0,0 +1,322 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [IT], 保留所有权利;
+/*  模 块 名:;
+/*  描    述:;
+/*
+/*  版    本:[V];	
+/*  作    者:[IT];
+/*  日    期:[12/22/2016];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[IT];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+#ifndef __FILEHELPING_20150818__
+#define __FILEHELPING_20150818__
+
+#pragma once
+
+#include "findfile.h"
+
+#define USE_IMGEXT 0
+
+class filehelper :public findfile
+{
+public:
+	filehelper(void);
+	~filehelper(void);
+
+public:
+	// 排序或乱序;
+	static void random(IN STR_VEC& vtContent, IN BOOL bSort = TRUE);
+	// 分隔数组;
+	static void split(OUT vector<TString>& vtSplit, IN const TString str, IN const TString strSplit);
+	// 根据后缀名保留文件;
+	static void keepdownbyext(IN const TString &keepExt, IN STR_VEC &vtfiles);
+	static void keepdownbyext(IN const TString &keepExt, IN const STR_VEC &vtfiles, OUT STR_VEC &vtresult);
+	// 根据后缀名移除文件;
+	static void keepoutbyext(IN const TString &removeext, IN STR_VEC &vtfiles);
+	static void keepoutbyext(IN const TString &removeext, IN const STR_VEC &vtfiles, OUT STR_VEC &vtresult);
+	// 保留指定文件名(有后缀的:001.jpg)的文件路径;
+	void keepdowbyname(IN const STR_VEC &vtnames);
+	static void keepdowbyname(IN const STR_VEC &vtnames, IN STR_VEC &vtfiles);
+	static void keepdowbyname(IN const STR_VEC &vtnames, IN const STR_VEC &vtfiles, OUT STR_VEC &vt_result);
+	// 保留指定文件名(无后缀的:001)的文件路径;
+	void keepdowbyname_noext(IN const STR_VEC &vtnames);
+	static void keepdowbyname_noext(IN const STR_VEC &vtnames, IN STR_VEC &vtfiles);
+	static void keepdowbyname_noext(IN const STR_VEC &vtnames, IN const STR_VEC &vtfiles, OUT STR_VEC &vt_result);
+	// 分组扩展名;
+	static void subgroupExt(IN const TString &ext1, IN STR_VEC &vt1, IN const TString &ext2, IN STR_VEC &vt2);	
+
+	// 根据有后缀的文件名(如:001.jpg)来过虑文件;
+	static void filterbyname(IN STR_VEC &vtfiles, IN const STR_VEC &vtnames);
+	void filterbyname(IN const STR_VEC &vtnames);
+
+	// 根据无后缀的文件名(如:001)来过虑文件;
+	static void filterbyname_noext(IN STR_VEC &vtfiles, IN const STR_VEC &vtnames);
+	void filterbyname_noext(IN const STR_VEC &vtnames);
+
+	// 获取文件名,带后缀名(如:001.jpg);
+	inline void getnames(IN STR_VEC &vtfiles, IN STR_VEC &vtnames)
+	{
+		INT nIndex = 0;
+		for ( STR_VEC::iterator it = vtfiles.begin(); it != vtfiles.end(); it++ )
+		{
+			if ( (nIndex = it->find_last_of(_T('\\'))) != TString::npos )
+			{
+				vtnames.push_back(it->substr(nIndex+1));
+			}
+		}
+	}
+
+	// 获取文件名,不带后缀名(如:001);
+	inline void getnames_noext(IN const STR_VEC &vtfiles, IN STR_VEC &vtnames)
+	{
+		INT nIndex = 0;
+		INT nIndex2 = 0;
+		for ( STR_VEC::const_iterator it = vtfiles.begin(); it != vtfiles.end(); it++ )
+		{
+			if ( (nIndex = it->find_last_of(_T('\\'))) != TString::npos )
+			{
+				if ( (nIndex2 = it->find_last_of(_T('.'))) != TString::npos)
+				{
+					if ( nIndex2 > nIndex)
+						vtnames.push_back(it->substr(nIndex+1, nIndex2-nIndex));
+				}
+			}
+			else
+			{// vtfiles是没有目录的文件名,如:001.jpg;
+				if ( (nIndex2 = it->find_last_of(_T('.'))) != TString::npos)
+				{
+					vtnames.push_back(it->substr(0, nIndex2));
+				}
+			}
+		}
+	}
+public:
+	// 查找全部;
+	BOOL getall(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN OUT STR_VEC *pvtfiles,							// 返回要查找的文件全名;
+		IN OUT STR_VEC *pvtnames,							// 返回要查找的文件名(无路径,包含扩展名);
+		IN OUT STR_VEC *pvtfolders							// 返回要查找的子目录;
+		);
+
+	// 只获取当前要目录的子文件夹,不获取子文件夹中的文件夹;
+	BOOL getsubfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN STR_VEC *pvtfolders								// 返回子文件夹;
+		);
+
+	BOOL getallsubfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN STR_VEC *pvtfolders								// 返回所有子文件夹;
+		);
+
+	//////////////////////////////////////////////////////////////////////////
+	BOOL getfiles_findin_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC *pvtfiles								// 返回要查找的文件全名;
+		);
+
+	BOOL getfiles_findout_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN OUT STR_VEC *pvtfiles							// 返回要查找的文件全名;
+		);
+
+	BOOL getfiles_within_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC *pvtfiles,								// 返回要查找的文件全名;
+		IN STR_VEC *pvtfolders								// 文件夹;
+		);
+
+	//////////////////////////////////////////////////////////////////////////
+	virtual BOOL getfiles_bynames_findin_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC &vtnames,
+		IN STR_VEC *pvtfiles								// 返回要查找的文件全名;
+		);
+
+	virtual BOOL getfiles_bynames_findout_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC &vtnames,
+		IN OUT STR_VEC *pvtfiles							// 返回要查找的文件全名;
+		) ;
+
+	virtual BOOL getfiles_bynames_within_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC &vtnames,
+		IN STR_VEC *pvtfiles,								// 返回要查找的文件全名;
+		IN STR_VEC *pvtfolders								// 文件夹;
+		) ;
+
+	//////////////////////////////////////////////////////////////////////////
+	BOOL getnames_findin_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC *pvtnames								// 返回要查找的文件名(无路径,包含扩展名);
+		);
+
+	BOOL getnames_findout_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC *pvtnames								// 返回要查找的文件名(无路径,包含扩展名);
+		);
+
+	BOOL getnames_within_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC *pvtnames,								// 返回要查找的文件全名;
+		IN STR_VEC *pvtfolders								// 文件夹;
+		);
+
+	//////////////////////////////////////////////////////////////////////////
+	BOOL getfilesnames_findin_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC *pvtfiles,								// 返回要查找的文件全名;
+		IN STR_VEC *pvtnames								// 返回要查找的文件名(无路径,包含扩展名);
+		);
+
+	BOOL getfilesnames_findout_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC *pvtfiles,								// 返回要查找的文件全名;
+		IN STR_VEC *pvtnames								// 返回要查找的文件名(无路径,包含扩展名);
+		);
+
+	BOOL getfilesnames_within_subfolder(
+		IN LPCTSTR lpfolder,								// 要查找的目录;
+		IN LPCTSTR lpfindext,								// 要查找的扩展名;
+		IN STR_VEC *pvtfiles,								// 返回要查找的文件全名;
+		IN STR_VEC *pvtnames,								// 返回要查找的文件名(无路径,包含扩展名);
+		IN STR_VEC *pvtfolders								// 文件夹;
+		);
+
+public:
+	//////////////////////////////////////////////////////////////////////////
+	BOOL comparename_findin_names(IN LPCTSTR name);
+
+	// 两个文件对比,是否同名;
+	BOOL comparename_2file(
+		IN LPCTSTR file1,									// 文件1(全路径);
+		IN LPCTSTR file2									// 文件2(全路径);
+		);
+
+	// 指定文件名,与一组文件名对比;
+	BOOL comparename_findin_names(
+		IN LPCTSTR name,									// 文件名(无路径,不含扩展名);
+		IN STR_VEC& vtnames									// 一组文件名(无路径,不含扩展名);
+		);
+
+	// 指定文件名,与一组文件对比;
+	BOOL comparename_findin_files(
+		IN LPCTSTR name,									// 文件名(不含扩展名);
+		IN STR_VEC& vtfiles									// 一组文件(全路径);
+		);
+
+	// 指定文件名,与一文件夹对比;
+	BOOL comparename_findin_folder(
+		IN LPCTSTR name,									// 文件名(不含扩展名);
+		IN LPCTSTR folder,									// 文件夹;
+		IN LPCTSTR findext,									// 要查找的扩展名;
+		IN BOOL bsubfolder									// 是否查找文件夹内的子目录;
+		);
+
+	//////////////////////////////////////////////////////////////////////////
+	// 指定一组文件名,与另一组文件名对比;
+	INT comparenames_findin_names(
+		IN STR_VEC vtSrcnames,								// 指定的一组源文件名(无路径,不含扩展名);
+		IN STR_VEC vtDesnames,								// 指定的一组目标文件名(无路径,不含扩展名);
+		OUT STR_VEC *pvtIsonym = NULL						// 返回同名的文件名;	
+		);
+
+	// 指定一组文件名,与一组文件(全路径)对比;
+	INT comparenames_findin_files(
+		IN STR_VEC vtnames,									// 指定的一组源文件名(无路径,不含扩展名);
+		IN STR_VEC vtfiles,									// 指定的一组目标文件(全路径);
+		IN LPCTSTR findext,									// 要查找的扩展名;
+		OUT STR_VEC *pvtIsonym = NULL						// 返回同名的文件名;
+		);
+
+	// 指定的一组文件名,与一文件夹对比;
+	INT comparenames_findin_folder(
+		IN STR_VEC vtnames,									// 指定的一组源文件名(无路径,不含扩展名);
+		IN LPCTSTR folder,									// 文件夹;
+		IN BOOL bsubfolder,									// 是否查找文件夹内的子目录;
+		IN LPCTSTR findext,									// 要查找的扩展名;
+		OUT STR_VEC *pvtIsonym = NULL						// 返回同名的文件名;
+		);
+
+	//////////////////////////////////////////////////////////////////////////
+	// 指定一文件,与一组文件名对比;
+	BOOL comparefile_findin_names(
+		IN LPCTSTR file,									// 文件1(全路径);
+		IN STR_VEC& vtnames									// 指定的一组文件名(无路径,不含扩展名)
+		);
+
+	// 指定一文件,与一组文件对比;
+	BOOL comparefile_findin_files(
+		IN LPCTSTR file,									// 文件(全路径);
+		IN STR_VEC& vtfiles,									// 指定的一组文件(全路径)
+		IN LPCTSTR findext									// 要查找的扩展名;
+		);
+
+	// 指定一文件,与一文件夹对比;
+	BOOL comparefile_findin_folder(
+		IN LPCTSTR file,									// 文件(全路径);
+		IN LPCTSTR folder,									// 文件夹;
+		IN BOOL bsubfolder,									// 是否查找子文件夹;
+		IN LPCTSTR findext									// 要查找的扩展名;
+		);
+
+	//////////////////////////////////////////////////////////////////////////
+	// 指定一组文件,与另一组文件对比;
+	INT comparefiles_findin_files(
+		IN STR_VEC &vtfiles1,								// 指定的一组源文件;
+		IN STR_VEC &vtfiles2,								// 指定的一组目标文件;
+		IN LPCTSTR findext,									// 要查找的扩展名;
+		OUT STR_VEC *pvtIsonym = NULL						// 返回同名的文件名;
+		);
+
+	// 指定一组文件,与一文件夹对比;
+	INT comparefiles_findin_folder(
+		IN STR_VEC &vtfiles,								// 指定的一组源文件;
+		IN LPCTSTR folder,									// 指定的文件夹;
+		IN BOOL bsubfolder,									// 是否查找子文件夹;
+		IN LPCTSTR findext,									// 要查找的扩展名;
+		OUT STR_VEC *pvtIsonym = NULL						// 返回同名的文件名;
+		);
+
+	// 文件夹自我对比(所有子文件夹对比)
+	INT comparefolderself(
+		IN LPCTSTR folder,									// 指定文件夹;
+		IN LPCTSTR findext,									// 要查找的扩展名;
+		OUT STR_VEC *pvtIsonym = NULL						// 返回同名的文件名;
+		);
+
+	// 指定一文件夹,与另一文件夹对比;
+	INT comparefolder_findin_folder(
+		IN LPCTSTR folder1,									// 指定源文件夹;
+		IN LPCTSTR folder2,									// 指定目标文件夹;
+		IN BOOL bsubfolder,									// 是否查找子文件夹;
+		IN LPCTSTR findext,									// 要查找的扩展名;
+		OUT STR_VEC *pvtIsonym = NULL						// 返回同名的文件名;
+		);
+
+	public:
+		INT copyfolder(IN LPCTSTR from, IN LPCTSTR to);
+};
+
+#endif

+ 877 - 0
SATService/filehelper/findfile.cpp

@@ -0,0 +1,877 @@
+#include "StdAfx.h"
+#include "findfile.h"
+#include <algorithm>
+
+// 查找文件的最大数量限制在nMaxLimit;
+CONST size_t nMaxLimit = 0xFFFFFFFF;	//--4294967295;
+// 查找文件的默认数量限制在nDefLimit;
+CONST size_t nDefLimit = 0xFFFFF;	//--1048575
+
+// 路径分隔符;
+CONST TCHAR		c_pathSeparator = _T('\\');
+CONST TString	s_pathSeparator = _T("\\");
+CONST TString	g_sVertical = _T("|");
+
+// 正确的扩展名及格式样列;
+// 这个值最好可以从配置文件ini或xml中获取,不用在代码里写死;
+CONST TString	g_sCorrectExt = _T("*.jpg|*.jpeg|*.png|*.bmp|*.cr2|*.nef|*.raw");
+
+TString lowercase(IN const TString &Str)
+{
+#if 1 // 多字节生僻字下会出问题;
+	TString sResult;
+	for ( TString::const_iterator it = Str.begin(); it != Str.end(); it++ )
+	{
+		if ( *it < 0 )
+		{// 小于0,含中文,什么都不做;
+			sResult.append(1, *(it++));
+			sResult.append(1, *it);
+		}
+		else
+		{
+			if (_T('A') <= *it && *it <= _T('Z'))
+				sResult.append(1, *it + 32);
+			else
+				sResult.append(1, *it);
+		}
+	}
+
+	return sResult;
+#else
+	TString sResult;
+	std::transform(Str.begin(), Str.end(), sResult.begin(), ::toupper);
+
+	
+	static CString str = _T("");
+	str = Str.c_str();
+	str.MakeLower();
+	sResult = str.GetString();
+	return sResult;
+#endif
+}
+
+TString uppercase(IN const TString &Str)
+{
+#if 1 // 多字节生僻字下会出问题;
+	TString sResult;
+	for ( TString::const_iterator it = Str.begin(); it != Str.end(); it++ )
+	{
+		if ( *it < 0 )
+		{// 小于0,含中文,什么都不做;
+			sResult.append(1, *(it++));
+			sResult.append(1, *it);
+		}
+		else
+		{
+			if (_T('A') <= *it && *it <= _T('Z'))
+				sResult.append(1, *it - 32);
+			else
+				sResult.append(1, *it);
+		}
+	}
+
+	return sResult;
+#else
+	TString sResult;
+	static CString strTemp = _T("");
+	strTemp = Str.c_str();
+	strTemp.MakeUpper();
+	sResult = strTemp.GetString();
+	return sResult;
+#endif
+}
+
+TString getfilename(IN CONST TString &file)
+{
+	TString name;
+	TString strfile = file;
+	int nIndex = strfile.find_last_of(_T('\\'));	// 如果file不包含 '\\' ,得不到文件名;
+	if (nIndex == TString::npos)
+	{
+		nIndex = strfile.find_last_of(_T('.'));
+		if ( nIndex == TString::npos )
+			return _T("");
+
+		return strfile.substr(0, nIndex);
+	}
+
+	name = strfile.substr(nIndex+1);
+	nIndex = name.find_last_of(_T('.'));
+	if (nIndex == TString::npos)
+		return _T("");
+
+	return name.substr(0, nIndex);
+}
+
+// 判断指定字符串是否在数组里(不区分大小写);
+BOOL IsStringExistNoCase(IN CONST TString& str, IN STR_VEC &tagVt)
+{
+	int nSize = tagVt.size();
+	if (nSize == 0) return FALSE;
+
+	BOOL bExist = FALSE;
+	TString stmp1 = uppercase(str);
+	TString stmp2;
+	for (STR_VEC::iterator it = tagVt.begin(); it != tagVt.end(); it++)
+	{
+		stmp2 = uppercase(*it);
+		if (stmp1.compare(stmp2) == 0)
+		{
+			bExist = TRUE;
+			break;
+		}
+	}
+
+	return bExist;
+}
+
+findfile::findfile(void):
+m_nlimit(nDefLimit),
+m_pvtfiles(NULL),
+m_pvtnames(NULL),
+m_pvtfolders(NULL),
+m_pvtfiles_sth(NULL),
+m_pvtfiles_mth(NULL)
+{
+	groupExt();
+}
+
+findfile::~findfile(void)
+{
+}
+
+/************************************************************************/
+/*  函数:groupExt[2/19/2017 Jeff];
+/*  描述:将字符串后缀名按"|"符号分隔成数组;
+/*  参数:;
+/*  	[GBL] g_sCorrectExt:全局字符串;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void findfile::groupExt()
+{
+	// 将所有扩展名解析到数组里;
+	INT nIndex = 0;
+	TString strtmp;
+	TString strEffctExt = g_sCorrectExt;
+	strEffctExt.append(_T("|"));
+	do
+	{
+		nIndex = strEffctExt.find(_T('|'));
+		if (nIndex != TString::npos)
+		{
+			strtmp = strEffctExt.substr(0, nIndex);
+			strEffctExt = strEffctExt.substr(nIndex + 1);
+
+			if (strtmp.compare(_T("*.*")) && strtmp.size())
+			{
+				if ( !IsStringExistNoCase(strtmp,m_vtEffctExt) )
+					m_vtEffctExt.push_back(strtmp);
+			}
+		}
+	} while (strEffctExt.find(_T('|')) != TString::npos);
+}
+
+/************************************************************************/
+/*  函数:[2/19/2017 Jeff];
+/*  描述:;
+/*  参数:;
+/*  	[IN] :;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void findfile::groupExt( IN CONST TString &exts, IN STR_VEC &vtExts )
+{
+	// 将所有扩展名解析到数组里;
+	INT nIndex = 0;
+	TString strtmp;
+	TString strEffctExt = exts;
+	strEffctExt.append(_T("|"));
+	do
+	{
+		nIndex = strEffctExt.find(_T('|'));
+		if (nIndex != TString::npos)
+		{
+			strtmp = strEffctExt.substr(0, nIndex);
+			strEffctExt = strEffctExt.substr(nIndex + 1);
+
+			if (strtmp.compare(_T("*.*")) && strtmp.size())
+			{
+				if ( !IsStringExistNoCase(strtmp,vtExts) )
+					vtExts.push_back(strtmp);
+			}
+		}
+	} while (strEffctExt.find(_T('|')) != TString::npos);
+}
+
+/************************************************************************/
+/*  函数:iscorrectext[2/18/2017 Jeff];
+/*  描述:判断指定的后缀串是否有效;
+/*  参数:;
+/*  	[IN] fext:输入的扩展名,fext的格式必须是:_T("*.jpg|*.jpeg|*.png|*.bmp");
+/*  	[IN] lpMistakenExt:不符合要求的扩展名;
+/*  	[IN/OUT] :;
+/*  返回:如果指定的扩展名符合要求,则返回TRUE,否则返回FALSE;
+/*  注意:
+/*       1.如果传入的扩展名包含_T("*.*"),则返回TRUE;若lpMistakenExt指针有效,记录第一次返回不符合要求的扩展名;
+/*       2.fext的格式必须是:_T("*.jpg|*.jpeg|*.png|*.bmp");
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+BOOL findfile::iscorrectext(IN const TString &fext, OUT TString* lpMistakenExt /*= NULL*/)
+{
+	if (fext.size() == 0) return FALSE;
+	if (fext.find(_T("*.*")) != TString::npos) return TRUE;
+
+	TString ext = lowercase(fext);
+	if (ext[ext.length() - 1] != _T('|'))
+		ext.append(g_sVertical);
+
+	BOOL bret = TRUE;
+	int nIndex = 0;
+	do
+	{
+		nIndex = ext.find(_T('|'));
+		if (nIndex != TString::npos)
+		{
+			if (g_sCorrectExt.find(ext.substr(0, nIndex)) == TString::npos)
+			{
+				if (lpMistakenExt)
+					*lpMistakenExt = ext.substr(0, nIndex);
+				bret = FALSE;
+				break;
+			}
+
+			ext = ext.substr(nIndex + 1);
+		}
+	} while (ext.find(_T('|')) != TString::npos);
+
+	return bret;
+}
+
+/************************************************************************/
+/*  函数:lowercase[2/18/2017 Jeff];
+/*  描述:将指定字符串小写化;
+/*  参数:;
+/*  	[IN] Str:要转为小写的字符串;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:返回转小写后的字符串;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+TString findfile::lowercase(IN const TString &Str)
+{
+	TString sResult;
+	for ( TString::const_iterator it = Str.begin(); it != Str.end(); it++ )
+	{
+		if ( *it < 0 )
+		{// 小于0,含中文,什么都不做;
+			sResult.append(1, *(it++));
+			sResult.append(1, *it);
+		}
+		else
+		{
+			if (_T('A') <= *it && *it <= _T('Z'))
+				sResult.append(1, *it + 32);
+			else
+				sResult.append(1, *it);
+		}
+	}
+
+	return sResult;
+}
+
+void findfile::lowercase(IN TString& Str)
+{
+	for ( TString::iterator it = Str.begin(); it != Str.end(); it++ )
+	{
+		if ( *it < 0 )
+		{// 小于0,含中文,什么都不做;
+			it++;
+		}
+		else
+		{
+			if (_T('A') <= *it && *it <= _T('Z'))
+				*it += 32;
+		}
+	}
+}
+
+/************************************************************************/
+/*  函数:uppercase[2/18/2017 Jeff];
+/*  描述:将指定字符串大写化;
+/*  参数:;
+/*  	[IN] Str:要转为大写的字符串;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:返回转大写后的字符串;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+TString findfile::uppercase(IN const TString &Str)
+{
+	TString sResult;
+	for ( TString::const_iterator it = Str.begin(); it != Str.end(); it++ )
+	{
+		if ( *it < 0 )
+		{// 小于0,含中文,什么都不做;
+			sResult.append(1, *(it++));
+			sResult.append(1, *it);
+		}
+		else
+		{
+			if (_T('a') <= *it && *it <= _T('z'))
+				sResult.append(1, *it - 32);
+			else
+				sResult.append(1, *it);
+		}
+	}
+
+	return sResult;
+}
+
+void findfile::uppercase(IN TString& Str)
+{
+	for ( TString::iterator it = Str.begin(); it != Str.end(); it++ )
+	{
+		if ( *it < 0 )
+		{// 小于0,含中文,什么都不做;
+			it++;
+		}
+		else
+		{
+			if (_T('a') <= *it && *it <= _T('z'))
+				*it -= 32;
+		}
+	}
+}
+
+/************************************************************************/
+/*
+函数:match
+描述:判断指定的文件命名是否匹配指定的扩展名;
+参数:
+IN	sFileExt	扩展名;
+IN	sFile		文件名;
+返回:匹配返回非0值,否则返回0;
+注意:
+*/
+/************************************************************************/
+void findfile::setlimit(IN CONST INT &nLimit)
+{
+	if (nLimit < 1)
+		m_nlimit = nDefLimit;
+	if (nLimit < nMaxLimit)
+		m_nlimit = nLimit;
+}
+
+
+
+// 获取文件名;
+TString findfile::getfilename(IN CONST TString &file)
+{
+	TString name;
+	TString strfile = file;
+	int nIndex = strfile.find_last_of(_T('\\'));	// 如果file不包含 '\\' ,得不到文件名;
+	if (nIndex == TString::npos)
+	{
+		nIndex = strfile.find_last_of(_T('.'));
+		if ( nIndex == TString::npos )
+			return _T("");
+
+		return strfile.substr(0, nIndex);
+	}
+
+	name = strfile.substr(nIndex+1);
+	nIndex = name.find_last_of(_T('.'));
+	if (nIndex == TString::npos)
+		return _T("");
+
+	return name.substr(0, nIndex);
+}
+
+// 全部;
+void findfile::findall(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+					findall(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit())
+					{
+						m_pvtnames->push_back(fileinfo.cFileName);
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findsubfolder(IN CONST TString& folder)	// 查找子目录;
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			if (_T('.') != fileinfo.cFileName[0])// '.'和 '..'的系统文件去除;
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findallsubfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			if (_T('.') != fileinfo.cFileName[0])// '.'和 '..'的系统文件去除;
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+					findallsubfolder(path + fileinfo.cFileName); // 不入子目录查找;
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+void findfile::findfiles_findin_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					findfiles_findin_subfolder(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit())
+					{
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findfiles_findout_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) != FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					if (!checklimit())
+					{
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findfiles_within_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+					findfiles_within_subfolder(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit())
+					{
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+void findfile::findnames_findin_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					findnames_findin_subfolder(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit() )
+					{
+						m_pvtnames->push_back(fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findnames_findout_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) != FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					if (!checklimit())
+					{
+						m_pvtnames->push_back(fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findnames_within_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+					findnames_within_subfolder(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit())
+					{
+						m_pvtnames->push_back(fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+void findfile::findfilesnames_findin_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					findfilesnames_findin_subfolder(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit() )
+					{
+						m_pvtnames->push_back(fileinfo.cFileName);
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findfilesnames_findout_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) != FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					if (!checklimit() )
+					{
+						m_pvtnames->push_back(fileinfo.cFileName);
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findfilesnames_within_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+					findfilesnames_within_subfolder(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit() )
+					{
+						m_pvtnames->push_back(fileinfo.cFileName);
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}

+ 110 - 0
SATService/filehelper/findfile.h

@@ -0,0 +1,110 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [IT], 保留所有权利;
+/*  模 块 名:;
+/*  描    述:;
+/*
+/*  版    本:[V];	
+/*  作    者:[IT];
+/*  日    期:[12/22/2016];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[IT];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+#ifndef __FIND_FILE_20151003__
+#define __FIND_FILE_20151003__
+
+#include <string>
+#include <vector>
+using namespace std;
+
+#ifndef _UNICODE
+typedef string TString;
+#else
+typedef wstring TString;
+#endif
+
+typedef vector<TString> STR_VEC;
+
+#pragma once
+
+class findfile
+{
+public:
+	findfile(void);
+	virtual ~findfile(void);
+
+//protected:
+public:
+	INT m_nlimit;
+	STR_VEC m_vtEffctExt;
+	STR_VEC *m_pvtfiles;
+	STR_VEC *m_pvtnames;
+	STR_VEC *m_pvtfolders;
+
+	STR_VEC *m_pvtfiles_sth;	// s小图;
+	STR_VEC *m_pvtfiles_mth;	// m小图;	
+public:
+	static BOOL iscorrectext(IN const TString &fext, OUT TString* lpMistakenExt = NULL);
+	// lowercase和uppercase在多字节下生僻字会出问题(珺、琤,珺转小写会变成琤,反过来琤转大写会变成珺);已修改;
+	static TString lowercase(IN const TString &Str);
+	static void lowercase(IN TString& str);
+	static TString uppercase(IN const TString &Str);
+	static void uppercase(IN TString& str);
+	//static int match(IN CONST TString &sExt, IN CONST TString &sFile);
+	static inline int findfile::match(IN CONST TString &strExt, IN CONST TString &strfile)
+	{
+		int pos = strfile.find_last_of(_T('.'));
+		if (TString::npos != pos)
+			return !_tcsicmp(strExt.c_str(), strfile.substr(pos).c_str());
+
+		return FALSE;
+	}
+	
+	static TString getfilename(IN CONST TString &file);
+protected:
+	INT  getlimit() const { return m_nlimit; }
+	void setlimit(IN CONST INT &nlimit);
+
+	void groupExt();
+	void groupExt( IN CONST TString &exts, IN STR_VEC &vtExts );
+
+	inline INT checklimit() { return m_pvtfiles->size() == getlimit(); }
+
+	BOOL IsaDirectory(CONST TString &sDirectory) { return (FILE_ATTRIBUTE_DIRECTORY == GetFileAttributes(sDirectory.c_str())); }
+
+public:
+	// 查找所有文件,包括子文件夹名、文件名、文件路径;
+	void findall(IN CONST TString& folder);
+	// 只返回2级子目录名(测试ok);
+	void findsubfolder(IN CONST TString& folder);
+	// 只返回所有子目录名(测试ok);
+	void findallsubfolder(IN CONST TString& folder);
+
+	// 查找文件路径,以及查找子目录的文件路径,但不获取子目录名(测试ok);
+	void findfiles_findin_subfolder(IN CONST TString& folder);
+	// 查找文件路径,不查找子目录的文件(测试ok);
+	void findfiles_findout_subfolder(IN CONST TString& folder);
+	// 查找文件路径,以及查找子目录的文件路径,同时获取子目录名(测试ok);
+	void findfiles_within_subfolder(IN CONST TString& folder);
+
+	// 查找文件名称,以及查找子目录的文件名称,但不获取子目录名(返回的文件包含扩展名)(测试ok);
+	void findnames_findin_subfolder(IN CONST TString& folder);
+	// 查找文件名称,不查找子目录的文件(返回的文件包含扩展名)(测试ok);
+	void findnames_findout_subfolder(IN CONST TString& folder);
+	// 查找文件名称,以及查找子目录的文件名称,同时获取子目录名(返回的文件包含扩展名)(测试ok);
+	void findnames_within_subfolder(IN CONST TString& folder);
+
+	// 查找文件路径和文件名,以及查找子目录的文件路径和文件名,但不获取子目录名;(返回的文件包含扩展名)(测试ok);
+	void findfilesnames_findin_subfolder(IN CONST TString& folder);
+	// 查找文件路径和文件名,不查找子目录;(返回的文件包含扩展名)(测试ok);
+	void findfilesnames_findout_subfolder(IN CONST TString& folder);
+	// 查找文件路径和文件名,以及查找子目录的文件路径和文件名,且获取子目录名;(返回的文件包含扩展名)(测试ok);
+	void findfilesnames_within_subfolder(IN CONST TString& folder);
+};
+
+#endif