Bladeren bron

日志模块

Jeff 5 jaren geleden
bovenliggende
commit
97d79297a5

+ 26 - 0
LogModule/LogModule.sln

@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LogModule", "LogModule\LogModule.vcproj", "{276AAD30-D6EF-47E3-9CF4-5631FBBC446D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcproj", "{5F43E5C6-6578-46E0-BC6D-A473C081259F}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{276AAD30-D6EF-47E3-9CF4-5631FBBC446D}.Debug|Win32.ActiveCfg = Debug|Win32
+		{276AAD30-D6EF-47E3-9CF4-5631FBBC446D}.Debug|Win32.Build.0 = Debug|Win32
+		{276AAD30-D6EF-47E3-9CF4-5631FBBC446D}.Release|Win32.ActiveCfg = Release|Win32
+		{276AAD30-D6EF-47E3-9CF4-5631FBBC446D}.Release|Win32.Build.0 = Release|Win32
+		{5F43E5C6-6578-46E0-BC6D-A473C081259F}.Debug|Win32.ActiveCfg = Debug|Win32
+		{5F43E5C6-6578-46E0-BC6D-A473C081259F}.Debug|Win32.Build.0 = Debug|Win32
+		{5F43E5C6-6578-46E0-BC6D-A473C081259F}.Release|Win32.ActiveCfg = Release|Win32
+		{5F43E5C6-6578-46E0-BC6D-A473C081259F}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 84 - 0
LogModule/LogModule/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__

+ 552 - 0
LogModule/LogModule/Global.cpp

@@ -0,0 +1,552 @@
+#include "stdafx.h"
+#include "Global.h"
+#include <SetupAPI.h>
+#include <InitGuid.h>
+#include <WinIoCtl.h>
+#include <direct.h>
+#include <fcntl.h>
+#include <share.h>
+#include <sys\stat.h>
+#pragma comment(lib, "SetupAPI.lib")
+
+namespace Global
+{
+	//////////////////////////////////////////////////////////////////////////
+	// 全局变量;
+	TCHAR g_szCurModuleDir[MAX_PATH] = { 0 };
+	TCHAR g_szCurModulePath[MAX_PATH] = { 0 };
+	TCHAR g_szFna[MAX_PATH] = { 0 };
+	TCHAR g_szConfig[MAX_PATH] = { 0 };
+	TCHAR g_szLogPath[MAX_PATH] = { 0 };
+	bool g_bEnableLog = false;
+	COleDateTime g_lastTime;
+	time_t g_time = time(NULL);
+	ThreadSection g_critSection;
+	FILE *gp_log_fp = NULL;
+	
+	//////////////////////////////////////////////////////////////////////////
+	void 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);
+					break;
+				}
+
+				nleft += nIndex + 1;
+			}
+		} while (nIndex != -1);
+	};
+
+	// 全局函数;
+	/************************************************************************/
+	/*  函数:WriteTextLog[7/28/2009 Jeff];
+	/*  描述:写文本日志;
+	/*  参数:;
+	/*  	[IN] :;
+	/*  返回:void;
+	/*  注意:;
+	/*  示例:;
+	/*
+	/*  修改:;
+	/*  日期:;
+	/*  内容:;
+	/************************************************************************/
+	void WriteTextLog(const TCHAR* format, ...)
+	{
+		static ThreadSection _critSection;
+		AutoThreadSection aSection(&_critSection);
+		// 将日志内容输入到文件中;
+		// 获取今年年份;
+		__time64_t gmt = time(NULL); // 获取当前日历时间(1900-01-01开始的Unix时间戳);
+		struct tm gmtm = { 0 };
+		localtime_s(&gmtm, &gmt); // 时间戳转成本地时间;
+
+		// 解析出日志路径;
+		TCHAR szlogpath[MAX_PATH] = { 0 };
+		_stprintf_s(szlogpath, _T("%slog %02d%02d.txt"), g_szCurModuleDir, gmtm.tm_mon + 1, gmtm.tm_mday);
+		// 打开或创建文件;
+		FILE* fp = NULL;
+		//MKDIR(g_szLogPath);
+		//if (_taccess(szlogpath, 0) != -1)
+#ifndef UNICODE
+		if (_access(szlogpath, 0) != -1)
+#else
+		if (_taccess(szlogpath, 0) != -1)
+#endif
+		{ // 存在;
+			if (0 == _tfopen_s(&fp, szlogpath, _T("a+")))
+				// 移动到末尾;
+				fseek(fp, 0, SEEK_END);
+		}
+		else
+		{ // 不存在;
+			_tfopen_s(&fp, szlogpath, _T("w+"));
+		}
+
+		if (fp == NULL)
+			return;
+
+		// 格式化前设置语言区域;
+		TCHAR* old_locale = _tcsdup(_tsetlocale(LC_CTYPE, NULL));
+		_tsetlocale(LC_CTYPE, _T("chs")); //设定中文;
+
+		// 格式化日志内容;
+		va_list args = NULL;
+		int len = 0;
+		TCHAR* buffer = NULL;
+		va_start(args, format);
+		// _vscprintf doesn't count. terminating '\0'
+		len = _vsctprintf(format, args) + 1;
+		buffer = (TCHAR*)malloc(len * sizeof(TCHAR));
+		_vstprintf_s(buffer, len, format, args);
+		_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, buffer);
+
+		// 关闭文件,释放资源并设置回原语言区域;
+		free(buffer);
+		fclose(fp);
+		_tsetlocale(LC_CTYPE, old_locale);
+		free(old_locale); //还原区域设定;
+	}
+
+	void WritePythonLog(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;
+		// 打开或创建文件;
+		static TCHAR szLastLog[MAX_PATH] = {0};
+		if ( _tcsicmp(szLastLog, g_szLogPath) != 0 )
+		{
+			MKDIR(g_szLogPath);
+			_tcscpy_s(szLastLog, g_szLogPath);
+			if ( gp_log_fp != NULL )
+			{	
+				fclose(gp_log_fp);
+				gp_log_fp = NULL;
+			}
+#if 0
+			int fd = 0;
+			if (_tsopen_s(&fd, g_szLogPath, _O_CREAT | _O_WRONLY | _O_APPEND, _SH_DENYRW, _S_IREAD | _S_IWRITE))
+			{
+				WriteTextLog(_T("创建文件描述符失败:%s,%d,%d"), g_szLogPath, nErr, GetLastError());
+				return;
+			}
+
+			// 创建并打开文件;
+			gp_log_fp = _tfdopen(fd, _T("w+"));
+			if (gp_log_fp == NULL )
+			{
+				WriteTextLog(_T("创建文件失败:%s,%d,%d"), g_szLogPath, nErr, GetLastError());
+				return;
+			}
+#else
+			nErr = _tfopen_s(&gp_log_fp, g_szLogPath, "w+");
+			if ( gp_log_fp == NULL )
+			{
+				WriteTextLog(_T("创建文件失败:%s,%d,%d"), g_szLogPath, nErr, GetLastError());
+				return;
+			}
+#endif
+		}
+
+		if (gp_log_fp == NULL )
+		{
+			WriteTextLog(_T("追加文件失败:%s,%d,%d"), g_szLogPath, nErr, GetLastError());
+			return;
+		}
+		
+		fseek(gp_log_fp, 0, SEEK_END);
+		// 格式化前设置语言区域;
+		TCHAR* old_locale = _tcsdup(_tsetlocale(LC_CTYPE, NULL));
+		_tsetlocale(LC_CTYPE, _T("chs")); //设定中文;
+
+		_ftprintf(gp_log_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);
+
+		fflush(gp_log_fp);
+		_tsetlocale(LC_CTYPE, old_locale);
+		free(old_locale); //还原区域设定;
+	}
+
+	void ClosePythonLog()
+	{
+		AutoThreadSection aSection(&g_critSection);
+		if ( gp_log_fp )
+		{
+			fclose(gp_log_fp);
+			gp_log_fp = NULL;
+		}
+	}
+
+	// 去除空格;
+	std::string& trim(std::string& str)
+	{
+		int nIndex = 0;
+		while ((nIndex = str.find_first_of(' ')) != std::string::npos)
+			str.erase(nIndex, 1);
+
+		return str;
+	}
+
+	// hModule 模块句柄 NULL表示当前模块;
+	bool GetVersion(IN const TCHAR* fname, OUT WORD* pdwFileVersion, OUT WORD* pdwProductVerion)
+	{
+		VS_FIXEDFILEINFO* pVi = NULL;
+		DWORD dwHandle = 0;
+		int size = GetFileVersionInfoSize(fname, &dwHandle);
+		if (size > 0)
+		{
+			BYTE* buffer = new BYTE[size];
+			memset(buffer, 0, size);
+
+			if (GetFileVersionInfo(fname, 0, size, buffer))
+			{
+				if (VerQueryValue(buffer, _T("\\"), (LPVOID*)& pVi, (PUINT)& size))
+				{
+					pdwFileVersion[0] = HIWORD(pVi->dwFileVersionMS);
+					pdwFileVersion[1] = LOWORD(pVi->dwFileVersionMS);
+					pdwFileVersion[2] = HIWORD(pVi->dwFileVersionLS);
+					pdwFileVersion[3] = LOWORD(pVi->dwFileVersionLS);
+
+					pdwProductVerion[0] = HIWORD(pVi->dwProductVersionMS);
+					pdwProductVerion[1] = LOWORD(pVi->dwProductVersionMS);
+					pdwProductVerion[2] = HIWORD(pVi->dwProductVersionLS);
+					pdwProductVerion[3] = LOWORD(pVi->dwProductVersionLS);
+
+					delete[] buffer;
+					return true;
+				}
+			}
+
+			delete[] buffer;
+		}
+
+		return false;
+	}
+
+	BOOL GetVersion(IN HMODULE hModule, OUT DWORD(&dwFVArray)[4], OUT DWORD(&dwPVArray)[4])
+	{
+		TCHAR fname[MAX_PATH];
+		VS_FIXEDFILEINFO* pVi;
+		DWORD dwHandle;
+
+		if (GetModuleFileName(hModule, fname, MAX_PATH))
+		{
+			INT nSize = GetFileVersionInfoSize(fname, &dwHandle);
+
+			if (nSize > 0)
+			{
+				BYTE* pBuffer = new BYTE[nSize];
+				memset(pBuffer, 0, nSize);
+
+				if (GetFileVersionInfo(fname, 0, nSize, pBuffer))
+				{
+					if (VerQueryValue(pBuffer, _T("\\"), (LPVOID*)& pVi, (PUINT)& nSize))
+					{
+						dwFVArray[0] = HIWORD(pVi->dwFileVersionMS);
+						dwFVArray[1] = LOWORD(pVi->dwFileVersionMS);
+						dwFVArray[2] = HIWORD(pVi->dwFileVersionLS);
+						dwFVArray[3] = LOWORD(pVi->dwFileVersionLS);
+
+						dwPVArray[0] = HIWORD(pVi->dwProductVersionMS);
+						dwPVArray[1] = LOWORD(pVi->dwProductVersionMS);
+						dwPVArray[2] = HIWORD(pVi->dwProductVersionLS);
+						dwPVArray[3] = LOWORD(pVi->dwProductVersionLS);
+
+						delete[]pBuffer;
+						return TRUE;
+					}
+				}
+
+				if (pBuffer)
+					delete[]pBuffer;
+			}
+		}
+
+		return FALSE;
+	}
+
+	WCHAR* ASCII2UNICODE(IN LPCCH lpASCIIStr)
+	{
+		if (lpASCIIStr == NULL)
+			return NULL;
+
+		// 获取宽字符字节数;
+		int cchWideChar = MultiByteToWideChar(CP_ACP, 0, lpASCIIStr, -1, NULL, 0);
+		if (cchWideChar == 0)
+			return NULL;
+
+		// 转换成宽字符串;
+		WCHAR* pWideChar = new WCHAR[cchWideChar + 1];
+		memset(pWideChar, 0, sizeof(WCHAR) * (cchWideChar + 1));
+		int nWriteNum = MultiByteToWideChar(CP_ACP, 0, lpASCIIStr, -1, pWideChar, cchWideChar);
+		if (nWriteNum != cchWideChar)
+		{
+			if (pWideChar)
+				delete[] pWideChar;
+			return NULL;
+		}
+
+		return pWideChar;
+	}
+
+	BOOL UNICODE2UTF8(IN LPWCH lpUNICODEStr, OUT string& strResult)
+	{
+		if (lpUNICODEStr == NULL)
+			return FALSE;
+
+		// 获取多字节字符字节数;
+		int cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, lpUNICODEStr, -1, NULL, 0, NULL, NULL);
+		if (cbMultiByte == 0)
+			return FALSE;
+
+		// 转换成多字节字符;
+		CHAR* pResult = new CHAR[cbMultiByte];
+		memset(pResult, 0, cbMultiByte);
+		int nWriteNum = WideCharToMultiByte(CP_UTF8, 0, lpUNICODEStr, -1, pResult, cbMultiByte, NULL, NULL);
+		if (nWriteNum != cbMultiByte)
+			return FALSE;
+
+		strResult = pResult;
+		if (pResult)
+			delete[] pResult;
+
+		return TRUE;
+	}
+
+	BOOL ASCII2UTF8(IN LPCCH lpASCIIStr, OUT string& strResult)
+	{
+		// 将ASCII字符串转成UNICODE字符串;
+		WCHAR* pWideChar = ASCII2UNICODE(lpASCIIStr);
+		if (pWideChar == NULL)
+			return FALSE;
+
+		// 再将UICODE转成UTF8;
+		BOOL bResult = UNICODE2UTF8(pWideChar, strResult);
+
+		if (pWideChar)
+			delete[] pWideChar;
+
+		return bResult;
+	}
+
+	string EnCode_UTF8URL(IN const CHAR* pText)
+	{
+		string tt = "";
+		string dd = "";
+		ASCII2UTF8(pText, tt);
+
+		size_t len = tt.length();
+		for (size_t i = 0; i < len; i++)
+		{
+			if (isalnum((BYTE)tt.at(i)))
+			{
+				char tempbuff[2] = { 0 };
+				sprintf_s(tempbuff, "%c", (BYTE)tt.at(i));
+				dd.append(tempbuff);
+			}
+			else if (isspace((BYTE)tt.at(i)))
+			{
+				dd.append("+");
+			}
+			else
+			{
+				char tempbuff[4];
+				sprintf_s(tempbuff, "%%%X%X", ((BYTE)tt.at(i)) >> 4, ((BYTE)tt.at(i)) % 16);
+				dd.append(tempbuff);
+			}
+		}
+		return dd;
+	}
+
+	void EnCode_UTF8URL(IN const CHAR* pText, OUT string& strResult)
+	{
+		string tt = "";
+		ASCII2UTF8(pText, tt);
+
+		size_t len = tt.length();
+		for (size_t i = 0; i < len; i++)
+		{
+			if (isalnum((BYTE)tt.at(i)))
+			{
+				char tempbuff[2] = { 0 };
+				sprintf_s(tempbuff, "%c", (BYTE)tt.at(i));
+				strResult.append(tempbuff);
+			}
+			else if (isspace((BYTE)tt.at(i)))
+			{
+				strResult.append("+");
+			}
+			else
+			{
+				char tempbuff[4];
+				sprintf_s(tempbuff, "%%%X%X", ((BYTE)tt.at(i)) >> 4, ((BYTE)tt.at(i)) % 16);
+				strResult.append(tempbuff);
+			}
+		}
+	}
+
+	//做为解Url使用
+	char CharToInt(char ch)
+	{
+		if (ch >= '0' && ch <= '9')return (char)(ch - '0');
+		if (ch >= 'a' && ch <= 'f')return (char)(ch - 'a' + 10);
+		if (ch >= 'A' && ch <= 'F')return (char)(ch - 'A' + 10);
+		return -1;
+	}
+
+	char StrToBin(IN char(&str)[2])
+	{
+		char tempWord[2];
+		char chn;
+
+		tempWord[0] = CharToInt(str[0]);                         //make the B to 11 -- 00001011
+		tempWord[1] = CharToInt(str[1]);                         //make the 0 to 0  -- 00000000
+
+		chn = (tempWord[0] << 4) | tempWord[1];                //to change the BO to 10110000
+
+		return chn;
+	}
+
+	CHAR* UNICODE2ASCII(IN LPWCH lpUNICODEStr)
+	{
+		if (lpUNICODEStr == NULL)
+			return NULL;
+
+		// 获取多字节字符字节数;
+		int cbMultiByte = WideCharToMultiByte(CP_OEMCP, 0, lpUNICODEStr, -1, NULL, 0, NULL, NULL);
+		if (cbMultiByte == 0)
+			return NULL;
+
+		// 转换成多字节字符;
+		CHAR *pMultiByteStr = new CHAR[cbMultiByte + 1];
+		memset(pMultiByteStr, 0, cbMultiByte + 1);
+		int nWriteNum = WideCharToMultiByte(CP_OEMCP, 0, lpUNICODEStr, -1, pMultiByteStr, cbMultiByte, NULL, NULL);
+		if (nWriteNum != cbMultiByte)
+		{
+			if (pMultiByteStr)
+				delete[]pMultiByteStr;
+			return NULL;
+		}
+
+		pMultiByteStr[cbMultiByte] = '\0';
+
+		return pMultiByteStr;
+	}
+
+	BOOL UNICODE2ASCII(IN LPWCH lpUNICODEStr, OUT LPCH pASCIIStr, IN CONST INT& nASCIIStrLen)
+	{
+		if (lpUNICODEStr == NULL)
+			return FALSE;
+
+		// 获取多字节字符字节数;
+		int cbMultiByte = WideCharToMultiByte(CP_OEMCP, 0, lpUNICODEStr, -1, NULL, 0, NULL, NULL);
+		if (cbMultiByte == 0 || cbMultiByte >= nASCIIStrLen)
+			return FALSE;
+
+		// 转换成多字节字符;
+		memset((void*)pASCIIStr, 0, nASCIIStrLen);
+		int nWriteNum = WideCharToMultiByte(CP_OEMCP, 0, lpUNICODEStr, -1, pASCIIStr, cbMultiByte, NULL, NULL);
+		if (nWriteNum != cbMultiByte)
+		{
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	/************************************************************************/
+	/*  函数:[7/26/2016 IT];
+	/*  描述:;
+	/*  参数:;
+	/*  	[IN] :;
+	/*  	[OUT] :;
+	/*  	[IN/OUT] :;
+	/*  返回:void;
+	/*  注意:;
+	/*  示例:;
+	/*
+	/*  修改:;
+	/*  日期:;
+	/*  内容:;
+	/************************************************************************/
+	string DeCode_URLUNICODE(IN const CHAR* pURLText)
+	{
+		string str = pURLText;
+		string strResult = "";
+
+		INT nIndex = 0;
+		string strTemp = "";
+
+		while (str.find("\\u", 0, 2) != string::npos)
+		{
+			nIndex = str.find("\\u", 0, 2);
+			strResult.append(str.substr(0, nIndex));
+			strTemp = str.substr(nIndex + 2, 4);
+			str = str.substr(nIndex + 2 + 4);
+			CHAR szReturn[10] = { 0 };
+
+			union  __UNION_VAR_INT {
+				BYTE   ch[2];
+				int	   value;
+			}unionVarInt;
+
+			unionVarInt.ch[0] = (CharToInt(strTemp.at(2)) << 4) | (CharToInt(strTemp.at(3)) & 0x00FF);
+			unionVarInt.ch[1] = (CharToInt(strTemp.at(0)) << 4) | (CharToInt(strTemp.at(1)) & 0x00FF);
+
+			WCHAR szWide[2] = { 0 };
+			szWide[0] = unionVarInt.value;
+			UNICODE2ASCII(szWide, szReturn, 10);
+			strResult.append(szReturn);
+		}
+		strResult.append(str);
+
+		return strResult;
+	}
+
+	// 通过注册表查找系统当前串口信息;
+	
+	BOOL IsValidString(LPCTSTR lpszString)
+	{
+		if (lpszString == NULL)
+			return FALSE;
+
+		do
+		{
+			// ASCII可显示的字符;
+			if (*lpszString < 32 || *lpszString > 126)
+			{
+				return FALSE;
+			}
+		} while (*++lpszString);
+
+		return TRUE;
+	}
+} // namespace Global

+ 45 - 0
LogModule/LogModule/Global.h

@@ -0,0 +1,45 @@
+
+#pragma once
+#include <stdio.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <time.h> //或者 #include <ctime>
+#include <io.h>
+#pragma comment(lib,"version.lib")
+#include "../cJson/cJSON.h"
+#include <vector>
+#include <string>
+using namespace std;
+namespace Global
+{
+	//////////////////////////////////////////////////////////////////////////
+	// 全局变量;
+	extern TCHAR g_szCurModuleDir[MAX_PATH];
+	extern TCHAR g_szCurModulePath[MAX_PATH];
+	extern TCHAR g_szFna[MAX_PATH];
+	extern TCHAR g_szConfig[MAX_PATH];
+
+	extern bool g_bEnableLog;
+	extern TCHAR g_szLogPath[MAX_PATH];
+	extern COleDateTime g_lastTime;
+	extern time_t g_time;
+	extern FILE *gp_log_fp;
+
+	typedef struct _TLOG_{
+		string report_type;
+		string report_data;
+	}TLog, *pTLog;
+
+	//////////////////////////////////////////////////////////////////////////
+	// 全局函数;
+	bool GetVersion(IN const TCHAR *fname, OUT WORD *pdwFileVersion, OUT WORD *pdwProductVerion);
+	BOOL GetVersion(IN HMODULE hModule, OUT DWORD(&dwFVArray)[4], OUT DWORD(&dwPVArray)[4]);
+	void WriteTextLog(const TCHAR *format, ...);
+	void WritePythonLog(const TCHAR *msg);
+	void ClosePythonLog();
+	std::string EnCode_UTF8URL(IN const CHAR* pText);
+	std::string &trim(std::string &str);
+	// 字符串是否由数字、字母、符号组成;
+	BOOL IsValidString(LPCTSTR lpszString);
+	string DeCode_URLUNICODE(IN const CHAR* pURLText);
+};

+ 815 - 0
LogModule/LogModule/IOCPModel.cpp

@@ -0,0 +1,815 @@
+#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);  
+
+	Global::g_time = time(NULL);
+	Global::g_lastTime = COleDateTime::GetCurrentTime();
+	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());
+
+			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 )
+{
+	Global::g_time = time(NULL);
+	Global::g_lastTime = COleDateTime::GetCurrentTime();
+	// 先把上一次的数据显示出现,然后就重置状态,发出下一个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 );
+	
+	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());
+
+			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
+}
+
+/////////////////////////////////////////////////////////////////////
+// 判断客户端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;
+	}
+}
+
+
+
+

+ 259 - 0
LogModule/LogModule/IOCPModel.h

@@ -0,0 +1,259 @@
+#pragma once
+
+// winsock 2 的头文件和库
+#include <winsock2.h>
+#include <MSWSock.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")
+
+
+//////////////////////////////////////////////////////////////////
+// 在完成端口上投递的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请求的
+
+	// 初始化
+	_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;
+
+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; 
+
+};
+

+ 132 - 0
LogModule/LogModule/LogModule.cpp

@@ -0,0 +1,132 @@
+// LogModule.cpp : 定义 DLL 应用程序的导出函数。
+//
+
+#include "stdafx.h"
+#include "LogModule.h"
+#include <Shlwapi.h>
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+
+// 唯一的应用程序对象
+
+CWinApp theApp;
+
+using namespace std;
+
+CIOCPModel g_IOCP; // 主要对象,完成端口模型
+
+// 启动tcp服务;
+BOOL StartServer(unsigned int port)
+{
+	// 1.加载配置文件;
+	TCHAR szDrive[_MAX_DRIVE] = { 0 };
+	TCHAR szDir[_MAX_DIR] = { 0 };
+	TCHAR szExt[_MAX_DIR] = { 0 };
+	::GetModuleFileName(NULL, Global::g_szCurModulePath, sizeof(Global::g_szCurModulePath) / sizeof(TCHAR));
+	_tsplitpath_s(Global::g_szCurModulePath, szDrive, szDir, Global::g_szFna, szExt);
+	_tcscpy_s(Global::g_szCurModuleDir, szDrive);
+	_tcscat_s(Global::g_szCurModuleDir, szDir);
+
+	// 初始化网络库;
+	if ( !g_IOCP.LoadSocketLib() )
+	{
+		return FALSE;
+	}
+
+	Global::WriteTextLog(_T("初始化网络库成功"));
+
+	// 开启tcp服务;
+	if ( !g_IOCP.Start(port) )
+	{
+		return FALSE;
+	}
+
+	Global::g_time = time(NULL);
+	Global::g_lastTime = COleDateTime::GetCurrentTime();
+
+	Global::WriteTextLog(_T("开启TCP服务成功"));
+	if ( PathFileExists(Global::g_szConfig) )
+	{
+		// runner_tcp_port
+		TCHAR szTcpPort[10] = {0};
+		_itoa_s(port, szTcpPort, 10);
+		Global::WriteTextLog(_T("配置文件路径:%s"), Global::g_szConfig);
+		for ( int i = 0; i < 10; i++ )
+		{
+			if (!WritePrivateProfileString(_T("COMM"), _T("runner_tcp_port"), szTcpPort, Global::g_szConfig))
+				Global::WriteTextLog(_T("保存端口号:%d 失败; 原因:%d"), port, GetLastError());
+			else
+			{
+				Global::WriteTextLog(_T("保存端口号:%d 成功;"), port);
+				break;
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+// 停止tcp服务;
+void StopServer()
+{
+	g_IOCP.Stop();
+}
+
+// 是否写入日志文件;
+void EnableWriteLog(bool bEnable)
+{
+	Global::g_bEnableLog = bEnable;
+	if ( !bEnable )
+		Global::ClosePythonLog();
+	Global::WriteTextLog(_T("当前日志路径:%s; 状态:%s"), Global::g_szLogPath, bEnable ? _T("启用日志") : _T("禁用日志") );
+}
+
+void SetCaselogPath(LPCTSTR lpCaselogPath)
+{
+	if ( lpCaselogPath == NULL || lpCaselogPath[0] == '\0')
+		return;
+
+	Global::WriteTextLog(_T("设置日志路径:%s"), lpCaselogPath);
+	_stprintf_s(Global::g_szLogPath, _T("%s"), lpCaselogPath);
+	Global::WritePythonLog(_T("C++ Operation"));
+}
+
+void SetConfigPath(LPCTSTR lpConfigPath)
+{
+	if ( lpConfigPath == NULL || lpConfigPath[0] == '\0')
+		return;
+
+	_stprintf_s(Global::g_szConfig, _T("%s"), lpConfigPath);
+}
+
+void SetCaselogPath3(const char* buffer, const int& len)
+{
+	if ( buffer == NULL || buffer[0] == '\0')
+		return;
+
+	Global::WriteTextLog(_T("路径3:%s, len=%d"), (char*)buffer, len);
+	_tcscpy_s(Global::g_szLogPath, (char*)buffer);
+}
+
+LPCTSTR GetCaselogPath()
+{
+	return Global::g_szLogPath;
+}
+
+__int64 GetReceivePrintTime()
+{
+	return Global::g_time;
+}
+
+void SetReceivePrintTime(__int64 dt)
+{
+	Global::g_time = dt;
+}
+
+void ClosePythonLog()
+{
+	Global::ClosePythonLog();
+}

+ 34 - 0
LogModule/LogModule/LogModule.h

@@ -0,0 +1,34 @@
+// 下列 ifdef 块是创建使从 DLL 导出更简单的
+// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 LOGMODULE_EXPORTS
+// 符号编译的。在使用此 DLL 的
+// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
+// LOGMODULE_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
+// 符号视为是被导出的。
+#ifdef LOGMODULE_EXPORTS
+#define LOGMODULE_API __declspec(dllexport)
+#else
+#define LOGMODULE_API __declspec(dllimport)
+#endif
+
+//// 此类是从 LogModule.dll 导出的
+//class LOGMODULE_API CLogModule {
+//public:
+//	CLogModule(void);
+//	// TODO: 在此添加您的方法。
+//};
+//
+//extern LOGMODULE_API int nLogModule;
+//
+//LOGMODULE_API int fnLogModule(void);
+
+extern "C" LOGMODULE_API BOOL StartServer(unsigned int port);
+extern "C" LOGMODULE_API void StopServer();
+extern "C" LOGMODULE_API void EnableWriteLog(bool bEnable);
+extern "C" LOGMODULE_API void SetCaselogPath(LPCTSTR lpCaselogPath);
+// 注意:给Python调用的接口,若不用于返回,尽量用int a 代替const int &a;
+extern "C" LOGMODULE_API void SetConfigPath(LPCTSTR lpConfigPath);
+extern "C" LOGMODULE_API void SetCaselogPath3(const char* buffer, const int& len);
+extern "C" LOGMODULE_API LPCTSTR GetCaselogPath();
+extern "C" LOGMODULE_API __int64 GetReceivePrintTime();
+extern "C" LOGMODULE_API void SetReceivePrintTime(__int64 dt);
+extern "C" LOGMODULE_API void ClosePythonLog();

+ 69 - 0
LogModule/LogModule/LogModule.rc

@@ -0,0 +1,69 @@
+//Microsoft Visual C++ 生成的资源脚本。
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// 从 TEXTINCLUDE 2 资源生成。
+//
+#include "afxres.h"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
+LANGUAGE 4, 2
+#pragma code_page(936)
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 字符串表
+//
+
+STRINGTABLE
+BEGIN
+   IDS_APP_TITLE       "LogModule"
+END
+
+#endif
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// 从 TEXTINCLUDE 3 资源生成。
+//
+#ifndef _AFXDLL
+#include "l.CHS\\afxres.rc"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // 不是 APSTUDIO_INVOKED

+ 291 - 0
LogModule/LogModule/LogModule.vcproj

@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="gb2312"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="LogModule"
+	ProjectGUID="{276AAD30-D6EF-47E3-9CF4-5631FBBC446D}"
+	RootNamespace="LogModule"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\..\..\bin\$(SolutionName)"
+			IntermediateDirectory="$(OutDir)\$(ProjectName)\$(ConfigurationName)"
+			ConfigurationType="2"
+			UseOfMFC="2"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\cJson;..\iconv"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LOGMODULE_EXPORTS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				CommandLine="copy $(TargetPath) E:\SAT\SAT_Runner\btc_runner_se\runner\Bll\$(TargetFileName) /y/a"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\..\..\bin\$(SolutionName)"
+			IntermediateDirectory="$(OutDir)\$(ProjectName)\$(ConfigurationName)"
+			ConfigurationType="2"
+			UseOfMFC="2"
+			CharacterSet="2"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="..\cJson;..\iconv"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LOGMODULE_EXPORTS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				CommandLine="copy $(TargetPath) E:\SAT\SAT_Runner\btc_runner_se\runner\Bll\$(TargetFileName) /y/a"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Ô´Îļþ"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\Global.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\IOCPModel.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\LogModule.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Í·Îļþ"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\CritSection.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Global.h"
+				>
+			</File>
+			<File
+				RelativePath=".\IOCPModel.h"
+				>
+			</File>
+			<File
+				RelativePath=".\LogModule.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Resource.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\targetver.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=".\LogModule.rc"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="cJson"
+			>
+			<File
+				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>
+		<File
+			RelativePath=".\ReadMe.txt"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 133 - 0
LogModule/LogModule/LogModule.vcxproj

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{276AAD30-D6EF-47E3-9CF4-5631FBBC446D}</ProjectGuid>
+    <RootNamespace>LogModule</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <PlatformToolset>v142</PlatformToolset>
+    <UseOfMfc>Dynamic</UseOfMfc>
+    <CharacterSet>MultiByte</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <PlatformToolset>v142</PlatformToolset>
+    <UseOfMfc>Dynamic</UseOfMfc>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>16.0.29124.152</_ProjectFileVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>..\..\..\..\bin\$(SolutionName)\</OutDir>
+    <IntDir>$(OutDir)$(ProjectName)\$(Configuration)\</IntDir>
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>..\..\..\..\bin\$(SolutionName)\</OutDir>
+    <IntDir>$(OutDir)$(ProjectName)\$(Configuration)\</IntDir>
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\cJson;..\iconv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LOGMODULE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy $(TargetPath) E:\SAT\SAT_Runner\btc_runner_se\runner\Bll\$(TargetFileName) /y/a</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>..\cJson;..\iconv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LOGMODULE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy $(TargetPath) E:\SAT\SAT_Runner\btc_runner_se\runner\Bll\$(TargetFileName) /y/a</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\cJson\cJSON.c">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+      </PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+      </PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="Global.cpp" />
+    <ClCompile Include="IOCPModel.cpp" />
+    <ClCompile Include="LogModule.cpp" />
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\cJson\cJSON.h" />
+    <ClInclude Include="CritSection.h" />
+    <ClInclude Include="Global.h" />
+    <ClInclude Include="IOCPModel.h" />
+    <ClInclude Include="LogModule.h" />
+    <ClInclude Include="Resource.h" />
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="targetver.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="LogModule.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Text Include="ReadMe.txt" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 71 - 0
LogModule/LogModule/LogModule.vcxproj.filters

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="源文件">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="头文件">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="资源文件">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
+    </Filter>
+    <Filter Include="cJson">
+      <UniqueIdentifier>{894590ea-3ff5-4f25-9d41-ff9c5a6f6ade}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Global.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="IOCPModel.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="LogModule.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="stdafx.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="..\cJson\cJSON.c">
+      <Filter>cJson</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="CritSection.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="Global.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="IOCPModel.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="LogModule.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="Resource.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="stdafx.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="targetver.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="..\cJson\cJSON.h">
+      <Filter>cJson</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="LogModule.rc">
+      <Filter>资源文件</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Text Include="ReadMe.txt" />
+  </ItemGroup>
+</Project>

+ 48 - 0
LogModule/LogModule/ReadMe.txt

@@ -0,0 +1,48 @@
+========================================================================
+    动态链接库:LogModule 项目概述
+========================================================================
+
+应用程序向导已为您创建了此 LogModule DLL。
+
+本文件概要介绍组成 LogModule 应用程序的
+的每个文件的内容。
+
+
+LogModule.vcproj
+    这是使用应用程序向导生成的 VC++ 项目的主项目文件,
+    其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
+
+LogModule.cpp
+    这是主 DLL 源文件。
+
+	此 DLL 在创建时不导出任何符号。因此,在生成此 DLL 时
+	将不会产生 .lib 文件。如果希望此项目
+	成为其他某个项目的项目依赖项,则需要
+	添加代码以从 DLL 导出某些符号,
+	以便产生一个导出库,或者,也可以在项目“属性页”对话框中的
+	“链接器”文件夹中,将“常规”属性页上的
+	“忽略输入库”属性设置为“是”。
+
+/////////////////////////////////////////////////////////////////////////////
+应用程序向导创建了下列资源:
+
+LogModule.rc
+这是程序使用的所有 Microsoft Windows 资源的列表。它包括 RES 子目录中存储的图标、位图和光标。
+此文件可以直接在 Microsoft Visual C++ 中进行编辑。
+
+Resource.h
+    这是标准头文件,可用于定义新的资源 ID。
+    Microsoft Visual C++ 将读取并更新此文件。
+
+/////////////////////////////////////////////////////////////////////////////
+其他标准文件:
+
+StdAfx.h, StdAfx.cpp
+    这些文件用于生成名为 LogModule.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
+
+/////////////////////////////////////////////////////////////////////////////
+其他注释:
+
+应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。
+
+/////////////////////////////////////////////////////////////////////////////

+ 17 - 0
LogModule/LogModule/Resource.h

@@ -0,0 +1,17 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by LogModule.rc
+//
+
+#define IDS_APP_TITLE			103
+
+// жÔÏóµÄÏÂÒ»×éĬÈÏÖµ
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE	101
+#define _APS_NEXT_COMMAND_VALUE		40001
+#define _APS_NEXT_CONTROL_VALUE		1000
+#define _APS_NEXT_SYMED_VALUE		101
+#endif
+#endif

+ 8 - 0
LogModule/LogModule/stdafx.cpp

@@ -0,0 +1,8 @@
+// stdafx.cpp : 只包括标准包含文件的源文件
+// LogModule.pch 将作为预编译头
+// stdafx.obj 将包含预编译类型信息
+
+#include "stdafx.h"
+
+// TODO: 在 STDAFX.H 中
+// 引用任何所需的附加头文件,而不是在此文件中引用

+ 33 - 0
LogModule/LogModule/stdafx.h

@@ -0,0 +1,33 @@
+// stdafx.h : 标准系统包含文件的包含文件,
+// 或是经常使用但不常更改的
+// 特定于项目的包含文件
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN             // 从 Windows 头中排除极少使用的资料
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // 某些 CString 构造函数将是显式的
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN            // 从 Windows 头中排除极少使用的资料
+#endif
+
+#include <afx.h>
+#include <afxwin.h>         // MFC 核心组件和标准组件
+#include <afxext.h>         // MFC 扩展
+#ifndef _AFX_NO_OLE_SUPPORT
+#include <afxdtctl.h>           // MFC 对 Internet Explorer 4 公共控件的支持
+#endif
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>                     // MFC 对 Windows 公共控件的支持
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <iostream>
+// Windows 头文件:
+#include <windows.h>
+#include "IOCPModel.h"
+#include "Global.h"
+#include "CritSection.h"
+// TODO: 在此处引用程序需要的其他头文件

+ 24 - 0
LogModule/LogModule/targetver.h

@@ -0,0 +1,24 @@
+#pragma once
+
+// 以下宏定义要求的最低平台。要求的最低平台
+// 是具有运行应用程序所需功能的 Windows、Internet Explorer 等产品的
+// 最早版本。通过在指定版本及更低版本的平台上启用所有可用的功能,宏可以
+// 正常工作。
+
+// 如果必须要针对低于以下指定版本的平台,请修改下列定义。
+// 有关不同平台对应值的最新信息,请参考 MSDN。
+#ifndef WINVER                          // 指定要求的最低平台是 Windows Vista。
+#define WINVER 0x0600           // 将此值更改为相应的值,以适用于 Windows 的其他版本。
+#endif
+
+#ifndef _WIN32_WINNT            // 指定要求的最低平台是 Windows Vista。
+#define _WIN32_WINNT 0x0600     // 将此值更改为相应的值,以适用于 Windows 的其他版本。
+#endif
+
+#ifndef _WIN32_WINDOWS          // 指定要求的最低平台是 Windows 98。
+#define _WIN32_WINDOWS 0x0410 // 将此值更改为适当的值,以适用于 Windows Me 或更高版本。
+#endif
+
+#ifndef _WIN32_IE                       // 指定要求的最低平台是 Internet Explorer 7.0。
+#define _WIN32_IE 0x0700        // 将此值更改为相应的值,以适用于 IE 的其他版本。
+#endif

+ 132 - 0
LogModule/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
LogModule/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
LogModule/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
LogModule/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
LogModule/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
LogModule/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
LogModule/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

+ 94 - 0
LogModule/client.py

@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import os, sys, time
+import socket
+import datetime
+
+
+class logClient:
+    def __init__(self):
+        self.port = 5566
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.constate = False
+
+    def __del__(self):
+        self.disconnect()
+
+    # 连接服务器;
+    def connect(self):
+        try:
+            self.sock.connect(('127.0.0.1', self.port))
+            self.constate = True
+        except Exception, e:
+            print "LoggingUtil:client=> socket connect error:", e, self.port
+            self.constate = False
+
+        return self.constate
+
+    # 发送消息;
+    def sendmsg(self, msg):
+        if self.constate is False:
+            if self.connect() is False:
+                return
+
+        msg_json = ""
+        msg_dict = {'ReportType': 'printLog', 'prinMsg': msg}
+        try:
+            msg_json = json.dumps(msg_dict)
+        except Exception:
+            print "LoggingUtil:client=> to json error:", msg
+            return
+
+        try:
+            self.sock.settimeout(1)
+            self.sock.sendall(msg_json)
+            self.sock.settimeout(None)
+        except Exception, e:
+            print "LoggingUtil:client=>无Runner接收结果。Error:", e, 'runner_sender_port:', self.port
+            self.disconnect()
+            self.connect()
+
+    # 断开连接;
+    def disconnect(self):
+        try:
+            self.sock.shutdown(2)
+            self.sock.close()
+        except Exception, e:
+            print "LoggingUtil:client=> socket disconnect error:", e
+
+
+# 单例模块;
+logClient_singleton = logClient()
+
+def current_time():
+    return time.strftime('%Y-%m-%d   %H:%M:%S  ', time.localtime(time.time()))
+
+
+def print_log(tag='', logmsg=''):
+    if logmsg == '':
+        msg = current_time() + 'printLog: %s' % str(tag)
+        tcp_msg = str(tag)
+    else:
+        msg = current_time() + 'printLog: %s:%s' % (str(tag), str(logmsg))
+        tcp_msg = str(tag) + str(logmsg)
+    print msg
+    logClient_singleton.sendmsg(tcp_msg)
+
+
+if __name__ == '__main__':
+    # print_log("printLog", "测试中……")
+    # print_log(unicode("this step is: Index : " + "1" + "    Name: " + str("1")+ "    Desc: " + str("1") + "    result: " + "Detail"))
+    # print_log("this step is: Index : " + "我" + "    Name: " + str("1")+ "    Desc: " + str("1") + "    result: " + "Detail")
+
+    # test_basic_general("1.jpg")
+    # test_basic_acurate("1.jpg")
+    # test_getStrWithImgProcess()
+    # print_log("printLog", "Test_Fail:Timing%s,%s截图,红场的比例与标准比例差异过大!&#10;")
+
+    while True:
+        time.sleep(5)
+        print_log("printLog", "测试中……")
+        # print_log("printLog", "Test_Fail:Timing%s,%s截图,红场的比例与标准比例差异过大!&#10;")
+        # time.sleep(2)
+        #         # print_log("printLog", u"测试中……")

+ 103 - 0
LogModule/test.py

@@ -0,0 +1,103 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+from ctypes.wintypes import HWND, LPCSTR, UINT
+from ctypes import *
+import chardet
+# 加载dll;
+logdll = CDLL(r'E:\bin\LogModule\logmodule.DLL')
+
+'''
+Python 2.x中,默认编码为ascii;
+如果在文件中指定编码为utf-8,要转在gbk是无法直接转的;需要先转换成unicode,再转成gbk;
+
+Python 3.x,默认编码为unicode;
+如果在文件中指定编码为uft-8,可以直接转gbk;
+'''
+
+
+if __name__ == '__main__':
+    # time.sleep(20)
+    # 启动服务;
+    if logdll.StartServer(5566):
+        print "启动服务成功"
+    else:
+        print "启动服务失败"
+
+    # 启用写日志;
+    logdll.EnableWriteLog(True)
+
+    '''
+    # 设置日志路径一;
+    #
+    # 错误方式:logdll.SetCaselogPath(r"E:\bin\城aa\我们aa.txt")  C++默认编码为GBK, Python为utf-8,无法直接传递;
+    #
+    '''
+
+    '''
+    # 设置日志路径二;
+    #
+    # 正确方式:若python字符编码为unicode,直接将unicode转码为gbk
+    #
+    '''
+    if 0:
+        path = u"E:\\bin\\测试日志\\测试日志.txt"
+        print type(path)
+        logdll.SetCaselogPath(path.encode("gbk"))
+
+        '''
+        # 获取日志路径;
+        '''
+        logdll.GetCaselogPath.restype = POINTER(c_byte)
+        path = logdll.GetCaselogPath()
+        dir = string_at(path, -1).decode("gbk")
+        print dir
+
+    '''
+    # 设置日志路径三;
+    #
+    # 正确方式:若python字符编码为utf-8,先将python字符utf-8转码为unicode,再将unicode转码为gbk
+    #
+    '''
+    if 0:
+        # 先解码utf-8为unicod,再转码为gbk;
+        path = "E:\\bin\\城aa\\我们aa.txt"
+        print type(path)
+        print chardet.detect(path)
+        logdll.SetCaselogPath(path.decode("utf-8").encode('gbk'))
+
+    '''
+    C接口: const int& len参数传递过程中会报错;改为int len不会报错;
+    '''
+    if 0:
+        path = "E:\\bin\\城aa\\我们aa.txt"
+        print chardet.detect(path)
+        logdll.SetCaselogPath2(path.decode("utf-8").encode('gbk'), path.__len__())
+
+        '''
+        # 获取日志路径;
+        '''
+        logdll.GetCaselogPath.restype = POINTER(c_byte)
+        path = logdll.GetCaselogPath()
+        dir = string_at(path, -1).decode("utf-8")
+        print dir
+
+    index = 0
+    # 获取最后的接收时间戳;
+    logdll.GetReceivePrintTime.restype = c_int64
+    while True:
+        logdll.EnableWriteLog(True)
+        time.sleep(0.1)
+        path = r"E:\bin\测试日志\测试日志%d.txt" % index
+        print "日志路径", path
+        logdll.SetCaselogPath(path.decode("utf-8").encode('gbk'))
+        dt = logdll.GetReceivePrintTime()
+        print "时间:", dt, time.time(), time.time() - dt
+        time.sleep(60)
+        logdll.EnableWriteLog(False)
+        time.sleep(0.1)
+        index = index+1
+
+    # 停止服务;
+    logdll.StopServer()

+ 40 - 0
LogModule/test/ReadMe.txt

@@ -0,0 +1,40 @@
+========================================================================
+    控制台应用程序:test 项目概述
+========================================================================
+
+应用程序向导已为您创建了此 test 应用程序。
+
+本文件概要介绍组成 test 应用程序的
+的每个文件的内容。
+
+
+test.vcproj
+    这是使用应用程序向导生成的 VC++ 项目的主项目文件,
+    其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
+
+test.cpp
+    这是主应用程序源文件。
+
+/////////////////////////////////////////////////////////////////////////////
+应用程序向导创建了下列资源:
+
+test.rc
+这是程序使用的所有 Microsoft Windows 资源的列表。它包括 RES 子目录中存储的图标、位图和光标。
+此文件可以直接在 Microsoft Visual C++ 中进行编辑。
+
+Resource.h
+    这是标准头文件,可用于定义新的资源 ID。
+    Microsoft Visual C++ 将读取并更新此文件。
+
+/////////////////////////////////////////////////////////////////////////////
+其他标准文件:
+
+StdAfx.h, StdAfx.cpp
+    这些文件用于生成名为 test.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
+
+/////////////////////////////////////////////////////////////////////////////
+其他注释:
+
+应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。
+
+/////////////////////////////////////////////////////////////////////////////

+ 17 - 0
LogModule/test/Resource.h

@@ -0,0 +1,17 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by test.rc
+//
+
+#define IDS_APP_TITLE			103
+
+// жÔÏóµÄÏÂÒ»×éĬÈÏÖµ
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE	101
+#define _APS_NEXT_COMMAND_VALUE		40001
+#define _APS_NEXT_CONTROL_VALUE		1000
+#define _APS_NEXT_SYMED_VALUE		101
+#endif
+#endif

+ 85 - 0
LogModule/test/stdafx.cpp

@@ -0,0 +1,85 @@
+// stdafx.cpp : 只包括标准包含文件的源文件
+// test.pch 将作为预编译头
+// stdafx.obj 将包含预编译类型信息
+
+#include "stdafx.h"
+
+// TODO: 在 STDAFX.H 中
+// 引用任何所需的附加头文件,而不是在此文件中引用
+
+//////////////////////////////////////////////////////////////////////////
+// 用例;
+HMODULE g_hlogdll = NULL;
+log_Start g_logStart = NULL;
+log_Stop g_logStop = NULL;
+log_enable g_logEnable = NULL;
+log_Setlogpath g_Setlogpath = NULL;
+log_Getlogpath g_Getlogpath = NULL;
+log_GetTime g_GetTime = NULL;
+
+BOOL LoadLogLibarary()
+{
+	if ( g_hlogdll == NULL )
+	{
+		g_hlogdll = (HMODULE)LoadLibrary(_T("logmodule.dll"));
+		if (!g_hlogdll)
+			return FALSE;
+	}
+
+	g_logStart = (log_Start)GetProcAddress(g_hlogdll, "StartServer");
+	if ( !g_logStart )
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_logStop = (log_Stop)GetProcAddress(g_hlogdll, "StopServer");
+	if ( !g_logStop )
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_logEnable = (log_enable)GetProcAddress(g_hlogdll, "EnableWriteLog");
+	if ( !g_logEnable )
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_Setlogpath = (log_Setlogpath)GetProcAddress(g_hlogdll, "SetCaselogPath");
+	if ( !g_Setlogpath )
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_Getlogpath = (log_Getlogpath)GetProcAddress(g_hlogdll, "GetCaselogPath");
+	if ( !g_Getlogpath )
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_GetTime= (log_GetTime)GetProcAddress(g_hlogdll, "GetReceivePrintTime");
+	if ( !g_GetTime )
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void FreeLogLibarary()
+{
+	if ( g_hlogdll )
+	{
+		if ( FreeLibrary(g_hlogdll) )
+		{
+			g_hlogdll = NULL;
+			g_logStart = NULL;
+			g_logStop = NULL;
+		}
+	}
+}

+ 53 - 0
LogModule/test/stdafx.h

@@ -0,0 +1,53 @@
+// stdafx.h : 标准系统包含文件的包含文件,
+// 或是经常使用但不常更改的
+// 特定于项目的包含文件
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#include <stdio.h>
+#include <tchar.h>
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // 某些 CString 构造函数将是显式的
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN            // 从 Windows 头中排除极少使用的资料
+#endif
+
+#include <afx.h>
+#include <afxwin.h>         // MFC 核心组件和标准组件
+#include <afxext.h>         // MFC 扩展
+#ifndef _AFX_NO_OLE_SUPPORT
+#include <afxdtctl.h>           // MFC 对 Internet Explorer 4 公共控件的支持
+#endif
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>                     // MFC 对 Windows 公共控件的支持
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <iostream>
+
+
+
+// TODO: 在此处引用程序需要的其他头文件
+
+//////////////////////////////////////////////////////////////////////////
+// 用例;
+extern HMODULE g_hlogdll;
+extern BOOL LoadLogLibarary();
+extern void FreeLogLibarary();
+
+typedef BOOL (*log_Start)(unsigned int port);
+typedef void (*log_Stop)();
+typedef void (*log_enable)(bool bEnable);
+typedef void (*log_Setlogpath)(LPCTSTR lplogpath);
+typedef LPCTSTR (*log_Getlogpath)();
+typedef double (*log_GetTime)();
+
+
+extern log_Start g_logStart;
+extern log_Stop g_logStop;
+extern log_enable g_logEnable;
+extern log_Setlogpath g_Setlogpath;
+extern log_Getlogpath g_Getlogpath;
+extern log_GetTime g_GetTime;

+ 24 - 0
LogModule/test/targetver.h

@@ -0,0 +1,24 @@
+#pragma once
+
+// 以下宏定义要求的最低平台。要求的最低平台
+// 是具有运行应用程序所需功能的 Windows、Internet Explorer 等产品的
+// 最早版本。通过在指定版本及更低版本的平台上启用所有可用的功能,宏可以
+// 正常工作。
+
+// 如果必须要针对低于以下指定版本的平台,请修改下列定义。
+// 有关不同平台对应值的最新信息,请参考 MSDN。
+#ifndef WINVER                          // 指定要求的最低平台是 Windows Vista。
+#define WINVER 0x0600           // 将此值更改为相应的值,以适用于 Windows 的其他版本。
+#endif
+
+#ifndef _WIN32_WINNT            // 指定要求的最低平台是 Windows Vista。
+#define _WIN32_WINNT 0x0600     // 将此值更改为相应的值,以适用于 Windows 的其他版本。
+#endif
+
+#ifndef _WIN32_WINDOWS          // 指定要求的最低平台是 Windows 98。
+#define _WIN32_WINDOWS 0x0410 // 将此值更改为适当的值,以适用于 Windows Me 或更高版本。
+#endif
+
+#ifndef _WIN32_IE                       // 指定要求的最低平台是 Internet Explorer 7.0。
+#define _WIN32_IE 0x0700        // 将此值更改为相应的值,以适用于 IE 的其他版本。
+#endif

+ 54 - 0
LogModule/test/test.cpp

@@ -0,0 +1,54 @@
+// test.cpp : 定义控制台应用程序的入口点。
+//
+
+#include "stdafx.h"
+#include "test.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+
+// 唯一的应用程序对象
+
+CWinApp theApp;
+
+using namespace std;
+
+int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
+{
+	int nRetCode = 0;
+
+	// 初始化 MFC 并在失败时显示错误
+	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
+	{
+		// TODO: 更改错误代码以符合您的需要
+		_tprintf(_T("错误: MFC 初始化失败\n"));
+		nRetCode = 1;
+	}
+	else
+	{
+		// TODO: 在此处为应用程序的行为编写代码。
+		if ( LoadLogLibarary() )
+		{
+			// 启动服务;
+			if ( g_logStart(5566) )
+				TRACE("start ok\r");
+			else
+				TRACE("start faild\r");
+
+			g_logEnable(true);
+			g_Setlogpath("E:\\bin\\aa\\aa.txt");
+	
+			printf("%lf\r\r",g_GetTime());
+
+			system("pause");
+
+			g_logEnable(true);
+			g_logStop();
+			FreeLogLibarary();
+		}
+	}
+
+	return nRetCode;
+}

+ 3 - 0
LogModule/test/test.h

@@ -0,0 +1,3 @@
+#pragma once
+
+#include "resource.h"

+ 69 - 0
LogModule/test/test.rc

@@ -0,0 +1,69 @@
+//Microsoft Visual C++ 生成的资源脚本。
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// 从 TEXTINCLUDE 2 资源生成。
+//
+#include "afxres.h"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
+LANGUAGE 4, 2
+#pragma code_page(936)
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 字符串表
+//
+
+STRINGTABLE
+BEGIN
+   IDS_APP_TITLE       "test"
+END
+
+#endif
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// 从 TEXTINCLUDE 3 资源生成。
+//
+#ifndef _AFXDLL
+#include "l.CHS\\afxres.rc"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // 不是 APSTUDIO_INVOKED

+ 239 - 0
LogModule/test/test.vcproj

@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="gb2312"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="test"
+	ProjectGUID="{5F43E5C6-6578-46E0-BC6D-A473C081259F}"
+	RootNamespace="test"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\..\..\bin\$(SolutionName)"
+			IntermediateDirectory="$(OutDir)\$(ProjectName)\$(ConfigurationName)"
+			ConfigurationType="1"
+			UseOfMFC="2"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\..\..\bin\$(SolutionName)"
+			IntermediateDirectory="$(OutDir)\$(ProjectName)\$(ConfigurationName)"
+			ConfigurationType="1"
+			UseOfMFC="2"
+			CharacterSet="2"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Ô´Îļþ"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\stdafx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\test.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Í·Îļþ"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\Resource.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\targetver.h"
+				>
+			</File>
+			<File
+				RelativePath=".\test.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=".\test.rc"
+				>
+			</File>
+		</Filter>
+		<File
+			RelativePath=".\ReadMe.txt"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 113 - 0
LogModule/test/test.vcxproj

@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{5F43E5C6-6578-46E0-BC6D-A473C081259F}</ProjectGuid>
+    <RootNamespace>test</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <PlatformToolset>v142</PlatformToolset>
+    <UseOfMfc>Dynamic</UseOfMfc>
+    <CharacterSet>MultiByte</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <PlatformToolset>v142</PlatformToolset>
+    <UseOfMfc>Dynamic</UseOfMfc>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>16.0.29124.152</_ProjectFileVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>..\..\..\..\bin\$(SolutionName)\</OutDir>
+    <IntDir>$(OutDir)$(ProjectName)\$(Configuration)\</IntDir>
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>..\..\..\..\bin\$(SolutionName)\</OutDir>
+    <IntDir>$(OutDir)$(ProjectName)\$(Configuration)\</IntDir>
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="test.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="Resource.h" />
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="targetver.h" />
+    <ClInclude Include="test.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="test.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Text Include="ReadMe.txt" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 47 - 0
LogModule/test/test.vcxproj.filters

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="源文件">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="头文件">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="资源文件">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="stdafx.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="test.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="Resource.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="stdafx.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="targetver.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="test.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="test.rc">
+      <Filter>资源文件</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Text Include="ReadMe.txt" />
+  </ItemGroup>
+</Project>