浏览代码

多文档SATHelper

scbc.sat2 5 年之前
父节点
当前提交
10cc04a402
共有 52 个文件被更改,包括 28638 次插入12 次删除
  1. 84 0
      SATHelper/SATHelper/CritSection.h
  2. 40 0
      SATHelper/SATHelper/Device.cpp
  3. 40 0
      SATHelper/SATHelper/Device.h
  4. 645 0
      SATHelper/SATHelper/Global.cpp
  5. 87 0
      SATHelper/SATHelper/Global.h
  6. 1007 0
      SATHelper/SATHelper/IOCPModel.cpp
  7. 294 0
      SATHelper/SATHelper/IOCPModel.h
  8. 44 0
      SATHelper/SATHelper/MainFrm.cpp
  9. 20 0
      SATHelper/SATHelper/MainFrm.h
  10. 338 0
      SATHelper/SATHelper/MemoryClient.cpp
  11. 41 0
      SATHelper/SATHelper/MemoryClient.h
  12. 139 0
      SATHelper/SATHelper/MemoryComm.cpp
  13. 30 0
      SATHelper/SATHelper/MemoryComm.h
  14. 134 0
      SATHelper/SATHelper/MemoryDef.h
  15. 2837 0
      SATHelper/SATHelper/QCAP.H
  16. 54 0
      SATHelper/SATHelper/SATHelper.vcxproj
  17. 159 12
      SATHelper/SATHelper/SATHelper.vcxproj.filters
  18. 305 0
      SATHelper/SATHelper/SynSerial.cpp
  19. 83 0
      SATHelper/SATHelper/SynSerial.h
  20. 471 0
      SATHelper/SATHelper/TrayIcon.cpp
  21. 84 0
      SATHelper/SATHelper/TrayIcon.h
  22. 364 0
      SATHelper/SATHelper/layout.xml
  23. 259 0
      SATHelper/SATHelper/stdint.h
  24. 2837 0
      SATHelper/SATHelper/tinyxml2.cpp
  25. 2310 0
      SATHelper/SATHelper/tinyxml2.h
  26. 3052 0
      SATHelper/UI/BCMenu.cpp
  27. 366 0
      SATHelper/UI/BCMenu.h
  28. 2559 0
      SATHelper/UI/BtnST.cpp
  29. 321 0
      SATHelper/UI/BtnST.h
  30. 161 0
      SATHelper/UI/ComboTreeCtrl.cpp
  31. 59 0
      SATHelper/UI/ComboTreeCtrl.h
  32. 36 0
      SATHelper/UI/FontSize.cpp
  33. 25 0
      SATHelper/UI/FontSize.h
  34. 1216 0
      SATHelper/UI/PictureEx.cpp
  35. 305 0
      SATHelper/UI/PictureEx.h
  36. 1364 0
      SATHelper/UI/SubLabel.cpp
  37. 117 0
      SATHelper/UI/SubLabel.h
  38. 244 0
      SATHelper/UI/TreeComboBox.cpp
  39. 90 0
      SATHelper/UI/TreeComboBox.h
  40. 260 0
      SATHelper/UI/XColorStatic.cpp
  41. 65 0
      SATHelper/UI/XColorStatic.h
  42. 132 0
      SATHelper/cJson/JsonUtils.cpp
  43. 15 0
      SATHelper/cJson/JsonUtils.h
  44. 132 0
      SATHelper/cJson/Json_Type.h
  45. 2973 0
      SATHelper/cJson/cJSON.c
  46. 285 0
      SATHelper/cJson/cJSON.h
  47. 1457 0
      SATHelper/cJson/cJSON_Utils.c
  48. 88 0
      SATHelper/cJson/cJSON_Utils.h
  49. 136 0
      SATHelper/filehelper/filehelper.cpp
  50. 61 0
      SATHelper/filehelper/filehelper.h
  51. 327 0
      SATHelper/filehelper/findfile.cpp
  52. 86 0
      SATHelper/filehelper/findfile.h

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

+ 40 - 0
SATHelper/SATHelper/Device.cpp

@@ -0,0 +1,40 @@
+#include "StdAfx.h"
+#include "Device.h"
+#include "Device.h"
+
+std::map<int, CDevice*> g_dmap;
+CDevice::CDevice()
+{
+}
+
+CDevice::~CDevice()
+{
+	m_obj.CloseSerialPort();
+}
+
+bool CDevice::Open()
+{
+	return m_obj.OpenSerialPort(m_iPort, m_iBaudrate, m_iDatabit, m_iParitybit, m_iStopbit, 0, 0);
+}
+
+void CDevice::Close()
+{
+	m_obj.CloseSerialPort();
+}
+
+std::string CDevice::SendCommond(std::string cmd)
+{
+	if ( !m_obj.IsOpen() )
+		return std::string("串口未打开成功");
+
+	DWORD dwRet = m_obj.WriteComm((byte*)cmd.data(), cmd.size());
+	if ( dwRet != cmd.size() )
+		return std::string("写串口失败");
+
+	byte szData[1024 * 4] = {0};
+	dwRet = m_obj.ReadComm(szData, 1024 * 4);
+	if (dwRet == 0 )
+		return std::string("读串口失败");
+
+	return std::string((char*)szData, dwRet);
+}

+ 40 - 0
SATHelper/SATHelper/Device.h

@@ -0,0 +1,40 @@
+#ifndef __DEVICE_HEADER__
+#define __DEVICE_HEADER__
+
+#include "SynSerial.h"
+
+#pragma once
+class CDevice
+{
+	CSynSerial m_obj;
+public:
+	CDevice();
+	~CDevice();
+
+	bool Open();
+	void Close();
+	std::string SendCommond(std::string cmd);
+public:
+	int m_iID;						// -- 设备的ID
+	int m_iPort;					// -- 设备串口号
+	std::string m_strDeviceName;	// -- 设备名称
+	int m_iDeviceType;				// -- 设备类型
+	int m_iBaudrate;				// -- 波特率
+	int m_iDatabit;					// -- 数据位
+	int m_iStopbit;					// -- 停止位
+	int m_iParitybit;				// -- 奇偶校验位
+	int m_iRsmode;					// -- 串口通信方式,0 - RS232;1 - RS422; 2 - RS485
+	//////////////////////////////////////////////////////////////////////////
+	std::string m_strIp;			// -- ip地址号
+	int m_iIpport;					// -- ip端口号
+	int m_iCommmode;				// -- 通信的方式,有 0 - 串口方式;1 - tcp/ip...
+	int m_iDevideaddr;				// -- 设备地址
+	int m_iTimeout;					// -- 通讯超时
+	int m_iIsuse; 					// -- 是否启用设备1:启用,0:禁用
+	std::string m_strProtocoldllname;	// -- 协议DLL名称
+	int m_bySpecialdevice;			// -- Modbus Rtu;Modbus Ascii;Modbus Tcp;Snmp;RS232;
+};
+
+extern std::map<int, CDevice*> g_dmap;
+
+#endif

+ 645 - 0
SATHelper/SATHelper/Global.cpp

@@ -0,0 +1,645 @@
+#include "stdafx.h"
+#include "Global.h"
+#include <tchar.h>
+#include <direct.h>
+#include <SetupAPI.h>
+#include <InitGuid.h>
+#include <WinIoCtl.h>
+#pragma comment(lib, "SetupAPI.lib")
+
+
+//////////////////////////////////////////////////////////////////////////
+// dll相关;
+HMODULE g_hdll = NULL;
+irc_IsAppRunning g_IsAppRunning = NULL;
+irc_StartIRApp g_StartIRApp = NULL;
+irc_CloseApp g_CloseApp = NULL;
+irc_Connect g_Connect = NULL;
+irc_DisConnect g_DisConnect = NULL;
+irc_loadSignalDataSet g_loadSignalDataSet = NULL;
+irc_getDeviceNames g_getDeviceNames = NULL;
+irc_getSignalsName g_getSignalsName = NULL;
+irc_sendSignal g_sendSignal = NULL;
+irc_sendSignals g_sendSignals = NULL;
+irc_sendRepeatsSignal g_sendRepeatsSignal = NULL;
+int g_nIRControl = 0;
+
+BOOL LoadLogLibarary()
+{
+	if (g_hdll == NULL)
+	{
+		g_hdll = (HMODULE)LoadLibrary(_T("IRControl.dll"));
+		if (!g_hdll)
+			return FALSE;
+	}
+
+	g_sendRepeatsSignal = (irc_sendRepeatsSignal)GetProcAddress(g_hdll, "sendRepeatsSignal");
+	if (!g_sendRepeatsSignal)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_sendSignals = (irc_sendSignals)GetProcAddress(g_hdll, "sendSignals");
+	if (!g_sendSignals)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_sendSignal = (irc_sendSignal)GetProcAddress(g_hdll, "sendSignal");
+	if (!g_sendSignal)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_loadSignalDataSet = (irc_loadSignalDataSet)GetProcAddress(g_hdll, "loadSignalDataSet");
+	if (!g_loadSignalDataSet)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_IsAppRunning = (irc_IsAppRunning)GetProcAddress(g_hdll, "IsAppRunning");
+	if (!g_IsAppRunning)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_StartIRApp = (irc_StartIRApp)GetProcAddress(g_hdll, "StartIRApp");
+	if (!g_StartIRApp)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_CloseApp = (irc_CloseApp)GetProcAddress(g_hdll, "CloseApp");
+	if (!g_CloseApp)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_getDeviceNames = (irc_getDeviceNames)GetProcAddress(g_hdll, "getDeviceNames");
+	if (!g_getDeviceNames)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_Connect = (irc_Connect)GetProcAddress(g_hdll, "Connect");
+	if (!g_Connect)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_getSignalsName = (irc_getSignalsName)GetProcAddress(g_hdll, "getSignalsName");
+	if (!g_getSignalsName)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	g_DisConnect = (irc_DisConnect)GetProcAddress(g_hdll, "DisConnect");
+	if (!g_DisConnect)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+	// 导出全局变量;
+	g_nIRControl = *(int*)GetProcAddress(g_hdll, "nIRControl");
+	if (!g_CloseApp)
+	{
+		FreeLogLibarary();
+		return FALSE;
+	}
+
+
+	return TRUE;
+}
+
+void FreeLogLibarary()
+{
+	if (g_hdll)
+	{
+		if (FreeLibrary(g_hdll))
+		{
+			g_hdll = NULL;
+			g_IsAppRunning = NULL;
+			g_StartIRApp = NULL;
+			g_CloseApp = NULL;
+			g_Connect = NULL;
+			g_getDeviceNames = NULL;
+			g_getSignalsName = NULL;
+			g_loadSignalDataSet = NULL;
+			g_sendSignal = NULL;
+			g_sendSignals = NULL;
+			g_sendRepeatsSignal = NULL;
+			g_DisConnect = NULL;
+		}
+	}
+}
+//////////////////////////////////////////////////////////////////////////
+
+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 };
+	STConfig g_Config;
+	//////////////////////////////////////////////////////////////////////////
+	// 全局函数;
+	/************************************************************************/
+	/*  函数:WriteTextLog[7/28/2009 Jeff];
+	/*  描述:写文本日志;
+	/*  参数:;
+	/*  	[IN] :;
+	/*  返回:void;
+	/*  注意:;
+	/*  示例:;
+	/*
+	/*  修改:;
+	/*  日期:;
+	/*  内容:;
+	/************************************************************************/
+	void WriteTextLog(const TCHAR* format, ...)
+	{
+		// 解析出日志路径;
+		TCHAR szlogpath[MAX_PATH] = { 0 };
+		_stprintf_s(szlogpath, _T("%s%s.txt"), g_szCurModuleDir, g_szFna);
+		// 打开或创建文件;
+		FILE* fp = NULL;
+		//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);
+		// 将日志内容输入到文件中;
+		// 获取今年年份;
+		__time64_t gmt = time(NULL);// 获取当前日历时间(1900-01-01开始的Unix时间戳);
+		struct tm gmtm = { 0 };
+		localtime_s(&gmtm, &gmt); // 时间戳转成本地时间;
+		_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);//还原区域设定;
+	}
+
+	int ReadReg(char* path, char* key, char* value)
+	{
+		HKEY hKey;
+		int ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_QUERY_VALUE, &hKey);
+		if (ret != ERROR_SUCCESS)
+		{
+			return 1;
+		}
+
+		//读取KEY
+		DWORD dwType = REG_SZ; //数据类型
+		DWORD cbData = 256;
+		ret = RegQueryValueEx(hKey, key, NULL, &dwType, (LPBYTE)value, &cbData);
+		if (ret != ERROR_SUCCESS)
+		{
+			RegCloseKey(hKey);
+			return 1;
+		}
+		RegCloseKey(hKey);
+
+		return 0;
+	}
+
+	// 读取配置文件-预留;
+	void GetConfig()
+	{
+		TCHAR szConfigpath[MAX_PATH] = { 0 };
+		_stprintf_s(szConfigpath, _T("%sConfig.ini"), Global::g_szCurModuleDir);
+		_tcscpy_s(g_szConfig, szConfigpath);
+
+		TCHAR szValue[MAX_PATH] = { 0 };
+		// 读取配置内容;
+		g_Config.bGenerics = GetPrivateProfileInt(_T("ir-device"), _T("generics"), 0, szConfigpath);
+
+		GetPrivateProfileString(_T("ir-device"), _T("redratpath"), NULL, szValue, MAX_PATH, szConfigpath);
+		g_Config.redratpath = szValue;
+		memset(szValue, 0, MAX_PATH);
+
+		GetPrivateProfileString(_T("ir-device"), _T("signal"), NULL, szValue, MAX_PATH, szConfigpath);
+		g_Config.signaldir = szValue;
+		memset(szValue, 0, MAX_PATH);
+
+		GetPrivateProfileString(_T("ir-device"), _T("use-signal"), NULL, szValue, MAX_PATH, szConfigpath);
+		g_Config.use_signal = szValue;
+		memset(szValue, 0, MAX_PATH);
+
+	}
+
+	BOOL GetOrientation(IN Image* pImg)
+	{
+		if (pImg == NULL) return FALSE;
+
+		UINT totalBufferSize;
+		UINT numProperties;
+		pImg->GetPropertySize(&totalBufferSize, &numProperties);
+		// Allocate the buffer that will receive the property items.
+		PropertyItem* pAllItems = (PropertyItem*)malloc(totalBufferSize);
+		// Fill the buffer.
+		pImg->GetAllPropertyItems(totalBufferSize, numProperties, pAllItems);
+		// Print the id data member of each property item.
+		for (UINT j = 0; j < numProperties; ++j)
+		{
+			if (PropertyTagOrientation == pAllItems[j].id)
+			{
+				short* ptrLong = (short*)(pAllItems[j].value);
+				int ret = (int)*ptrLong;
+				free(pAllItems);
+				return ret;
+			}
+		}
+		free(pAllItems);
+
+		return TRUE;
+	}
+
+
+	/************************************************************************/
+	/*
+	函数:GetEncoderClsid
+	描述:获取GDI+支持的图像格式编码器种类,以及所有种类编码器信息;
+	参数:
+	IN:  format 要获取的图像格式;
+	OUT: pClsid 返回符合条件的图像编码器信息;
+	返回:成功返回编码器索引,否则返回-1;
+	*/
+	/************************************************************************/
+	int GetEncoderClsid(IN CONST WCHAR* format, OUT CLSID* pClsid)
+	{
+		// GDI+支持的图像编码器数量;
+		UINT  numEncoders = 0;
+		// GDI+所有图像格式编码器详细信息所需要的空间大小;
+		UINT  nSize = 0;
+
+		ImageCodecInfo* pImageCodecInfo = NULL;
+
+		// 2.获取GDI+支持的所有图像格式编码器详细信息所需要的空间大小;
+		GetImageEncodersSize(&numEncoders, &nSize);
+		if (nSize == 0)
+			return -1;
+
+		//3.为ImageCodecInfo数组分配足额空间;
+		pImageCodecInfo = (ImageCodecInfo*)(malloc(nSize));
+		if (pImageCodecInfo == NULL)
+			return -1;
+
+		//4.获取所有的图像编码器信息;
+		GetImageEncoders(numEncoders, nSize, pImageCodecInfo);
+
+		//5.查找符合的图像编码器的Clsid;
+		for (UINT i = 0; i < numEncoders; ++i)
+		{
+			if (wcscmp(pImageCodecInfo[i].MimeType, format) == 0)
+			{
+				*pClsid = pImageCodecInfo[i].Clsid;
+				free(pImageCodecInfo);
+				return i;  // Success
+			}
+		}
+
+		//6.释放步骤3分配的内存;
+		free(pImageCodecInfo);
+
+		return -1;
+	}
+
+
+	BOOL LoadImgFromFile(IN Image** pImg, LPCTSTR lpPath)
+	{
+		if (!PathFileExists(lpPath))
+			return FALSE;
+
+		if (*pImg)
+			delete* pImg;
+
+		*pImg = NULL;
+
+#ifdef UNICODE
+		* pImg = Image::FromFile(lpPath);
+#else
+		BSTR strtmp = _bstr_t(lpPath);
+		*pImg = Image::FromFile(strtmp, TRUE);
+		SysFreeString(strtmp);
+#endif
+
+		return (*pImg ? TRUE : FALSE);
+	}
+
+	BOOL LoadImgFromBuffer(IN Image** pImg, IN BYTE* pBuffer, IN CONST INT& nBufLen)
+	{
+		if (pBuffer == NULL)
+			return FALSE;
+
+		if (*pImg)
+			delete* pImg;
+
+		*pImg = NULL;
+		HGLOBAL hMemery = GlobalAlloc(GMEM_MOVEABLE, nBufLen);
+		if (hMemery == NULL)
+			return FALSE;
+
+		BYTE* pMem = (BYTE*)GlobalLock(hMemery);
+		memcpy(pMem, pBuffer, nBufLen);
+
+		IStream* pstream = NULL;
+		CreateStreamOnHGlobal(hMemery, TRUE, &pstream);
+		*pImg = Image::FromStream(pstream);
+		GlobalUnlock(hMemery);
+		pstream->Release();
+
+		return (*pImg ? TRUE : FALSE);
+	}
+
+	// 先以只读方式从文件中读取二进制流出来,可以做到不独占文件;
+	BOOL LoadImgFromBuffer(IN Image** pImg, LPCTSTR lpPath)
+	{
+		if (!PathFileExists(lpPath))
+			return FALSE;
+
+		if (*pImg)
+			delete* pImg;
+		*pImg = NULL;
+
+		return LoadImgFromFile(pImg, lpPath);
+
+		CFile fp;
+		CFileException e;
+		BOOL bRet = FALSE;
+		if (fp.Open(lpPath, CFile::modeRead, &e))
+		{
+			DWORD dwLength = (DWORD)fp.GetLength();
+			BYTE* pData = new BYTE[dwLength];
+
+			fp.Read(pData, dwLength);
+			fp.Close();
+
+			bRet = LoadImgFromBuffer(pImg, pData, dwLength);
+
+			if (pData)
+				delete[]pData;
+		}
+
+		return bRet;
+	}
+
+	/************************************************************************/
+	/*  函数:LoadImgFromResource[9/21/2016 IT];
+	/*  描述:从资源中加载;
+	/*  参数:;
+	/*  	[IN] :;
+	/*  	[OUT] :;
+	/*  	[IN/OUT] :;
+	/*  返回:void;
+	/*  注意:;
+	/*  示例:;
+	/*
+	/*  修改:;
+	/*  日期:;
+	/*  内容:;
+	/************************************************************************/
+	Image* LoadImgFromResource(IN HMODULE hModule, IN LPCTSTR lpName, IN LPCTSTR lpType)
+	{
+		HGLOBAL	hGlobal = NULL;
+		HRSRC	hSource = NULL;
+		LPVOID  lpBuffer = NULL;
+		DWORD   dwSize = 0;
+		// 1.定位我们的自定义资源,这里因为我们是从本模块定位资源,所以将句柄简单地置为NULL即可
+		hSource = FindResource(hModule, lpName, lpType);
+		if (hSource == NULL)
+		{
+			_tprintf(_T("载入资源失败:%s"), lpName);
+			return NULL;
+		}
+
+		// 2.获取资源的大小;
+		dwSize = (UINT)SizeofResource(NULL, hSource);
+
+		// 3.加载资源;
+		hGlobal = LoadResource(NULL, hSource);
+		if (hGlobal == NULL)
+		{
+			_tprintf(_T("载入资源失败:%s"), lpName);
+			return NULL;
+		}
+
+		// 4.锁定资源,获取buffer;
+		lpBuffer = LockResource(hGlobal);
+		if (lpBuffer == NULL)
+		{
+			_tprintf(_T("载入资源失败:%s"), lpName);
+			return NULL;
+		}
+
+		// lpFileFullName需要先判断文件是否存在??不需要;
+		Image* pImg = NULL;
+		LoadImgFromBuffer(&pImg, (BYTE*)lpBuffer, dwSize);
+
+		UnlockResource(hGlobal);
+		FreeResource(hGlobal);
+
+		return pImg;
+	}
+
+	BOOL SaveImgByRotate(LPCTSTR lpszFileName, BYTE* pBuffer, const INT& nBufLen, BOOL bHoriontal, BOOL bVertically)
+	{
+		Image* pImg = NULL;
+		HGLOBAL hMemery = GlobalAlloc(GMEM_MOVEABLE, nBufLen);
+		if (hMemery == NULL)
+			return FALSE;
+
+		BYTE* pMem = NULL;
+		pMem = (BYTE*)GlobalLock(hMemery);
+		if (pMem == NULL)
+			return FALSE;
+		memcpy(pMem, pBuffer, nBufLen);
+
+		IStream* pstream = NULL;
+		HRESULT hr = CreateStreamOnHGlobal(hMemery, TRUE, &pstream);
+		if (pstream == NULL)
+			return FALSE;
+		pImg = Image::FromStream(pstream);
+		GlobalUnlock(hMemery);
+		pstream->Release();
+
+		if (bHoriontal && !bVertically)
+			pImg->RotateFlip(RotateNoneFlipX);// 水平翻转;
+		else if (bHoriontal && bVertically)
+			pImg->RotateFlip(Rotate180FlipNone);// 270度;
+		else if (!bHoriontal && bVertically)
+			pImg->RotateFlip(Rotate180FlipX);// 垂直翻转;
+
+		CString strNewfile = lpszFileName;
+		// 需要判断路径是否存在,不存在创建目录;
+		int nIndex = strNewfile.ReverseFind(_T('\\'));
+		if (nIndex == -1)
+			return FALSE;
+
+		if (!PathFileExists(strNewfile.Left(nIndex)))
+		{
+			// 如果文件夹不存在,创建;
+			SHCreateDirectoryEx(NULL, strNewfile.Left(nIndex), NULL);
+		}
+
+		nIndex = strNewfile.ReverseFind(_T('.'));
+		if (nIndex == -1)
+			return FALSE;
+
+		Status stat = GenericError;
+		CLSID encoderClsid = { 0 };
+		BSTR newfile = strNewfile.AllocSysString();
+
+		strNewfile = strNewfile.Mid(nIndex + 1);
+		if (strNewfile.CompareNoCase(_T("bmp")) == 0)
+		{
+			GetEncoderClsid(L"image/bmp", &encoderClsid);
+			stat = pImg->Save(newfile, &encoderClsid, NULL);
+		}
+		else if (strNewfile.CompareNoCase(_T("png")) == 0)
+		{
+			GetEncoderClsid(L"image/png", &encoderClsid);
+			stat = pImg->Save(newfile, &encoderClsid, NULL);
+		}
+		else// if ( strNewfile.CompareNoCase(_T("jpeg")) == 0 )
+		{
+			GetEncoderClsid(L"image/jpeg", &encoderClsid);
+
+			EncoderParameters encoderParameters;
+			encoderParameters.Count = 1;
+			encoderParameters.Parameter[0].Guid = EncoderQuality;
+			encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
+			encoderParameters.Parameter[0].NumberOfValues = 1;
+			// Save the image as a JPEG with quality level 100.
+			ULONG uQuality = 100;
+			encoderParameters.Parameter[0].Value = &uQuality;
+			stat = pImg->Save(newfile, &encoderClsid, &encoderParameters);
+		}
+
+		if (pImg)
+			delete pImg;
+		pImg = NULL;
+
+		SysFreeString(newfile);
+
+		return stat == Ok ? TRUE : FALSE;
+	}
+
+
+	void MKDIR(LPCTSTR dir)
+	{
+		//////////////////////////////////////////////////////////////////////////
+		// 创建目录;
+		int nleft = 0;
+		int nIndex = -1;
+		TString strdir = dir;
+
+		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 != TString::npos)
+			{
+				if (_tmkdir(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);
+	}
+
+	// hModule 模块句柄 NULL表示当前模块;
+	bool GetVersion(OUT WORD* pdwFileVersion, OUT WORD* pdwProductVerion)
+	{
+		TCHAR szFilePath[MAX_PATH] = { 0 };
+		DWORD dwRet = GetModuleFileName(NULL, szFilePath, MAX_PATH);
+		if (dwRet == 0)
+			return false;
+
+		VS_FIXEDFILEINFO* pVi = NULL;
+		DWORD dwHandle = 0;
+		int size = GetFileVersionInfoSize(szFilePath, &dwHandle);
+		if (size > 0)
+		{
+			BYTE* buffer = new BYTE[size];
+			memset(buffer, 0, size);
+
+			if (GetFileVersionInfo(szFilePath, dwHandle, 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;
+	}
+}

+ 87 - 0
SATHelper/SATHelper/Global.h

@@ -0,0 +1,87 @@
+
+#pragma once
+#include <stdio.h>
+#include <locale.h>
+#include <stdlib.h>
+//#include <time.h> //或者 #include <ctime>
+#include <ctime>
+#include <io.h>
+#pragma comment(lib,"version.lib")
+#include <vector>
+#include <string>
+// GDI+
+#include <gdiplus.h>
+using namespace Gdiplus;
+#pragma comment(lib, "gdiplus.lib")
+
+
+#ifndef _UNICODE 
+typedef string TString;
+#else
+typedef wstring TString;
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+// dll相关;
+extern HMODULE g_hdll;
+extern BOOL LoadLogLibarary();
+extern void FreeLogLibarary();
+
+typedef DWORD(*irc_IsAppRunning)(LPCTSTR lpszAppDir);
+typedef bool (*irc_StartIRApp)(LPCTSTR lpszAppDir, LPCTSTR lpSignalXml, DWORD dwPort);
+typedef bool (*irc_CloseApp)();
+typedef bool (*irc_Connect)(LPCTSTR lpServer, int port);
+typedef void (*irc_DisConnect)();
+typedef bool (*irc_loadSignalDataSet)(LPCTSTR lpDatasetXml);
+typedef LPCTSTR(*irc_getDeviceNames)();
+typedef LPCTSTR(*irc_getSignalsName)();
+typedef bool (*irc_sendSignal)(LPCTSTR lpSignal, int send_times, int sleep_time);
+typedef bool (*irc_sendSignals)(LPCTSTR lpSignals, int sleep_time);
+typedef bool (*irc_sendRepeatsSignal)(LPCTSTR lpSignal, int repeat_time);
+
+extern int g_nIRControl;
+extern irc_IsAppRunning g_IsAppRunning;
+extern irc_StartIRApp g_StartIRApp;
+extern irc_CloseApp g_CloseApp;
+extern irc_Connect g_Connect;
+extern irc_DisConnect g_DisConnect;
+extern irc_getDeviceNames g_getDeviceNames;
+extern irc_getSignalsName g_getSignalsName;
+extern irc_loadSignalDataSet g_loadSignalDataSet;
+extern irc_sendSignal g_sendSignal;
+extern irc_sendSignals g_sendSignals;
+extern irc_sendRepeatsSignal g_sendRepeatsSignal;
+//////////////////////////////////////////////////////////////////////////
+
+namespace Global
+{
+	typedef struct __ST_CONFIG__
+	{
+		std::string redratpath;
+		bool bGenerics;
+		std::string signaldir;
+		std::string use_signal;
+	}STConfig, * pSTConfig;
+
+	//////////////////////////////////////////////////////////////////////////
+	// 全局变量;
+	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 STConfig g_Config;
+	
+	//////////////////////////////////////////////////////////////////////////
+	// 全局函数;
+	void GetConfig();
+	int ReadReg(char* path, char* key, char* value);
+	void WriteTextLog(const TCHAR* format, ...);
+	BOOL LoadImgFromFile(IN Image** pImg, LPCTSTR lpPath);
+	BOOL LoadImgFromBuffer(IN Image** pImg, IN BYTE* pBuffer, IN CONST INT& nBufLen);
+	BOOL LoadImgFromBuffer(IN Image** pImg, LPCTSTR lpPath);
+	int GetEncoderClsid(IN CONST WCHAR* format, OUT CLSID* pClsid);
+	Image* LoadImgFromResource(IN HMODULE hModule, IN LPCTSTR lpName, IN LPCTSTR lpType);
+	BOOL SaveImgByRotate(LPCTSTR lpszFileName, BYTE* pBuffer, CONST INT& nBufLen, BOOL bHoriontal, BOOL bVertically);
+	void MKDIR(LPCTSTR dir);
+	bool GetVersion(OUT WORD* pdwFileVersion, OUT WORD* pdwProductVerion);
+};

+ 1007 - 0
SATHelper/SATHelper/IOCPModel.cpp

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

+ 294 - 0
SATHelper/SATHelper/IOCPModel.h

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

+ 44 - 0
SATHelper/SATHelper/MainFrm.cpp

@@ -69,6 +69,12 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 	m_wndRibbonBar.Create(this);
 	m_wndRibbonBar.LoadFromResource(IDR_RIBBON);
 
+#if !__QUICKACCESS_COMMAND__
+	// 添加以下代码段,禁用快速启动栏;
+	CMFCRibbonQuickAccessToolBar* pQAToolbar = m_wndRibbonBar.GetQuickAccessToolbar();
+	pQAToolbar->RemoveAll();
+#endif
+
 	if (!m_wndStatusBar.Create(this))
 	{
 		TRACE0("未能创建状态栏\n");
@@ -89,6 +95,7 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 	// 启用 Visual Studio 2005 样式停靠窗口自动隐藏行为
 	EnableAutoHidePanes(CBRS_ALIGN_ANY);
 
+#if __OUTLOOKBAR__
 	// 导航窗格将创建在左侧,因此将暂时禁用左侧的停靠: 
 	EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_RIGHT);
 
@@ -98,21 +105,27 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 		TRACE0("未能创建导航窗格\n");
 		return -1;      // 未能创建
 	}
+#endif
 
+#if __CAPTIONBAR__
 	// 创建标题栏: 
 	if (!CreateCaptionBar())
 	{
 		TRACE0("未能创建标题栏\n");
 		return -1;      // 未能创建
 	}
+#endif
 
+#if __OUTLOOKBAR__
 	// 已创建 Outlook 栏,应允许在左侧停靠。
 	EnableDocking(CBRS_ALIGN_LEFT);
 	EnableAutoHidePanes(CBRS_ALIGN_RIGHT);
+#endif
 
 	// 加载菜单项图像(不在任何标准工具栏上): 
 	CMFCToolBar::AddToolBarForImageCollection(IDR_MENU_IMAGES, theApp.m_bHiColorIcons ? IDB_MENU_IMAGES_24 : 0);
 
+#if __DOCKINGBAR__
 	// 创建停靠窗口
 	if (!CreateDockingWindows())
 	{
@@ -120,15 +133,20 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 		return -1;
 	}
 
+#if __FC_WIN__
 	m_wndFileView.EnableDocking(CBRS_ALIGN_ANY);
 	m_wndClassView.EnableDocking(CBRS_ALIGN_ANY);
 	DockPane(&m_wndFileView);
 	CDockablePane* pTabbedBar = nullptr;
 	m_wndClassView.AttachToTabWnd(&m_wndFileView, DM_SHOW, TRUE, &pTabbedBar);
+#endif
+#if __OUT_PUT_WIN__
 	m_wndOutput.EnableDocking(CBRS_ALIGN_ANY);
 	DockPane(&m_wndOutput);
+#endif
 	m_wndProperties.EnableDocking(CBRS_ALIGN_ANY);
 	DockPane(&m_wndProperties);
+#endif
 
 	// 基于持久值设置视觉管理器和样式
 	OnApplicationLook(theApp.m_nAppLook);
@@ -155,10 +173,12 @@ BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
 	return TRUE;
 }
 
+#if __DOCKINGBAR__
 BOOL CMainFrame::CreateDockingWindows()
 {
 	BOOL bNameValid;
 
+#if __FC_WIN__
 	// 创建类视图
 	CString strClassView;
 	bNameValid = strClassView.LoadString(IDS_CLASS_VIEW);
@@ -178,7 +198,9 @@ BOOL CMainFrame::CreateDockingWindows()
 		TRACE0("未能创建“文件视图”窗口\n");
 		return FALSE; // 未能创建
 	}
+#endif 
 
+#if __OUT_PUT_WIN__
 	// 创建输出窗口
 	CString strOutputWnd;
 	bNameValid = strOutputWnd.LoadString(IDS_OUTPUT_WND);
@@ -188,6 +210,7 @@ BOOL CMainFrame::CreateDockingWindows()
 		TRACE0("未能创建输出窗口\n");
 		return FALSE; // 未能创建
 	}
+#endif
 
 	// 创建属性窗口
 	CString strPropertiesWnd;
@@ -205,14 +228,18 @@ BOOL CMainFrame::CreateDockingWindows()
 
 void CMainFrame::SetDockingWindowIcons(BOOL bHiColorIcons)
 {
+#if __FC_WIN__
 	HICON hFileViewIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_FILE_VIEW_HC : IDI_FILE_VIEW), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
 	m_wndFileView.SetIcon(hFileViewIcon, FALSE);
 
 	HICON hClassViewIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_CLASS_VIEW_HC : IDI_CLASS_VIEW), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
 	m_wndClassView.SetIcon(hClassViewIcon, FALSE);
+#endif
 
+#if __OUT_PUT_WIN__
 	HICON hOutputBarIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_OUTPUT_WND_HC : IDI_OUTPUT_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
 	m_wndOutput.SetIcon(hOutputBarIcon, FALSE);
+#endif
 
 	HICON hPropertiesBarIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_PROPERTIES_WND_HC : IDI_PROPERTIES_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
 	m_wndProperties.SetIcon(hPropertiesBarIcon, FALSE);
@@ -221,7 +248,9 @@ void CMainFrame::SetDockingWindowIcons(BOOL bHiColorIcons)
 	UpdateMDITabbedBarsIcons();
 #endif
 }
+#endif
 
+#if __OUTLOOKBAR__
 BOOL CMainFrame::CreateOutlookBar(CMFCOutlookBar& bar, UINT uiID, CMFCShellTreeCtrl& tree, CCalendarBar& calendar, int nInitialWidth)
 {
 	bar.SetMode2003();
@@ -276,7 +305,9 @@ BOOL CMainFrame::CreateOutlookBar(CMFCOutlookBar& bar, UINT uiID, CMFCShellTreeC
 
 	return TRUE;
 }
+#endif
 
+#if __CAPTIONBAR__
 BOOL CMainFrame::CreateCaptionBar()
 {
 	if (!m_wndCaptionBar.Create(WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, this, ID_VIEW_CAPTION_BAR, -1, TRUE))
@@ -308,6 +339,7 @@ BOOL CMainFrame::CreateCaptionBar()
 
 	return TRUE;
 }
+#endif
 
 // CMainFrame 诊断
 
@@ -404,7 +436,11 @@ void CMainFrame::OnApplicationLook(UINT id)
 		m_wndRibbonBar.SetWindows7Look(FALSE);
 	}
 
+#if __DOCKINGBAR__
+#if __OUT_PUT_WIN__
 	m_wndOutput.UpdateFonts();
+#endif
+#endif
 	RedrawWindow(nullptr, nullptr, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW | RDW_FRAME | RDW_ERASE);
 
 	theApp.WriteInt(_T("ApplicationLook"), theApp.m_nAppLook);
@@ -417,13 +453,17 @@ void CMainFrame::OnUpdateApplicationLook(CCmdUI* pCmdUI)
 
 void CMainFrame::OnViewCaptionBar()
 {
+#if __CAPTIONBAR__
 	m_wndCaptionBar.ShowWindow(m_wndCaptionBar.IsVisible() ? SW_HIDE : SW_SHOW);
 	RecalcLayout(FALSE);
+#endif
 }
 
 void CMainFrame::OnUpdateViewCaptionBar(CCmdUI* pCmdUI)
 {
+#if __CAPTIONBAR__
 	pCmdUI->SetCheck(m_wndCaptionBar.IsVisible());
+#endif
 }
 
 void CMainFrame::OnOptions()
@@ -439,5 +479,9 @@ void CMainFrame::OnOptions()
 void CMainFrame::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
 {
 	CMDIFrameWndEx::OnSettingChange(uFlags, lpszSection);
+#if __DOCKINGBAR__
+#if __OUT_PUT_WIN__
 	m_wndOutput.UpdateFonts();
+#endif
+#endif
 }

+ 20 - 0
SATHelper/SATHelper/MainFrm.h

@@ -55,14 +55,26 @@ protected:  // 控件条嵌入成员
 	CMFCRibbonApplicationButton m_MainButton;
 	CMFCToolBarImages m_PanelImages;
 	CMFCRibbonStatusBar  m_wndStatusBar;
+#if __DOCKINGBAR__
+#if __FC_WIN__
 	CFileView         m_wndFileView;
 	CClassView        m_wndClassView;
+#endif
+#if __OUT_PUT_WIN__
 	COutputWnd        m_wndOutput;
+#endif
 	CPropertiesWnd    m_wndProperties;
+#endif
+
+#if __OUTLOOKBAR__
 	COutlookBar       m_wndNavigationBar;
 	CMFCShellTreeCtrl m_wndTree;
 	CCalendarBar      m_wndCalendar;
+#endif
+
+#if __CAPTIONBAR__
 	CMFCCaptionBar    m_wndCaptionBar;
+#endif
 
 // 生成的消息映射函数
 protected:
@@ -76,10 +88,18 @@ protected:
 	afx_msg void OnSettingChange(UINT uFlags, LPCTSTR lpszSection);
 	DECLARE_MESSAGE_MAP()
 
+#if __DOCKINGBAR__
 	BOOL CreateDockingWindows();
 	void SetDockingWindowIcons(BOOL bHiColorIcons);
+#endif
+
+#if __OUTLOOKBAR__
 	BOOL CreateOutlookBar(CMFCOutlookBar& bar, UINT uiID, CMFCShellTreeCtrl& tree, CCalendarBar& calendar, int nInitialWidth);
+#endif
+
+#if __CAPTIONBAR__
 	BOOL CreateCaptionBar();
+#endif
 
 	int FindFocusedOutlookWnd(CMFCOutlookBarTabCtrl** ppOutlookWnd);
 

+ 338 - 0
SATHelper/SATHelper/MemoryClient.cpp

@@ -0,0 +1,338 @@
+#include "stdafx.h"
+#include "MemoryClient.h"
+#include "resource.h"
+#include <Shlwapi.h>
+#include "Global.h"
+#include "MainFrm.h"
+
+CVideoCaptureView* CMemoryClient::m_pView = NULL;
+HANDLE CMemoryClient::m_hRecordFile = NULL;
+HANDLE CMemoryClient::m_hThreadAudio = NULL;
+BOOL CMemoryClient::m_bCapture = FALSE;
+BOOL CMemoryClient::m_bIsConnect = FALSE;
+
+CMemoryClient::CMemoryClient(void)
+{
+	m_hThread = NULL;
+	m_hEvent = NULL;
+}
+
+CMemoryClient::~CMemoryClient(void)
+{
+	EndOfThread();
+}
+
+BOOL CMemoryClient::StartThread()
+{
+	// 创建事件对象;
+	if ( m_hEvent == NULL )
+		m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+	if ( m_hEvent == NULL )
+		return FALSE;
+
+	// 创建线程;
+	if ( m_hThread == NULL )
+	{
+		m_hThread = CreateThread(NULL, 0, ThreadProc, this, 0, NULL);
+		if ( m_hThread == NULL )
+		{
+			CloseHandle(m_hEvent);
+			m_hEvent = NULL;
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+void CMemoryClient::EndOfThread()
+{
+	if ( m_hEvent )
+		SetEvent(m_hEvent);
+
+	if ( m_hThread )
+		WaitForSingleObject(m_hThread, INFINITE);
+
+	if (m_hThread) CloseHandle(m_hThread);
+	m_hThread = NULL;
+	if (m_hEvent) CloseHandle(m_hEvent);
+	m_hEvent = NULL;
+}
+
+DWORD CMemoryClient::ThreadProc(LPVOID lpVoid)
+{
+	CMemoryClient *pThis = (CMemoryClient*)lpVoid;
+	if ( pThis )
+	{
+		CommandHead cmdHead;
+		do 
+		{
+			// 读取内存信息;
+			pThis->Lock(INFINITE);
+			// 解析命令;
+			memcpy(&cmdHead, pThis->m_pMemory, sizeof(CommandHead));
+			// 获取设置命令;
+			if ( cmdHead.cmdFlag == 0x7F && cmdHead.cmdUser == TRUE )
+			{
+				switch( cmdHead.cmdType )
+				{
+					// 显示窗口;
+				case SHOW_APP:
+				{
+					CMainFrame* pFrameWnd = (CMainFrame*)m_pView->GetParentFrame();
+					pFrameWnd->ShowWindow(SW_SHOWNORMAL);
+					pFrameWnd->m_bNoticeTray = TRUE;
+					memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+				}
+					break;
+					// 隐藏窗口;
+				case HIDE_APP:
+				{
+					CMainFrame* pFrameWnd = (CMainFrame*)m_pView->GetParentFrame();
+					pFrameWnd->ShowWindow(SW_HIDE);
+					pFrameWnd->m_bNoticeTray = FALSE;
+					memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+				}
+					break;
+					// 连接设备;
+				case CONNECT_DEVICE:
+					{
+						CMD_Result result;
+						unsigned short nIndex = 0;
+						memcpy(&nIndex, ((BYTE*)pThis->m_pMemory) + sizeof(CommandHead), sizeof(unsigned short));
+						result.bResult = m_pView->HwInitialize();
+
+						// 返回结果;						
+						result.cmdHead = cmdHead;
+						result.cmdHead.cmdUser = FALSE;
+						memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+						memcpy(pThis->m_pMemory, &result, sizeof(CMD_Result));
+					}
+					break;
+					// 断开设备;
+				case DIS_CONNECT_DEVICE:
+					{
+						CMD_Result result;
+						unsigned short nIndex = 0;
+						memcpy(&nIndex, ((BYTE*)pThis->m_pMemory) + sizeof(CommandHead), sizeof(unsigned short));
+						result.bResult = m_pView->HwUninitialize();
+						m_pView->Invalidate();
+
+						// 返回结果;						
+						result.cmdHead = cmdHead;
+						result.cmdHead.cmdUser = FALSE;
+						memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+						memcpy(pThis->m_pMemory, &result, sizeof(CMD_Result));
+					}
+					break;
+					// 开始、停止流操作;
+				case START_STREAMING:
+				case STOP_STREAMING:
+					{
+						CMD_Result result;
+						BOOL bStartStreaming = TRUE;
+						memcpy(&bStartStreaming, ((BYTE*)pThis->m_pMemory) + sizeof(CommandHead), sizeof(BOOL));
+						if (bStartStreaming)
+							result.bResult = Startstreaming();
+						else
+							result.bResult = Stoptstreaming();
+
+						// 返回结果;
+						result.cmdHead = cmdHead;
+						result.cmdHead.cmdUser = FALSE;
+						memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+						memcpy(pThis->m_pMemory, &result, sizeof(CMD_Result));
+					}
+					break;
+					// 截图;
+				case CAPTURE_IMAGE_COUNT:
+				case CAPTURE_IMAGE_TIME:
+				case CAPTURE_IMAGE_SINGLE:
+					{
+						CMD_Result result;
+						CMD_CaputerImage cmd;
+						memcpy(&cmd, pThis->m_pMemory, sizeof(CMD_CaputerImage));
+						result.bResult = CaptureImage(cmd, cmd.bSingle = (cmdHead.cmdType == CAPTURE_IMAGE_SINGLE));
+
+						// 返回结果;
+						result.cmdHead = cmdHead;
+						result.cmdHead.cmdUser = FALSE;
+						memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+						memcpy(pThis->m_pMemory, &result, sizeof(CMD_Result));
+					}
+					break;
+					// 停止截图;
+				case STOP_CAPTUREIMAGE:
+					{// 停止截图;
+						memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+					}
+					break;
+					// 同步录频;
+				case SYN_CAPTURE_AUDIO:
+					{
+						CMD_Result result;
+						CMD_CaputerAudio cmd;
+						memcpy(&cmd, pThis->m_pMemory, sizeof(CMD_CaputerAudio));
+						
+						result.bResult = SynCaptureAudio(cmd);
+
+						// 返回结果;
+						result.cmdHead = cmdHead;
+						result.cmdHead.cmdUser = FALSE;
+						memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+						memcpy(pThis->m_pMemory, &result, sizeof(CMD_Result));						
+					}
+					break;
+					// 异步录频;
+				case ASY_CAPTURE_AUDIO:
+					{
+						CMD_Result result;
+						CMD_CaputerAudio cmd;
+						memcpy(&cmd, pThis->m_pMemory, sizeof(CMD_CaputerAudio));
+
+						result.bResult = AsyCaptureAudio(cmd);
+
+						// 返回结果;
+						result.cmdHead = cmdHead;
+						result.cmdHead.cmdUser = FALSE;
+						memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+						memcpy(pThis->m_pMemory, &result, sizeof(CMD_Result));						
+					}
+					break;
+					// 停止录屏;
+				case STOP_CAPTUREAUDIO:
+					if (m_hRecordFile != NULL)
+					{
+						CMD_Result result;
+						CMD_CaputerAudio cmd;
+						memcpy(&cmd, pThis->m_pMemory, sizeof(CMD_CaputerAudio));
+
+						result.bResult = StopCaptureAudio();
+
+						// 返回结果;
+						result.cmdHead = cmdHead;
+						result.cmdHead.cmdUser = FALSE;
+						memset(pThis->m_pMemory, 0, sizeof(MEMERY_SIZE));
+						memcpy(pThis->m_pMemory, &result, sizeof(CMD_Result));
+					}
+					break;
+				default:
+					break;
+				}
+			}
+			pThis->Unlock();
+		} while ( WaitForSingleObject(pThis->m_hEvent, 10) == WAIT_TIMEOUT);
+	}
+
+	return 0L;
+}
+
+// 异步线程;
+DWORD CMemoryClient::ThreadAsyAudio(LPVOID lpVoid)
+{
+	DWORD dwDuration = *(DWORD*)lpVoid;
+	
+	return 0L;
+}
+
+
+BOOL CMemoryClient::Startstreaming()
+{
+	
+	return TRUE;
+}
+
+BOOL CMemoryClient::Stoptstreaming()
+{
+
+	return TRUE;
+}
+
+BOOL CMemoryClient::CaptureImage(const CMD_CaputerImage& cmd, BOOL bSingle)
+{
+	BOOL bIsJPG = FALSE;
+	TString strFilePath = cmd.szSaveDir;
+	if ( cmd.dwImageType == 2 )
+		bIsJPG = TRUE;
+
+	if ( !bSingle )
+	{// 不是单张;
+		m_bCapture = TRUE;
+		if (!PathFileExists(cmd.szSaveDir))
+		{// 目录不存在,创建;
+			Global::MKDIR(cmd.szSaveDir);
+			if ( !PathFileExists(cmd.szSaveDir) )
+			{
+				// 创建目录失败,返回结果;
+				m_bCapture = FALSE;
+				return FALSE;
+			}
+		}
+
+		m_pView->CaptureMultiImageEx(cmd.szSaveDir, cmd.szPrefix, bIsJPG, cmd.nKeepTime);
+	}
+	else
+	{// 单张;
+		TString strFile = cmd.szSaveDir;
+		int nIndex = strFile.find_last_of(_T('\\'));
+		if ( TString::npos != nIndex )
+		{
+			Global::MKDIR(strFile.substr(0, nIndex).c_str());
+			if (!PathFileExists(strFile.substr(0, nIndex).c_str()))
+				return FALSE;
+		}
+		else
+		{
+			return FALSE;
+		}
+
+		if (cmd.IsAutoName)
+		{
+			nIndex = strFile.find_last_of(_T('.'));
+			if (nIndex != TString::npos)
+			{
+				nIndex = strFile.find_last_of(_T('\\'));
+				if (nIndex != TString::npos)
+					strFile = strFile.substr(0, nIndex + 1);
+				else
+					strFile.append(_T("\\"));
+			}
+			else
+			{
+				if (strFile.at(strFile.length() - 1) != _T('\\'))
+					strFile.append(_T("\\"));
+			}
+
+			m_pView->CaptureSingleImageAutoNameEx(strFile.c_str(), bIsJPG);
+		}
+		else
+		{
+			TCHAR szFile[MAX_PATH] = { 0 };
+			_stprintf_s(szFile, _T("%s"), cmd.szSaveDir);
+			m_pView->CaptureSingleImageEx(szFile, bIsJPG);
+		}
+		// 等待磁盘完成写入;
+		//Sleep(bIsJPG ? 200 : 200);
+	}
+
+	return TRUE;
+}
+
+BOOL CMemoryClient::SynCaptureAudio(const CMD_CaputerAudio& cmd)
+{
+
+	return TRUE;
+}
+
+BOOL CMemoryClient::AsyCaptureAudio(const CMD_CaputerAudio& cmd)
+{
+	m_pView->StartRecord(cmd.dwDuration, cmd.szSaveDir);
+	return TRUE;
+}
+
+BOOL CMemoryClient::StopCaptureAudio()
+{
+	m_pView->StopRecord();
+	return TRUE;
+}

+ 41 - 0
SATHelper/SATHelper/MemoryClient.h

@@ -0,0 +1,41 @@
+#ifndef __CD750_CLIENT__
+#define __CD750_CLIENT__
+
+#pragma once
+
+#include "MemoryDef.h"
+#include "MemoryComm.h"
+#include "VideoCaptureView.h"
+ class CVideoCaptureView;
+
+class CMemoryClient:public CMemoryComm
+{
+public:
+	CMemoryClient(void);
+	~CMemoryClient(void);
+
+private:
+	HANDLE m_hThread;
+	HANDLE m_hEvent;
+	static BOOL m_bCapture;
+	static BOOL m_bIsConnect;
+	static HANDLE m_hThreadAudio;
+public:
+	static CVideoCaptureView* m_pView;
+	static HANDLE m_hRecordFile;
+	BOOL StartThread();
+	void EndOfThread();
+	static DWORD WINAPI ThreadProc(LPVOID lpVoid);
+	static DWORD WINAPI ThreadAsyAudio(LPVOID lpVoid);
+	//static BOOL WINAPI NotifyEventCallback(DWORD dwEventCode, LPVOID lpEventData, LPVOID lpUserData);
+	//static void ModifyName(const CAPTUREIMAGE_NOTIFY_INFO* pCapImageNotifyInfo);
+private:
+	static BOOL CaptureImage(const CMD_CaputerImage& cmd, BOOL bSingle = TRUE);
+	static BOOL SynCaptureAudio(const CMD_CaputerAudio& cmd);
+	static BOOL AsyCaptureAudio(const CMD_CaputerAudio& cmd);
+	static BOOL StopCaptureAudio();
+	static BOOL Startstreaming();
+	static BOOL Stoptstreaming();
+};
+
+#endif // __CD750_CLIENT__

+ 139 - 0
SATHelper/SATHelper/MemoryComm.cpp

@@ -0,0 +1,139 @@
+#include "stdafx.h"
+#include "MemoryComm.h"
+#include "MemoryDef.h"
+
+CMemoryComm::CMemoryComm(void) :m_hLock(NULL),
+m_hFileMap(NULL),
+m_pMemory(NULL)
+{
+
+}
+
+CMemoryComm::~CMemoryComm(void)
+{
+	Close();
+	if (m_hLock != NULL)
+	{
+		CloseHandle(m_hLock);
+	}
+}
+
+
+CMemoryComm::CMemoryComm(const CMemoryComm& other)
+{
+	//if ( this != other)
+	//{
+	//	m_hLock = other.m_hLock;
+	//	m_hFileMap = other.m_hFileMap;
+	//	m_pMemory = other.m_pMemory;
+	//}
+}
+
+CMemoryComm& CMemoryComm::operator=(const CMemoryComm& other)
+{
+	// TODO: 在此处插入 return 语句
+	//if (this != other)
+	//{
+	//	m_hLock = other.m_hLock;
+	//	m_hFileMap = other.m_hFileMap;
+	//	m_pMemory = other.m_pMemory;
+	//}
+
+	return *this;
+}
+
+
+void CMemoryComm::Unmap()
+{
+	if (m_pMemory)
+	{
+		::UnmapViewOfFile(m_pMemory);
+		m_pMemory = NULL;
+	}
+}
+
+
+// 锁定共享内存
+BOOL CMemoryComm::Lock(DWORD dwTime)
+{
+	// 如果还没有创建锁就先创建一个
+	if (!m_hLock)
+	{
+		// 初始化的时候不被任何线程占用
+		m_hLock = ::CreateMutex(NULL, FALSE, MEMERY_NAME);
+		if (!m_hLock)
+			return FALSE;
+	}
+
+	// 哪个线程最先调用等待函数就最先占用这个互斥量
+	DWORD dwRet = ::WaitForSingleObject(m_hLock, dwTime);
+	return (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_ABANDONED);
+}
+
+void CMemoryComm::Unlock()
+{
+	if (m_hLock)
+	{
+		::ReleaseMutex(m_hLock);
+	}
+}
+
+void CMemoryComm::Close()
+{
+	Unmap();
+
+	if (m_hFileMap)
+	{
+		::CloseHandle(m_hFileMap);
+		m_hFileMap = NULL;
+	}
+}
+
+/************************************************************************/
+/*  函数:[7/3/2018 Wang];
+/*  描述:初始化共享内存,若已创建内存则打开;
+/*  参数:;
+/*  	[IN] :;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+BOOL CMemoryComm::InitMemery(DWORD dwOffset /* = 0*/)
+{
+	if (m_hFileMap == NULL)
+	{
+		// 创建共享内存;
+		m_hFileMap = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MEMERY_SIZE, MEMERY_NAME);
+		if (ERROR_ALREADY_EXISTS == GetLastError())
+		{
+			CloseHandle(m_hFileMap);
+			m_hFileMap = NULL;
+
+			// 打开共享内存;
+			m_hFileMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, MEMERY_NAME);
+			if (!m_hFileMap)
+				return FALSE;
+		}
+	}
+
+	// 将内存映射出来;
+	if (m_pMemory == NULL)
+	{
+		ULARGE_INTEGER ui;
+		ui.QuadPart = static_cast<ULONGLONG>(dwOffset);
+		m_pMemory = ::MapViewOfFile(m_hFileMap, FILE_MAP_ALL_ACCESS, ui.HighPart, ui.LowPart, MEMERY_SIZE);
+		if (m_pMemory == NULL)
+		{
+			//ShowSystemErrorInfo(_T("映射内存失败"), GetLastError());
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}

+ 30 - 0
SATHelper/SATHelper/MemoryComm.h

@@ -0,0 +1,30 @@
+#ifndef __MEMORY_COMM_HEADER__
+#define __MEMORY_COMM_HEADER__
+
+#pragma once
+
+class CMemoryComm
+{
+public:
+	CMemoryComm(void);
+	~CMemoryComm(void);
+
+protected:
+	HANDLE m_hLock;
+	HANDLE m_hFileMap;
+	LPVOID m_pMemory;
+
+	CMemoryComm(const CMemoryComm& other);
+	CMemoryComm& operator = (const CMemoryComm& other);
+
+public:
+	BOOL InitMemery(DWORD dwOffset = 0);
+	void Unmap();
+	LPVOID GetMemory() const { return m_pMemory; }
+	HANDLE GetHandle() const { return m_hFileMap; }
+	BOOL Lock(DWORD dwTime);
+	void Unlock();
+	void Close();
+};
+
+#endif

+ 134 - 0
SATHelper/SATHelper/MemoryDef.h

@@ -0,0 +1,134 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [Wang], 保留所有权利;
+/*  模 块 名:内存共享C/S通信协议;
+/*  描    述:;
+/*
+/*  版    本:[V];	
+/*  作    者:[Wang];
+/*  日    期:[7/4/2018];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[Wang];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+#ifndef __CD750_PROTO__
+#define __CD750_PROTO__
+
+// 命令类型;
+enum CommandType {
+	SHOW_APP,
+	HIDE_APP,
+	CONNECT_DEVICE,
+	DIS_CONNECT_DEVICE,
+	START_STREAMING,
+	STOP_STREAMING,
+	STOP_CAPTUREIMAGE,
+	CAPTURE_IMAGE_COUNT,
+	CAPTURE_IMAGE_TIME,
+	CAPTURE_IMAGE_SINGLE,
+	STOP_CAPTUREAUDIO,
+	SYN_CAPTURE_AUDIO,
+	ASY_CAPTURE_AUDIO
+};
+
+// 命令头;
+typedef struct COMMANDHAED{
+	unsigned short cmdFlag;		// 命令标识符;//固定:0x7F;
+	unsigned short cmdType;		// 命令类型;
+	BOOL  cmdUser;				// 命令者:C端=false,S端=true;
+	DWORD cmdCRC32;				// 对具体命令的crc32校验;//校验耗时,暂不做;
+
+	COMMANDHAED()
+	{
+		cmdFlag = 0x7F;
+		cmdType = 0;
+		cmdUser = TRUE;
+		cmdCRC32 = 0;
+	}
+	// 拷贝构造函数;
+	COMMANDHAED(const COMMANDHAED& other)
+	{
+		if (this != &other)
+		{
+			cmdFlag = other.cmdFlag;
+			cmdType = other.cmdType;
+			cmdUser = other.cmdUser;
+			cmdCRC32 = other.cmdCRC32;
+		}
+	}
+
+	// 赋值重载函数;
+	COMMANDHAED& operator = (const COMMANDHAED& other)
+	{
+		if (this != &other)
+		{
+			cmdFlag = other.cmdFlag;
+			cmdType = other.cmdType;
+			cmdUser = other.cmdUser;
+			cmdCRC32 = other.cmdCRC32;
+		}
+
+		return *this;
+	}
+}CommandHead,*pCommandHead;
+
+// 连接设备;
+typedef struct CMD_CONECTDEVICE {
+	CommandHead cmdHead;
+	unsigned short nIndex;		// 设备索引;
+}CMD_ConnectDevice, *pCMD_ConnectDevice;
+
+// 截图;
+typedef struct CMD_CAPUTERIMAGE{
+	// 命令头;
+	CommandHead cmdHead;
+	// 图片格式;
+	unsigned short dwImageType;
+	// 持续方式;//false=以张数为单位; true以秒为单位;
+	BOOL bContinuType;	
+	// 持续时间;
+	int nKeepTime;
+	// 每秒抓取张数;
+	int nCaputerCount;
+	// 保存路径;
+	TCHAR szSaveDir[MAX_PATH];
+	// 文件名前缀;
+	TCHAR szPrefix[64];
+	// 是否自动命名;
+	BOOL IsAutoName;
+	// 是否单张截图;
+	BOOL bSingle;
+}CMD_CaputerImage,*pCMD_CaputerImage;
+
+// 视屏保存;
+typedef struct CMD_CAPUTERAUDIO {
+	// 命令头;
+	CommandHead cmdHead;
+	// 录制时长//单位毫秒;
+	DWORD dwDuration;
+	// 保存路径;
+	TCHAR szSaveDir[MAX_PATH];
+}CMD_CaputerAudio,*pCMD_CaputerAudio;
+
+// 开始/停止流命令;
+typedef struct CMD_STREAMOPT{
+	CommandHead cmdHead;
+	BOOL bStartStreaming;
+}CMD_StreamOpt, *pCMD_StreamOpt;
+
+// 返回结果;
+typedef struct CMD_RESULT{
+	CommandHead cmdHead;
+	BOOL bResult;
+}CMD_Result,*pCMD_Result;
+
+// 共享内存名称;
+#define MEMERY_NAME _T("UB530#TCL#ShareMemery")
+// 共享内存的大小;
+#define MEMERY_SIZE 1024*8	//8k;
+
+#endif // __CD750_PROTO__

+ 2837 - 0
SATHelper/SATHelper/QCAP.H

@@ -0,0 +1,2837 @@
+#ifndef QCAP_H
+#define QCAP_H
+
+#ifdef BUILD_QCAP_DLL
+
+#define QCAP_EXT_API __declspec(dllexport) 
+
+#else 
+
+#define QCAP_EXT_API __declspec(dllimport) 
+#endif
+
+#ifdef _QCAP_VB_EXPORT
+
+#define QCAP_EXPORT __stdcall
+
+#else // _QCAP_VB_EXPORT
+
+#define QCAP_EXPORT __cdecl
+
+#endif // _QCAP_VB_EXPORT
+
+// LABVIEW 2010/2012 SUPPORT
+// 
+#ifdef _QCAP_LABVIEW_IMPORT
+
+#define QCAP_EXT_API __declspec(dllimport)
+
+#define QCAP_EXPORT __cdecl
+
+typedef void VOID;
+
+typedef void * PVOID;
+ 
+typedef unsigned int UINT;
+
+typedef unsigned long ULONG;
+
+typedef unsigned long DWORD;
+
+typedef unsigned long long ULONGLONG;
+
+typedef char CHAR;
+
+typedef unsigned char BYTE;
+
+#endif // _QCAP_LABVIEW_IMPORT
+
+typedef unsigned short * WSTRING;
+
+// RESULT OF FUNCTION
+//
+typedef enum {
+
+	QCAP_RS_SUCCESSFUL								= 0x00000000,
+
+	QCAP_RS_ERROR_GENERAL							= 0x00000001,
+
+	QCAP_RS_ERROR_OUT_OF_MEMORY						= 0x00000002,
+
+	QCAP_RS_ERROR_OUT_OF_RESOURCE					= 0x00000003,
+
+	QCAP_RS_ERROR_INVALID_DEVICE					= 0x00000004,
+
+	QCAP_RS_ERROR_INVALID_PARAMETER					= 0x00000005,
+
+	QCAP_RS_ERROR_NON_SUPPORT						= 0x00000006,
+
+	QCAP_RS_ERROR_TIMEOUT							= 0x00000007,
+
+	QCAP_RS_ERROR_INVALID_ANIMATION_SCRIPT			= 0x00000008,
+
+	QCAP_RS_ERROR_NO_SIGNAL_DETECTED				= 0x00000009,
+
+	QCAP_RS_ERROR_NEED_MORE_DATA					= 0x0000000A,
+
+	QCAP_RS_ERROR_CONNECT_FAIL						= 0x0000000B,
+
+	QCAP_RS_ERROR_FILE_ACCESS_FAIL					= 0x0000000C,
+
+	QCAP_RS_ERROR_NETWORK_ACCESS_FAIL				= 0x0000000D,
+
+	QCAP_RS_ERROR_FILE_IS_BOX_MOVED					= 0x0000000E,
+
+	QCAP_RS_ERROR_FRAME_IS_COPIED					= 0x0000000F,
+
+	QCAP_RS_ERROR_VIDEO_ENCODER_ACCESS_FAIL			= 0x00000010,
+
+	QCAP_RS_ERROR_AUDIO_ENCODER_ACCESS_FAIL			= 0x00000011,
+
+	QCAP_RS_ERROR_NETWORK_CONNECT_UNKNOWN_FAIL		= 0x80000000,
+
+	QCAP_RS_ERROR_NETWORK_CONNECT_URL_FAIL			= 0x80000001,
+
+	QCAP_RS_ERROR_NETWORK_CONNECT_PASSWORD_FAIL		= 0x80000002,
+
+	QCAP_RS_ERROR_NETWORK_CONNECT_SESSION_NAME_FAIL = 0x80000003,
+
+} QRESULT;
+
+// RETURN OF CALLBACK
+//
+typedef enum {
+
+	QCAP_RT_OK					= 0x00000000,
+	
+	QCAP_RT_FAIL				= 0x00000001,
+
+	QCAP_RT_SKIP_RECORD_NUM_00	= 0x00010002, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+	QCAP_RT_SKIP_RECORD_NUM_01	= 0x00020002, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+	QCAP_RT_SKIP_RECORD_NUM_02	= 0x00040002, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+	QCAP_RT_SKIP_RECORD_NUM_03	= 0x00080002, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+	QCAP_RT_SKIP_DISPLAY		= 0x00000004, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK / 
+											  //
+											  //		  PF_VIDEO_BROADCAST_CLIENT_CALLBACK / PF_AUDIO_BROADCAST_CLIENT_CALLBACK / 
+											  //
+											  //		  PF_VIDEO_DECODER_BROADCAST_CLIENT_CALLBACK / PF_AUDIO_DECODER_BROADCAST_CLIENT_CALLBACK /
+											  //
+											  //		  PF_VIDEO_DECODER_FILE_CALLBACK / PF_AUDIO_DECODER_FILE_CALLBACK
+											  
+	QCAP_RT_SKIP_CLONE_DISPLAY	= 0x00000008, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+	QCAP_RT_RESET_RECORD_NUM_00	= 0x01000010, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+	QCAP_RT_RESET_RECORD_NUM_01	= 0x02000010, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+	QCAP_RT_RESET_RECORD_NUM_02	= 0x04000010, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+	QCAP_RT_RESET_RECORD_NUM_03	= 0x08000010, // ONLY FOR PF_VIDEO_PREVIEW_CALLBACK / PF_AUDIO_PREVIEW_CALLBACK
+
+} QRETURN;
+
+// DEVICE ENUMERATION TYPE
+//
+enum {
+
+	QCAP_ENUM_TYPE_DEVICE_NAME		= 0,
+
+	QCAP_ENUM_TYPE_SERIAL_NUMBER	= 1,
+};
+
+// INPUT VIDEO SOURCE
+//
+enum {
+
+	QCAP_INPUT_TYPE_COMPOSITE	 = 0,
+
+	QCAP_INPUT_TYPE_SVIDEO		 = 1,
+
+	QCAP_INPUT_TYPE_HDMI		 = 2,
+
+	QCAP_INPUT_TYPE_DVI_D		 = 3,
+
+	QCAP_INPUT_TYPE_COMPONENTS	 = 4,
+
+	QCAP_INPUT_TYPE_YCBCR		 = 4,
+
+	QCAP_INPUT_TYPE_DVI_A		 = 5,
+
+	QCAP_INPUT_TYPE_RGB			 = 5,
+
+	QCAP_INPUT_TYPE_VGA			 = 5,
+
+	QCAP_INPUT_TYPE_SDI			 = 6,
+
+	QCAP_INPUT_TYPE_DISPLAY_PORT = 8,
+
+	QCAP_INPUT_TYPE_AUTO		 = 7,
+};
+
+// INPUT AUDIO SOURCE
+//
+enum {	
+
+	QCAP_INPUT_TYPE_EMBEDDED_AUDIO			= 0,
+
+	QCAP_INPUT_TYPE_LINE_IN					= 1,
+
+	QCAP_INPUT_TYPE_SOUNDCARD_MICROPHONE	= 2,
+
+	QCAP_INPUT_TYPE_SOUNDCARD_LINE_IN		= 3,
+};
+
+// VIDEO STANDARD TYPE
+//
+enum {	
+
+	QCAP_STANDARD_TYPE_NTSC_M		= 0x00000001,
+
+	QCAP_STANDARD_TYPE_NTSC_M_J		= 0x00000002,
+
+	QCAP_STANDARD_TYPE_NTSC_433		= 0x00000004,
+
+	QCAP_STANDARD_TYPE_PAL_M		= 0x00000200,
+
+	QCAP_STANDARD_TYPE_PAL_60		= 0x00000800,
+
+	QCAP_STANDARD_TYPE_PAL_B		= 0x00000010,
+
+	QCAP_STANDARD_TYPE_PAL_D		= 0x00000020,
+
+	QCAP_STANDARD_TYPE_PAL_G		= 0x00000040,
+
+	QCAP_STANDARD_TYPE_PAL_H		= 0x00000080,
+
+	QCAP_STANDARD_TYPE_PAL_I		= 0x00000100,
+
+	QCAP_STANDARD_TYPE_PAL_N		= 0x00000400,
+
+	QCAP_STANDARD_TYPE_PAL_N_COMBO	= 0x00100000,
+
+	QCAP_STANDARD_TYPE_AUTO			= 0x80000000, // DEFAULT, [P.S. SC310N8/N16            (CX2581 PCI) DON'T SUPPORT AUTO STANDARD DETECTION]
+												  //
+												  //		  [P.S. SC290N4/SC390N4/N8/N16 (AH8400 PCI) DON'T SUPPORT AUTO STANDARD DETECTION]
+												  //
+												  //		  [P.S. SC2B0N4/SC3B0N4/N8/N16 (TW5864 PCI) DON'T SUPPORT AUTO STANDARD DETECTION]
+												  //
+												  //		  [P.S. SC3A0N4/N8/N16         (FH8735 PCI) DON'T SUPPORT AUTO STANDARD DETECTION]
+												  //
+												  //		  [P.S. SC3C0N4/N8/N16         (MZ0380 PCI) DON'T SUPPORT AUTO STANDARD DETECTION]
+};
+
+// VIDEO DEINTERLACE TYPE
+//
+enum {	
+
+	QCAP_SOFTWARE_DEINTERLACE_TYPE_BLENDING			    = 0x00000000, // DEFAULT
+
+	QCAP_SOFTWARE_DEINTERLACE_TYPE_MOTION_ADAPTIVE	    = 0x00000001,
+
+	QCAP_SOFTWARE_DEINTERLACE_TYPE_FILTER_TRIANGLE	    = 0x00000002,
+
+	QCAP_SOFTWARE_DEINTERLACE_TYPE_BOB				    = 0x00000003,
+
+	QCAP_SOFTWARE_DEINTERLACE_TYPE_ADVANCED_3D_ADAPTIVE = 0x00000004,
+
+	QCAP_SOFTWARE_DEINTERLACE_TYPE_BEST_QUALITY         = 0x00000004,
+
+	QCAP_SOFTWARE_DEINTERLACE_TYPE_MEDICAL_RGB_REPACK   = 0x00000010, // BEST QUALITY FOR OLYMPUS RGB ENDOSCOPY
+};
+
+// COLORRANGE TYPE
+//
+enum {
+
+	QCAP_COLORRANGE_TYPE_FULL		        =  0, //  0 ~ 255 (ITU BT.709 )
+
+	QCAP_COLORRANGE_TYPE_LIMIT		        =  1, // 16 ~ 235 (ITU BT.709 )
+
+	QCAP_COLORRANGE_TYPE_FULL_BGR32	        =  2, //  0 ~ 255 (ITU BT.709 ) (RENDERER @ RGB32) 
+
+	QCAP_COLORRANGE_TYPE_LIMIT_BGR32        =  3, // 16 ~ 235 (ITU BT.709 ) (RENDERER @ RGB32) 
+
+//	QCAP_COLORRANGE_TYPE_BT0709_FULL		=  0, //  0 ~ 255 (ITU BT.709 )
+//
+//	QCAP_COLORRANGE_TYPE_BT0709_LIMIT		=  1, // 16 ~ 235 (ITU BT.709 )
+//
+//	QCAP_COLORRANGE_TYPE_BT0709_FULL_BGR32	=  2, //  0 ~ 255 (ITU BT.709 ) (RENDERER @ RGB32) 
+//
+//	QCAP_COLORRANGE_TYPE_BT0709_LIMIT_BGR32 =  3, // 16 ~ 235 (ITU BT.709 ) (RENDERER @ RGB32) 
+
+	QCAP_COLORRANGE_TYPE_BT0601_LIMIT       =  5, // 16 ~ 235 (ITU BT.601 )
+ 
+	QCAP_COLORRANGE_TYPE_BT0601_LIMIT_BGR32 =  7, // 16 ~ 235 (ITU BT.601 ) (RENDERER @ RGB32)
+
+	QCAP_COLORRANGE_TYPE_BT2020_FULL        =  8, //  0 ~ 255 (ITU BT.2020)
+
+	QCAP_COLORRANGE_TYPE_BT2020_LIMIT       =  9, // 16 ~ 235 (ITU BT.2020)
+
+	QCAP_COLORRANGE_TYPE_BT2020_FULL_BGR32  = 10, //  0 ~ 255 (ITU BT.2020) (RENDERER @ RGB32)
+
+	QCAP_COLORRANGE_TYPE_BT2020_LIMIT_BGR32 = 11, // 16 ~ 235 (ITU BT.2020) (RENDERER @ RGB32)
+};
+
+// COLORSPACE TYPE
+//
+enum {
+
+	QCAP_COLORSPACE_TYPE_RGB24	= 0,		  //   0xBBGGRR -> R0 G0 B0 R1 G1 B1 R2 G2 B2 ... >>
+	QCAP_COLORSPACE_TYEP_RGB24	= 0,
+
+	QCAP_COLORSPACE_TYPE_BGR24	= 1,		  //   0xRRGGBB -> B0 G0 R0 B1 G1 R1 B2 G2 R2 ... >>
+	QCAP_COLORSPACE_TYEP_BGR24	= 1,
+
+	QCAP_COLORSPACE_TYPE_ARGB32	= 2,		  // 0xAABBGGRR -> R0 G0 B0 A0 R1 G1 B1 A1 R2 G2 B2 A2 ... >>
+	QCAP_COLORSPACE_TYEP_ARGB32	= 2,
+
+	QCAP_COLORSPACE_TYPE_ABGR32	= 3,		  // 0xAARRGGBB -> B0 G0 R0 A0 B1 G1 R1 A1 B2 G2 R2 A2 ... >>
+	QCAP_COLORSPACE_TYEP_ABGR32	= 3,
+
+	QCAP_COLORSPACE_TYPE_YUY2	= 0x32595559, // 0x32595559 -> MAKEFOURCC('Y', 'U', 'Y', '2')
+	QCAP_COLORSPACE_TYEP_YUY2	= 0x32595559,
+
+	QCAP_COLORSPACE_TYPE_UYVY	= 0x59565955, // 0x59565955 -> MAKEFOURCC('U', 'Y', 'V', 'Y')
+	QCAP_COLORSPACE_TYEP_UYVY	= 0x59565955,
+
+	QCAP_COLORSPACE_TYPE_YV12	= 0x32315659, // 0x32315659 -> MAKEFOURCC('Y', 'V', '1', '2') (Y V U)
+	QCAP_COLORSPACE_TYEP_YV12	= 0x32315659,
+
+	QCAP_COLORSPACE_TYPE_I420	= 0x30323449, // 0x30323449 -> MAKEFOURCC('I', '4', '2', '0') (Y U V)
+	QCAP_COLORSPACE_TYEP_I420	= 0x30323449,
+
+	QCAP_COLORSPACE_TYPE_NV12	= 0x3231564E, // 0x3231564E -> MAKEFOURCC('N', 'V', '1', '2')
+	QCAP_COLORSPACE_TYEP_NV12	= 0x3231564E,
+
+	QCAP_COLORSPACE_TYPE_Y800	= 0x30303859, // 0x30303859 -> MAKEFOURCC('Y', '8', '0', '0') -> Y
+	QCAP_COLORSPACE_TYEP_Y800	= 0x30303859,
+
+	QCAP_COLORSPACE_TYPE_MJPG	= 0x47504A4D, // 0x47504A4D -> MAKEFOURCC('M', 'J', 'P', 'G')
+	QCAP_COLORSPACE_TYEP_MJPG	= 0x47504A4D,
+
+	QCAP_COLORSPACE_TYPE_H264	= 0x34363248, // 0x34363248 -> MAKEFOURCC('H', '2', '6', '4')
+	QCAP_COLORSPACE_TYEP_H264	= 0x34363248,
+
+	QCAP_COLORSPACE_TYPE_H265	= 0x35363248, // 0x35363248 -> MAKEFOURCC('H', '2', '6', '5')
+	QCAP_COLORSPACE_TYEP_H265	= 0x35363248,
+
+	QCAP_COLORSPACE_TYPE_MPG2	= 0x3247504D, // 0x3247504D -> MAKEFOURCC('M', 'P', 'G', '2')
+	QCAP_COLORSPACE_TYEP_MPG2	= 0x3247504D,
+};
+
+// ENCODER TYPE (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_ENCODER_TYPE_SOFTWARE			= 0,
+
+	QCAP_ENCODER_TYPE_HARDWARE			= 1, // IT IS ONLY USED FOR HARDWARE COMPRESSION CAPTURE CARD, SUCH AS SC580 & SC590 & SC5A0 & SC5C0
+
+	QCAP_ENCODER_TYPE_INTEL_MEDIA_SDK	= 2,
+
+	QCAP_ENCODER_TYPE_AMD_VCE			= 3,
+
+	QCAP_ENCODER_TYPE_NVIDIA_CUDA		= 4,
+
+	QCAP_ENCODER_TYPE_NVIDIA_NVENC		= 5,
+};
+
+// ENCODER VIDEO FORMAT (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_ENCODER_FORMAT_MPEG2		=  1,
+
+	QCAP_ENCODER_FORMAT_H264		=  0,
+
+	QCAP_ENCODER_FORMAT_H264_3D		=  2, // H264 MVC (STEREOSCOPIC 3D)
+
+	QCAP_ENCODER_FORMAT_H264_VC		=  3, // H264 SVC (MULTIPLE TEMPORAL SCALABILITY)
+
+	QCAP_ENCODER_FORMAT_RAW			=  4,
+
+	QCAP_ENCODER_FORMAT_RAW_NATIVE	=  4,
+
+	QCAP_ENCODER_FORMAT_H265		=  5,
+
+	QCAP_ENCODER_FORMAT_RAW_YUY2	= 20,
+
+	QCAP_ENCODER_FORMAT_RAW_UYVY	= 21, // DEVELOPING ...
+
+	QCAP_ENCODER_FORMAT_RAW_YV12	= 22,
+
+	QCAP_ENCODER_FORMAT_RAW_I420	= 23,
+
+	QCAP_ENCODER_FORMAT_RAW_NV12	= 25,
+
+	QCAP_ENCODER_FORMAT_RAW_Y800	= 24,
+};
+
+// ENCODER AUDIO FORMAT (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_ENCODER_FORMAT_PCM			= 0,
+
+	QCAP_ENCODER_FORMAT_AAC			= 1,
+
+	QCAP_ENCODER_FORMAT_AAC_RAW		= 1,
+
+	QCAP_ENCODER_FORMAT_AAC_ADTS	= 2,
+
+	QCAP_ENCODER_FORMAT_MP2			= 4, // MPEG AUDIO LAYER 2
+
+	QCAP_ENCODER_FORMAT_MP3			= 5, // MPEG AUDIO LAYER 3
+
+	QCAP_ENCODER_FORMAT_OPUS		= 6,
+
+	QCAP_ENCODER_FORMAT_AC3			= 7,
+};
+
+// RECORD PROFILE (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_RECORD_PROFILE_BASELINE	         = 0, // DEFAULT
+
+	QCAP_RECORD_PROFILE_MAIN		         = 1,
+
+	QCAP_RECORD_PROFILE_HIGH		         = 2,
+
+	QCAP_RECORD_PROFILE_CONSTRAINED_BASELINE = 3, // ONLY FOR QCAP_ENCODER_FORMAT_H264_VC
+
+	QCAP_RECORD_PROFILE_CONSTRAINED_HIGH     = 4, // ONLY FOR QCAP_ENCODER_FORMAT_H264_VC
+};
+
+// RECORD LEVEL (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_RECORD_LEVEL_1		= 10,
+
+	QCAP_RECORD_LEVEL_1B	=  9,
+
+	QCAP_RECORD_LEVEL_11	= 11,
+
+	QCAP_RECORD_LEVEL_12	= 12,
+
+	QCAP_RECORD_LEVEL_13	= 13,
+
+	QCAP_RECORD_LEVEL_2		= 20,
+
+	QCAP_RECORD_LEVEL_21	= 21,
+
+	QCAP_RECORD_LEVEL_22	= 22,
+
+	QCAP_RECORD_LEVEL_3		= 30,
+
+	QCAP_RECORD_LEVEL_31	= 31,
+
+	QCAP_RECORD_LEVEL_32	= 32,
+
+	QCAP_RECORD_LEVEL_4		= 40,
+
+	QCAP_RECORD_LEVEL_41	= 41, // DEFAULT
+
+	QCAP_RECORD_LEVEL_42	= 42,
+
+	QCAP_RECORD_LEVEL_50	= 50,
+
+	QCAP_RECORD_LEVEL_51	= 51,
+
+	QCAP_RECORD_LEVEL_52	= 52,
+
+	QCAP_RECORD_LEVEL_60	= 60,
+
+	QCAP_RECORD_LEVEL_61	= 61,
+
+	QCAP_RECORD_LEVEL_62	= 62,
+};
+
+// RECORD ENTROPY (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_RECORD_ENTROPY_CAVLC	= 0, // DEFAULT
+
+	QCAP_RECORD_ENTROPY_CABAC	= 1,
+};
+
+// RECORD COMPLEXITY (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_RECORD_COMPLEXITY_0	= 0, // BEST SPEED, DEFAULT
+
+	QCAP_RECORD_COMPLEXITY_1	= 1,
+
+	QCAP_RECORD_COMPLEXITY_2	= 2,
+
+	QCAP_RECORD_COMPLEXITY_3	= 3,
+
+	QCAP_RECORD_COMPLEXITY_4	= 4,
+
+	QCAP_RECORD_COMPLEXITY_5	= 5,
+
+	QCAP_RECORD_COMPLEXITY_6	= 6, // BEST QUALITY
+};
+
+// RECORD MODE (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_RECORD_MODE_VBR		= 0, 
+
+	QCAP_RECORD_MODE_CBR		= 1, 
+
+	QCAP_RECORD_MODE_ABR		= 2, 
+
+	QCAP_RECORD_MODE_CQP		= 3, // DEVELOPING ...
+
+	QCAP_RECORD_MODE_LOSSLESS	= 4, 
+};
+
+// DOWNSCALE MODE (RECORD PROPERTY)
+// 
+enum {
+
+	QCAP_DOWNSCALE_MODE_OFF	= 0, // (1920, 1080) * (1 / 1) = (1920, 1080)
+
+	QCAP_DOWNSCALE_MODE_2_3	= 1, // (1920, 1080) * (2 / 3) = (1280,  720)
+
+	QCAP_DOWNSCALE_MODE_1_2	= 2, // (1920, 1080) * (1 / 2) = ( 960,  540)
+
+	QCAP_DOWNSCALE_MODE_1_4	= 3, // (1920, 1080) * (1 / 4) = ( 480,  270)
+};
+
+// RECORD FLAG (RECORD PROPERTY)
+//
+enum {
+
+	QCAP_RECORD_FLAG_FULL						= 0x00000007,
+
+	QCAP_RECORD_FLAG_FILE						= 0x00000001,
+
+	QCAP_RECORD_FLAG_ENCODE						= 0x00000002,
+
+	QCAP_RECORD_FLAG_DISPLAY					= 0x00000004,
+
+	QCAP_RECORD_FLAG_DECODE						= 0x00000008,
+
+	QCAP_RECORD_FLAG_VIDEO_ONLY					= 0x00000010,
+
+	QCAP_RECORD_FLAG_AUDIO_ONLY					= 0x00000020,
+
+	QCAP_RECORD_FLAG_VIDEO_USE_IDEAL_TIMESTAMP	= 0x00000040,
+
+	QCAP_RECORD_FLAG_AUDIO_USE_IDEAL_TIMESTAMP	= 0x00000080,
+
+	QCAP_RECORD_FLAG_INGNORE_FORMAT_CHANGED		= 0x00000100,
+
+	QCAP_RECORD_FLAG_SYNCHRONIZED_RECORD		= 0x00000200, // START BY QCAP_RESUME_RECORD
+
+	QCAP_RECORD_FLAG_VIDEO_USE_MEDIA_TIMER		= 0x00000400, // ONLY FOR QCAP_SET_VIDEO_*_UNCOMPRESSION_BUFFER_*
+
+	QCAP_RECORD_FLAG_AUDIO_USE_MEDIA_TIMER		= 0x00000800, // ONLY FOR QCAP_SET_AUDIO_*_UNCOMPRESSION_BUFFER_*
+};
+
+// BROADCAST FLAG (BROADCAST PROPERTY)
+//
+enum {
+
+	QCAP_BROADCAST_FLAG_FULL						= 0x00000007,
+
+	QCAP_BROADCAST_FLAG_NETWORK						= 0x00000001,
+
+	QCAP_BROADCAST_FLAG_ENCODE						= 0x00000002,
+
+	QCAP_BROADCAST_FLAG_DISPLAY						= 0x00000004,
+
+	QCAP_BROADCAST_FLAG_DECODE						= 0x00000008,
+
+	QCAP_BROADCAST_FLAG_VIDEO_ONLY					= 0x00000010,
+	
+	QCAP_BROADCAST_FLAG_AUDIO_ONLY					= 0x00000020,
+
+	QCAP_BROADCAST_FLAG_VIDEO_USE_IDEAL_TIMESTAMP	= 0x00000040,
+
+	QCAP_BROADCAST_FLAG_AUDIO_USE_IDEAL_TIMESTAMP	= 0x00000080,
+
+	QCAP_BROADCAST_FLAG_VIDEO_USE_MEDIA_TIMER		= 0x00000400, // ONLY FOR QCAP_SET_VIDEO_*_UNCOMPRESSION_BUFFER_*
+
+	QCAP_BROADCAST_FLAG_AUDIO_USE_MEDIA_TIMER		= 0x00000800, // ONLY FOR QCAP_SET_AUDIO_*_UNCOMPRESSION_BUFFER_*
+};
+
+// BROADCAST PROTOCOL (BROADCAST PROPERTY)
+//
+enum {
+
+	QCAP_BROADCAST_PROTOCOL_UDP	 = 0x00000000,
+
+	QCAP_BROADCAST_PROTOCOL_TCP	 = 0x00000001, // BPS >= 12MBPS, SUGGEST TO SUE QCAP_BROADCAST_PROTOCOL_TCP
+
+	QCAP_BROADCAST_PROTOCOL_HTTP = 0x00000002,
+};
+
+// OSD FONT STYLE (OSD PROPERTY)
+//
+enum {
+
+    QCAP_FONT_STYLE_REGULAR     = 0x00000000,
+    
+	QCAP_FONT_STYLE_BOLD        = 0x00000001,
+    
+	QCAP_FONT_STYLE_ITALIC      = 0x00000002,
+    
+	QCAP_FONT_STYLE_BOLDITALIC  = 0x00000003,
+    
+	QCAP_FONT_STYLE_UNDERLINE   = 0x00000004,
+    
+	QCAP_FONT_STYLE_STRIKEOUT   = 0x00000008,
+};
+
+// OSD STRING ALIGNMENT (OSD PROPERTY)
+//
+enum {
+
+	QCAP_STRING_ALIGNMENT_STYLE_LEFT    = 0x00000000,
+
+    QCAP_STRING_ALIGNMENT_STYLE_NEAR    = 0x00000000,
+    
+	QCAP_STRING_ALIGNMENT_STYLE_CENTER  = 0x00000001,
+    
+	QCAP_STRING_ALIGNMENT_STYLE_RIGHT   = 0x00000002,
+    
+	QCAP_STRING_ALIGNMENT_STYLE_FAR		= 0x00000002,
+};
+
+// OSD SEQUENCE STYLE (OSD PROPERTY)
+//
+enum {
+
+	//  GENERAL DEVICE: FOREMOST -> PREVIEW CALLBACK -> BEFORE_ENCODE -> RECORDING (ENCODING) -> AFTERMOST
+	//
+	// SHARE RECORDING: FOREMOST -> RECORDING (ENCODING) -> AFTERMOST
+	//
+	// SHARE STREAMING: FOREMOST -> STREAMING (ENCODING) -> AFTERMOST
+	//
+	//  VIRTUAL CAMERA: FOREMOST -> AFTERMOST -> SHARING
+	//
+	//   FILE PLAYBACK: PLAYBACK (DECODING) -> FOREMOST -> DECODER CALLBACK -> AFTERMOST
+	//
+	QCAP_SEQUENCE_STYLE_FOREMOST		= 0x00000000,
+
+    QCAP_SEQUENCE_STYLE_BEFORE_ENCODE	= 0x00000001, // IT IS ONLY USED FOR GENERAL DEVICE
+    
+	QCAP_SEQUENCE_STYLE_AFTERMOST		= 0x00000002,
+};
+
+// BUFFER SCALE STYLE (BUFFER PROPERTY)
+//
+enum {
+
+	QCAP_SCALE_STYLE_STRETCH	= 0x00000000,
+
+    QCAP_SCALE_STYLE_FIT		= 0x00000001,
+    
+	QCAP_SCALE_STYLE_FILL		= 0x00000002,
+};
+
+// DECODER TYPE (PLAYBACK PROPERTY)
+//
+enum {
+
+	QCAP_DECODER_TYPE_SOFTWARE			= 0,
+
+	QCAP_DECODER_TYPE_HARDWARE			= 1, // DEVELOPING ...
+
+	QCAP_DECODER_TYPE_INTEL_MEDIA_SDK	= 2,
+
+	QCAP_DECODER_TYPE_AMD_UVD			= 3, // DEVELOPING ...
+
+	QCAP_DECODER_TYPE_AMD_VCE			= 3, // == QCAP_DECODER_TYPE_AMD_UVD
+
+	QCAP_DECODER_TYPE_NVIDIA_CUDA		= 4,
+
+	QCAP_DECODER_TYPE_NVIDIA_NVDEC		= 5,
+
+	QCAP_DECODER_TYPE_NVIDIA_NVENC		= 5, // == QCAP_DECODER_TYPE_NVIDIA_NVDEC
+};
+
+// DECODER VIDEO FORMAT (PLAYBACK PROPERTY)
+//
+enum {
+
+	QCAP_DECODER_FORMAT_MPEG2		=  1,
+
+	QCAP_DECODER_FORMAT_H264		=  0,
+
+	QCAP_DECODER_FORMAT_H264_3D		=  2, // H264 MVC (STEREOSCOPIC 3D)
+
+	QCAP_DECODER_FORMAT_H264_VC		=  3, // H264 SVC (MULTIPLE TEMPORAL SCALABILITY)
+
+	QCAP_DECODER_FORMAT_RAW			=  4,
+
+	QCAP_DECODER_FORMAT_H265		=  5,
+};
+
+// 3D STEREO BUFFER TYPE
+//
+enum {
+
+	QCAP_3D_STEREO_BUFFER_SIDE_BY_SIDE		= 0,
+
+	QCAP_3D_STEREO_BUFFER_TOP_BOTTOM		= 1,
+
+	QCAP_3D_STEREO_BUFFER_LINE_BY_LINE		= 2,
+};
+
+// 3D STEREO DISPLAY MODE
+//
+enum {
+
+	QCAP_3D_STEREO_DISPLAY_MODE_SIDE_BY_SIDE	= 0,
+
+	QCAP_3D_STEREO_DISPLAY_MODE_TOP_BOTTOM		= 1,
+
+	QCAP_3D_STEREO_DISPLAY_MODE_LINE_BY_LINE	= 2,
+
+	QCAP_3D_STEREO_DISPLAY_MODE_LEFT_ONLY		= 3,
+
+	QCAP_3D_STEREO_DISPLAY_MODE_RIGHT_ONLY		= 4,
+};
+
+// DICOM QUERY LEVEL (DICOM QUERY/RETRIEVE INFORMATION MODEL)
+//
+enum {	
+
+	QCAP_DICOM_QUERY_LEVEL_PATIENT	= 0,
+
+	QCAP_DICOM_QUERY_LEVEL_STUDY	= 1,
+
+	QCAP_DICOM_QUERY_LEVEL_SERIES	= 2,
+
+	QCAP_DICOM_QUERY_LEVEL_IMAGE	= 3, // COMPOSITE OBJECT INSTANCE
+};
+
+// FILE TIMEUNIT
+//
+enum {
+
+	QCAP_FILE_TIMEUNIT_TIME		= 0,
+
+	QCAP_FILE_TIMEUNIT_FRAME	= 1,
+};
+
+// ANIMATION CLIP BLINDS TYPE
+//
+enum {
+
+	QCAP_ANIMATION_CLIP_BLINDS_TYPE_NONE		= 0,
+
+	QCAP_ANIMATION_CLIP_BLINDS_TYPE_VERTICAL	= 1,
+
+	QCAP_ANIMATION_CLIP_BLINDS_TYPE_HORIZONTAL	= 2,
+};
+
+// SERIAL PORT PARITY CHECK (SERIAL PORT PROPERTY)
+//
+enum {
+
+	QCAP_SERIAL_PORT_PARITY_CHECK_NONE	= 0x00000000,
+
+	QCAP_SERIAL_PORT_PARITY_CHECK_ODD	= 0x00000001,
+
+	QCAP_SERIAL_PORT_PARITY_CHECK_EVEN	= 0x00000002,
+
+	QCAP_SERIAL_PORT_PARITY_CHECK_MARK	= 0x00000003,		
+
+	QCAP_SERIAL_PORT_PARITY_CHECK_SPACE	= 0x00000004,
+};
+
+// SERIAL PORT FLOW CONTROL (SERIAL PORT PROPERTY)
+//
+enum {
+
+	QCAP_SERIAL_PORT_FLOW_CONTROL_NONE		= 0x00000000,
+
+	QCAP_SERIAL_PORT_FLOW_CONTROL_CTS_RTS	= 0x00000001,
+
+	QCAP_SERIAL_PORT_FLOW_CONTROL_CTS_DTR	= 0x00000002,
+
+	QCAP_SERIAL_PORT_FLOW_CONTROL_DSR_RTS	= 0x00000003,
+
+	QCAP_SERIAL_PORT_FLOW_CONTROL_DSR_DTR	= 0x00000004,
+
+	QCAP_SERIAL_PORT_FLOW_CONTROL_XON_XOFF	= 0x00000005,
+};
+
+// SERIAL PORT STOP BITS (SERIAL PORT PROPERTY)
+//
+enum {
+
+	QCAP_SERIAL_PORT_STOP_BITS_ONE				= 0x00000000,
+
+	QCAP_SERIAL_PORT_STOP_BITS_ONE_POINT_FIVE	= 0x00000001,
+
+	QCAP_SERIAL_PORT_STOP_BITS_TWO				= 0x00000002,
+};
+
+// SERIAL PORT SIGNAL TYPE (SERIAL PORT PROPERTY)
+//
+enum {
+
+	QCAP_SERIAL_PORT_SIGNAL_TYPE_CLEAR_DTR		= 0x00000000, // CLEAR THE DTR (DATA-TERMINAL-READY) SIGNAL
+
+	QCAP_SERIAL_PORT_SIGNAL_TYPE_CLEAR_RTS		= 0x00000001, // CLEAR THE RTS (REQUEST-TO-SEND) SIGNAL
+
+	QCAP_SERIAL_PORT_SIGNAL_TYPE_SET_DTR		= 0x00000002, // SEND THE DTR (DATA-TERMINAL-READY) SIGNAL
+
+	QCAP_SERIAL_PORT_SIGNAL_TYPE_SET_RTS		= 0x00000003, // SEND THE RTS (REQUEST-TO-SEND) SIGNAL
+
+	QCAP_SERIAL_PORT_SIGNAL_TYPE_SET_XOFF		= 0x00000004, // CAUSE TRANSMISSION TO ACT AS IF AN XOFF CHARACTER HAS BEEN RECEIVED
+
+	QCAP_SERIAL_PORT_SIGNAL_TYPE_SET_XON		= 0x00000005, // CAUSE TRANSMISSION TO ACT AS IF AN XON CHARACTER HAS BEEN RECEIVED
+
+	QCAP_SERIAL_PORT_SIGNAL_TYPE_SET_BREAK		= 0x00000006, // SUSPEND CHARACTER TRANSMISSION FOR A SPECIFIED COMMUNICATIONS DEVICE AND PLACES THE TRANSMISSION LINE IN A BREAK STATE UNTIL THE CLEARCOMMBREAK FUNCTION IS CALLED
+
+	QCAP_SERIAL_PORT_SIGNAL_TYPE_CLEAR_BREAK	= 0x00000007, // RESTORE CHARACTER TRANSMISSION FOR A SPECIFIED COMMUNICATIONS DEVICE AND PLACES THE TRANSMISSION LINE IN A NONBREAK STATE
+};
+
+// CALLBACK FUNCTION TYPES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+typedef QRETURN (QCAP_EXPORT *PF_NO_SIGNAL_DETECTED_CALLBACK)( PVOID pDevice /*IN*/, ULONG nVideoInput /*IN*/, ULONG nAudioInput /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_SIGNAL_REMOVED_CALLBACK)( PVOID pDevice /*IN*/, ULONG nVideoInput /*IN*/, ULONG nAudioInput /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_FORMAT_CHANGED_CALLBACK)( PVOID pDevice /*IN*/, ULONG nVideoInput /*IN*/, ULONG nAudioInput /*IN*/, ULONG nVideoWidth /*IN*/, ULONG nVideoHeight /*IN*/, BOOL bVideoIsInterleaved /*IN*/, double dVideoFrameRate /*IN*/, ULONG nAudioChannels /*IN*/, ULONG nAudioBitsPerSample /*IN*/, ULONG nAudioSampleFrequency /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_FORMAT_CHANGED_CALLBACK_EX)( PVOID pDevice /*IN*/, ULONG nVideoInput /*IN*/, ULONG nAudioInput /*IN*/, ULONG nVideoWidth /*IN*/, ULONG nVideoHeight /*IN*/, BOOL bVideoIsInterleaved /*IN*/, double dVideoNativeFrameRate /*IN*/, double dVideoGrabFrameRate /*IN*/, ULONG nAudioChannels /*IN*/, ULONG nAudioBitsPerSample /*IN*/, ULONG nAudioSampleFrequency /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_SNAPSHOT_DONE_CALLBACK)( PVOID pDevice /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_SNAPSHOT_STREAM_CALLBACK)( PVOID pDevice /*IN*/, CHAR * pszFilePathName /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_RECORD_DONE_CALLBACK)( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_RECORD_FAIL_CALLBACK)( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, QRESULT nErrorStatus /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_SHARE_RECORD_DONE_CALLBACK)( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_SHARE_RECORD_FAIL_CALLBACK)( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, QRESULT nErrorStatus /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_SHARE_RECORD_SNAPSHOT_DONE_CALLBACK)( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_SHARE_RECORD_SNAPSHOT_STREAM_CALLBACK)( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_VERTICAL_ANCILLARY_DATA_CALLBACK)( PVOID pDevice /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_PREVIEW_CALLBACK)( PVOID pDevice /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_PREVIEW_CALLBACK)( PVOID pDevice /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_HARDWARE_ENCODER_CALLBACK)( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, BOOL bIsKeyFrame /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_RECORD_CALLBACK)( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, BOOL bIsKeyFrame /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_RECORD_CALLBACK)( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_MEDIA_RECORD_CALLBACK)( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, BOOL bIsKeyFrame /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_MX_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, UINT iTrackNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_MEDIA_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_DECODER_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_DECODER_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_DECODER_MX_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, UINT iTrackNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_DISPLAY_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_DISPLAY_SHARE_RECORD_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_SHARE_RECORD_MEDIA_TIMER_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, double dDelayTime /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_SHARE_RECORD_MEDIA_TIMER_CALLBACK)( UINT iRecNum /*IN*/, double dSampleTime /*IN*/, double dDelayTime /*IN*/, PVOID pUserData /*IN*/ );
+
+// CALLBACK FUNCTIONS PART.I (EVENT) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_NO_SIGNAL_DETECTED_CALLBACK( PVOID pDevice /*IN*/, PF_NO_SIGNAL_DETECTED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SIGNAL_REMOVED_CALLBACK( PVOID pDevice /*IN*/, PF_SIGNAL_REMOVED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_FORMAT_CHANGED_CALLBACK( PVOID pDevice /*IN*/, PF_FORMAT_CHANGED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_FORMAT_CHANGED_CALLBACK_EX( PVOID pDevice /*IN*/, PF_FORMAT_CHANGED_CALLBACK_EX pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SNAPSHOT_DONE_CALLBACK( PVOID pDevice /*IN*/, PF_SNAPSHOT_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_RECORD_DONE_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_RECORD_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_RECORD_FAIL_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_RECORD_FAIL_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SHARE_RECORD_DONE_CALLBACK( UINT iRecNum /*IN*/, PF_SHARE_RECORD_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SHARE_RECORD_FAIL_CALLBACK( UINT iRecNum /*IN*/, PF_SHARE_RECORD_FAIL_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SHARE_RECORD_SNAPSHOT_DONE_CALLBACK( UINT iRecNum /*IN*/, PF_SHARE_RECORD_SNAPSHOT_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// CALLBACK FUNCTIONS PART.II (DATA) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_VERTICAL_ANCILLARY_DATA_CALLBACK( PVOID pDevice /*IN*/, PF_VIDEO_VERTICAL_ANCILLARY_DATA_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ ); // VANC
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_PREVIEW_CALLBACK( PVOID pDevice /*IN*/, PF_VIDEO_PREVIEW_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_PREVIEW_CALLBACK( PVOID pDevice /*IN*/, PF_AUDIO_PREVIEW_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_HARDWARE_ENCODER_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_VIDEO_HARDWARE_ENCODER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// CALLBACK FUNCTIONS PART.III (DATA) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SNAPSHOT_STREAM_CALLBACK( PVOID pDevice /*IN*/, PF_SNAPSHOT_STREAM_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SHARE_RECORD_SNAPSHOT_STREAM_CALLBACK( UINT iRecNum /*IN*/, PF_SHARE_RECORD_SNAPSHOT_STREAM_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// CALLBACK FUNCTIONS PART.IIII (DATA) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_RECORD_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_VIDEO_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_RECORD_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_AUDIO_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_MEDIA_RECORD_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_MEDIA_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR TS & FLV FILE FORMAT
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_VIDEO_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_MX_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_MX_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_MEDIA_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_MEDIA_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR TS & FLV FILE FORMAT
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_DECODER_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_VIDEO_DECODER_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_DECODER_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_DECODER_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_DECODER_MX_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_DECODER_MX_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_DISPLAY_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_VIDEO_DISPLAY_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_DISPLAY_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_DISPLAY_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_SHARE_RECORD_MEDIA_TIMER_CALLBACK( UINT iRecNum /*IN*/, PF_VIDEO_SHARE_RECORD_MEDIA_TIMER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_SHARE_RECORD_MEDIA_TIMER_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_SHARE_RECORD_MEDIA_TIMER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// SYSTEM FUNCTIONS /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VERSION( ULONG * pMajorVersion /*OUT*/, ULONG * pMinorVersion /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_SYSTEM_CONFIGURATION( BOOL   bEnableMultipleUsersAccess = TRUE,					 /*IN*/ // If true, qcap will alllow multiple users to access the device.
+
+															    BOOL   bEnableVideoPreviewDevice = TRUE,					 /*IN*/ // If true, the video capture device will be enabled.
+
+															    BOOL   bEnableAudioPreviewDevice = TRUE,					 /*IN*/ // If true, the audio capture device will be enabled.
+
+															    BOOL   bEnableVideoHardwareMainEncoderDevice = TRUE,		 /*IN*/ // If true, the main hardware encoder will be enabled.
+
+															    BOOL   bEnableVideoHardwareSubEncoderDevice = TRUE,			 /*IN*/ // If true, the sub hardware encoder will be enabled.
+
+															    ULONG  nAutoInputDetectionTimeout = 3000,					 /*IN*/ // The parameter allows you to specify the timeout duration of the auto input detection, default is 3sec.
+
+															    BOOL   bEnableSCF = FALSE,									 /*IN*/ // If true, the SCF file recording function will be turned on.
+															   
+															    CHAR * pszDB3 = NULL,										 /*IN*/ // The parameter allows you to specify the file path of SQL database.
+															   
+															    BOOL   bEnableAsyncBackgroundSnapshot = FALSE,				 /*IN*/ // If true, snapshot function will be moved from preview callback into another thread. 
+															   
+															    BOOL   bEnableEnhancedVideoRenderer = TRUE,					 /*IN*/ // If true, QCAP will enable the DirectShow's Enhanced Video Renderer Filter.
+															   
+															    BOOL   bEnableSystemTimeCallback = FALSE,					 /*IN*/ // If true, the preview's and encoder's callbacks will return one local system time at dSampleTime.
+															   
+															    BOOL   bEnableFileRepairFunction = TRUE,					 /*IN*/ // If true, the recording file will own self-repaired ability.
+															   
+															    BOOL   bEnableNewRTSPLibrary = TRUE,						 /*IN*/ // If true, we will use new rtsp lib to repalce old one after 1.1.0.130.0
+																
+																CHAR * pszWebServerRootFolderPath = NULL,					 /*IN*/ // The parameter allows you to specify the web server's root folder path.
+
+																CHAR * pszWebServerIP = NULL,								 /*IN*/ // The parameter allows you to specify the web server's ip address.
+																
+																ULONG  nSystemColorRangeType = QCAP_COLORRANGE_TYPE_FULL,    /*IN*/ // The parameter allows you to specify the system color range setting.
+																
+																BOOL   bEnableVideoMixingRendererBugPatch = TRUE,            /*IN*/ // If true, QCAP will enable the bug patch for DirectShow's Video Mixing Renderer, but it will delay the device's initialized time.
+																
+																ULONG  nEnableCustomVideoRenderer = 0x00000000,              /*IN*/ // If non-zero, QCAP will enable the Custom Video Renderer as major display engine.
+																
+																BOOL   bEnableGraphicMemoryForVideoEncoder = FALSE,          /*IN*/ // If true, QCAP will use the graph memory for all gpu encoders.
+																
+																BOOL   bEnableSingleGraphCaptureMode = FALSE,				 /*IN*/ // If true, QCAP will use single DirectShow's graph to capture video and audio streams.
+																
+																ULONG  nSignalDetectionDuration = 1000,                      /*IN*/ // The parameter allows you to specify the duration of the signal detection, default is 1sec.
+																
+																BOOL   bEnableNewChromaKeyLibrary = FALSE,                   /*IN*/ // If true, we will use new chroma key lib to repalce old one after 1.1.0.183.5            
+
+																BOOL   bEnableNewSnapshotLibrary = TRUE );                   /*IN*/ // If true, we will use new snapshot lib to repalce old one after 1.1.0.165.4                  
+																	
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_QUERY_ENCODER_TYPE_CAP( ULONG nEncoderType /*IN*/, ULONG nEncoderFormat /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_QUERY_DECODER_TYPE_CAP( ULONG nDecoderType /*IN*/, ULONG nDecoderFormat /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_QUERY_ENCODER_STATUS( ULONG nEncoderType /*IN*/, ULONG * pExistInstances /*OUT*/ );
+
+// DEVICE FUNCTIONS /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DEVICE_ENUMERATION( ULONGLONG * * ppVideoDeviceList /*OUT*/, ULONG * pVideoDeviceSize /*OUT*/, ULONGLONG * * ppVideoEncoderDeviceList /*OUT*/, ULONG * pVideoEncoderDeviceSize /*OUT*/, ULONGLONG * * ppAudioDeviceList /*OUT*/, ULONG * pAudioDeviceSize /*OUT*/, ULONGLONG * * ppAudioEncoderDeviceList /*OUT*/, ULONG * pAudioEncoderDeviceSize /*OUT*/, ULONG nDeviceEnumType = QCAP_ENUM_TYPE_DEVICE_NAME /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_DEVICE_ENUMERATION_ITEM_INFO( UINT iDevNum /*IN*/, ULONGLONG * pDeviceList /*IN*/, ULONG * pDeviceInfoH /*OUT*/, ULONG * pDeviceInfoL /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE( CHAR * pszDevName /*IN*/, UINT iDevNum /*IN*/, HWND hAttachedWindow /*IN*/, PVOID * ppDevice /*OUT*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/ );
+
+// DEVICE NAME TABLE:
+//
+// "DC1150 USB" IS FOR PD652, PD652.3D
+//
+// "QP0204 USB" IS FOR PD5A0
+//
+// "UB658G USB" IS FOR UB658
+//
+// "CY3014 USB" IS FOR UB530
+//
+// "UB3200 USB" IS FOR PD560
+//
+// "UB3300 USB" IS FOR PD570
+//
+// "UB3400 USB" IS FOR UB5H0
+//
+// "TW6802 PCI" IS FOR SC200, SC300, SC230, SC330
+//
+// "CX2581 PCI" IS FOR SC310, SC340
+//
+// "CX2385 PCI" IS FOR SC350
+//
+// "AH8400 PCI" IS FOR SC290, SC390
+//
+// "FH8735 PCI" IS FOR SC2A0, SC3A0
+//
+// "TW5864 PCI" IS FOR SC2B0, SC3B0
+//
+// "MZ0380 PCI" IS FOR SC3C0
+//
+// "SA7160 PCI" IS FOR SC500, SC510, TB510
+//
+// "FH8735 PCI" IS FOR SC580
+//
+// "TW2809 PCI" IS FOR SC590
+//
+// "QP0203 PCI" IS FOR SC540, SC5A0
+//
+// "MZ0380 PCI" IS FOR SC350, SC3C0, SC550, SC560, SC5C0
+//
+// "SC0700 PCI" IS FOR SC700
+//
+// "SC0710 PCI" IS FOR SC710
+//
+//     "CAMERA" IS FOR GENERAL USB CAMERA
+//
+//  "SOUNDCARD" IS FOR GENERAL SOUND CARD
+//
+//    "DESKTOP" IS FOR GENERAL DESKTOP SCREEN GRABBER
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY( PVOID pDevice /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_RUN( PVOID pDevice /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_RUN_EX( PVOID pDevice /*IN*/, BOOL bStopAfterSignalRemoved = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP( PVOID pDevice /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_QUERY_DEVICE_CAP( PVOID pDevice /*IN*/, DWORD * pDeviceSerialNumber /*OUT*/, CHAR * pszDeviceSystemPath /*OUT*/, BOOL * pHasHardwareMainEncoder /*OUT*/, BOOL * pHasHardwareSubEncoder /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_INPUT_CONFIG( PVOID pDevice /*IN*/, DWORD * pConfig /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_INPUT_CONFIG( PVOID pDevice /*IN*/, DWORD dwConfig /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_INPUT( PVOID pDevice /*IN*/, ULONG * pInput /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_INPUT( PVOID pDevice /*IN*/, ULONG nInput /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_INPUT( PVOID pDevice /*IN*/, ULONG * pInput /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_INPUT( PVOID pDevice /*IN*/, ULONG nInput /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_STANDARD( PVOID pDevice /*IN*/, ULONG * pStandard /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_STANDARD( PVOID pDevice /*IN*/, ULONG nStandard /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_CURRENT_CONTENT_PROTECTION_STATUS( PVOID pDevice /*IN*/, ULONG * pStatus /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_CURRENT_INPUT_FORMAT( PVOID pDevice /*IN*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, BOOL * pIsInterleaved /*OUT*/, double * pFrameRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_CURRENT_INPUT_FORMAT_EX( PVOID pDevice /*IN*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, BOOL * pIsInterleaved /*OUT*/, double * pNativeFrameRate /*OUT*/, double * pGrabFrameRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_CURRENT_INPUT_FORMAT( PVOID pDevice /*IN*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_DEFAULT_OUTPUT_FORMAT( PVOID pDevice /*IN*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, BOOL * pIsInterleaved /*OUT*/, double * pFrameRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_DEFAULT_OUTPUT_FORMAT( PVOID pDevice /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BOOL bIsInterleaved /*IN*/, double dFrameRate /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_DEFAULT_OUTPUT_FORMAT( PVOID pDevice /*IN*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_DEFAULT_OUTPUT_FORMAT( PVOID pDevice /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_VERTICAL_ANCILLARY_DATA_GRABBED_LINES( PVOID pDevice /*IN*/, ULONG * pLines /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_VERTICAL_ANCILLARY_DATA_GRABBED_LINES( PVOID pDevice /*IN*/, ULONG nLines /*IN*/ ); // 0 = DISABLE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_DEINTERLACE_TYPE( PVOID pDevice /*IN*/, ULONG * pType /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_DEINTERLACE_TYPE( PVOID pDevice /*IN*/, ULONG nType /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_DEINTERLACE( PVOID pDevice /*IN*/, BOOL * pEnable /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_DEINTERLACE( PVOID pDevice /*IN*/, BOOL bEnable /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_REGION_DISPLAY( PVOID pDevice /*IN*/, ULONG * pCropX /*OUT*/, ULONG * pCropY /*OUT*/, ULONG * pCropW /*OUT*/, ULONG * pCropH /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_REGION_DISPLAY( PVOID pDevice /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_MIRROR( PVOID pDevice /*IN*/, BOOL * pHorizontalMirror /*OUT*/, BOOL * pVerticalMirror /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_MIRROR( PVOID pDevice /*IN*/, BOOL bHorizontalMirror /*IN*/, BOOL bVerticalMirror /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_ASPECT_RATIO( PVOID pDevice /*IN*/, BOOL * pMaintainAspectRatio /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_ASPECT_RATIO( PVOID pDevice /*IN*/, BOOL bMaintainAspectRatio /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_BRIGHTNESS( PVOID pDevice /*IN*/, ULONG * pValue /*OUT*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_BRIGHTNESS( PVOID pDevice /*IN*/, ULONG nValue /*IN*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_CONTRAST( PVOID pDevice /*IN*/, ULONG * pValue /*OUT*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_CONTRAST( PVOID pDevice /*IN*/, ULONG nValue /*IN*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_HUE( PVOID pDevice /*IN*/, ULONG * pValue /*OUT*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_HUE( PVOID pDevice /*IN*/, ULONG nValue /*IN*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SATURATION( PVOID pDevice /*IN*/, ULONG * pValue /*OUT*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SATURATION( PVOID pDevice /*IN*/, ULONG nValue /*IN*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARPNESS( PVOID pDevice /*IN*/, ULONG * pValue /*OUT*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARPNESS( PVOID pDevice /*IN*/, ULONG nValue /*IN*/ /*PHYSICAL VALUE 0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_BRIGHTNESS_EX( PVOID pDevice /*IN*/, ULONG * pPhysicalValue /*OUT*/ /*0 ~ 255*/, ULONG * pDisplayValue /*OUT*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_BRIGHTNESS_EX( PVOID pDevice /*IN*/, ULONG nPhysicalValue /*IN*/ /*0 ~ 255*/, ULONG nDisplayValue /*IN*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_CONTRAST_EX( PVOID pDevice /*IN*/, ULONG * pPhysicalValue /*OUT*/ /*0 ~ 255*/, ULONG * pDisplayValue /*OUT*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_CONTRAST_EX( PVOID pDevice /*IN*/, ULONG nPhysicalValue /*IN*/ /*0 ~ 255*/, ULONG nDisplayValue /*IN*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_HUE_EX( PVOID pDevice /*IN*/, ULONG * pPhysicalValue /*OUT*/ /*0 ~ 255*/, ULONG * pDisplayValue /*OUT*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_HUE_EX( PVOID pDevice /*IN*/, ULONG nPhysicalValue /*IN*/ /*0 ~ 255*/, ULONG nDisplayValue /*IN*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SATURATION_EX( PVOID pDevice /*IN*/, ULONG * pPhysicalValue /*OUT*/ /*0 ~ 255*/, ULONG * pDisplayValue /*OUT*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SATURATION_EX( PVOID pDevice /*IN*/, ULONG nPhysicalValue /*IN*/ /*0 ~ 255*/, ULONG nDisplayValue /*IN*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARPNESS_EX( PVOID pDevice /*IN*/, ULONG * pPhysicalValue /*OUT*/ /*0 ~ 255*/, ULONG * pDisplayValue /*OUT*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARPNESS_EX( PVOID pDevice /*IN*/, ULONG nPhysicalValue /*IN*/ /*0 ~ 255*/, ULONG nDisplayValue /*IN*/ /*0 ~ 255*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_PREVIEW_PROPERTY_EX( PVOID pDevice /*IN*/, ULONG * pDownscaleMode /*OUT*/, ULONG * pPostSkipFrameRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pPostAvgFrameRate /*OUT*/ /*DEFAULT = 0*/ ); // SKIP = -1 (0xFFFFFFFF)
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_PREVIEW_PROPERTY_EX( PVOID pDevice /*IN*/, ULONG   nDownscaleMode /*IN*/,  ULONG   nPostSkipFrameRate /*IN*/  /*DEFAULT = 0*/, ULONG   nPostAvgFrameRate /*IN*/  /*DEFAULT = 0*/ ); // SKIP = -1 (0xFFFFFFFF)
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_HARDWARE_ENCODER_PROPERTY( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/, ULONG * pDownscaleMode /*OUT*/, ULONG * pPostSkipFrameRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pPostAvgFrameRate /*OUT*/ /*DEFAULT = 0*/ ); // SKIP = -1 (0xFFFFFFFF)
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_HARDWARE_ENCODER_PROPERTY( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG   nEncoderFormat /*IN*/,  ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/,  ULONG   nDownscaleMode /*IN*/,  ULONG   nPostSkipFrameRate /*IN*/  /*DEFAULT = 0*/, ULONG   nPostAvgFrameRate /*IN*/  /*DEFAULT = 0*/ ); // SKIP = -1 (0xFFFFFFFF)
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_HARDWARE_ENCODER_PROPERTY_EX( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pRecordProfile /*OUT*/, ULONG * pRecordLevel /*OUT*/, ULONG * pRecordEntropy /*OUT*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pBFrames /*OUT*/, BOOL * pIsInterleaved /*OUT*/, ULONG * pSlices /*OUT*/, ULONG * pLayers /*OUT*/, ULONG * pSceneCut /*OUT*/, BOOL * pMultiThread /*OUT*/, BOOL * pMBBRC /*OUT*/, BOOL * pExtBRC /*OUT*/, ULONG * pMinQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pMaxQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVMaxRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVBufSize /*OUT*/ /*DEFAULT = 0*/, ULONG * pCBRVariation /*OUT*/ /*DEFAULT = 0*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/, ULONG * pCropX /*OUT*/, ULONG * pCropY /*OUT*/, ULONG * pCropW /*OUT*/, ULONG * pCropH /*OUT*/, ULONG * pDstW /*OUT*/, ULONG * pDstH /*OUT*/, ULONG * pPostSkipFrameRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pPostAvgFrameRate /*OUT*/ /*DEFAULT = 0*/ ); // SKIP = -1 (0xFFFFFFFF)
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_HARDWARE_ENCODER_PROPERTY_EX( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG   nEncoderFormat /*IN*/,  ULONG   nRecordProfile /*IN*/ , ULONG   nRecordLevel /*IN*/ , ULONG   nRecordEntropy /*IN*/ , ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nBFrames /*IN*/ , BOOL   bIsInterleaved /*IN*/ , ULONG   nSlices /*IN*/ , ULONG   nLayers /*IN*/ , ULONG   nSceneCut /*IN*/,  BOOL   bMultiThread /*IN*/,  BOOL   bMBBRC /*IN*/,  BOOL   bExtBRC /*IN*/,  ULONG   nMinQP /*IN*/  /*DEFAULT = 0*/, ULONG   nMaxQP /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVMaxRate /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVBufSize /*IN*/  /*DEFAULT = 0*/, ULONG   nCBRVariation /*IN*/  /*DEFAULT = 0*/, ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/,  ULONG   nCropX /*IN*/,  ULONG   nCropY /*IN*/,  ULONG   nCropW /*IN*/,  ULONG   nCropH /*IN*/,  ULONG   nDstW /*IN*/,  ULONG   nDstH /*IN*/,  ULONG   nPostSkipFrameRate /*IN*/  /*DEFAULT = 0*/, ULONG   nPostAvgFrameRate /*IN*/  /*DEFAULT = 0*/ ); // SKIP = -1 (0xFFFFFFFF)
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_HARDWARE_ENCODER_VIDEOCOMPRESSION_PROPERTY( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG nProperty /*IN*/, ULONG * pValue /*OUT*/ ); // = AMESDK_GET_VIDEOCOMPRESSION_PROPERTY. PLS CHECK PRODUCT'S EXTRA PROGRAMMING GUIDE.
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_HARDWARE_ENCODER_VIDEOCOMPRESSION_PROPERTY( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG nProperty /*IN*/, ULONG nValue /*IN*/ ); // = AMESDK_SET_VIDEOCOMPRESSION_PROPERTY. PLS CHECK PRODUCT'S EXTRA PROGRAMMING GUIDE.
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_SOUND_RENDERER( PVOID pDevice /*IN*/, UINT * pSoundNum /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_SOUND_RENDERER( PVOID pDevice /*IN*/, UINT iSoundNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_VOLUME( PVOID pDevice /*IN*/, ULONG * pVolume /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_VOLUME( PVOID pDevice /*IN*/, ULONG nVolume /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_DEVICE_CUSTOM_PROPERTY_PAGE( PVOID pDevice /*IN*/, HWND hAttachedWindow /*IN*/, UINT iPageNum = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_DEVICE_CUSTOM_PROPERTY( PVOID pDevice /*IN*/, ULONG nProperty /*IN*/, ULONG * pValue /*OUT*/ ); // = AMESDK_GET_CUSTOM_PROPERTY. PLS CHECK PRODUCT'S EXTRA PROGRAMMING GUIDE.
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_DEVICE_CUSTOM_PROPERTY( PVOID pDevice /*IN*/, ULONG nProperty /*IN*/, ULONG nValue /*IN*/ ); // = AMESDK_SET_CUSTOM_PROPERTY. PLS CHECK PRODUCT'S EXTRA PROGRAMMING GUIDE.
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_DEVICE_CUSTOM_PROPERTY_EX( PVOID pDevice /*IN*/, ULONG nProperty /*IN*/, BYTE * pValue /*OUT*/, ULONG nBytes /*IN*/ ); // = AMESDK_GET_CUSTOM_PROPERTY_EX. PLS CHECK PRODUCT'S EXTRA PROGRAMMING GUIDE.
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_DEVICE_CUSTOM_PROPERTY_EX( PVOID pDevice /*IN*/, ULONG nProperty /*IN*/, BYTE * pValue /*IN*/, ULONG nBytes /*IN*/ ); // = AMESDK_SET_CUSTOM_PROPERTY_EX. PLS CHECK PRODUCT'S EXTRA PROGRAMMING GUIDE.
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DIAGNOSE_VIDEO_STREAM_STATUS( PVOID pDevice /*IN*/, BOOL * pIsStill /*OUT*/, ULONG nStillThreshold = 64 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DIAGNOSE_AUDIO_STREAM_STATUS( PVOID pDevice /*IN*/, double * pVolumeDB_L /*OUT*/ /*RANGE = -100 ~ 0 (DB)*/, double * pVolumeDB_R /*OUT*/ /*RANGE = -100 ~ 0 (DB)*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_NO_SIGNAL_DETECTED_CALLBACK( PVOID pDevice /*IN*/, PF_NO_SIGNAL_DETECTED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SIGNAL_REMOVED_CALLBACK( PVOID pDevice /*IN*/, PF_SIGNAL_REMOVED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_FORMAT_CHANGED_CALLBACK( PVOID pDevice /*IN*/, PF_FORMAT_CHANGED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_FORMAT_CHANGED_CALLBACK_EX( PVOID pDevice /*IN*/, PF_FORMAT_CHANGED_CALLBACK_EX pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_VERTICAL_ANCILLARY_DATA_CALLBACK( PVOID pDevice /*IN*/, PF_VIDEO_VERTICAL_ANCILLARY_DATA_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ ); // VANC
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_PREVIEW_CALLBACK( PVOID pDevice /*IN*/, PF_VIDEO_PREVIEW_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_PREVIEW_CALLBACK( PVOID pDevice /*IN*/, PF_AUDIO_PREVIEW_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_HARDWARE_ENCODER_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_VIDEO_HARDWARE_ENCODER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// LOCK/UNLOCK FUNCTIONS (FOR LABVIEW 2010/2012 DEVELOPER) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COPY_VIDEO_PREVIEW_BUFFER( PVOID pDevice /*IN*/, double * pSampleTime /*OUT*/, ULONG nColorSpaceType /*IN*/, VOID * pFrameBuffer /*IN*/, ULONG * pFrameBufferLen /*IN/OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COPY_AUDIO_PREVIEW_BUFFER( PVOID pDevice /*IN*/, double * pSampleTime /*OUT*/, VOID * pFrameBuffer /*IN*/, ULONG * pFrameBufferLen /*IN/OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_LOCK_VIDEO_PREVIEW_BUFFER( PVOID pDevice /*IN*/, double * pSampleTime /*OUT*/, VOID * * ppFrameBuffer /*OUT*/, ULONG * pFrameBufferLen /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_UNLOCK_VIDEO_PREVIEW_BUFFER( PVOID pDevice /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_LOCK_AUDIO_PREVIEW_BUFFER( PVOID pDevice /*IN*/, double * pSampleTime /*OUT*/, VOID * * ppFrameBuffer /*OUT*/, ULONG * pFrameBufferLen /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_UNLOCK_AUDIO_PREVIEW_BUFFER( PVOID pDevice /*IN*/ );
+
+// SNAPSHOT FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_BMP( PVOID pDevice /*IN*/, CHAR * pszFilePathName /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_JPG( PVOID pDevice /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_BMP_EX( PVOID pDevice /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_JPG_EX( PVOID pDevice /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SNAPSHOT_DONE_CALLBACK( PVOID pDevice /*IN*/, PF_SNAPSHOT_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SNAPSHOT_STREAM_CALLBACK( PVOID pDevice /*IN*/, PF_SNAPSHOT_STREAM_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// CHANNEL RECORDING FUNCTIONS (SUPPORT MULTIPLE-STREAMS CHANNEL RECORDING) (RECNUM: 0 ~ 3) /////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_RECORD_PROPERTY( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/, ULONG * pDownscaleMode /*OUT*/ ); // HARDWARE ENCODER NEED USE QCAP_GET_VIDEO_HARDWARE_ENCODER_PROPERTY
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_RECORD_PROPERTY( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/,  ULONG   nDownscaleMode /*IN*/  ); // HARDWARE ENCODER NEED USE QCAP_SET_VIDEO_HARDWARE_ENCODER_PROPERTY
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_RECORD_PROPERTY_EX( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pRecordProfile /*OUT*/ /*DEFAULT = BASELINE*/, ULONG * pRecordLevel /*OUT*/ /*DEFAULT = 41*/, ULONG * pRecordEntropy /*OUT*/ /*DEFAULT = CAVLC*/, ULONG * pRecordComplexity /*OUT*/ /*DEFAULT = 0*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pBFrames /*OUT*/ /*DEFAULT = 0*/, BOOL * pIsInterleaved /*OUT*/ /*DEFAULT = FALSE*/, ULONG * pSlices /*OUT*/ /*DEFAULT = 0*/, ULONG * pLayers /*OUT*/ /*DEFAULT = 0*/, ULONG * pSceneCut /*OUT*/ /*DEFAULT = 0*/, BOOL * pMultiThread /*OUT*/ /*DEFAULT = TRUE*/, BOOL * pMBBRC /*OUT*/ /*DEFAULT = FALSE*/, BOOL * pExtBRC /*OUT*/ /*DEFAULT = FALSE*/, ULONG * pMinQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pMaxQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVMaxRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVBufSize /*OUT*/ /*DEFAULT = 0*/, ULONG * pCBRVariation /*OUT*/ /*DEFAULT = 0*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/, ULONG * pCropX /*OUT*/, ULONG * pCropY /*OUT*/, ULONG * pCropW /*OUT*/, ULONG * pCropH /*OUT*/, ULONG * pDstW /*OUT*/, ULONG * pDstH /*OUT*/, ULONG * pPostSkipFrameRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pPostAvgFrameRate /*OUT*/ /*DEFAULT = 0*/ ); // HARDWARE ENCODER NEED USE QCAP_GET_VIDEO_HARDWARE_ENCODER_PROPERTY_EX
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_RECORD_PROPERTY_EX( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nRecordProfile /*IN*/  /*DEFAULT = BASELINE*/, ULONG   nRecordLevel /*IN*/  /*DEFAULT = 41*/, ULONG   nRecordEntropy /*IN*/  /*DEFAULT = CAVLC*/, ULONG   nRecordComplexity /*IN*/  /*DEFAULT = 0*/, ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nBFrames /*IN*/  /*DEFAULT = 0*/, BOOL   bIsInterleaved /*IN*/  /*DEFAULT = FALSE*/, ULONG   nSlices /*IN*/  /*DEFAULT = 0*/, ULONG   nLayers /*IN*/  /*DEFAULT = 0*/, ULONG   nSceneCut /*IN*/  /*DEFAULT = 0*/, BOOL   bMultiThread /*IN*/  /*DEFAULT = TRUE*/, BOOL   bMBBRC /*IN*/  /*DEFAULT = FALSE*/, BOOL   bExtBRC /*IN*/  /*DEFAULT = FALSE*/, ULONG   nMinQP /*IN*/  /*DEFAULT = 0*/, ULONG   nMaxQP /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVMaxRate /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVBufSize /*IN*/  /*DEFAULT = 0*/, ULONG   nCBRVariation /*IN*/  /*DEFAULT = 0*/, ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/,  ULONG   nCropX /*IN*/,  ULONG   nCropY /*IN*/,  ULONG   nCropW /*IN*/,  ULONG   nCropH /*IN*/,  ULONG   nDstW /*IN*/,  ULONG   nDstH /*IN*/,  ULONG   nPostSkipFrameRate /*IN*/  /*DEFAULT = 0*/, ULONG   nPostAvgFrameRate /*IN*/  /*DEFAULT = 0*/ ); // HARDWARE ENCODER NEED USE QCAP_SET_VIDEO_HARDWARE_ENCODER_PROPERTY_EX
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_RECORD_PROPERTY( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_RECORD_PROPERTY( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_RECORD_PROPERTY_EX( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pBitRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_RECORD_PROPERTY_EX( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nBitRate /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_RECORD_DYNAMIC_PROPERTY_EX( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_RECORD_DYNAMIC_PROPERTY_EX( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_METADATA_RECORD_HEADER( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, CHAR * pszTitle /*IN*/, CHAR * pszArtist /*IN*/, CHAR * pszComments /*IN*/, CHAR * pszGenre /*IN*/, CHAR * pszComposer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_METADATA_RECORD_HEADER_W( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, WSTRING pwszTitle /*IN*/, WSTRING pwszArtist /*IN*/, WSTRING pwszComments /*IN*/, WSTRING pwszGenre /*IN*/, WSTRING pwszComposer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_METADATA_RECORD_DATA_BUFFER( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, BYTE * pDataBuffer /*IN*/, ULONG nDataBufferSize /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_CLONE_RECORD( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, CHAR * pszFilePathNameA /*IN*/, CHAR * pszFilePathNameB /*IN*/, DWORD dwFlags = QCAP_RECORD_FLAG_FULL /*IN*/, double dVideoDelayTime = 0.0 /*IN*/, double dAudioDelayTime = 0.0 /*IN*/, double dSegmentDurationTime = 0.0 /*IN*/, ULONG nSegmentDurationSizeKB = 0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_TIMESHIFT_RECORD( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID * ppPhysicalFileWriter /*OUT*/, DWORD dwFlags = QCAP_RECORD_FLAG_FULL /*IN*/, double dVideoDelayTime = 0.0 /*IN*/, double dAudioDelayTime = 0.0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_FAILSAFE_RECORD( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, UINT iLinkRecNum /*IN*/, PVOID pLinkPhysicalFileWriter /*IN*/, double dPreRecordTime = 0.0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_RECORD( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, DWORD dwFlags = QCAP_RECORD_FLAG_FULL /*IN*/, double dVideoDelayTime = 0.0 /*IN*/, double dAudioDelayTime = 0.0 /*IN*/, double dSegmentDurationTime = 0.0 /*IN*/, ULONG nSegmentDurationSizeKB = 0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_RECORD( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_RESUME_RECORD( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_PAUSE_RECORD( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SEGMENT_RECORD( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_RECORD_DONE_CALLBACK(  PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_RECORD_DONE_CALLBACK  pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_RECORD_FAIL_CALLBACK(  PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_RECORD_FAIL_CALLBACK  pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_RECORD_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_VIDEO_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_RECORD_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_AUDIO_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_MEDIA_RECORD_CALLBACK( PVOID pDevice /*IN*/, UINT iRecNum /*IN*/, PF_MEDIA_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR TS & FLV FILE FORMAT
+
+// CLONE FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_CLONE( PVOID pDevice /*IN*/, HWND hAttachedWindow /*IN*/, PVOID * ppCloneDevice /*OUT*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/ );
+
+// OSD FUNCTIONS (SUPPORT MULTIPLE-LAYERS OSD OBJECTS) (OSDNUM: 0 ~ 511) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_MOVE_OSD_OBJECT( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ ); // FOR SCROLLING TEXT/PICTURE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_OSD_TEXT_BOUNDARY( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_OSD_TEXT_BOUNDARY_W( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_TEXT( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_TEXT_W( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_TEXT_EX( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_TEXT_EX_W( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_PICTURE( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nTransparent /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_BUFFER( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_BUFFER_EX( PVOID pDevice /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+// MULTIPLE CHANNELS SYNCHRONIZED RECORDING/PLAYBACK FUNCTIONS (SYNCHRONIZED RECORDING/PLAYBACK) ////////////////////////////////////////////////////////////////////////////////
+//
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_SYNCHRONIZED_RECORD( PVOID * ppSyncRecorder /*IN*/, ULONG nFileArgs /*IN*/, CHAR * pszFilePathName1 /*IN*/, PVOID pDevice1 /*IN*/, UINT iRecNum1 /*IN*/, CHAR * pszFilePathName2 /*IN*/, PVOID pDevice2 /*IN*/, UINT iRecNum2 /*IN*/, ... );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_SYNCHRONIZED_RECORD( PVOID pSyncRecorder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_SYNCHRONIZED_TIMESHIFT_RECORD( PVOID pSyncRecorder /*IN*/, PVOID * ppPhysicalFileWriters /*OUT*/, DWORD dwFlags = QCAP_RECORD_FLAG_FULL /*IN*/, double dSegmentDurationTime = 0.0 /*IN*/, ULONG nSegmentDurationSizeKB = 0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_SYNCHRONIZED_RECORD( PVOID pSyncRecorder /*IN*/, DWORD dwFlags = QCAP_RECORD_FLAG_FULL /*IN*/, double dSegmentDurationTime = 0.0 /*IN*/, ULONG nSegmentDurationSizeKB = 0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_SYNCHRONIZED_RECORD( PVOID pSyncRecorder /*IN*/, BOOL bIsAsync /*IN*/, ULONG nMilliseconds /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_RESUME_SYNCHRONIZED_RECORD( PVOID pSyncRecorder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_PAUSE_SYNCHRONIZED_RECORD( PVOID pSyncRecorder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SEGMENT_SYNCHRONIZED_RECORD( PVOID pSyncRecorder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_OPEN_SYNCHRONIZED_FILE( PVOID * ppFile /*OUT*/, ULONG nFileArgs /*IN*/, ULONG nDecoderType /*IN*/, ULONG * pVideoEncoderFormat /*OUT*/, ULONG * pVideoWidth /*OUT*/, ULONG * pVideoHeight /*OUT*/, double * pVideoFrameRate /*OUT*/, ULONG * pAudioEncoderFormat /*OUT*/, ULONG * pAudioChannels /*OUT*/, ULONG * pAudioBitsPerSample /*OUT*/, ULONG * pAudioSampleFrequency /*OUT*/, double * pTotalDurationTimes /*OUT*/, CHAR * pszFilePathName1 /*IN*/,  HWND hAttachedWindow1 /*IN*/, BOOL bThumbDraw1 /*IN*/, BOOL bMaintainAspectRatio1 /*IN*/, CHAR * pszFilePathName2 /*IN*/,  HWND hAttachedWindow2 /*IN*/, BOOL bThumbDraw2 /*IN*/, BOOL bMaintainAspectRatio2 /*IN*/, ... );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_SYNCHRONIZED_FILE_INFO( PVOID pFile /*IN*/, UINT iFileNum /*IN*/, ULONG * pVideoEncoderFormat /*OUT*/, ULONG * pVideoWidth /*OUT*/, ULONG * pVideoHeight /*OUT*/, double * pVideoFrameRate /*OUT*/, ULONG * pAudioEncoderFormat /*OUT*/, ULONG * pAudioChannels /*OUT*/, ULONG * pAudioBitsPerSample /*OUT*/, ULONG * pAudioSampleFrequency /*OUT*/ );
+
+// SYNCHRONIZED RECORDING EXAMPLE
+//
+// QCAP_CREATE_SYNCHRONIZED_RECORD( &pSyncRecorder, 4, "SR_CH01.MP4", pDevices[ 0 ], 0, 
+//
+//								                       "SR_CH02.MP4", pDevices[ 1 ], 0, 
+//
+//								                       "SR_CH03.MP4", pDevices[ 2 ], 0, 
+//
+//								                       "SR_CH04.MP4", pDevices[ 3 ], 0 );
+//
+// QCAP_START_SYNCHRONIZED_RECORD( pSyncRecorder );
+// 
+// QCAP_STOP_SYNCHRONIZED_RECORD( pSyncRecorder, TRUE, 0 );
+// 
+// QCAP_DESTROY_SYNCHRONIZED_RECORD( pSyncRecorder );
+
+// SYNCHRONIZED PLAYBACK EXAMPLE
+//
+// QCAP_OPEN_SYNCHRONIZED_FILE( &pFile, 4, QCAP_DECODER_TYPE_SOFTWARE, &nVideoEncoderFormat, &nVideoWidth, &nVideoHeight, &dVideoFrameRate, 
+//
+//                                                                     &nAudioEncoderFormat, &nAudioChannels, &nAudioBitsPerSample, &pAudioSampleFrequency, 
+//
+//                                                                     &nTotalDurationTimes, 
+
+//                                                                     "SR_CH01.MP4", hAttachedWindow[ 0 ], BOOL bThumbDraw[ 0 ], BOOL bMaintainAspectRatio[ 0 ],
+//
+//                                                                     "SR_CH02.MP4", hAttachedWindow[ 1 ], BOOL bThumbDraw[ 1 ], BOOL bMaintainAspectRatio[ 1 ],
+//
+//                                                                     "SR_CH03.MP4", hAttachedWindow[ 2 ], BOOL bThumbDraw[ 2 ], BOOL bMaintainAspectRatio[ 2 ],
+//
+//                                                                     "SR_CH04.MP4", hAttachedWindow[ 3 ], BOOL bThumbDraw[ 3 ], BOOL bMaintainAspectRatio[ 3 ] );
+// QCAP_PLAY_FILE( pFile );
+//
+// QCAP_STOP_FILE( pFile );
+//
+// QCAP_DESTROY_FILE( pFile );
+//
+// CAMERA FUNCTIONS /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CAMERA_ENUMERATION( CHAR * * ppszCameraDevName /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CAMERA_ENUMERATION_W( WSTRING * ppwszCameraDevName /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_DEFAULT_CAMERA( CHAR * pszCameraDevName /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_CAMERA_COUNT_OF_SUPPORT_OUTPUT_FORMATS( PVOID pDevice /*IN*/, ULONG * pCount /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_CAMERA_SUPPORT_OUTPUT_FORMAT( PVOID pDevice /*IN*/, UINT iFmtNum /*IN*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, BOOL * pIsInterleaved /*OUT*/, double * pFrameRate /*OUT*/ );
+
+// CAMERA EXAMPLE:
+// 
+// STEP#01 QCAP_CREATE( "CAMERA", 0, hWindow, &pDevice, TRUE );
+// 
+// STEP#03 QCAP_RUN( pDevice );
+// 
+// STEP#04 QCAP_STOP( pDevice );
+// 
+// STEP#05 QCAP_DESTROY( pDevice );
+
+// SOUNCARD FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SOUNDCARD_ENUMERATION( CHAR * * ppszSoundCardDevName /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SOUNDCARD_ENUMERATION_W( WSTRING * ppwszSoundCardDevName /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_DEFAULT_SOUNDCARD( CHAR * pszMicrophoneDevName /*IN*/, CHAR * pszLineInDevName /*IN*/ );
+
+// SOUNCARD EXAMPLE:
+// 
+// STEP#01 QCAP_CREATE( "SOUNDCARD", 0, hWindow, &pDevice, TRUE );
+// 
+// STEP#02 QCAP_SET_AUDIO_INPUT( pDevice, QCAP_INPUT_TYPE_SOUNDCARD_LINE_IN );
+// 
+// STEP#03 QCAP_RUN( pDevice );
+// 
+// STEP#04 QCAP_STOP( pDevice );
+// 
+// STEP#05 QCAP_DESTROY( pDevice );
+
+// SHARE RECORDING FUNCTIONS (SUPPORT MULTIPLE-STREAMS SHARE RECORDING) (RECNUM: 0 ~ 63) ////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_SHARE_RECORD_STATUS( UINT iRecNum /*IN*/, BOOL * pIsValid /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARE_RECORD_PROPERTY( UINT iRecNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, double * pFrameRate /*OUT*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/, HWND * pAttachedWindow = NULL /*OUT*/, BOOL * pThumbDraw = NULL /*OUT*/, BOOL * pMaintainAspectRatio = NULL /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_PROPERTY( UINT iRecNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nColorSpaceType /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  double   dFrameRate /*IN*/,  ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/,  HWND   hAttachedWindow = NULL /*IN*/,  BOOL   bThumbDraw = FALSE /*IN*/, BOOL   bMaintainAspectRatio = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARE_RECORD_PROPERTY_EX( UINT iRecNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, double * pFrameRate /*OUT*/, ULONG * pRecordProfile /*OUT*/ /*DEFAULT = BASELINE*/, ULONG * pRecordLevel /*OUT*/ /*DEFAULT = 41*/, ULONG * pRecordEntropy /*OUT*/ /*DEFAULT = CAVLC*/, ULONG * pRecordComplexity /*OUT*/ /*DEFAULT = 0*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pBFrames /*OUT*/ /*DEFAULT = 0*/, BOOL * pIsInterleaved /*OUT*/ /*DEFAULT = FALSE*/, ULONG * pSlices /*OUT*/ /*DEFAULT = 0*/, ULONG * pLayers /*OUT*/ /*DEFAULT = 0*/, ULONG * pSceneCut /*OUT*/ /*DEFAULT = 0*/, BOOL * pMultiThread /*OUT*/ /*DEFAULT = TRUE*/, BOOL * pMBBRC /*OUT*/ /*DEFAULT = FALSE*/, BOOL * pExtBRC /*OUT*/ /*DEFAULT = FALSE*/, ULONG * pMinQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pMaxQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVMaxRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVBufSize /*OUT*/ /*DEFAULT = 0*/, ULONG * pCBRVariation /*OUT*/ /*DEFAULT = 0*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/, HWND * pAttachedWindow = NULL /*OUT*/, BOOL * pThumbDraw = NULL /*OUT*/, BOOL * pMaintainAspectRatio = NULL /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_PROPERTY_EX( UINT iRecNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nColorSpaceType /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  double   dFrameRate /*IN*/,  ULONG   nRecordProfile /*IN*/  /*DEFAULT = BASELINE*/, ULONG   nRecordLevel /*IN*/  /*DEFAULT = 41*/, ULONG   nRecordEntropy /*IN*/  /*DEFAULT = CAVLC*/, ULONG   nRecordComplexity /*IN*/  /*DEFAULT = 0*/, ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nBFrames /*IN*/  /*DEFAULT = 0*/, BOOL   bIsInterleaved /*IN*/  /*DEFAULT = FALSE*/, ULONG   nSlices /*IN*/  /*DEFAULT = 0*/, ULONG   nLayers /*IN*/  /*DEFAULT = 0*/, ULONG   nSceneCut /*IN*/  /*DEFAULT = 0*/, BOOL   bMultiThread /*IN*/  /*DEFAULT = TRUE*/, BOOL   bMBBRC /*IN*/  /*DEFAULT = FALSE*/, BOOL   bExtBRC /*IN*/  /*DEFAULT = FALSE*/, ULONG   nMinQP /*IN*/  /*DEFAULT = 0*/, ULONG   nMaxQP /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVMaxRate /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVBufSize /*IN*/  /*DEFAULT = 0*/, ULONG   nCBRVariation /*IN*/  /*DEFAULT = 0*/, ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/,  HWND   hAttachedWindow = NULL /*IN*/,  BOOL   bThumbDraw = FALSE /*IN*/, BOOL   bMaintainAspectRatio = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARE_RECORD_DYNAMIC_PROPERTY_EX( UINT iRecNum /*IN*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_DYNAMIC_PROPERTY_EX( UINT iRecNum /*IN*/, ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARE_RECORD_COPP( UINT iRecNum /*IN*/, BOOL * pEnable /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_COPP( UINT iRecNum /*IN*/, BOOL   bEnable /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARE_RECORD_BRIGHTNESS( UINT iRecNum /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_BRIGHTNESS( UINT iRecNum /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARE_RECORD_CONTRAST( UINT iRecNum /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_CONTRAST( UINT iRecNum /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARE_RECORD_HUE( UINT iRecNum /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_HUE( UINT iRecNum /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_SHARE_RECORD_SATURATION( UINT iRecNum /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_SATURATION( UINT iRecNum /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_SHARE_RECORD_PROPERTY( UINT iRecNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_SHARE_RECORD_PROPERTY( UINT iRecNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_SHARE_RECORD_PROPERTY_EX( UINT iRecNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/, ULONG * pBitRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_SHARE_RECORD_PROPERTY_EX( UINT iRecNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/,  ULONG   nBitRate /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_SHARE_RECORD_SOUND_RENDERER( UINT iRecNum /*IN*/, UINT * pSoundNum /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_SHARE_RECORD_SOUND_RENDERER( UINT iRecNum /*IN*/, UINT   iSoundNum /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_SHARE_RECORD_VOLUME( UINT iRecNum /*IN*/, ULONG * pVolume /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_SHARE_RECORD_VOLUME( UINT iRecNum /*IN*/, ULONG   nVolume /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_UNCOMPRESSION_BUFFER( UINT iRecNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_UNCOMPRESSION_BUFFER_EX( UINT iRecNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/, BOOL bForceKeyFrame = FALSE /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_SHARE_RECORD_UNCOMPRESSION_BUFFER( UINT iRecNum /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_SHARE_RECORD_UNCOMPRESSION_BUFFER_EX( UINT iRecNum /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_SHARE_RECORD_COMPRESSION_BUFFER( UINT iRecNum /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, ULONG bIsKeyFrame /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_SHARE_RECORD_COMPRESSION_BUFFER( UINT iRecNum /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_METADATA_SHARE_RECORD_HEADER( UINT iRecNum /*IN*/, CHAR * pszTitle /*IN*/, CHAR * pszArtist /*IN*/, CHAR * pszComments /*IN*/, CHAR * pszGenre /*IN*/, CHAR * pszComposer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_METADATA_SHARE_RECORD_HEADER_W( UINT iRecNum /*IN*/, WSTRING pwszTitle /*IN*/, WSTRING pwszArtist /*IN*/, WSTRING pwszComments /*IN*/, WSTRING pwszGenre /*IN*/, WSTRING pwszComposer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_METADATA_SHARE_RECORD_DATA_BUFFER( UINT iRecNum /*IN*/, BYTE * pDataBuffer /*IN*/, ULONG nDataBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_TIMESHIFT_SHARE_RECORD( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID * ppPhysicalFileWriter /*OUT*/, DWORD dwFlags = QCAP_RECORD_FLAG_FULL /*IN*/, double dVideoDelayTime = 0.0 /*IN*/, double dAudioDelayTime = 0.0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_SHARE_RECORD( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, DWORD dwFlags = QCAP_RECORD_FLAG_FULL /*IN*/, double dVideoDelayTime = 0.0 /*IN*/, double dAudioDelayTime = 0.0 /*IN*/, double dSegmentDurationTime = 0.0 /*IN*/, ULONG nSegmentDurationSizeKB = 0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_SHARE_RECORD( UINT iRecNum /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_RESUME_SHARE_RECORD( UINT iRecNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_PAUSE_SHARE_RECORD( UINT iRecNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SEGMENT_SHARE_RECORD( UINT iRecNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_SHARE_RECORD_BMP( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_SHARE_RECORD_JPG( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_SHARE_RECORD_BMP_EX( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_SHARE_RECORD_JPG_EX( UINT iRecNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_MOVE_OSD_SHARE_RECORD_OBJECT( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ ); // FOR SCROLLING TEXT/PICTURE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_OSD_SHARE_RECORD_TEXT_BOUNDARY( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_OSD_SHARE_RECORD_TEXT_BOUNDARY_W( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_SHARE_RECORD_TEXT( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_SHARE_RECORD_TEXT_W( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_SHARE_RECORD_TEXT_EX( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_SHARE_RECORD_TEXT_EX_W( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_SHARE_RECORD_PICTURE( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nTransparent /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_SHARE_RECORD_BUFFER( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_OSD_SHARE_RECORD_BUFFER_EX( UINT iRecNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SHARE_RECORD_DONE_CALLBACK( UINT iRecNum /*IN*/, PF_SHARE_RECORD_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SHARE_RECORD_FAIL_CALLBACK( UINT iRecNum /*IN*/, PF_SHARE_RECORD_FAIL_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SHARE_RECORD_SNAPSHOT_DONE_CALLBACK( UINT iRecNum /*IN*/, PF_SHARE_RECORD_SNAPSHOT_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SHARE_RECORD_SNAPSHOT_STREAM_CALLBACK( UINT iRecNum /*IN*/, PF_SHARE_RECORD_SNAPSHOT_STREAM_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_VIDEO_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_MEDIA_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_MEDIA_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR TS & FLV FILE FORMAT
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_DECODER_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_VIDEO_DECODER_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_DECODER_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_DECODER_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_DISPLAY_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_VIDEO_DISPLAY_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_DISPLAY_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_DISPLAY_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_VIDEO_SHARE_RECORD_MEDIA_TIMER_CALLBACK( UINT iRecNum /*IN*/, PF_VIDEO_SHARE_RECORD_MEDIA_TIMER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_SHARE_RECORD_MEDIA_TIMER_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_SHARE_RECORD_MEDIA_TIMER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// VIDEO 3D SHARE RECORDING FUNCTIONS (SUPPORT MULTIPLE-STREAMS SHARE RECORDING) (RECNUM: 0 ~ 63) ///////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_3D_SHARE_RECORD_STEREO_UNCOMPRESSION_BUFFER( UINT iRecNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nStereoBufferType = QCAP_3D_STEREO_BUFFER_SIDE_BY_SIDE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_3D_SHARE_RECORD_STEREO_UNCOMPRESSION_BUFFER_EX( UINT iRecNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/, ULONG nStereoBufferType = QCAP_3D_STEREO_BUFFER_SIDE_BY_SIDE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_3D_SHARE_RECORD_L_UNCOMPRESSION_BUFFER( UINT iRecNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_3D_SHARE_RECORD_L_UNCOMPRESSION_BUFFER_EX( UINT iRecNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_3D_SHARE_RECORD_R_UNCOMPRESSION_BUFFER( UINT iRecNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_3D_SHARE_RECORD_R_UNCOMPRESSION_BUFFER_EX( UINT iRecNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_3D_SHARE_RECORD_UNCOMPRESSION_BUFFER( UINT iRecNum /*IN*/, ULONG nStereoDisplayMode = QCAP_3D_STEREO_DISPLAY_MODE_LINE_BY_LINE /*IN*/, BOOL bLeftRightSwap = FALSE /*IN*/, BOOL bForceKeyFrame = FALSE /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+// AUDIO MIXING/TRACKS SHARE RECORDING FUNCTIONS (SUPPORT MULTIPLE-STREAMS SHARE RECORDING) (RECNUM: 0 ~ 63) (TRACKNUM: 0 ~ 3) //////////////////////////////////////////////////////////////////////
+//
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_MX_SHARE_RECORD_PROPERTY_EX( UINT iRecNum /*IN*/, ULONG * pTracks /*OUT*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/, ULONG * pBitRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_MX_SHARE_RECORD_PROPERTY_EX( UINT iRecNum /*IN*/, ULONG   nTracks /*IN*/,  ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/,  ULONG   nBitRate /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_MX_SHARE_RECORD_MIXING_UNCOMPRESSION_BUFFER( UINT iRecNum /*IN*/, UINT iTrackNum /*IN*/, UINT iMixNum /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_MX_SHARE_RECORD_MIXING_UNCOMPRESSION_BUFFER_EX( UINT iRecNum /*IN*/, UINT iTrackNum /*IN*/, UINT iMixNum /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_MX_SHARE_RECORD_UNCOMPRESSION_BUFFER( UINT iRecNum /*IN*/, UINT iTrackNum /*IN*/, double dSampleTime = 0.0 /*IN*/, BOOL bEnableSyncMixer = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_AUDIO_MX_SHARE_RECORD_COMPRESSION_BUFFER( UINT iRecNum /*IN*/, UINT iTrackNum /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_MX_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_MX_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_AUDIO_DECODER_MX_SHARE_RECORD_CALLBACK( UINT iRecNum /*IN*/, PF_AUDIO_DECODER_MX_SHARE_RECORD_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # BROADCASTING SERVER FUNCTIONS (RTSP/RTMP/HLS/TS/MMS) (2D/3D)
+// #
+// ##########################################################################################################################################################################################
+//
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_SERVER_GET_CUSTOM_PROPERTY_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszProperty /*IN*/, CHAR * pszValue /*OUT*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_SERVER_SET_CUSTOM_PROPERTY_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszProperty /*IN*/, CHAR * pszValue /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_SERVER_SNAPSHOT_DONE_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_SERVER_SNAPSHOT_STREAM_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_BROADCAST_SERVER_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, BOOL bIsKeyFrame /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_BROADCAST_SERVER_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_MX_BROADCAST_SERVER_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iTrackNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_DECODER_BROADCAST_SERVER_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_DECODER_BROADCAST_SERVER_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_DECODER_MX_BROADCAST_SERVER_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iTrackNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_BROADCAST_SERVER_MEDIA_TIMER_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, double dDelayTime /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_BROADCAST_SERVER_MEDIA_TIMER_CALLBACK)( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, double dDelayTime /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_BROADCAST_SERVER_STATUS( UINT iSvrNum /*IN*/ /*0 ~ 63*/, BOOL * pIsValid /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |2D|3D|  |*/ QCAP_CREATE_BROADCAST_RTSP_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, ULONG nTotalSessions /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszAccount = NULL /*IN*/, CHAR * pszPassword = NULL /*IN*/, ULONG nNetworkPort_RTSP = 554 /*IN*/, ULONG nNetworkPort_RTSPOverHTTP = 0 /*IN*/ /*DEFAULT = TURN OFF*/, BOOL bEnableMulticasting = FALSE /*IN*/, BOOL bEnableUltraLowLatency = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |2D|3D|  |*/ QCAP_CREATE_BROADCAST_RTSP_RAW_UDP_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, ULONG nTotalSessions /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszAccount = NULL /*IN*/, CHAR * pszPassword = NULL /*IN*/, ULONG nNetworkPort_RTSP = 554 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |HLS|  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_HLS_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, ULONG nTotalSessions /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszWebServerRootFolderPath /*IN*/, CHAR * pszSubFolderPath /*IN*/, ULONG nSegmentDuration = 1000 /*IN*/, BOOL bResumeSegmentNum = FALSE /*IN*/, ULONG nSegmentPlaylistCount = 3 /*IN*/, BOOL bSegmentPreserveAll = FALSE /*IN*/, CHAR * pszWebServerIP = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_RTMP_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, ULONG nTotalSessions /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszAccount = NULL /*IN*/, CHAR * pszPassword = NULL /*IN*/, ULONG nNetworkPort_RTMP = 1935 /*IN*/, ULONG nNetworkPort_RTMPOverHTTP = 0 /*IN*/ /*DEFAULT = TURN OFF*/, CHAR * pszServerName = NULL /*IN*/ /*DEFAULT = "flvplayback"*/, CHAR * pszMediaFolderPath = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_RTMP_SERVER_EX( UINT iSvrNum /*IN*/ /*0 ~ 63*/, ULONG nTotalSessions /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszAccount = NULL /*IN*/, CHAR * pszPassword = NULL /*IN*/, ULONG nNetworkPort_RTMP = 1935 /*IN*/, ULONG nNetworkPort_RTMPOverHTTP = 0 /*IN*/ /*DEFAULT = TURN OFF*/, CHAR * pszServerName = NULL /*IN*/ /*DEFAULT = "flvplayback"*/, CHAR * pszMediaFolderPath = NULL /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_RTMP_WEB_PORTAL_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszAccount = NULL /*IN*/, CHAR * pszPassword = NULL /*IN*/, ULONG nConnectionTimeout = 10000 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_RTMP_WEB_PORTAL_SERVER_EX( UINT iSvrNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszAccount = NULL /*IN*/, CHAR * pszPassword = NULL /*IN*/, ULONG nConnectionTimeout = 10000 /*IN*/ /*ms*/, CHAR * pszEncryptKey = NULL /*IN*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_FACEBOOK_RTMP_WEB_PORTAL_SERVER( UINT iSvrNum /*IN*/, CHAR * pszAccount /*IN*/, CHAR * pszPassword /*IN*/, PVOID * ppServer /*OUT*/, ULONG nConnectionTimeout = 10000 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_FACEBOOK_RTMP_WEB_PORTAL_SERVER_EX( UINT iSvrNum /*IN*/, CHAR * pszAccount /*IN*/, CHAR * pszPassword /*IN*/, PVOID * ppServer /*OUT*/, ULONG nConnectionTimeout = 10000 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_YOUTUBE_RTMP_WEB_PORTAL_SERVER( UINT iSvrNum /*IN*/, CHAR * pszAccount /*IN*/, CHAR * pszPassword /*IN*/, PVOID * ppServer /*OUT*/, ULONG nConnectionTimeout = 10000 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_YOUTUBE_RTMP_WEB_PORTAL_SERVER_EX( UINT iSvrNum /*IN*/, CHAR * pszAccount /*IN*/, CHAR * pszPassword /*IN*/, PVOID * ppServer /*OUT*/, ULONG nConnectionTimeout = 10000 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_TWITCH_RTMP_WEB_PORTAL_SERVER( UINT iSvrNum /*IN*/, CHAR * pszAccount /*IN*/, CHAR * pszPassword /*IN*/, PVOID * ppServer /*OUT*/, ULONG nConnectionTimeout = 10000 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |RTMP|   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_TWITCH_RTMP_WEB_PORTAL_SERVER_EX( UINT iSvrNum /*IN*/, CHAR * pszAccount /*IN*/, CHAR * pszPassword /*IN*/, PVOID * ppServer /*OUT*/, ULONG nConnectionTimeout = 10000 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |   |  |MMS|2D|  |  |*/ QCAP_CREATE_BROADCAST_MMS_WEB_PORTAL_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszAccount = NULL /*IN*/, CHAR * pszPassword = NULL /*IN*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |   |TS|   |2D|  |  |*/ QCAP_CREATE_BROADCAST_TS_OVER_RTP_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszNetworkAdapterIP = NULL /*IN*/, ULONG nServiceID = 1 /*IN*/, CHAR * pszServiceName = NULL /*IN*/, CHAR * pszServiceProviderName = NULL /*IN*/, BOOL bEnableCBRMode = TRUE /*IN*/, ULONG nTransferBitRate = 0 /*IN*/ /*0 = FREE*/, ULONG nOriginalNetworkID = 1 /*IN*/, ULONG nTransportStreamID = 1 /*IN*/, ULONG nPMT_PID = 4096 /*IN*/, ULONG nPCR_PID = 4097 /*IN*/, ULONG nVideo_PID = 256 /*IN*/, ULONG nAudio_PID = 257 /*IN*/, ULONG nVideo_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nAudio_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nPCRInterval = 0 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |   |TS|   |2D|  |  |*/ QCAP_CREATE_BROADCAST_TS_OVER_UDP_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszNetworkAdapterIP = NULL /*IN*/, ULONG nServiceID = 1 /*IN*/, CHAR * pszServiceName = NULL /*IN*/, CHAR * pszServiceProviderName = NULL /*IN*/, BOOL bEnableCBRMode = TRUE /*IN*/, ULONG nTransferBitRate = 0 /*IN*/ /*0 = FREE*/, ULONG nOriginalNetworkID = 1 /*IN*/, ULONG nTransportStreamID = 1 /*IN*/, ULONG nPMT_PID = 4096 /*IN*/, ULONG nPCR_PID = 4097 /*IN*/, ULONG nVideo_PID = 256 /*IN*/, ULONG nAudio_PID = 257 /*IN*/, ULONG nVideo_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nAudio_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nPCRInterval = 0 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |   |TS|   |2D|  |  |*/ QCAP_CREATE_BROADCAST_TS_OVER_TCP_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, PVOID * ppServer /*OUT*/, ULONG nNetworkPort_TCP = 1234 /*IN*/, CHAR * pszNetworkAdapterIP = NULL /*IN*/, ULONG nServiceID = 1 /*IN*/, CHAR * pszServiceName = NULL /*IN*/, CHAR * pszServiceProviderName = NULL /*IN*/, ULONG nTransferBitRate = 0 /*IN*/ /*0 = FREE*/, ULONG nOriginalNetworkID = 1 /*IN*/, ULONG nTransportStreamID = 1 /*IN*/, ULONG nPMT_PID = 4096 /*IN*/, ULONG nPCR_PID = 4097 /*IN*/, ULONG nVideo_PID = 256 /*IN*/, ULONG nAudio_PID = 257 /*IN*/, ULONG nVideo_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nAudio_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nPCRInterval = 0 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |   |TS|   |2D|  |  |*/ QCAP_CREATE_BROADCAST_TS_OVER_HTTP_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, PVOID * ppServer /*OUT*/, ULONG nNetworkPort_HTTP = 8888 /*IN*/, CHAR * pszNetworkAdapterIP = NULL /*IN*/, ULONG nServiceID = 1 /*IN*/, CHAR * pszServiceName = NULL /*IN*/, CHAR * pszServiceProviderName = NULL /*IN*/, ULONG nTransferBitRate = 0 /*IN*/ /*0 = FREE*/, ULONG nOriginalNetworkID = 1 /*IN*/, ULONG nTransportStreamID = 1 /*IN*/, ULONG nPMT_PID = 4096 /*IN*/, ULONG nPCR_PID = 4097 /*IN*/, ULONG nVideo_PID = 256 /*IN*/, ULONG nAudio_PID = 257 /*IN*/, ULONG nVideo_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nAudio_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nPCRInterval = 0 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |   |TS|   |2D|  |  |*/ QCAP_CREATE_BROADCAST_TS_OVER_SRT_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, PVOID * ppServer /*OUT*/, ULONG nNetworkPort_SRT = 1234 /*IN*/, CHAR * pszNetworkAdapterIP = NULL /*IN*/, ULONG nServiceID = 1 /*IN*/, CHAR * pszServiceName = NULL /*IN*/, CHAR * pszServiceProviderName = NULL /*IN*/, ULONG nTransferBitRate = 0 /*IN*/ /*0 = FREE*/, ULONG nOriginalNetworkID = 1 /*IN*/, ULONG nTransportStreamID = 1 /*IN*/, ULONG nPMT_PID = 4096 /*IN*/, ULONG nPCR_PID = 4097 /*IN*/, ULONG nVideo_PID = 256 /*IN*/, ULONG nAudio_PID = 257 /*IN*/, ULONG nVideo_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nAudio_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nPCRInterval = 0 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |   |TS|   |2D|  |  |*/ QCAP_CREATE_BROADCAST_TS_OVER_TCP_PASSIVE_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppServer /*OUT*/, ULONG nServiceID = 1 /*IN*/, CHAR * pszServiceName = NULL /*IN*/, CHAR * pszServiceProviderName = NULL /*IN*/, ULONG nTransferBitRate = 0 /*IN*/ /*0 = FREE*/, ULONG nOriginalNetworkID = 1 /*IN*/, ULONG nTransportStreamID = 1 /*IN*/, ULONG nPMT_PID = 4096 /*IN*/, ULONG nPCR_PID = 4097 /*IN*/, ULONG nVideo_PID = 256 /*IN*/, ULONG nAudio_PID = 257 /*IN*/, ULONG nVideo_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nAudio_CodecID = 0 /*IN*/ /*0 = AUTO*/, ULONG nPCRInterval = 0 /*IN*/ /*ms*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |   |  |   |2D|  |  |*/ QCAP_CREATE_BROADCAST_NDI_SERVER( UINT iSvrNum /*IN*/ /*0 ~ 63*/, CHAR * pszNDIName /*IN*/, CHAR * pszGroupName /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszConnectionMetadata  = NULL /*IN*/ /*XML*/ ); // TOTOAL SESSION = 1
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_DESTROY_BROADCAST_SERVER( PVOID pServer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_START_BROADCAST_SERVER( PVOID pServer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_STOP_BROADCAST_SERVER( PVOID pServer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|  |  |*/ QCAP_SET_SESSION_BROADCAST_SERVER_SOURCE( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PVOID pDevice /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|  |  |*/ QCAP_SET_SESSION_BROADCAST_SERVER_SOURCE_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PVOID pDevice /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_SESSION_BROADCAST_SERVER_PROPERTY( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * * ppszSessionName /*OUT*/, ULONG * pVideoDelayLiveDuration /*OUT*/ /*ms*/, ULONG * pAudioDelayLiveDuration /*OUT*/ /*ms*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_SESSION_BROADCAST_SERVER_PROPERTY( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszSessionName /*IN*/ /*DEFAULT = "session%d.mpg"*/, ULONG nVideoDelayLiveDuration /*IN*/ /*ms*/, ULONG nAudioDelayLiveDuration /*IN*/ /*ms*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_VIDEO_BROADCAST_SERVER_PROPERTY( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, double * pFrameRate /*OUT*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/, HWND * pAttachedWindow = NULL /*OUT*/, BOOL * pThumbDraw = NULL /*OUT*/, BOOL * pMaintainAspectRatio = NULL /*OUT*/, DWORD * pFlags = NULL /*OUT*/                    );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_VIDEO_BROADCAST_SERVER_PROPERTY( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nColorSpaceType /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  double   dFrameRate /*IN*/,  ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/,  HWND   hAttachedWindow = NULL /*IN*/,  BOOL   bThumbDraw = FALSE /*IN*/, BOOL   bMaintainAspectRatio = FALSE /*IN*/, DWORD  dwFlags = QCAP_BROADCAST_FLAG_FULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_VIDEO_BROADCAST_SERVER_PROPERTY_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, double * pFrameRate /*OUT*/, ULONG * pRecordProfile /*OUT*/ /*DEFAULT = BASELINE*/, ULONG * pRecordLevel /*OUT*/ /*DEFAULT = 41*/, ULONG * pRecordEntropy /*OUT*/ /*DEFAULT = CAVLC*/, ULONG * pRecordComplexity /*OUT*/ /*DEFAULT = 0*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pBFrames /*OUT*/ /*DEFAULT = 0*/, BOOL * pIsInterleaved /*OUT*/ /*DEFAULT = FALSE*/, ULONG * pSlices /*OUT*/ /*DEFAULT = 0*/, ULONG * pLayers /*OUT*/ /*DEFAULT = 0*/, ULONG * pSceneCut /*OUT*/ /*DEFAULT = 0*/, BOOL * pMultiThread /*OUT*/ /*DEFAULT = TRUE*/, BOOL * pMBBRC /*OUT*/ /*DEFAULT = FALSE*/, BOOL * pExtBRC /*OUT*/ /*DEFAULT = FALSE*/, ULONG * pMinQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pMaxQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVMaxRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVBufSize /*OUT*/ /*DEFAULT = 0*/, ULONG * pCBRVariation /*OUT*/ /*DEFAULT = 0*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/, HWND * pAttachedWindow = NULL /*OUT*/, BOOL * pThumbDraw = NULL /*OUT*/, BOOL * pMaintainAspectRatio = NULL /*OUT*/, DWORD * pFlags = NULL /*OUT*/                    );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_VIDEO_BROADCAST_SERVER_PROPERTY_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nColorSpaceType /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  double   dFrameRate /*IN*/,  ULONG   nRecordProfile /*IN*/  /*DEFAULT = BASELINE*/, ULONG   nRecordLevel /*IN*/  /*DEFAULT = 41*/, ULONG   nRecordEntropy /*IN*/  /*DEFAULT = CAVLC*/, ULONG   nRecordComplexity /*IN*/  /*DEFAULT = 0*/, ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nBFrames /*IN*/  /*DEFAULT = 0*/, BOOL   bIsInterleaved /*IN*/  /*DEFAULT = FALSE*/, ULONG   nSlices /*IN*/  /*DEFAULT = 0*/, ULONG   nLayers /*IN*/  /*DEFAULT = 0*/, ULONG   nSceneCut /*IN*/  /*DEFAULT = 0*/, BOOL   bMultiThread /*IN*/  /*DEFAULT = TRUE*/, BOOL   bMBBRC /*IN*/  /*DEFAULT = FALSE*/, BOOL   bExtBRC /*IN*/  /*DEFAULT = FALSE*/, ULONG   nMinQP /*IN*/  /*DEFAULT = 0*/, ULONG   nMaxQP /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVMaxRate /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVBufSize /*IN*/  /*DEFAULT = 0*/, ULONG   nCBRVariation /*IN*/  /*DEFAULT = 0*/, ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/,  HWND   hAttachedWindow = NULL /*IN*/,  BOOL   bThumbDraw = FALSE /*IN*/, BOOL   bMaintainAspectRatio = FALSE /*IN*/, DWORD  dwFlags = QCAP_BROADCAST_FLAG_FULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_VIDEO_BROADCAST_SERVER_DYNAMIC_PROPERTY_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_VIDEO_BROADCAST_SERVER_DYNAMIC_PROPERTY_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_AUDIO_BROADCAST_SERVER_PROPERTY( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_AUDIO_BROADCAST_SERVER_PROPERTY( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_AUDIO_BROADCAST_SERVER_PROPERTY_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/, ULONG * pBitRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_AUDIO_BROADCAST_SERVER_PROPERTY_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/,  ULONG   nBitRate /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_GET_AUDIO_MX_BROADCAST_SERVER_PROPERTY_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG * pTracks /*OUT*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/, ULONG * pBitRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_SET_AUDIO_MX_BROADCAST_SERVER_PROPERTY_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG   nTracks /*IN*/,  ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/,  ULONG   nBitRate /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_AUDIO_BROADCAST_SERVER_SOUND_RENDERER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT * pSoundNum /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_AUDIO_BROADCAST_SERVER_SOUND_RENDERER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT   iSoundNum /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_AUDIO_BROADCAST_SERVER_VOLUME( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG * pVolume /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_AUDIO_BROADCAST_SERVER_VOLUME( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG   nVolume /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_VIDEO_BROADCAST_SERVER_NETWORK_QOS_STATUS( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, BOOL * pIsTransferBusy /*OUT*/, ULONG * pQueueFrames /*OUT*/, ULONG * pQueueBytes /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_AUDIO_BROADCAST_SERVER_NETWORK_QOS_STATUS( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, BOOL * pIsTransferBusy /*OUT*/, ULONG * pQueueFrames /*OUT*/, ULONG * pQueueBytes /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_VIDEO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_VIDEO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/, BOOL bForceKeyFrame = FALSE /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_AUDIO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_AUDIO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_VIDEO_BROADCAST_SERVER_COMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, ULONG bIsKeyFrame /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_AUDIO_BROADCAST_SERVER_COMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |  |3D|  |*/ QCAP_SET_VIDEO_3D_BROADCAST_SERVER_STEREO_UNCOMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nStereoBufferType = QCAP_3D_STEREO_BUFFER_SIDE_BY_SIDE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |  |3D|  |*/ QCAP_SET_VIDEO_3D_BROADCAST_SERVER_STEREO_UNCOMPRESSION_BUFFER_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/, ULONG nStereoBufferType = QCAP_3D_STEREO_BUFFER_SIDE_BY_SIDE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |  |3D|  |*/ QCAP_SET_VIDEO_3D_BROADCAST_SERVER_L_UNCOMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |  |3D|  |*/ QCAP_SET_VIDEO_3D_BROADCAST_SERVER_L_UNCOMPRESSION_BUFFER_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |  |3D|  |*/ QCAP_SET_VIDEO_3D_BROADCAST_SERVER_R_UNCOMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |  |3D|  |*/ QCAP_SET_VIDEO_3D_BROADCAST_SERVER_R_UNCOMPRESSION_BUFFER_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |  |3D|  |*/ QCAP_SET_VIDEO_3D_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, ULONG nStereoDisplayMode = QCAP_3D_STEREO_DISPLAY_MODE_LINE_BY_LINE /*IN*/, BOOL bLeftRightSwap = FALSE /*IN*/, BOOL bForceKeyFrame = FALSE /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_SET_AUDIO_MX_BROADCAST_SERVER_MIXING_UNCOMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iTrackNum /*IN*/, UINT iMixNum /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_SET_AUDIO_MX_BROADCAST_SERVER_MIXING_UNCOMPRESSION_BUFFER_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iTrackNum /*IN*/, UINT iMixNum /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_SET_AUDIO_MX_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iTrackNum /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/, BOOL bEnableSyncMixer = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_SET_AUDIO_MX_BROADCAST_SERVER_COMPRESSION_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iTrackNum /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_CLEAR_VIDEO_BROADCAST_SERVER_DELAY_LIVE_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, BOOL bEnableClear /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, ULONG bIsKeyFrame /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_CLEAR_AUDIO_BROADCAST_SERVER_DELAY_LIVE_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, BOOL bEnableClear /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_CLEAR_AUDIO_MX_BROADCAST_SERVER_DELAY_LIVE_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iTrackNum /*IN*/, BOOL bEnableClear /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ /*HLS NEED IT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SNAPSHOT_BROADCAST_SERVER_BMP( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SNAPSHOT_BROADCAST_SERVER_JPG( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SNAPSHOT_BROADCAST_SERVER_BMP_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SNAPSHOT_BROADCAST_SERVER_JPG_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_MOVE_OSD_BROADCAST_SERVER_OBJECT( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ ); // FOR SCROLLING TEXT/PICTURE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_OSD_BROADCAST_SERVER_TEXT_BOUNDARY( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_GET_OSD_BROADCAST_SERVER_TEXT_BOUNDARY_W( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_OSD_BROADCAST_SERVER_TEXT( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_OSD_BROADCAST_SERVER_TEXT_W( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_OSD_BROADCAST_SERVER_TEXT_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_OSD_BROADCAST_SERVER_TEXT_EX_W( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_OSD_BROADCAST_SERVER_PICTURE( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nTransparent /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_OSD_BROADCAST_SERVER_BUFFER( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_SET_OSD_BROADCAST_SERVER_BUFFER_EX( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |2D|3D|  |*/ QCAP_REGISTER_BROADCAST_SERVER_GET_CUSTOM_PROPERTY_CALLBACK( PVOID pServer /*IN*/, PF_BROADCAST_SERVER_GET_CUSTOM_PROPERTY_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |   |  |   |2D|3D|  |*/ QCAP_REGISTER_BROADCAST_SERVER_SET_CUSTOM_PROPERTY_CALLBACK( PVOID pServer /*IN*/, PF_BROADCAST_SERVER_SET_CUSTOM_PROPERTY_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_REGISTER_BROADCAST_SERVER_SNAPSHOT_DONE_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_BROADCAST_SERVER_SNAPSHOT_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_REGISTER_BROADCAST_SERVER_SNAPSHOT_STREAM_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_BROADCAST_SERVER_SNAPSHOT_STREAM_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_REGISTER_VIDEO_BROADCAST_SERVER_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_VIDEO_BROADCAST_SERVER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_REGISTER_AUDIO_BROADCAST_SERVER_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_AUDIO_BROADCAST_SERVER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_REGISTER_VIDEO_DECODER_BROADCAST_SERVER_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_VIDEO_DECODER_BROADCAST_SERVER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_REGISTER_AUDIO_DECODER_BROADCAST_SERVER_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_AUDIO_DECODER_BROADCAST_SERVER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_REGISTER_AUDIO_MX_BROADCAST_SERVER_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_AUDIO_MX_BROADCAST_SERVER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|MX|*/ QCAP_REGISTER_AUDIO_DECODER_MX_BROADCAST_SERVER_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_AUDIO_DECODER_MX_BROADCAST_SERVER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_REGISTER_VIDEO_BROADCAST_SERVER_MEDIA_TIMER_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_VIDEO_BROADCAST_SERVER_MEDIA_TIMER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|HLS|TS|MMS|2D|3D|  |*/ QCAP_REGISTER_AUDIO_BROADCAST_SERVER_MEDIA_TIMER_CALLBACK( PVOID pServer /*IN*/, UINT iSessionNum /*IN*/, PF_AUDIO_BROADCAST_SERVER_MEDIA_TIMER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// BROADCASTING RTSP SERVER EXAMPLE:
+//
+// STEP#01 QCAP_CREATE_BROADCAST_RTSP_SERVER( 0, 4 /*4 CHANNELS*/, &pServer, "root", "root", 554 );
+//
+//         CH01 URL -> rtsp://root:root@127.0.0.1:554/session0.mpg
+//
+//         CH02 URL -> rtsp://root:root@127.0.0.1:554/session1.mpg
+//
+//         CH03 URL -> rtsp://root:root@127.0.0.1:554/session2.mpg
+//
+//         CH04 URL -> rtsp://root:root@127.0.0.1:554/session3.mpg
+// 
+// STEP#02 QCAP_SET_VIDEO_BROADCAST_SERVER_PROPERTY( pServer, 0 /*CH01*/, QCAP_ENCODER_TYPE_INTEL_MEDIA_SDK, QCAP_ENCODER_FORMAT_H264, QCAP_COLORSPACE_TYPE_YUY2, 480, 270, 30, QCAP_RECORD_MODE_CBR, 8000, 1000000, 30, 0, 0, NULL, FALSE, QCAP_BROADCAST_FLAG_NETWORK | QCAP_BROADCAST_FLAG_ENCODE );
+//
+//         QCAP_SET_VIDEO_BROADCAST_SERVER_PROPERTY( pServer, 1 /*CH02*/, QCAP_ENCODER_TYPE_INTEL_MEDIA_SDK, QCAP_ENCODER_FORMAT_H264, QCAP_COLORSPACE_TYPE_YUY2, 480, 270, 30, QCAP_RECORD_MODE_CBR, 8000, 1000000, 30, 0, 0, NULL, FALSE, QCAP_BROADCAST_FLAG_NETWORK | QCAP_BROADCAST_FLAG_ENCODE );
+//
+//         QCAP_SET_VIDEO_BROADCAST_SERVER_PROPERTY( pServer, 2 /*CH03*/, QCAP_ENCODER_TYPE_INTEL_MEDIA_SDK, QCAP_ENCODER_FORMAT_H264, QCAP_COLORSPACE_TYPE_YUY2, 480, 270, 30, QCAP_RECORD_MODE_CBR, 8000, 1000000, 30, 0, 0, NULL, FALSE, QCAP_BROADCAST_FLAG_NETWORK | QCAP_BROADCAST_FLAG_ENCODE );
+//
+//         QCAP_SET_VIDEO_BROADCAST_SERVER_PROPERTY( pServer, 3 /*CH04*/, QCAP_ENCODER_TYPE_INTEL_MEDIA_SDK, QCAP_ENCODER_FORMAT_H264, QCAP_COLORSPACE_TYPE_YUY2, 480, 270, 30, QCAP_RECORD_MODE_CBR, 8000, 1000000, 30, 0, 0, NULL, FALSE, QCAP_BROADCAST_FLAG_NETWORK | QCAP_BROADCAST_FLAG_ENCODE );
+//
+//         QCAP_SET_AUDIO_BROADCAST_SERVER_PROPERTY( pServer, 0 /*CH01*/, QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_AAC, 2, 16, 48000, 0 );
+//
+//         QCAP_SET_AUDIO_BROADCAST_SERVER_PROPERTY( pServer, 1 /*CH02*/, QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_AAC, 2, 16, 48000, 0 );
+//
+//         QCAP_SET_AUDIO_BROADCAST_SERVER_PROPERTY( pServer, 2 /*CH03*/, QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_AAC, 2, 16, 48000, 0 );
+//
+//         QCAP_SET_AUDIO_BROADCAST_SERVER_PROPERTY( pServer, 3 /*CH04*/, QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_AAC, 2, 16, 48000, 0 );
+//
+// STEP#03 QCAP_START_BROADCAST_SERVER( pServer );
+//
+// STEP#04 QCAP_SET_VIDEO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( pServer, 0 /*CH01*/, QCAP_COLORSPACE_TYPE_YUY2, 1920, 1080, pFrameBufferA, nFrameBufferLenA );
+//
+//         QCAP_SET_VIDEO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( pServer, 1 /*CH02*/, QCAP_COLORSPACE_TYPE_YUY2, 1920, 1080, pFrameBufferB, nFrameBufferLenB );
+//
+//         QCAP_SET_VIDEO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( pServer, 2 /*CH03*/, QCAP_COLORSPACE_TYPE_YUY2, 1920, 1080, pFrameBufferC, nFrameBufferLenC );
+//
+//         QCAP_SET_VIDEO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( pServer, 3 /*CH04*/, QCAP_COLORSPACE_TYPE_YUY2, 1920, 1080, pFrameBufferD, nFrameBufferLenD );
+//
+//         QCAP_SET_AUDIO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( pServer, 0 /*CH01*/, pFrameBufferA, nFrameBufferLenA );
+//
+//         QCAP_SET_AUDIO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( pServer, 1 /*CH02*/, pFrameBufferB, nFrameBufferLenB );
+//
+//         QCAP_SET_AUDIO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( pServer, 2 /*CH03*/, pFrameBufferC, nFrameBufferLenC );
+//
+//         QCAP_SET_AUDIO_BROADCAST_SERVER_UNCOMPRESSION_BUFFER( pServer, 3 /*CH04*/, pFrameBufferD, nFrameBufferLenD );
+//
+// STEP#05 QCAP_STOP_BROADCAST_SERVER( pServer );
+//
+// STEP#06 QCAP_DRSTROY_BROADCAST_SERVER( pServer );
+
+// BROADCASTING RTMP SERVER EXAMPLE:
+//
+// STEP#01 QCAP_CREATE_BROADCAST_RTMP_SERVER( 0, 4 /*4 CHANNELS*/, &pServer, NULL, NULL, 1935 );
+//
+//         CH01 URL -> rtmp://xxx.xxx.xxx.xxx:1935/flvplayback/session0.mpg (HERE, xxx.xxx.xxx.xxx IS YOUR WEB SERVER ADDRESS)
+//
+//         CH02 URL -> rtmp://xxx.xxx.xxx.xxx:1935/flvplayback/session1.mpg
+//
+//         CH03 URL -> rtmp://xxx.xxx.xxx.xxx:1935/flvplayback/session2.mpg
+//
+//         CH04 URL -> rtmp://xxx.xxx.xxx.xxx:1935/flvplayback/session3.mpg
+
+// BROADCASTING HLS SERVER EXAMPLE:
+//
+// STEP#01 QCAP_CREATE_BROADCAST_HLS_SERVER( 0, 4 /*4 CHANNELS*/, &pServer, "C:\\AppServ\\www\\", "hls\\", 3000 ); // HERE, C:\\AppServ\\www\\ IS YOUR WEB SERVER ROOT FOLDER, hls\\ is SUB FOLDER TO SAVE HLS FILES
+//
+//         CH01 URL -> http://xxx.xxx.xxx.xxx/hls/session0.m3u8 (HERE, xxx.xxx.xxx.xxx IS YOUR WEB SERVER ADDRESS)
+//
+//         CH02 URL -> http://xxx.xxx.xxx.xxx/hls/session1.m3u8
+//
+//         CH03 URL -> http://xxx.xxx.xxx.xxx/hls/session2.m3u8
+//
+//         CH04 URL -> http://xxx.xxx.xxx.xxx/hls/session3.m3u8
+
+// BROADCASTING UDP SERVER EXAMPLE:
+//
+// STEP#01 QCAP_CREATE_BROADCAST_TS_OVER_UDP_SERVER( 0, "udp://xxx.xxx.xxx.xxx:xxx", &pServer );
+
+// ##########################################################################################################################################################################################
+// #
+// # BROADCASTING CLIENT FUNCTIONS (RTSP/RTMP/TS) (2D/3D)
+// #
+// ##########################################################################################################################################################################################
+//
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_CLIENT_MEDIAINFO_CALLBACK)( PVOID pClient /*IN*/, ULONG nTotalStreams /*IN*/, UINT iStreamNum /*IN*/, ULONG nStream_PID /*IN*/, ULONG nProgram_PID /*IN*/, ULONG nVideoWidth /*IN*/, ULONG nVideoHeight /*IN*/, BOOL bVideoIsInterleaved /*IN*/, double dVideoFrameRate /*IN*/, ULONG nAudioChannels /*IN*/, ULONG nAudioBitsPerSample /*IN*/, ULONG nAudioSampleFrequency /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_CLIENT_CONNECTED_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nVideoWidth /*IN*/, ULONG nVideoHeight /*IN*/, BOOL bVideoIsInterleaved /*IN*/, double dVideoFrameRate /*IN*/, ULONG nAudioChannels /*IN*/, ULONG nAudioBitsPerSample /*IN*/, ULONG nAudioSampleFrequency /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_CLIENT_FAIL_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, QRESULT nErrorStatus /*IN*/, DWORD nErrorCode /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_CLIENT_RECORD_DONE_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_CLIENT_RECORD_FAIL_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, QRESULT nErrorStatus /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_CLIENT_SNAPSHOT_DONE_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_BROADCAST_CLIENT_SNAPSHOT_STREAM_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_BROADCAST_CLIENT_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, BOOL bIsKeyFrame /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_BROADCAST_CLIENT_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_DECODER_BROADCAST_CLIENT_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_DECODER_BROADCAST_CLIENT_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_DECODER_3D_BROADCAST_CLIENT_CALLBACK)( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iChNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |  |    |NDI|  |  |*/ QCAP_BROADCAST_NDI_CLIENT_ENUMERATION( CHAR * * ppszNDIName /*OUT*/, CHAR * * ppszGroupName /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|   |2D|  |*/ QCAP_CREATE_BROADCAST_CLIENT( UINT iCliNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppClient /*OUT*/, ULONG nDecoderType = QCAP_DECODER_TYPE_SOFTWARE /*IN*/, HWND hAttachedWindow = NULL /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|   |2D|  |*/ QCAP_CREATE_BROADCAST_CLIENT_EX( UINT iCliNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppClient /*OUT*/, ULONG nDecoderType = QCAP_DECODER_TYPE_SOFTWARE /*IN*/, CHAR * pszNetworkAdapterIP = NULL /*IN*/, HWND hAttachedWindow = NULL /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |  |    |   |  |3D|*/ QCAP_CREATE_3D_BROADCAST_CLIENT( UINT iCliNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppClient /*OUT*/, ULONG nDecoderType = QCAP_DECODER_TYPE_SOFTWARE /*IN*/, HWND hAttachedWindowL = NULL /*IN*/, BOOL bThumbDrawL = FALSE /*IN*/, BOOL bMaintainAspectRatioL = FALSE /*IN*/, HWND hAttachedWindowR = NULL /*IN*/, BOOL bThumbDrawR = FALSE /*IN*/, BOOL bMaintainAspectRatioR = FALSE /*IN*/ ); // ONLY FOR RTSP
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |  |    |   |  |3D|*/ QCAP_CREATE_3D_BROADCAST_CLIENT_EX( UINT iCliNum /*IN*/ /*0 ~ 63*/, CHAR * pszURL /*IN*/, PVOID * ppClient /*OUT*/, ULONG nDecoderType = QCAP_DECODER_TYPE_SOFTWARE /*IN*/, CHAR * pszNetworkAdapterIP = NULL /*IN*/, HWND hAttachedWindowL = NULL /*IN*/, BOOL bThumbDrawL = FALSE /*IN*/, BOOL bMaintainAspectRatioL = FALSE /*IN*/, HWND hAttachedWindowR = NULL /*IN*/, BOOL bThumbDrawR = FALSE /*IN*/, BOOL bMaintainAspectRatioR = FALSE /*IN*/ ); // ONLY FOR RTSP
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |  |    |NDI|2D|  |*/ QCAP_CREATE_BROADCAST_NDI_CLIENT( UINT iCliNum /*IN*/ /*0 ~ 63*/, CHAR * pszNDIName /*IN*/, CHAR * pszGroupName /*IN*/, PVOID * ppClient /*OUT*/, ULONG nDecoderType = QCAP_DECODER_TYPE_SOFTWARE /*IN*/, HWND hAttachedWindow = NULL /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_DESTROY_BROADCAST_CLIENT( PVOID pClient /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_START_BROADCAST_CLIENT( PVOID pClient /*IN*/, ULONG nProtocol = QCAP_BROADCAST_PROTOCOL_TCP /*IN*/ /*FOR RTSP ONLY*/, ULONG nReconnectionTimeout = 3000 /*IN*/ /*ms*/, ULONG nDelayQueueDuration = 0 /*IN*/ /*ms*/ ); // BPS >= 12MBPS, SUGGEST TO SUE QCAP_BROADCAST_PROTOCOL_TCP
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_START_BROADCAST_CLIENT_EX( PVOID pClient /*IN*/, ULONG nProtocol = QCAP_BROADCAST_PROTOCOL_TCP /*IN*/ /*FOR RTSP ONLY*/, ULONG nReconnectionTimeout = 3000 /*IN*/ /*ms*/, ULONG nDelayQueueDuration = 0 /*IN*/ /*ms*/, BOOL bEnableUltraLowLatency = FALSE /*IN*/ /*FOR RTSP ONLY*/, ULONG nAnalyzeDuration = 0 /*IN*/ /*FOR TS ONLY, MPTS = 10000*/ /*ms*/, ULONG nProbeDurationSizeKB = 0 /*IN*/ /*FOR TS ONLY, MPTS = 100000 KB*/, CHAR * pszEncryptKey = NULL /*IN*/ /*FOR RTMP ONLY*/ ); // BPS >= 12MBPS, SUGGEST TO SUE QCAP_BROADCAST_PROTOCOL_TCP
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_STOP_BROADCAST_CLIENT( PVOID pClient /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |TS|MPTS|   |2D|  |*/ QCAP_GET_BROADCAST_CLIENT_OUTPUT_STREAMS( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pVideoStream_PID /*OUT*/, ULONG * pAudioStream_PID /*OUT*/, HWND * pAttachedWindow = NULL /*OUT*/, BOOL * pThumbDraw = NULL /*OUT*/, BOOL * pMaintainAspectRatio = NULL /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|    |    |TS|MPTS|   |2D|  |*/ QCAP_SET_BROADCAST_CLIENT_OUTPUT_STREAMS( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nVideoStream_PID /*IN*/, ULONG nAudioStream_PID /*IN*/, HWND hAttachedWindow = NULL /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_DIAGNOSE_VIDEO_BROADCAST_CLIENT_STREAM_STATUS( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, BOOL * pIsStill /*OUT*/, ULONG nStillThreshold = 64 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_DIAGNOSE_AUDIO_BROADCAST_CLIENT_STREAM_STATUS( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, double * pVolumeDB_L /*OUT*/ /*RANGE = -100 ~ 0 (DB)*/, double * pVolumeDB_R /*OUT*/ /*RANGE = -100 ~ 0 (DB)*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |  |    |   |  |3D|*/ QCAP_GET_VIDEO_3D_BROADCAST_CLIENT_DISPLAY_MODE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pStereoDisplayMode /*OUT*/, BOOL * pLeftRightSwap /*OUT*/ ); 
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |  |    |   |  |3D|*/ QCAP_SET_VIDEO_3D_BROADCAST_CLIENT_DISPLAY_MODE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nStereoDisplayMode = QCAP_3D_STEREO_DISPLAY_MODE_LINE_BY_LINE /*IN*/, BOOL bLeftRightSwap = FALSE /*IN*/ ); 
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_DEINTERLACE_TYPE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pType /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_DEINTERLACE_TYPE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nType /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_DEINTERLACE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, BOOL * pEnable /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_DEINTERLACE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, BOOL bEnable /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_REGION_DISPLAY( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pCropX /*OUT*/, ULONG * pCropY /*OUT*/, ULONG * pCropW /*OUT*/, ULONG * pCropH /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_REGION_DISPLAY( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_MIRROR( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, BOOL * pHorizontalMirror /*OUT*/, BOOL * pVerticalMirror /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_MIRROR( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, BOOL bHorizontalMirror /*IN*/, BOOL bVerticalMirror /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_ASPECT_RATIO( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, BOOL * pMaintainAspectRatio /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_ASPECT_RATIO( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, BOOL bMaintainAspectRatio /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_BRIGHTNESS( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_BRIGHTNESS( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_CONTRAST( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_CONTRAST( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_HUE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_HUE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_VIDEO_BROADCAST_CLIENT_SATURATION( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_VIDEO_BROADCAST_CLIENT_SATURATION( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_AUDIO_BROADCAST_CLIENT_SOUND_RENDERER( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT * pSoundNum /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_AUDIO_BROADCAST_CLIENT_SOUND_RENDERER( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iSoundNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_AUDIO_BROADCAST_CLIENT_VOLUME( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG * pVolume /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_AUDIO_BROADCAST_CLIENT_VOLUME( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, ULONG nVolume /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_START_BROADCAST_CLIENT_CLONE_RECORD( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathNameA /*IN*/, CHAR * pszFilePathNameB /*IN*/, double dVideoDelayTime = 0.0 /*IN*/, double dAudioDelayTime = 0.0 /*IN*/, double dSegmentDurationTime = 0.0 /*IN*/, ULONG nSegmentDurationSizeKB = 0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_START_BROADCAST_CLIENT_TIMESHIFT_RECORD( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID * ppPhysicalFileWriter /*OUT*/, double dVideoDelayTime = 0.0 /*IN*/, double dAudioDelayTime = 0.0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_START_BROADCAST_CLIENT_RECORD( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, double dVideoDelayTime = 0.0 /*IN*/, double dAudioDelayTime = 0.0 /*IN*/, double dSegmentDurationTime = 0.0 /*IN*/, ULONG nSegmentDurationSizeKB = 0 /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_STOP_BROADCAST_CLIENT_RECORD( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_RESUME_BROADCAST_CLIENT_RECORD( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_PAUSE_BROADCAST_CLIENT_RECORD( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SEGMENT_BROADCAST_CLIENT_RECORD( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SNAPSHOT_BROADCAST_CLIENT_BMP( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SNAPSHOT_BROADCAST_CLIENT_JPG( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SNAPSHOT_BROADCAST_CLIENT_BMP_EX( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SNAPSHOT_BROADCAST_CLIENT_JPG_EX( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_MOVE_OSD_BROADCAST_CLIENT_OBJECT( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ ); // FOR SCROLLING TEXT/PICTURE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_OSD_BROADCAST_CLIENT_TEXT_BOUNDARY( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_GET_OSD_BROADCAST_CLIENT_TEXT_BOUNDARY_W( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_OSD_BROADCAST_CLIENT_TEXT( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_OSD_BROADCAST_CLIENT_TEXT_W( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_OSD_BROADCAST_CLIENT_TEXT_EX( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_OSD_BROADCAST_CLIENT_TEXT_EX_W( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_OSD_BROADCAST_CLIENT_PICTURE( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nTransparent /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_OSD_BROADCAST_CLIENT_BUFFER( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_SET_OSD_BROADCAST_CLIENT_BUFFER_EX( PVOID pClient /*IN*/, UINT iSessionNum /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |  |    |   |2D|3D|*/ QCAP_GET_BROADCAST_CLIENT_CUSTOM_PROPERTY( PVOID pClient /*IN*/, CHAR * pszProperty /*IN*/, CHAR * * ppszValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |  |    |   |2D|3D|*/ QCAP_SET_BROADCAST_CLIENT_CUSTOM_PROPERTY( PVOID pClient /*IN*/, CHAR * pszProperty /*IN*/, CHAR * pszValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_BROADCAST_CLIENT_MEDIAINFO_CALLBACK( PVOID pClient /*IN*/, PF_BROADCAST_CLIENT_MEDIAINFO_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_BROADCAST_CLIENT_CONNECTED_CALLBACK( PVOID pClient /*IN*/, PF_BROADCAST_CLIENT_CONNECTED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_BROADCAST_CLIENT_FAIL_CALLBACK( PVOID pClient /*IN*/, PF_BROADCAST_CLIENT_FAIL_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_BROADCAST_CLIENT_RECORD_DONE_CALLBACK( PVOID pClient /*IN*/, PF_BROADCAST_CLIENT_RECORD_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_BROADCAST_CLIENT_RECORD_FAIL_CALLBACK( PVOID pClient /*IN*/, PF_BROADCAST_CLIENT_RECORD_FAIL_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_BROADCAST_CLIENT_SNAPSHOT_DONE_CALLBACK( PVOID pClient /*IN*/, PF_BROADCAST_CLIENT_SNAPSHOT_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_BROADCAST_CLIENT_SNAPSHOT_STREAM_CALLBACK( PVOID pClient /*IN*/, PF_BROADCAST_CLIENT_SNAPSHOT_STREAM_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_VIDEO_BROADCAST_CLIENT_CALLBACK( PVOID pClient /*IN*/, PF_VIDEO_BROADCAST_CLIENT_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_AUDIO_BROADCAST_CLIENT_CALLBACK( PVOID pClient /*IN*/, PF_AUDIO_BROADCAST_CLIENT_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_VIDEO_DECODER_BROADCAST_CLIENT_CALLBACK( PVOID pClient /*IN*/, PF_VIDEO_DECODER_BROADCAST_CLIENT_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|RTMP|TS|MPTS|NDI|2D|3D|*/ QCAP_REGISTER_AUDIO_DECODER_BROADCAST_CLIENT_CALLBACK( PVOID pClient /*IN*/, PF_AUDIO_DECODER_BROADCAST_CLIENT_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|RTSP|    |  |    |   |  |3D|*/ QCAP_REGISTER_VIDEO_DECODER_3D_BROADCAST_CLIENT_CALLBACK( PVOID pClient /*IN*/, PF_VIDEO_DECODER_3D_BROADCAST_CLIENT_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// BROADCASTING CLIENT EXAMPLE:
+//
+// STEP#01 QCAP_CREATE_BROADCAST_CLIENT( 0, "rtsp://root:root@127.0.0.1:554/session0.mpg", &pClient, QCAP_DECODER_TYPE_SOFTWARE, hWindow, TRUE );
+//
+// STEP#02 QCAP_REGISTER_VIDEO_BROADCAST_CLIENT_CALLBACK( pClient, on_process_video_stream, this );
+//
+//         QCAP_REGISTER_AUDIO_BROADCAST_CLIENT_CALLBACK( pClient, on_process_audio_stream, this );
+//
+// STEP#03 QCAP_START_BROADCAST_CLIENT( pClient );
+//
+// STEP#04 QCAP_STOP_BROADCAST_CLIENT( pClient );
+//
+// STEP#05 QCAP_DESTROY_BROADCAST_CLIENT( pClient );
+
+// ##########################################################################################################################################################################################
+// #
+// # ONVIF COMMUNICATION FUNCTIONS (SERVER/EMULATOR/CLIENT)
+// #
+// ##########################################################################################################################################################################################
+//
+// COMMUNICATION SERVER FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+typedef QRETURN (QCAP_EXPORT *PF_COMMUNICATION_SERVER_GET_CUSTOM_PROPERTY_CALLBACK)( PVOID pServer /*IN*/, CHAR * pszProperty /*IN*/, CHAR * pszValue /*OUT*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_COMMUNICATION_SERVER_SET_CUSTOM_PROPERTY_CALLBACK)( PVOID pServer /*IN*/, CHAR * pszProperty /*IN*/, CHAR * pszValue /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_COMMUNICATION_SERVER_PROFILE_VIDEO_SETUP_CALLBACK)( PVOID pServer /*IN*/, UINT iProNum /*IN*/, ULONG nEncoderFormat /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, ULONG nFrameRate /*IN*/, ULONG nRecordMode /*IN*/, ULONG nQuality /*IN*/, ULONG nBitRate /*IN*/, ULONG nGOP /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_COMMUNICATION_SERVER_PROFILE_AUDIO_SETUP_CALLBACK)( PVOID pServer /*IN*/, UINT iProNum /*IN*/, ULONG nEncoderFormat /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, ULONG nBitRate /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_COMMUNICATION_ONVIF_SERVER( ULONG nTotalVideoSources /*IN*/, ULONG nTotalAudioSources /*IN*/, ULONG nTotalVideoEncoders /*IN*/, ULONG nTotalAudioEncoders /*IN*/, ULONG nTotalProfiles /*IN*/, PVOID * ppServer /*OUT*/, CHAR * pszName = NULL /*IN*/, CHAR * pszLocation = NULL /*IN*/, CHAR * pszManufacturer = NULL /*IN*/, CHAR * pszModel = NULL /*IN*/, CHAR * pszHardwareVersion = NULL /*IN*/, CHAR * pszFirmwareVersion = NULL /*IN*/, CHAR * pszDeviceID = NULL /*IN*/, CHAR * pszRootAccount = NULL /*IN*/, CHAR * pszRootPassword = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_COMMUNICATION_SERVER( PVOID pServer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_COMMUNICATION_SERVER( PVOID pServer /*IN*/, ULONG nNetworkPort = 8001 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_COMMUNICATION_SERVER( PVOID pServer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_SERVER_VIDEO_SOURCE( PVOID pServer /*IN*/, UINT iVidSrcNum /*IN*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, ULONG * pFrameRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_COMMUNICATION_SERVER_VIDEO_SOURCE( PVOID pServer /*IN*/, UINT iVidSrcNum /*IN*/, ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  ULONG   nFrameRate /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_SERVER_AUDIO_SOURCE( PVOID pServer /*IN*/, UINT iAudSrcNum /*IN*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_COMMUNICATION_SERVER_AUDIO_SOURCE( PVOID pServer /*IN*/, UINT iAudSrcNum /*IN*/, ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_SERVER_VIDEO_ENCODER( PVOID pServer /*IN*/, UINT iVidEncNum /*IN*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, ULONG * pFrameRate_Min /*OUT*/, ULONG * pFrameRate_Max /*OUT*/, ULONG * pFrameRate_Default /*OUT*/, ULONG * pRecordMode_Min /*OUT*/, ULONG * pRecordMode_Max /*OUT*/, ULONG * pRecordMode_Default /*OUT*/, ULONG * pQuality_Min /*OUT*/, ULONG * pQuality_Max /*OUT*/, ULONG * pQuality_Default /*OUT*/, ULONG * pBitRate_Min /*OUT*/, ULONG * pBitRate_Max /*OUT*/, ULONG * pBitRate_Default /*OUT*/, ULONG * pGOP_Min /*OUT*/, ULONG * pGOP_Max /*OUT*/, ULONG * pGOP_Default /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_COMMUNICATION_SERVER_VIDEO_ENCODER( PVOID pServer /*IN*/, UINT iVidEncNum /*IN*/, ULONG   nEncoderFormat /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  ULONG   nFrameRate_Min /*IN*/,  ULONG   nFrameRate_Max /*IN*/,  ULONG   nFrameRate_Default /*IN*/,  ULONG   nRecordMode_Min /*IN*/,  ULONG   nRecordMode_Max /*IN*/,  ULONG   nRecordMode_Default /*IN*/,  ULONG   nQuality_Min /*IN*/,  ULONG   nQuality_Max /*IN*/,  ULONG   nQuality_Default /*IN*/,  ULONG   nBitRate_Min /*IN*/,  ULONG   nBitRate_Max /*IN*/,  ULONG   nBitRate_Default /*IN*/,  ULONG   nGOP_Min /*IN*/,  ULONG   nGOP_Max /*IN*/,  ULONG   nGOP_Default /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_SERVER_AUDIO_ENCODER( PVOID pServer /*IN*/, UINT iAudEncNum /*IN*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/, ULONG * pBitRate_Min /*OUT*/, ULONG * pBitRate_Max /*OUT*/, ULONG * pBitRate_Default /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_COMMUNICATION_SERVER_AUDIO_ENCODER( PVOID pServer /*IN*/, UINT iAudEncNum /*IN*/, ULONG   nEncoderFormat /*IN*/,  ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/,  ULONG   nBitRate_Min /*IN*/,  ULONG   nBitRate_Max /*IN*/,  ULONG   nBitRate_Default /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_SERVER_PROFILE( PVOID pServer /*IN*/, UINT iProNum /*IN*/, CHAR * * ppszURL /*OUT*/, UINT * pVidSrcNum /*OUT*/, UINT * pAudSrcNum /*OUT*/, UINT * pVidEncNum /*OUT*/, UINT * pAudEncNum /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_COMMUNICATION_SERVER_PROFILE( PVOID pServer /*IN*/, UINT iProNum /*IN*/, CHAR *   pszURL /*IN*/,   UINT   iVidSrcNum /*IN*/,  UINT   iAudSrcNum /*IN*/,  UINT   iVidEncNum /*IN*/,  UINT   iAudEncNum /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_COMMUNICATION_SERVER_GET_CUSTOM_PROPERTY_CALLBACK( PVOID pServer /*IN*/, PF_COMMUNICATION_SERVER_GET_CUSTOM_PROPERTY_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_COMMUNICATION_SERVER_SET_CUSTOM_PROPERTY_CALLBACK( PVOID pServer /*IN*/, PF_COMMUNICATION_SERVER_SET_CUSTOM_PROPERTY_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_COMMUNICATION_SERVER_PROFILE_VIDEO_SETUP_CALLBACK( PVOID pServer /*IN*/, PF_COMMUNICATION_SERVER_PROFILE_VIDEO_SETUP_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_COMMUNICATION_SERVER_PROFILE_AUDIO_SETUP_CALLBACK( PVOID pServer /*IN*/, PF_COMMUNICATION_SERVER_PROFILE_AUDIO_SETUP_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// COMMUNICATION EMULATOR FUNCTIONS /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+typedef QRETURN (QCAP_EXPORT *PF_COMMUNICATION_EMULATOR_SCAN_DONE_CALLBACK)( PVOID pEmulator /*IN*/, ULONG nTotalServers /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_COMMUNICATION_ONVIF_EMULATOR( PVOID * ppEmulator /*OUT*/, ULONG nEmulatorPort = 3702 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_COMMUNICATION_EMULATOR( PVOID pEmulator /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_COMMUNICATION_EMULATOR( PVOID pEmulator /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_COMMUNICATION_EMULATOR( PVOID pEmulator /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_EMULATOR_SERVER_INFO( PVOID pEmulator /*IN*/, UINT iSvrNum /*IN*/, CHAR * * ppszNetworkIP /*OUT*/, ULONG * pNetworkPort /*OUT*/, CHAR * * ppszNetworkURL /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_COMMUNICATION_EMULATOR_SCAN_DONE_CALLBACK( PVOID pEmulator /*IN*/, PF_COMMUNICATION_EMULATOR_SCAN_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// COMMUNICATION CLIENT FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+typedef QRETURN (QCAP_EXPORT *PF_COMMUNICATION_CLIENT_CONNECTED_CALLBACK)( PVOID pClient /*IN*/, ULONG nTotalVideoSources /*IN*/, ULONG nTotalAudioSources /*IN*/, ULONG nTotalVideoEncoders /*IN*/, ULONG nTotalAudioEncoders /*IN*/, ULONG nTotalProfiles /*IN*/, CHAR * pszName /*IN*/, CHAR * pszLocationr /*IN*/, CHAR * pszManufacturer /*IN*/, CHAR * pszModel /*IN*/, CHAR * pszHardwareVersion /*IN*/, CHAR * pszFirmwareVersion /*IN*/, CHAR * pszDeviceID /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_COMMUNICATION_ONVIF_CLIENT( CHAR * pszNetworkURL /*IN*/, PVOID * ppClient /*OUT*/, CHAR * pszAccount = NULL /*IN*/ /*DEFAULT = "root"*/, CHAR * pszPassword = NULL /*IN*/ /*DEFAULT = "root"*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_COMMUNICATION_CLIENT( PVOID pClient /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_COMMUNICATION_CLIENT( PVOID pClient /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_COMMUNICATION_CLIENT( PVOID pClient /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_VIDEO_SOURCE_INFO( PVOID pClient /*IN*/, UINT iVidSrcNum /*IN*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, ULONG * pFrameRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_AUDIO_SOURCE_INFO( PVOID pClient /*IN*/, UINT iAudSrcNum /*IN*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_VIDEO_ENCODER_INFO( PVOID pClient /*IN*/, UINT iVidEncNum /*IN*/, ULONG * pTotalVidEncOptions /*OUT*/, UINT * pVidEncOptionNum_Default /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_AUDIO_ENCODER_INFO( PVOID pClient /*IN*/, UINT iAudEncNum /*IN*/, ULONG * pTotalAudEncOptions /*OUT*/, UINT * pAudEncOptionNum_Default /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_VIDEO_ENCODER_OPTION_INFO( PVOID pClient /*IN*/, UINT iVidEncNum /*IN*/, UINT iVidEncOptionNum /*IN*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pWidthList /*OUT*/, ULONG * pWidthListSize /*OUT*/, ULONG * pWidth_Default /*OUT*/, ULONG * pHeightList /*OUT*/, ULONG * pHeightListSize /*OUT*/, ULONG * pHeight_Default /*OUT*/, ULONG * pFrameRate_Min /*OUT*/, ULONG * pFrameRate_Max /*OUT*/, ULONG * pFrameRate_Default /*OUT*/, ULONG * pRecordMode_Min /*OUT*/, ULONG * pRecordMode_Max /*OUT*/, ULONG * pRecordMode_Default /*OUT*/, ULONG * pQuality_Min /*OUT*/, ULONG * pQuality_Max /*OUT*/, ULONG * pQuality_Default /*OUT*/, ULONG * pBitRate_Min /*OUT*/, ULONG * pBitRate_Max /*OUT*/, ULONG * pBitRate_Default /*OUT*/, ULONG * pGOP_Min /*OUT*/, ULONG * pGOP_Max /*OUT*/, ULONG * pGOP_Default /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_AUDIO_ENCODER_OPTION_INFO( PVOID pClient /*IN*/, UINT iAudEncNum /*IN*/, UINT iAudEncOptionNum /*IN*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannelsList /*OUT*/, ULONG * pChannelsListSize /*OUT*/, ULONG * pChannels_Default /*OUT*/, ULONG * pBitsPerSampleList /*OUT*/, ULONG * pBitsPerSampleListSize /*OUT*/, ULONG * pBitsPerSample_Default /*OUT*/, ULONG * pSampleFrequencyList /*OUT*/, ULONG * pSampleFrequencyListSize /*OUT*/, ULONG * pSampleFrequency_Default /*OUT*/, ULONG * pBitRate_Min /*OUT*/, ULONG * pBitRate_Max /*OUT*/, ULONG * pBitRate_Default /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_PROFILE_INFO( PVOID pClient /*IN*/, UINT iProNum /*IN*/, CHAR * * ppszURL /*OUT*/, UINT * pVidSrcNum /*OUT*/, UINT * pAudSrcNum /*OUT*/, UINT * pVidEncNum /*OUT*/, UINT * pAudEncNum /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_PROFILE_VIDEO_PROPERTY( PVOID pClient /*IN*/, UINT iProNum /*IN*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, ULONG * pFrameRate /*OUT*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_COMMUNICATION_CLIENT_PROFILE_VIDEO_PROPERTY( PVOID pClient /*IN*/, UINT iProNum /*IN*/, ULONG   nEncoderFormat /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  ULONG   nFrameRate /*IN*/,  ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_PROFILE_AUDIO_PROPERTY( PVOID pClient /*IN*/, UINT iProNum /*IN*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/, ULONG * pBitRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_COMMUNICATION_CLIENT_PROFILE_AUDIO_PROPERTY( PVOID pClient /*IN*/, UINT iProNum /*IN*/, ULONG   nEncoderFormat /*IN*/,  ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/,  ULONG   nBitRate /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_COMMUNICATION_CLIENT_CUSTOM_PROPERTY( PVOID pClient /*IN*/, CHAR * pszProperty /*IN*/, CHAR * * ppszValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_COMMUNICATION_CLIENT_CUSTOM_PROPERTY( PVOID pClient /*IN*/, CHAR * pszProperty /*IN*/, CHAR * pszValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_COMMUNICATION_CLIENT_CONNECTED_CALLBACK( PVOID pClient /*IN*/, PF_COMMUNICATION_CLIENT_CONNECTED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # DICOM FUNCTIONS (DICOM WORKSTATION <-> PACS SERVER)
+// #
+// ##########################################################################################################################################################################################
+//
+typedef QRETURN (QCAP_EXPORT *PF_DICOM_DOWNLOAD_OPERATION_CALLBACK)( PVOID pStation /*IN*/, double dPercentageCompleted /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_DICOM_UPLOAD_OPERATION_CALLBACK)( PVOID pStation /*IN*/, double dPercentageCompleted /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_DICOM_WORKSTATION( CHAR * pszServerIP_PACS /*IN*/, ULONG nServerPort_PACS /*IN*/, CHAR * pszServerApplicationEntityTitle /*IN*/, PVOID * ppStation /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_DICOM_WORKSTATION( PVOID pStation /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_DICOM_WORKSTATION( PVOID pStation /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_DICOM_WORKSTATION( PVOID pStation /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_DICOM_OPERATION_CONDITION( PVOID   pStation,                          /*IN*/ 
+
+																	 ULONG   nQueryLevel,                       /*IN*/
+																	 
+																	 WSTRING pwszPatientName,                   /*IN*/ // 患者姓名 [PATIENT]
+
+																	 WSTRING pwszPatientID,                     /*IN*/ // 病歷號碼 [PATIENT]
+
+																	 WSTRING pwszStudyInstanceUID,              /*IN*/ // 檢查序號 [GENERAL STUDY]
+
+																	 WSTRING pwszStudyDate,                     /*IN*/ // 檢查日期 [GENERAL STUDY]
+
+																	 WSTRING pwszStudyTime,                     /*IN*/ // 檢查時間 [GENERAL STUDY]
+
+																	 WSTRING pwszReferringPhysicianName,        /*IN*/ // 咨詢醫師 [GENERAL STUDY]
+
+																	 WSTRING pwszStudyID,                       /*IN*/ // 檢查編號 [GENERAL STUDY]
+
+																	 WSTRING pwszAccessionNumber,               /*IN*/ // 登記號碼 [GENERAL STUDY]
+
+																	 WSTRING pwszStudyDescription,              /*IN*/ // 檢查說明 [GENERAL STUDY]
+
+																	 WSTRING pwszNameOfPhysiciansReadingStudy,  /*IN*/ // 判讀醫生 [GENERAL STUDY]
+
+																	 WSTRING pwszModality,                      /*IN*/ // 圖像設備 [GENERAL SERIES]
+
+																	 WSTRING pwszSeriesInstanceUID,             /*IN*/ // 序列序號 [GENERAL SERIES]
+
+																	 WSTRING pwszPerformingPhysicianName,       /*IN*/ // 執行醫生 [GENERAL SERIES]
+
+																	 WSTRING pwszInstitutionalDepartmentName ); /*IN*/ // 機構名稱 [GENERAL EQUIPMENT]
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_DICOM_QUERY_OPERATION( PVOID pStation /*IN*/, CHAR * pszClientApplicationEntityTitle /*IN*/, ULONG nClientPort /*IN*/, CHAR * pszOutputFolder /*IN*/, CHAR * pszOutputXMLFileName /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_DICOM_QUERY_OPERATION( PVOID pStation /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_DICOM_DOWNLOAD_OPERATION( PVOID pStation /*IN*/, CHAR * pszClientApplicationEntityTitle /*IN*/, ULONG nClientPort /*IN*/, CHAR * pszDownloadFolder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_DICOM_DOWNLOAD_OPERATION( PVOID pStation /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_DICOM_UPLOAD_OPERATION( PVOID pStation /*IN*/, CHAR * pszClientApplicationEntityTitle /*IN*/, ULONG nClientPort /*IN*/, CHAR * pszUploadFolder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_DICOM_UPLOAD_OPERATION( PVOID pStation /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_DICOM_DOWNLOAD_OPERATION_CALLBACK( PVOID pStation /*IN*/, PF_DICOM_DOWNLOAD_OPERATION_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_DICOM_UPLOAD_OPERATION_CALLBACK( PVOID pStation /*IN*/, PF_DICOM_UPLOAD_OPERATION_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # WEBRTC FUNCTIONS (CHATROOM)
+// #
+// ##########################################################################################################################################################################################
+//
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_WEBRTC_CHATROOM( ULONG nNetworkPort /*IN*/, PVOID * ppChatRoom /*OUT*/, CHAR * pszNetworkAdapterIP = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_WEBRTC_CHATROOM( PVOID pChatRoom /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # WEBRTC FUNCTIONS (P2P)
+// #
+// ##########################################################################################################################################################################################
+//
+typedef QRETURN (QCAP_EXPORT *PF_WEBRTC_CHATROOM_LOGIN_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, CHAR * pszPeerUserName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_WEBRTC_CHATROOM_LOGOUT_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_WEBRTC_PRIVATE_SDP_PROPERTY_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, CHAR * pszType /*IN*/, CHAR * pszSDP /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_WEBRTC_PEER_CONNECTED_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, QRESULT nConnectionStatus /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_WEBRTC_PEER_DISCONNECTED_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN(QCAP_EXPORT *PF_WEBRTC_SEND_DATA_DONE_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, QRESULT nSendStatus /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN(QCAP_EXPORT *PF_WEBRTC_SEND_FILE_DONE_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, QRESULT nSendStatus /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN(QCAP_EXPORT *PF_WEBRTC_RECEIVED_DATA_DONE_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, CHAR * pszData /*IN*/, ULONG nDataSize /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN(QCAP_EXPORT *PF_WEBRTC_RECEIVED_FILE_DONE_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, CHAR * pszFileNamePath /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN(QCAP_EXPORT *PF_WEBRTC_RECEIVED_FILE_STATUS_CALLBACK)( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, ULONG nRecvSize /*IN*/, ULONG nTotalSize /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN(QCAP_EXPORT *PF_WEBRTC_NETWORK_QOS_STATUS_CALLBACK)( PVOID pChatter /*IN*/, UINT iSessionNum /*IN*/, ULONG nExpectedBitRate /*IN*/, ULONG nExpectedFrameRate /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_WEBRTC_CHATTER( CHAR * pszChatRoomLoginIP /*IN*/, ULONG nChatRoomLoginPort /*IN*/, CHAR * pszUserName /*IN*/, PVOID * ppChatter /*OUT*/, ULONG * pLoginID /*OUT*/, ULONG nConnectionTimeout = 5000 /*IN*/, CHAR * pszIceURL = NULL /*IN*/, CHAR * pszIceUserName = NULL /*IN*/, CHAR * pszIcePassword = NULL /*IN*/, CHAR * pszReceivedFileFolderPath = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_WEBRTC_CHATTER( PVOID pChatter /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_WEBRTC_CHAT( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_WEBRTC_CHAT( PVOID pChatter /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_WEBRTC_SENDER( PVOID pChatter /*IN*/, UINT iSvrNum /*IN*/ /*0 ~ 63*/, ULONG nTotalSessions /*IN*/, PVOID * ppServer /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_WEBRTC_RECEIVER( PVOID pChatter /*IN*/, UINT iCliNum /*IN*/ /*0 ~ 63*/, PVOID * ppClient /*OUT*/, ULONG nDecoderType = QCAP_DECODER_TYPE_SOFTWARE /*IN*/, HWND hAttachedWindow = NULL /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_WEBRTC_PRIVATE_SDP_PROPERTY( PVOID pChatter /*IN*/, ULONG nPeerID /*IN*/, CHAR * pszSDP /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_ENUM_WEBRTC_USER_IN_CHATROOM( PVOID pChatter /*IN*/, ULONG * pPeerID /*OUT*/, CHAR * * ppszPeerUserName /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SEND_WEBRTC_DATA( PVOID pChatter /*IN*/, CHAR * pszData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SEND_WEBRTC_FILE( PVOID pChatter /*IN*/, CHAR * pszFilePathName /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_CHATROOM_LOGIN_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_CHATROOM_LOGIN_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_CHATROOM_LOGOUT_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_CHATROOM_LOGOUT_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_PRIVATE_SDP_PROPERTY_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_PRIVATE_SDP_PROPERTY_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_PEER_CONNECTED_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_PEER_CONNECTED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_PEER_DISCONNECTED_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_PEER_DISCONNECTED_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_SEND_DATA_DONE_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_SEND_DATA_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_SEND_FILE_DONE_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_SEND_FILE_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_RECEIVED_DATA_DONE_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_RECEIVED_DATA_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_RECEIVED_FILE_DONE_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_RECEIVED_FILE_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_RECEIVED_FILE_STATUS_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_RECEIVED_FILE_STATUS_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_WEBRTC_NETWORK_QOS_STATUS_CALLBACK( PVOID pChatter /*IN*/, PF_WEBRTC_NETWORK_QOS_STATUS_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # FILE PLAYBACK FUNCTIONS (PLAYBACK/EDITING/REPAIR/FILE)
+// #
+// ##########################################################################################################################################################################################
+//
+// FILE PLAYBACK FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+typedef QRETURN (QCAP_EXPORT *PF_FILE_SNAPSHOT_DONE_CALLBACK)( PVOID pFile /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_FILE_SNAPSHOT_STREAM_CALLBACK)( PVOID pFile /*IN*/, CHAR * pszFilePathName /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_DECODER_FILE_CALLBACK)( PVOID pFile /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_DECODER_FILE_CALLBACK)( PVOID pFile /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_DECODER_FILE_CALLBACK_EX)( PVOID pFile /*IN*/, UINT iFrameNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR QCAP_OPEN_FILE_EX & QCAP_OPEN_TIMESHIFT_FILE_EX
+
+typedef QRETURN (QCAP_EXPORT *PF_AUDIO_DECODER_FILE_CALLBACK_EX)( PVOID pFile /*IN*/, UINT iFrameNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR QCAP_OPEN_FILE_EX & QCAP_OPEN_TIMESHIFT_FILE_EX
+
+typedef QRETURN (QCAP_EXPORT *PF_VIDEO_DECODER_3D_FILE_CALLBACK)( PVOID pFile /*IN*/, UINT iChNum /*IN*/, double dSampleTime /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|   |  |  |   |*/ QCAP_OPEN_FILE( CHAR * pszFileName /*IN*/, PVOID * ppFile /*OUT*/, ULONG nDecoderType /*IN*/, ULONG * pVideoEncoderFormat /*OUT*/, ULONG * pVideoWidth /*OUT*/, ULONG * pVideoHeight /*OUT*/, double * pVideoFrameRate /*OUT*/, ULONG * pAudioEncoderFormat /*OUT*/, ULONG * pAudioChannels /*OUT*/, ULONG * pAudioBitsPerSample /*OUT*/, ULONG * pAudioSampleFrequency /*OUT*/, double * pTotalDurationTimes /*OUT*/, ULONG * pTotalVideoFrames /*OUT*/, ULONG * pTotalAudioFrames /*OUT*/, ULONG * pTotalMetadataFrames /*OUT*/, HWND hAttachedWindow /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/, BOOL bEnableEditFunction = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |2D+|  |  |   |*/ QCAP_OPEN_FILE_EX( CHAR * pszFileName /*IN*/, PVOID * ppFile /*OUT*/, ULONG nDecoderType /*IN*/, ULONG * pVideoEncoderFormat /*OUT*/, ULONG * pVideoWidth /*OUT*/, ULONG * pVideoHeight /*OUT*/, double * pVideoFrameRate /*OUT*/, ULONG * pAudioEncoderFormat /*OUT*/, ULONG * pAudioChannels /*OUT*/, ULONG * pAudioBitsPerSample /*OUT*/, ULONG * pAudioSampleFrequency /*OUT*/, double * pTotalDurationTimes /*OUT*/, ULONG * pTotalVideoFrames /*OUT*/, ULONG * pTotalAudioFrames /*OUT*/, ULONG * pTotalMetadataFrames /*OUT*/, HWND hAttachedWindow /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ ); // MP4 ONLY, NOW
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |3D|  |   |*/ QCAP_OPEN_3D_FILE( CHAR * pszFileName /*IN*/, PVOID * ppFile /*OUT*/, BOOL * pIs3D /*OUT*/, ULONG nDecoderType /*IN*/, ULONG * pVideoEncoderFormat /*OUT*/, ULONG * pVideoWidth /*OUT*/, ULONG * pVideoHeight /*OUT*/, double * pVideoFrameRate /*OUT*/, ULONG * pAudioEncoderFormat /*OUT*/, ULONG * pAudioChannels /*OUT*/, ULONG * pAudioBitsPerSample /*OUT*/, ULONG * pAudioSampleFrequency /*OUT*/, double * pTotalDurationTimes /*OUT*/, ULONG * pTotalVideoFrames /*OUT*/, ULONG * pTotalAudioFrames /*OUT*/, ULONG * pTotalMetadataFrames /*OUT*/, HWND hAttachedWindowL /*IN*/, BOOL bThumbDrawL = FALSE /*IN*/, BOOL bMaintainAspectRatioL = FALSE /*IN*/, HWND hAttachedWindowR = NULL /*IN*/, BOOL bThumbDrawR = FALSE /*IN*/, BOOL bMaintainAspectRatioR = FALSE /*IN*/, BOOL bEnableEditFunction = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |TS|   |*/ QCAP_OPEN_TIMESHIFT_FILE_EX( PVOID pPhysicalFileWriter /*IN*/, PVOID * ppFile /*OUT*/, ULONG nDecoderType /*IN*/, ULONG * pVideoEncoderFormat /*OUT*/, ULONG * pVideoWidth /*OUT*/, ULONG * pVideoHeight /*OUT*/, double * pVideoFrameRate /*OUT*/, ULONG * pAudioEncoderFormat /*OUT*/, ULONG * pAudioChannels /*OUT*/, ULONG * pAudioBitsPerSample /*OUT*/, ULONG * pAudioSampleFrequency /*OUT*/, double * pTotalDurationTimes /*OUT*/, ULONG * pTotalVideoFrames /*OUT*/, ULONG * pTotalAudioFrames /*OUT*/, ULONG * pTotalMetadataFrames /*OUT*/, HWND hAttachedWindow /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/, CHAR * pszEncryptKey = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |  |SCF|*/ QCAP_OPEN_SCF_FILE( UINT iChNum /*IN*/, PVOID * ppFile /*OUT*/, ULONG nDecoderType /*IN*/, HWND hAttachedWindow /*IN*/, BOOL bThumbDraw = FALSE /*IN*/, BOOL bMaintainAspectRatio = FALSE /*IN*/, double dStartSampleTime = 0.0 /*IN*/, double dStopSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_DESTROY_FILE( PVOID pFile /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_PLAY_FILE( PVOID pFile /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_PAUSE_FILE( PVOID pFile /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_STOP_FILE( PVOID pFile /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |  |SCF|*/ QCAP_PLAY_GLOBAL_SCF_FILE();
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |  |SCF|*/ QCAP_PAUSE_GLOBAL_SCF_FILE();
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |  |SCF|*/ QCAP_STOP_GLOBAL_SCF_FILE();
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |TS|   |*/ QCAP_REFRESH_TIMESHIFT_FILE_INFO( PVOID pFile /*IN*/, double * pTotalDurationTimes /*OUT*/, ULONG * pTotalVideoFrames /*OUT*/, ULONG * pTotalAudioFrames /*OUT*/, ULONG * pTotalMetadataFrames /*OUT*/ ); // QCAP_OPEN_TIMESHIFT_FILE_EX
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |  |SCF|*/ QCAP_SCF_FILE_ENUMERATION( UINT iChNum /*IN*/, double dStartSearchTime /*IN*/, double dStopSearchTime /*IN*/, ULONG * pFileSizeHigh /*OUT*/, ULONG * pFileSizeLow /*OUT*/, double * pFileStartTime /*OUT*/, double * pFileStopTime /*OUT*/, double * pVideoStartTime /*OUT*/, double * pVideoStopTime /*OUT*/, double * pAudioStartTime /*OUT*/, double * pAudioStopTime /*OUT*/, ULONG * pVideoEncoderFormat /*OUT*/, ULONG * pVideoWidth /*OUT*/, ULONG * pVideoHeight /*OUT*/, double * pVideoFrameRate /*OUT*/, ULONG * pAudioEncoderFormat /*OUT*/, ULONG * pAudioChannels /*OUT*/, ULONG * pAudioBitsPerSample /*OUT*/, ULONG * pAudioSampleFrequency /*OUT*/, double * pTotalDurationTimes /*OUT*/, ULONG * pTotalVideoFrames /*OUT*/, ULONG * pTotalAudioFrames /*OUT*/, ULONG * pTotalMetadataFrames /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |  |SCF|*/ QCAP_SET_SCF_FILE_TIMER( UINT iChNum /*IN*/, BOOL bEnableGlobalTimer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_FILE_POSITION( PVOID pFile /*IN*/, double * pSampleTime /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_FILE_POSITION( PVOID pFile /*IN*/, double dSampleTime /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |2D+|  |TS|   |*/ QCAP_GET_FILE_POSITION_EX( PVOID pFile /*IN*/, double * pSampleTime /*OUT*/, ULONG nTimeUnit = QCAP_FILE_TIMEUNIT_FRAME /*IN*/ ); // ONLY FOR QCAP_OPEN_FILE_EX & QCAP_OPEN_TIMESHIFT_FILE_EX
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |2D+|  |TS|   |*/ QCAP_SET_FILE_POSITION_EX( PVOID pFile /*IN*/, double dSampleTime /*IN*/, ULONG nTimeUnit = QCAP_FILE_TIMEUNIT_FRAME /*IN*/ ); // ONLY FOR QCAP_OPEN_FILE_EX & QCAP_OPEN_TIMESHIFT_FILE_EX
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |  |SCF|*/ QCAP_GET_GLOBAL_SCF_FILE_POSITION( double * pSampleTime /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |  |  |SCF|*/ QCAP_SET_GLOBAL_SCF_FILE_POSITION( double dSampleTime /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_FILE_PLAYBACK_PROPERTY( PVOID pFile /*IN*/, ULONG * pRepeatTimes /*OUT*/ /*-1 = FOREVER, 0 = DISABLE (DEFAULT)*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_FILE_PLAYBACK_PROPERTY( PVOID pFile /*IN*/, ULONG nRepeatTimes /*IN*/ /*-1 = FOREVER, 0 = DISABLE (DEFAULT)*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_FILE_PLAYBACK_SPEED( PVOID pFile /*IN*/, double * pSpeed /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_FILE_PLAYBACK_SPEED( PVOID pFile /*IN*/, double dSpeed /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_VIDEO_FILE_STREAM_BUFFER( PVOID pFile /*IN*/, UINT iFrameNum /*IN*/, BYTE * pStreamBuffer /*OUT*/, ULONG * pStreamBufferLen /*IN/OUT*/, BOOL * pIsKeyFrame /*OUT*/, double * pSampleTime /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_AUDIO_FILE_STREAM_BUFFER( PVOID pFile /*IN*/, UINT iFrameNum /*IN*/, BYTE * pStreamBuffer /*OUT*/, ULONG * pStreamBufferLen /*IN/OUT*/, double * pSampleTime /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_METADATA_FILE_DATA_BUFFER( PVOID pFile /*IN*/, UINT iFrameNum /*IN*/, BYTE * pDataBuffer /*OUT*/, ULONG * pDataBufferLen /*IN/OUT*/, double * pSampleTime /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_METADATA_FILE_HEADER( PVOID pFile /*IN*/, CHAR * * ppszTitle /*OUT*/, CHAR * * ppszArtist /*OUT*/, CHAR * * ppszComments /*OUT*/, CHAR * * ppszGenre /*OUT*/, CHAR * * ppszComposer /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_METADATA_FILE_HEADER_W( PVOID pFile /*IN*/, WSTRING * ppwszTitle /*OUT*/, WSTRING * ppwszArtist /*OUT*/, WSTRING * ppwszComments /*OUT*/, WSTRING * ppwszGenre /*OUT*/, WSTRING * ppwszComposer /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |3D|  |   |*/ QCAP_GET_VIDEO_3D_FILE_DISPLAY_MODE( PVOID pFile /*IN*/, ULONG * pStereoDisplayMode /*OUT*/, BOOL * pLeftRightSwap /*OUT*/ );  // ONLY FOR QCAP_OPEN_3D_FILE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |3D|  |   |*/ QCAP_SET_VIDEO_3D_FILE_DISPLAY_MODE( PVOID pFile /*IN*/, ULONG nStereoDisplayMode = QCAP_3D_STEREO_DISPLAY_MODE_LINE_BY_LINE /*IN*/, BOOL bLeftRightSwap = FALSE /*IN*/ ); // ONLY FOR QCAP_OPEN_3D_FILE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_VIDEO_FILE_REGION_DISPLAY( PVOID pFile /*IN*/, ULONG * pCropX /*OUT*/, ULONG * pCropY /*OUT*/, ULONG * pCropW /*OUT*/, ULONG * pCropH /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_VIDEO_FILE_REGION_DISPLAY( PVOID pFile /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_VIDEO_FILE_MIRROR( PVOID pFile /*IN*/, BOOL * pHorizontalMirror /*OUT*/, BOOL * pVerticalMirror /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_VIDEO_FILE_MIRROR( PVOID pFile /*IN*/, BOOL bHorizontalMirror /*IN*/, BOOL bVerticalMirror /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_VIDEO_FILE_ASPECT_RATIO( PVOID pFile /*IN*/, BOOL * pMaintainAspectRatio /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_VIDEO_FILE_ASPECT_RATIO( PVOID pFile /*IN*/, BOOL bMaintainAspectRatio /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_VIDEO_FILE_BRIGHTNESS( PVOID pFile /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_VIDEO_FILE_BRIGHTNESS( PVOID pFile /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_VIDEO_FILE_CONTRAST( PVOID pFile /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_VIDEO_FILE_CONTRAST( PVOID pFile /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_VIDEO_FILE_HUE( PVOID pFile /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_VIDEO_FILE_HUE( PVOID pFile /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_VIDEO_FILE_SATURATION( PVOID pFile /*IN*/, ULONG * pValue /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_VIDEO_FILE_SATURATION( PVOID pFile /*IN*/, ULONG nValue /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_AUDIO_FILE_SOUND_RENDERER( PVOID pFile /*IN*/, UINT * pSoundNum /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_AUDIO_FILE_SOUND_RENDERER( PVOID pFile /*IN*/, UINT iSoundNum /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_AUDIO_FILE_VOLUME( PVOID pFile /*IN*/, ULONG * pVolume /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_AUDIO_FILE_VOLUME( PVOID pFile /*IN*/, ULONG nVolume /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SNAPSHOT_FILE_BMP( PVOID pFile /*IN*/, CHAR * pszFilePathName /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SNAPSHOT_FILE_JPG( PVOID pFile /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SNAPSHOT_FILE_BMP_EX( PVOID pFile /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SNAPSHOT_FILE_JPG_EX( PVOID pFile /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_MOVE_OSD_FILE_OBJECT( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ ); // FOR SCROLLING TEXT/PICTURE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_OSD_FILE_TEXT_BOUNDARY( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_GET_OSD_FILE_TEXT_BOUNDARY_W( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_OSD_FILE_TEXT( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_OSD_FILE_TEXT_W( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_OSD_FILE_TEXT_EX( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_OSD_FILE_TEXT_EX_W( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_OSD_FILE_PICTURE( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nTransparent /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_OSD_FILE_BUFFER( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_SET_OSD_FILE_BUFFER_EX( PVOID pFile /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_REGISTER_FILE_SNAPSHOT_DONE_CALLBACK( PVOID pFile /*IN*/, PF_FILE_SNAPSHOT_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_REGISTER_FILE_SNAPSHOT_STREAM_CALLBACK( PVOID pFile /*IN*/, PF_FILE_SNAPSHOT_STREAM_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_REGISTER_VIDEO_DECODER_FILE_CALLBACK( PVOID pFile /*IN*/, PF_VIDEO_DECODER_FILE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|2D|2D+|3D|TS|SCF|*/ QCAP_REGISTER_AUDIO_DECODER_FILE_CALLBACK( PVOID pFile /*IN*/, PF_AUDIO_DECODER_FILE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |2D+|  |TS|   |*/ QCAP_REGISTER_VIDEO_DECODER_FILE_CALLBACK_EX( PVOID pFile /*IN*/, PF_VIDEO_DECODER_FILE_CALLBACK_EX pCB /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR QCAP_OPEN_FILE_EX & QCAP_OPEN_TIMESHIFT_FILE_EX
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |2D+|  |TS|   |*/ QCAP_REGISTER_AUDIO_DECODER_FILE_CALLBACK_EX( PVOID pFile /*IN*/, PF_AUDIO_DECODER_FILE_CALLBACK_EX pCB /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR QCAP_OPEN_FILE_EX & QCAP_OPEN_TIMESHIFT_FILE_EX
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |   |3D|  |   |*/ QCAP_REGISTER_VIDEO_DECODER_3D_FILE_CALLBACK( PVOID pFile /*IN*/, PF_VIDEO_DECODER_3D_FILE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ ); // ONLY FOR QCAP_OPEN_3D_FILE
+
+// FILE EDITING FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_EXPORT_FILE( PVOID pFile /*IN*/, double dStartSampleTime /*IN*/, double dStopSampleTime /*IN*/, CHAR * pszExportedFileName /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_EXPORT_FILE_EX( PVOID pFile /*IN*/, CHAR * pszExportedFileName /*IN*/, ULONG nFileArgs /*IN*/, double dStartSampleTime1 /*IN*/, double dStopSampleTime1 /*IN*/, double dStartSampleTime2 /*IN*/, double dStopSampleTime2 /*IN*/, ... );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_EXPORT_FILE_EX_C( PVOID pFile /*IN*/, CHAR * pszExportedFileName /*IN*/, ULONG nFileArgs /*IN*/, double dStartSampleTimes[ ] /*IN*/, double dStopSampleTimes[ ] /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_EXPORT_FILES_EX( PVOID pFile /*IN*/, ULONG nFileArgs /*IN*/, double dStartSampleTime1 /*IN*/, double dStopSampleTime1 /*IN*/, CHAR * pszExportedFileName1 /*IN*/, double dStartSampleTime2 /*IN*/, double dStopSampleTime2 /*IN*/, CHAR * pszExportedFileName2 /*IN*/, ... );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_EXPORT_FILES_EX_C( PVOID pFile /*IN*/, ULONG nFileArgs /*IN*/, double dStartSampleTimes[ ] /*IN*/, double dStopSampleTimes[ ] /*IN*/, CHAR * pszExportedFileNames[ ] /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_MERGE_FILES( CHAR * pszFrontEndFileName /*IN*/, CHAR * pszBackEndFileName /*IN*/, CHAR * pszMergedFileName /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_MERGE_FILES_EX( CHAR * pszMergedFileName /*IN*/, ULONG nFileArgs /*IN*/, CHAR * pszSourceFileName1 /*IN*/, CHAR * pszSourceFileName2 /*IN*/, ... );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_MERGE_FILES_EX_C( CHAR * pszMergedFileName /*IN*/, ULONG nFileArgs /*IN*/, CHAR * pszSourceFileNames[ ] /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_MERGE_FILES_BY_EDL( CHAR * pszEDLFileName /*IN*/ ); // PLEASE REFERENCE DOC\MANUALS\13. FILE\MERGE.EDL.EXAMPLE.INI
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_BUILD_VIDEO_ON_DEMAND_FILE( CHAR * pszOriginalFileName /*IN*/, CHAR * pszVideoOnDemandFileName /*IN*/ ); // ONLY FOR MP4 FILE BOXING
+
+// FILE REPAIR FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DIAGNOSE_FILE( CHAR * pszFileName /*IN*/, BOOL * pIsHealthy /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REPAIR_FILE( CHAR * pszBadFileName /*IN*/, CHAR * pszRepairedFileName /*IN*/ );
+
+// FILE TRANSCODING FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+typedef QRETURN (QCAP_EXPORT *PF_FILE_TRANSCODER_CALLBACK)( PVOID pFileTranscoder /*IN*/, double dPercentageCompleted /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_FILE_TRANSCODER( CHAR * pszSrcFileName /*IN*/, PVOID * ppFileTranscoder /*OUT*/, ULONG nDecoderType /*IN*/, ULONG * pVideoEncoderFormat /*OUT*/, ULONG * pVideoWidth /*OUT*/, ULONG * pVideoHeight /*OUT*/, double * pVideoFrameRate /*OUT*/, ULONG * pAudioEncoderFormat /*OUT*/, ULONG * pAudioChannels /*OUT*/, ULONG * pAudioBitsPerSample /*OUT*/, ULONG * pAudioSampleFrequency /*OUT*/, double * pTotalDurationTimes /*OUT*/, ULONG * pTotalVideoFrames /*OUT*/, ULONG * pTotalAudioFrames /*OUT*/, ULONG * pTotalMetadataFrames /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_FILE_TRANSCODER( PVOID pFileTranscoder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_FILE_TRANSCODER_PROPERTY( PVOID pFileTranscoder /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, double * pFrameRate /*OUT*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_FILE_TRANSCODER_PROPERTY( PVOID pFileTranscoder /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  double   dFrameRate /*IN*/,  ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_VIDEO_FILE_TRANSCODER_PROPERTY_EX( PVOID pFileTranscoder /*IN*/, ULONG * pEncoderType /*OUT*/, ULONG * pEncoderFormat /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, double * pFrameRate /*OUT*/, ULONG * pRecordProfile /*OUT*/ /*DEFAULT = BASELINE*/, ULONG * pRecordLevel /*OUT*/ /*DEFAULT = 41*/, ULONG * pRecordEntropy /*OUT*/ /*DEFAULT = CAVLC*/, ULONG * pRecordComplexity /*OUT*/ /*DEFAULT = 0*/, ULONG * pRecordMode /*OUT*/, ULONG * pQuality /*OUT*/, ULONG * pBitRate /*OUT*/, ULONG * pGOP /*OUT*/, ULONG * pBFrames /*OUT*/ /*DEFAULT = 0*/, BOOL * pIsInterleaved /*OUT*/ /*DEFAULT = FALSE*/, ULONG * pSlices /*OUT*/ /*DEFAULT = 0*/, ULONG * pLayers /*OUT*/ /*DEFAULT = 0*/, ULONG * pSceneCut /*OUT*/ /*DEFAULT = 0*/, BOOL * pMultiThread /*OUT*/ /*DEFAULT = TRUE*/, BOOL * pMBBRC /*OUT*/ /*DEFAULT = FALSE*/, BOOL * pExtBRC /*OUT*/ /*DEFAULT = FALSE*/, ULONG * pMinQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pMaxQP /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVMaxRate /*OUT*/ /*DEFAULT = 0*/, ULONG * pVBVBufSize /*OUT*/ /*DEFAULT = 0*/, ULONG * pCBRVariation /*OUT*/ /*DEFAULT = 0*/, ULONG * pAspectRatioX /*OUT*/, ULONG * pAspectRatioY /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_VIDEO_FILE_TRANSCODER_PROPERTY_EX( PVOID pFileTranscoder /*IN*/, ULONG   nEncoderType /*IN*/,  ULONG   nEncoderFormat /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  double   dFrameRate /*IN*/,  ULONG   nRecordProfile /*IN*/  /*DEFAULT = BASELINE*/, ULONG   nRecordLevel /*IN*/  /*DEFAULT = 41*/, ULONG   nRecordEntropy /*IN*/  /*DEFAULT = CAVLC*/, ULONG   nRecordComplexity /*IN*/  /*DEFAULT = 0*/, ULONG   nRecordMode /*IN*/,  ULONG   nQuality /*IN*/,  ULONG   nBitRate /*IN*/,  ULONG   nGOP /*IN*/,  ULONG   nBFrames /*IN*/  /*DEFAULT = 0*/, BOOL   bIsInterleaved /*IN*/  /*DEFAULT = FALSE*/, ULONG   nSlices /*IN*/  /*DEFAULT = 0*/, ULONG   nLayers /*IN*/  /*DEFAULT = 0*/, ULONG   nSceneCut /*IN*/  /*DEFAULT = 0*/, BOOL   bMultiThread /*IN*/  /*DEFAULT = TRUE*/, BOOL   bMBBRC /*IN*/  /*DEFAULT = FALSE*/, BOOL   bExtBRC /*IN*/  /*DEFAULT = FALSE*/, ULONG   nMinQP /*IN*/  /*DEFAULT = 0*/, ULONG   nMaxQP /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVMaxRate /*IN*/  /*DEFAULT = 0*/, ULONG   nVBVBufSize /*IN*/  /*DEFAULT = 0*/, ULONG   nCBRVariation /*IN*/  /*DEFAULT = 0*/, ULONG   nAspectRatioX /*IN*/,  ULONG   nAspectRatioY /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_FILE_TRANSCODER( PVOID pFileTranscoder /*IN*/, CHAR * pszDstFileName /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_FILE_TRANSCODER( PVOID pFileTranscoder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_FILE_TRANSCODER_CALLBACK( PVOID pFileTranscoder /*IN*/, PF_FILE_TRANSCODER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # ANIMATION FUNCTIONS
+// #
+// ##########################################################################################################################################################################################
+//
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_ANIMATION_CLIP( CHAR * pszAniFileName /*IN*/, PVOID * ppClip /*OUT*/, ULONG * pTotalFrames /*OUT*/, ULONG * pTotalSprites /*OUT*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_ANIMATION_CLIP( PVOID pClip /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_ANIMATION_CLIP( PVOID pClip /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_ANIMATION_CLIP( PVOID pClip /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STEP_ANIMATION_CLIP( PVOID pClip /*IN*/, UINT * pFrameNum /*OUT*/, BYTE * * ppFrameBuffer /*OUT*/, ULONG * pFrameBufferLen /*OUT*/, BOOL bClearBackground = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SEEK_ANIMATION_CLIP( PVOID pClip /*IN*/, UINT iFrameNum /*IN*/, BYTE * * ppFrameBuffer /*OUT*/, ULONG * pFrameBufferLen /*OUT*/, BOOL bClearBackground = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_ANIMATION_CLIP_SPRITE_TRANSFORM_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, UINT iFrameNum /*IN*/, INT * pPositionX /*OUT*/, INT * pPositionY /*OUT*/, INT * pScaleW /*OUT*/, INT * pScaleH /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_TRANSFORM_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, UINT iFrameNum /*IN*/, INT nPositionX /*IN*/, INT nPositionY /*IN*/, INT nScaleW /*IN*/, INT nScaleH /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_ANIMATION_CLIP_SPRITE_ALPHA_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, UINT iFrameNum /*IN*/, double * pAlpha /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_ALPHA_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, UINT iFrameNum /*IN*/, double dAlpha /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_ANIMATION_CLIP_SPRITE_ZORDER_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, INT * pZOrder /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_ZORDER_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, INT nZOrder /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_ANIMATION_CLIP_SPRITE_SCALE_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, ULONG * pScaleStyle /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_SCALE_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, ULONG nScaleStyle /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_ANIMATION_CLIP_SPRITE_BLINDS_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, ULONG * pBlindsType /*OUT*/, ULONG * pSlices /*OUT*/, ULONG * pDirection /*OUT*/ /*0 = BOTTOM-TOP, LEFT-RIGHT, 1 = TOP-BOTTOM, RIGHT-LEFT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_BLINDS_PROPERTY( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, ULONG nBlindsType /*IN*/, ULONG nSlices /*IN*/, ULONG nDirection /*IN*/ /*0 = BOTTOM-TOP, LEFT-RIGHT, 1 = TOP-BOTTOM, RIGHT-LEFT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_PICTURE( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, CHAR * pszFilePathName /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_BUFFER( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, BOOL bCloneCopy = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_BUFFER_EX( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, BOOL bCloneCopy = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_SOURCE( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, PVOID pDevice /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, BOOL bCloneCopy = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SET_ANIMATION_CLIP_SPRITE_SOURCE_EX( PVOID pClip /*IN*/, UINT iSpriteNum /*IN*/, PVOID pDevice /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, BOOL bCloneCopy = FALSE /*IN*/ );
+
+// ANIMATION EXAMPLE:
+//
+// STEP#01 QCAP_CREATE_ANIMATION_CLIP( "EXAMPLE.FADEOUT.XML", &pClip, &nFrames, &nSprites, &nColorSpaceType, &nWidth, &nHeight );
+//
+// STEP#02 QCAP_START_ANIMATION_CLIP( pClip );
+//
+// STEP#03 QCAP_SET_ANIMATION_CLIP_SPRITE_BUFFER( pClip, 0 /*CH01*/, QCAP_COLORSPACE_TYPE_YUY2, 1920, 1080, pFrameBufferA, nFrameBufferLenA );
+// 
+//         QCAP_SET_ANIMATION_CLIP_SPRITE_SOURCE( pClip, 1 /*CH02*/, QCAP_COLORSPACE_TYPE_YUY2, 1920, 1080, pFrameBufferB, nFrameBufferLenB );
+//
+// STEP#04 QCAP_STEP_ANIMATION_CLIP( pClip, &iFrameNum, &pFrameBuffer, &nFrameBufferLen, TRUE );
+//
+// STEP#05 QCAP_STOP_ANIMATION_CLIP( pClip );
+//
+// STEP#06 QCAP_DESTROY_ANIMATION_CLIP( pClip );
+
+// ##########################################################################################################################################################################################
+// #
+// # VIRTUAL CAMERA FUNCTIONS
+// #
+// ##########################################################################################################################################################################################
+//
+typedef QRETURN (QCAP_EXPORT *PF_VIRTUAL_CAMERA_SNAPSHOT_DONE_CALLBACK)( PVOID pCamera /*IN*/, CHAR * pszFilePathName /*IN*/, PVOID pUserData /*IN*/ );
+
+typedef QRETURN (QCAP_EXPORT *PF_VIRTUAL_CAMERA_SNAPSHOT_STREAM_CALLBACK)( PVOID pCamera /*IN*/, CHAR * pszFilePathName /*IN*/, BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_CREATE_VIRTUAL_CAMERA( UINT iCamNum /*IN*/ /*0 ~ 63*/, PVOID * ppCamera /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_DESTROY_VIRTUAL_CAMERA( PVOID pCamera /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_GET_VIDEO_VIRTUAL_CAMERA_PROPERTY( PVOID pCamera /*IN*/, ULONG * pColorSpaceType /*OUT*/, ULONG * pWidth /*OUT*/, ULONG * pHeight /*OUT*/, double * pFrameRate /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_VIDEO_VIRTUAL_CAMERA_PROPERTY( PVOID pCamera /*IN*/, ULONG   nColorSpaceType /*IN*/,  ULONG   nWidth /*IN*/,  ULONG   nHeight /*IN*/,  double   dFrameRate /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_GET_AUDIO_VIRTUAL_CAMERA_PROPERTY( PVOID pCamera /*IN*/, ULONG * pChannels /*OUT*/, ULONG * pBitsPerSample /*OUT*/, ULONG * pSampleFrequency /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_AUDIO_VIRTUAL_CAMERA_PROPERTY( PVOID pCamera /*IN*/, ULONG   nChannels /*IN*/,  ULONG   nBitsPerSample /*IN*/,  ULONG   nSampleFrequency /*IN*/  );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_VIDEO_VIRTUAL_CAMERA_UNCOMPRESSION_BUFFER( PVOID pCamera /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_VIDEO_VIRTUAL_CAMERA_UNCOMPRESSION_BUFFER_EX( PVOID pCamera /*IN*/, ULONG nColorSpaceType /*IN*/, ULONG nWidth /*IN*/, ULONG nHeight /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nScaleStyle = QCAP_SCALE_STYLE_STRETCH /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_AUDIO_VIRTUAL_CAMERA_UNCOMPRESSION_BUFFER( PVOID pCamera /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_AUDIO_VIRTUAL_CAMERA_UNCOMPRESSION_BUFFER_EX( PVOID pCamera /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, double dSampleTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|MX|*/ QCAP_SET_AUDIO_MX_VIRTUAL_CAMERA_MIXING_UNCOMPRESSION_BUFFER( PVOID pCamera /*IN*/, UINT iMixNum /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ ); // ONLY FOR AUDIO MIXING
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|MX|*/ QCAP_SET_AUDIO_MX_VIRTUAL_CAMERA_MIXING_UNCOMPRESSION_BUFFER_EX( PVOID pCamera /*IN*/, UINT iMixNum /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/ ); // ONLY FOR AUDIO MIXING
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|MX|*/ QCAP_SET_AUDIO_MX_VIRTUAL_CAMERA_UNCOMPRESSION_BUFFER( PVOID pCamera /*IN*/, double dSampleTime = 0.0 /*IN*/, BOOL bEnableSyncMixer = TRUE /*IN*/ ); // ONLY FOR AUDIO MIXING
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_START_VIRTUAL_CAMERA( PVOID pCamera /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_STOP_VIRTUAL_CAMERA( PVOID pCamera /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SNAPSHOT_VIRTUAL_CAMERA_BMP( PVOID pCamera /*IN*/, CHAR * pszFilePathName /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SNAPSHOT_VIRTUAL_CAMERA_JPG( PVOID pCamera /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SNAPSHOT_VIRTUAL_CAMERA_BMP_EX( PVOID pCamera /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SNAPSHOT_VIRTUAL_CAMERA_JPG_EX( PVOID pCamera /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, ULONG nDstW /*IN*/, ULONG nDstH /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_MOVE_OSD_VIRTUAL_CAMERA_OBJECT( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ ); // FOR SCROLLING TEXT/PICTURE
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_GET_OSD_VIRTUAL_CAMERA_TEXT_BOUNDARY( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_GET_OSD_VIRTUAL_CAMERA_TEXT_BOUNDARY_W( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, ULONG * pBoundaryWidth /*OUT*/, ULONG * pBoundaryHeight /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_OSD_VIRTUAL_CAMERA_TEXT( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_OSD_VIRTUAL_CAMERA_TEXT_W( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_OSD_VIRTUAL_CAMERA_TEXT_EX( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszString /*IN*/, CHAR * pszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_OSD_VIRTUAL_CAMERA_TEXT_EX_W( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, WSTRING pwszString /*IN*/, WSTRING pwszFontFamilyName /*IN*/, ULONG nFontStyle /*IN*/, ULONG nFontSize /*IN*/, DWORD dwFontColor /*IN*/, DWORD dwBackgroundColor /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, INT nTextStartPosX = 0 /*IN*/, INT nTextStartPosY = 0 /*IN*/, ULONG nStringAlignmentStyle = QCAP_STRING_ALIGNMENT_STYLE_LEFT /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_OSD_VIRTUAL_CAMERA_PICTURE( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, CHAR * pszFilePathName /*IN*/, ULONG nTransparent /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_OSD_VIRTUAL_CAMERA_BUFFER( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_SET_OSD_VIRTUAL_CAMERA_BUFFER_EX( PVOID pCamera /*IN*/, UINT iOsdNum /*IN*/, INT x /*IN*/, INT y /*IN*/, INT w /*IN*/, INT h /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pFrameBuffer /*IN*/, ULONG nFrameWidth /*IN*/, ULONG nFrameHeight /*IN*/, ULONG nFramePitch /*IN*/, ULONG nCropX /*IN*/, ULONG nCropY /*IN*/, ULONG nCropW /*IN*/, ULONG nCropH /*IN*/, DWORD dwBorderColor /*IN*/, ULONG nBorderWidth /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/, BYTE * pMaskBuffer = NULL /*IN*/, ULONG nSequenceStyle = QCAP_SEQUENCE_STYLE_FOREMOST /*IN*/, double dLifeTime = 0.0 /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_REGISTER_VIRTUAL_CAMERA_SNAPSHOT_DONE_CALLBACK( PVOID pCamera /*IN*/, PF_VIRTUAL_CAMERA_SNAPSHOT_DONE_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT /*|  |*/ QCAP_REGISTER_VIRTUAL_CAMERA_SNAPSHOT_STREAM_CALLBACK( PVOID pCamera /*IN*/, PF_VIRTUAL_CAMERA_SNAPSHOT_STREAM_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # SERIAL PORT I/O FUNCTIONS (RS232/RS485)
+// #
+// ##########################################################################################################################################################################################
+//
+typedef QRETURN (QCAP_EXPORT *PF_SERIAL_PORT_RECEIVED_DATA_CALLBACK)( PVOID pPort /*IN*/, UINT iPortNum /*IN*/, BYTE * pDataBuffer /*IN*/, ULONG nDataBufferLen /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SERIAL_PORT_ENUMERATION( UINT * pAvailablePortNum /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_SERIAL_PORT( UINT iPortNum /*IN*/, PVOID * ppPort /*OUT*/, ULONG nBaudRate = 9600 /*IN*/, ULONG nDataBits = 8 /*IN*/, ULONG nParityCheck = QCAP_SERIAL_PORT_PARITY_CHECK_NONE /*IN*/, ULONG nStopBits = QCAP_SERIAL_PORT_STOP_BITS_ONE /*IN*/, ULONG nFlowControl = QCAP_SERIAL_PORT_FLOW_CONTROL_NONE /*IN*/, BOOL bAsynchronousIO = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_SERIAL_PORT( PVOID pPort /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_SERIAL_PORT( PVOID pPort /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_SERIAL_PORT( PVOID pPort /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SEND_SERIAL_PORT_SIGNAL( PVOID pPort /*IN*/, ULONG nSignalType /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SEND_SERIAL_PORT_DATA( PVOID pPort /*IN*/, BYTE * pDataBuffer /*IN*/, ULONG nDataBufferLen /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_SERIAL_PORT_RECEIVED_DATA_CALLBACK( PVOID pPort /*IN*/, PF_SERIAL_PORT_RECEIVED_DATA_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # DISC BURNING FUNCTIONS
+// #
+// ##########################################################################################################################################################################################
+//
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_BURNING_DRIVE( CHAR cDriveName /*IN*/, CHAR * pVolumeName /*IN*/, PVOID * ppDrive /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_BURNING_DRIVE( PVOID pDrive /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_BURNING_DRIVE( PVOID pDrive /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_BURNING_DRIVE( PVOID pDrive /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_UNLOAD_DISC( PVOID pDrive /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_LOAD_DISC( PVOID pDrive /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_DISC_INFO( PVOID pDrive /*IN*/, BOOL * pIsDiscBlank /*OUT*/, BOOL * pIsDiscWriteable /*OUT*/, DWORD * pDiscType /*OUT*/, ULONGLONG * pDiscRemainCapability /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_ERASE_DISC( PVOID pDrive /*IN*/ );
+
+// BURNING EXAMPLE:
+// 
+// STEP#01 QCAP_CREATE_BURNING_DRIVE( 'E', "TEST", &pDrive );
+//
+// STEP#02 QCAP_START_BURNING_DRIVE( pDrive );
+// 
+// STEP#03 QCAP_START_RECORD( pDevice, 0, "E:\\CH01.TS" );
+// 
+// STEP#04 QCAP_STOP_RECORD( pDevice, 0, FALSE, INFINE ); // NOTE!! WAITIING FILE CLOSE
+// 
+// STEP#05 QCAP_STOP_BURNING_DRIVE( pDrive );
+// 
+// STEP#06 QCAP_UNLOAD_DISC( pDrive );
+// 
+// STEP#07 QCAP_DESTROY_BURNING_DRIVE( pDrive );
+
+// ##########################################################################################################################################################################################
+// #
+// # MEDIA TIMER FUNCTIONS (HIGH PRECISION)
+// #
+// ##########################################################################################################################################################################################
+//
+typedef QRETURN (QCAP_EXPORT *PF_MEDIA_TIMER_CALLBACK)( PVOID pTimer /*IN*/, double dSampleTime /*IN*/, double dDelayTime /*IN*/, PVOID pUserData /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CREATE_MEDIA_TIMER( ULONG nElapseTimeNum /*IN*/, ULONG nElapseTimeDeno /*IN*/, PVOID * ppTimer /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_DESTROY_MEDIA_TIMER( PVOID pTimer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_START_MEDIA_TIMER( PVOID pTimer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_STOP_MEDIA_TIMER( PVOID pTimer /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_REGISTER_MEDIA_TIMER_CALLBACK( PVOID pTimer /*IN*/, PF_MEDIA_TIMER_CALLBACK pCB /*IN*/, PVOID pUserData /*IN*/ );
+
+// ##########################################################################################################################################################################################
+// #
+// # HELPER FUNCTIONS (ENCODER/VIDEO/AUDIO/DEVELOPER)
+// #
+// ##########################################################################################################################################################################################
+//
+// HELPER FUNCTIONS PART.1 (FOR ENCODER) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_H264_BUFFER_LAYER_ID( BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, ULONG * pLayerID /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_H264_BUFFER_COLOR_INFO( BYTE * pStreamBuffer /*IN*/, ULONG nStreamBufferLen /*IN*/, ULONG * pColorRangeType /*OUT*/ );
+
+// HELPER FUNCTIONS PART.2 (FOR VIDEO) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YUY2_TO_ABGR32( BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BYTE bAlpah = 0x00 /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YV12_TO_ABGR32( BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BYTE bAlpah = 0x00 /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YUY2_TO_ARGB32( BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BYTE bAlpah = 0x00 /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YV12_TO_ARGB32( BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BYTE bAlpah = 0x00 /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YUY2_TO_BGR24(  BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YV12_TO_BGR24(  BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YUY2_TO_RGB24(  BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YV12_TO_RGB24(  BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YUY2_TO_YV12(   BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_COLORSPACE_YV12_TO_YUY2(   BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BOOL bHorizontalMirror = FALSE /*IN*/, BOOL bVerticalMirror = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_BUFFER_TO_BMP_EX( CHAR * pszFilePathName /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, UINT nCropX /*IN*/, UINT nCropY /*IN*/, UINT nCropW /*IN*/, UINT nCropH /*IN*/, UINT nDstW /*IN*/, UINT nDstH /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/, BYTE * pszDstStreamBuffer = NULL /*OUT*/, ULONG * pDstStreamBufferSize = NULL /*IN/OUT*/, PF_SNAPSHOT_DONE_CALLBACK pCB = NULL /*IN*/, PVOID pUserData = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SNAPSHOT_BUFFER_TO_JPG_EX( CHAR * pszFilePathName /*IN*/, ULONG nColorSpaceType /*IN*/, BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, UINT nCropX /*IN*/, UINT nCropY /*IN*/, UINT nCropW /*IN*/, UINT nCropH /*IN*/, UINT nDstW /*IN*/, UINT nDstH /*IN*/, ULONG nQuality /*IN*/, BOOL bIsAsync = TRUE /*IN*/, ULONG nMilliseconds = 0 /*IN*/, BYTE * pszDstStreamBuffer = NULL /*OUT*/, ULONG * pDstStreamBufferSize = NULL /*IN/OUT*/, PF_SNAPSHOT_DONE_CALLBACK pCB = NULL /*IN*/, PVOID pUserData = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CONVERT_3D_STEREO_BUFFER( ULONG nColorSpaceType /*IN*/, ULONG nSrcStereoDisplayMode /*IN*/, BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, ULONG nDstStereoDisplayMode /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, BOOL bLeftRightSwap = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_RESIZE_VIDEO_BUFFER( ULONG nColorSpaceType /*IN*/, BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_ROTATE_VIDEO_BUFFER_BOUNDARY( ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG * pDstWidth /*OUT*/, ULONG * pDstHeight /*OUT*/, double dAngle /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_ROTATE_VIDEO_BUFFER( ULONG nColorSpaceType /*IN*/, BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, double dAngle /*IN*/, BYTE * pSrcTempFrameBuffer = NULL /*IN*/, BYTE * pDstTempFrameBuffer = NULL /*IN*/, BOOL bClearBackground = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_WARP_VIDEO_BUFFER( ULONG nColorSpaceType /*IN*/, BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, LONG nWarpDstX1 /*IN*/, LONG nWarpDstY1 /*IN*/, LONG nWarpDstX2 /*IN*/, LONG nWarpDstY2 /*IN*/, LONG nWarpDstX3 /*IN*/, LONG nWarpDstY3 /*IN*/, LONG nWarpDstX4 /*IN*/, LONG nWarpDstY4 /*IN*/, BYTE * pSrcTempFrameBuffer = NULL /*IN*/, BYTE * pDstTempFrameBuffer = NULL /*IN*/, BOOL bClearBackground = TRUE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_LOAD_PICTURE_BUFFER( CHAR * pszFilePathName /*IN*/, ULONG * pColorSpaceType /*OUT*/, BYTE * pFrameBuffer /*OUT*/, ULONG * pFrameBufferSize /*IN/OUT*/, ULONG * pFrameWidth /*OUT*/, ULONG * pFrameHeight /*OUT*/, ULONG * pFramePitch /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_CALCULATE_CHROMAKEY( ULONG nColorSpaceType /*IN*/, BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcWidth /*IN*/, ULONG nSrcHeight /*IN*/, ULONG nSrcPitch /*IN*/, ULONG nSrcCropX /*IN*/, ULONG nSrcCropY /*IN*/, ULONG nSrcCropW /*IN*/, ULONG nSrcCropH /*IN*/, BYTE * pDstAlpahBuffer /*OUT*/, BYTE * pDstYBuffer /*OUT*/, BYTE * pDstCbBuffer /*OUT*/, BYTE * pDstCrBuffer /*OUT*/, BYTE * pDstABGRBuffer /*OUT*/, ULONG nDstWidth /*IN*/, ULONG nDstHeight /*IN*/, ULONG nDstPitch /*IN*/, ULONG nTransparent /*IN*/, DWORD dwKeyColor = 0xFFFFFFFF /*IN*/ /*0xFFFFFFFF (OFF) / 0x00FF0000 (BLUE) / 0x0000FF00 (GREEN)*/, ULONG nKeyColorThreshold = 25 /*IN*/ /*0 ~ 128*/, ULONG nKeyColorBlurLevel = 2 /*IN*/ /*0 ~ 2*/, BOOL bKeyColorSpillSuppress = TRUE /*IN*/, ULONG nKeyColorSpillSuppressThreshold = 22 /*IN*/ );
+
+// HELPER FUNCTIONS PART.3 (FOR AUDIO) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_BUFFER_VOLUME_DB( BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, UINT iChNum /*IN*/, double * pVolumeDB /*OUT*/ /*RANGE = -100 ~ 0 (DB)*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_GET_AUDIO_BUFFER_FAST_FOURIER_TRANSFORM_DATA( BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, UINT iChNum /*IN*/, double * pPeakFrq /*OUT*/, double * pPeakFrqAmp /*OUT*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_RESAMPLE_AUDIO_BUFFER( BYTE * pSrcFrameBuffer /*IN*/, ULONG nSrcFrameBufferLen /*IN*/, ULONG nSrcChannels /*IN*/, ULONG nSrcBitsPerSample /*IN*/, ULONG nSrcSampleFrequency /*IN*/, BYTE * pDstFrameBuffer /*OUT*/, ULONG * pDstFrameBufferLen /*IN/OUT*/, ULONG nDstChannels /*IN*/, ULONG nDstBitsPerSample /*IN*/, ULONG nDstSampleFrequency /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_RESCALE_AUDIO_BUFFER( BYTE * pFrameBuffer /*IN*/, ULONG nFrameBufferLen /*IN*/, ULONG nChannels /*IN*/, ULONG nBitsPerSample /*IN*/, ULONG nSampleFrequency /*IN*/, ULONG nVolume /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SOUNDRENDERER_ENUMERATION( CHAR * * ppszSoundRendererDevName /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SOUNDRENDERER_ENUMERATION_W( WSTRING * ppwszSoundRendererDevName /*OUT*/, BOOL bNext = FALSE /*IN*/ );
+
+// HELPER FUNCTIONS PART.4 (FOR SYSTEM) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SYSTEM_ENABLE_HARDWARE_DEVICE( DWORD dwVendorID /*IN*/, DWORD dwProductID /*IN*/, CHAR * pszDevicePath = NULL /*IN*/ );
+
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_SYSTEM_DISABLE_HARDWARE_DEVICE( DWORD dwVendorID /*IN*/, DWORD dwProductID /*IN*/, CHAR * pszDevicePath = NULL /*IN*/ );
+
+// HELPER FUNCTIONS PART.5 (FOR MATRIX) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API QRESULT QCAP_EXPORT QCAP_MATRIX_QUAD_MAPPING( double * pMatrixA /*IN*/ /*(x1, y1) ~ (x4, y4)*/, double * pMatrixB /*IN*/ /*(X1, Y1) ~ (X4, Y4)*/, double * pSrcA /*IN*/ /*(x, y)*/, double * pDstB /*OUT*/ /*(X, Y)*/ );
+
+// HELPER FUNCTIONS PART.6 (FOR .NET DEVELOPER) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 
+QCAP_EXT_API ULONGLONG QCAP_EXPORT QCAP_HELPER_OBJPTR( PVOID pObj /*IN*/ );
+
+#endif // QCAP_H
+

+ 54 - 0
SATHelper/SATHelper/SATHelper.vcxproj

@@ -98,9 +98,11 @@
       <SDLCheck>true</SDLCheck>
       <PreprocessorDefinitions>WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>..\UI;..\filehelper;..\cJson</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
+      <AdditionalDependencies>QCAP.X86.lib;VFW32.LIB;legacy_stdio_definitions.lib</AdditionalDependencies>
     </Link>
     <Midl>
       <MkTypLibCompatible>false</MkTypLibCompatible>
@@ -146,6 +148,7 @@
       <SDLCheck>true</SDLCheck>
       <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>..\UI;..\filehelper;..\cJson</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -191,28 +194,70 @@
     </ResourceCompile>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClInclude Include="..\cJson\cJSON.h" />
+    <ClInclude Include="..\filehelper\filehelper.h" />
+    <ClInclude Include="..\filehelper\findfile.h" />
+    <ClInclude Include="..\UI\BCMenu.h" />
+    <ClInclude Include="..\UI\BtnST.h" />
+    <ClInclude Include="..\UI\ComboTreeCtrl.h" />
+    <ClInclude Include="..\UI\FontSize.h" />
+    <ClInclude Include="..\UI\PictureEx.h" />
+    <ClInclude Include="..\UI\SubLabel.h" />
+    <ClInclude Include="..\UI\TreeComboBox.h" />
+    <ClInclude Include="..\UI\XColorStatic.h" />
     <ClInclude Include="calendarbar.h" />
     <ClInclude Include="ChildFrm.h" />
     <ClInclude Include="ClassView.h" />
+    <ClInclude Include="CritSection.h" />
+    <ClInclude Include="Device.h" />
     <ClInclude Include="FileView.h" />
     <ClInclude Include="framework.h" />
+    <ClInclude Include="Global.h" />
+    <ClInclude Include="IOCPModel.h" />
     <ClInclude Include="MainFrm.h" />
+    <ClInclude Include="MemoryClient.h" />
+    <ClInclude Include="MemoryComm.h" />
+    <ClInclude Include="MemoryDef.h" />
     <ClInclude Include="OutputWnd.h" />
+    <ClInclude Include="QCAP.H" />
     <ClInclude Include="stdafx.h" />
     <ClInclude Include="PropertiesWnd.h" />
     <ClInclude Include="Resource.h" />
     <ClInclude Include="SATHelper.h" />
     <ClInclude Include="SATHelperDoc.h" />
     <ClInclude Include="SATHelperView.h" />
+    <ClInclude Include="stdint.h" />
+    <ClInclude Include="SynSerial.h" />
     <ClInclude Include="targetver.h" />
+    <ClInclude Include="tinyxml2.h" />
+    <ClInclude Include="TrayIcon.h" />
     <ClInclude Include="ViewTree.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="..\cJson\cJSON.c">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\filehelper\filehelper.cpp" />
+    <ClCompile Include="..\filehelper\findfile.cpp" />
+    <ClCompile Include="..\UI\BCMenu.cpp" />
+    <ClCompile Include="..\UI\BtnST.cpp" />
+    <ClCompile Include="..\UI\ComboTreeCtrl.cpp" />
+    <ClCompile Include="..\UI\FontSize.cpp" />
+    <ClCompile Include="..\UI\PictureEx.cpp" />
+    <ClCompile Include="..\UI\SubLabel.cpp" />
+    <ClCompile Include="..\UI\TreeComboBox.cpp" />
+    <ClCompile Include="..\UI\XColorStatic.cpp" />
     <ClCompile Include="calendarbar.cpp" />
     <ClCompile Include="ChildFrm.cpp" />
     <ClCompile Include="ClassView.cpp" />
+    <ClCompile Include="Device.cpp" />
     <ClCompile Include="FileView.cpp" />
+    <ClCompile Include="Global.cpp" />
+    <ClCompile Include="IOCPModel.cpp" />
     <ClCompile Include="MainFrm.cpp" />
+    <ClCompile Include="MemoryClient.cpp" />
+    <ClCompile Include="MemoryComm.cpp" />
     <ClCompile Include="OutputWnd.cpp" />
     <ClCompile Include="stdafx.cpp">
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
@@ -224,6 +269,9 @@
     <ClCompile Include="SATHelper.cpp" />
     <ClCompile Include="SATHelperDoc.cpp" />
     <ClCompile Include="SATHelperView.cpp" />
+    <ClCompile Include="SynSerial.cpp" />
+    <ClCompile Include="tinyxml2.cpp" />
+    <ClCompile Include="TrayIcon.cpp" />
     <ClCompile Include="ViewTree.cpp" />
   </ItemGroup>
   <ItemGroup>
@@ -272,6 +320,12 @@
     <None Include="res\ribbon.mfcribbon-ms" />
     <None Include="res\SATHelper.rc2" />
   </ItemGroup>
+  <ItemGroup>
+    <Xml Include="layout.xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Library Include="QCAP.X86.LIB" />
+  </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>

+ 159 - 12
SATHelper/SATHelper/SATHelper.vcxproj.filters

@@ -13,6 +13,21 @@
       <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;mfcribbon-ms</Extensions>
     </Filter>
+    <Filter Include="DockPane">
+      <UniqueIdentifier>{5335a608-bbc8-4b83-9bb5-be9e7caee446}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="filehelper">
+      <UniqueIdentifier>{ecef0b7a-364a-41e6-8862-2aebc61f8401}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="iocp">
+      <UniqueIdentifier>{e99ed651-2195-43dc-8290-630d4891751c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="json">
+      <UniqueIdentifier>{d78a04c9-45f2-4224-92db-5850c67af94b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="UI">
+      <UniqueIdentifier>{71b6c470-1206-4d26-a155-8475a6ffec8a}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="SATHelper.h">
@@ -39,27 +54,96 @@
     <ClInclude Include="Resource.h">
       <Filter>头文件</Filter>
     </ClInclude>
-    <ClInclude Include="FileView.h">
+    <ClInclude Include="ViewTree.h">
       <Filter>头文件</Filter>
     </ClInclude>
+    <ClInclude Include="stdafx.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="calendarbar.h">
+      <Filter>DockPane</Filter>
+    </ClInclude>
+    <ClInclude Include="FileView.h">
+      <Filter>DockPane</Filter>
+    </ClInclude>
     <ClInclude Include="ClassView.h">
+      <Filter>DockPane</Filter>
+    </ClInclude>
+    <ClInclude Include="OutputWnd.h">
+      <Filter>DockPane</Filter>
+    </ClInclude>
+    <ClInclude Include="PropertiesWnd.h">
+      <Filter>DockPane</Filter>
+    </ClInclude>
+    <ClInclude Include="..\filehelper\filehelper.h">
+      <Filter>filehelper</Filter>
+    </ClInclude>
+    <ClInclude Include="..\filehelper\findfile.h">
+      <Filter>filehelper</Filter>
+    </ClInclude>
+    <ClInclude Include="..\cJson\cJSON.h">
+      <Filter>json</Filter>
+    </ClInclude>
+    <ClInclude Include="..\UI\BCMenu.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\UI\BtnST.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\UI\ComboTreeCtrl.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\UI\FontSize.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\UI\PictureEx.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\UI\SubLabel.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\UI\TreeComboBox.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\UI\XColorStatic.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="Device.h">
       <Filter>头文件</Filter>
     </ClInclude>
-    <ClInclude Include="ViewTree.h">
+    <ClInclude Include="Global.h">
       <Filter>头文件</Filter>
     </ClInclude>
-    <ClInclude Include="OutputWnd.h">
+    <ClInclude Include="MemoryClient.h">
       <Filter>头文件</Filter>
     </ClInclude>
-    <ClInclude Include="PropertiesWnd.h">
+    <ClInclude Include="MemoryComm.h">
       <Filter>头文件</Filter>
     </ClInclude>
-    <ClInclude Include="calendarbar.h">
+    <ClInclude Include="MemoryDef.h">
       <Filter>头文件</Filter>
     </ClInclude>
-    <ClInclude Include="stdafx.h">
+    <ClInclude Include="QCAP.H">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="stdint.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="SynSerial.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="tinyxml2.h">
       <Filter>头文件</Filter>
     </ClInclude>
+    <ClInclude Include="TrayIcon.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="CritSection.h">
+      <Filter>iocp</Filter>
+    </ClInclude>
+    <ClInclude Include="IOCPModel.h">
+      <Filter>iocp</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="SATHelper.cpp">
@@ -77,27 +161,84 @@
     <ClCompile Include="SATHelperView.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
-    <ClCompile Include="FileView.cpp">
+    <ClCompile Include="ViewTree.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
+    <ClCompile Include="stdafx.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="calendarbar.cpp">
+      <Filter>DockPane</Filter>
+    </ClCompile>
     <ClCompile Include="ClassView.cpp">
+      <Filter>DockPane</Filter>
+    </ClCompile>
+    <ClCompile Include="FileView.cpp">
+      <Filter>DockPane</Filter>
+    </ClCompile>
+    <ClCompile Include="OutputWnd.cpp">
+      <Filter>DockPane</Filter>
+    </ClCompile>
+    <ClCompile Include="PropertiesWnd.cpp">
+      <Filter>DockPane</Filter>
+    </ClCompile>
+    <ClCompile Include="..\filehelper\filehelper.cpp">
+      <Filter>filehelper</Filter>
+    </ClCompile>
+    <ClCompile Include="..\filehelper\findfile.cpp">
+      <Filter>filehelper</Filter>
+    </ClCompile>
+    <ClCompile Include="..\cJson\cJSON.c">
+      <Filter>json</Filter>
+    </ClCompile>
+    <ClCompile Include="..\UI\BCMenu.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\UI\BtnST.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\UI\ComboTreeCtrl.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\UI\FontSize.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\UI\PictureEx.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\UI\SubLabel.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\UI\TreeComboBox.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\UI\XColorStatic.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="Device.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
-    <ClCompile Include="ViewTree.cpp">
+    <ClCompile Include="Global.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
-    <ClCompile Include="OutputWnd.cpp">
+    <ClCompile Include="MemoryClient.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
-    <ClCompile Include="PropertiesWnd.cpp">
+    <ClCompile Include="MemoryComm.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
-    <ClCompile Include="calendarbar.cpp">
+    <ClCompile Include="SynSerial.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
-    <ClCompile Include="stdafx.cpp">
+    <ClCompile Include="tinyxml2.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="TrayIcon.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
+    <ClCompile Include="IOCPModel.cpp">
+      <Filter>iocp</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="SATHelper.rc">
@@ -225,4 +366,10 @@
       <Filter>资源文件</Filter>
     </None>
   </ItemGroup>
+  <ItemGroup>
+    <Xml Include="layout.xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Library Include="QCAP.X86.LIB" />
+  </ItemGroup>
 </Project>

+ 305 - 0
SATHelper/SATHelper/SynSerial.cpp

@@ -0,0 +1,305 @@
+#include "StdAfx.h"
+#include "SynSerial.h"
+
+CSynSerial::CSynSerial(void) :m_hSerialPort(NULL)
+, m_dwInQueue(1024)
+, m_dwOutQueue(1024)
+{
+	memset(&m_dcb, 0, sizeof(DCB));
+	memset(&m_cts, 0, sizeof(COMMTIMEOUTS));
+	memset(&m_szSerialPort, 0, sizeof(TCHAR)*MAX_PORT_LEN);
+}
+
+CSynSerial::~CSynSerial(void)
+{
+	CloseSerialPort();
+}
+
+BOOL CSynSerial::OpenSerialPort(IN CONST BYTE & byCommPort, IN CONST DWORD & dwBaudRate, IN CONST BYTE & bySize, IN CONST BYTE & byParity, IN CONST BYTE & byStopBits, IN CONST BYTE & byStartAddr, IN CONST INT & nInterval)
+{
+	ASSERT(byCommPort);
+	CloseSerialPort();
+
+	_stprintf_s(m_szSerialPort, _T("\\\\.\\com%d"), (int)byCommPort);
+	m_hSerialPort = CreateFile(m_szSerialPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);
+	if (m_hSerialPort == INVALID_HANDLE_VALUE)
+	{
+		// 打开串口失败;
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	BOOL bResult = FALSE;
+	// SetupComm设置缓冲区大小;
+	bResult = SetupComm(m_hSerialPort, m_dwInQueue, m_dwOutQueue);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 在串口读写之前,清除缓冲区;
+	bResult = PurgeComm(m_hSerialPort, PURGE_TXCLEAR | PURGE_RXCLEAR);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// GetCommState获取设备控制块状态;
+	memset(&m_dcb, 0, sizeof(DCB));
+	bResult = GetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	m_dcb.Parity = byParity;
+	if (m_dcb.Parity == NOPARITY)
+		m_dcb.fParity = FALSE;
+	else
+		m_dcb.fParity = TRUE;
+
+	m_dcb.BaudRate = dwBaudRate;
+	m_dcb.ByteSize = bySize;
+	m_dcb.StopBits = byStopBits;
+	if (m_dcb.ByteSize == 8)
+		m_dcb.StopBits = ONESTOPBIT;
+
+	// SetCommState设置设备的控制块状态;
+	memset(&m_cts, 0, sizeof(COMMTIMEOUTS));
+	bResult = SetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 获取设备的超时值;
+	bResult = GetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 两字符之间最大的延时,设置0表示参数不起作用;
+	m_cts.ReadIntervalTimeout = 50;
+	m_cts.ReadTotalTimeoutMultiplier = 100;
+	m_cts.ReadTotalTimeoutConstant = 8000;
+	m_cts.WriteTotalTimeoutMultiplier = 100;
+	m_cts.WriteTotalTimeoutConstant = 5000;
+
+	// 设置设备的超时值;
+	bResult = SetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CSynSerial::ReOpenSerialPort()
+{
+	ASSERT(_tcslen(m_szSerialPort));
+	CloseSerialPort();
+
+	m_hSerialPort = CreateFile(m_szSerialPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);
+	if (m_hSerialPort == INVALID_HANDLE_VALUE)
+	{
+		// 打开串口失败;
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	BOOL bResult = FALSE;
+	// SetupComm设置缓冲区大小;
+	bResult = SetupComm(m_hSerialPort, m_dwInQueue, m_dwOutQueue);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 在串口读写之前,清除缓冲区;
+	bResult = PurgeComm(m_hSerialPort, PURGE_TXCLEAR | PURGE_RXCLEAR);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// SetCommState设置设备的控制块状态;
+	memset(&m_cts, 0, sizeof(COMMTIMEOUTS));
+	bResult = SetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 设置设备的超时值;
+	bResult = SetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void CSynSerial::CloseSerialPort()
+{
+	if (m_hSerialPort != NULL && m_hSerialPort != INVALID_HANDLE_VALUE)
+		CloseHandle(m_hSerialPort);
+	m_hSerialPort = NULL;
+}
+
+BOOL CSynSerial::SetSerialPort(IN CONST DWORD & dwBaudRate, IN CONST BYTE & byByteSize, IN CONST BYTE & byParity, IN CONST BYTE & byStopBits, IN CONST BYTE & byStartAddr, IN CONST DWORD & dwInQueue, IN CONST DWORD & dwOutQueue)
+{
+	if (!IsOpen())
+		return FALSE;
+
+	BOOL bResult = FALSE;
+	// GetCommState获取设备控制块状态;
+	memset(&m_dcb, 0, sizeof(DCB));
+	bResult = GetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	m_dcb.Parity = byParity;
+	if (m_dcb.Parity == NOPARITY)
+		m_dcb.fParity = FALSE;
+	else
+		m_dcb.fParity = TRUE;
+
+	m_dcb.BaudRate = dwBaudRate;
+	m_dcb.ByteSize = byByteSize;
+	m_dcb.StopBits = byStopBits;
+	if (m_dcb.ByteSize == 8)
+		m_dcb.StopBits = ONESTOPBIT;
+
+	// SetCommState设置设备的控制块状态;
+	memset(&m_cts, 0, sizeof(COMMTIMEOUTS));
+	bResult = SetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 获取设备的超时值;
+	bResult = GetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 两字符之间最大的延时,设置0表示参数不起作用;
+	m_cts.ReadIntervalTimeout = 50;
+	m_cts.ReadTotalTimeoutMultiplier = 100;
+	m_cts.ReadTotalTimeoutConstant = 8000;
+	m_cts.WriteTotalTimeoutMultiplier = 100;
+	m_cts.WriteTotalTimeoutConstant = 3000;
+
+	// 设置设备的超时值;
+	bResult = SetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void CSynSerial::SetSerialDCB(IN CONST DCB & dcb)
+{
+}
+
+void CSynSerial::SetSerialCommTimeouts(IN CONST COMMTIMEOUTS & commTimeouts)
+{
+}
+
+DWORD CSynSerial::WriteComm(IN BYTE * pWirteBuf, IN CONST INT32 nWriteSize)
+{
+	if (!IsOpen())
+		return 0;
+
+	if (pWirteBuf == NULL || !::AfxIsValidAddress(pWirteBuf, nWriteSize, FALSE))
+	{
+		return 0;
+	}
+	
+	DWORD dwErrorFlags;
+	DWORD dwBytesWritten = 0;	// 实际写入的字节数;
+	// 写前, 清除错误;
+	COMSTAT ComStat;
+	ClearCommError(m_hSerialPort, &dwErrorFlags, &ComStat);	
+	// 写前,清空缓存区;
+	PurgeComm(m_hSerialPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
+	if ( !WriteFile(m_hSerialPort, pWirteBuf, nWriteSize, &dwBytesWritten, NULL) )
+	{
+		DWORD dwError = GetLastError();
+	}	
+
+	// 返回写入字节数;
+	return dwBytesWritten;
+}
+
+DWORD CSynSerial::ReadComm(IN BYTE * pReadBuf, IN CONST DWORD & dwReadSize)
+{
+	if (!IsOpen())
+		return 0;
+
+	if (pReadBuf == NULL || !::AfxIsValidAddress(pReadBuf, dwReadSize, FALSE))
+	{
+		return 0;
+	}
+
+	DWORD dwBytesRead = 0;
+	DWORD dwErrorFlags = 0;
+	// ReadFile前,使用ClearCommError清除错误;
+	COMSTAT ComStat = {0};
+	DWORD dwLastLen = 0;
+	ULONGLONG ulTick = GetTickCount64();	
+	// 直到有数据为止,超时3秒;
+	while (true)
+	{// cbInQue表示输入缓冲区的字节数; 
+		Sleep(150);
+		if (GetTickCount64() - ulTick > 3000)
+			break;
+		ClearCommError(m_hSerialPort, &dwErrorFlags, &ComStat);
+		// 防止读一次没读完,再读一次;二次长度一样表示读完;
+		if (ComStat.cbInQue != 0 && dwLastLen == ComStat.cbInQue)
+			break;
+		dwLastLen = ComStat.cbInQue;
+	}
+
+	if (ComStat.cbInQue == 0)
+	{
+		// 串口超时,无数据返回;
+		return 0;
+	}
+
+	dwBytesRead = ComStat.cbInQue;
+	if (dwReadSize < dwBytesRead)
+	{
+		dwBytesRead = dwReadSize;
+	}
+
+	if (ReadFile(m_hSerialPort, pReadBuf, dwBytesRead, &dwBytesRead, NULL))
+	{
+		return (INT)dwBytesRead;
+	}
+
+	return dwBytesRead;
+}

+ 83 - 0
SATHelper/SATHelper/SynSerial.h

@@ -0,0 +1,83 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [Home], 保留所有权利;
+/*  模 块 名:同步串口模块;
+/*  描    述:;
+/*
+/*  版    本:[V];
+/*  作    者:[Home];
+/*  日    期:[12/20/2017];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[Home];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+
+#ifndef __SYN_SERIAL__
+#define __SYN_SERIAL__
+
+#pragma once
+
+#define MAX_PORT_LEN 10
+
+class CSynSerial
+{
+public:
+	CSynSerial(void);
+	virtual ~CSynSerial(void);
+
+private:
+	DCB		m_dcb;
+	HANDLE	m_hSerialPort;
+	COMMTIMEOUTS	m_cts;
+
+	// 串口号(1~255);
+	TCHAR	m_szSerialPort[MAX_PORT_LEN];
+	// 输入缓存大小(byte);
+	DWORD	m_dwInQueue;
+	// 输出缓存大小(byte);
+	DWORD	m_dwOutQueue;
+
+public:
+	inline BOOL IsOpen() const {
+		return (m_hSerialPort == NULL || m_hSerialPort == INVALID_HANDLE_VALUE) ? FALSE : TRUE;
+	}
+
+	// 打开串口;
+	BOOL OpenSerialPort(
+		IN CONST BYTE& byCommPort,		// 串口号;
+		IN CONST DWORD& dwBaudRate,		// 波特率;
+		IN CONST BYTE& bySize,			// 数据位;
+		IN CONST BYTE& byParity,		// 校验位;
+		IN CONST BYTE& byStopBits,		// 停止位;
+		IN CONST BYTE& byStartAddr,		// 起始地址;
+		IN CONST INT& nInterval			// 间隔时间;
+	);
+	// 重装打开串口;
+	BOOL ReOpenSerialPort();
+	// 关闭串口;
+	void CloseSerialPort();
+	// 设置串口参数;
+	BOOL SetSerialPort(
+		IN CONST DWORD& dwBaudRate,		// 波特率;
+		IN CONST BYTE& byByteSize,		// 数据位;
+		IN CONST BYTE& byParity,		// 校验位;
+		IN CONST BYTE& byStopBits,		// 停止位;
+		IN CONST BYTE& byStartAddr,		// 起始地址;
+		IN CONST DWORD& dwInQueue,		// 输入缓存;
+		IN CONST DWORD& dwOutQueue		// 输出缓存;
+		);
+
+	// 设置dcb参数;
+	void SetSerialDCB(IN CONST DCB &dcb);
+	// 设置commtimeouts参数;
+	void SetSerialCommTimeouts(IN CONST COMMTIMEOUTS &commTimeouts);
+
+	virtual DWORD WriteComm(IN BYTE *pWirteBuf, IN CONST INT32 nWriteSize);
+	virtual DWORD ReadComm(IN BYTE *pReadBuf, IN CONST DWORD& nReadSize);
+};
+
+#endif // __SYN_SERIAL__

+ 471 - 0
SATHelper/SATHelper/TrayIcon.cpp

@@ -0,0 +1,471 @@
+#include "stdafx.h"
+#include "trayicon.h"
+#include <strsafe.h>
+#include <afxpriv.h>		// for AfxLoadString
+
+IMPLEMENT_DYNAMIC(ITrayIcon, CCmdTarget)
+typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); 
+
+ITrayIcon::ITrayIcon(UINT uID)
+{
+	// Initialize NOTIFYICONDATA
+	memset(&m_nid, 0 , sizeof(m_nid));
+	m_nid.cbSize = sizeof(m_nid);
+	m_nid.uID = uID;	// never changes after construction
+	OpendThread();
+	
+	bShowMinibox = FALSE;
+	//m_pWnd = AfxGetMainWnd();
+	// Use resource string as tip if there is one
+	AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
+}
+
+ITrayIcon::~ITrayIcon()
+{
+	SetIcon(0); // remove icon from system tray
+	TerminalThread();
+}
+
+//////////////////
+// Set notification window. It must created already.
+//
+void ITrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg)
+{
+	// If the following assert fails, you're probably
+	// calling me before you created your window. Oops.
+	ASSERT(pNotifyWnd==NULL || ::IsWindow(pNotifyWnd->GetSafeHwnd()));
+	m_nid.hWnd = pNotifyWnd->GetSafeHwnd();
+
+	ASSERT(uCbMsg==0 || uCbMsg>=WM_USER);
+	m_nid.uCallbackMessage = uCbMsg;
+}
+
+//////////////////
+// This is the main variant for setting the icon.
+// Sets both the icon and tooltip from resource ID
+// To remove the icon, call SetIcon(0)
+//
+BOOL ITrayIcon::SetIcon(UINT uID)
+{
+	HICON hicon=NULL;
+	if (uID) 
+		hicon = AfxGetApp()->LoadIcon(uID);
+
+	UINT msg;
+	m_nid.uFlags = 0;
+
+	// Set the icon
+	if (hicon) 
+	{
+		// Add or replace icon in system tray
+		msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD;
+		m_nid.hIcon = hicon;
+		m_nid.uFlags |= NIF_ICON;
+	}
+	else 
+	{ // remove icon from tray
+		if (m_nid.hIcon==NULL)
+			return TRUE;		// already deleted
+		msg = NIM_DELETE;
+	}
+
+	// Use callback if any
+	if (m_nid.uCallbackMessage && m_nid.hWnd)
+		m_nid.uFlags |= NIF_MESSAGE;
+
+	// Do it
+	BOOL bRet = Shell_NotifyIcon(msg, &m_nid);
+	if (msg==NIM_DELETE || !bRet)
+		m_nid.hIcon = NULL;	// failed
+	return bRet;
+}
+
+BOOL ITrayIcon::SetIcon(UINT uID, LPCTSTR lpTip)
+{ 
+	HICON hicon=NULL;
+	if (uID) 
+	{
+		AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
+		hicon = AfxGetApp()->LoadIcon(uID);
+	}
+
+	return SetIcon(hicon, lpTip ? lpTip:NULL); 
+}
+
+//////////////////
+// Common SetIcon for all overloads. 
+//
+BOOL ITrayIcon::SetIcon(HICON hicon, LPCTSTR lpTip)
+{
+	UINT msg;
+	m_nid.uFlags = 0;
+
+	// Set the icon
+	if (hicon) 
+	{
+		// Add or replace icon in system tray
+		msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD;
+		m_nid.hIcon = hicon;
+		m_nid.uFlags |= NIF_ICON;
+	} 
+	else 
+	{ // remove icon from tray
+		if (m_nid.hIcon==NULL)
+			return TRUE;		// already deleted
+		msg = NIM_DELETE;
+	}
+
+	// Use the tip, if any
+	if (lpTip)
+		//strncpy(m_nid.szTip, lpTip, sizeof(m_nid.szTip));
+		StringCchCopy(m_nid.szTip, sizeof(m_nid.szTip),lpTip);
+	if (m_nid.szTip[0])
+		m_nid.uFlags |= NIF_TIP;
+
+	// Use callback if any
+	if (m_nid.uCallbackMessage && m_nid.hWnd)
+		m_nid.uFlags |= NIF_MESSAGE;
+
+	// Do it
+	BOOL bRet = Shell_NotifyIcon(msg, &m_nid);
+	if (msg==NIM_DELETE || !bRet)
+		m_nid.hIcon = NULL;	// failed
+	return bRet;
+}
+
+BOOL ITrayIcon::SetTip(LPCTSTR lpTip) 
+{
+	UINT msg;
+	m_nid.uFlags = 0;
+
+	if (m_nid.hIcon == NULL)
+		return TRUE;		// already deleted
+
+	msg = NIM_MODIFY;
+
+	// Use the tip, if any
+	//if (lpTip)
+	//strncpy(m_nid.szTip, lpTip, sizeof(m_nid.szTip));
+	StringCchCopy(m_nid.szTip, sizeof(m_nid.szTip), lpTip);
+	if (m_nid.szTip[0])
+		m_nid.uFlags |= NIF_TIP;
+
+	// Use callback if any
+	if (m_nid.uCallbackMessage && m_nid.hWnd)
+		m_nid.uFlags |= NIF_MESSAGE;
+
+	return Shell_NotifyIcon(msg, &m_nid);
+}
+
+BOOL ITrayIcon::SetTipTitle(LPCTSTR lpTipTitle)
+{
+	UINT msg;
+	m_nid.uFlags = 0;
+
+	if (m_nid.hIcon == NULL)
+		return TRUE;		// already deleted
+
+	msg = NIM_MODIFY;
+	StringCchCopy(m_nid.szInfo, sizeof(m_nid.szInfo), _lpTip);
+	if ( lpTipTitle != NULL)
+	{
+		StringCchCopy(m_nid.szInfoTitle, sizeof(m_nid.szInfoTitle), lpTipTitle);
+		m_nid.uFlags |= NIF_INFO|NIIF_INFO;
+		m_nid.uTimeout = 1000;
+	}
+	else
+	{
+		m_nid.uFlags |= NIF_INFO;
+	}
+
+	// Use callback if any
+	if (m_nid.uCallbackMessage && m_nid.hWnd)
+		m_nid.uFlags |= NIF_MESSAGE;
+
+	return Shell_NotifyIcon(msg, &m_nid);
+}
+
+//BOOL ITrayIcon::SetInfo(LPCSTR lpInof, LPCSTR lpInfoTitle) 
+BOOL ITrayIcon::SetInfo(LPCTSTR lpInof, LPCTSTR lpInfoTitle)
+{
+	UINT msg;
+	m_nid.uFlags = 0;
+
+	if (m_nid.hIcon == NULL)
+		return TRUE;		// already deleted
+
+	msg = NIM_MODIFY;
+	//strncpy(m_nid.szInfo, lpInof, sizeof(m_nid.szInfo));
+	StringCchCopy(m_nid.szInfo, sizeof(m_nid.szInfo),lpInof);
+	if ( lpInfoTitle != NULL)
+	{
+		StringCchCopy(m_nid.szInfoTitle, sizeof(m_nid.szInfoTitle), lpInfoTitle);
+		m_nid.uFlags |= NIF_INFO|NIIF_INFO;
+		m_nid.uTimeout = 1000;
+	}
+	else
+	{
+		m_nid.uFlags |= NIF_INFO;
+	}
+
+	// Use callback if any
+	if (m_nid.uCallbackMessage && m_nid.hWnd)
+		m_nid.uFlags |= NIF_MESSAGE;
+
+	return Shell_NotifyIcon(msg, &m_nid);
+}
+
+/////////////////
+// Default event handler handles right-menu and doubleclick.
+// Call this function from your own notification handler.
+//
+LRESULT ITrayIcon::OnTrayNotification(WPARAM wID, LPARAM lEvent)
+{
+	if (wID!=m_nid.uID || (lEvent!=WM_RBUTTONUP && lEvent!=WM_LBUTTONDBLCLK))
+		return 0;
+
+	// If there's a resource menu with the same ID as the icon, use it as 
+	// the right-button popup menu. CTrayIcon will interprets the first
+	// item in the menu as the default command for WM_LBUTTONDBLCLK
+	// 
+#if 0
+	CMenu menu;
+	if (!menu.LoadMenu(m_nid.uID))
+		return 0;
+	CMenu* pSubMenu = menu.GetSubMenu(0);
+	if (!pSubMenu) 
+		return 0;
+
+	if (lEvent==WM_RBUTTONUP) 
+	{
+
+		// Make first menu item the default (bold font)
+		::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);
+
+		// Display the menu at the current mouse location. There's a "bug"
+		// (Microsoft calls it a feature) in Windows 95 that requires calling
+		// SetForegroundWindow. To find out more, search for Q135788 in MSDN.
+		//
+		CPoint mouse;
+		GetCursorPos(&mouse);
+		::SetForegroundWindow(m_nid.hWnd);	
+		::TrackPopupMenu(pSubMenu->m_hMenu, 0, mouse.x, mouse.y, 0,
+			m_nid.hWnd, NULL);
+
+	} else  // double click: execute first menu item
+		::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0);
+#endif
+	return 1; // handled
+}
+
+// ------------------------------- [5/30/2013 Z.t]
+BOOL ITrayIcon::IsWow64()  
+{  
+	BOOL bIsWow64 = FALSE;  
+
+	LPFN_ISWOW64PROCESS  fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(_T("kernel32")),"IsWow64Process");  
+
+	if (NULL != fnIsWow64Process)  
+	{  
+		if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))  
+		{  
+			// handle error   
+		}  
+	}  
+	return bIsWow64;  
+}
+
+/************************************************************************/
+/* 
+	函数:GetTrayRect,获取托盘图标区域;(有待完善,添加各种错误判断)
+	参数:None;
+	返回:返回指定托盘图标的区域;
+
+	注意:函数的功能有些太复杂,暂时没办法使用更简单的方法获取图标区域;
+*/
+/************************************************************************/
+void ITrayIcon::GetTrayRect(RECT &rc)
+{
+	HWND hWnd,hWndPaper;
+	long ret;
+	
+	LPVOID lngAddress;
+//	long lngTextAdr; //,lngHwndAdr,lngHwnd,lngButtonID;
+	TCHAR strBuff[1024]={0};
+
+	TBBUTTON btnData={0};
+
+	hWnd = FindWindow(_T("Shell_TrayWnd"), NULL);				// 获取任务栏句柄 [5/31/2013 Z.t]
+	hWnd = FindWindowEx(hWnd, 0, _T("TrayNotifyWnd"), NULL);	// 获取托盘区域句柄 [5/31/2013 Z.t]
+	hWndPaper = FindWindowEx(hWnd, 0, _T("SysPager"), NULL);	// 获取系统页句柄 [5/31/2013 Z.t]
+
+	if(!hWndPaper)
+		hWnd = FindWindowEx(hWnd, 0, _T("ToolbarWindow32"), NULL);
+	else
+		hWnd = FindWindowEx(hWndPaper, 0, _T("ToolbarWindow32"), NULL);
+
+	DWORD dwProcessId = 0; 
+	GetWindowThreadProcessId(hWnd, &dwProcessId);//LOG4C_NO_FILENUM((LOG_NOTICE,"进程ID%d",dwProcessId));进程其实就是explorer.exe
+	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS|PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE,0,dwProcessId);
+	if ( hProcess == NULL )
+	{
+		//LOG4C_NO_FILENUM((LOG_NOTICE,"hProcess == NULL--"));
+		return ;
+	}
+	
+	lngAddress = VirtualAllocEx(hProcess,0, 0x4096, MEM_COMMIT, PAGE_READWRITE); 
+	if( lngAddress == NULL) 
+	{
+		//LOG4C_NO_FILENUM((LOG_NOTICE,"lngAddress == NULL--"));
+		return;
+	}
+
+	DWORD lTextAdr = 0;
+	BYTE buff[1024] = {0};
+	CString strFilePath;
+	CString strTile;
+	HWND hMainWnd = NULL;
+	int nDataOffset = sizeof(TBBUTTON) - sizeof(INT_PTR) - sizeof(DWORD_PTR);
+	int nStrOffset = 18; 
+	if ( IsWow64() )
+	{
+		nDataOffset+=4;
+		nStrOffset+=6;
+	}
+
+	 // 获取托盘区域的所有图标数量 [5/31/2013 Z.t]      
+	int ibtnCount = SendMessage(hWnd, TB_BUTTONCOUNT, 0, 0);               
+	LPVOID lngRect = VirtualAllocEx(hProcess,0,sizeof(RECT), MEM_COMMIT, PAGE_READWRITE);
+	
+	CRect rect;
+	for(int i=0 ;i< ibtnCount;i++)
+	{
+		int j = i;
+		ret = SendMessage(hWnd,TB_GETBUTTON,j, (LPARAM)(lngAddress));
+		// 读文本地址;
+		ret = ReadProcessMemory(hProcess, LPVOID(long(lngAddress) + nDataOffset),&lTextAdr,4,0);
+		if(lTextAdr != -1)
+		{
+			// 读文本;
+			ret = ReadProcessMemory(hProcess, LPVOID(lTextAdr),buff,1024,0);
+
+			hMainWnd = (HWND)(*((DWORD*)buff));
+			strFilePath = (WCHAR *)buff + nStrOffset;			// 获取托盘图标进程路径 [5/31/2013 Z.t]
+			strTile = (WCHAR *)buff + nStrOffset + MAX_PATH;	// 获取托盘图标tip标题 [5/31/2013 Z.t]
+			//_tprintf(_T("%s %s\n"),strTile,strFilePath);
+
+			if (strTile.Compare(m_nid.szTip) == 0)
+			{
+				::SendMessage(hWnd,TB_GETITEMRECT,(WPARAM)j,(LPARAM)lngRect);
+				ReadProcessMemory(hProcess,lngRect,&rc, sizeof(rc),0);			// 获取托盘图标区域;
+				CWnd::FromHandle(hWnd)->ClientToScreen(&rc);
+			}
+		}
+	}
+	VirtualFreeEx( hProcess, lngAddress, 0x4096, MEM_DECOMMIT);
+	VirtualFreeEx( hProcess, lngAddress, 0, MEM_RELEASE);
+	VirtualFreeEx( hProcess, lngRect, sizeof(RECT), MEM_DECOMMIT);
+	VirtualFreeEx( hProcess, lngRect, 0, MEM_RELEASE);
+	CloseHandle(hProcess);
+}
+
+int ITrayIcon::OpendThread()
+{
+	//LOG4C_NO_FILENUM((LOG_NOTICE,"创建TrayIcon"));
+	m_hThreadCtrl = CreateEvent(NULL,TRUE,FALSE,NULL);	// 无信号事件;
+	if ( m_hThreadCtrl == NULL )
+	{
+		//LOG4C_NO_FILENUM((LOG_NOTICE,"创建TrayIcon事件失败"));
+		return -1;
+	}
+
+	m_hThreadObj = CreateThread(NULL,0,FlashingThread,this,CREATE_SUSPENDED,&m_dwThreadID);
+	if ( m_hThreadObj == NULL )
+	{
+		//LOG4C_NO_FILENUM((LOG_NOTICE,"创建TrayIcon线程失败"));
+		return -1;
+	}
+	bSuspending = true;
+	return 0;
+}
+
+void ITrayIcon::TerminalThread()
+{
+	if ( m_hThreadCtrl )
+	{
+		SetEvent( m_hThreadCtrl );
+	}
+
+	if (WaitForSingleObject(m_hThreadCtrl,INFINITE) != WAIT_TIMEOUT)
+	{
+		CloseHandle(m_hThreadObj);
+		m_hThreadObj = NULL;
+	}
+
+	CloseHandle( m_hThreadCtrl );
+	m_hThreadCtrl = NULL;
+
+	bSuspending = false;
+}
+
+BOOL ITrayIcon::StartFlashing()
+{
+	if ( !bSuspending ) return FALSE;
+	if(::ResumeThread(m_hThreadObj) == 0xFFFFFFFF)
+		return FALSE;
+	
+	bSuspending = false;
+	SetInfo(_T("环境监控系统产生新警报,请及时处理!"),_T("警报提示"));
+	return TRUE;
+}
+
+BOOL ITrayIcon::StopFlashing()
+{
+	if ( bSuspending ) return FALSE;
+
+	if(::SuspendThread(m_hThreadObj) == 0xFFFFFFFF)
+		return FALSE;
+	
+	bSuspending = true;
+	SetIcon(_utIcon[0]);
+	return TRUE;
+}
+
+/************************************************************************/
+/* 
+	线程函数:闪烁线程;
+*/
+/************************************************************************/
+DWORD WINAPI ITrayIcon::FlashingThread(LPVOID lpVoid)
+{
+	ITrayIcon *pTrayIcon = (ITrayIcon*)lpVoid;
+
+	CPoint pt; 
+	CRect IconRect;
+	bool bChange = FALSE;
+	do 
+	{
+		if ( bChange)
+		{
+			pTrayIcon->SetIcon(pTrayIcon->_utIcon[1]); 
+			bChange = FALSE;
+		}
+		else
+		{
+			pTrayIcon->SetIcon(pTrayIcon->_utIcon[2]);
+			bChange = TRUE;
+		}
+
+		if( pTrayIcon->bShowMinibox )
+		{
+			GetCursorPos(&pt); 
+			pTrayIcon->GetTrayRect(IconRect);
+			if ( IconRect.PtInRect( pt) ) 
+				::PostMessage(pTrayIcon->m_nid.hWnd,MYWM_SHOWMINBOX,1,0);
+			else
+				::PostMessage(pTrayIcon->m_nid.hWnd,MYWM_SHOWMINBOX,0,0);
+		}
+	}while (WaitForSingleObject(pTrayIcon->m_hThreadCtrl,300) == WAIT_TIMEOUT);
+
+	return 0;
+}

+ 84 - 0
SATHelper/SATHelper/TrayIcon.h

@@ -0,0 +1,84 @@
+#ifndef _ZEROT_TRAYICON_H
+#define _ZEROT_TRAYICON_H
+
+////////////////
+#define MYWM_SHOWMINBOX WM_USER + 2  // 自定义消息,显示迷你对话框;
+
+
+class ITrayIcon : public CCmdTarget 
+{
+protected:
+	DECLARE_DYNAMIC(ITrayIcon)
+	NOTIFYICONDATA m_nid;			// struct for Shell_NotifyIcon args
+
+public:
+	ITrayIcon(UINT uID);
+	~ITrayIcon();
+
+	// Call this to receive tray notifications
+	void SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg);
+
+	// SetIcon functions. To remove icon, call SetIcon(0)
+	BOOL SetIcon(UINT uID); // only set the icon,but without tip zt.
+	BOOL SetIcon(HICON hicon, LPCTSTR lpTip);
+	BOOL SetIcon(UINT uID, LPCTSTR lpTip);
+	BOOL SetIcon(LPCTSTR lpResName, LPCTSTR lpTip)
+	{ 
+		return SetIcon(lpResName ? 	AfxGetApp()->LoadIcon(lpResName) : NULL, lpTip); 
+	}
+
+	// 设置提示(设置提示时,是否也要同时设置图标???);
+	BOOL SetTip(LPCTSTR lpTip);
+	BOOL SetTipTitle(LPCTSTR lpTipTitle);
+	//BOOL SetInfo(LPCSTR lpInfo,LPCSTR lpInfoTitle = _T(""));	// NIF_INFO
+	BOOL SetInfo(LPCTSTR lpInfo, LPCTSTR lpInfoTitle = NULL);	// NIF_INFO
+
+	BOOL SetStandardIcon(LPCTSTR lpszIconName, LPCTSTR lpTip)
+	{ 
+		return SetIcon(::LoadIcon(NULL, lpszIconName), lpTip); 
+	}
+
+	virtual LRESULT OnTrayNotification(WPARAM uID, LPARAM lEvent);
+
+	// -------------------------------以下是托盘闪烁实现------------------------------- [5/30/2013 Z.t]
+public:
+	bool IsFlashing(){return bSuspending;}						// 外部调用,判断是否在闪烁;
+	void SetMiniShow(BOOL bSet){bShowMinibox = bSet;}
+
+	BOOL StartFlashing();
+	BOOL StopFlashing();
+	inline void SetIconInfos(const UINT &u1,const UINT &u2,const UINT &u3);
+	inline void SetDefaultTip(LPCTSTR lpTip){_lpTip=lpTip;}
+protected:
+	bool bSuspending;			// 线程运行标识;
+	
+	LPCTSTR _lpTip;				// 默认的提示;
+	UINT _utIcon[3];			// 1.停止闪烁时的默认图标;2.闪烁时的报警图标;3.闪烁时的盘里背景图标;
+
+	DWORD  m_dwThreadID;		// 线程ID; 
+	HANDLE m_hThreadCtrl;		// 线程控件事件;
+	HANDLE m_hThreadObj;		// 线程本身;
+
+	BOOL bShowMinibox;			// 闪烁期间,是否对窗口发送消息;
+	void GetTrayRect(RECT &rc);
+	BOOL IsWow64();
+
+	// 托盘闪烁线程;
+	int OpendThread();
+	void TerminalThread();
+	static DWORD WINAPI FlashingThread(LPVOID pVoid);
+
+};
+
+inline void ITrayIcon::SetIconInfos(const UINT &u1,const UINT &u2,const UINT &u3)
+{
+	_utIcon[0] = u1;
+	_utIcon[1] = u2;
+	_utIcon[2] = u3;
+}
+
+#endif
+
+// 未处理的BUG:
+// 1.在任务管理器中结束进程,托盘图标未能清除;
+// SetIcon(0)代表着清除托盘图标(NIF_DELETE);

+ 364 - 0
SATHelper/SATHelper/layout.xml

@@ -0,0 +1,364 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ButtonList desc="button info">
+	<!-- POS:位置;NAME:名称;SIZE:大小;COLOR:颜色;-->
+	<ITEM>
+		<NAME>POWER</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">FF6666</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>MUTE</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">FF1493</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>0</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>1</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>2</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>3</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>4</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>5</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>6</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>7</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>8</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>9</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>V+</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00BFFF</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>UP</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00FF7F</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>C+</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00BFFF</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>HOME</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">FF6666</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>LEFT</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00FF7F</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>OK</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">FFD700</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>RIGHT</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00FF7F</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>RETURN</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">FF6666</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>V-</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00BFFF</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>DOWN</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00FF7F</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>C-</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00BFFF</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>EXIT</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">FF6666</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>SETTING</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">EE82EE</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>MENU</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">EE82EE</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>TV</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>SOURCE</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">EE82EE</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>FAV</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>INFO</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>LIST</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>PRE-CH</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>EPG</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>GUID</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>SUBTITILE</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>TEXT</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>LANG</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>ZOOM+</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>ZOOM-</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>REC</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>RED</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">FF0000</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>GREEN</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">00DD00</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>BLUE</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">0066FF</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>YELLOW</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">EEEE00</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>PLAY</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>PAUSE</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>STOP</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>NEXT</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>SLEEP</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>FAST-BACK</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>FAST-FORWAR</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>PREVIOUS</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>APP</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>CH-LIST</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>OPTION</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>POWERL</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>SMART</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>SUBTUTLE</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>YOUTUBE</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+	<ITEM>
+		<NAME>ZOOM</NAME>
+		<WIDTH>58</WIDTH>
+		<HEIGHT>20</HEIGHT>
+		<COLOR name="ivory">87CEFA</COLOR>
+	</ITEM>
+</ButtonList>

+ 259 - 0
SATHelper/SATHelper/stdint.h

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

+ 2837 - 0
SATHelper/SATHelper/tinyxml2.cpp

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

+ 2310 - 0
SATHelper/SATHelper/tinyxml2.h

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

+ 3052 - 0
SATHelper/UI/BCMenu.cpp

@@ -0,0 +1,3052 @@
+#include "stdafx.h"       // Standard windows header file
+#include "BCMenu.h"        // BCMenu class declaration
+#include <afxpriv.h>       //SK: makes A2W and other spiffy AFX macros work
+#include <VersionHelpers.h>
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define BCMENU_GAP 1
+#ifndef OBM_CHECK
+#define OBM_CHECK 32760 // from winuser.h
+#endif
+
+#if _MFC_VER <0x400
+#error This code does not work on Versions of MFC prior to 4.0
+#endif
+
+static CPINFO CPInfo;
+// how the menu's are drawn in win9x/NT/2000
+UINT BCMenu::original_drawmode=BCMENU_DRAWMODE_ORIGINAL;
+BOOL BCMenu::original_select_disabled=TRUE;
+// how the menu's are drawn in winXP
+UINT BCMenu::xp_drawmode=BCMENU_DRAWMODE_XP;
+BOOL BCMenu::xp_select_disabled=FALSE;
+BOOL BCMenu::xp_draw_3D_bitmaps=TRUE;
+
+CImageList BCMenu::m_AllImages;
+CArray<int,int&> BCMenu::m_AllImagesID;
+
+enum Win32Type{
+	Win32s,
+	WinNT3,
+	Win95,
+	Win98,
+	WinME,
+	WinNT4,
+	Win2000,
+	WinXP
+};
+
+
+Win32Type IsShellType()
+{
+	Win32Type  ShellType;
+
+	if (IsWindowsXPOrGreater())
+	{
+		ShellType = WinXP;
+	}
+	else if (IsWindowsXPSP3OrGreater())
+	{
+		ShellType = WinXP;
+	}
+	else
+	{
+		ShellType = WinNT4;
+	}
+
+	return ShellType;
+}
+
+static Win32Type g_Shell=IsShellType();
+
+void BCMenuData::SetAnsiString(LPCSTR szAnsiString)
+{
+	USES_CONVERSION;
+	SetWideString(A2W(szAnsiString));  //SK:  see MFC Tech Note 059
+}
+
+CString BCMenuData::GetString(void)//returns the menu text in ANSI or UNICODE
+//depending on the MFC-Version we are using
+{
+	CString strText;
+	if (m_szMenuText)
+	{
+#ifdef UNICODE
+		strText = m_szMenuText;
+#else
+		USES_CONVERSION;
+		strText=W2A(m_szMenuText);     //SK:  see MFC Tech Note 059
+#endif    
+	}
+	return strText;
+}
+
+CTypedPtrArray<CPtrArray, HMENU> BCMenu::m_AllSubMenus;  // Stores list of all sub-menus
+
+IMPLEMENT_DYNAMIC( BCMenu, CMenu )
+
+/*
+===============================================================================
+BCMenu::BCMenu()
+BCMenu::~BCMenu()
+-----------------
+
+Constructor and Destructor.
+
+===============================================================================
+*/
+
+BCMenu::BCMenu()
+{
+	m_bDynIcons = FALSE;     // O.S. - no dynamic icons by default
+	disable_old_style=FALSE;
+	m_iconX = 16;            // Icon sizes default to 16 x 16
+	m_iconY = 15;            // ...
+	m_selectcheck = -1;
+	m_unselectcheck = -1;
+	checkmaps=NULL;
+	checkmapsshare=FALSE;
+	// set the color used for the transparent background in all bitmaps
+	m_bitmapBackground=RGB(192,192,192); //gray
+	m_bitmapBackgroundFlag=FALSE;
+	GetCPInfo(CP_ACP,&CPInfo);
+	m_loadmenu=FALSE;
+}
+
+
+BCMenu::~BCMenu()
+{
+	DestroyMenu();
+}
+
+BOOL BCMenu::IsNewShell ()
+{
+	return (g_Shell>=Win95);
+}
+
+BOOL BCMenu::IsWinXPLuna()
+{
+	if(g_Shell==WinXP){
+		if(IsWindowsClassicTheme())return(FALSE);
+		else return(TRUE);
+	}
+	return(FALSE);
+}
+
+BOOL BCMenu::IsLunaMenuStyle()
+{
+	if(IsWinXPLuna()){
+		if(xp_drawmode==BCMENU_DRAWMODE_XP)return(TRUE);
+	}
+	else{
+		if(original_drawmode==BCMENU_DRAWMODE_XP)return(TRUE);
+	}
+	return(FALSE);
+}
+
+BCMenuData::~BCMenuData()
+{
+	if(bitmap)
+		delete(bitmap);
+
+	delete[] m_szMenuText; //Need not check for NULL because ANSI X3J16 allows "delete NULL"
+}
+
+
+void BCMenuData::SetWideString(const wchar_t *szWideString)
+{
+	delete[] m_szMenuText;//Need not check for NULL because ANSI X3J16 allows "delete NULL"
+
+	if (szWideString)
+	{
+		m_szMenuText = new wchar_t[sizeof(wchar_t)*(wcslen(szWideString)+1)];
+		if (m_szMenuText)
+			wcscpy_s(m_szMenuText, sizeof(wchar_t) * (wcslen(szWideString) + 1), szWideString);
+	}
+	else
+		m_szMenuText=NULL;//set to NULL so we need not bother about dangling non-NULL Ptrs
+}
+
+BOOL BCMenu::IsMenu(CMenu *submenu)
+{
+	int m;
+	int numSubMenus = m_AllSubMenus.GetUpperBound();
+	for(m=0;m<=numSubMenus;++m){
+		if(submenu->m_hMenu==m_AllSubMenus[m])return(TRUE);
+	}
+	return(FALSE);
+}
+
+BOOL BCMenu::IsMenu(HMENU submenu)
+{
+	int m;
+	int numSubMenus = m_AllSubMenus.GetUpperBound();
+	for(m=0;m<=numSubMenus;++m){
+		if(submenu==m_AllSubMenus[m])return(TRUE);
+	}
+	return(FALSE);
+}
+
+BOOL BCMenu::DestroyMenu()
+{
+	// Destroy Sub menus:
+	int m,n;
+	int numAllSubMenus = m_AllSubMenus.GetUpperBound();
+	for(n = numAllSubMenus; n>= 0; n--){
+		if(m_AllSubMenus[n]==this->m_hMenu)m_AllSubMenus.RemoveAt(n);
+	}
+	int numSubMenus = m_SubMenus.GetUpperBound();
+	for(m = numSubMenus; m >= 0; m--){
+		numAllSubMenus = m_AllSubMenus.GetUpperBound();
+		for(n = numAllSubMenus; n>= 0; n--){
+			if(m_AllSubMenus[n]==m_SubMenus[m])m_AllSubMenus.RemoveAt(n);
+		}
+		CMenu *ptr=FromHandle(m_SubMenus[m]);
+		BOOL flag=ptr->IsKindOf(RUNTIME_CLASS( BCMenu ));
+		if(flag)delete((BCMenu *)ptr);
+	}
+	m_SubMenus.RemoveAll();
+	// Destroy menu data
+	int numItems = m_MenuList.GetUpperBound();
+	for(m = 0; m <= numItems; m++)delete(m_MenuList[m]);
+	m_MenuList.RemoveAll();
+	if(checkmaps&&!checkmapsshare){
+		delete checkmaps;
+		checkmaps=NULL;
+	}
+	// Call base-class implementation last:
+	return(CMenu::DestroyMenu());
+};
+
+int BCMenu::GetMenuDrawMode(void)
+{
+	if(IsWinXPLuna())return(xp_drawmode);
+	return(original_drawmode);
+}
+
+BOOL BCMenu::GetSelectDisableMode(void)
+{
+	if(IsLunaMenuStyle())return(xp_select_disabled);
+	return(original_select_disabled);
+}
+
+
+/*
+==========================================================================
+void BCMenu::DrawItem(LPDRAWITEMSTRUCT)
+---------------------------------------
+
+Called by the framework when a particular item needs to be drawn.  We
+overide this to draw the menu item in a custom-fashion, including icons
+and the 3D rectangle bar.
+==========================================================================
+*/
+
+void BCMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
+{
+	ASSERT(lpDIS != NULL);
+	CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+	if(pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)DrawItem_Win9xNT2000(lpDIS);
+	else{
+		if(IsWinXPLuna()){
+			if(xp_drawmode==BCMENU_DRAWMODE_XP) DrawItem_WinXP(lpDIS);
+			else DrawItem_Win9xNT2000(lpDIS);
+		}
+		else{
+			if(original_drawmode==BCMENU_DRAWMODE_XP) DrawItem_WinXP(lpDIS);
+			else DrawItem_Win9xNT2000(lpDIS);
+		}	
+	}
+}
+
+void BCMenu::DrawItem_Win9xNT2000 (LPDRAWITEMSTRUCT lpDIS)
+{
+	ASSERT(lpDIS != NULL);
+	CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+	CRect rect;
+	UINT state = (((BCMenuData*)(lpDIS->itemData))->nFlags);
+	CBrush m_brBackground;
+	COLORREF m_clrBack;
+
+	if(IsWinXPLuna())m_clrBack=GetSysColor(COLOR_3DFACE);
+	else m_clrBack=GetSysColor(COLOR_MENU);
+
+	m_brBackground.CreateSolidBrush(m_clrBack);
+
+	// remove the selected bit if it's grayed out
+	if(lpDIS->itemState & ODS_GRAYED&&!original_select_disabled){
+		if(lpDIS->itemState & ODS_SELECTED)lpDIS->itemState=lpDIS->itemState & ~ODS_SELECTED;
+	}
+
+	if(state & MF_SEPARATOR){
+		rect.CopyRect(&lpDIS->rcItem);
+		pDC->FillRect (rect,&m_brBackground);
+		rect.top += (rect.Height()>>1);
+		pDC->DrawEdge(&rect,EDGE_ETCHED,BF_TOP);
+	}
+	else{
+		CRect rect2;
+		BOOL standardflag=FALSE,selectedflag=FALSE,disableflag=FALSE;
+		BOOL checkflag=FALSE;
+		COLORREF crText = GetSysColor(COLOR_MENUTEXT);
+		CBrush m_brSelect;
+		CPen m_penBack;
+		int x0,y0,dy;
+		int nIconNormal=-1,xoffset=-1,global_offset=-1;
+		CImageList *bitmap=NULL;
+
+		// set some colors
+		m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
+		m_brSelect.CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
+
+		// draw the colored rectangle portion
+
+		rect.CopyRect(&lpDIS->rcItem);
+		rect2=rect;
+
+		// draw the up/down/focused/disabled state
+
+		UINT state = lpDIS->itemState;
+		CString strText;
+
+		if(lpDIS->itemData != NULL){
+			nIconNormal = (((BCMenuData*)(lpDIS->itemData))->menuIconNormal);
+			xoffset = (((BCMenuData*)(lpDIS->itemData))->xoffset);
+			global_offset = (((BCMenuData*)(lpDIS->itemData))->global_offset);
+			bitmap = (((BCMenuData*)(lpDIS->itemData))->bitmap);
+			strText = ((BCMenuData*) (lpDIS->itemData))->GetString();
+
+			if(nIconNormal<0&&global_offset>=0){
+				xoffset=global_offset;
+				nIconNormal=0;
+				bitmap = &m_AllImages;
+			}
+
+			if(state&ODS_CHECKED && nIconNormal<0){
+				if(state&ODS_SELECTED && m_selectcheck>0)checkflag=TRUE;
+				else if(m_unselectcheck>0) checkflag=TRUE;
+			}
+			else if(nIconNormal != -1){
+				standardflag=TRUE;
+				if(state&ODS_SELECTED && !(state&ODS_GRAYED))selectedflag=TRUE;
+				else if(state&ODS_GRAYED) disableflag=TRUE;
+			}
+		}
+		else{
+			strText.Empty();
+		}
+
+		if(state&ODS_SELECTED){ // draw the down edges
+
+			CPen *pOldPen = pDC->SelectObject (&m_penBack);
+
+			// You need only Text highlight and thats what you get
+
+			if(checkflag||standardflag||selectedflag||disableflag||state&ODS_CHECKED)
+				rect2.SetRect(rect.left+m_iconX+4+BCMENU_GAP,rect.top,rect.right,rect.bottom);
+			pDC->FillRect (rect2,&m_brSelect);
+
+			pDC->SelectObject (pOldPen);
+			crText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+		}
+		else {
+			CPen *pOldPen = pDC->SelectObject (&m_penBack);
+			pDC->FillRect (rect,&m_brBackground);
+			pDC->SelectObject (pOldPen);
+
+			// draw the up edges	
+			pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
+		}
+
+		// draw the text if there is any
+		//We have to paint the text only if the image is nonexistant
+
+		dy = (rect.Height()-4-m_iconY)/2;
+		dy = dy<0 ? 0 : dy;
+
+		if(checkflag||standardflag||selectedflag||disableflag){
+			rect2.SetRect(rect.left+1,rect.top+1+dy,rect.left+m_iconX+3,
+				rect.top+m_iconY+3+dy);
+			pDC->Draw3dRect (rect2,m_clrBack,m_clrBack);
+			if(checkflag && checkmaps){
+				pDC->FillRect (rect2,&m_brBackground);
+				rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
+					rect.top+m_iconY+4+dy);
+
+				pDC->Draw3dRect (rect2,m_clrBack,m_clrBack);
+				CPoint ptImage(rect.left+2,rect.top+2+dy);
+
+				if(state&ODS_SELECTED)checkmaps->Draw(pDC,1,ptImage,ILD_TRANSPARENT);
+				else checkmaps->Draw(pDC,0,ptImage,ILD_TRANSPARENT);
+			}
+			else if(disableflag){
+				if(!selectedflag){
+					CBitmap bitmapstandard;
+					GetBitmapFromImageList(pDC,bitmap,xoffset,bitmapstandard);
+					rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
+						rect.top+m_iconY+4+dy);
+					pDC->Draw3dRect (rect2,m_clrBack,m_clrBack);
+					if(disable_old_style)
+						DitherBlt(lpDIS->hDC,rect.left+2,rect.top+2+dy,m_iconX,m_iconY,
+						(HBITMAP)(bitmapstandard),0,0,m_clrBack);
+					else
+						DitherBlt2(pDC,rect.left+2,rect.top+2+dy,m_iconX,m_iconY,
+						bitmapstandard,0,0,m_clrBack);
+					bitmapstandard.DeleteObject();
+				}
+			}
+			else if(selectedflag){
+				pDC->FillRect (rect2,&m_brBackground);
+				rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
+					rect.top+m_iconY+4+dy);
+				if (IsNewShell()){
+					if(state&ODS_CHECKED)
+						pDC->Draw3dRect(rect2,GetSysColor(COLOR_3DSHADOW),
+						GetSysColor(COLOR_3DHILIGHT));
+					else
+						pDC->Draw3dRect(rect2,GetSysColor(COLOR_3DHILIGHT),
+						GetSysColor(COLOR_3DSHADOW));
+				}
+				CPoint ptImage(rect.left+2,rect.top+2+dy);
+				if(bitmap)bitmap->Draw(pDC,xoffset,ptImage,ILD_TRANSPARENT);
+			}
+			else{
+				if(state&ODS_CHECKED){
+					CBrush brush;
+					COLORREF col = m_clrBack;
+					col = LightenColor(col,0.6);
+					brush.CreateSolidBrush(col);
+					pDC->FillRect(rect2,&brush);
+					brush.DeleteObject();
+					rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
+						rect.top+m_iconY+4+dy);
+					if (IsNewShell())
+						pDC->Draw3dRect(rect2,GetSysColor(COLOR_3DSHADOW),
+						GetSysColor(COLOR_3DHILIGHT));
+				}
+				else{
+					pDC->FillRect (rect2,&m_brBackground);
+					rect2.SetRect(rect.left,rect.top+dy,rect.left+m_iconX+4,
+						rect.top+m_iconY+4+dy);
+					pDC->Draw3dRect (rect2,m_clrBack,m_clrBack);
+				}
+				CPoint ptImage(rect.left+2,rect.top+2+dy);
+				if(bitmap)bitmap->Draw(pDC,xoffset,ptImage,ILD_TRANSPARENT);
+			}
+		}
+		if(nIconNormal<0 && state&ODS_CHECKED && !checkflag){
+			rect2.SetRect(rect.left+1,rect.top+2+dy,rect.left+m_iconX+1,
+				rect.top+m_iconY+2+dy);
+			CMenuItemInfo info;
+			info.fMask = MIIM_CHECKMARKS;
+			::GetMenuItemInfo((HMENU)lpDIS->hwndItem,lpDIS->itemID,
+				MF_BYCOMMAND, &info);
+			if(state&ODS_CHECKED || info.hbmpUnchecked) {
+				Draw3DCheckmark(pDC, rect2, state&ODS_SELECTED,
+					state&ODS_CHECKED ? info.hbmpChecked :
+					info.hbmpUnchecked);
+			}
+		}
+
+		//This is needed always so that we can have the space for check marks
+
+		x0=rect.left;y0=rect.top;
+		rect.left = rect.left + m_iconX + 8 + BCMENU_GAP; 
+
+		if(!strText.IsEmpty()){
+
+			CRect rectt(rect.left,rect.top-1,rect.right,rect.bottom-1);
+
+			//   Find tabs
+
+			CString leftStr,rightStr;
+			leftStr.Empty();rightStr.Empty();
+			int tablocr=strText.ReverseFind(_T('\t'));
+			if(tablocr!=-1){
+				rightStr=strText.Mid(tablocr+1);
+				leftStr=strText.Left(strText.Find(_T('\t')));
+				rectt.right-=m_iconX;
+			}
+			else leftStr=strText;
+
+			int iOldMode = pDC->GetBkMode();
+			pDC->SetBkMode( TRANSPARENT);
+
+			// Draw the text in the correct colour:
+
+			UINT nFormat  = DT_LEFT|DT_SINGLELINE|DT_VCENTER;
+			UINT nFormatr = DT_RIGHT|DT_SINGLELINE|DT_VCENTER;
+			if(!(lpDIS->itemState & ODS_GRAYED)){
+				pDC->SetTextColor(crText);
+				pDC->DrawText (leftStr,rectt,nFormat);
+				if(tablocr!=-1) pDC->DrawText (rightStr,rectt,nFormatr);
+			}
+			else{
+
+				// Draw the disabled text
+				if(!(state & ODS_SELECTED)){
+					RECT offset = *rectt;
+					offset.left+=1;
+					offset.right+=1;
+					offset.top+=1;
+					offset.bottom+=1;
+					pDC->SetTextColor(GetSysColor(COLOR_BTNHILIGHT));
+					pDC->DrawText(leftStr,&offset, nFormat);
+					if(tablocr!=-1) pDC->DrawText (rightStr,&offset,nFormatr);
+					pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
+					pDC->DrawText(leftStr,rectt, nFormat);
+					if(tablocr!=-1) pDC->DrawText (rightStr,rectt,nFormatr);
+				}
+				else{
+					// And the standard Grey text:
+					pDC->SetTextColor(m_clrBack);
+					pDC->DrawText(leftStr,rectt, nFormat);
+					if(tablocr!=-1) pDC->DrawText (rightStr,rectt,nFormatr);
+				}
+			}
+			pDC->SetBkMode( iOldMode );
+		}
+
+		m_penBack.DeleteObject();
+		m_brSelect.DeleteObject();
+	}
+	m_brBackground.DeleteObject();
+}
+
+COLORREF BCMenu::LightenColor(COLORREF col,double factor)
+{
+	if(factor>0.0&&factor<=1.0){
+		BYTE red,green,blue,lightred,lightgreen,lightblue;
+		red = GetRValue(col);
+		green = GetGValue(col);
+		blue = GetBValue(col);
+		lightred = (BYTE)((factor*(255-red)) + red);
+		lightgreen = (BYTE)((factor*(255-green)) + green);
+		lightblue = (BYTE)((factor*(255-blue)) + blue);
+		col = RGB(lightred,lightgreen,lightblue);
+	}
+	return(col);
+}
+
+COLORREF BCMenu::DarkenColor(COLORREF col,double factor)
+{
+	if(factor>0.0&&factor<=1.0){
+		BYTE red,green,blue,lightred,lightgreen,lightblue;
+		red = GetRValue(col);
+		green = GetGValue(col);
+		blue = GetBValue(col);
+		lightred = (BYTE)(red-(factor*red));
+		lightgreen = (BYTE)(green-(factor*green));
+		lightblue = (BYTE)(blue-(factor*blue));
+		col = RGB(lightred,lightgreen,lightblue);
+	}
+	return(col);
+}
+
+
+void BCMenu::DrawItem_WinXP (LPDRAWITEMSTRUCT lpDIS)
+{
+	ASSERT(lpDIS != NULL);
+	CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+#ifdef BCMENU_USE_MEMDC
+	BCMenuMemDC *pMemDC=NULL;
+#endif
+	CRect rect,rect2;
+	UINT state = (((BCMenuData*)(lpDIS->itemData))->nFlags);
+	COLORREF m_newclrBack=GetSysColor(COLOR_3DFACE);
+	COLORREF m_clrBack=GetSysColor(COLOR_WINDOW);
+	CFont m_fontMenu,*pFont=NULL;
+	LOGFONT m_lf;
+	if(!IsWinXPLuna())m_newclrBack=LightenColor(m_newclrBack,0.25);
+	CBrush m_newbrBackground,m_brBackground;
+	m_brBackground.CreateSolidBrush(m_clrBack);
+	m_newbrBackground.CreateSolidBrush(m_newclrBack);
+	int BCMENU_PAD=4;
+	if(xp_draw_3D_bitmaps)BCMENU_PAD=7;
+	int barwidth=m_iconX+BCMENU_PAD;
+
+	// remove the selected bit if it's grayed out
+	if(lpDIS->itemState & ODS_GRAYED&&!xp_select_disabled){
+		if(lpDIS->itemState & ODS_SELECTED)lpDIS->itemState=lpDIS->itemState & ~ODS_SELECTED;
+#ifdef BCMENU_USE_MEMDC
+		pMemDC=new BCMenuMemDC(pDC,&lpDIS->rcItem);
+		pDC = pMemDC;
+		ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
+		NONCLIENTMETRICS nm;
+		nm.cbSize = sizeof (NONCLIENTMETRICS);
+		VERIFY (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,nm.cbSize,&nm,0)); 
+		m_lf =  nm.lfMenuFont;
+		m_fontMenu.CreateFontIndirect (&m_lf);
+		pFont = pDC->SelectObject (&m_fontMenu);
+#endif
+
+	}
+
+	if(state & MF_SEPARATOR){
+		rect.CopyRect(&lpDIS->rcItem);
+		pDC->FillRect (rect,&m_brBackground);
+		rect2.SetRect(rect.left,rect.top,rect.left+barwidth,rect.bottom);
+		rect.top+=rect.Height()>>1;
+		rect.left = rect2.right+BCMENU_PAD;
+		pDC->DrawEdge(&rect,EDGE_ETCHED,BF_TOP);
+		pDC->FillRect (rect2,&m_newbrBackground);
+		pDC->Draw3dRect (rect2,m_newclrBack,m_newclrBack);
+	}
+	else{
+		BOOL standardflag=FALSE,selectedflag=FALSE,disableflag=FALSE;
+		BOOL checkflag=FALSE;
+		COLORREF crText = GetSysColor(COLOR_MENUTEXT);
+		COLORREF crSelect = GetSysColor(COLOR_HIGHLIGHT);
+		COLORREF crSelectFill;
+		if(!IsWinXPLuna())crSelectFill=LightenColor(crSelect,0.85);
+		else crSelectFill=LightenColor(crSelect,0.7);
+		CBrush m_brSelect;
+		CPen m_penBack;
+		int x0,y0,dx,dy;
+		int nIconNormal=-1,xoffset=-1,global_offset=-1;
+		int faded_offset=1,shadow_offset=2;
+		CImageList *bitmap=NULL;
+		BOOL CanDraw3D=FALSE;
+
+		// set some colors
+		m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
+		m_brSelect.CreateSolidBrush(crSelectFill);
+
+		// draw the colored rectangle portion
+
+		rect.CopyRect(&lpDIS->rcItem);
+		rect2=rect;
+
+		// draw the up/down/focused/disabled state
+
+		UINT state = lpDIS->itemState;
+		CString strText;
+
+		if(lpDIS->itemData != NULL){
+			nIconNormal = (((BCMenuData*)(lpDIS->itemData))->menuIconNormal);
+			xoffset = (((BCMenuData*)(lpDIS->itemData))->xoffset);
+			bitmap = (((BCMenuData*)(lpDIS->itemData))->bitmap);
+			strText = ((BCMenuData*) (lpDIS->itemData))->GetString();
+			global_offset = (((BCMenuData*)(lpDIS->itemData))->global_offset);
+
+			if(xoffset==0&&xp_draw_3D_bitmaps&&bitmap&&bitmap->GetImageCount()>2)CanDraw3D=TRUE;
+
+			if(nIconNormal<0&&xoffset<0&&global_offset>=0){
+				xoffset=global_offset;
+				nIconNormal=0;
+				bitmap = &m_AllImages;
+				if(xp_draw_3D_bitmaps&&CanDraw3DImageList(global_offset)){
+					CanDraw3D=TRUE;
+					faded_offset=global_offset+1;
+					shadow_offset=global_offset+2;
+				}
+			}
+
+
+			if(state&ODS_CHECKED && nIconNormal<0){
+				if(state&ODS_SELECTED && m_selectcheck>0)checkflag=TRUE;
+				else if(m_unselectcheck>0) checkflag=TRUE;
+			}
+			else if(nIconNormal != -1){
+				standardflag=TRUE;
+				if(state&ODS_SELECTED && !(state&ODS_GRAYED))selectedflag=TRUE;
+				else if(state&ODS_GRAYED) disableflag=TRUE;
+			}
+		}
+		else{
+			strText.Empty();
+		}
+
+		if(state&ODS_SELECTED){ // draw the down edges
+
+			CPen *pOldPen = pDC->SelectObject (&m_penBack);
+
+			pDC->FillRect (rect,&m_brSelect);
+			pDC->Draw3dRect (rect,crSelect,crSelect);
+
+			pDC->SelectObject (pOldPen);
+		}
+		else {
+			rect2.SetRect(rect.left,rect.top,rect.left+barwidth,rect.bottom);
+			CPen *pOldPen = pDC->SelectObject (&m_penBack);
+			pDC->FillRect (rect,&m_brBackground);
+			pDC->FillRect (rect2,&m_newbrBackground);
+			pDC->SelectObject (pOldPen);
+
+			// draw the up edges
+
+			pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
+			pDC->Draw3dRect (rect2,m_newclrBack,m_newclrBack);
+		}
+
+		// draw the text if there is any
+		//We have to paint the text only if the image is nonexistant
+
+		dy = (int)(0.5+(rect.Height()-m_iconY)/2.0);
+		dy = dy<0 ? 0 : dy;
+		dx = (int)(0.5+(barwidth-m_iconX)/2.0);
+		dx = dx<0 ? 0 : dx;
+
+		if(checkflag||standardflag||selectedflag||disableflag){
+			rect2.SetRect(rect.left+1,rect.top+1,rect.left+barwidth-1,
+				rect.bottom-1);
+			if(checkflag && checkmaps){
+				pDC->FillRect (rect2,&m_newbrBackground);
+				CPoint ptImage(rect.left+dx,rect.top+dy);		
+				if(state&ODS_SELECTED)checkmaps->Draw(pDC,1,ptImage,ILD_TRANSPARENT);
+				else checkmaps->Draw(pDC,0,ptImage,ILD_TRANSPARENT);
+			}
+			else if(disableflag){
+				if(!selectedflag){
+					CBitmap bitmapstandard;
+					GetBitmapFromImageList(pDC,bitmap,xoffset,bitmapstandard);
+					COLORREF transparentcol=m_newclrBack;
+					if(state&ODS_SELECTED)transparentcol=crSelectFill;
+					if(disable_old_style)
+						DitherBlt(lpDIS->hDC,rect.left+dx,rect.top+dy,m_iconX,m_iconY,
+						(HBITMAP)(bitmapstandard),0,0,transparentcol);
+					else
+						DitherBlt2(pDC,rect.left+dx,rect.top+dy,m_iconX,m_iconY,
+						bitmapstandard,0,0,transparentcol);
+					if(state&ODS_SELECTED)pDC->Draw3dRect (rect,crSelect,crSelect);
+					bitmapstandard.DeleteObject();
+				}
+			}
+			else if(selectedflag){
+				pDC->FillRect (rect2,&m_brSelect);
+				CPoint ptImage(rect.left+dx,rect.top+dy);
+				if(state&ODS_CHECKED){
+					pDC->Draw3dRect(rect2,crSelect,crSelect);
+					ptImage.x-=1;ptImage.y-=1;
+				}
+				if(bitmap){
+					if(CanDraw3D&&!(state&ODS_CHECKED)){
+						CPoint ptImage1(ptImage.x+1,ptImage.y+1);
+						CPoint ptImage2(ptImage.x-1,ptImage.y-1);
+						bitmap->Draw(pDC,shadow_offset,ptImage1,ILD_TRANSPARENT);
+						bitmap->Draw(pDC,xoffset,ptImage2,ILD_TRANSPARENT);
+					}
+					else bitmap->Draw(pDC,xoffset,ptImage,ILD_TRANSPARENT);
+				}
+			}
+			else{
+				if(state&ODS_CHECKED){
+					CBrush brushin;
+					brushin.CreateSolidBrush(LightenColor(crSelect,0.85));
+					pDC->FillRect(rect2,&brushin);
+					brushin.DeleteObject();
+					pDC->Draw3dRect(rect2,crSelect,crSelect);
+					CPoint ptImage(rect.left+dx-1,rect.top+dy-1);
+					if(bitmap)bitmap->Draw(pDC,xoffset,ptImage,ILD_TRANSPARENT);
+				}
+				else{
+					pDC->FillRect (rect2,&m_newbrBackground);
+					pDC->Draw3dRect (rect2,m_newclrBack,m_newclrBack);
+					CPoint ptImage(rect.left+dx,rect.top+dy);
+					if(bitmap){
+						if(CanDraw3D)
+							bitmap->Draw(pDC,faded_offset,ptImage,ILD_TRANSPARENT);
+						else
+							bitmap->Draw(pDC,xoffset,ptImage,ILD_TRANSPARENT);
+					}
+				}
+			}
+		}
+		if(nIconNormal<0 && state&ODS_CHECKED && !checkflag){
+			dy = (int)(0.5+(rect.Height()-16)/2.0);
+			dy = dy<0 ? 0 : dy;
+			dx = (int)(0.5+(barwidth-16)/2.0);
+			dx = dx<0 ? 0 : dx;
+			CMenuItemInfo info;
+			info.fMask = MIIM_CHECKMARKS;
+			::GetMenuItemInfo((HMENU)lpDIS->hwndItem,lpDIS->itemID,
+				MF_BYCOMMAND, &info);
+			if(state&ODS_CHECKED || info.hbmpUnchecked) {
+				rect2.SetRect(rect.left+dx,rect.top+dy,rect.left+dx+16,rect.top+dy+16);
+				DrawXPCheckmark(pDC, rect2,state&ODS_CHECKED ? info.hbmpChecked :
+					info.hbmpUnchecked,crSelect);
+			}
+		}
+
+		//This is needed always so that we can have the space for check marks
+
+		x0=rect.left;y0=rect.top;
+		rect.left = rect.left + barwidth + 8; 
+
+		if(!strText.IsEmpty()){
+
+			CRect rectt(rect.left,rect.top-1,rect.right,rect.bottom-1);
+
+			//   Find tabs
+
+			CString leftStr,rightStr;
+			leftStr.Empty();rightStr.Empty();
+			int tablocr=strText.ReverseFind(_T('\t'));
+			if(tablocr!=-1){
+				rightStr=strText.Mid(tablocr+1);
+				leftStr=strText.Left(strText.Find(_T('\t')));
+				rectt.right-=m_iconX;
+			}
+			else leftStr=strText;
+
+			int iOldMode = pDC->GetBkMode();
+			pDC->SetBkMode( TRANSPARENT);
+
+			// Draw the text in the correct colour:
+
+			UINT nFormat  = DT_LEFT|DT_SINGLELINE|DT_VCENTER;
+			UINT nFormatr = DT_RIGHT|DT_SINGLELINE|DT_VCENTER;
+			if(!(lpDIS->itemState & ODS_GRAYED)){
+				pDC->SetTextColor(crText);
+				pDC->DrawText (leftStr,rectt,nFormat);
+				if(tablocr!=-1) pDC->DrawText (rightStr,rectt,nFormatr);
+			}
+			else{
+				RECT offset = *rectt;
+				offset.left+=1;
+				offset.right+=1;
+				offset.top+=1;
+				offset.bottom+=1;
+				if(!IsWinXPLuna()){
+					COLORREF graycol=GetSysColor(COLOR_GRAYTEXT);
+					if(!(state&ODS_SELECTED))graycol = LightenColor(graycol,0.4);
+					pDC->SetTextColor(graycol);
+				}
+				else pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
+				pDC->DrawText(leftStr,rectt, nFormat);
+				if(tablocr!=-1) pDC->DrawText (rightStr,rectt,nFormatr);
+			}
+			pDC->SetBkMode( iOldMode );
+		}
+
+		m_penBack.DeleteObject();
+		m_brSelect.DeleteObject();
+	}
+	m_brBackground.DeleteObject();
+	m_newbrBackground.DeleteObject();
+#ifdef BCMENU_USE_MEMDC
+	if(pFont)pDC->SelectObject (pFont); //set it to the old font
+	m_fontMenu.DeleteObject();
+	if(pMemDC)delete pMemDC;
+#endif
+}
+
+BOOL BCMenu::GetBitmapFromImageList(CDC* pDC,CImageList *imglist,int nIndex,CBitmap &bmp)
+{
+	HICON hIcon = imglist->ExtractIcon(nIndex);
+	CDC dc;
+	dc.CreateCompatibleDC(pDC);
+	bmp.CreateCompatibleBitmap(pDC,m_iconX,m_iconY);
+	CBitmap* pOldBmp = dc.SelectObject(&bmp);
+	CBrush brush ;
+	COLORREF m_newclrBack;
+	m_newclrBack=GetSysColor(COLOR_3DFACE);
+	brush.CreateSolidBrush(m_newclrBack);
+	::DrawIconEx(
+		dc.GetSafeHdc(),
+		0,
+		0,
+		hIcon,
+		m_iconX,
+		m_iconY,
+		0,
+		(HBRUSH)brush,
+		DI_NORMAL
+		);
+	dc.SelectObject( pOldBmp );
+	dc.DeleteDC();
+	// the icon is not longer needed
+	::DestroyIcon(hIcon);
+	return(TRUE);
+}
+
+/*
+==========================================================================
+void BCMenu::MeasureItem(LPMEASUREITEMSTRUCT)
+---------------------------------------------
+
+Called by the framework when it wants to know what the width and height
+of our item will be.  To accomplish this we provide the width of the
+icon plus the width of the menu text, and then the height of the icon.
+
+==========================================================================
+*/
+
+void BCMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
+{
+	UINT state = (((BCMenuData*)(lpMIS->itemData))->nFlags);
+	int BCMENU_PAD=4;
+	if(IsLunaMenuStyle()&&xp_draw_3D_bitmaps)BCMENU_PAD=7;
+	if(state & MF_SEPARATOR){
+		lpMIS->itemWidth = 0;
+		int temp = GetSystemMetrics(SM_CYMENU)>>1;
+		lpMIS->itemHeight = temp>(m_iconY+BCMENU_PAD)/2 ? temp : (m_iconY+BCMENU_PAD)/2;
+	}
+	else{
+		CFont m_fontMenu;
+		LOGFONT m_lf;
+		ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
+		NONCLIENTMETRICS nm;
+		nm.cbSize = sizeof (NONCLIENTMETRICS);
+		VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
+			nm.cbSize,&nm,0)); 
+		m_lf =  nm.lfMenuFont;
+		m_fontMenu.CreateFontIndirect (&m_lf);
+
+		// Obtain the width of the text:
+		CWnd *pWnd = AfxGetMainWnd();            // Get main window
+		if (pWnd == NULL) pWnd = CWnd::GetDesktopWindow();
+		CDC *pDC = pWnd->GetDC();              // Get device context
+		CFont* pFont=NULL;    // Select menu font in...
+
+		if (IsNewShell())
+			pFont = pDC->SelectObject (&m_fontMenu);// Select menu font in...
+
+		//Get pointer to text SK
+		const wchar_t *lpstrText = ((BCMenuData*)(lpMIS->itemData))->GetWideString();//SK: we use const to prevent misuse
+
+		SIZE size;
+		size.cx=size.cy=0;
+
+		if (Win32s!=g_Shell)
+			VERIFY(::GetTextExtentPoint32W(pDC->m_hDC,lpstrText,
+			wcslen(lpstrText),&size)); //SK should also work on 95
+#ifndef UNICODE //can't be UNICODE for Win32s
+		else{//it's Win32suckx
+			RECT rect;
+			rect.left=rect.top=0;
+			size.cy=DrawText(pDC->m_hDC,(LPCTSTR)lpstrText,
+				wcslen(lpstrText),&rect,
+				DT_SINGLELINE|DT_LEFT|DT_VCENTER|DT_CALCRECT);
+			//+3 makes at least three pixels space to the menu border
+			size.cx=rect.right-rect.left+3;
+			size.cx += 3*(size.cx/wcslen(lpstrText));
+		}
+#endif    
+
+		CSize t = CSize(size);
+		if(IsNewShell())
+			pDC->SelectObject (pFont);  // Select old font in
+		pWnd->ReleaseDC(pDC);  // Release the DC
+
+		// Set width and height:
+
+		if(IsLunaMenuStyle())lpMIS->itemWidth = m_iconX+BCMENU_PAD+8+t.cx;
+		else lpMIS->itemWidth = m_iconX + t.cx + m_iconX + BCMENU_GAP;
+		int temp = GetSystemMetrics(SM_CYMENU);
+		lpMIS->itemHeight = temp>m_iconY+BCMENU_PAD ? temp : m_iconY+BCMENU_PAD;
+		m_fontMenu.DeleteObject();
+	}
+}
+
+void BCMenu::SetIconSize (int width, int height)
+{
+	m_iconX = width;
+	m_iconY = height;
+}
+
+BOOL BCMenu::AppendODMenuA(LPCSTR lpstrText,UINT nFlags,UINT nID,
+						   int nIconNormal)
+{
+	USES_CONVERSION;
+	return AppendODMenuW(A2W(lpstrText),nFlags,nID,nIconNormal);//SK: See MFC Tech Note 059
+}
+
+
+BOOL BCMenu::AppendODMenuW(wchar_t *lpstrText,UINT nFlags,UINT nID,
+						   int nIconNormal)
+{
+	// Add the MF_OWNERDRAW flag if not specified:
+	if(!nID){
+		if(nFlags&MF_BYPOSITION)nFlags=MF_SEPARATOR|MF_OWNERDRAW|MF_BYPOSITION;
+		else nFlags=MF_SEPARATOR|MF_OWNERDRAW;
+	}
+	else if(!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
+
+	if(nFlags & MF_POPUP){
+		m_AllSubMenus.Add((HMENU)nID);
+		m_SubMenus.Add((HMENU)nID);
+	}
+
+	BCMenuData *mdata = new BCMenuData;
+	m_MenuList.Add(mdata);
+	mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
+
+	mdata->menuIconNormal = -1;
+	mdata->xoffset = -1;
+
+	if(nIconNormal>=0){
+		CImageList bitmap;
+		int xoffset=0;
+		LoadFromToolBar(nID,nIconNormal,xoffset);
+		if(mdata->bitmap){
+			mdata->bitmap->DeleteImageList();
+			mdata->bitmap=NULL;
+		}
+		bitmap.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+		if(AddBitmapToImageList(&bitmap,nIconNormal)){
+			mdata->global_offset = AddToGlobalImageList(&bitmap,xoffset,nID);
+		}
+	}
+	else mdata->global_offset = GlobalImageListOffset(nID);
+
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+	BOOL returnflag=CMenu::AppendMenu(nFlags, nID, (LPCTSTR)mdata);
+	if(m_loadmenu)RemoveTopLevelOwnerDraw();
+	return(returnflag);
+}
+
+BOOL BCMenu::AppendODMenuA(LPCSTR lpstrText,UINT nFlags,UINT nID,
+						   CImageList *il,int xoffset)
+{
+	USES_CONVERSION;
+	return AppendODMenuW(A2W(lpstrText),nFlags,nID,il,xoffset);
+}
+
+BOOL BCMenu::AppendODMenuW(wchar_t *lpstrText,UINT nFlags,UINT nID,
+						   CImageList *il,int xoffset)
+{
+	// Add the MF_OWNERDRAW flag if not specified:
+	if(!nID){
+		if(nFlags&MF_BYPOSITION)nFlags=MF_SEPARATOR|MF_OWNERDRAW|MF_BYPOSITION;
+		else nFlags=MF_SEPARATOR|MF_OWNERDRAW;
+	}
+	else if(!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
+
+	if(nFlags & MF_POPUP){
+		m_AllSubMenus.Add((HMENU)nID);
+		m_SubMenus.Add((HMENU)nID);
+	}
+
+	BCMenuData *mdata = new BCMenuData;
+	m_MenuList.Add(mdata);
+	mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
+
+	if(il){
+		mdata->menuIconNormal = 0;
+		mdata->xoffset=0;
+		if(mdata->bitmap)mdata->bitmap->DeleteImageList();
+		else mdata->bitmap=new(CImageList);
+		ImageListDuplicate(il,xoffset,mdata->bitmap);
+	}
+	else{
+		mdata->menuIconNormal = -1;
+		mdata->xoffset = -1;
+	}
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+	return(CMenu::AppendMenu(nFlags, nID, (LPCTSTR)mdata));
+}
+
+BOOL BCMenu::InsertODMenuA(UINT nPosition,LPCSTR lpstrText,UINT nFlags,UINT nID,
+						   int nIconNormal)
+{
+	USES_CONVERSION;
+	return InsertODMenuW(nPosition,A2W(lpstrText),nFlags,nID,nIconNormal);
+}
+
+
+BOOL BCMenu::InsertODMenuW(UINT nPosition,wchar_t *lpstrText,UINT nFlags,UINT nID,
+						   int nIconNormal)
+{
+	if(!(nFlags & MF_BYPOSITION)){
+		int iPosition =0;
+		BCMenu* pMenu = FindMenuOption(nPosition,iPosition);
+		if(pMenu){
+			return(pMenu->InsertODMenuW(iPosition,lpstrText,nFlags|MF_BYPOSITION,nID,nIconNormal));
+		}
+		else return(FALSE);
+	}
+
+	if(!nID)nFlags=MF_SEPARATOR|MF_OWNERDRAW|MF_BYPOSITION;
+	else if(!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
+
+	int menustart=0;
+
+	if(nFlags & MF_POPUP){
+		if(m_loadmenu){
+			menustart=GetMenuStart();
+			if(nPosition<(UINT)menustart)menustart=0;
+		}
+		m_AllSubMenus.Add((HMENU)nID);
+		m_SubMenus.InsertAt(nPosition,(HMENU)nID);
+	}
+
+	//Stephane Clog suggested adding this, believe it or not it's in the help 
+	if(nPosition==(UINT)-1)nPosition=GetMenuItemCount();
+
+	BCMenuData *mdata = new BCMenuData;
+	m_MenuList.InsertAt(nPosition-menustart,mdata);
+	mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
+
+	mdata->menuIconNormal = nIconNormal;
+	mdata->xoffset=-1;
+	if(nIconNormal>=0){
+		CImageList bitmap;
+		int xoffset=0;
+		LoadFromToolBar(nID,nIconNormal,xoffset);
+		if(mdata->bitmap){
+			mdata->bitmap->DeleteImageList();
+			mdata->bitmap=NULL;
+		}
+		bitmap.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+		if(AddBitmapToImageList(&bitmap,nIconNormal)){
+			mdata->global_offset = AddToGlobalImageList(&bitmap,xoffset,nID);
+		}
+	}
+	else mdata->global_offset = GlobalImageListOffset(nID);
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+	BOOL returnflag=CMenu::InsertMenu(nPosition,nFlags,nID,(LPCTSTR)mdata);
+	if(m_loadmenu)RemoveTopLevelOwnerDraw();
+	return(returnflag);
+}
+
+BOOL BCMenu::InsertODMenuA(UINT nPosition,LPCSTR lpstrText,UINT nFlags,UINT nID,
+						   CImageList *il,int xoffset)
+{
+	USES_CONVERSION;
+	return InsertODMenuW(nPosition,A2W(lpstrText),nFlags,nID,il,xoffset);
+}
+
+BOOL BCMenu::InsertODMenuW(UINT nPosition,wchar_t *lpstrText,UINT nFlags,UINT nID,
+						   CImageList *il,int xoffset)
+{
+	if(!(nFlags & MF_BYPOSITION)){
+		int iPosition =0;
+		BCMenu* pMenu = FindMenuOption(nPosition,iPosition);
+		if(pMenu){
+			return(pMenu->InsertODMenuW(iPosition,lpstrText,nFlags|MF_BYPOSITION,nID,il,xoffset));
+		}
+		else return(FALSE);
+	}
+
+	if(!nID)nFlags=MF_SEPARATOR|MF_OWNERDRAW|MF_BYPOSITION;
+	else if(!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
+
+	if(nFlags & MF_POPUP){
+		m_AllSubMenus.Add((HMENU)nID);
+		m_SubMenus.InsertAt(nPosition,(HMENU)nID);
+	}
+
+	//Stephane Clog suggested adding this, believe it or not it's in the help 
+	if(nPosition==(UINT)-1)nPosition=GetMenuItemCount();
+
+	BCMenuData *mdata = new BCMenuData;
+	m_MenuList.InsertAt(nPosition,mdata);
+	mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
+
+	mdata->menuIconNormal = -1;
+	mdata->xoffset = -1;
+
+	if(il){
+		if(mdata->bitmap){
+			mdata->bitmap->DeleteImageList();
+			mdata->bitmap=NULL;
+		}
+		mdata->global_offset = AddToGlobalImageList(il,xoffset,nID);
+	}
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+	return(CMenu::InsertMenu(nPosition,nFlags,nID,(LPCTSTR)mdata));
+}
+
+BOOL BCMenu::ModifyODMenuA(const char * lpstrText,UINT nID,int nIconNormal)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText),nID,nIconNormal);//SK: see MFC Tech Note 059
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText,UINT nID,int nIconNormal)
+{
+	int nLoc;
+	BCMenuData *mdata;
+	CArray<BCMenu*,BCMenu*>bcsubs;
+	CArray<int,int&>bclocs;
+
+	// Find the old BCMenuData structure:
+	BCMenu *psubmenu = FindMenuOption(nID,nLoc);
+	do{
+		if(psubmenu && nLoc>=0)mdata = psubmenu->m_MenuList[nLoc];
+		else{
+			// Create a new BCMenuData structure:
+			mdata = new BCMenuData;
+			m_MenuList.Add(mdata);
+		}
+
+		ASSERT(mdata);
+		if(lpstrText)
+			mdata->SetWideString(lpstrText);  //SK: modified for dynamic allocation
+		mdata->menuIconNormal = -1;
+		mdata->xoffset = -1;
+		if(nIconNormal>=0){
+			CImageList bitmap;
+			int xoffset=0;
+			LoadFromToolBar(nID,nIconNormal,xoffset);
+			if(mdata->bitmap){
+				mdata->bitmap->DeleteImageList();
+				mdata->bitmap=NULL;
+			}
+			bitmap.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+			if(AddBitmapToImageList(&bitmap,nIconNormal)){
+				mdata->global_offset = AddToGlobalImageList(&bitmap,xoffset,nID);
+			}
+		}
+		else mdata->global_offset = GlobalImageListOffset(nID);
+		mdata->nFlags &= ~(MF_BYPOSITION);
+		mdata->nFlags |= MF_OWNERDRAW;
+		mdata->nID = nID;
+		bcsubs.Add(psubmenu);
+		bclocs.Add(nLoc);
+		if(psubmenu && nLoc>=0)psubmenu = FindAnotherMenuOption(nID,nLoc,bcsubs,bclocs);
+		else psubmenu=NULL;
+	}while(psubmenu);
+	return (CMenu::ModifyMenu(nID,mdata->nFlags,nID,(LPCTSTR)mdata));
+}
+
+BOOL BCMenu::ModifyODMenuA(const char * lpstrText,UINT nID,CImageList *il,int xoffset)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText),nID,il,xoffset);
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText,UINT nID,CImageList *il,int xoffset)
+{
+	int nLoc;
+	BCMenuData *mdata;
+	CArray<BCMenu*,BCMenu*>bcsubs;
+	CArray<int,int&>bclocs;
+
+	// Find the old BCMenuData structure:
+	BCMenu *psubmenu = FindMenuOption(nID,nLoc);
+	do{
+		if(psubmenu && nLoc>=0)mdata = psubmenu->m_MenuList[nLoc];
+		else{
+			// Create a new BCMenuData structure:
+			mdata = new BCMenuData;
+			m_MenuList.Add(mdata);
+		}
+
+		ASSERT(mdata);
+		if(lpstrText)
+			mdata->SetWideString(lpstrText);  //SK: modified for dynamic allocation
+		mdata->menuIconNormal = -1;
+		mdata->xoffset = -1;
+		if(il){
+			if(mdata->bitmap){
+				mdata->bitmap->DeleteImageList();
+				mdata->bitmap=NULL;
+			}
+			mdata->global_offset = AddToGlobalImageList(il,xoffset,nID);
+		}
+		mdata->nFlags &= ~(MF_BYPOSITION);
+		mdata->nFlags |= MF_OWNERDRAW;
+		mdata->nID = nID;
+		bcsubs.Add(psubmenu);
+		bclocs.Add(nLoc);
+		if(psubmenu && nLoc>=0)psubmenu = FindAnotherMenuOption(nID,nLoc,bcsubs,bclocs);
+		else psubmenu=NULL;
+	}while(psubmenu);
+	return (CMenu::ModifyMenu(nID,mdata->nFlags,nID,(LPCTSTR)mdata));
+}
+
+BOOL BCMenu::ModifyODMenuA(const char * lpstrText,UINT nID,CBitmap *bmp)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText),nID,bmp);
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText,UINT nID,CBitmap *bmp)
+{
+	if(bmp){
+		CImageList temp;
+		temp.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+		if(m_bitmapBackgroundFlag)temp.Add(bmp,m_bitmapBackground);
+		else temp.Add(bmp,GetSysColor(COLOR_3DFACE));
+		return ModifyODMenuW(lpstrText,nID,&temp,0);
+	}
+	return ModifyODMenuW(lpstrText,nID,NULL,0);
+}
+
+// courtesy of Warren Stevens
+BOOL BCMenu::ModifyODMenuA(const char * lpstrText,UINT nID,COLORREF fill,COLORREF border,int hatchstyle,CSize *pSize)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText),nID,fill,border,hatchstyle,pSize);
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText,UINT nID,COLORREF fill,COLORREF border,int hatchstyle,CSize *pSize)
+{
+	CWnd *pWnd = AfxGetMainWnd();            // Get main window
+	CDC *pDC = pWnd->GetDC();              // Get device context
+	SIZE sz;
+	if(!pSize){
+		sz.cx = m_iconX;
+		sz.cy = m_iconY;
+	}
+	else{
+		sz.cx = pSize->cx;
+		sz.cy = pSize->cy;
+	}
+	CSize bitmap_size(sz);
+	CSize icon_size(m_iconX,m_iconY);
+	CBitmap bmp;
+	ColorBitmap(pDC,bmp,bitmap_size,icon_size,fill,border,hatchstyle);		
+	pWnd->ReleaseDC(pDC);
+	return ModifyODMenuW(lpstrText,nID,&bmp);
+}
+
+
+BOOL BCMenu::ModifyODMenuA(const char *lpstrText,const char *OptionText,int nIconNormal)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText),A2W(OptionText),nIconNormal);//SK: see MFC  Tech Note 059
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText,wchar_t *OptionText,int nIconNormal)
+{
+	BCMenuData *mdata;
+
+	// Find the old BCMenuData structure:
+	CStringW junk=OptionText;
+	mdata=FindMenuOption(OptionText);
+	if(mdata){
+		if(lpstrText)
+			mdata->SetWideString(lpstrText);//SK: modified for dynamic allocation
+		mdata->menuIconNormal = nIconNormal;
+		mdata->xoffset=-1;
+		if(nIconNormal>=0){
+			mdata->xoffset=0;
+			if(mdata->bitmap)mdata->bitmap->DeleteImageList();
+			else mdata->bitmap=new(CImageList);
+			mdata->bitmap->Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+			if(!AddBitmapToImageList(mdata->bitmap,nIconNormal)){
+				mdata->bitmap->DeleteImageList();
+				delete mdata->bitmap;
+				mdata->bitmap=NULL;
+				mdata->menuIconNormal = nIconNormal = -1;
+				mdata->xoffset = -1;
+			}
+		}
+		return(TRUE);
+	}
+	return(FALSE);
+}
+
+BCMenuData *BCMenu::NewODMenu(UINT pos,UINT nFlags,UINT nID,CString string)
+{
+	BCMenuData *mdata;
+
+	mdata = new BCMenuData;
+	mdata->menuIconNormal = -1;
+	mdata->xoffset=-1;
+#ifdef UNICODE
+	mdata->SetWideString((LPCTSTR)string);//SK: modified for dynamic allocation
+#else
+	mdata->SetAnsiString(string);
+#endif
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+
+	//	if(nFlags & MF_POPUP)m_AllSubMenus.Add((HMENU)nID);
+
+	if (nFlags&MF_OWNERDRAW){
+		ASSERT(!(nFlags&MF_STRING));
+		ModifyMenu(pos,nFlags,nID,(LPCTSTR)mdata);
+	}
+	else if (nFlags&MF_STRING){
+		ASSERT(!(nFlags&MF_OWNERDRAW));
+		ModifyMenu(pos,nFlags,nID,mdata->GetString());
+	}
+	else{
+		ASSERT(nFlags&MF_SEPARATOR);
+		ModifyMenu(pos,nFlags,nID);
+	}
+
+	return(mdata);
+};
+
+BOOL BCMenu::LoadToolbars(const UINT *arID,int n)
+{
+	ASSERT(arID);
+	BOOL returnflag=TRUE;
+	for(int i=0;i<n;++i){
+		if(!LoadToolbar(arID[i]))returnflag=FALSE;
+	}
+	return(returnflag);
+}
+
+BOOL BCMenu::LoadToolbar(UINT nToolBar)
+{
+	UINT nID,nStyle;
+	BOOL returnflag=FALSE;
+	BCMenuToolBar bar;
+	int xoffset=-1,xset;
+	int iconx,icony;
+
+	CWnd* pWnd = AfxGetMainWnd();
+	if (pWnd == NULL)pWnd = CWnd::GetDesktopWindow();
+	bar.Create(pWnd);
+	if(bar.LoadToolBar(nToolBar)){
+		bar.GetIconSize(iconx,icony);
+		if(iconx>m_iconX)m_iconX=iconx;
+		if(icony>m_iconY)m_iconY=icony;
+		CImageList imglist;
+		imglist.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+		if(AddBitmapToImageList(&imglist,nToolBar)){
+			returnflag=TRUE;
+			for(int i=0;i<bar.GetCount();++i){
+				nID = bar.GetItemID(i); 
+				if(nID && GetMenuState(nID, MF_BYCOMMAND)
+					!=0xFFFFFFFF){
+						xoffset=bar.CommandToIndex(nID);
+						if(xoffset>=0){
+							bar.GetButtonInfo(xoffset,nID,nStyle,xset);
+							if(xset>0)xoffset=xset;
+						}
+						ModifyODMenu(NULL,nID,&imglist,xoffset);
+				}
+			}
+		}
+	}
+	return(returnflag);
+}
+
+BOOL BCMenu::LoadFromToolBar(UINT nID,UINT nToolBar,int& xoffset)
+{
+	int xset,offset;
+	UINT nStyle;
+	BOOL returnflag=FALSE;
+	CToolBar bar;
+
+	CWnd* pWnd = AfxGetMainWnd();
+	if (pWnd == NULL)pWnd = CWnd::GetDesktopWindow();
+	bar.Create(pWnd);
+	if(bar.LoadToolBar(nToolBar)){
+		offset=bar.CommandToIndex(nID);
+		if(offset>=0){
+			bar.GetButtonInfo(offset,nID,nStyle,xset);
+			if(xset>0)xoffset=xset;
+			returnflag=TRUE;
+		}
+	}
+	return(returnflag);
+}
+
+// O.S.
+BCMenuData *BCMenu::FindMenuItem(UINT nID)
+{
+	BCMenuData *pData = NULL;
+	int i;
+
+	for(i = 0; i <= m_MenuList.GetUpperBound(); i++){
+		if (m_MenuList[i]->nID == nID){
+			pData = m_MenuList[i];
+			break;
+		}
+	}
+	if (!pData){
+		int loc;
+		BCMenu *pMenu = FindMenuOption(nID, loc);
+		ASSERT(pMenu != this);
+		if (loc >= 0){
+			return pMenu->FindMenuItem(nID);
+		}
+	}
+	return pData;
+}
+
+
+BCMenu *BCMenu::FindAnotherMenuOption(int nId,int& nLoc,CArray<BCMenu*,BCMenu*>&bcsubs,
+									  CArray<int,int&>&bclocs)
+{
+	int i,numsubs,j;
+	BCMenu *psubmenu,*pgoodmenu;
+	BOOL foundflag;
+
+	for(i=0;i<(int)(GetMenuItemCount());++i){
+#ifdef _CPPRTTI 
+		psubmenu=dynamic_cast<BCMenu *>(GetSubMenu(i));
+#else
+		psubmenu=(BCMenu *)GetSubMenu(i);
+#endif
+		if(psubmenu){
+			pgoodmenu=psubmenu->FindAnotherMenuOption(nId,nLoc,bcsubs,bclocs);
+			if(pgoodmenu)return(pgoodmenu);
+		}
+		else if(nId==(int)GetMenuItemID(i)){
+			numsubs=bcsubs.GetSize();
+			foundflag=TRUE;
+			for(j=0;j<numsubs;++j){
+				if(bcsubs[j]==this&&bclocs[j]==i){
+					foundflag=FALSE;
+					break;
+				}
+			}
+			if(foundflag){
+				nLoc=i;
+				return(this);
+			}
+		}
+	}
+	nLoc = -1;
+	return(NULL);
+}
+
+BCMenu *BCMenu::FindMenuOption(int nId,int& nLoc)
+{
+	int i;
+	BCMenu *psubmenu,*pgoodmenu;
+
+	for(i=0;i<(int)(GetMenuItemCount());++i){
+#ifdef _CPPRTTI 
+		psubmenu=dynamic_cast<BCMenu *>(GetSubMenu(i));
+#else
+		psubmenu=(BCMenu *)GetSubMenu(i);
+#endif
+		if(psubmenu){
+			pgoodmenu=psubmenu->FindMenuOption(nId,nLoc);
+			if(pgoodmenu)return(pgoodmenu);
+		}
+		else if(nId==(int)GetMenuItemID(i)){
+			nLoc=i;
+			return(this);
+		}
+	}
+	nLoc = -1;
+	return(NULL);
+}
+
+BCMenuData *BCMenu::FindMenuOption(wchar_t *lpstrText)
+{
+	int i,j;
+	BCMenu *psubmenu;
+	BCMenuData *pmenulist;
+
+	for(i=0;i<(int)(GetMenuItemCount());++i){
+#ifdef _CPPRTTI 
+		psubmenu=dynamic_cast<BCMenu *>(GetSubMenu(i));
+#else
+		psubmenu=(BCMenu *)GetSubMenu(i);
+#endif
+		if(psubmenu){
+			pmenulist=psubmenu->FindMenuOption(lpstrText);
+			if(pmenulist)return(pmenulist);
+		}
+		else{
+			const wchar_t *szWide;//SK: we use const to prevent misuse of this Ptr
+			for(j=0;j<=m_MenuList.GetUpperBound();++j){     
+				szWide = m_MenuList[j]->GetWideString ();
+				if(szWide && !wcscmp(lpstrText,szWide))//SK: modified for dynamic allocation
+					return(m_MenuList[j]);
+			}
+		}
+	}
+	return(NULL);
+}
+
+
+BOOL BCMenu::LoadMenu(int nResource)
+{
+	return(BCMenu::LoadMenu(MAKEINTRESOURCE(nResource)));
+};
+
+BOOL BCMenu::LoadMenu(LPCTSTR lpszResourceName)
+{
+	ASSERT_VALID(this);
+	ASSERT(lpszResourceName != NULL);
+
+	// Find the Menu Resource:
+	HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName,RT_MENU);
+	HRSRC hRsrc = ::FindResource(hInst,lpszResourceName,RT_MENU);
+	if (hRsrc == NULL){
+		hInst = NULL;
+		hRsrc = ::FindResource(hInst,lpszResourceName,RT_MENU);
+	}
+	if(hRsrc == NULL)return FALSE;
+
+	// Load the Menu Resource:
+
+	HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
+	if(hGlobal == NULL)return FALSE;
+
+	// first destroy the menu if we're trying to loadmenu again
+	DestroyMenu();
+
+	// Attempt to create us as a menu...
+	if(!CMenu::CreateMenu())return FALSE;
+
+	// Get Item template Header, and calculate offset of MENUITEMTEMPLATES
+
+	MENUITEMTEMPLATEHEADER *pTpHdr=
+		(MENUITEMTEMPLATEHEADER*)LockResource(hGlobal);
+	BYTE* pTp=(BYTE*)pTpHdr + 
+		(sizeof(MENUITEMTEMPLATEHEADER) + pTpHdr->offset);
+
+
+	// Variables needed during processing of Menu Item Templates:
+
+	int j=0;
+	WORD    dwFlags = 0;              // Flags of the Menu Item
+	WORD    dwID  = 0;              // ID of the Menu Item
+	UINT    uFlags;                  // Actual Flags.
+	wchar_t *szCaption=NULL;
+	int      nLen   = 0;                // Length of caption
+	CTypedPtrArray<CPtrArray, BCMenu*>  m_Stack;    // Popup menu stack
+	CArray<BOOL,BOOL>  m_StackEnd;    // Popup menu stack
+	m_Stack.Add(this);                  // Add it to this...
+	m_StackEnd.Add(FALSE);
+
+	do{
+		// Obtain Flags and (if necessary), the ID...
+		memcpy(&dwFlags, pTp, sizeof(WORD));pTp+=sizeof(WORD);// Obtain Flags
+		if(!(dwFlags & MF_POPUP)){
+			memcpy(&dwID, pTp, sizeof(WORD)); // Obtain ID
+			pTp+=sizeof(WORD);
+		}
+		else dwID = 0;
+
+		uFlags = (UINT)dwFlags; // Remove MF_END from the flags that will
+		if(uFlags & MF_END) // be passed to the Append(OD)Menu functions.
+			uFlags -= MF_END;
+
+		// Obtain Caption (and length)
+
+		nLen = 0;
+		szCaption=new wchar_t[wcslen((wchar_t *)pTp)+1];
+		wcscpy_s(szCaption, wcslen((wchar_t*)pTp) + 1, (wchar_t *)pTp);
+		pTp=&pTp[(wcslen((wchar_t *)pTp)+1)*sizeof(wchar_t)];//modified SK
+
+		// Handle popup menus first....
+
+		//WideCharToMultiByte
+		if(dwFlags & MF_POPUP){
+			if(dwFlags & MF_END)m_StackEnd.SetAt(m_Stack.GetUpperBound(),TRUE);
+			BCMenu* pSubMenu = new BCMenu;
+			pSubMenu->m_unselectcheck=m_unselectcheck;
+			pSubMenu->m_selectcheck=m_selectcheck;
+			pSubMenu->checkmaps=checkmaps;
+			pSubMenu->checkmapsshare=TRUE;
+			pSubMenu->CreatePopupMenu();
+
+			// Append it to the top of the stack:
+
+			m_Stack[m_Stack.GetUpperBound()]->AppendODMenuW(szCaption,uFlags,
+				(UINT)pSubMenu->m_hMenu, -1);
+			m_Stack.Add(pSubMenu);
+			m_StackEnd.Add(FALSE);
+		}
+		else {
+			m_Stack[m_Stack.GetUpperBound()]->AppendODMenuW(szCaption, uFlags,
+				dwID, -1);
+			if(dwFlags & MF_END)m_StackEnd.SetAt(m_Stack.GetUpperBound(),TRUE);
+			j = m_Stack.GetUpperBound();
+			while(j>=0 && m_StackEnd.GetAt(j)){
+				m_Stack[m_Stack.GetUpperBound()]->InsertSpaces();
+				m_Stack.RemoveAt(j);
+				m_StackEnd.RemoveAt(j);
+				--j;
+			}
+		}
+
+		delete[] szCaption;
+	}while(m_Stack.GetUpperBound() != -1);
+
+	for(int i=0;i<(int)GetMenuItemCount();++i){
+		CString str=m_MenuList[i]->GetString();
+		if(GetSubMenu(i)){
+			m_MenuList[i]->nFlags=MF_POPUP|MF_BYPOSITION;
+			ModifyMenu(i,MF_POPUP|MF_BYPOSITION,
+				(UINT)GetSubMenu(i)->m_hMenu,str);
+		}
+		else{
+			m_MenuList[i]->nFlags=MF_STRING|MF_BYPOSITION;
+			ModifyMenu(i,MF_STRING|MF_BYPOSITION,m_MenuList[i]->nID,str);
+		}
+	}
+
+	m_loadmenu=TRUE;
+
+	return(TRUE);
+}
+
+int BCMenu::GetMenuStart(void)
+{
+	if(!m_loadmenu)return(0);
+
+	CString name,str;
+	int menuloc=-1,listloc=-1,menustart=0,i=0,j=0;
+	int nummenulist=m_MenuList.GetSize();
+	int nummenu=(int)GetMenuItemCount();
+
+	while(i<nummenu&&menuloc==-1){
+		GetMenuString (i, name, MF_BYPOSITION);
+		if(name.GetLength()>0){
+			for(j=0;j<nummenulist;++j){
+				str=m_MenuList[j]->GetString();
+				if(name==str){
+					menuloc=i;
+					listloc=j;
+					break;
+				}
+			}
+		}
+		++i;
+	}
+	if(menuloc>=0&&listloc>=0&&menuloc>=listloc)menustart=menuloc-listloc;
+	return(menustart);
+}
+
+void BCMenu::RemoveTopLevelOwnerDraw(void)
+{
+	CString str;
+	int i=0,j=0;
+	int nummenulist=m_MenuList.GetSize(),menustart;
+
+	menustart=GetMenuStart();
+	for(i=menustart,j=0;i<(int)GetMenuItemCount();++i,++j){
+		if(j<nummenulist){
+			str=m_MenuList[j]->GetString();
+			if(GetSubMenu(i)){
+				m_MenuList[j]->nFlags=MF_POPUP|MF_BYPOSITION;
+				ModifyMenu(i,MF_POPUP|MF_BYPOSITION,
+					(UINT)GetSubMenu(i)->m_hMenu,str);
+			}
+		}
+	}
+
+}
+
+void BCMenu::InsertSpaces(void)
+{
+	int i,j,numitems,maxlength;
+	CString string,newstring;
+	CSize t;
+	CFont m_fontMenu;
+	LOGFONT m_lf;
+
+	ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
+	NONCLIENTMETRICS nm;
+	nm.cbSize = sizeof (NONCLIENTMETRICS);
+	VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,nm.cbSize,&nm,0)); 
+	m_lf =  nm.lfMenuFont;
+	m_fontMenu.CreateFontIndirect (&m_lf);
+
+	CWnd *pWnd = AfxGetMainWnd();  
+	if (pWnd == NULL)pWnd = CWnd::GetDesktopWindow();
+	CDC *pDC = pWnd->GetDC();
+	CFont* pFont = pDC->SelectObject (&m_fontMenu);
+
+	numitems=GetMenuItemCount();
+	maxlength = -1;
+	for(i=0;i<numitems;++i){
+		string=m_MenuList[i]->GetString();
+		j=string.Find((char)9);
+		newstring.Empty();
+		if(j!=-1)newstring=string.Left(j);
+		else newstring=string;
+		newstring+=_T(" ");//SK: modified for Unicode correctness. 
+		LPCTSTR lpstrText = (LPCTSTR)newstring;
+		t=pDC->GetTextExtent(lpstrText,_tcslen(lpstrText));
+		if(t.cx>maxlength)maxlength = t.cx;
+	}
+	for(i=0;i<numitems;++i){
+		string=m_MenuList[i]->GetString();
+		j=string.Find((char)9);
+		if(j!=-1){
+			newstring.Empty();
+			newstring=string.Left(j);
+			LPCTSTR lpstrText = (LPCTSTR)(newstring);
+			t=pDC->GetTextExtent(lpstrText,_tcslen(lpstrText));
+			while(t.cx<maxlength){
+				newstring+=_T(' ');//SK: modified for Unicode correctness
+				LPCTSTR lpstrText = (LPCTSTR)(newstring);
+				t=pDC->GetTextExtent(lpstrText,_tcslen(lpstrText));
+			}
+			newstring+=string.Mid(j);
+#ifdef UNICODE      
+			m_MenuList[i]->SetWideString(newstring);//SK: modified for dynamic allocation
+#else
+			m_MenuList[i]->SetAnsiString(newstring);
+#endif
+		}
+	}
+	pDC->SelectObject (pFont);              // Select old font in
+	pWnd->ReleaseDC(pDC);       // Release the DC
+	m_fontMenu.DeleteObject();
+}
+
+void BCMenu::LoadCheckmarkBitmap(int unselect, int select)
+{
+	if(unselect>0 && select>0){
+		m_selectcheck=select;
+		m_unselectcheck=unselect;
+		if(checkmaps)checkmaps->DeleteImageList();
+		else checkmaps=new(CImageList);
+		checkmaps->Create(m_iconX,m_iconY,ILC_MASK,2,1);
+		BOOL flag1=AddBitmapToImageList(checkmaps,unselect);
+		BOOL flag2=AddBitmapToImageList(checkmaps,select);
+		if(!flag1||!flag2){
+			checkmaps->DeleteImageList();
+			delete checkmaps;
+			checkmaps=NULL;
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//[18.06.99 rj]
+BOOL BCMenu::GetMenuText(UINT id, CString& string, UINT nFlags/*= MF_BYPOSITION*/)
+{
+	BOOL returnflag=FALSE;
+
+	if(MF_BYPOSITION&nFlags){
+		UINT numMenuItems = m_MenuList.GetUpperBound();
+		if(id<=numMenuItems){
+			string=m_MenuList[id]->GetString();
+			returnflag=TRUE;
+		}
+	}
+	else{
+		int uiLoc;
+		BCMenu* pMenu = FindMenuOption(id,uiLoc);
+		if(NULL!=pMenu) returnflag = pMenu->GetMenuText(uiLoc,string);
+	}
+	return(returnflag);
+}
+
+
+void BCMenu::DrawRadioDot(CDC *pDC,int x,int y,COLORREF color)
+{
+	CRect rcDot(x,y,x+6,y+6);
+	CBrush brush;
+	CPen pen;
+	brush.CreateSolidBrush(color);
+	pen.CreatePen(PS_SOLID,0,color);
+	CBrush *pOldBrush=pDC->SelectObject(&brush);
+	CPen *pOldPen=pDC->SelectObject(&pen);
+	pDC->Ellipse(&rcDot);
+	pDC->SelectObject(pOldBrush);
+	pDC->SelectObject(pOldPen);
+	pen.DeleteObject();
+	brush.DeleteObject();
+}
+
+void BCMenu::DrawCheckMark(CDC* pDC,int x,int y,COLORREF color)
+{
+	CPen m_penBack;
+	m_penBack.CreatePen (PS_SOLID,0,color);
+	CPen *pOldPen = pDC->SelectObject (&m_penBack);
+	pDC->MoveTo(x,y+2);
+	pDC->LineTo(x,y+5);
+
+	pDC->MoveTo(x+1,y+3);
+	pDC->LineTo(x+1,y+6);
+
+	pDC->MoveTo(x+2,y+4);
+	pDC->LineTo(x+2,y+7);
+
+	pDC->MoveTo(x+3,y+3);
+	pDC->LineTo(x+3,y+6);
+
+	pDC->MoveTo(x+4,y+2);
+	pDC->LineTo(x+4,y+5);
+
+	pDC->MoveTo(x+5,y+1);
+	pDC->LineTo(x+5,y+4);
+
+	pDC->MoveTo(x+6,y);
+	pDC->LineTo(x+6,y+3);
+
+	pDC->SelectObject (pOldPen);
+	m_penBack.DeleteObject();
+}
+
+BCMenuData *BCMenu::FindMenuList(UINT nID)
+{
+	for(int i=0;i<=m_MenuList.GetUpperBound();++i){
+		if(m_MenuList[i]->nID==nID && !m_MenuList[i]->syncflag){
+			m_MenuList[i]->syncflag=1;
+			return(m_MenuList[i]);
+		}
+	}
+	return(NULL);
+}
+
+void BCMenu::InitializeMenuList(int value)
+{
+	for(int i=0;i<=m_MenuList.GetUpperBound();++i)
+		m_MenuList[i]->syncflag=value;
+}
+
+void BCMenu::DeleteMenuList(void)
+{
+	for(int i=0;i<=m_MenuList.GetUpperBound();++i){
+		if(!m_MenuList[i]->syncflag){
+			delete m_MenuList[i];
+		}
+	}
+}
+
+void BCMenu::SynchronizeMenu(void)
+{
+	CTypedPtrArray<CPtrArray, BCMenuData*> temp;
+	BCMenuData *mdata;
+	CString string;
+	UINT submenu,nID=0,state,j;
+
+	InitializeMenuList(0);
+	for(j=0;j<GetMenuItemCount();++j){
+		mdata=NULL;
+		state=GetMenuState(j,MF_BYPOSITION);
+		if(state&MF_POPUP){
+			submenu=(UINT)GetSubMenu(j)->m_hMenu;
+			mdata=FindMenuList(submenu);
+			GetMenuString(j,string,MF_BYPOSITION);
+			if(!mdata)mdata=NewODMenu(j,
+				(state&0xFF)|MF_BYPOSITION|MF_POPUP|MF_OWNERDRAW,submenu,string);
+			else if(string.GetLength()>0)
+#ifdef UNICODE
+				mdata->SetWideString(string);  //SK: modified for dynamic allocation
+#else
+				mdata->SetAnsiString(string);
+#endif
+		}
+		else if(state&MF_SEPARATOR){
+			mdata=FindMenuList(0);
+			if(!mdata)mdata=NewODMenu(j,
+				state|MF_BYPOSITION|MF_SEPARATOR|MF_OWNERDRAW,0,_T(""));//SK: modified for Unicode correctness
+			else ModifyMenu(j,mdata->nFlags,nID,(LPCTSTR)mdata);
+		}
+		else{
+			nID=GetMenuItemID(j);
+			mdata=FindMenuList(nID);
+			GetMenuString(j,string,MF_BYPOSITION);
+			if(!mdata)mdata=NewODMenu(j,state|MF_BYPOSITION|MF_OWNERDRAW,
+				nID,string);
+			else{
+				mdata->nFlags=state|MF_BYPOSITION|MF_OWNERDRAW;
+				if(string.GetLength()>0)
+#ifdef UNICODE
+					mdata->SetWideString(string);//SK: modified for dynamic allocation
+#else
+					mdata->SetAnsiString(string);
+#endif
+
+				ModifyMenu(j,mdata->nFlags,nID,(LPCTSTR)mdata);
+			}
+		}
+		if(mdata)temp.Add(mdata);
+	}
+	DeleteMenuList();
+	m_MenuList.RemoveAll();
+	m_MenuList.Append(temp);
+	temp.RemoveAll(); 
+}
+
+void BCMenu::UpdateMenu(CMenu *pmenu)
+{
+#ifdef _CPPRTTI 
+	BCMenu *psubmenu = dynamic_cast<BCMenu *>(pmenu);
+#else
+	BCMenu *psubmenu = (BCMenu *)pmenu;
+#endif
+	if(psubmenu)psubmenu->SynchronizeMenu();
+}
+
+LRESULT BCMenu::FindKeyboardShortcut(UINT nChar, UINT nFlags,
+									 CMenu *pMenu)
+{
+#ifdef _CPPRTTI 
+	BCMenu *pBCMenu = dynamic_cast<BCMenu *>(pMenu);
+#else
+	BCMenu *pBCMenu = (BCMenu *)pMenu;
+#endif
+	if(pBCMenu && nFlags&MF_POPUP){
+		CString key(_T('&'),2);//SK: modified for Unicode correctness
+		key.SetAt(1,(TCHAR)nChar);
+		key.MakeLower();
+		CString menutext;
+		int menusize = (int)pBCMenu->GetMenuItemCount();
+		if(menusize!=(pBCMenu->m_MenuList.GetUpperBound()+1))
+			pBCMenu->SynchronizeMenu();
+		for(int i=0;i<menusize;++i){
+			if(pBCMenu->GetMenuText(i,menutext)){
+				menutext.MakeLower();
+				if(menutext.Find(key)>=0)return(MAKELRESULT(i,2));
+			}
+		}
+	}
+	return(0);
+}
+
+void BCMenu::DitherBlt (HDC hdcDest, int nXDest, int nYDest, int nWidth, 
+						int nHeight, HBITMAP hbm, int nXSrc, int nYSrc,
+						COLORREF bgcolor)
+{
+	ASSERT(hdcDest && hbm);
+	ASSERT(nWidth > 0 && nHeight > 0);
+
+	// Create a generic DC for all BitBlts
+	HDC hDC = CreateCompatibleDC(hdcDest);
+	ASSERT(hDC);
+
+	if (hDC)
+	{
+		// Create a DC for the monochrome DIB section
+		HDC bwDC = CreateCompatibleDC(hDC);
+		ASSERT(bwDC);
+
+		if (bwDC)
+		{
+			// Create the monochrome DIB section with a black and white palette
+			struct {
+				BITMAPINFOHEADER bmiHeader; 
+				RGBQUAD      bmiColors[2]; 
+			} RGBBWBITMAPINFO = {
+
+				{    // a BITMAPINFOHEADER
+					sizeof(BITMAPINFOHEADER),  // biSize 
+						nWidth,         // biWidth; 
+						nHeight,        // biHeight; 
+						1,            // biPlanes; 
+						1,            // biBitCount 
+						BI_RGB,         // biCompression; 
+						0,            // biSizeImage; 
+						0,            // biXPelsPerMeter; 
+						0,            // biYPelsPerMeter; 
+						0,            // biClrUsed; 
+						0            // biClrImportant; 
+				},    
+				{
+					{ 0x00, 0x00, 0x00, 0x00 }, { 0xFF, 0xFF, 0xFF, 0x00 }
+					} 
+			};
+			VOID *pbitsBW;
+			HBITMAP hbmBW = CreateDIBSection(bwDC,
+				(LPBITMAPINFO)&RGBBWBITMAPINFO, DIB_RGB_COLORS, &pbitsBW, NULL, 0);
+			ASSERT(hbmBW);
+
+			if (hbmBW)
+			{
+				// Attach the monochrome DIB section and the bitmap to the DCs
+				HBITMAP olddib = (HBITMAP)SelectObject(bwDC, hbmBW);
+				HBITMAP hdcolddib = (HBITMAP)SelectObject(hDC, hbm);
+
+				// BitBlt the bitmap into the monochrome DIB section
+				BitBlt(bwDC, 0, 0, nWidth, nHeight, hDC, nXSrc, nYSrc, SRCCOPY);
+
+				// Paint the destination rectangle in gray
+				FillRect(hdcDest, CRect(nXDest, nYDest, nXDest + nWidth, nYDest +
+					nHeight), GetSysColorBrush(bgcolor));
+				//SK: looks better on the old shell
+				// BitBlt the black bits in the monochrome bitmap into COLOR_3DHILIGHT
+				// bits in the destination DC
+				// The magic ROP comes from the Charles Petzold's book
+				HBRUSH hb = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT));
+				HBRUSH oldBrush = (HBRUSH)SelectObject(hdcDest, hb);
+				BitBlt(hdcDest,nXDest+1,nYDest+1,nWidth,nHeight,bwDC,0,0,0xB8074A);
+
+				// BitBlt the black bits in the monochrome bitmap into COLOR_3DSHADOW
+				// bits in the destination DC
+				hb = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
+				DeleteObject(SelectObject(hdcDest, hb));
+				BitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight,bwDC,0,0,0xB8074A);
+				DeleteObject(SelectObject(hdcDest, oldBrush));
+				VERIFY(DeleteObject(SelectObject(bwDC, olddib)));
+				SelectObject(hDC, hdcolddib);
+			}
+
+			VERIFY(DeleteDC(bwDC));
+		}
+
+		VERIFY(DeleteDC(hDC));
+	}
+}
+
+void BCMenu::GetFadedBitmap(CBitmap &bmp)
+{
+	CDC ddc;
+	COLORREF bgcol,col;
+	BITMAP BitMap;
+
+	bmp.GetBitmap(&BitMap);
+	ddc.CreateCompatibleDC(NULL);
+	CBitmap * pddcOldBmp = ddc.SelectObject(&bmp);
+
+	// use this to get the background color, takes into account color shifting
+	CDC ddc2;
+	CBrush brush;
+	CBitmap bmp2;
+	ddc2.CreateCompatibleDC(NULL);
+	bmp2.CreateCompatibleBitmap(&ddc,BitMap.bmWidth,BitMap.bmHeight);
+	col=GetSysColor(COLOR_3DFACE);
+	brush.CreateSolidBrush(col);
+	CBitmap * pddcOldBmp2 = ddc2.SelectObject(&bmp2);
+	CRect rect(0,0,BitMap.bmWidth,BitMap.bmHeight);
+	ddc2.FillRect(rect,&brush);
+	bgcol=ddc2.GetPixel(1,1);
+	brush.DeleteObject();
+	ddc2.SelectObject(pddcOldBmp2);
+
+	for(int i=0;i<BitMap.bmWidth;++i){
+		for(int j=0;j<BitMap.bmHeight;++j){
+			col=ddc.GetPixel(i,j);
+			if(col!=bgcol)ddc.SetPixel(i,j,LightenColor(col,0.3));
+		}
+	}
+	ddc.SelectObject(pddcOldBmp);
+}
+
+void BCMenu::GetShadowBitmap(CBitmap &bmp)
+{
+	CDC ddc;
+	COLORREF bgcol,col,shadowcol=GetSysColor(COLOR_BTNSHADOW);
+	BITMAP BitMap;
+
+	if(!IsWinXPLuna())shadowcol=LightenColor(shadowcol,0.49);
+	bmp.GetBitmap(&BitMap);
+	ddc.CreateCompatibleDC(NULL);
+	CBitmap * pddcOldBmp = ddc.SelectObject(&bmp);
+
+	// use this to get the background color, takes into account color shifting
+	CDC ddc2;
+	CBrush brush;
+	CBitmap bmp2;
+	ddc2.CreateCompatibleDC(NULL);
+	bmp2.CreateCompatibleBitmap(&ddc,BitMap.bmWidth,BitMap.bmHeight);
+	col=GetSysColor(COLOR_3DFACE);
+	brush.CreateSolidBrush(col);
+	CBitmap * pddcOldBmp2 = ddc2.SelectObject(&bmp2);
+	CRect rect(0,0,BitMap.bmWidth,BitMap.bmHeight);
+	ddc2.FillRect(rect,&brush);
+	bgcol=ddc2.GetPixel(1,1);
+	brush.DeleteObject();
+	ddc2.SelectObject(pddcOldBmp2);
+
+	for(int i=0;i<BitMap.bmWidth;++i){
+		for(int j=0;j<BitMap.bmHeight;++j){
+			col=ddc.GetPixel(i,j);
+			if(col!=bgcol)ddc.SetPixel(i,j,shadowcol);
+		}
+	}
+	ddc.SelectObject(pddcOldBmp);
+}
+
+
+BOOL BCMenu::AddBitmapToImageList(CImageList *bmplist,UINT nResourceID)
+{
+	BOOL bReturn=FALSE;
+
+	// O.S.
+	if (m_bDynIcons){
+		bmplist->Add((HICON)nResourceID);
+		bReturn=TRUE;
+	}
+	else{
+		HBITMAP hbmp=LoadSysColorBitmap(nResourceID);
+		if(hbmp){
+			CBitmap bmp;
+			bmp.Attach(hbmp);
+			if(m_bitmapBackgroundFlag){
+				if(bmplist->Add(&bmp,m_bitmapBackground)>=0)bReturn=TRUE;
+			}
+			else{
+				if(bmplist->Add(&bmp,GetSysColor(COLOR_3DFACE))>=0)bReturn=TRUE;
+			}
+			bmp.Detach();
+			DeleteObject(hbmp);
+		}
+		else{
+			CBitmap mybmp;
+			if(mybmp.LoadBitmap(nResourceID)){
+				if(m_bitmapBackgroundFlag){
+					if(bmplist->Add(&mybmp,m_bitmapBackground)>=0)bReturn=TRUE;
+				}
+				else{
+					if(bmplist->Add(&mybmp,GetSysColor(COLOR_3DFACE))>=0)bReturn=TRUE;
+				}
+			}
+		}
+	}
+	if(bReturn&&IsLunaMenuStyle()&&xp_draw_3D_bitmaps){
+		CWnd *pWnd = AfxGetMainWnd();            // Get main window
+		if (pWnd == NULL) pWnd = CWnd::GetDesktopWindow();
+		CDC *pDC = pWnd->GetDC();              // Get device context
+		CBitmap bmp,bmp2;
+		GetBitmapFromImageList(pDC,bmplist,0,bmp);
+		GetFadedBitmap(bmp);
+		bmplist->Add(&bmp,GetSysColor(COLOR_3DFACE));
+		GetBitmapFromImageList(pDC,bmplist,0,bmp2);
+		GetShadowBitmap(bmp2);
+		bmplist->Add(&bmp2,GetSysColor(COLOR_3DFACE));
+		pWnd->ReleaseDC(pDC);  // Release the DC
+	}
+	return(bReturn);
+}
+
+void BCMenu::SetBitmapBackground(COLORREF color)
+{
+	m_bitmapBackground=color;
+	m_bitmapBackgroundFlag=TRUE;
+}
+
+void BCMenu::UnSetBitmapBackground(void)
+{
+	m_bitmapBackgroundFlag=FALSE;
+}
+
+// Given a toolbar, append all the options from it to this menu
+// Passed a ptr to the toolbar object and the toolbar ID
+// Author : Robert Edward Caldecott
+void BCMenu::AddFromToolBar(CToolBar* pToolBar, int nResourceID)
+{
+	for (int i = 0; i < pToolBar->GetCount(); i++) {
+		UINT nID = pToolBar->GetItemID(i);
+		// See if this toolbar option
+		// appears as a command on this
+		// menu or is a separator
+		if (nID == 0 || GetMenuState(nID, MF_BYCOMMAND) == 0xFFFFFFFF)
+			continue; // Item doesn't exist
+		UINT nStyle;
+		int nImage;
+		// Get the toolbar button info
+		pToolBar->GetButtonInfo(i, nID, nStyle, nImage);
+		// OK, we have the command ID of the toolbar
+		// option, and the tollbar bitmap offset
+		int nLoc;
+		BCMenuData* pData;
+		BCMenu *pSubMenu = FindMenuOption(nID, nLoc);
+		if (pSubMenu && nLoc >= 0)pData = pSubMenu->m_MenuList[nLoc];
+		else {
+			// Create a new BCMenuData structure
+			pData = new BCMenuData;
+			m_MenuList.Add(pData);
+		}
+		// Set some default structure members
+		pData->menuIconNormal = nResourceID;
+		pData->nID = nID;
+		pData->nFlags =  MF_BYCOMMAND | MF_OWNERDRAW;
+		pData->xoffset = nImage;
+		if (pData->bitmap)pData->bitmap->DeleteImageList();
+		else pData->bitmap = new CImageList;
+		pData->bitmap->Create(m_iconX, m_iconY,ILC_COLORDDB|ILC_MASK, 1, 1);
+
+		if(!AddBitmapToImageList(pData->bitmap, nResourceID)){
+			pData->bitmap->DeleteImageList();
+			delete pData->bitmap;
+			pData->bitmap=NULL;
+			pData->menuIconNormal = -1;
+			pData->xoffset = -1;
+		}
+
+		// Modify our menu
+		ModifyMenu(nID,pData->nFlags,nID,(LPCTSTR)pData);
+	}
+}
+
+BOOL BCMenu::Draw3DCheckmark(CDC *dc, const CRect& rc,
+							 BOOL bSelected, HBITMAP hbmCheck)
+{
+	CRect rcDest = rc;
+	CBrush brush;
+	COLORREF col;
+	if(IsWinXPLuna())col=GetSysColor(COLOR_3DFACE);
+	else col=GetSysColor(COLOR_MENU);
+	if(!bSelected)col = LightenColor(col,0.6);
+	brush.CreateSolidBrush(col);
+	dc->FillRect(rcDest,&brush);
+	brush.DeleteObject();
+	if (IsNewShell()) //SK: looks better on the old shell
+		dc->DrawEdge(&rcDest, BDR_SUNKENOUTER, BF_RECT);
+	if (!hbmCheck)DrawCheckMark(dc,rc.left+4,rc.top+4,GetSysColor(COLOR_MENUTEXT));
+	else DrawRadioDot(dc,rc.left+5,rc.top+4,GetSysColor(COLOR_MENUTEXT));
+	return TRUE;
+}
+
+BOOL BCMenu::DrawXPCheckmark(CDC *dc, const CRect& rc, HBITMAP hbmCheck,COLORREF &colorout)
+{
+	CBrush brushin;
+	brushin.CreateSolidBrush(LightenColor(colorout,0.85));
+	dc->FillRect(rc,&brushin);
+	brushin.DeleteObject();
+	dc->Draw3dRect (rc,colorout,colorout);
+	if (!hbmCheck)DrawCheckMark(dc,rc.left+5,rc.top+4,GetSysColor(COLOR_MENUTEXT));
+	else DrawRadioDot(dc,rc.left+5,rc.top+4,GetSysColor(COLOR_MENUTEXT));
+	return TRUE;
+}
+
+void BCMenu::DitherBlt2(CDC *drawdc, int nXDest, int nYDest, int nWidth, 
+						int nHeight, CBitmap &bmp, int nXSrc, int nYSrc,
+						COLORREF bgcolor)
+{
+	// create a monochrome memory DC
+	CDC ddc;
+	ddc.CreateCompatibleDC(0);
+	CBitmap bwbmp;
+	bwbmp.CreateCompatibleBitmap(&ddc, nWidth, nHeight);
+	CBitmap * pddcOldBmp = ddc.SelectObject(&bwbmp);
+
+	CDC dc;
+	dc.CreateCompatibleDC(0);
+	CBitmap * pdcOldBmp = dc.SelectObject(&bmp);
+
+	// build a mask
+	ddc.PatBlt(0, 0, nWidth, nHeight, WHITENESS);
+	dc.SetBkColor(GetSysColor(COLOR_BTNFACE));
+	ddc.BitBlt(0, 0, nWidth, nHeight, &dc, nXSrc,nYSrc, SRCCOPY);
+	dc.SetBkColor(GetSysColor(COLOR_BTNHILIGHT));
+	ddc.BitBlt(0, 0, nWidth, nHeight, &dc, nXSrc,nYSrc, SRCPAINT);
+
+	// Copy the image from the toolbar into the memory DC
+	// and draw it (grayed) back into the toolbar.
+	dc.FillSolidRect(0,0, nWidth, nHeight, bgcolor);
+	//SK: Looks better on the old shell
+	dc.SetBkColor(RGB(0, 0, 0));
+	dc.SetTextColor(RGB(255, 255, 255));
+	CBrush brShadow, brHilight;
+	brHilight.CreateSolidBrush(GetSysColor(COLOR_BTNHILIGHT));
+	brShadow.CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));
+	CBrush * pOldBrush = dc.SelectObject(&brHilight);
+	dc.BitBlt(0,0, nWidth, nHeight, &ddc, 0, 0, 0x00E20746L);
+	drawdc->BitBlt(nXDest+1,nYDest+1,nWidth, nHeight, &dc,0,0,SRCCOPY);
+	dc.BitBlt(1,1, nWidth, nHeight, &ddc, 0, 0, 0x00E20746L);
+	dc.SelectObject(&brShadow);
+	dc.BitBlt(0,0, nWidth, nHeight, &ddc, 0, 0, 0x00E20746L);
+	drawdc->BitBlt(nXDest,nYDest,nWidth, nHeight, &dc,0,0,SRCCOPY);
+	// reset DCs
+	ddc.SelectObject(pddcOldBmp);
+	ddc.DeleteDC();
+	dc.SelectObject(pOldBrush);
+	dc.SelectObject(pdcOldBmp);
+	dc.DeleteDC();
+
+	brShadow.DeleteObject();
+	brHilight.DeleteObject();
+	bwbmp.DeleteObject();
+}
+
+void BCMenu::SetDisableOldStyle(void)
+{
+	disable_old_style=TRUE;
+}
+
+void BCMenu::UnSetDisableOldStyle(void)
+{
+	disable_old_style=FALSE;
+}
+
+BOOL BCMenu::GetDisableOldStyle(void)
+{
+	return(disable_old_style);
+}
+
+
+WORD BCMenu::NumBitmapColors(LPBITMAPINFOHEADER lpBitmap)
+{
+	if ( lpBitmap->biClrUsed != 0)
+		return (WORD)lpBitmap->biClrUsed;
+
+	switch (lpBitmap->biBitCount){
+		case 1:
+			return 2;
+		case 4:
+			return 16;
+		case 8:
+			return 256;
+		default:
+			return 0;
+	}
+	return 0;
+}
+
+HBITMAP BCMenu::LoadSysColorBitmap(int nResourceId)
+{
+	HINSTANCE hInst = 
+		AfxFindResourceHandle(MAKEINTRESOURCE(nResourceId),RT_BITMAP);
+	HRSRC hRsrc = 
+		::FindResource(hInst,MAKEINTRESOURCE(nResourceId),RT_BITMAP);
+	if (hRsrc == NULL){
+		hInst = NULL;
+		hRsrc = ::FindResource(hInst,MAKEINTRESOURCE(nResourceId),RT_BITMAP);
+	}
+	if (hRsrc == NULL)return NULL;
+
+	// determine how many colors in the bitmap
+	HGLOBAL hglb;
+	if ((hglb = LoadResource(hInst, hRsrc)) == NULL)
+		return NULL;
+	LPBITMAPINFOHEADER lpBitmap = (LPBITMAPINFOHEADER)LockResource(hglb);
+	if (lpBitmap == NULL)return NULL;
+	WORD numcol = NumBitmapColors(lpBitmap);
+	::FreeResource(hglb);
+
+	if(numcol!=16)return(NULL);
+
+	return AfxLoadSysColorBitmap(hInst, hRsrc, FALSE);
+}
+
+BOOL BCMenu::RemoveMenu(UINT uiId,UINT nFlags)
+{
+	if(MF_BYPOSITION&nFlags){
+		UINT uint = GetMenuState(uiId,MF_BYPOSITION);
+		if(uint&MF_SEPARATOR && !(uint&MF_POPUP)){
+			delete m_MenuList.GetAt(uiId);
+			m_MenuList.RemoveAt(uiId);
+		}
+		else{
+			BCMenu* pSubMenu = (BCMenu*) GetSubMenu(uiId);
+			if(NULL==pSubMenu){
+				UINT uiCommandId = GetMenuItemID(uiId);
+				for(int i=0;i<m_MenuList.GetSize(); i++){
+					if(m_MenuList[i]->nID==uiCommandId){
+						delete m_MenuList.GetAt(i);
+						m_MenuList.RemoveAt(i);
+						break;
+					}
+				}
+			}
+			else{
+				int numSubMenus = m_SubMenus.GetUpperBound();
+				for(int m = numSubMenus; m >= 0; m--){
+					if(m_SubMenus[m]==pSubMenu->m_hMenu){
+						int numAllSubMenus = m_AllSubMenus.GetUpperBound();
+						for(int n = numAllSubMenus; n>= 0; n--){
+							if(m_AllSubMenus[n]==m_SubMenus[m])m_AllSubMenus.RemoveAt(n);
+						}
+						m_SubMenus.RemoveAt(m);
+					}
+				}
+				int num = pSubMenu->GetMenuItemCount();
+				for(int i=num-1;i>=0;--i)pSubMenu->RemoveMenu(i,MF_BYPOSITION);
+				for(int i=m_MenuList.GetUpperBound();i>=0;i--){
+					if(m_MenuList[i]->nID==(UINT)pSubMenu->m_hMenu){
+						delete m_MenuList.GetAt(i);
+						m_MenuList.RemoveAt(i);
+						break;
+					}
+				}
+				delete pSubMenu; 
+			}
+		}
+	}
+	else{
+		int iPosition =0;
+		BCMenu* pMenu = FindMenuOption(uiId,iPosition);
+		// bug fix RIA 14th September 2000 
+		// failed to return correct value on call to remove menu as the item was 
+		// removed twice. The second time its not found 
+		// so a value of 0 was being returned 
+		if(pMenu) return pMenu->RemoveMenu(iPosition,MF_BYPOSITION); // added return 
+	}
+	return CMenu::RemoveMenu(uiId,nFlags);
+}
+
+BOOL BCMenu::DeleteMenu(UINT uiId,UINT nFlags)
+{
+	if(MF_BYPOSITION&nFlags){
+		UINT uint = GetMenuState(uiId,MF_BYPOSITION);
+		if(uint&MF_SEPARATOR && !(uint&MF_POPUP)){
+			// make sure it's a separator
+			int menulistsize=m_MenuList.GetSize();	
+			if(uiId<(UINT)menulistsize){
+				CString str=m_MenuList[uiId]->GetString();
+				if(str==""){
+					delete m_MenuList.GetAt(uiId);
+					m_MenuList.RemoveAt(uiId);
+				}
+			}
+		}
+		else{
+			BCMenu* pSubMenu = (BCMenu*) GetSubMenu(uiId);
+			if(NULL==pSubMenu){
+				UINT uiCommandId = GetMenuItemID(uiId);
+				for(int i=0;i<m_MenuList.GetSize(); i++){
+					if(m_MenuList[i]->nID==uiCommandId){
+						delete m_MenuList.GetAt(i);
+						m_MenuList.RemoveAt(i);
+						break;
+					}
+				}
+			}
+			else{
+				int numSubMenus = m_SubMenus.GetUpperBound();
+				for(int m = numSubMenus; m >= 0; m--){
+					if(m_SubMenus[m]==pSubMenu->m_hMenu){
+						int numAllSubMenus = m_AllSubMenus.GetUpperBound();
+						for(int n = numAllSubMenus; n>= 0; n--){
+							if(m_AllSubMenus[n]==m_SubMenus[m])m_AllSubMenus.RemoveAt(n);
+						}
+						m_SubMenus.RemoveAt(m);
+					}
+				}
+				int num = pSubMenu->GetMenuItemCount();
+				for(int i=num-1;i>=0;--i)pSubMenu->DeleteMenu(i,MF_BYPOSITION);
+				for(int i=m_MenuList.GetUpperBound();i>=0;i--){
+					if(m_MenuList[i]->nID==(UINT)pSubMenu->m_hMenu){
+						delete m_MenuList.GetAt(i);
+						m_MenuList.RemoveAt(i);
+						break;
+					}
+				}
+				delete pSubMenu;
+			}
+		}
+	}
+	else{
+		int iPosition =0;
+		BCMenu* pMenu = FindMenuOption(uiId,iPosition);
+		if(pMenu)return pMenu->DeleteMenu(iPosition,MF_BYPOSITION);
+	}
+
+	return(CMenu::DeleteMenu(uiId,nFlags));
+}
+
+
+BOOL BCMenu::AppendMenuA(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,int nIconNormal)
+{
+	USES_CONVERSION;
+	return AppendMenuW(nFlags,nIDNewItem,A2W(lpszNewItem),nIconNormal);
+}
+
+BOOL BCMenu::AppendMenuW(UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,int nIconNormal)
+{
+	return AppendODMenuW(lpszNewItem,nFlags,nIDNewItem,nIconNormal);
+}
+
+BOOL BCMenu::AppendMenuA(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CImageList *il,int xoffset)
+{
+	USES_CONVERSION;
+	return AppendMenuW(nFlags,nIDNewItem,A2W(lpszNewItem),il,xoffset);
+}
+
+BOOL BCMenu::AppendMenuW(UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CImageList *il,int xoffset)
+{
+	return AppendODMenuW(lpszNewItem,nFlags,nIDNewItem,il,xoffset);
+}
+
+BOOL BCMenu::AppendMenuA(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CBitmap *bmp)
+{
+	USES_CONVERSION;
+	return AppendMenuW(nFlags,nIDNewItem,A2W(lpszNewItem),bmp);
+}
+
+BOOL BCMenu::AppendMenuW(UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CBitmap *bmp)
+{
+	if(bmp){
+		CImageList temp;
+		temp.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+		if(m_bitmapBackgroundFlag)temp.Add(bmp,m_bitmapBackground);
+		else temp.Add(bmp,GetSysColor(COLOR_3DFACE));
+		return AppendODMenuW(lpszNewItem,nFlags,nIDNewItem,&temp,0);
+	}
+	return AppendODMenuW(lpszNewItem,nFlags,nIDNewItem,NULL,0);
+}
+
+BOOL BCMenu::InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,int nIconNormal)
+{
+	USES_CONVERSION;
+	return InsertMenuW(nPosition,nFlags,nIDNewItem,A2W(lpszNewItem),nIconNormal);
+}
+
+BOOL BCMenu::InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,int nIconNormal)
+{
+	return InsertODMenuW(nPosition,lpszNewItem,nFlags,nIDNewItem,nIconNormal);
+}
+
+BOOL BCMenu::InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CImageList *il,int xoffset)
+{
+	USES_CONVERSION;
+	return InsertMenuW(nPosition,nFlags,nIDNewItem,A2W(lpszNewItem),il,xoffset);
+}
+
+BOOL BCMenu::InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CImageList *il,int xoffset)
+{
+	return InsertODMenuW(nPosition,lpszNewItem,nFlags,nIDNewItem,il,xoffset);
+}
+
+BOOL BCMenu::InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CBitmap *bmp)
+{
+	USES_CONVERSION;
+	return InsertMenuW(nPosition,nFlags,nIDNewItem,A2W(lpszNewItem),bmp);
+}
+
+BOOL BCMenu::InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CBitmap *bmp)
+{
+	if(bmp){
+		CImageList temp;
+		temp.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+		if(m_bitmapBackgroundFlag)temp.Add(bmp,m_bitmapBackground);
+		else temp.Add(bmp,GetSysColor(COLOR_3DFACE));
+		return InsertODMenuW(nPosition,lpszNewItem,nFlags,nIDNewItem,&temp,0);
+	}
+	return InsertODMenuW(nPosition,lpszNewItem,nFlags,nIDNewItem,NULL,0);
+}
+
+//--------------------------------------------------------------------------
+//[21.06.99 rj]
+BCMenu* BCMenu::AppendODPopupMenuW(wchar_t *lpstrText)
+{
+	BCMenu* pSubMenu = new BCMenu;
+	pSubMenu->m_unselectcheck=m_unselectcheck;
+	pSubMenu->m_selectcheck=m_selectcheck;
+	pSubMenu->checkmaps=checkmaps;
+	pSubMenu->checkmapsshare=TRUE;
+	pSubMenu->CreatePopupMenu();
+	AppendODMenuW(lpstrText,MF_POPUP,(UINT)pSubMenu->m_hMenu, -1);
+	return pSubMenu;
+}
+
+//--------------------------------------------------------------------------
+//[21.06.99 rj]
+BCMenu* BCMenu::AppendODPopupMenuA(LPCSTR lpstrText)
+{
+	USES_CONVERSION;
+	return AppendODPopupMenuW(A2W(lpstrText));
+}
+
+BOOL BCMenu::ImageListDuplicate(CImageList *il,int xoffset,CImageList *newlist)
+{
+	if (il == NULL||newlist==NULL||xoffset<0) return FALSE;
+	HICON hIcon = il->ExtractIcon(xoffset);
+	int cx, cy;
+	ImageList_GetIconSize(il->m_hImageList, &cx, &cy);
+	newlist->Create(cx,cy,ILC_COLORDDB|ILC_MASK,1,1);
+	newlist->Add(hIcon);
+	::DestroyIcon(hIcon);
+	if(IsLunaMenuStyle()&&xp_draw_3D_bitmaps){
+		CWnd *pWnd = AfxGetMainWnd();            // Get main window
+		if (pWnd == NULL) pWnd = CWnd::GetDesktopWindow();
+		CDC *pDC = pWnd->GetDC();              // Get device context
+		CBitmap bmp,bmp2;
+		GetBitmapFromImageList(pDC,newlist,0,bmp);
+		GetFadedBitmap(bmp);
+		newlist->Add(&bmp,GetSysColor(COLOR_3DFACE));
+		GetBitmapFromImageList(pDC,newlist,0,bmp2);
+		GetShadowBitmap(bmp2);
+		newlist->Add(&bmp2,GetSysColor(COLOR_3DFACE));
+		pWnd->ReleaseDC(pDC);  // Release the DC
+	}
+	return TRUE;
+}
+
+// 2001-07-12, Damir Valiulin:
+//          Added GetSubMenu (LPCTSTR lpszSubMenuName) function
+//
+
+CMenu* BCMenu::GetSubMenu(int nPos)
+{
+	return CMenu::GetSubMenu (nPos);
+}
+
+CMenu* BCMenu::GetSubMenu(LPCTSTR lpszSubMenuName)
+{
+	int num = GetMenuItemCount ();
+	CString name;
+
+	for (int i=0; i<num; i++)
+	{
+		GetMenuString (i, name, MF_BYPOSITION);
+		if (name.Compare (lpszSubMenuName) == 0)
+		{
+			return CMenu::GetSubMenu (i);
+		}
+	}
+
+	return NULL;
+}
+
+BCMenu* BCMenu::GetSubBCMenu(char* pText)
+{
+	USES_CONVERSION;
+	return GetSubBCMenu(A2W(pText));
+}
+
+BCMenu* BCMenu::GetSubBCMenu(wchar_t* lpszSubMenuName)
+{
+	BCMenuData *mdata;
+	mdata=FindMenuOption(lpszSubMenuName);
+	if(mdata){
+		HMENU bchmenu=(HMENU)mdata->nID;
+		CMenu *ptr=FromHandle(bchmenu);
+		BOOL flag=ptr->IsKindOf(RUNTIME_CLASS( BCMenu ));
+		if(flag)return((BCMenu *)ptr);
+	}
+	return NULL;
+}
+
+
+// Tongzhe Cui, Functions to remove a popup menu based on its name. Seperators
+// before and after the popup menu can also be removed if they exist.
+int BCMenu::GetMenuPosition(char* pText)
+{
+	USES_CONVERSION;
+	return GetMenuPosition(A2W(pText));
+}
+
+int BCMenu::GetMenuPosition(wchar_t* pText)
+{
+	int i,j;
+	BCMenu* psubmenu;
+	for(i=0;i<(int)(GetMenuItemCount());++i)
+	{
+		psubmenu=(BCMenu *)GetSubMenu(i);
+		if(!psubmenu)
+		{
+			const wchar_t *szWide;//SK: we use const to prevent misuse of this Ptr
+			for(j=0;j<=m_MenuList.GetUpperBound();++j)
+			{     
+				szWide = m_MenuList[j]->GetWideString ();
+				if(szWide && !wcscmp(pText,szWide))//SK: modified for dynamic allocation
+					return j;
+			}
+		}
+	}
+	// means no found;
+	return -1;
+}
+
+int BCMenu::RemoveMenu(char* pText, BC_Seperator sPos)
+{
+	USES_CONVERSION;
+	return RemoveMenu(A2W(pText), sPos);
+}
+
+int BCMenu::RemoveMenu(wchar_t* pText, BC_Seperator sPos)
+{
+	int nPos = GetMenuPosition(pText);
+	if(nPos != -1)
+	{
+		RemoveMenu(nPos, MF_BYPOSITION);
+	}
+	if(sPos == BCMENU_HEAD)
+	{
+		ASSERT(nPos - 1 >= 0);
+		RemoveMenu(nPos-1, MF_BYPOSITION);
+	}
+	else if(sPos == BCMENU_TAIL)
+	{
+		RemoveMenu(nPos-1, MF_BYPOSITION);
+	}
+	else if(sPos == BCMENU_BOTH)
+	{
+		// remove the end first;
+		RemoveMenu(nPos-1, MF_BYPOSITION);
+		// remove the head;
+		ASSERT(nPos - 1 >= 0);
+		RemoveMenu(nPos-1, MF_BYPOSITION);
+	}
+	return nPos;
+}
+
+int BCMenu::DeleteMenu(char* pText, BC_Seperator sPos)
+{
+	USES_CONVERSION;
+	return DeleteMenu(A2W(pText), sPos);
+}
+
+int BCMenu::DeleteMenu(wchar_t* pText, BC_Seperator sPos)
+{
+	int nPos = GetMenuPosition(pText);
+	if(nPos != -1)
+	{
+		DeleteMenu(nPos, MF_BYPOSITION);
+	}
+	if(sPos == BCMENU_HEAD)
+	{
+		ASSERT(nPos - 1 >= 0);
+		DeleteMenu(nPos-1, MF_BYPOSITION);
+	}
+	else if(sPos == BCMENU_TAIL)
+	{
+		DeleteMenu(nPos-1, MF_BYPOSITION);
+	}
+	else if(sPos == BCMENU_BOTH)
+	{
+		// remove the end first;
+		DeleteMenu(nPos-1, MF_BYPOSITION);
+		// remove the head;
+		ASSERT(nPos - 1 >= 0);
+		DeleteMenu(nPos-1, MF_BYPOSITION);
+	}
+	return nPos;
+}
+
+// Larry Antram
+BOOL BCMenu::SetMenuText(UINT id, CString string, UINT nFlags/*= MF_BYPOSITION*/ )
+{
+	BOOL returnflag=FALSE;
+
+	if(MF_BYPOSITION&nFlags)
+	{
+		UINT numMenuItems = m_MenuList.GetUpperBound();
+		if(id<=numMenuItems){
+#ifdef UNICODE
+			m_MenuList[id]->SetWideString((LPCTSTR)string);
+#else
+			m_MenuList[id]->SetAnsiString(string);
+#endif
+			returnflag=TRUE;
+		}
+	}
+	else{
+		int uiLoc;
+		BCMenu* pMenu = FindMenuOption(id,uiLoc);
+		if(NULL!=pMenu) returnflag = pMenu->SetMenuText(uiLoc,string);
+	}
+	return(returnflag);
+}
+
+// courtesy of Warren Stevens
+void BCMenu::ColorBitmap(CDC* pDC,CBitmap& bmp,CSize bitmap_size,CSize icon_size,COLORREF fill,COLORREF border,int hatchstyle)
+{
+	CDC bmpdc;
+	COLORREF m_newclrBack;
+	int x1=0,y1=0,x2=bitmap_size.cx,y2=bitmap_size.cy;
+
+	if(IsWinXPLuna())m_newclrBack=GetSysColor(COLOR_3DFACE);
+	else m_newclrBack=GetSysColor(COLOR_MENU);
+
+	bmpdc.CreateCompatibleDC(pDC);
+
+	bmp.CreateCompatibleBitmap(pDC, icon_size.cx, icon_size.cy);	
+	CBitmap* pOldBitmap = bmpdc.SelectObject(&bmp);
+
+	if(bitmap_size!=icon_size){
+		CBrush background_brush;
+		background_brush.CreateSolidBrush(m_newclrBack);
+		CRect rect(0,0, icon_size.cx, icon_size.cy);
+		bmpdc.FillRect(rect,&background_brush);
+		x1 = (icon_size.cx-bitmap_size.cx)/2;
+		y1 = (icon_size.cy-bitmap_size.cy)/2;
+		x2 = x1+bitmap_size.cx;
+		y2 = y1+bitmap_size.cy;
+		background_brush.DeleteObject();
+	}
+
+	CPen border_pen(PS_SOLID, 1, border);
+	CBrush fill_brush;
+	if(hatchstyle!=-1) { fill_brush.CreateHatchBrush(hatchstyle, fill); }
+	else      { fill_brush.CreateSolidBrush(fill);             }
+
+	CPen*    pOldPen    = bmpdc.SelectObject(&border_pen);
+	CBrush*  pOldBrush  = bmpdc.SelectObject(&fill_brush);
+
+	bmpdc.Rectangle(x1,y1,x2,y2);
+
+	if(NULL!=pOldBrush)  { bmpdc.SelectObject(pOldBrush);  }
+	if(NULL!=pOldPen)    { bmpdc.SelectObject(pOldPen);    }
+	if(NULL!=pOldBitmap) { bmpdc.SelectObject(pOldBitmap); }
+}
+
+BOOL BCMenu::IsWindowsClassicTheme(void)
+{
+	TCHAR Buf[_MAX_PATH+10];
+	HKEY hKey;
+	DWORD size,type; 
+	long lRetCode; 
+	static BOOL XPTheme_returnflag=FALSE;
+	static BOOL XPTheme_checkflag=FALSE;
+
+	if(XPTheme_checkflag)return(XPTheme_returnflag);
+
+	XPTheme_checkflag=TRUE;
+	lRetCode = RegOpenKeyEx ( HKEY_CURRENT_USER, 
+		_T("Software\\Microsoft\\Plus!\\Themes\\Current"), 
+		0,KEY_READ,&hKey);  
+	if (lRetCode == ERROR_SUCCESS){ 
+		size = _MAX_PATH;type=REG_SZ;
+		lRetCode=::RegQueryValueEx(hKey,NULL,NULL,&type,
+			(unsigned char *)Buf,&size);
+		if(lRetCode == ERROR_SUCCESS){
+			TCHAR szClassicTheme[]=_T("Windows Classic.theme");
+			int len=lstrlen(Buf);
+			if(len>=lstrlen(szClassicTheme)){
+				if(!lstrcmpi(&Buf[len-lstrlen(szClassicTheme)],szClassicTheme)){
+					XPTheme_returnflag=TRUE;
+				}
+			}
+		}
+		RegCloseKey(hKey);  
+	}
+	return(XPTheme_returnflag);
+}
+
+int BCMenu::GlobalImageListOffset(int nID)
+{
+	int numcurrent=m_AllImagesID.GetSize();
+	int existsloc = -1;
+	for(int i=0;i<numcurrent;++i){
+		if(m_AllImagesID[i]==nID){
+			existsloc=i;
+			break;
+		}
+	}
+	return existsloc;
+}
+
+BOOL BCMenu::CanDraw3DImageList(int offset)
+{
+	BOOL retflag=FALSE;
+	int numcurrent=m_AllImagesID.GetSize();
+	if(offset+1<numcurrent&&offset+2<numcurrent){
+		int nID=m_AllImagesID[offset];
+		if(m_AllImagesID[offset+1]==nID&&m_AllImagesID[offset+2]==nID)retflag=TRUE;
+	}
+	return(retflag);
+}
+
+int BCMenu::AddToGlobalImageList(CImageList *il,int xoffset,int nID)
+{
+	int loc = -1;
+	HIMAGELIST hImageList = m_AllImages.m_hImageList;
+	if(!hImageList){
+		m_AllImages.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+	}
+	HICON hIcon = il->ExtractIcon(xoffset);
+	if(hIcon){
+		CBitmap bmp,bmp2;
+		if(IsLunaMenuStyle()&&xp_draw_3D_bitmaps){
+			CWnd *pWnd = AfxGetMainWnd();            // Get main window
+			if (pWnd == NULL) pWnd = CWnd::GetDesktopWindow();
+			CDC *pDC = pWnd->GetDC();              // Get device context
+			GetBitmapFromImageList(pDC,il,xoffset,bmp);
+			GetFadedBitmap(bmp);
+			GetBitmapFromImageList(pDC,il,xoffset,bmp2);
+			GetShadowBitmap(bmp2);
+			pWnd->ReleaseDC(pDC);  // Release the DC
+		}
+		int numcurrent=m_AllImagesID.GetSize();
+		int existsloc = -1;
+		for(int i=0;i<numcurrent;++i){
+			if(m_AllImagesID[i]==nID){
+				existsloc=i;
+				break;
+			}
+		}
+		if(existsloc>=0){
+			m_AllImages.Replace(existsloc,hIcon);
+			loc = existsloc;
+			if(IsLunaMenuStyle()&&xp_draw_3D_bitmaps){
+				if(existsloc+1<numcurrent&&m_AllImagesID[existsloc+1]==nID){
+					if(existsloc+2<numcurrent&&m_AllImagesID[existsloc+2]==nID){
+						CImageList il2;
+						il2.Create(m_iconX,m_iconY,ILC_COLORDDB|ILC_MASK,1,1);
+						il2.Add(&bmp,GetSysColor(COLOR_3DFACE));
+						HICON hIcon2 = il2.ExtractIcon(0);
+						m_AllImages.Replace(existsloc+1,hIcon2);
+						il2.Add(&bmp2,GetSysColor(COLOR_3DFACE));
+						HICON hIcon3 = il2.ExtractIcon(1);
+						m_AllImages.Replace(existsloc+2,hIcon3);
+						::DestroyIcon(hIcon2);
+						::DestroyIcon(hIcon3);
+					}
+				}
+			}
+		}
+		else{
+			m_AllImages.Add(hIcon);
+			m_AllImagesID.Add(nID);
+			loc=numcurrent;
+			if(IsLunaMenuStyle()&&xp_draw_3D_bitmaps){
+				m_AllImages.Add(&bmp,GetSysColor(COLOR_3DFACE));
+				m_AllImages.Add(&bmp2,GetSysColor(COLOR_3DFACE));
+				m_AllImagesID.Add(nID);
+				m_AllImagesID.Add(nID);
+			}
+		}
+		::DestroyIcon(hIcon);
+	}
+	return(loc);
+}
+
+//*************************************************************************
+
+/////////////////////////////////////////////////////////////////////////////
+// Toolbar for loading images
+/////////////////////////////////////////////////////////////////////////////
+
+struct CToolBarData
+{
+	WORD wVersion;
+	WORD wWidth;
+	WORD wHeight;
+	WORD wItemCount;
+	WORD* items(){
+		return (WORD*)(this+1);
+	}
+};
+
+BOOL BCMenuToolBar::LoadToolBar(LPCTSTR lpszResourceName)
+{
+	ASSERT_VALID(this);
+	ASSERT(lpszResourceName != NULL);
+
+	HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName,RT_TOOLBAR);
+	HRSRC hRsrc = ::FindResource(hInst,lpszResourceName,RT_TOOLBAR);
+	if (hRsrc == NULL){
+		hInst = NULL;
+		hRsrc = ::FindResource(hInst,lpszResourceName,RT_TOOLBAR);
+	}
+	if (hRsrc == NULL)
+		return FALSE;
+
+	HGLOBAL hGlobal = ::LoadResource(hInst,hRsrc);
+	if (hGlobal == NULL)
+		return FALSE;
+
+	CToolBarData* pData = (CToolBarData*)::LockResource(hGlobal);
+	if (pData == NULL)
+		return FALSE;
+	ASSERT(pData->wVersion == 1);
+
+	UINT* pItems = new UINT[pData->wItemCount];
+	for (int i = 0; i < pData->wItemCount; i++)
+		pItems[i] = pData->items()[i];
+	BOOL bResult = SetButtons(pItems,pData->wItemCount);
+	delete[] pItems;
+
+	if (bResult){
+		CSize sizeImage(pData->wWidth,pData->wHeight);
+		CSize sizeButton(pData->wWidth+7,pData->wHeight+7);
+		SetSizes(sizeButton,sizeImage);
+		m_iconX=pData->wWidth;
+		m_iconY=pData->wHeight;
+		bResult = LoadBitmap(lpszResourceName);
+	}
+
+	::UnlockResource(hGlobal);
+	::FreeResource(hGlobal);
+	return bResult;
+}
+
+BOOL BCMenuToolBar::LoadBitmap(LPCTSTR lpszResourceName)
+{
+	ASSERT_VALID(this);
+	ASSERT(lpszResourceName != NULL);
+
+	HINSTANCE hInstImageWell = AfxFindResourceHandle(lpszResourceName,RT_BITMAP);
+	HRSRC hRsrcImageWell = ::FindResource(hInstImageWell,lpszResourceName,RT_BITMAP);
+	if (hRsrcImageWell == NULL){
+		hInstImageWell = NULL;
+		hRsrcImageWell = ::FindResource(hInstImageWell,lpszResourceName,RT_BITMAP);
+	}
+	if (hRsrcImageWell == NULL)
+		return FALSE;
+
+	HBITMAP hbmImageWell;
+	hbmImageWell = AfxLoadSysColorBitmap(hInstImageWell,hRsrcImageWell);
+
+	if (!AddReplaceBitmap(hbmImageWell))
+		return FALSE;
+
+	m_hInstImageWell = hInstImageWell;
+	m_hRsrcImageWell = hRsrcImageWell;
+	return TRUE;
+}

+ 366 - 0
SATHelper/UI/BCMenu.h

@@ -0,0 +1,366 @@
+#ifndef BCMenu_H
+#define BCMenu_H
+
+#include <afxtempl.h>
+
+// BCMenuData class. Fill this class structure to define a single menu item:
+class BCMenuData
+{
+	wchar_t *m_szMenuText;
+public:
+	BCMenuData () {menuIconNormal=-1;xoffset=-1;bitmap=NULL;pContext=NULL;
+	nFlags=0;nID=0;syncflag=0;m_szMenuText=NULL;global_offset=-1;threeD=FALSE;};
+	void SetAnsiString(LPCSTR szAnsiString);
+	void SetWideString(const wchar_t *szWideString);
+	const wchar_t *GetWideString(void) {return m_szMenuText;};
+	~BCMenuData ();
+	CString GetString(void);//returns the menu text in ANSI or UNICODE
+	int xoffset,global_offset;
+	BOOL threeD;
+	int menuIconNormal;
+	UINT nFlags,nID,syncflag;
+	CImageList *bitmap;
+	void *pContext; // used to attach user data
+};
+
+//struct CMenuItemInfo : public MENUITEMINFO {
+struct CMenuItemInfo : public 
+	//MENUITEMINFO 
+#ifndef UNICODE   //SK: this fixes warning C4097: typedef-name 'MENUITEMINFO' used as synonym for class-name 'tagMENUITEMINFOA'
+	tagMENUITEMINFOA
+#else
+	tagMENUITEMINFOW
+#endif
+{
+	CMenuItemInfo()
+	{
+		memset(this, 0, sizeof(MENUITEMINFO));
+		cbSize = sizeof(MENUITEMINFO);
+	}
+};
+
+// how the menu's are drawn, either original or XP style
+typedef enum {BCMENU_DRAWMODE_ORIGINAL,BCMENU_DRAWMODE_XP} BC_MenuDrawMode;
+
+// how seperators are handled when removing a menu (Tongzhe Cui)
+typedef enum {BCMENU_NONE, BCMENU_HEAD, BCMENU_TAIL, BCMENU_BOTH} BC_Seperator;
+
+// defines for unicode support
+#ifndef UNICODE
+#define AppendMenu AppendMenuA
+#define InsertMenu InsertMenuA
+#define InsertODMenu InsertODMenuA
+#define AppendODMenu AppendODMenuA
+#define AppendODPopupMenu AppendODPopupMenuA
+#define ModifyODMenu ModifyODMenuA
+#else
+#define AppendMenu AppendMenuW
+#define InsertMenu InsertMenuW
+#define InsertODMenu InsertODMenuW
+#define AppendODMenu AppendODMenuW
+#define ModifyODMenu ModifyODMenuW
+#define AppendODPopupMenu AppendODPopupMenuW
+#endif
+
+
+class BCMenu : public CMenu
+{
+	DECLARE_DYNAMIC( BCMenu )
+public:
+	BCMenu(); 
+	virtual ~BCMenu();
+
+	// Functions for loading and applying bitmaps to menus (see example application)
+	virtual BOOL LoadMenu(LPCTSTR lpszResourceName);
+	virtual BOOL LoadMenu(int nResource);
+	BOOL LoadToolbar(UINT nToolBar);
+	BOOL LoadToolbars(const UINT *arID,int n);
+	void AddFromToolBar(CToolBar* pToolBar, int nResourceID);
+	BOOL LoadFromToolBar(UINT nID,UINT nToolBar,int& xoffset);
+	BOOL AddBitmapToImageList(CImageList *list,UINT nResourceID);
+	static HBITMAP LoadSysColorBitmap(int nResourceId);
+	void LoadCheckmarkBitmap(int unselect,int select); // custom check mark bitmaps
+
+	// functions for appending a menu option, use the AppendMenu call (see above define)
+	BOOL AppendMenuA(UINT nFlags,UINT nIDNewItem=0,const char *lpszNewItem=NULL,int nIconNormal=-1);
+	BOOL AppendMenuA(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CImageList *il,int xoffset);
+	BOOL AppendMenuA(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CBitmap *bmp);
+	BOOL AppendMenuW(UINT nFlags,UINT nIDNewItem=0,wchar_t *lpszNewItem=NULL,int nIconNormal=-1);
+	BOOL AppendMenuW(UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CImageList *il,int xoffset);
+	BOOL AppendMenuW(UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CBitmap *bmp);
+	BOOL AppendODMenuA(LPCSTR lpstrText,UINT nFlags = MF_OWNERDRAW,UINT nID = 0,int nIconNormal = -1);  
+	BOOL AppendODMenuW(wchar_t *lpstrText,UINT nFlags = MF_OWNERDRAW,UINT nID = 0,int nIconNormal = -1);  
+	BOOL AppendODMenuA(LPCSTR lpstrText,UINT nFlags,UINT nID,CImageList *il,int xoffset);
+	BOOL AppendODMenuW(wchar_t *lpstrText,UINT nFlags,UINT nID,CImageList *il,int xoffset);
+
+	// for appending a popup menu (see example application)
+	BCMenu* AppendODPopupMenuA(LPCSTR lpstrText);
+	BCMenu* AppendODPopupMenuW(wchar_t *lpstrText);
+
+	// functions for inserting a menu option, use the InsertMenu call (see above define)
+	BOOL InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,const char *lpszNewItem=NULL,int nIconNormal=-1);
+	BOOL InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CImageList *il,int xoffset);
+	BOOL InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CBitmap *bmp);
+	BOOL InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,wchar_t *lpszNewItem=NULL,int nIconNormal=-1);
+	BOOL InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CImageList *il,int xoffset);
+	BOOL InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CBitmap *bmp);
+	BOOL InsertODMenuA(UINT nPosition,LPCSTR lpstrText,UINT nFlags = MF_OWNERDRAW,UINT nID = 0,int nIconNormal = -1); 
+	BOOL InsertODMenuW(UINT nPosition,wchar_t *lpstrText,UINT nFlags = MF_OWNERDRAW,UINT nID = 0,int nIconNormal = -1);  
+	BOOL InsertODMenuA(UINT nPosition,LPCSTR lpstrText,UINT nFlags,UINT nID,CImageList *il,int xoffset);
+	BOOL InsertODMenuW(UINT nPosition,wchar_t *lpstrText,UINT nFlags,UINT nID,CImageList *il,int xoffset);
+
+	// functions for modifying a menu option, use the ModifyODMenu call (see above define)
+	BOOL ModifyODMenuA(const char *lpstrText,UINT nID=0,int nIconNormal=-1);
+	BOOL ModifyODMenuA(const char *lpstrText,UINT nID,CImageList *il,int xoffset);
+	BOOL ModifyODMenuA(const char *lpstrText,UINT nID,CBitmap *bmp);
+	BOOL ModifyODMenuA(const char *lpstrText,const char *OptionText,int nIconNormal);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,UINT nID=0,int nIconNormal=-1);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,UINT nID,CImageList *il,int xoffset);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,UINT nID,CBitmap *bmp);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,wchar_t *OptionText,int nIconNormal);
+	// use this method for adding a solid/hatched colored square beside a menu option
+	// courtesy of Warren Stevens
+	BOOL ModifyODMenuA(const char *lpstrText,UINT nID,COLORREF fill,COLORREF border,int hatchstyle=-1,CSize *pSize=NULL);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,UINT nID,COLORREF fill,COLORREF border,int hatchstyle=-1,CSize *pSize=NULL);
+
+	// for deleting and removing menu options
+	BOOL	RemoveMenu(UINT uiId,UINT nFlags);
+	BOOL	DeleteMenu(UINT uiId,UINT nFlags);
+	// sPos means Seperator's position, since we have no way to find the seperator's position in the menu
+	// we have to specify them when we call the RemoveMenu to make sure the unused seperators are removed;
+	// sPos  = None no seperator removal;
+	//       = Head  seperator in front of this menu item;
+	//       = Tail  seperator right after this menu item;
+	//       = Both  seperators at both ends;
+	// remove the menu item based on their text, return -1 if not found, otherwise return the menu position;
+	int RemoveMenu(char* pText, BC_Seperator sPos=BCMENU_NONE);
+	int RemoveMenu(wchar_t* pText, BC_Seperator sPos=BCMENU_NONE);
+	int DeleteMenu(char* pText, BC_Seperator sPos=BCMENU_NONE);
+	int DeleteMenu(wchar_t* pText, BC_Seperator sPos=BCMENU_NONE);
+
+	// Destoying
+	virtual BOOL DestroyMenu();
+
+	// function for retrieving and setting a menu options text (use this function
+	// because it is ownerdrawn)
+	BOOL GetMenuText(UINT id,CString &string,UINT nFlags = MF_BYPOSITION);
+	BOOL SetMenuText(UINT id,CString string, UINT nFlags = MF_BYPOSITION);
+
+	// Getting a submenu from it's name or position
+	BCMenu* GetSubBCMenu(char* lpszSubMenuName);
+	BCMenu* GetSubBCMenu(wchar_t* lpszSubMenuName);
+	CMenu* GetSubMenu (LPCTSTR lpszSubMenuName);
+	CMenu* GetSubMenu (int nPos);
+	int GetMenuPosition(char* pText);
+	int GetMenuPosition(wchar_t* pText);
+
+	// Drawing: 
+	virtual void DrawItem( LPDRAWITEMSTRUCT);  // Draw an item
+	virtual void MeasureItem( LPMEASUREITEMSTRUCT );  // Measure an item
+
+	// Static functions used for handling menu's in the mainframe
+	static void UpdateMenu(CMenu *pmenu);
+	static BOOL IsMenu(CMenu *submenu);
+	static BOOL IsMenu(HMENU submenu);
+	static LRESULT FindKeyboardShortcut(UINT nChar,UINT nFlags,CMenu *pMenu);
+
+	// Function to set how menu is drawn, either original or XP style
+	static void SetMenuDrawMode(UINT mode){
+		BCMenu::original_drawmode=mode;
+		BCMenu::xp_drawmode=mode;
+	};
+	// Function to set how disabled items are drawn (mode=FALSE means they are not drawn selected)
+	static void SetSelectDisableMode(BOOL mode){
+		BCMenu::original_select_disabled=mode;
+		BCMenu::xp_select_disabled=mode;
+	};
+	static int BCMenu::GetMenuDrawMode(void);
+	static BOOL BCMenu::GetSelectDisableMode(void);
+
+	// how the bitmaps are drawn in XP Luna mode
+	static void SetXPBitmap3D(BOOL val){
+		BCMenu::xp_draw_3D_bitmaps=val;
+	};
+	static BOOL GetXPBitmap3D(void){return BCMenu::xp_draw_3D_bitmaps;}
+
+	// Customizing:
+	// Set icon size
+	void SetIconSize (int, int); 
+	// set the color in the bitmaps that is the background transparent color
+	void SetBitmapBackground(COLORREF color);
+	void UnSetBitmapBackground(void);
+	// obsolete functions for setting how menu images are dithered for disabled menu options
+	BOOL GetDisableOldStyle(void);
+	void SetDisableOldStyle(void);
+	void UnSetDisableOldStyle(void);
+	static COLORREF LightenColor(COLORREF col,double factor);
+	static COLORREF DarkenColor(COLORREF col,double factor);
+
+	// Miscellaneous Protected Member functions
+protected:
+	static BOOL IsNewShell(void);
+	static BOOL IsWinXPLuna(void);
+	static BOOL IsLunaMenuStyle(void);
+	static BOOL IsWindowsClassicTheme(void);
+	BCMenuData *BCMenu::FindMenuItem(UINT nID);
+	BCMenu *FindMenuOption(int nId,int& nLoc);
+	BCMenu *FindAnotherMenuOption(int nId,int& nLoc,CArray<BCMenu*,BCMenu*>&bcsubs,
+		CArray<int,int&>&bclocs);
+	BCMenuData *FindMenuOption(wchar_t *lpstrText);
+	void InsertSpaces(void);
+	void DrawCheckMark(CDC *pDC,int x,int y,COLORREF color);
+	void DrawRadioDot(CDC *pDC,int x,int y,COLORREF color);
+	BCMenuData *NewODMenu(UINT pos,UINT nFlags,UINT nID,CString string);
+	void SynchronizeMenu(void);
+	void BCMenu::InitializeMenuList(int value);
+	void BCMenu::DeleteMenuList(void);
+	BCMenuData *BCMenu::FindMenuList(UINT nID);
+	void DrawItem_Win9xNT2000 (LPDRAWITEMSTRUCT lpDIS);
+	void DrawItem_WinXP (LPDRAWITEMSTRUCT lpDIS);
+	BOOL Draw3DCheckmark(CDC *dc, const CRect& rc,BOOL bSelected,HBITMAP hbmCheck);
+	BOOL DrawXPCheckmark(CDC *dc, const CRect& rc, HBITMAP hbmCheck,COLORREF &colorout);
+	void DitherBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, 
+		int nHeight, HBITMAP hbm, int nXSrc, int nYSrc,COLORREF bgcolor);
+	void DitherBlt2(CDC *drawdc, int nXDest, int nYDest, int nWidth, 
+		int nHeight, CBitmap &bmp, int nXSrc, int nYSrc,COLORREF bgcolor);
+	BOOL GetBitmapFromImageList(CDC* pDC,CImageList *imglist,int nIndex,CBitmap &bmp);
+	BOOL ImageListDuplicate(CImageList *il,int xoffset,CImageList *newlist);
+	static WORD NumBitmapColors(LPBITMAPINFOHEADER lpBitmap);
+	void ColorBitmap(CDC* pDC, CBitmap& bmp,CSize bitmap_size,CSize icon_size,COLORREF fill,COLORREF border,int hatchstyle=-1);
+	void RemoveTopLevelOwnerDraw(void);
+	int GetMenuStart(void);
+	void GetFadedBitmap(CBitmap &bmp);
+	void GetShadowBitmap(CBitmap &bmp);
+	int AddToGlobalImageList(CImageList *il,int xoffset,int nID);
+	int GlobalImageListOffset(int nID);
+	BOOL CanDraw3DImageList(int offset);
+
+	// Member Variables
+protected:
+	CTypedPtrArray<CPtrArray, BCMenuData*> m_MenuList;  // Stores list of menu items 
+	// When loading an owner-drawn menu using a Resource, BCMenu must keep track of
+	// the popup menu's that it creates. Warning, this list *MUST* be destroyed
+	// last item first :)
+	CTypedPtrArray<CPtrArray, HMENU>  m_SubMenus;  // Stores list of sub-menus 
+	// Stores a list of all BCMenu's ever created 
+	static CTypedPtrArray<CPtrArray, HMENU>  m_AllSubMenus;
+	// Global ImageList
+	static CImageList m_AllImages;
+	static CArray<int,int&> m_AllImagesID;
+	// icon size
+	int m_iconX,m_iconY;
+	COLORREF m_bitmapBackground;
+	BOOL m_bitmapBackgroundFlag;
+	BOOL disable_old_style;
+	static UINT original_drawmode;
+	static BOOL original_select_disabled;
+	static UINT xp_drawmode;
+	static BOOL xp_select_disabled;
+	static BOOL xp_draw_3D_bitmaps;
+	CImageList *checkmaps;
+	BOOL checkmapsshare;
+	int m_selectcheck;
+	int m_unselectcheck;
+	BOOL m_bDynIcons;
+	BOOL m_loadmenu;
+}; 
+
+class BCMenuToolBar : public CToolBar{
+public:
+	BCMenuToolBar() : CToolBar() {m_iconX=m_iconY=0;}
+	BOOL LoadToolBar(LPCTSTR lpszResourceName);
+	BOOL LoadToolBar(UINT nIDResource){
+		return LoadToolBar(MAKEINTRESOURCE(nIDResource));
+	}
+	BOOL LoadBitmap(LPCTSTR lpszResourceName);
+	void GetIconSize(int &iconx,int &icony){iconx=m_iconX;icony=m_iconY;}
+protected:
+	int m_iconX,m_iconY;
+};
+
+#define BCMENU_USE_MEMDC
+
+#ifdef BCMENU_USE_MEMDC
+//////////////////////////////////////////////////
+// BCMenuMemDC - memory DC
+//
+// Author: Keith Rule
+// Email:  keithr@europa.com
+// Copyright 1996-1997, Keith Rule
+//
+// You may freely use or modify this code provided this
+// Copyright is included in all derived versions.
+//
+// History - 10/3/97 Fixed scrolling bug.
+//                   Added print support.
+//           25 feb 98 - fixed minor assertion bug
+//
+// This class implements a memory Device Context
+
+class BCMenuMemDC : public CDC
+{
+public:
+
+	// constructor sets up the memory DC
+	BCMenuMemDC(CDC* pDC,LPCRECT lpSrcRect) : CDC()
+	{
+		ASSERT(pDC != NULL);
+
+		m_rect.CopyRect(lpSrcRect);
+		m_pDC = pDC;
+		m_pOldBitmap = NULL;
+		m_bMemDC = !pDC->IsPrinting();
+
+		if (m_bMemDC)    // Create a Memory DC
+		{
+			CreateCompatibleDC(pDC);
+			m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
+			m_pOldBitmap = SelectObject(&m_bitmap);
+			SetWindowOrg(m_rect.left, m_rect.top);
+		}
+		else        // Make a copy of the relevent parts of the current DC for printing
+		{
+			m_bPrinting = pDC->m_bPrinting;
+			m_hDC       = pDC->m_hDC;
+			m_hAttribDC = pDC->m_hAttribDC;
+		}
+	}
+
+	// Destructor copies the contents of the mem DC to the original DC
+	~BCMenuMemDC()
+	{
+		if (m_bMemDC) 
+		{    
+			// Copy the offscreen bitmap onto the screen.
+			m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
+				this, m_rect.left, m_rect.top, SRCCOPY);
+
+			//Swap back the original bitmap.
+			SelectObject(m_pOldBitmap);
+		} else {
+			// All we need to do is replace the DC with an illegal value,
+			// this keeps us from accidently deleting the handles associated with
+			// the CDC that was passed to the constructor.
+			m_hDC = m_hAttribDC = NULL;
+		}
+	}
+
+	// Allow usage as a pointer
+	BCMenuMemDC* operator->() {return this;}
+
+	// Allow usage as a pointer
+	operator BCMenuMemDC*() {return this;}
+
+private:
+	CBitmap  m_bitmap;      // Offscreen bitmap
+	CBitmap* m_pOldBitmap;  // bitmap originally found in BCMenuMemDC
+	CDC*     m_pDC;         // Saves CDC passed in constructor
+	CRect    m_rect;        // Rectangle of drawing area.
+	BOOL     m_bMemDC;      // TRUE if CDC really is a Memory DC.
+};
+
+#endif
+
+#endif
+
+//*************************************************************************

+ 2559 - 0
SATHelper/UI/BtnST.cpp

@@ -0,0 +1,2559 @@
+#include "stdafx.h"
+#include "BtnST.h"
+
+#ifdef	BTNST_USE_SOUND
+#pragma comment(lib, "winmm.lib")
+#include <Mmsystem.h>
+#endif
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CButtonST
+
+// Mask for control's type
+#define BS_TYPEMASK SS_TYPEMASK
+
+#ifndef	TTM_SETTITLE
+#define TTM_SETTITLEA           (WM_USER + 32)  // wParam = TTI_*, lParam = char* szTitle
+#define TTM_SETTITLEW           (WM_USER + 33)  // wParam = TTI_*, lParam = wchar* szTitle
+#ifdef	UNICODE
+#define TTM_SETTITLE            TTM_SETTITLEW
+#else
+#define TTM_SETTITLE            TTM_SETTITLEA
+#endif
+#endif
+
+#ifndef	TTS_BALLOON
+#define	TTS_BALLOON		0x40
+#endif
+
+CButtonST::CButtonST()
+{
+	m_bIsPressed		= FALSE;
+	m_bIsFocused		= FALSE;
+	m_bIsDisabled		= FALSE;
+	m_bMouseOnButton	= FALSE;
+
+	FreeResources(FALSE);
+
+	// Default type is "flat" button
+	m_bIsFlat = TRUE;
+	// Button will be tracked also if when the window is inactive (like Internet Explorer)
+	m_bAlwaysTrack = TRUE;
+  
+	// By default draw border in "flat" button 
+	m_bDrawBorder = TRUE; 
+  
+	// By default icon is aligned horizontally
+	m_byAlign = ST_ALIGN_HORIZ; 
+
+	// By default use usual pressed style
+	SetPressedStyle(BTNST_PRESSED_LEFTRIGHT, FALSE);
+  
+	// By default, for "flat" button, don't draw the focus rect
+	m_bDrawFlatFocus = FALSE;
+
+	// By default the button is not the default button
+	m_bIsDefault = FALSE;
+	// Invalid value, since type still unknown
+	m_nTypeStyle = BS_TYPEMASK;
+
+	// By default the button is not a checkbox
+	m_bIsCheckBox = FALSE;
+	m_nCheck = 0;
+
+	// Set default colors
+	SetDefaultColors(FALSE);
+
+	// No tooltip created
+	m_ToolTip.m_hWnd = NULL;
+	m_dwToolTipStyle = 0;
+
+	// Do not draw as a transparent button
+	m_bDrawTransparent = FALSE;
+	m_pbmpOldBk = NULL;
+
+	// No URL defined
+	SetURL(NULL);
+
+	// No cursor defined
+	m_hCursor = NULL;
+
+	// No associated menu
+#ifndef	BTNST_USE_BCMENU
+	m_hMenu = NULL;
+#endif
+	m_hParentWndMenu = NULL;
+	m_bMenuDisplayed = FALSE;
+
+	m_bShowDisabledBitmap = TRUE;
+
+	m_ptImageOrg.x = 3;
+	m_ptImageOrg.y = 3;
+
+	// No defined callbacks
+	::ZeroMemory(&m_csCallbacks, sizeof(m_csCallbacks));
+
+#ifdef	BTNST_USE_SOUND
+	// No defined sounds
+	::ZeroMemory(&m_csSounds, sizeof(m_csSounds));
+#endif
+} // End of CButtonST
+
+CButtonST::~CButtonST()
+{
+	m_font.DeleteObject();
+
+	// Restore old bitmap (if any)
+	if (m_dcBk.m_hDC && m_pbmpOldBk)
+	{
+		m_dcBk.SelectObject(m_pbmpOldBk);
+	} // if
+
+	FreeResources();
+
+	// Destroy the cursor (if any)
+	if (m_hCursor) ::DestroyCursor(m_hCursor);
+
+	// Destroy the menu (if any)
+#ifdef	BTNST_USE_BCMENU
+	if (m_menuPopup.m_hMenu)	m_menuPopup.DestroyMenu();
+#else
+	if (m_hMenu)	::DestroyMenu(m_hMenu);
+#endif
+} // End of ~CButtonST
+
+BEGIN_MESSAGE_MAP(CButtonST, CButton)
+    //{{AFX_MSG_MAP(CButtonST)
+	ON_WM_SETCURSOR()
+	ON_WM_KILLFOCUS()
+	ON_WM_MOUSEMOVE()
+	ON_WM_SYSCOLORCHANGE()
+	ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)
+	ON_WM_ACTIVATE()
+	ON_WM_ENABLE()
+	ON_WM_CANCELMODE()
+	ON_WM_GETDLGCODE()
+	ON_WM_CTLCOLOR_REFLECT()
+	//}}AFX_MSG_MAP
+#ifdef	BTNST_USE_BCMENU
+	ON_WM_MENUCHAR()
+	ON_WM_MEASUREITEM()
+#endif
+
+	ON_MESSAGE(BM_SETSTYLE, OnSetStyle)
+	ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
+	ON_MESSAGE(BM_SETCHECK, OnSetCheck)
+	ON_MESSAGE(BM_GETCHECK, OnGetCheck)
+END_MESSAGE_MAP()
+
+void CButtonST::FreeResources(BOOL bCheckForNULL)
+{
+	if (bCheckForNULL)
+	{
+		// Destroy icons
+		// Note: the following two lines MUST be here! even if
+		// BoundChecker says they are unnecessary!
+		if (m_csIcons[0].hIcon)	::DestroyIcon(m_csIcons[0].hIcon);
+		if (m_csIcons[1].hIcon)	::DestroyIcon(m_csIcons[1].hIcon);
+
+		// Destroy bitmaps
+		if (m_csBitmaps[0].hBitmap)	::DeleteObject(m_csBitmaps[0].hBitmap);
+		if (m_csBitmaps[1].hBitmap)	::DeleteObject(m_csBitmaps[1].hBitmap);
+
+		// Destroy mask bitmaps
+		if (m_csBitmaps[0].hMask)	::DeleteObject(m_csBitmaps[0].hMask);
+		if (m_csBitmaps[1].hMask)	::DeleteObject(m_csBitmaps[1].hMask);
+	} // if
+
+	::ZeroMemory(&m_csIcons, sizeof(m_csIcons));
+	::ZeroMemory(&m_csBitmaps, sizeof(m_csBitmaps));
+} // End of FreeResources
+
+void CButtonST::PreSubclassWindow() 
+{
+	UINT nBS;
+
+	nBS = GetButtonStyle();
+
+	// Set initial control type
+	m_nTypeStyle = nBS & BS_TYPEMASK;
+
+	// Check if this is a checkbox
+	if (nBS & BS_CHECKBOX) m_bIsCheckBox = TRUE;
+
+	// Set initial default state flag
+	if (m_nTypeStyle == BS_DEFPUSHBUTTON)
+	{
+		// Set default state for a default button
+		m_bIsDefault = TRUE;
+
+		// Adjust style for default button
+		m_nTypeStyle = BS_PUSHBUTTON;
+	} // If
+
+	// You should not set the Owner Draw before this call
+	// (don't use the resource editor "Owner Draw" or
+	// ModifyStyle(0, BS_OWNERDRAW) before calling PreSubclassWindow() )
+	ASSERT(m_nTypeStyle != BS_OWNERDRAW);
+
+	// Switch to owner-draw
+	ModifyStyle(BS_TYPEMASK, BS_OWNERDRAW, SWP_FRAMECHANGED);
+
+#if 1 // »ñÈ¡³õʼ×ÖÌå;
+	CFont* cf = GetFont();
+	if(cf !=NULL)
+	{
+		cf->GetObject(sizeof(m_lf),&m_lf);
+	}
+	else
+	{
+		GetObject(GetStockObject(SYSTEM_FONT),sizeof(m_lf),&m_lf);
+	}
+
+	ReconstructFont();
+#endif
+
+	CButton::PreSubclassWindow();
+} // End of PreSubclassWindow
+
+UINT CButtonST::OnGetDlgCode() 
+{
+	UINT nCode = CButton::OnGetDlgCode();
+
+	// Tell the system if we want default state handling
+	// (losing default state always allowed)
+	nCode |= (m_bIsDefault ? DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON);
+
+	return nCode;
+} // End of OnGetDlgCode
+
+BOOL CButtonST::PreTranslateMessage(MSG* pMsg) 
+{
+	InitToolTip();
+	m_ToolTip.RelayEvent(pMsg);
+	
+	if (pMsg->message == WM_LBUTTONDBLCLK)
+		pMsg->message = WM_LBUTTONDOWN;
+
+	return CButton::PreTranslateMessage(pMsg);
+} // End of PreTranslateMessage
+
+HBRUSH CButtonST::CtlColor(CDC* pDC, UINT nCtlColor) 
+{
+	return (HBRUSH)::GetStockObject(NULL_BRUSH); 
+} // End of CtlColor
+
+void CButtonST::OnSysColorChange() 
+{
+	CButton::OnSysColorChange();
+
+	m_dcBk.DeleteDC();
+	m_bmpBk.DeleteObject();	
+	SetDefaultColors();
+} // End of OnSysColorChange
+
+LRESULT CButtonST::OnSetStyle(WPARAM wParam, LPARAM lParam)
+{
+	UINT nNewType = (wParam & BS_TYPEMASK);
+
+	// Update default state flag
+	if (nNewType == BS_DEFPUSHBUTTON)
+	{
+		m_bIsDefault = TRUE;
+	} // if
+	else if (nNewType == BS_PUSHBUTTON)
+	{
+		// Losing default state always allowed
+		m_bIsDefault = FALSE;
+	} // if
+
+	// Can't change control type after owner-draw is set.
+	// Let the system process changes to other style bits
+	// and redrawing, while keeping owner-draw style
+	return DefWindowProc(BM_SETSTYLE,
+		(wParam & ~BS_TYPEMASK) | BS_OWNERDRAW, lParam);
+} // End of OnSetStyle
+
+LRESULT CButtonST::OnSetCheck(WPARAM wParam, LPARAM lParam)
+{
+	ASSERT(m_bIsCheckBox);
+
+	switch (wParam)
+	{
+		case BST_CHECKED:
+		case BST_INDETERMINATE:	// Indeterminate state is handled like checked state
+			SetCheck(1);
+			break;
+		default:
+			SetCheck(0);
+			break;
+	} // switch
+
+	return 0;
+} // End of OnSetCheck
+
+LRESULT CButtonST::OnGetCheck(WPARAM wParam, LPARAM lParam)
+{
+	ASSERT(m_bIsCheckBox);
+	return GetCheck();
+} // End of OnGetCheck
+
+#ifdef	BTNST_USE_BCMENU
+LRESULT CButtonST::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu) 
+{
+	LRESULT lResult;
+	if (BCMenu::IsMenu(pMenu))
+		lResult = BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu);
+	else
+		lResult = CButton::OnMenuChar(nChar, nFlags, pMenu);
+	return lResult;
+} // End of OnMenuChar
+#endif
+
+#ifdef	BTNST_USE_BCMENU
+void CButtonST::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
+{
+	BOOL bSetFlag = FALSE;
+	if (lpMeasureItemStruct->CtlType == ODT_MENU)
+	{
+		if (IsMenu((HMENU)lpMeasureItemStruct->itemID) && BCMenu::IsMenu((HMENU)lpMeasureItemStruct->itemID))
+		{
+			m_menuPopup.MeasureItem(lpMeasureItemStruct);
+			bSetFlag = TRUE;
+		} // if
+	} // if
+	if (!bSetFlag) CButton::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
+} // End of OnMeasureItem
+#endif
+
+void CButtonST::OnEnable(BOOL bEnable) 
+{
+	CButton::OnEnable(bEnable);
+	
+	if (bEnable == FALSE)	
+	{
+		CWnd*	pWnd = GetParent()->GetNextDlgTabItem(this);
+		if (pWnd)
+			pWnd->SetFocus();
+		else
+			GetParent()->SetFocus();
+
+		CancelHover();
+	} // if
+} // End of OnEnable
+
+void CButtonST::OnKillFocus(CWnd * pNewWnd)
+{
+	CButton::OnKillFocus(pNewWnd);
+	CancelHover();
+} // End of OnKillFocus
+
+void CButtonST::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) 
+{
+	CButton::OnActivate(nState, pWndOther, bMinimized);
+	if (nState == WA_INACTIVE)	CancelHover();
+} // End of OnActivate
+
+void CButtonST::OnCancelMode() 
+{
+	CButton::OnCancelMode();
+	CancelHover();
+} // End of OnCancelMode
+
+BOOL CButtonST::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
+{
+	// If a cursor was specified then use it!
+	if (m_hCursor != NULL)
+	{
+		::SetCursor(m_hCursor);
+		return TRUE;
+	} // if
+
+	return CButton::OnSetCursor(pWnd, nHitTest, message);
+} // End of OnSetCursor
+
+void CButtonST::CancelHover()
+{
+	// Only for flat buttons
+	if (m_bIsFlat)
+	{
+		if (m_bMouseOnButton)
+		{
+			m_bMouseOnButton = FALSE;
+			Invalidate();
+		} // if
+	} // if
+} // End of CancelHover
+
+void CButtonST::OnMouseMove(UINT nFlags, CPoint point)
+{
+	CWnd*				wndUnderMouse = NULL;
+	CWnd*				wndActive = this;
+	TRACKMOUSEEVENT		csTME;
+
+	CButton::OnMouseMove(nFlags, point);
+
+	ClientToScreen(&point);
+	wndUnderMouse = WindowFromPoint(point);
+
+	// If the mouse enter the button with the left button pressed then do nothing
+	if (nFlags & MK_LBUTTON && m_bMouseOnButton == FALSE) return;
+
+	// If our button is not flat then do nothing
+	if (m_bIsFlat == FALSE) return;
+
+	if (m_bAlwaysTrack == FALSE)	wndActive = GetActiveWindow();
+
+	if (wndUnderMouse && wndUnderMouse->m_hWnd == m_hWnd && wndActive)
+	{
+		if (!m_bMouseOnButton)
+		{
+			m_bMouseOnButton = TRUE;
+
+			Invalidate();
+
+#ifdef	BTNST_USE_SOUND
+			// Play sound ?
+			if (m_csSounds[0].lpszSound)
+				::PlaySound(m_csSounds[0].lpszSound, m_csSounds[0].hMod, m_csSounds[0].dwFlags);
+#endif
+
+			csTME.cbSize = sizeof(csTME);
+			csTME.dwFlags = TME_LEAVE;
+			csTME.hwndTrack = m_hWnd;
+			::_TrackMouseEvent(&csTME);
+		} // if
+	} else CancelHover();
+} // End of OnMouseMove
+
+// Handler for WM_MOUSELEAVE
+LRESULT CButtonST::OnMouseLeave(WPARAM wParam, LPARAM lParam)
+{
+	CancelHover();
+	return 0;
+} // End of OnMouseLeave
+
+BOOL CButtonST::OnClicked() 
+{	
+	SetFocus();
+
+#ifdef	BTNST_USE_SOUND
+	// Play sound ?
+	if (m_csSounds[1].lpszSound)
+		::PlaySound(m_csSounds[1].lpszSound, m_csSounds[1].hMod, m_csSounds[1].dwFlags);
+#endif
+
+	if (m_bIsCheckBox)
+	{
+		m_nCheck = !m_nCheck;
+		Invalidate();
+	} // if
+	else
+	{
+		// Handle the menu (if any)
+#ifdef	BTNST_USE_BCMENU
+		if (m_menuPopup.m_hMenu)
+#else
+		if (m_hMenu)
+#endif
+		{
+			CRect	rWnd;
+			GetWindowRect(rWnd);
+
+			m_bMenuDisplayed = TRUE;
+			Invalidate();
+
+#ifdef	BTNST_USE_BCMENU
+			BCMenu* psub = (BCMenu*)m_menuPopup.GetSubMenu(0);
+			if (m_csCallbacks.hWnd)	::SendMessage(m_csCallbacks.hWnd, m_csCallbacks.nMessage, (WPARAM)psub, m_csCallbacks.lParam);
+			DWORD dwRetValue = psub->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, rWnd.left, rWnd.bottom, this, NULL);
+#else
+			HMENU hSubMenu = ::GetSubMenu(m_hMenu, 0);
+			if (m_csCallbacks.hWnd)	::SendMessage(m_csCallbacks.hWnd, m_csCallbacks.nMessage, (WPARAM)hSubMenu, m_csCallbacks.lParam);
+			DWORD dwRetValue = ::TrackPopupMenuEx(hSubMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, rWnd.left, rWnd.bottom, m_hParentWndMenu, NULL);
+#endif
+
+			m_bMenuDisplayed = FALSE;
+			Invalidate();
+
+			if (dwRetValue)
+				::PostMessage(m_hParentWndMenu, WM_COMMAND, MAKEWPARAM(dwRetValue, 0), (LPARAM)NULL);
+		} // if
+		else
+		{
+			// Handle the URL (if any)
+			if (_tcslen(m_szURL) > 0)
+			{
+				SHELLEXECUTEINFO	csSEI;
+
+				memset(&csSEI, 0, sizeof(csSEI));
+				csSEI.cbSize = sizeof(SHELLEXECUTEINFO);
+				csSEI.fMask = SEE_MASK_FLAG_NO_UI;
+				csSEI.lpVerb = _T("open");
+				csSEI.lpFile = m_szURL;
+				csSEI.nShow = SW_SHOWMAXIMIZED;
+				::ShellExecuteEx(&csSEI);
+			} // if
+		} // else
+	} // else
+
+	return FALSE;
+} // End of OnClicked
+
+void CButtonST::DrawItem(LPDRAWITEMSTRUCT lpDIS)
+{
+	CDC*	pDC = CDC::FromHandle(lpDIS->hDC);
+
+	// Checkbox?
+	if (m_bIsCheckBox)
+	{
+		m_bIsPressed  =  (lpDIS->itemState & ODS_SELECTED) || (m_nCheck != 0);
+	} // if
+	else	// Normal button OR other button style ...
+	{
+		m_bIsPressed = (lpDIS->itemState & ODS_SELECTED);
+
+		// If there is a menu and it's displayed, draw the button as pressed
+		if (
+#ifdef	BTNST_USE_BCMENU
+			m_menuPopup.m_hMenu 
+#else
+			m_hMenu 
+#endif
+			&& m_bMenuDisplayed)	m_bIsPressed = TRUE;
+	} // else
+
+	m_bIsFocused  = (lpDIS->itemState & ODS_FOCUS);
+	m_bIsDisabled = (lpDIS->itemState & ODS_DISABLED);
+
+	CRect itemRect = lpDIS->rcItem;
+
+	pDC->SetBkMode(TRANSPARENT);
+
+	// Prepare draw... paint button background
+
+	// Draw transparent?
+	if (m_bDrawTransparent)
+		PaintBk(pDC);
+	else
+		OnDrawBackground(pDC, &itemRect);
+
+	// Draw button border
+	OnDrawBorder(pDC, &itemRect);
+
+	// Read the button's title
+	CString sTitle;
+	GetWindowText(sTitle);
+
+	CRect captionRect = lpDIS->rcItem;
+
+	// Draw the icon
+	if (m_csIcons[0].hIcon)
+	{
+		DrawTheIcon(pDC, !sTitle.IsEmpty(), &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+	} // if
+
+	if (m_csBitmaps[0].hBitmap)
+	{
+		pDC->SetBkColor(RGB(255,255,255));
+		DrawTheBitmap(pDC, !sTitle.IsEmpty(), &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+	} // if
+
+	// Write the button title (if any)
+	if (sTitle.IsEmpty() == FALSE)
+	{
+		DrawTheText(pDC, (LPCTSTR)sTitle, &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+	} // if
+
+	if (m_bIsFlat == FALSE || (m_bIsFlat && m_bDrawFlatFocus))
+	{
+		// Draw the focus rect
+		if (m_bIsFocused)
+		{
+			CRect focusRect = itemRect;
+			focusRect.DeflateRect(3, 3);
+			pDC->DrawFocusRect(&focusRect);
+		} // if
+	} // if
+} // End of DrawItem
+
+void CButtonST::PaintBk(CDC* pDC)
+{
+	CClientDC clDC(GetParent());
+	CRect rect;
+	CRect rect1;
+
+	GetClientRect(rect);
+
+	GetWindowRect(rect1);
+	GetParent()->ScreenToClient(rect1);
+
+	if (m_dcBk.m_hDC == NULL)
+	{
+		m_dcBk.CreateCompatibleDC(&clDC);
+		m_bmpBk.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height());
+		m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
+		m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left, rect1.top, SRCCOPY);
+	} // if
+
+	pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcBk, 0, 0, SRCCOPY);
+} // End of PaintBk
+
+HBITMAP CButtonST::CreateBitmapMask(HBITMAP hSourceBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTransColor)
+{
+	HBITMAP		hMask		= NULL;
+	HDC			hdcSrc		= NULL;
+	HDC			hdcDest		= NULL;
+	HBITMAP		hbmSrcT		= NULL;
+	HBITMAP		hbmDestT	= NULL;
+	COLORREF	crSaveBk;
+	COLORREF	crSaveDestText;
+
+	hMask = ::CreateBitmap(dwWidth, dwHeight, 1, 1, NULL);
+	if (hMask == NULL)	return NULL;
+
+	hdcSrc	= ::CreateCompatibleDC(NULL);
+	hdcDest	= ::CreateCompatibleDC(NULL);
+
+	hbmSrcT = (HBITMAP)::SelectObject(hdcSrc, hSourceBitmap);
+	hbmDestT = (HBITMAP)::SelectObject(hdcDest, hMask);
+
+	crSaveBk = ::SetBkColor(hdcSrc, crTransColor);
+
+	::BitBlt(hdcDest, 0, 0, dwWidth, dwHeight, hdcSrc, 0, 0, SRCCOPY);
+
+	crSaveDestText = ::SetTextColor(hdcSrc, RGB(255, 255, 255));
+	::SetBkColor(hdcSrc,RGB(0, 0, 0));
+
+	::BitBlt(hdcSrc, 0, 0, dwWidth, dwHeight, hdcDest, 0, 0, SRCAND);
+
+	SetTextColor(hdcDest, crSaveDestText);
+
+	::SetBkColor(hdcSrc, crSaveBk);
+	::SelectObject(hdcSrc, hbmSrcT);
+	::SelectObject(hdcDest, hbmDestT);
+
+	::DeleteDC(hdcSrc);
+	::DeleteDC(hdcDest);
+
+	return hMask;
+} // End of CreateBitmapMask
+
+//
+// Parameters:
+//		[IN]	bHasTitle
+//				TRUE if the button has a text
+//		[IN]	rpItem
+//				A pointer to a RECT structure indicating the allowed paint area
+//		[IN/OUT]rpTitle
+//				A pointer to a CRect object indicating the paint area reserved for the
+//				text. This structure will be modified if necessary.
+//		[IN]	bIsPressed
+//				TRUE if the button is currently pressed
+//		[IN]	dwWidth
+//				Width of the image (icon or bitmap)
+//		[IN]	dwHeight
+//				Height of the image (icon or bitmap)
+//		[OUT]	rpImage
+//				A pointer to a CRect object that will receive the area available to the image
+//
+void CButtonST::PrepareImageRect(BOOL bHasTitle, RECT* rpItem, CRect* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, CRect* rpImage)
+{
+	CRect rBtn;
+
+	rpImage->CopyRect(rpItem);
+
+	switch (m_byAlign)
+	{
+		case ST_ALIGN_HORIZ:
+			if (bHasTitle == FALSE)
+			{
+				// Center image horizontally
+				rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+			}
+			else
+			{
+				// Image must be placed just inside the focus rect
+				rpImage->left += m_ptImageOrg.x;  
+				rpTitle->left += dwWidth + m_ptImageOrg.x;
+			}
+			// Center image vertically
+			rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
+			break;
+
+		case ST_ALIGN_HORIZ_RIGHT:
+			GetClientRect(&rBtn);
+			if (bHasTitle == FALSE)
+			{
+				// Center image horizontally
+				rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+			}
+			else
+			{
+				// Image must be placed just inside the focus rect
+				rpTitle->right = rpTitle->Width() - dwWidth - m_ptImageOrg.x;
+				rpTitle->left = m_ptImageOrg.x;
+				rpImage->left = rBtn.right - dwWidth - m_ptImageOrg.x;
+				// Center image vertically
+				rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
+			}
+			break;
+		
+		case ST_ALIGN_VERT:
+			// Center image horizontally
+			rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+			if (bHasTitle == FALSE)
+			{
+				// Center image vertically
+				rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);           
+			}
+			else
+			{
+				rpImage->top = m_ptImageOrg.y;
+				rpTitle->top += dwHeight;
+			}
+			break;
+
+		case ST_ALIGN_OVERLAP:
+			break;
+	} // switch
+    
+	// If button is pressed then press image also
+	if (bIsPressed && m_bIsCheckBox == FALSE)
+		rpImage->OffsetRect(m_ptPressedOffset.x, m_ptPressedOffset.y);
+} // End of PrepareImageRect
+
+void CButtonST::DrawTheIcon(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
+{
+	BYTE		byIndex		= 0;
+
+	// Select the icon to use
+	if ((m_bIsCheckBox && bIsPressed) || (!m_bIsCheckBox && (bIsPressed || m_bMouseOnButton)))
+		byIndex = 0;
+	else
+		byIndex = (m_csIcons[1].hIcon == NULL ? 0 : 1);
+
+	CRect	rImage;
+	PrepareImageRect(bHasTitle, rpItem, rpCaption, bIsPressed, m_csIcons[byIndex].dwWidth, m_csIcons[byIndex].dwHeight, &rImage);
+
+	// Ole'!
+	pDC->DrawState(	rImage.TopLeft(),
+					rImage.Size(), 
+					m_csIcons[byIndex].hIcon,
+					(bIsDisabled ? DSS_DISABLED : DSS_NORMAL), 
+					(CBrush*)NULL);
+} // End of DrawTheIcon
+
+void CButtonST::DrawTheBitmap(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
+{
+	HDC			hdcBmpMem	= NULL;
+	HBITMAP		hbmOldBmp	= NULL;
+	HDC			hdcMem		= NULL;
+	HBITMAP		hbmT		= NULL;
+
+	BYTE		byIndex		= 0;
+
+	// Select the bitmap to use
+	if ((m_bIsCheckBox && bIsPressed) || (!m_bIsCheckBox && (bIsPressed || m_bMouseOnButton)))
+		byIndex = 0;
+	else
+		byIndex = (m_csBitmaps[1].hBitmap == NULL ? 0 : 1);
+
+	CRect	rImage;
+	PrepareImageRect(bHasTitle, rpItem, rpCaption, bIsPressed, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, &rImage);
+
+	hdcBmpMem = ::CreateCompatibleDC(pDC->m_hDC);
+
+	hbmOldBmp = (HBITMAP)::SelectObject(hdcBmpMem, m_csBitmaps[byIndex].hBitmap);
+
+	hdcMem = ::CreateCompatibleDC(NULL);
+
+	hbmT = (HBITMAP)::SelectObject(hdcMem, m_csBitmaps[byIndex].hMask);
+
+	if (bIsDisabled && m_bShowDisabledBitmap)
+	{
+		HDC		hDC = NULL;
+		HBITMAP	hBitmap = NULL;
+
+		hDC = ::CreateCompatibleDC(pDC->m_hDC);
+		hBitmap = ::CreateCompatibleBitmap(pDC->m_hDC, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight);
+		HBITMAP	hOldBmp2 = (HBITMAP)::SelectObject(hDC, hBitmap);
+
+		RECT	rRect;
+		rRect.left = 0;
+		rRect.top = 0;
+		rRect.right = rImage.right + 1;
+		rRect.bottom = rImage.bottom + 1;
+		::FillRect(hDC, &rRect, (HBRUSH)RGB(255, 255, 255));
+
+		COLORREF crOldColor = ::SetBkColor(hDC, RGB(255,255,255));
+
+		::BitBlt(hDC, 0, 0, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcMem, 0, 0, SRCAND);
+		::BitBlt(hDC, 0, 0, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcBmpMem, 0, 0, SRCPAINT);
+
+		::SetBkColor(hDC, crOldColor);
+		::SelectObject(hDC, hOldBmp2);
+		::DeleteDC(hDC);
+
+		pDC->DrawState(	CPoint(rImage.left/*+1*/, rImage.top), 
+						CSize(m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight), 
+						hBitmap, DST_BITMAP | DSS_DISABLED);
+
+		::DeleteObject(hBitmap);
+	} // if
+	else
+	{
+		::BitBlt(pDC->m_hDC, rImage.left, rImage.top, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcMem, 0, 0, SRCAND);
+
+		::BitBlt(pDC->m_hDC, rImage.left, rImage.top, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcBmpMem, 0, 0, SRCPAINT);
+	} // else
+
+	::SelectObject(hdcMem, hbmT);
+	::DeleteDC(hdcMem);
+
+	::SelectObject(hdcBmpMem, hbmOldBmp);
+	::DeleteDC(hdcBmpMem);
+} // End of DrawTheBitmap
+
+void CButtonST::DrawTheText(CDC* pDC, LPCTSTR lpszText, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
+{
+	// Draw the button's title
+	// If button is pressed then "press" title also
+	if (m_bIsPressed && m_bIsCheckBox == FALSE)
+		rpCaption->OffsetRect(m_ptPressedOffset.x, m_ptPressedOffset.y);
+
+	// ONLY FOR DEBUG 
+	//CBrush brBtnShadow(RGB(255, 0, 0));
+	//pDC->FrameRect(rCaption, &brBtnShadow);
+
+	// Center text
+	CRect centerRect = rpCaption;
+	// ÉèÖÃ×ÖÌå;
+	CFont *pOldFont = pDC->SelectObject(&m_font);
+	pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER | DT_CALCRECT);
+	rpCaption->OffsetRect((centerRect.Width() - rpCaption->Width())/2, (centerRect.Height() - rpCaption->Height())/2);
+	/* RFU
+	rpCaption->OffsetRect(0, (centerRect.Height() - rpCaption->Height())/2);
+	rpCaption->OffsetRect((centerRect.Width() - rpCaption->Width())-4, (centerRect.Height() - rpCaption->Height())/2);
+	*/
+
+	pDC->SetBkMode(TRANSPARENT);
+	/*
+	pDC->DrawState(rCaption->TopLeft(), rCaption->Size(), (LPCTSTR)sTitle, (bIsDisabled ? DSS_DISABLED : DSS_NORMAL), 
+					TRUE, 0, (CBrush*)NULL);
+	*/
+	if (m_bIsDisabled)
+	{
+		rpCaption->OffsetRect(1, 1);
+		pDC->SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
+		pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+		rpCaption->OffsetRect(-1, -1);
+		pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
+		pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+	} // if
+	else
+	{
+		if (m_bMouseOnButton || m_bIsPressed) 
+		{
+			pDC->SetTextColor(m_crColors[BTNST_COLOR_FG_IN]);
+			pDC->SetBkColor(m_crColors[BTNST_COLOR_BK_IN]);
+		} // if
+		else 
+		{
+			if (m_bIsFocused)
+			{ 
+				pDC->SetTextColor(m_crColors[BTNST_COLOR_FG_FOCUS]); 
+				pDC->SetBkColor(m_crColors[BTNST_COLOR_BK_FOCUS]); 
+			} // if
+			else 
+			{
+				pDC->SetTextColor(m_crColors[BTNST_COLOR_FG_OUT]); 
+				pDC->SetBkColor(m_crColors[BTNST_COLOR_BK_OUT]); 
+			} // else
+		} // else
+		pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+	} // if
+
+	pDC->SelectObject(pOldFont);
+} // End of DrawTheText
+
+// This function creates a grayscale bitmap starting from a given bitmap.
+// The resulting bitmap will have the same size of the original one.
+//
+// Parameters:
+//		[IN]	hBitmap
+//				Handle to the original bitmap.
+//		[IN]	dwWidth
+//				Specifies the bitmap width, in pixels.
+//		[IN]	dwHeight
+//				Specifies the bitmap height, in pixels.
+//		[IN]	crTrans
+//				Color to be used as transparent color. This color will be left unchanged.
+//
+// Return value:
+//		If the function succeeds, the return value is the handle to the newly created
+//		grayscale bitmap.
+//		If the function fails, the return value is NULL.
+//
+HBITMAP CButtonST::CreateGrayscaleBitmap(HBITMAP hBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTrans)
+{
+	HBITMAP		hGrayBitmap = NULL;
+	HDC			hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+	HBITMAP		hOldBmp1 = NULL, hOldBmp2 = NULL;
+
+	hMainDC = ::GetDC(NULL);
+	if (hMainDC == NULL)	return NULL;
+	hMemDC1 = ::CreateCompatibleDC(hMainDC);
+	if (hMemDC1 == NULL)
+	{
+		::ReleaseDC(NULL, hMainDC);
+		return NULL;
+	} // if
+	hMemDC2 = ::CreateCompatibleDC(hMainDC);
+	if (hMemDC2 == NULL)
+	{
+		::DeleteDC(hMemDC1);
+		::ReleaseDC(NULL, hMainDC);
+		return NULL;
+	} // if
+
+	hGrayBitmap = ::CreateCompatibleBitmap(hMainDC, dwWidth, dwHeight);
+	if (hGrayBitmap)
+	{
+		hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, hGrayBitmap);
+		hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, hBitmap);
+
+		//::BitBlt(hMemDC1, 0, 0, dwWidth, dwHeight, hMemDC2, 0, 0, SRCCOPY);
+
+		DWORD		dwLoopY = 0, dwLoopX = 0;
+		COLORREF	crPixel = 0;
+		BYTE		byNewPixel = 0;
+
+		for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+		{
+			for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+			{
+				crPixel = ::GetPixel(hMemDC2, dwLoopX, dwLoopY);
+				byNewPixel = (BYTE)((GetRValue(crPixel) * 0.299) + (GetGValue(crPixel) * 0.587) + (GetBValue(crPixel) * 0.114));
+
+				if (crPixel != crTrans)
+					::SetPixel(hMemDC1, dwLoopX, dwLoopY, RGB(byNewPixel, byNewPixel, byNewPixel));
+				else
+					::SetPixel(hMemDC1, dwLoopX, dwLoopY, crPixel);
+			} // for
+		} // for
+
+		::SelectObject(hMemDC1, hOldBmp1);
+		::SelectObject(hMemDC2, hOldBmp2);
+	} // if
+
+	::DeleteDC(hMemDC1);
+	::DeleteDC(hMemDC2);
+	::ReleaseDC(NULL, hMainDC);
+
+	return hGrayBitmap;
+} // End of CreateGrayscaleBitmap
+
+// This function creates a bitmap that is 25% darker than the original.
+// The resulting bitmap will have the same size of the original one.
+//
+// Parameters:
+//		[IN]	hBitmap
+//				Handle to the original bitmap.
+//		[IN]	dwWidth
+//				Specifies the bitmap width, in pixels.
+//		[IN]	dwHeight
+//				Specifies the bitmap height, in pixels.
+//		[IN]	crTrans
+//				Color to be used as transparent color. This color will be left unchanged.
+//
+// Return value:
+//		If the function succeeds, the return value is the handle to the newly created
+//		darker bitmap.
+//		If the function fails, the return value is NULL.
+//
+HBITMAP CButtonST::CreateDarkerBitmap(HBITMAP hBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTrans)
+{
+	HBITMAP		hGrayBitmap = NULL;
+	HDC			hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+	HBITMAP		hOldBmp1 = NULL, hOldBmp2 = NULL;
+
+	hMainDC = ::GetDC(NULL);
+	if (hMainDC == NULL)	return NULL;
+	hMemDC1 = ::CreateCompatibleDC(hMainDC);
+	if (hMemDC1 == NULL)
+	{
+		::ReleaseDC(NULL, hMainDC);
+		return NULL;
+	} // if
+	hMemDC2 = ::CreateCompatibleDC(hMainDC);
+	if (hMemDC2 == NULL)
+	{
+		::DeleteDC(hMemDC1);
+		::ReleaseDC(NULL, hMainDC);
+		return NULL;
+	} // if
+
+	hGrayBitmap = ::CreateCompatibleBitmap(hMainDC, dwWidth, dwHeight);
+	if (hGrayBitmap)
+	{
+		hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, hGrayBitmap);
+		hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, hBitmap);
+
+		//::BitBlt(hMemDC1, 0, 0, dwWidth, dwHeight, hMemDC2, 0, 0, SRCCOPY);
+
+		DWORD		dwLoopY = 0, dwLoopX = 0;
+		COLORREF	crPixel = 0;
+
+		for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+		{
+			for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+			{
+				crPixel = ::GetPixel(hMemDC2, dwLoopX, dwLoopY);
+
+				if (crPixel != crTrans)
+					::SetPixel(hMemDC1, dwLoopX, dwLoopY, DarkenColor(crPixel, 0.25));
+				else
+					::SetPixel(hMemDC1, dwLoopX, dwLoopY, crPixel);
+			} // for
+		} // for
+
+		::SelectObject(hMemDC1, hOldBmp1);
+		::SelectObject(hMemDC2, hOldBmp2);
+	} // if
+
+	::DeleteDC(hMemDC1);
+	::DeleteDC(hMemDC2);
+	::ReleaseDC(NULL, hMainDC);
+
+	return hGrayBitmap;
+} // End of CreateDarkerBitmap
+
+// This function creates a grayscale icon starting from a given icon.
+// The resulting icon will have the same size of the original one.
+//
+// Parameters:
+//		[IN]	hIcon
+//				Handle to the original icon.
+//
+// Return value:
+//		If the function succeeds, the return value is the handle to the newly created
+//		grayscale icon.
+//		If the function fails, the return value is NULL.
+//
+// Updates:
+//		26/Nov/2002	Restored 1 BitBlt operation
+//		03/May/2002	Removed dependancy from m_hWnd
+//					Removed 1 BitBlt operation
+//
+HICON CButtonST::CreateGrayscaleIcon(HICON hIcon)
+{
+	HICON		hGrayIcon = NULL;
+	HDC			hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+	BITMAP		bmp;
+	HBITMAP		hOldBmp1 = NULL, hOldBmp2 = NULL;
+	ICONINFO	csII, csGrayII;
+	BOOL		bRetValue = FALSE;
+
+	bRetValue = ::GetIconInfo(hIcon, &csII);
+	if (bRetValue == FALSE)	return NULL;
+
+	hMainDC = ::GetDC(NULL);
+	hMemDC1 = ::CreateCompatibleDC(hMainDC);
+	hMemDC2 = ::CreateCompatibleDC(hMainDC);
+	if (hMainDC == NULL || hMemDC1 == NULL || hMemDC2 == NULL)	return NULL;
+  
+	if (::GetObject(csII.hbmColor, sizeof(BITMAP), &bmp))
+	{
+		DWORD	dwWidth = csII.xHotspot*2;
+		DWORD	dwHeight = csII.yHotspot*2;
+
+		csGrayII.hbmColor = ::CreateBitmap(dwWidth, dwHeight, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
+		if (csGrayII.hbmColor)
+		{
+			hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, csII.hbmColor);
+			hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, csGrayII.hbmColor);
+
+			//::BitBlt(hMemDC2, 0, 0, dwWidth, dwHeight, hMemDC1, 0, 0, SRCCOPY);
+
+			DWORD		dwLoopY = 0, dwLoopX = 0;
+			COLORREF	crPixel = 0;
+			BYTE		byNewPixel = 0;
+
+			for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+			{
+				for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+				{
+					crPixel = ::GetPixel(hMemDC1, dwLoopX, dwLoopY);
+					byNewPixel = (BYTE)((GetRValue(crPixel) * 0.299) + (GetGValue(crPixel) * 0.587) + (GetBValue(crPixel) * 0.114));
+
+					if (crPixel)	
+						::SetPixel(hMemDC2, dwLoopX, dwLoopY, RGB(byNewPixel, byNewPixel, byNewPixel));
+					else
+						::SetPixel(hMemDC2, dwLoopX, dwLoopY, crPixel);
+				} // for
+			} // for
+
+			::SelectObject(hMemDC1, hOldBmp1);
+			::SelectObject(hMemDC2, hOldBmp2);
+
+			csGrayII.hbmMask = csII.hbmMask;
+
+			csGrayII.fIcon = TRUE;
+			hGrayIcon = ::CreateIconIndirect(&csGrayII);
+		} // if
+
+		::DeleteObject(csGrayII.hbmColor);
+		//::DeleteObject(csGrayII.hbmMask);
+	} // if
+
+	::DeleteObject(csII.hbmColor);
+	::DeleteObject(csII.hbmMask);
+	::DeleteDC(hMemDC1);
+	::DeleteDC(hMemDC2);
+	::ReleaseDC(NULL, hMainDC);
+
+	return hGrayIcon;
+} // End of CreateGrayscaleIcon
+
+// This function creates a icon that is 25% darker than the original.
+// The resulting icon will have the same size of the original one.
+//
+// Parameters:
+//		[IN]	hIcon
+//				Handle to the original icon.
+//
+// Return value:
+//		If the function succeeds, the return value is the handle to the newly created
+//		darker icon.
+//		If the function fails, the return value is NULL.
+//
+HICON CButtonST::CreateDarkerIcon(HICON hIcon)
+{
+	HICON		hGrayIcon = NULL;
+	HDC			hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+	BITMAP		bmp;
+	HBITMAP		hOldBmp1 = NULL, hOldBmp2 = NULL;
+	ICONINFO	csII, csGrayII;
+	BOOL		bRetValue = FALSE;
+
+	bRetValue = ::GetIconInfo(hIcon, &csII);
+	if (bRetValue == FALSE)	return NULL;
+
+	hMainDC = ::GetDC(NULL);
+	hMemDC1 = ::CreateCompatibleDC(hMainDC);
+	hMemDC2 = ::CreateCompatibleDC(hMainDC);
+	if (hMainDC == NULL || hMemDC1 == NULL || hMemDC2 == NULL)	return NULL;
+  
+	if (::GetObject(csII.hbmColor, sizeof(BITMAP), &bmp))
+	{
+		DWORD	dwWidth = csII.xHotspot*2;
+		DWORD	dwHeight = csII.yHotspot*2;
+
+		csGrayII.hbmColor = ::CreateBitmap(dwWidth, dwHeight, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
+		if (csGrayII.hbmColor)
+		{
+			hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, csII.hbmColor);
+			hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, csGrayII.hbmColor);
+
+			//::BitBlt(hMemDC2, 0, 0, dwWidth, dwHeight, hMemDC1, 0, 0, SRCCOPY);
+
+			DWORD		dwLoopY = 0, dwLoopX = 0;
+			COLORREF	crPixel = 0;
+
+			for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+			{
+				for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+				{
+					crPixel = ::GetPixel(hMemDC1, dwLoopX, dwLoopY);
+
+					if (crPixel)	
+						::SetPixel(hMemDC2, dwLoopX, dwLoopY, DarkenColor(crPixel, 0.25));
+					else
+						::SetPixel(hMemDC2, dwLoopX, dwLoopY, crPixel);
+				} // for
+			} // for
+
+			::SelectObject(hMemDC1, hOldBmp1);
+			::SelectObject(hMemDC2, hOldBmp2);
+
+			csGrayII.hbmMask = csII.hbmMask;
+
+			csGrayII.fIcon = TRUE;
+			hGrayIcon = ::CreateIconIndirect(&csGrayII);
+		} // if
+
+		::DeleteObject(csGrayII.hbmColor);
+		//::DeleteObject(csGrayII.hbmMask);
+	} // if
+
+	::DeleteObject(csII.hbmColor);
+	::DeleteObject(csII.hbmMask);
+	::DeleteDC(hMemDC1);
+	::DeleteDC(hMemDC2);
+	::ReleaseDC(NULL, hMainDC);
+
+	return hGrayIcon;
+} // End of CreateDarkerIcon
+
+COLORREF CButtonST::DarkenColor(COLORREF crColor, double dFactor)
+{
+	if (dFactor > 0.0 && dFactor <= 1.0)
+	{
+		BYTE red,green,blue,lightred,lightgreen,lightblue;
+		red = GetRValue(crColor);
+		green = GetGValue(crColor);
+		blue = GetBValue(crColor);
+		lightred = (BYTE)(red-(dFactor * red));
+		lightgreen = (BYTE)(green-(dFactor * green));
+		lightblue = (BYTE)(blue-(dFactor * blue));
+		crColor = RGB(lightred,lightgreen,lightblue);
+	} // if
+
+	return crColor;
+} // End of DarkenColor
+
+// This function assigns icons to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	nIconIn
+//				ID number of the icon resource to show when the mouse is over the button.
+//				Pass NULL to remove any icon from the button.
+//		[IN]	nCxDesiredIn
+//				Specifies the width, in pixels, of the icon to load.
+//		[IN]	nCyDesiredIn
+//				Specifies the height, in pixels, of the icon to load.
+//		[IN]	nIconOut
+//				ID number of the icon resource to show when the mouse is outside the button.
+//				Can be NULL.
+//				If this parameter is the special value BTNST_AUTO_GRAY (cast to int) the second
+//				icon will be automatically created starting from nIconIn and converted to grayscale.
+//				If this parameter is the special value BTNST_AUTO_DARKER (cast to int) the second
+//				icon will be automatically created 25% darker starting from nIconIn.
+//		[IN]	nCxDesiredOut
+//				Specifies the width, in pixels, of the icon to load.
+//		[IN]	nCyDesiredOut
+//				Specifies the height, in pixels, of the icon to load.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+DWORD CButtonST::SetIcon(int nIconIn, int nCxDesiredIn, int nCyDesiredIn, int nIconOut, int nCxDesiredOut, int nCyDesiredOut)
+{
+	HICON		hIconIn			= NULL;
+	HICON		hIconOut		= NULL;
+	HINSTANCE	hInstResource	= NULL;
+
+	// Find correct resource handle
+	hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nIconIn), RT_GROUP_ICON);
+
+	// Set icon when the mouse is IN the button
+	hIconIn = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIconIn), IMAGE_ICON, nCxDesiredIn, nCyDesiredIn, 0);
+
+  	// Set icon when the mouse is OUT the button
+	switch (nIconOut)
+	{
+		case NULL:
+			break;
+		case (int)BTNST_AUTO_GRAY:
+			hIconOut = BTNST_AUTO_GRAY;
+			break;
+		case (int)BTNST_AUTO_DARKER:
+			hIconOut = BTNST_AUTO_DARKER;
+			break;
+		default:
+			hIconOut = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIconOut), IMAGE_ICON, nCxDesiredOut, nCyDesiredOut, 0);
+			break;
+	} // switch
+
+	return SetIcon(hIconIn, hIconOut);
+} // End of SetIcon
+
+// This function assigns icons to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	nIconIn
+//				ID number of the icon resource to show when the mouse is over the button.
+//				Pass NULL to remove any icon from the button.
+//		[IN]	nIconOut
+//				ID number of the icon resource to show when the mouse is outside the button.
+//				Can be NULL.
+//				If this parameter is the special value BTNST_AUTO_GRAY (cast to int) the second
+//				icon will be automatically created starting from nIconIn and converted to grayscale.
+//				If this parameter is the special value BTNST_AUTO_DARKER (cast to int) the second
+//				icon will be automatically created 25% darker starting from nIconIn.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+DWORD CButtonST::SetIcon(int nIconIn, int nIconOut)
+{
+	return SetIcon(nIconIn, 0, 0, nIconOut, 0, 0);
+} // End of SetIcon
+
+// This function assigns icons to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	hIconIn
+//				Handle fo the icon to show when the mouse is over the button.
+//				Pass NULL to remove any icon from the button.
+//		[IN]	hIconOut
+//				Handle to the icon to show when the mouse is outside the button.
+//				Can be NULL.
+//				If this parameter is the special value BTNST_AUTO_GRAY the second
+//				icon will be automatically created starting from hIconIn and converted to grayscale.
+//				If this parameter is the special value BTNST_AUTO_DARKER the second
+//				icon will be automatically created 25% darker starting from hIconIn.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+DWORD CButtonST::SetIcon(HICON hIconIn, HICON hIconOut)
+{
+	BOOL		bRetValue;
+	ICONINFO	ii;
+
+	// Free any loaded resource
+	FreeResources();
+
+	if (hIconIn)
+	{
+		// Icon when mouse over button?
+		m_csIcons[0].hIcon = hIconIn;
+		// Get icon dimension
+		::ZeroMemory(&ii, sizeof(ICONINFO));
+		bRetValue = ::GetIconInfo(hIconIn, &ii);
+		if (bRetValue == FALSE)
+		{
+			FreeResources();
+			return BTNST_INVALIDRESOURCE;
+		} // if
+
+		m_csIcons[0].dwWidth	= (DWORD)(ii.xHotspot * 2);
+		m_csIcons[0].dwHeight	= (DWORD)(ii.yHotspot * 2);
+		::DeleteObject(ii.hbmMask);
+		::DeleteObject(ii.hbmColor);
+
+		// Icon when mouse outside button?
+		if (hIconOut)
+		{
+			switch ((int)hIconOut)
+			{
+				case (int)BTNST_AUTO_GRAY:
+					hIconOut = CreateGrayscaleIcon(hIconIn);
+					break;
+				case (int)BTNST_AUTO_DARKER:
+					hIconOut = CreateDarkerIcon(hIconIn);
+					break;
+			} // switch
+
+			m_csIcons[1].hIcon = hIconOut;
+			// Get icon dimension
+			::ZeroMemory(&ii, sizeof(ICONINFO));
+			bRetValue = ::GetIconInfo(hIconOut, &ii);
+			if (bRetValue == FALSE)
+			{
+				FreeResources();
+				return BTNST_INVALIDRESOURCE;
+			} // if
+
+			m_csIcons[1].dwWidth	= (DWORD)(ii.xHotspot * 2);
+			m_csIcons[1].dwHeight	= (DWORD)(ii.yHotspot * 2);
+			::DeleteObject(ii.hbmMask);
+			::DeleteObject(ii.hbmColor);
+		} // if
+	} // if
+
+	Invalidate();
+
+	return BTNST_OK;
+} // End of SetIcon
+
+// This function assigns bitmaps to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	nBitmapIn
+//				ID number of the bitmap resource to show when the mouse is over the button.
+//				Pass NULL to remove any bitmap from the button.
+//		[IN]	crTransColorIn
+//				Color (inside nBitmapIn) to be used as transparent color.
+//		[IN]	nBitmapOut
+//				ID number of the bitmap resource to show when the mouse is outside the button.
+//				Can be NULL.
+//		[IN]	crTransColorOut
+//				Color (inside nBitmapOut) to be used as transparent color.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//		BTNST_FAILEDMASK
+//			Failed creating mask bitmap.
+//
+DWORD CButtonST::SetBitmaps(int nBitmapIn, COLORREF crTransColorIn, int nBitmapOut, COLORREF crTransColorOut)
+{
+	HBITMAP		hBitmapIn		= NULL;
+	HBITMAP		hBitmapOut		= NULL;
+	HINSTANCE	hInstResource	= NULL;
+	
+	// Find correct resource handle
+	hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nBitmapIn), RT_BITMAP);
+
+	// Load bitmap In
+	hBitmapIn = (HBITMAP)::LoadImage(hInstResource, MAKEINTRESOURCE(nBitmapIn), IMAGE_BITMAP, 0, 0, 0);
+
+	// Load bitmap Out
+	switch (nBitmapOut)
+	{
+		case NULL:
+			break;
+		case (int)BTNST_AUTO_GRAY:
+			hBitmapOut = (HBITMAP)BTNST_AUTO_GRAY;
+			break;
+		case (int)BTNST_AUTO_DARKER:
+			hBitmapOut = (HBITMAP)BTNST_AUTO_DARKER;
+			break;
+		default:
+			hBitmapOut = (HBITMAP)::LoadImage(hInstResource, MAKEINTRESOURCE(nBitmapOut), IMAGE_BITMAP, 0, 0, 0);
+			break;
+	} // if
+
+	return SetBitmaps(hBitmapIn, crTransColorIn, hBitmapOut, crTransColorOut);
+} // End of SetBitmaps
+
+// This function assigns bitmaps to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	hBitmapIn
+//				Handle fo the bitmap to show when the mouse is over the button.
+//				Pass NULL to remove any bitmap from the button.
+//		[IN]	crTransColorIn
+//				Color (inside hBitmapIn) to be used as transparent color.
+//		[IN]	hBitmapOut
+//				Handle to the bitmap to show when the mouse is outside the button.
+//				Can be NULL.
+//		[IN]	crTransColorOut
+//				Color (inside hBitmapOut) to be used as transparent color.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//		BTNST_FAILEDMASK
+//			Failed creating mask bitmap.
+//
+DWORD CButtonST::SetBitmaps(HBITMAP hBitmapIn, COLORREF crTransColorIn, HBITMAP hBitmapOut, COLORREF crTransColorOut)
+{
+	int		nRetValue = 0;
+	BITMAP	csBitmapSize;
+
+	// Free any loaded resource
+	FreeResources();
+
+	if (hBitmapIn)
+	{
+		m_csBitmaps[0].hBitmap = hBitmapIn;
+		m_csBitmaps[0].crTransparent = crTransColorIn;
+		// Get bitmap size
+		nRetValue = ::GetObject(hBitmapIn, sizeof(csBitmapSize), &csBitmapSize);
+		if (nRetValue == 0)
+		{
+			FreeResources();
+			return BTNST_INVALIDRESOURCE;
+		} // if
+		m_csBitmaps[0].dwWidth = (DWORD)csBitmapSize.bmWidth;
+		m_csBitmaps[0].dwHeight = (DWORD)csBitmapSize.bmHeight;
+
+		// Create grayscale/darker bitmap BEFORE mask (of hBitmapIn)
+		switch ((int)hBitmapOut)
+		{
+			case (int)BTNST_AUTO_GRAY:
+				hBitmapOut = CreateGrayscaleBitmap(hBitmapIn, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight, crTransColorIn);
+				m_csBitmaps[1].hBitmap = hBitmapOut;
+				crTransColorOut = crTransColorIn;
+				break;
+			case (int)BTNST_AUTO_DARKER:
+				hBitmapOut = CreateDarkerBitmap(hBitmapIn, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight, crTransColorIn);
+				m_csBitmaps[1].hBitmap = hBitmapOut;
+				crTransColorOut = crTransColorIn;
+				break;
+		} // switch
+
+		// Create mask for bitmap In
+		m_csBitmaps[0].hMask = CreateBitmapMask(hBitmapIn, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight, crTransColorIn);
+		if (m_csBitmaps[0].hMask == NULL)
+		{
+			FreeResources();
+			return BTNST_FAILEDMASK;
+		} // if
+
+		if (hBitmapOut)
+		{
+			m_csBitmaps[1].hBitmap = hBitmapOut;
+			m_csBitmaps[1].crTransparent = crTransColorOut;
+			// Get bitmap size
+			nRetValue = ::GetObject(hBitmapOut, sizeof(csBitmapSize), &csBitmapSize);
+			if (nRetValue == 0)
+			{
+				FreeResources();
+				return BTNST_INVALIDRESOURCE;
+			} // if
+			m_csBitmaps[1].dwWidth = (DWORD)csBitmapSize.bmWidth;
+			m_csBitmaps[1].dwHeight = (DWORD)csBitmapSize.bmHeight;
+
+			// Create mask for bitmap Out
+			m_csBitmaps[1].hMask = CreateBitmapMask(hBitmapOut, m_csBitmaps[1].dwWidth, m_csBitmaps[1].dwHeight, crTransColorOut);
+			if (m_csBitmaps[1].hMask == NULL)
+			{
+				FreeResources();
+				return BTNST_FAILEDMASK;
+			} // if
+		} // if
+	} // if
+
+	Invalidate();
+
+	return BTNST_OK;
+} // End of SetBitmaps
+
+// This functions sets the button to have a standard or flat style.
+//
+// Parameters:
+//		[IN]	bFlat
+//				If TRUE the button will have a flat style, else
+//				will have a standard style.
+//				By default, CButtonST buttons are flat.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetFlat(BOOL bFlat, BOOL bRepaint)
+{
+	m_bIsFlat = bFlat;
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of SetFlat
+
+// This function sets the alignment type between icon/bitmap and text.
+//
+// Parameters:
+//		[IN]	byAlign
+//				Alignment type. Can be one of the following values:
+//				ST_ALIGN_HORIZ			Icon/bitmap on the left, text on the right
+//				ST_ALIGN_VERT			Icon/bitmap on the top, text on the bottom
+//				ST_ALIGN_HORIZ_RIGHT	Icon/bitmap on the right, text on the left
+//				ST_ALIGN_OVERLAP		Icon/bitmap on the same space as text
+//				By default, CButtonST buttons have ST_ALIGN_HORIZ alignment.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDALIGN
+//			Alignment type not supported.
+//
+DWORD CButtonST::SetAlign(BYTE byAlign, BOOL bRepaint)
+{
+	switch (byAlign)
+	{    
+		case ST_ALIGN_HORIZ:
+		case ST_ALIGN_HORIZ_RIGHT:
+		case ST_ALIGN_VERT:
+		case ST_ALIGN_OVERLAP:
+			m_byAlign = byAlign;
+			if (bRepaint)	Invalidate();
+			return BTNST_OK;
+			break;
+	} // switch
+
+	return BTNST_INVALIDALIGN;
+} // End of SetAlign
+
+// This function sets the pressed style.
+//
+// Parameters:
+//		[IN]	byStyle
+//				Pressed style. Can be one of the following values:
+//				BTNST_PRESSED_LEFTRIGHT		Pressed style from left to right (as usual)
+//				BTNST_PRESSED_TOPBOTTOM		Pressed style from top to bottom
+//				By default, CButtonST buttons have BTNST_PRESSED_LEFTRIGHT style.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDPRESSEDSTYLE
+//			Pressed style not supported.
+//
+DWORD CButtonST::SetPressedStyle(BYTE byStyle, BOOL bRepaint)
+{
+	switch (byStyle)
+	{
+		case BTNST_PRESSED_LEFTRIGHT:
+			m_ptPressedOffset.x = 1;
+			m_ptPressedOffset.y = 1;
+			break;
+		case BTNST_PRESSED_TOPBOTTOM:
+			m_ptPressedOffset.x = 0;
+			m_ptPressedOffset.y = 2;
+			break;
+		default:
+			return BTNST_INVALIDPRESSEDSTYLE;
+	} // switch
+
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of SetPressedStyle
+
+// This function sets the state of the checkbox.
+// If the button is not a checkbox, this function has no meaning.
+//
+// Parameters:
+//		[IN]	nCheck
+//				1 to check the checkbox.
+//				0 to un-check the checkbox.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetCheck(int nCheck, BOOL bRepaint)
+{
+	if (m_bIsCheckBox)
+	{
+		if (nCheck == 0) m_nCheck = 0;
+		else m_nCheck = 1;
+
+		if (bRepaint) Invalidate();
+	} // if
+
+	return BTNST_OK;
+} // End of SetCheck
+
+// This function returns the current state of the checkbox.
+// If the button is not a checkbox, this function has no meaning.
+//
+// Return value:
+//		The current state of the checkbox.
+//			1 if checked.
+//			0 if not checked or the button is not a checkbox.
+//
+int CButtonST::GetCheck()
+{
+	return m_nCheck;
+} // End of GetCheck
+
+// This function sets all colors to a default value.
+//
+// Parameters:
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetDefaultColors(BOOL bRepaint)
+{
+	m_crColors[BTNST_COLOR_BK_IN]		= ::GetSysColor(COLOR_BTNFACE);
+	m_crColors[BTNST_COLOR_FG_IN]		= ::GetSysColor(COLOR_BTNTEXT);
+	m_crColors[BTNST_COLOR_BK_OUT]		= ::GetSysColor(COLOR_BTNFACE);
+	m_crColors[BTNST_COLOR_FG_OUT]		= ::GetSysColor(COLOR_BTNTEXT);
+	m_crColors[BTNST_COLOR_BK_FOCUS]	= ::GetSysColor(COLOR_BTNFACE);
+	m_crColors[BTNST_COLOR_FG_FOCUS]	= ::GetSysColor(COLOR_BTNTEXT);
+
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of SetDefaultColors
+
+// This function sets the color to use for a particular state.
+//
+// Parameters:
+//		[IN]	byColorIndex
+//				Index of the color to set. Can be one of the following values:
+//				BTNST_COLOR_BK_IN		Background color when mouse is over the button
+//				BTNST_COLOR_FG_IN		Text color when mouse is over the button
+//				BTNST_COLOR_BK_OUT		Background color when mouse is outside the button
+//				BTNST_COLOR_FG_OUT		Text color when mouse is outside the button
+//				BTNST_COLOR_BK_FOCUS	Background color when the button is focused
+//				BTNST_COLOR_FG_FOCUS	Text color when the button is focused
+//		[IN]	crColor
+//				New color.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDINDEX
+//			Invalid color index.
+//
+DWORD CButtonST::SetColor(BYTE byColorIndex, COLORREF crColor, BOOL bRepaint)
+{
+	if (byColorIndex >= BTNST_MAX_COLORS)	return BTNST_INVALIDINDEX;
+
+	// Set new color
+	m_crColors[byColorIndex] = crColor;
+
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of SetColor
+
+CButtonST& CButtonST::SetFontSize(int nSize)
+{
+
+	CFont cf;
+	LOGFONT lf;
+
+	cf.CreatePointFont(nSize * 10, m_lf.lfFaceName);
+	cf.GetLogFont(&lf);
+
+	m_lf.lfHeight = lf.lfHeight;
+	m_lf.lfWidth  = lf.lfWidth;
+
+	//	nSize*=-1;
+	//	m_lf.lfHeight = nSize;
+	ReconstructFont();
+	UpdateSurface();
+
+	return *this;
+}
+
+CButtonST& CButtonST::SetFontName(const CString& strFont, BYTE byCharSet /* Default = ANSI_CHARSET */)
+{	
+
+	m_lf.lfCharSet = byCharSet;
+
+	_tcscpy_s(m_lf.lfFaceName,strFont);
+	ReconstructFont();
+	UpdateSurface();
+
+	return *this;
+}
+
+void CButtonST::ReconstructFont()
+{
+	m_font.DeleteObject();
+	BOOL bCreated = m_font.CreateFontIndirect(&m_lf);
+
+	ASSERT(bCreated);
+}
+
+CButtonST& CButtonST::SetFontItalic(BOOL bSet)
+{
+
+	m_lf.lfItalic = bSet;
+	ReconstructFont();
+	UpdateSurface();
+
+	return *this;	
+}
+
+CButtonST& CButtonST::SetFontUnderline(BOOL bSet)
+{	
+	m_lf.lfUnderline = bSet;
+	ReconstructFont();
+	UpdateSurface();
+
+	return *this;
+}
+
+CButtonST& CButtonST::SetFontBold(BOOL bBold)
+{	
+
+	m_lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
+	ReconstructFont();
+	UpdateSurface();
+	return *this;
+}
+
+void CButtonST::UpdateSurface()
+{
+	CRect (rc);
+	GetWindowRect(rc);
+	RedrawWindow();
+
+	GetParent()->ScreenToClient(rc);
+	GetParent()->InvalidateRect(rc,TRUE);
+	GetParent()->UpdateWindow();
+}
+
+// This functions returns the color used for a particular state.
+//
+// Parameters:
+//		[IN]	byColorIndex
+//				Index of the color to get.
+//				See SetColor for the list of available colors.
+//		[OUT]	crpColor
+//				A pointer to a COLORREF that will receive the color.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDINDEX
+//			Invalid color index.
+//
+DWORD CButtonST::GetColor(BYTE byColorIndex, COLORREF* crpColor)
+{
+	if (byColorIndex >= BTNST_MAX_COLORS)	return BTNST_INVALIDINDEX;
+
+	// Get color
+	*crpColor = m_crColors[byColorIndex];
+
+	return BTNST_OK;
+} // End of GetColor
+
+// This function applies an offset to the RGB components of the specified color.
+// This function can be seen as an easy way to make a color darker or lighter than
+// its default value.
+//
+// Parameters:
+//		[IN]	byColorIndex
+//				Index of the color to set.
+//				See SetColor for the list of available colors.
+//		[IN]	shOffsetColor
+//				A short value indicating the offset to apply to the color.
+//				This value must be between -255 and 255.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDINDEX
+//			Invalid color index.
+//		BTNST_BADPARAM
+//			The specified offset is out of range.
+//
+DWORD CButtonST::OffsetColor(BYTE byColorIndex, short shOffset, BOOL bRepaint)
+{
+	BYTE	byRed = 0;
+	BYTE	byGreen = 0;
+	BYTE	byBlue = 0;
+	short	shOffsetR = shOffset;
+	short	shOffsetG = shOffset;
+	short	shOffsetB = shOffset;
+
+	if (byColorIndex >= BTNST_MAX_COLORS)	return BTNST_INVALIDINDEX;
+	if (shOffset < -255 || shOffset > 255)	return BTNST_BADPARAM;
+
+	// Get RGB components of specified color
+	byRed = GetRValue(m_crColors[byColorIndex]);
+	byGreen = GetGValue(m_crColors[byColorIndex]);
+	byBlue = GetBValue(m_crColors[byColorIndex]);
+
+	// Calculate max. allowed real offset
+	if (shOffset > 0)
+	{
+		if (byRed + shOffset > 255)		shOffsetR = 255 - byRed;
+		if (byGreen + shOffset > 255)	shOffsetG = 255 - byGreen;
+		if (byBlue + shOffset > 255)	shOffsetB = 255 - byBlue;
+
+		shOffset = min(min(shOffsetR, shOffsetG), shOffsetB);
+	} // if
+	else
+	{
+		if (byRed + shOffset < 0)		shOffsetR = -byRed;
+		if (byGreen + shOffset < 0)		shOffsetG = -byGreen;
+		if (byBlue + shOffset < 0)		shOffsetB = -byBlue;
+
+		shOffset = max(max(shOffsetR, shOffsetG), shOffsetB);
+	} // else
+
+	// Set new color
+	m_crColors[byColorIndex] = RGB(byRed + shOffset, byGreen + shOffset, byBlue + shOffset);
+
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of OffsetColor
+
+// This function sets the hilight logic for the button.
+// Applies only to flat buttons.
+//
+// Parameters:
+//		[IN]	bAlwaysTrack
+//				If TRUE the button will be hilighted even if the window that owns it, is
+//				not the active window.
+//				If FALSE the button will be hilighted only if the window that owns it,
+//				is the active window.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetAlwaysTrack(BOOL bAlwaysTrack)
+{
+	m_bAlwaysTrack = bAlwaysTrack;
+	return BTNST_OK;
+} // End of SetAlwaysTrack
+
+// This function sets the cursor to be used when the mouse is over the button.
+//
+// Parameters:
+//		[IN]	nCursorId
+//				ID number of the cursor resource.
+//				Pass NULL to remove a previously loaded cursor.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+DWORD CButtonST::SetBtnCursor(int nCursorId, BOOL bRepaint)
+{
+	HINSTANCE	hInstResource = NULL;
+	// Destroy any previous cursor
+	if (m_hCursor)
+	{
+		::DestroyCursor(m_hCursor);
+		m_hCursor = NULL;
+	} // if
+
+	// Load cursor
+	if (nCursorId)
+	{
+		hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nCursorId), RT_GROUP_CURSOR);
+		// Load cursor resource
+		m_hCursor = (HCURSOR)::LoadImage(hInstResource, MAKEINTRESOURCE(nCursorId), IMAGE_CURSOR, 0, 0, 0);
+		// Repaint the button
+		if (bRepaint) Invalidate();
+		// If something wrong
+		if (m_hCursor == NULL) return BTNST_INVALIDRESOURCE;
+	} // if
+
+	return BTNST_OK;
+} // End of SetBtnCursor
+
+// This function sets if the button border must be drawn.
+// Applies only to flat buttons.
+//
+// Parameters:
+//		[IN]	bDrawBorder
+//				If TRUE the border will be drawn.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::DrawBorder(BOOL bDrawBorder, BOOL bRepaint)
+{
+	m_bDrawBorder = bDrawBorder;
+	// Repaint the button
+	if (bRepaint) Invalidate();
+
+	return BTNST_OK;
+} // End of DrawBorder
+
+// This function sets if the focus rectangle must be drawn for flat buttons.
+//
+// Parameters:
+//		[IN]	bDrawFlatFocus
+//				If TRUE the focus rectangle will be drawn also for flat buttons.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::DrawFlatFocus(BOOL bDrawFlatFocus, BOOL bRepaint)
+{
+	m_bDrawFlatFocus = bDrawFlatFocus;
+	// Repaint the button
+	if (bRepaint) Invalidate();
+
+	return BTNST_OK;
+} // End of DrawFlatFocus
+
+void CButtonST::InitToolTip()
+{
+	if (m_ToolTip.m_hWnd == NULL)
+	{
+		// Create ToolTip control
+		m_ToolTip.Create(this, m_dwToolTipStyle);
+		// Create inactive
+		m_ToolTip.Activate(FALSE);
+		// Enable multiline
+		m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, 400);
+		//m_ToolTip.SendMessage(TTM_SETTITLE, TTI_INFO, (LPARAM)_T("Title"));
+	} // if
+} // End of InitToolTip
+
+// This function sets the text to show in the button tooltip.
+//
+// Parameters:
+//		[IN]	nText
+//				ID number of the string resource containing the text to show.
+//		[IN]	bActivate
+//				If TRUE the tooltip will be created active.
+//
+void CButtonST::SetTooltipText(int nText, BOOL bActivate)
+{
+	CString sText;
+
+	// Load string resource
+	sText.LoadString(nText);
+	// If string resource is not empty
+	if (sText.IsEmpty() == FALSE) SetTooltipText((LPCTSTR)sText, bActivate);
+} // End of SetTooltipText
+
+// This function sets the text to show in the button tooltip.
+//
+// Parameters:
+//		[IN]	lpszText
+//				Pointer to a null-terminated string containing the text to show.
+//		[IN]	bActivate
+//				If TRUE the tooltip will be created active.
+//
+void CButtonST::SetTooltipText(LPCTSTR lpszText, BOOL bActivate)
+{
+	// We cannot accept NULL pointer
+	if (lpszText == NULL) return;
+
+	// Initialize ToolTip
+	InitToolTip();
+
+	// If there is no tooltip defined then add it
+	if (m_ToolTip.GetToolCount() == 0)
+	{
+		CRect rectBtn; 
+		GetClientRect(rectBtn);
+		m_ToolTip.AddTool(this, lpszText, rectBtn, 1);
+	} // if
+
+	// Set text for tooltip
+	m_ToolTip.UpdateTipText(lpszText, this, 1);
+	m_ToolTip.Activate(bActivate);
+} // End of SetTooltipText
+
+// This function enables or disables the button tooltip.
+//
+// Parameters:
+//		[IN]	bActivate
+//				If TRUE the tooltip will be activated.
+//
+void CButtonST::ActivateTooltip(BOOL bActivate)
+{
+	// If there is no tooltip then do nothing
+	if (m_ToolTip.GetToolCount() == 0) return;
+
+	// Activate tooltip
+	m_ToolTip.Activate(bActivate);
+} // End of EnableTooltip
+
+// This function enables the tooltip to be displayed using the balloon style.
+// This function must be called before any call to SetTooltipText is made.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::EnableBalloonTooltip()
+{
+	m_dwToolTipStyle |= TTS_BALLOON;
+	return BTNST_OK;
+} // End of EnableBalloonTooltip
+
+// This function returns if the button is the default button.
+//
+// Return value:
+//		TRUE
+//			The button is the default button.
+//		FALSE
+//			The button is not the default button.
+//
+BOOL CButtonST::GetDefault()
+{
+	return m_bIsDefault;
+} // End of GetDefault
+
+// This function enables the transparent mode.
+// Note: this operation is not reversible.
+// DrawTransparent should be called just after the button is created.
+// Do not use trasparent buttons until you really need it (you have a bitmapped
+// background) since each transparent button makes a copy in memory of its background.
+// This may bring unnecessary memory use and execution overload.
+//
+// Parameters:
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+void CButtonST::DrawTransparent(BOOL bRepaint)
+{
+	m_bDrawTransparent = TRUE;
+
+	// Restore old bitmap (if any)
+	if (m_dcBk.m_hDC != NULL && m_pbmpOldBk != NULL)
+	{
+		m_dcBk.SelectObject(m_pbmpOldBk);
+	} // if
+
+	m_bmpBk.DeleteObject();
+	m_dcBk.DeleteDC();
+
+	// Repaint the button
+	if (bRepaint) Invalidate();
+} // End of DrawTransparent
+
+DWORD CButtonST::SetBk(CDC* pDC)
+{
+	if (m_bDrawTransparent && pDC)
+	{
+		// Restore old bitmap (if any)
+		if (m_dcBk.m_hDC != NULL && m_pbmpOldBk != NULL)
+		{
+			m_dcBk.SelectObject(m_pbmpOldBk);
+		} // if
+
+		m_bmpBk.DeleteObject();
+		m_dcBk.DeleteDC();
+
+		CRect rect;
+		CRect rect1;
+
+		GetClientRect(rect);
+
+		GetWindowRect(rect1);
+		GetParent()->ScreenToClient(rect1);
+
+		m_dcBk.CreateCompatibleDC(pDC);
+		m_bmpBk.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
+		m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
+		m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, rect1.left, rect1.top, SRCCOPY);
+
+		return BTNST_OK;
+	} // if
+
+	return BTNST_BADPARAM;
+} // End of SetBk
+
+// This function sets the URL that will be opened when the button is clicked.
+//
+// Parameters:
+//		[IN]	lpszURL
+//				Pointer to a null-terminated string that contains the URL.
+//				Pass NULL to removed any previously specified URL.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetURL(LPCTSTR lpszURL)
+{
+	// Remove any existing URL
+	memset(m_szURL, 0, sizeof(m_szURL));
+
+	if (lpszURL)
+	{
+		// Store the URL
+		_tcsncpy_s(m_szURL, lpszURL, _MAX_PATH);
+	} // if
+
+	return BTNST_OK;
+} // End of SetURL
+
+// This function associates a menu to the button.
+// The menu will be displayed clicking the button.
+//
+// Parameters:
+//		[IN]	nMenu
+//				ID number of the menu resource.
+//				Pass NULL to remove any menu from the button.
+//		[IN]	hParentWnd
+//				Handle to the window that owns the menu.
+//				This window receives all messages from the menu.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+#ifndef	BTNST_USE_BCMENU
+DWORD CButtonST::SetMenu(UINT nMenu, HWND hParentWnd, BOOL bRepaint)
+{
+	HINSTANCE	hInstResource	= NULL;
+
+	// Destroy any previous menu
+	if (m_hMenu)
+	{
+		::DestroyMenu(m_hMenu);
+		m_hMenu = NULL;
+		m_hParentWndMenu = NULL;
+		m_bMenuDisplayed = FALSE;
+	} // if
+
+	// Load menu
+	if (nMenu)
+	{
+		// Find correct resource handle
+		hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nMenu), RT_MENU);
+		// Load menu resource
+		m_hMenu = ::LoadMenu(hInstResource, MAKEINTRESOURCE(nMenu));
+		m_hParentWndMenu = hParentWnd;
+		// If something wrong
+		if (m_hMenu == NULL) return BTNST_INVALIDRESOURCE;
+	} // if
+
+	// Repaint the button
+	if (bRepaint) Invalidate();
+
+	return BTNST_OK;
+} // End of SetMenu
+#endif
+
+// This function associates a menu to the button.
+// The menu will be displayed clicking the button.
+// The menu will be handled by the BCMenu class.
+//
+// Parameters:
+//		[IN]	nMenu
+//				ID number of the menu resource.
+//				Pass NULL to remove any menu from the button.
+//		[IN]	hParentWnd
+//				Handle to the window that owns the menu.
+//				This window receives all messages from the menu.
+//		[IN]	bWinXPStyle
+//				If TRUE the menu will be displayed using the new Windows XP style.
+//				If FALSE the menu will be displayed using the standard style.
+//		[IN]	nToolbarID
+//				Resource ID of the toolbar to be associated to the menu.
+//		[IN]	sizeToolbarIcon
+//				A CSize object indicating the size (in pixels) of each icon into the toolbar.
+//				All icons into the toolbar must have the same size.
+//		[IN]	crToolbarBk
+//				A COLORREF value indicating the color to use as background for the icons into the toolbar.
+//				This color will be used as the "transparent" color.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+#ifdef	BTNST_USE_BCMENU
+DWORD CButtonST::SetMenu(UINT nMenu, HWND hParentWnd, BOOL bWinXPStyle, UINT nToolbarID, CSize sizeToolbarIcon, COLORREF crToolbarBk, BOOL bRepaint)
+{
+	BOOL	bRetValue = FALSE;
+
+	// Destroy any previous menu
+	if (m_menuPopup.m_hMenu)
+	{
+		m_menuPopup.DestroyMenu();
+		m_hParentWndMenu = NULL;
+		m_bMenuDisplayed = FALSE;
+	} // if
+
+	// Load menu
+	if (nMenu)
+	{
+		m_menuPopup.SetMenuDrawMode(bWinXPStyle);
+		// Load menu
+		bRetValue = m_menuPopup.LoadMenu(nMenu);
+		// If something wrong
+		if (bRetValue == FALSE) return BTNST_INVALIDRESOURCE;
+
+		// Load toolbar
+		if (nToolbarID)
+		{
+			m_menuPopup.SetBitmapBackground(crToolbarBk);
+			m_menuPopup.SetIconSize(sizeToolbarIcon.cx, sizeToolbarIcon.cy);
+
+			bRetValue = m_menuPopup.LoadToolbar(nToolbarID);
+			// If something wrong
+			if (bRetValue == FALSE) 
+			{
+				m_menuPopup.DestroyMenu();
+				return BTNST_INVALIDRESOURCE;
+			} // if
+		} // if
+
+		m_hParentWndMenu = hParentWnd;
+	} // if
+
+	// Repaint the button
+	if (bRepaint) Invalidate();
+
+	return BTNST_OK;
+} // End of SetMenu
+#endif
+
+// This function sets the callback message that will be sent to the
+// specified window just before the menu associated to the button is displayed.
+//
+// Parameters:
+//		[IN]	hWnd
+//				Handle of the window that will receive the callback message.
+//				Pass NULL to remove any previously specified callback message.
+//		[IN]	nMessage
+//				Callback message to send to window.
+//		[IN]	lParam
+//				A 32 bits user specified value that will be passed to the callback function.
+//
+// Remarks:
+//		the callback function must be in the form:
+//		LRESULT On_MenuCallback(WPARAM wParam, LPARAM lParam)
+//		Where:
+//				[IN]	wParam
+//						If support for BCMenu is enabled: a pointer to BCMenu
+//						else a HMENU handle to the menu that is being to be displayed.
+//				[IN]	lParam
+//						The 32 bits user specified value.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetMenuCallback(HWND hWnd, UINT nMessage, LPARAM lParam)
+{
+	m_csCallbacks.hWnd = hWnd;
+	m_csCallbacks.nMessage = nMessage;
+	m_csCallbacks.lParam = lParam;
+
+	return BTNST_OK;
+} // End of SetMenuCallback
+
+// This function resizes the button to the same size of the image.
+// To get good results both the IN and OUT images should have the same size.
+//
+void CButtonST::SizeToContent()
+{
+	if (m_csIcons[0].hIcon)
+	{
+		m_ptImageOrg.x = 0;
+		m_ptImageOrg.y = 0;
+	    SetWindowPos(	NULL, -1, -1, m_csIcons[0].dwWidth, m_csIcons[0].dwHeight,
+						SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
+	} // if
+	else
+	if (m_csBitmaps[0].hBitmap)
+	{
+		m_ptImageOrg.x = 0;
+		m_ptImageOrg.y = 0;
+	    SetWindowPos(	NULL, -1, -1, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight,
+						SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
+	} // if
+} // End of SizeToContent
+
+// This function sets the sound that must be played on particular button states.
+//
+// Parameters:
+//		[IN]	lpszSound
+//				A string that specifies the sound to play.
+//				If hMod is NULL this string is interpreted as a filename, else it
+//				is interpreted as a resource identifier.
+//				Pass NULL to remove any previously specified sound.
+//		[IN]	hMod
+//				Handle to the executable file that contains the resource to be loaded.
+//				This parameter must be NULL unless lpszSound specifies a resource identifier.
+//		[IN]	bPlayOnClick
+//				TRUE if the sound must be played when the button is clicked.
+//				FALSE if the sound must be played when the mouse is moved over the button.
+//		[IN]	bPlayAsync
+//				TRUE if the sound must be played asynchronously.
+//				FALSE if the sound must be played synchronously. The application takes control
+//				when the sound is completely played.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+#ifdef	BTNST_USE_SOUND
+DWORD CButtonST::SetSound(LPCTSTR lpszSound, HMODULE hMod, BOOL bPlayOnClick, BOOL bPlayAsync)
+{
+	BYTE	byIndex = bPlayOnClick ? 1 : 0;
+
+	// Store new sound
+	if (lpszSound)
+	{
+		if (hMod)	// From resource identifier ?
+		{
+			m_csSounds[byIndex].lpszSound = lpszSound;
+		} // if
+		else
+		{
+			_tcscpy_s(m_csSounds[byIndex].szSound, lpszSound);
+			m_csSounds[byIndex].lpszSound = m_csSounds[byIndex].szSound;
+		} // else
+
+		m_csSounds[byIndex].hMod = hMod;
+		m_csSounds[byIndex].dwFlags = SND_NODEFAULT | SND_NOWAIT;
+		m_csSounds[byIndex].dwFlags |= hMod ? SND_RESOURCE : SND_FILENAME;
+		m_csSounds[byIndex].dwFlags |= bPlayAsync ? SND_ASYNC : SND_SYNC;
+	} // if
+	else
+	{
+		// Or remove any existing
+		::ZeroMemory(&m_csSounds[byIndex], sizeof(STRUCT_SOUND));
+	} // else
+
+	return BTNST_OK;
+} // End of SetSound
+#endif
+
+// This function is called every time the button background needs to be painted.
+// If the button is in transparent mode this function will NOT be called.
+// This is a virtual function that can be rewritten in CButtonST-derived classes
+// to produce a whole range of buttons not available by default.
+//
+// Parameters:
+//		[IN]	pDC
+//				Pointer to a CDC object that indicates the device context.
+//		[IN]	pRect
+//				Pointer to a CRect object that indicates the bounds of the
+//				area to be painted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::OnDrawBackground(CDC* pDC, CRect* pRect)
+{
+	COLORREF	crColor;
+
+	if (m_bIsFlat == FALSE)
+	{
+		if (m_bIsFocused || m_bIsDefault)
+		{
+			CBrush br(RGB(0,0,0));  
+			pDC->FrameRect(pRect, &br);
+			pRect->DeflateRect(1, 1);
+		} // if
+	} // if
+
+	if (m_bMouseOnButton || m_bIsPressed)
+		crColor = m_crColors[BTNST_COLOR_BK_IN];
+	else
+	{
+		if (m_bIsFocused)
+			crColor = m_crColors[BTNST_COLOR_BK_FOCUS];
+		else
+			crColor = m_crColors[BTNST_COLOR_BK_OUT];
+	} // else
+
+	CBrush		brBackground(crColor);
+
+	pDC->FillRect(pRect, &brBackground);
+
+	return BTNST_OK;
+} // End of OnDrawBackground
+
+// This function is called every time the button border needs to be painted.
+// This is a virtual function that can be rewritten in CButtonST-derived classes
+// to produce a whole range of buttons not available by default.
+//
+// Parameters:
+//		[IN]	pDC
+//				Pointer to a CDC object that indicates the device context.
+//		[IN]	pRect
+//				Pointer to a CRect object that indicates the bounds of the
+//				area to be painted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::OnDrawBorder(CDC* pDC, CRect* pRect)
+{
+	// Draw pressed button
+	if (m_bIsPressed)
+	{
+		if (m_bIsFlat)
+		{
+			if (m_bDrawBorder)
+				pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHILIGHT));
+		}
+		else    
+		{
+			CBrush brBtnShadow(GetSysColor(COLOR_BTNSHADOW));
+			pDC->FrameRect(pRect, &brBtnShadow);
+		}
+	}
+	else // ...else draw non pressed button
+	{
+		CPen penBtnHiLight(PS_SOLID, 0, GetSysColor(COLOR_BTNHILIGHT)); // White
+		CPen pen3DLight(PS_SOLID, 0, GetSysColor(COLOR_3DLIGHT));       // Light gray
+		CPen penBtnShadow(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));   // Dark gray
+		CPen pen3DDKShadow(PS_SOLID, 0, GetSysColor(COLOR_3DDKSHADOW)); // Black
+
+		if (m_bIsFlat)
+		{
+			if (m_bMouseOnButton && m_bDrawBorder)
+				pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNHILIGHT), ::GetSysColor(COLOR_BTNSHADOW));
+		}
+		else
+		{
+			// Draw top-left borders
+			// White line
+			CPen* pOldPen = pDC->SelectObject(&penBtnHiLight);
+			pDC->MoveTo(pRect->left, pRect->bottom-1);
+			pDC->LineTo(pRect->left, pRect->top);
+			pDC->LineTo(pRect->right, pRect->top);
+			// Light gray line
+			pDC->SelectObject(pen3DLight);
+			pDC->MoveTo(pRect->left+1, pRect->bottom-1);
+			pDC->LineTo(pRect->left+1, pRect->top+1);
+			pDC->LineTo(pRect->right, pRect->top+1);
+			// Draw bottom-right borders
+			// Black line
+			pDC->SelectObject(pen3DDKShadow);
+			pDC->MoveTo(pRect->left, pRect->bottom-1);
+			pDC->LineTo(pRect->right-1, pRect->bottom-1);
+			pDC->LineTo(pRect->right-1, pRect->top-1);
+			// Dark gray line
+			pDC->SelectObject(penBtnShadow);
+			pDC->MoveTo(pRect->left+1, pRect->bottom-2);
+			pDC->LineTo(pRect->right-2, pRect->bottom-2);
+			pDC->LineTo(pRect->right-2, pRect->top);
+			//
+			pDC->SelectObject(pOldPen);
+		} // else
+	} // else
+
+	return BTNST_OK;
+} // End of OnDrawBorder
+
+#undef BS_TYPEMASK

+ 321 - 0
SATHelper/UI/BtnST.h

@@ -0,0 +1,321 @@
+//
+//	Class:		CButtonST
+//
+//	Compiler:	Visual C++
+//	Tested on:	Visual C++ 5.0
+//				Visual C++ 6.0
+//
+//	Version:	See GetVersionC() or GetVersionI()
+//
+//	Created:	xx/xxxx/1998
+//	Updated:	03/March/2003
+//
+//	Author:		Davide Calabro'		davide_calabro@yahoo.com
+//									http://www.softechsoftware.it
+//
+//	Note:		Code for the PreSubclassWindow and OnSetStyle functions
+//				has been taken from the COddButton class
+//				published by Paolo Messina and Jerzy Kaczorowski
+//
+//	Disclaimer
+//	----------
+//	THIS SOFTWARE AND THE ACCOMPANYING FILES ARE DISTRIBUTED "AS IS" AND WITHOUT
+//	ANY WARRANTIES WHETHER EXPRESSED OR IMPLIED. NO REPONSIBILITIES FOR POSSIBLE
+//	DAMAGES OR EVEN FUNCTIONALITY CAN BE TAKEN. THE USER MUST ASSUME THE ENTIRE
+//	RISK OF USING THIS SOFTWARE.
+//
+//	Terms of use
+//	------------
+//	THIS SOFTWARE IS FREE FOR PERSONAL USE OR FREEWARE APPLICATIONS.
+//	IF YOU USE THIS SOFTWARE IN COMMERCIAL OR SHAREWARE APPLICATIONS YOU
+//	ARE GENTLY ASKED TO DONATE 5$ (FIVE U.S. DOLLARS) TO THE AUTHOR:
+//
+//		Davide Calabro'
+//		P.O. Box 65
+//		21019 Somma Lombardo (VA)
+//		Italy
+//
+#ifndef _BTNST_H
+#define _BTNST_H
+
+// Uncomment the following 2 lines to enable support for BCMenu class
+#define	BTNST_USE_BCMENU
+#include "BCMenu.h"
+
+// Uncomment the following line to enable support for sound effects
+#define	BTNST_USE_SOUND
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+// Return values
+#ifndef	BTNST_OK
+#define	BTNST_OK						0
+#endif
+#ifndef	BTNST_INVALIDRESOURCE
+#define	BTNST_INVALIDRESOURCE			1
+#endif
+#ifndef	BTNST_FAILEDMASK
+#define	BTNST_FAILEDMASK				2
+#endif
+#ifndef	BTNST_INVALIDINDEX
+#define	BTNST_INVALIDINDEX				3
+#endif
+#ifndef	BTNST_INVALIDALIGN
+#define	BTNST_INVALIDALIGN				4
+#endif
+#ifndef	BTNST_BADPARAM
+#define	BTNST_BADPARAM					5
+#endif
+#ifndef	BTNST_INVALIDPRESSEDSTYLE
+#define	BTNST_INVALIDPRESSEDSTYLE		6
+#endif
+
+// Dummy identifier for grayscale icon
+#ifndef	BTNST_AUTO_GRAY
+#define	BTNST_AUTO_GRAY					(HICON)(0xffffffff - 1L)
+#endif
+// Dummy identifier for 15% darker icon
+#ifndef	BTNST_AUTO_DARKER
+#define	BTNST_AUTO_DARKER				(HICON)(0xffffffff - 2L)
+#endif
+
+class CButtonST : public CButton
+{
+public:
+    CButtonST();
+	~CButtonST();
+
+    enum	{	ST_ALIGN_HORIZ	= 0,			// Icon/bitmap on the left, text on the right
+				ST_ALIGN_VERT,					// Icon/bitmap on the top, text on the bottom
+				ST_ALIGN_HORIZ_RIGHT,			// Icon/bitmap on the right, text on the left
+				ST_ALIGN_OVERLAP				// Icon/bitmap on the same space as text
+			};
+
+	enum	{	BTNST_COLOR_BK_IN	= 0,		// Background color when mouse is INside
+				BTNST_COLOR_FG_IN,				// Text color when mouse is INside
+				BTNST_COLOR_BK_OUT,				// Background color when mouse is OUTside
+				BTNST_COLOR_FG_OUT,				// Text color when mouse is OUTside
+				BTNST_COLOR_BK_FOCUS,			// Background color when the button is focused
+				BTNST_COLOR_FG_FOCUS,			// Text color when the button is focused
+
+				BTNST_MAX_COLORS
+			};
+
+    enum	{	BTNST_PRESSED_LEFTRIGHT = 0,	// Pressed style from left to right (as usual)
+				BTNST_PRESSED_TOPBOTTOM			// Pressed style from top to bottom
+			};
+
+	// ClassWizard generated virtual function overrides
+    //{{AFX_VIRTUAL(CButtonST)
+	public:
+	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+	virtual BOOL PreTranslateMessage(MSG* pMsg);
+	protected:
+	virtual void PreSubclassWindow();
+	//}}AFX_VIRTUAL
+
+public:
+	void ReconstructFont();
+	void UpdateSurface();
+	virtual CButtonST& SetFontBold(BOOL bBold = TRUE);
+	virtual CButtonST& SetFontName(const CString& strFont, BYTE byCharSet = ANSI_CHARSET);
+	virtual CButtonST& SetFontUnderline(BOOL bSet = TRUE);
+	virtual CButtonST& SetFontItalic(BOOL bSet = TRUE);
+	virtual CButtonST& SetFontSize(int nSize);
+
+	DWORD SetDefaultColors(BOOL bRepaint = TRUE);
+	DWORD SetColor(BYTE byColorIndex, COLORREF crColor, BOOL bRepaint = TRUE);
+	DWORD GetColor(BYTE byColorIndex, COLORREF* crpColor);
+	DWORD OffsetColor(BYTE byColorIndex, short shOffset, BOOL bRepaint = TRUE);
+
+	DWORD SetCheck(int nCheck, BOOL bRepaint = TRUE);
+	int GetCheck();
+
+	DWORD SetURL(LPCTSTR lpszURL = NULL);
+	void DrawTransparent(BOOL bRepaint = FALSE);
+	DWORD SetBk(CDC* pDC);
+
+	BOOL GetDefault();
+	DWORD SetAlwaysTrack(BOOL bAlwaysTrack = TRUE);
+
+	void SetTooltipText(int nText, BOOL bActivate = TRUE);
+	void SetTooltipText(LPCTSTR lpszText, BOOL bActivate = TRUE);
+	void ActivateTooltip(BOOL bEnable = TRUE);
+	DWORD EnableBalloonTooltip();
+
+	DWORD SetBtnCursor(int nCursorId = NULL, BOOL bRepaint = TRUE);
+
+	DWORD SetFlat(BOOL bFlat = TRUE, BOOL bRepaint = TRUE);
+	DWORD SetAlign(BYTE byAlign, BOOL bRepaint = TRUE);
+	DWORD SetPressedStyle(BYTE byStyle, BOOL bRepaint = TRUE);
+
+	DWORD DrawBorder(BOOL bDrawBorder = TRUE, BOOL bRepaint = TRUE);
+	DWORD DrawFlatFocus(BOOL bDrawFlatFocus, BOOL bRepaint = TRUE);
+
+	DWORD SetIcon(int nIconIn, int nCxDesiredIn, int nCyDesiredIn, int nIconOut = NULL, int nCxDesiredOut = 0, int nCyDesiredOut = 0);
+	DWORD SetIcon(int nIconIn, int nIconOut = NULL);
+	DWORD SetIcon(HICON hIconIn, HICON hIconOut = NULL);
+
+	DWORD SetBitmaps(int nBitmapIn, COLORREF crTransColorIn, int nBitmapOut = NULL, COLORREF crTransColorOut = 0);
+	DWORD SetBitmaps(HBITMAP hBitmapIn, COLORREF crTransColorIn, HBITMAP hBitmapOut = NULL, COLORREF crTransColorOut = 0);
+
+	void SizeToContent();
+
+#ifdef	BTNST_USE_BCMENU
+	DWORD SetMenu(UINT nMenu, HWND hParentWnd, BOOL bWinXPStyle = TRUE, UINT nToolbarID = NULL, CSize sizeToolbarIcon = CSize(16, 16), COLORREF crToolbarBk = RGB(255, 0, 255), BOOL bRepaint = TRUE);
+#else
+	DWORD SetMenu(UINT nMenu, HWND hParentWnd, BOOL bRepaint = TRUE);
+#endif
+	DWORD SetMenuCallback(HWND hWnd, UINT nMessage, LPARAM lParam = 0);
+
+#ifdef	BTNST_USE_SOUND
+	DWORD SetSound(LPCTSTR lpszSound, HMODULE hMod = NULL, BOOL bPlayOnClick = FALSE, BOOL bPlayAsync = TRUE);
+#endif
+
+	static short GetVersionI()		{return 39;}
+	static LPCTSTR GetVersionC()	{return (LPCTSTR)_T("3.9");}
+
+	BOOL	m_bShowDisabledBitmap;
+	POINT	m_ptImageOrg;
+	POINT	m_ptPressedOffset;
+
+protected:
+    //{{AFX_MSG(CButtonST)
+	afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
+	afx_msg void OnKillFocus(CWnd* pNewWnd);
+	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+	afx_msg void OnSysColorChange();
+	afx_msg BOOL OnClicked();
+	afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
+	afx_msg void OnEnable(BOOL bEnable);
+	afx_msg void OnCancelMode();
+	afx_msg UINT OnGetDlgCode();
+	//}}AFX_MSG
+
+#ifdef	BTNST_USE_BCMENU
+	afx_msg LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu);
+	afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
+#endif
+
+	afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
+	HICON CreateGrayscaleIcon(HICON hIcon);
+	HICON CreateDarkerIcon(HICON hIcon);
+	HBITMAP CreateGrayscaleBitmap(HBITMAP hBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTrans);
+	HBITMAP CreateDarkerBitmap(HBITMAP hBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTrans);
+	COLORREF DarkenColor(COLORREF crColor, double dFactor);
+	virtual DWORD OnDrawBackground(CDC* pDC, CRect* pRect);
+	virtual DWORD OnDrawBorder(CDC* pDC, CRect* pRect);
+
+	BOOL		m_bIsFlat;			// Is a flat button?
+	BOOL		m_bMouseOnButton;	// Is mouse over the button?
+	BOOL		m_bDrawTransparent;	// Draw transparent?
+	BOOL		m_bIsPressed;		// Is button pressed?
+	BOOL		m_bIsFocused;		// Is button focused?
+	BOOL		m_bIsDisabled;		// Is button disabled?
+	BOOL		m_bIsDefault;		// Is default button?
+	BOOL		m_bIsCheckBox;		// Is the button a checkbox?
+	BYTE		m_byAlign;			// Align mode
+	BOOL		m_bDrawBorder;		// Draw border?
+	BOOL		m_bDrawFlatFocus;	// Draw focus rectangle for flat button?
+	COLORREF	m_crColors[BTNST_MAX_COLORS];	// Colors to be used
+	HWND		m_hParentWndMenu;	// Handle to window for menu selection
+	BOOL		m_bMenuDisplayed;	// Is menu displayed ?
+
+	LOGFONT			m_lf;
+	CFont			m_font;
+
+#ifdef	BTNST_USE_BCMENU
+	BCMenu		m_menuPopup;		// BCMenu class instance
+#else
+	HMENU		m_hMenu;			// Handle to associated menu
+#endif
+
+private:
+	LRESULT OnSetCheck(WPARAM wParam, LPARAM lParam);
+	LRESULT OnGetCheck(WPARAM wParam, LPARAM lParam);
+	LRESULT OnSetStyle(WPARAM wParam, LPARAM lParam);
+	LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
+
+	void CancelHover();
+	void FreeResources(BOOL bCheckForNULL = TRUE);
+	void PrepareImageRect(BOOL bHasTitle, RECT* rpItem, CRect* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, CRect* rpImage);
+	HBITMAP CreateBitmapMask(HBITMAP hSourceBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTransColor);
+	virtual void DrawTheIcon(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+	virtual void DrawTheBitmap(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+	virtual void DrawTheText(CDC* pDC, LPCTSTR lpszText, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+	void PaintBk(CDC* pDC);
+
+	void InitToolTip();
+
+	HCURSOR		m_hCursor;			// Handle to cursor
+	CToolTipCtrl m_ToolTip;			// Tooltip
+
+	CDC			m_dcBk;
+	CBitmap		m_bmpBk;
+	CBitmap*	m_pbmpOldBk;
+
+	BOOL		m_bAlwaysTrack;		// Always hilight button?
+	int			m_nCheck;			// Current value for checkbox
+	UINT		m_nTypeStyle;		// Button style
+	DWORD		m_dwToolTipStyle;	// Style of tooltip control
+
+	TCHAR		m_szURL[_MAX_PATH];	// URL to open when clicked
+
+#pragma pack(1)
+	typedef struct _STRUCT_ICONS
+	{
+		HICON		hIcon;			// Handle to icon
+		DWORD		dwWidth;		// Width of icon
+		DWORD		dwHeight;		// Height of icon
+	} STRUCT_ICONS;
+#pragma pack()
+
+#pragma pack(1)
+	typedef struct _STRUCT_BITMAPS
+	{
+		HBITMAP		hBitmap;		// Handle to bitmap
+		DWORD		dwWidth;		// Width of bitmap
+		DWORD		dwHeight;		// Height of bitmap
+		HBITMAP		hMask;			// Handle to mask bitmap
+		COLORREF	crTransparent;	// Transparent color
+	} STRUCT_BITMAPS;
+#pragma pack()
+
+#pragma pack(1)
+	typedef struct _STRUCT_CALLBACK
+	{
+		HWND		hWnd;			// Handle to window
+		UINT		nMessage;		// Message identifier
+		WPARAM		wParam;
+		LPARAM		lParam;
+	} STRUCT_CALLBACK;
+#pragma pack()
+
+	STRUCT_ICONS	m_csIcons[2];
+	STRUCT_BITMAPS	m_csBitmaps[2];
+
+	STRUCT_CALLBACK	m_csCallbacks;
+
+#ifdef	BTNST_USE_SOUND
+#pragma pack(1)
+	typedef struct _STRUCT_SOUND
+	{
+		TCHAR		szSound[_MAX_PATH];
+		LPCTSTR		lpszSound;
+		HMODULE		hMod;
+		DWORD		dwFlags;
+	} STRUCT_SOUND;
+#pragma pack()
+
+	STRUCT_SOUND	m_csSounds[2];	// Index 0 = Over	1 = Clicked
+#endif
+
+	DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif

+ 161 - 0
SATHelper/UI/ComboTreeCtrl.cpp

@@ -0,0 +1,161 @@
+// ComboTreeCtrl.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "ComboTreeCtrl.h"
+
+#include "TreeComboBox.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CComboTreeCtrl
+
+CComboTreeCtrl::CComboTreeCtrl() :m_pCombo(NULL)
+{
+}
+
+CComboTreeCtrl::~CComboTreeCtrl()
+{
+}
+
+
+BEGIN_MESSAGE_MAP(CComboTreeCtrl, CTreeCtrl)
+	ON_NOTIFY_REFLECT(NM_KILLFOCUS, OnKillfocus)
+	ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
+	ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
+	ON_MESSAGE(TREECTRL_CHECK_STATE_CHANGE, OnTreeCtrlCheckStateChange)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CComboTreeCtrl message handlers
+
+void CComboTreeCtrl::Display(IN CRect rc)
+{
+	if (GetSafeHwnd() == NULL)
+		return;
+
+	SetWindowPos(&wndNoTopMost, rc.left, rc.top, rc.right, rc.bottom, SWP_SHOWWINDOW);
+
+	CWnd* pTopParent = GetParent()->GetParentOwner();
+	if (pTopParent != NULL)
+	{
+		pTopParent->SendMessage(WM_NCACTIVATE, TRUE);
+		pTopParent->SetRedraw(TRUE);
+	}
+}
+
+BOOL CComboTreeCtrl::PreTranslateMessage(MSG* pMsg)
+{
+	if (pMsg->hwnd != m_hWnd)
+		return FALSE;
+
+	// 过滤掉Alt+F4;
+	if (WM_SYSKEYDOWN == pMsg->message && VK_F4 == pMsg->wParam)
+		return TRUE;
+
+	// 回车键和esc键关闭控件;
+	if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE))
+	{
+		// WMU_CLOSE_CONTROL->关闭控件;
+		::PostMessage(m_pCombo->m_hWnd, WMU_CLOSE_CONTROL, (WPARAM)0, (LPARAM)0);
+		return TRUE;
+	}
+
+	return CTreeCtrl::PreTranslateMessage(pMsg);
+}
+
+void CComboTreeCtrl::OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	// WMU_CLOSE_CONTROL->关闭控件;
+	::PostMessage(m_pCombo->m_hWnd, WMU_CLOSE_CONTROL, (WPARAM)0, (LPARAM)0);
+}
+
+LRESULT CComboTreeCtrl::OnTreeCtrlCheckStateChange(WPARAM wParam, LPARAM lParam)
+{
+	HTREEITEM hItemChanged = (HTREEITEM)lParam;
+	BOOL bCheckState = GetCheck(hItemChanged);
+	SelectItem(hItemChanged);
+
+	while (hItemChanged = GetParentItem(hItemChanged))
+		SetCheck(hItemChanged);
+
+	return 1;
+}
+
+void CComboTreeCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	UINT nFlags;
+	HTREEITEM hItem;
+	// verify that we have a mouse click in the check box area 
+	DWORD dwPos = GetMessagePos();
+	CPoint point(LOWORD(dwPos), HIWORD(dwPos));
+	ScreenToClient(&point);
+	hItem = HitTest(point, &nFlags);
+	if (hItem)
+	{
+		if (GetParentItem(hItem))
+			::PostMessage(m_pCombo->m_hWnd, WMU_CLOSE_CONTROL, (WPARAM)0, (LPARAM)hItem);// WMU_CLOSE_CONTROL->关闭控件;
+	}
+
+	*pResult = 0;
+}
+
+void CComboTreeCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	TV_KEYDOWN* pTVKeyDown = (TV_KEYDOWN*)pNMHDR;
+	if (pTVKeyDown->wVKey == VK_SPACE)
+	{
+		HTREEITEM hItem = GetSelectedItem();
+		if (hItem != NULL)
+		{
+			// handle state change here or post message to another handler
+			// Post message state has changed
+			PostMessage(TREECTRL_CHECK_STATE_CHANGE, 0, (LPARAM)hItem);
+		}
+	}
+
+	*pResult = 0;
+}
+
+/************************************************************************/
+/*  函数:FindItem[12/7/2016 IT];
+/*  描述:从指定父项开始查找指定名称的项;
+/*  参数:;
+/*  	[IN] strName:要查找的项名称;
+/*  	[IN] hRoot:要开始查找的父项;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+HTREEITEM CComboTreeCtrl::FindItem(IN const CString& strName, IN HTREEITEM hRoot)
+{
+	// 判断父项是否就是要查找的项;
+	CString sText = GetItemText(hRoot);
+	if (sText.Compare(strName) == 0)
+		return hRoot;
+
+	HTREEITEM hItem = NULL;
+	// 获取第一个子项句柄;
+	HTREEITEM hSub = GetChildItem(hRoot);
+	// 遍历所有子项;
+	while (hSub)
+	{
+		// 当前子项是否是要查找的子项;
+		if ((hItem = FindItem(strName, hSub)) != NULL)
+			break;
+
+		// 若未到,查找下一个兄弟项;
+		hSub = GetNextSiblingItem(hSub);
+	}
+
+	return hItem;
+}

+ 59 - 0
SATHelper/UI/ComboTreeCtrl.h

@@ -0,0 +1,59 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [IT], 保留所有权利;
+/*  模 块 名:;
+/*  描    述:;
+/*
+/*  版    本:[V];
+/*  作    者:[IT];
+/*  日    期:[11/4/2016];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[IT];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+
+#ifndef __COMBO_TREE_CTRL__
+#define __COMBO_TREE_CTRL__
+
+
+#pragma once
+
+// 自定义函数;
+#define TREECTRL_CHECK_STATE_CHANGE		(WM_USER + 100)
+#define WMU_CLOSE_CONTROL				(WM_USER + 101)
+
+// 前置声明;
+class CTreeComboBox;
+class CComboTreeCtrl : public CTreeCtrl
+{
+public:
+	CComboTreeCtrl();
+	virtual ~CComboTreeCtrl();
+public:
+	void Display(IN CRect rc);
+
+public:
+	virtual BOOL PreTranslateMessage(MSG* pMsg);
+
+public:
+	void Init(CTreeComboBox* pCombo)
+	{
+		m_pCombo = pCombo;
+	};
+
+protected:
+	afx_msg void OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnClick(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnKeydown(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg LRESULT OnTreeCtrlCheckStateChange(WPARAM wParam, LPARAM lParam);
+	DECLARE_MESSAGE_MAP()
+private:
+	CTreeComboBox* m_pCombo;
+	HTREEITEM FindItem(IN const CString& strName, IN HTREEITEM hRoot);
+};
+
+#endif // __COMBO_TREE_CTRL__

+ 36 - 0
SATHelper/UI/FontSize.cpp

@@ -0,0 +1,36 @@
+#include "stdafx.h"
+#include "FontSize.h"
+
+/***************************************************************************************/
+// GetFontPointSize
+int GetFontPointSize(int iHeight)
+{
+    HDC hDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
+    ASSERT(hDC);
+
+    int icyPixelsPerInch = GetDeviceCaps(hDC, LOGPIXELSY);
+    DeleteDC(hDC);
+
+    int iPointSize = MulDiv(iHeight, 72, icyPixelsPerInch);
+    if(iPointSize < 0)
+    {
+        iPointSize = -iPointSize;
+    }
+
+    return iPointSize;
+}
+
+/***************************************************************************************/
+// GetFontHeight
+int GetFontHeight(int iPointSize)
+{
+    HDC hDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
+    ASSERT(hDC);
+
+    int icyPixelsPerInch = GetDeviceCaps(hDC, LOGPIXELSY);
+    DeleteDC(hDC);
+
+    int iHeight = -MulDiv(iPointSize, icyPixelsPerInch, 72);
+
+    return iHeight;
+}

+ 25 - 0
SATHelper/UI/FontSize.h

@@ -0,0 +1,25 @@
+/*************************************************************************************
+ * FontSize.h
+ *
+ * 作者: 雷登似
+ * 公司: TCL多媒体科技控股有限公司
+ * 版本: 0.01
+ * 编程日期: 2010-04-21
+ * 联系方式: wp2.leids@tcl.com / 0752-3270423(81423)
+ *
+ * 功能: 获取字体大小
+ * 说明: 本软件释放给TCL公司, 只要你喜欢, 在TCL公司内部可以免费使用, 但在使用过程中
+ *       你必须保证此软件的说明信息完整!
+ *
+ * 此软件的最终解释权归原作者拥有, 作者不对软件的分发和使用负责, 如果你因使用此软
+ * 件而造成的任何损失或商业风险均由使用者自己承担所有责任.
+ *
+ *************************************************************************************/
+
+#ifndef __FONT_SIZE_H__
+#define __FONT_SIZE_H__
+
+int GetFontPointSize(int iHeight);
+int GetFontHeight(int iPointSize);
+
+#endif

+ 1216 - 0
SATHelper/UI/PictureEx.cpp

@@ -0,0 +1,1216 @@
+
+
+#include "stdafx.h"
+#include "PictureEx.h"
+#include <process.h>
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Nested structures member functions
+//////////////////////////////////////////////////////////////////////
+
+inline int CPictureEx::TGIFControlExt::GetPackedValue(enum ControlExtValues Value)
+{
+ int nRet = (int)m_cPacked;
+ switch (Value)
+ {
+ case GCX_PACKED_DISPOSAL:
+  nRet = (nRet & 28) >> 2;
+  break;
+
+ case GCX_PACKED_USERINPUT:
+  nRet = (nRet & 2) >> 1;
+  break;
+
+ case GCX_PACKED_TRANSPCOLOR:
+  nRet &= 1;
+  break;
+ };
+
+ return nRet;
+}
+
+inline int CPictureEx::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value)
+{
+ int nRet = (int)m_cPacked;
+
+ switch (Value)
+ {
+ case LSD_PACKED_GLOBALCT:
+  nRet = nRet >> 7;
+  break;
+
+ case LSD_PACKED_CRESOLUTION:
+  nRet = ((nRet & 0x70) >> 4) + 1;
+  break;
+
+ case LSD_PACKED_SORT:
+  nRet = (nRet & 8) >> 3;
+  break;
+
+ case LSD_PACKED_GLOBALCTSIZE:
+  nRet &= 7;
+  break;
+ };
+
+ return nRet;
+}
+
+inline int CPictureEx::TGIFImageDescriptor::GetPackedValue(enum IDPackedValues Value)
+{
+ int nRet = (int)m_cPacked;
+
+ switch (Value)
+ {
+ case ID_PACKED_LOCALCT:
+  nRet >>= 7;
+  break;
+
+ case ID_PACKED_INTERLACE:
+  nRet = ((nRet & 0x40) >> 6);
+  break;
+
+ case ID_PACKED_SORT:
+  nRet = (nRet & 0x20) >> 5;
+  break;
+
+ case ID_PACKED_LOCALCTSIZE:
+  nRet &= 7;
+  break;
+ };
+
+ return nRet;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CPictureEx::CPictureEx()
+{
+ // check structures size
+ ASSERT(sizeof(TGIFImageDescriptor) == 10);
+ ASSERT(sizeof(TGIFAppExtension)    == 14);
+ ASSERT(sizeof(TGIFPlainTextExt)    == 15);
+ ASSERT(sizeof(TGIFLSDescriptor)    ==  7);
+ ASSERT(sizeof(TGIFControlExt)    ==  8);
+ ASSERT(sizeof(TGIFCommentExt)    ==  2);
+ ASSERT(sizeof(TGIFHeader)     ==  6);
+
+ m_pGIFLSDescriptor = NULL;
+ m_pGIFHeader    = NULL;
+ m_pPicture     = NULL;
+ m_pRawData     = NULL;
+ m_hThread     = NULL;
+ m_hBitmap          = NULL;
+ m_hMemDC     = NULL;
+
+ m_hDispMemDC       = NULL;
+ m_hDispMemBM       = NULL;
+ m_hDispOldBM       = NULL;
+
+ m_bIsInitialized   = FALSE;
+ m_bExitThread    = FALSE;
+ m_bIsPlaying       = FALSE;
+ m_bIsGIF     = FALSE;
+ m_clrBackground    = RGB(255,255,255); // white by default
+ m_nGlobalCTSize    = 0;
+ m_nCurrOffset    = 0;
+ m_nCurrFrame    = 0;
+ m_nDataSize     = 0;
+ m_PictureSize.cx = m_PictureSize.cy = 0;
+ SetRect(&m_PaintRect,0,0,0,0);
+
+ m_hExitEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+}
+
+CPictureEx::~CPictureEx()
+{
+ UnLoad();
+ CloseHandle(m_hExitEvent);
+}
+
+BEGIN_MESSAGE_MAP(CPictureEx, CStatic)
+ //{{AFX_MSG_MAP(CPictureEx)
+ ON_WM_DESTROY()
+ ON_WM_PAINT()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+BOOL CPictureEx::Load(HGLOBAL hGlobal, DWORD dwSize)
+{
+ IStream *pStream = NULL;
+ UnLoad();
+
+ if (!(m_pRawData = reinterpret_cast<unsigned char*> (GlobalLock(hGlobal))) )
+ {
+  TRACE(_T("Load: Error locking memory\n"));
+  return FALSE;
+ };
+
+ m_nDataSize = dwSize;
+ m_pGIFHeader = reinterpret_cast<TGIFHeader *> (m_pRawData);
+
+ if ((memcmp(&m_pGIFHeader->m_cSignature,"GIF",3) != 0) &&
+  ((memcmp(&m_pGIFHeader->m_cVersion,"87a",3) != 0) ||
+   (memcmp(&m_pGIFHeader->m_cVersion,"89a",3) != 0)) )
+ {
+ // it's neither GIF87a nor GIF89a
+ // do the default processing
+
+  // clear GIF variables
+  m_pRawData = NULL;
+  GlobalUnlock(hGlobal);
+
+  // don't delete memory on object's release
+  if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK)
+   return FALSE;
+
+  if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
+   reinterpret_cast<LPVOID *>(&m_pPicture)) != S_OK)
+  {
+   pStream->Release();
+   return FALSE;
+  };
+  pStream->Release();
+
+  // store picture's size
+
+  long hmWidth;
+  long hmHeight;
+  m_pPicture->get_Width(&hmWidth);
+  m_pPicture->get_Height(&hmHeight);
+
+  HDC hDC = ::GetDC(m_hWnd);
+  m_PictureSize.cx = MulDiv(hmWidth, GetDeviceCaps(hDC,LOGPIXELSX), 2540);
+  m_PictureSize.cy = MulDiv(hmHeight, GetDeviceCaps(hDC,LOGPIXELSY), 2540);
+  ::ReleaseDC(m_hWnd,hDC);
+ }
+ else
+ {
+  // it's a GIF
+  m_bIsGIF = TRUE;
+  m_pGIFLSDescriptor = reinterpret_cast<TGIFLSDescriptor *>
+   (m_pRawData + sizeof(TGIFHeader));
+  if (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1)
+  {
+   // calculate the globat color table size
+   m_nGlobalCTSize = static_cast<int>
+    (3* (1 << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE)+1)));
+   // get the background color if GCT is present
+   unsigned char *pBkClr = m_pRawData + sizeof(TGIFHeader) + 
+    sizeof(TGIFLSDescriptor) + 3*m_pGIFLSDescriptor->m_cBkIndex;
+   m_clrBackground = RGB(pBkClr[0],pBkClr[1],pBkClr[2]);
+  };
+
+  // store the picture's size
+  m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth;
+  m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight;
+
+  // determine frame count for this picture
+  UINT nFrameCount=0;
+  ResetDataPointer();
+  while (SkipNextGraphicBlock())
+   nFrameCount++;
+
+#ifdef GIF_TRACING
+  TRACE(
+   _T(" -= GIF encountered\n"
+      "Logical Screen dimensions = %dx%d\n"
+      "Global color table = %d\n"
+      "Color depth = %d\n"
+      "Sort flag = %d\n"
+      "Size of Global Color Table = %d\n"
+      "Background color index = %d\n"
+      "Pixel aspect ratio = %d\n"
+      "Frame count = %d\n"
+      "Background color = %06Xh\n\n"
+     ),
+   m_pGIFLSDescriptor->m_wWidth,
+   m_pGIFLSDescriptor->m_wHeight,
+   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT),
+   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_CRESOLUTION),
+   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_SORT),
+   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE),
+   m_pGIFLSDescriptor->m_cBkIndex,
+   m_pGIFLSDescriptor->m_cPixelAspect,
+   nFrameCount,
+   m_clrBackground
+   );
+  EnumGIFBlocks();
+#endif
+
+  if (nFrameCount == 0) // it's an empty GIF!
+  {
+   m_pRawData = NULL;
+   GlobalUnlock(hGlobal);
+   return FALSE;
+  };
+
+  // now check the frame count
+  // if there's only one frame, no need to animate this GIF
+  // therefore, treat it like any other pic
+
+  if (nFrameCount == 1)
+  {
+   // clear GIF variables
+   m_pRawData = NULL;
+   GlobalUnlock(hGlobal);
+
+   // don't delete memory on object's release
+   if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK)
+    return FALSE;
+
+   if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
+    (LPVOID *)&m_pPicture) != S_OK)
+   {
+    pStream->Release();
+    return FALSE;
+   };
+
+   pStream->Release();
+  }
+  else
+  {
+  // if, on the contrary, there are several frames
+  // then store separate frames in an array
+
+   TFrame frame;
+   UINT nBlockLen;
+   HGLOBAL hFrameData;
+   UINT nCurFrame = 0;
+
+   ResetDataPointer();
+   while (hFrameData = GetNextGraphicBlock(&nBlockLen,
+    &frame.m_nDelay, &frame.m_frameSize,
+    &frame.m_frameOffset, &frame.m_nDisposal) )
+   {
+    #ifdef GIF_TRACING
+    //////////////////////////////////////////////
+    // uncomment the following strings if you want 
+    // to write separate frames on disk
+    //
+    // CString szName;
+    // szName.Format(_T("%.4d.gif"),nCurFrame);
+    // WriteDataOnDisk(szName,hFrameData,nBlockLen);
+    // nCurFrame++;
+    #endif // GIF_TRACING
+
+    IStream *pStream = NULL;
+
+    // delete memory on object's release
+    if (CreateStreamOnHGlobal(hFrameData,TRUE,&pStream) != S_OK)
+    {
+     GlobalFree(hFrameData);
+     continue;
+    };
+
+    if (OleLoadPicture(pStream,nBlockLen,FALSE,
+     IID_IPicture,
+     reinterpret_cast<LPVOID *>(&frame.m_pPicture)) != S_OK)
+    {
+     pStream->Release();
+     continue;
+    };
+    pStream->Release();
+   
+    // everything went well, add this frame
+    m_arrFrames.push_back(frame);
+   };
+
+   // clean after ourselves
+   m_pRawData = NULL;
+   GlobalUnlock(hGlobal);
+
+   if (m_arrFrames.empty()) // couldn't load any frames
+    return FALSE;
+  };
+ }; // if (!IsGIF...
+
+ return PrepareDC(m_PictureSize.cx,m_PictureSize.cy);
+}
+
+void CPictureEx::UnLoad()
+{
+ Stop();
+ if (m_pPicture)
+ {
+  m_pPicture->Release();
+  m_pPicture = NULL;
+ };
+ 
+ std::vector<TFrame>::iterator it;
+ for (it=m_arrFrames.begin();it<m_arrFrames.end();it++)
+  (*it).m_pPicture->Release();
+ m_arrFrames.clear();
+
+ if (m_hMemDC)
+ {
+  SelectObject(m_hMemDC,m_hOldBitmap);
+  ::DeleteDC(m_hMemDC);
+  ::DeleteObject(m_hBitmap);
+  m_hMemDC  = NULL;
+  m_hBitmap = NULL;
+ };
+
+ if (m_hDispMemDC)
+ {
+  SelectObject(m_hDispMemDC,m_hDispOldBM);
+  ::DeleteDC(m_hDispMemDC);
+  ::DeleteObject(m_hDispMemBM);
+  m_hDispMemDC  = NULL;
+  m_hDispMemBM = NULL;
+ };
+
+ SetRect(&m_PaintRect,0,0,0,0);
+ m_pGIFLSDescriptor = NULL;
+ m_pGIFHeader    = NULL;
+ m_pRawData     = NULL;
+ m_hThread     = NULL;
+ m_bIsInitialized   = FALSE;
+ m_bExitThread    = FALSE;
+ m_bIsGIF     = FALSE;
+ m_clrBackground    = RGB(255,255,255); // white by default
+ m_nGlobalCTSize    = 0;
+ m_nCurrOffset    = 0;
+ m_nCurrFrame    = 0;
+ m_nDataSize     = 0;
+}
+
+BOOL CPictureEx::Draw()
+{
+ if (!m_bIsInitialized)
+ {
+  TRACE(_T("Call one of the CPictureEx::Load() member functions before calling Draw()\n"));
+  return FALSE;
+ };
+
+ if (IsAnimatedGIF())
+ {
+ // the picture needs animation
+ // we'll start the thread that will handle it for us
+ 
+  unsigned int nDummy;
+  m_hThread = (HANDLE) _beginthreadex(NULL,0,_ThreadAnimation,this,
+   CREATE_SUSPENDED,&nDummy);
+  if (!m_hThread)
+  {
+   TRACE(_T("Draw: Couldn't start a GIF animation thread\n"));
+   return FALSE;
+  } 
+  else 
+   ResumeThread(m_hThread);
+ } 
+ else
+ {
+  if (m_pPicture)
+  {
+   long hmWidth;
+   long hmHeight;
+   m_pPicture->get_Width(&hmWidth);
+   m_pPicture->get_Height(&hmHeight);
+   if (m_pPicture->Render(m_hMemDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy, 
+    0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
+   {
+    Invalidate(FALSE);
+    return TRUE;
+   };
+  };
+ };
+
+ return FALSE; 
+}
+
+SIZE CPictureEx::GetSize() const
+{
+ return m_PictureSize;
+}
+
+BOOL CPictureEx::Load(LPCTSTR szFileName)
+{
+ ASSERT(szFileName);
+ 
+ CFile file;
+ HGLOBAL hGlobal;
+ DWORD dwSize;
+
+ if (!file.Open(szFileName,
+    CFile::modeRead | 
+    CFile::shareDenyWrite) )
+ {
+  TRACE(_T("Load (file): Error opening file %s\n"),szFileName);
+  return FALSE;
+ };
+
+ dwSize = file.GetLength();
+ hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
+ if (!hGlobal)
+ {
+  TRACE(_T("Load (file): Error allocating memory\n"));
+  return FALSE;
+ };
+ 
+ char *pData = reinterpret_cast<char*>(GlobalLock(hGlobal));
+ if (!pData)
+ {
+  TRACE(_T("Load (file): Error locking memory\n"));
+  GlobalFree(hGlobal);
+  return FALSE;
+ };
+
+ TRY
+ {
+  file.Read(pData,dwSize);
+ }
+ CATCH(CFileException, e);                                          
+ {
+  TRACE(_T("Load (file): An exception occured while reading the file %s\n"),
+   szFileName);
+  GlobalFree(hGlobal);
+  e->Delete();
+  file.Close();
+  return FALSE;
+ }
+ END_CATCH
+ GlobalUnlock(hGlobal);
+ file.Close();
+
+ BOOL bRetValue = Load(hGlobal,dwSize);
+ GlobalFree(hGlobal);
+ return bRetValue;
+}
+
+BOOL CPictureEx::Load(LPCTSTR szResourceName, LPCTSTR szResourceType)
+{
+ ASSERT(szResourceName);
+ ASSERT(szResourceType);
+
+ HRSRC hPicture = FindResource(AfxGetResourceHandle(),szResourceName,szResourceType);
+ HGLOBAL hResData;
+ if (!hPicture || !(hResData = LoadResource(AfxGetResourceHandle(),hPicture)))
+ {
+  TRACE(_T("Load (resource): Error loading resource %s\n"),szResourceName);
+  return FALSE;
+ };
+ DWORD dwSize = SizeofResource(AfxGetResourceHandle(),hPicture);
+
+ // hResData is not the real HGLOBAL (we can't lock it)
+ // let's make it real
+
+ HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
+ if (!hGlobal)
+ {
+  TRACE(_T("Load (resource): Error allocating memory\n"));
+  FreeResource(hResData);
+  return FALSE;
+ };
+ 
+ char *pDest = reinterpret_cast<char *> (GlobalLock(hGlobal));
+ char *pSrc = reinterpret_cast<char *> (LockResource(hResData));
+ if (!pSrc || !pDest)
+ {
+  TRACE(_T("Load (resource): Error locking memory\n"));
+  GlobalFree(hGlobal);
+  FreeResource(hResData);
+  return FALSE;
+ };
+ CopyMemory(pDest,pSrc,dwSize);
+ FreeResource(hResData);
+ GlobalUnlock(hGlobal);
+
+ BOOL bRetValue = Load(hGlobal,dwSize);
+ GlobalFree(hGlobal);
+ return bRetValue;
+}
+
+void CPictureEx::ResetDataPointer()
+{
+ // skip header and logical screen descriptor
+ m_nCurrOffset = 
+  sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
+}
+
+BOOL CPictureEx::SkipNextGraphicBlock()
+{
+ if (!m_pRawData) return FALSE;
+
+ // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
+
+ enum GIFBlockTypes nBlock;
+
+ nBlock = GetNextBlock();
+
+ while ((nBlock != BLOCK_CONTROLEXT) &&
+     (nBlock != BLOCK_IMAGE) &&
+     (nBlock != BLOCK_PLAINTEXT) &&
+     (nBlock != BLOCK_UNKNOWN) &&
+     (nBlock != BLOCK_TRAILER) )
+ {
+  if (!SkipNextBlock()) return NULL;
+  nBlock = GetNextBlock();
+ };
+
+ if ((nBlock == BLOCK_UNKNOWN) ||
+  (nBlock == BLOCK_TRAILER))
+  return FALSE;
+
+ // it's either a control ext.block, an image or a plain text
+
+ if (GetNextBlockLen() <= 0) return FALSE;
+
+ if (nBlock == BLOCK_CONTROLEXT)
+ {
+  if (!SkipNextBlock()) return FALSE;
+  nBlock = GetNextBlock();
+
+  // skip everything until we meet an image block or a plain-text block
+  while ((nBlock != BLOCK_IMAGE) &&
+      (nBlock != BLOCK_PLAINTEXT) &&
+      (nBlock != BLOCK_UNKNOWN) &&
+      (nBlock != BLOCK_TRAILER) )
+  {
+   if (!SkipNextBlock()) return NULL;
+   nBlock = GetNextBlock();
+  };
+
+  if ((nBlock == BLOCK_UNKNOWN) ||
+   (nBlock == BLOCK_TRAILER))
+   return FALSE;
+ };
+
+ // skip the found data block (image or plain-text)
+ if (!SkipNextBlock()) return FALSE;
+
+ return TRUE;
+}
+
+UINT CPictureEx::GetSubBlocksLen(UINT nStartingOffset) const
+{
+ UINT nRet = 0;
+ UINT nCurOffset = nStartingOffset;
+ 
+ while (m_pRawData[nCurOffset] != 0)
+ {
+  nRet += m_pRawData[nCurOffset]+1;
+  nCurOffset += m_pRawData[nCurOffset]+1;
+ };
+
+ return nRet+1;
+}
+
+enum CPictureEx::GIFBlockTypes CPictureEx::GetNextBlock() const
+{
+ switch(m_pRawData[m_nCurrOffset])
+ {
+ case 0x21:
+ // extension block
+  switch(m_pRawData[m_nCurrOffset+1])
+  {
+  case 0x01:
+  // plain text extension
+   return BLOCK_PLAINTEXT;
+   break;
+
+  case 0xF9:
+  // graphic control extension
+   return BLOCK_CONTROLEXT;
+   break;
+
+  case 0xFE:
+  // comment extension
+   return BLOCK_COMMEXT;
+   break;
+
+  case 0xFF:
+  // application extension
+   return BLOCK_APPEXT;
+   break;
+  };
+  break;
+ 
+ case 0x3B:
+ // trailer
+  return BLOCK_TRAILER;
+  break;
+
+ case 0x2C:
+ // image data
+  return BLOCK_IMAGE;
+  break;
+ };
+
+ return BLOCK_UNKNOWN;
+}
+
+BOOL CPictureEx::SkipNextBlock()
+{
+ if (!m_pRawData) return FALSE;
+
+ int nLen = GetNextBlockLen();
+ if ((nLen <= 0) || ((m_nCurrOffset+nLen) > m_nDataSize))
+  return FALSE;
+
+ m_nCurrOffset += nLen;
+ return TRUE;
+}
+
+int CPictureEx::GetNextBlockLen() const
+{
+ GIFBlockTypes nBlock = GetNextBlock();
+
+ int nTmp;
+
+ switch(nBlock)
+ {
+ case BLOCK_UNKNOWN:
+  return -1;
+  break;
+
+ case BLOCK_TRAILER:
+  return 1;
+  break;
+
+ case BLOCK_APPEXT:
+  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFAppExtension));
+  if (nTmp > 0)
+   return sizeof(TGIFAppExtension)+nTmp;
+  break;
+
+ case BLOCK_COMMEXT:
+  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFCommentExt));
+  if (nTmp > 0)
+   return sizeof(TGIFCommentExt)+nTmp;
+  break;
+
+ case BLOCK_CONTROLEXT:
+  return sizeof(TGIFControlExt);
+  break;
+
+ case BLOCK_PLAINTEXT:
+  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFPlainTextExt));
+  if (nTmp > 0)
+   return sizeof(TGIFPlainTextExt)+nTmp;
+  break;
+
+ case BLOCK_IMAGE:
+  TGIFImageDescriptor *pIDescr = 
+   reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
+  int nLCTSize = (int)
+   (pIDescr->GetPackedValue(ID_PACKED_LOCALCT)*3*
+   (1 << (pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE)+1)));
+
+  int nTmp = GetSubBlocksLen(m_nCurrOffset+
+   sizeof(TGIFImageDescriptor) + nLCTSize + 1);
+  if (nTmp > 0)
+   return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp;
+  break;
+ };
+
+ return 0;
+}
+
+UINT WINAPI CPictureEx::_ThreadAnimation(LPVOID pParam)
+{
+ ASSERT(pParam);
+ CPictureEx *pPic = reinterpret_cast<CPictureEx *> (pParam);
+
+ pPic->m_bIsPlaying = TRUE;
+ pPic->ThreadAnimation();
+ pPic->m_bIsPlaying = FALSE;
+
+ // this thread has finished its work so we close the handle
+ CloseHandle(pPic->m_hThread); 
+ // and init the handle to zero (so that Stop() doesn't Wait on it)
+ pPic->m_hThread = 0;
+ return 0;
+}
+
+void CPictureEx::ThreadAnimation()
+{
+ // first, restore background (for stop/draw support)
+ // disposal method #2
+ if (m_arrFrames[m_nCurrFrame].m_nDisposal == 2)
+ {
+  HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
+  if (hBrush)
+  {
+   RECT rect = {
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cx + m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cy + m_arrFrames[m_nCurrFrame].m_frameSize.cy };
+   FillRect(m_hMemDC,&rect,hBrush);
+   DeleteObject(hBrush);
+  };
+ } 
+ else
+  // disposal method #3
+  if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) )
+  {
+   // put it back
+   BitBlt(m_hMemDC,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+    m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+    m_arrFrames[m_nCurrFrame].m_frameSize.cy,
+    m_hDispMemDC,0,0, SRCCOPY);
+   // init variables
+   SelectObject(m_hDispMemDC,m_hDispOldBM);
+   DeleteDC(m_hDispMemDC); m_hDispMemDC = NULL;
+   DeleteObject(m_hDispMemBM); m_hDispMemBM = NULL;
+  };
+
+ while (!m_bExitThread)
+ {
+  if (m_arrFrames[m_nCurrFrame].m_pPicture)
+  {
+  ///////////////////////////////////////////////////////
+  // Before rendering a frame we should take care of what's 
+  // behind that frame. TFrame::m_nDisposal will be our guide:
+  //   0 - no disposal specified (do nothing)
+  //   1 - do not dispose (again, do nothing)
+  //   2 - restore to background color (m_clrBackground)
+  //   3 - restore to previous
+
+   //////// disposal method #3
+   if (m_arrFrames[m_nCurrFrame].m_nDisposal == 3)
+   {
+    // prepare a memory DC and store the background in it
+    m_hDispMemDC = CreateCompatibleDC(m_hMemDC);
+    m_hDispMemBM = CreateCompatibleBitmap(m_hMemDC,
+       m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+       m_arrFrames[m_nCurrFrame].m_frameSize.cy);
+    
+    if (m_hDispMemDC && m_hDispMemBM)
+    {
+     m_hDispOldBM = reinterpret_cast<HBITMAP> (SelectObject(m_hDispMemDC,m_hDispMemBM));
+     BitBlt(m_hDispMemDC,0,0,
+      m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+      m_arrFrames[m_nCurrFrame].m_frameSize.cy,
+      m_hMemDC,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+      SRCCOPY);
+    };
+   };
+   ///////////////////////
+
+   long hmWidth;
+   long hmHeight;
+   m_arrFrames[m_nCurrFrame].m_pPicture->get_Width(&hmWidth);
+   m_arrFrames[m_nCurrFrame].m_pPicture->get_Height(&hmHeight);
+
+   if (m_arrFrames[m_nCurrFrame].m_pPicture->Render(m_hMemDC, 
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cx, 
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cy, 
+    m_arrFrames[m_nCurrFrame].m_frameSize.cx, 
+    m_arrFrames[m_nCurrFrame].m_frameSize.cy, 
+    0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
+   {
+    Invalidate(FALSE);
+   };
+   
+   if (m_bExitThread) break;
+
+   // if the delay time is too short (like in old GIFs), wait for 100ms
+   if (m_arrFrames[m_nCurrFrame].m_nDelay < 5) 
+    WaitForSingleObject(m_hExitEvent, 100);
+   else
+    WaitForSingleObject(m_hExitEvent, 10*m_arrFrames[m_nCurrFrame].m_nDelay);
+
+   if (m_bExitThread) break;
+
+   // disposal method #2
+   if (m_arrFrames[m_nCurrFrame].m_nDisposal == 2)
+   {
+    HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
+    if (hBrush)
+    {
+     RECT rect = {
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cx + m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cy + m_arrFrames[m_nCurrFrame].m_frameSize.cy };
+     FillRect(m_hMemDC,&rect,hBrush);
+     DeleteObject(hBrush);
+    };
+   } 
+   else
+    if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) )
+    {
+     // put it back
+     BitBlt(m_hMemDC,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+      m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+      m_arrFrames[m_nCurrFrame].m_frameSize.cy,
+      m_hDispMemDC,0,0, SRCCOPY);
+     // init variables
+     SelectObject(m_hDispMemDC,m_hDispOldBM);
+     DeleteDC(m_hDispMemDC); m_hDispMemDC = NULL;
+     DeleteObject(m_hDispMemBM); m_hDispMemBM = NULL;
+    };
+  };
+  m_nCurrFrame++;
+  if (m_nCurrFrame == m_arrFrames.size())
+  {
+   m_nCurrFrame
+    = 0; 
+  // init the screen for the first frame,
+   HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
+   if (hBrush)
+   {
+    RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
+    FillRect(m_hMemDC,&rect,hBrush);
+    DeleteObject(hBrush);
+   };
+  };
+ };
+}
+
+void CPictureEx::Stop()
+{
+ m_bIsPlaying = FALSE;
+ m_bExitThread = TRUE;
+ SetEvent(m_hExitEvent);
+ if (m_hThread)
+ {
+  // we'll wait for 5 seconds then continue execution
+  WaitForSingleObject(m_hThread,5000);
+  CloseHandle(m_hThread);
+  m_hThread = NULL;
+ }
+
+ // make it possible to Draw() again
+ ResetEvent(m_hExitEvent);
+ m_bExitThread = FALSE;
+}
+
+HGLOBAL CPictureEx::GetNextGraphicBlock(UINT *pBlockLen, 
+ UINT *pDelay, SIZE *pBlockSize, SIZE *pBlockOffset, 
+ UINT *pDisposal)
+{
+ if (!m_pRawData) return NULL;
+
+ // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
+
+ *pDisposal = 0;
+ enum GIFBlockTypes nBlock;
+ nBlock = GetNextBlock();
+
+ while (
+  (nBlock != BLOCK_CONTROLEXT) &&
+  (nBlock != BLOCK_IMAGE) &&
+  (nBlock != BLOCK_PLAINTEXT) &&
+  (nBlock != BLOCK_UNKNOWN) &&
+  (nBlock != BLOCK_TRAILER)
+  )
+ {
+  if (!SkipNextBlock()) return NULL;
+  nBlock = GetNextBlock();
+ };
+
+ if ((nBlock == BLOCK_UNKNOWN) ||
+  (nBlock == BLOCK_TRAILER))
+  return NULL;
+
+ // it's either a control ext.block, an image or a plain text
+
+ int nStart = m_nCurrOffset;
+ int nBlockLen = GetNextBlockLen();
+
+ if (nBlockLen <= 0) return NULL;
+
+ if (nBlock == BLOCK_CONTROLEXT)
+ {
+  // get the following data
+  TGIFControlExt *pControl = 
+   reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
+  // store delay time
+  *pDelay = pControl->m_wDelayTime;
+  // store disposal method
+  *pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL);
+
+  if (!SkipNextBlock()) return NULL;
+  nBlock = GetNextBlock();
+  
+  // skip everything until we find data to display 
+  // (image block or plain-text block)
+  
+  while (
+   (nBlock != BLOCK_IMAGE) &&
+   (nBlock != BLOCK_PLAINTEXT) &&
+   (nBlock != BLOCK_UNKNOWN) &&
+   (nBlock != BLOCK_TRAILER)
+   )
+  {
+   if (!SkipNextBlock()) return NULL;
+   nBlock = GetNextBlock();
+   nBlockLen += GetNextBlockLen();
+  };
+
+  if ((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER))
+   return NULL;
+  nBlockLen += GetNextBlockLen();
+ }
+ else
+  *pDelay = -1; // to indicate that there was no delay value
+
+ if (nBlock == BLOCK_IMAGE)
+ {
+  // store size and offsets
+  TGIFImageDescriptor *pImage = 
+   reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
+  pBlockSize->cx = pImage->m_wWidth;
+  pBlockSize->cy = pImage->m_wHeight;
+  pBlockOffset->cx = pImage->m_wLeftPos;
+  pBlockOffset->cy = pImage->m_wTopPos;
+ };
+
+ if (!SkipNextBlock()) return NULL;
+
+ HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED,
+  sizeof(TGIFHeader) +
+  sizeof(TGIFLSDescriptor) +
+  m_nGlobalCTSize +
+  nBlockLen + 
+  1);  // for the trailer
+
+ if (!hGlobal) return NULL;
+
+ int nOffset = 0; 
+
+ // GMEM_FIXED means we get a pointer
+ unsigned char *pGlobal = reinterpret_cast<unsigned char *> (hGlobal);
+
+ CopyMemory(pGlobal,m_pRawData, 
+  sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize);
+ nOffset += sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
+
+ CopyMemory(pGlobal + nOffset,&m_pRawData[nStart], nBlockLen);
+ nOffset += nBlockLen;
+
+ pGlobal[nOffset] = 0x3B; // trailer
+ nOffset++;
+
+ *pBlockLen = nOffset;
+
+ return hGlobal;
+}
+
+BOOL CPictureEx::IsGIF() const
+{
+ return m_bIsGIF;
+}
+
+BOOL CPictureEx::IsAnimatedGIF() const
+{
+ return (m_bIsGIF && (m_arrFrames.size() > 1));
+}
+
+BOOL CPictureEx::IsPlaying() const
+{
+ return m_bIsPlaying;
+}
+
+int CPictureEx::GetFrameCount() const
+{
+ if (!IsAnimatedGIF())
+  return 0;
+
+ return m_arrFrames.size();
+}
+
+COLORREF CPictureEx::GetBkColor() const
+{
+ return m_clrBackground;
+}
+
+void CPictureEx::OnPaint() 
+{
+ CPaintDC dc(this); // device context for painting
+
+ LONG nPaintWidth = m_PaintRect.right-m_PaintRect.left;
+
+ if (nPaintWidth > 0)
+ {
+  LONG nPaintHeight = m_PaintRect.bottom - m_PaintRect.top;
+  ::BitBlt(dc.m_hDC, 0, 0, nPaintWidth, nPaintHeight, 
+   m_hMemDC, m_PaintRect.left, m_PaintRect.top, SRCCOPY);
+ }
+ else
+ {
+  ::BitBlt(dc.m_hDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy,
+   m_hMemDC, 0, 0, SRCCOPY);
+ };
+}
+
+BOOL CPictureEx::PrepareDC(int nWidth, int nHeight)
+{
+ SetWindowPos(NULL,0,0,nWidth,nHeight,SWP_NOMOVE | SWP_NOZORDER);
+
+ HDC hWinDC = ::GetDC(m_hWnd);
+ if (!hWinDC) return FALSE;
+ 
+ m_hMemDC = CreateCompatibleDC(hWinDC);
+ if (!m_hMemDC) 
+ {
+  ::ReleaseDC(m_hWnd,hWinDC);
+  return FALSE;
+ };
+
+ m_hBitmap  = CreateCompatibleBitmap(hWinDC,nWidth,nHeight);
+ if (!m_hBitmap) 
+ {
+  ::ReleaseDC(m_hWnd,hWinDC);
+  ::DeleteDC(m_hMemDC);
+  return FALSE;
+ };
+
+ m_hOldBitmap = reinterpret_cast<HBITMAP> 
+      (SelectObject(m_hMemDC,m_hBitmap));
+ 
+ // fill the background
+ m_clrBackground = GetSysColor(COLOR_3DFACE);
+ RECT rect = {0,0,nWidth,nHeight};
+ FillRect(m_hMemDC,&rect,(HBRUSH)(COLOR_WINDOW));
+
+ ::ReleaseDC(m_hWnd,hWinDC);
+ m_bIsInitialized = TRUE;
+ return TRUE;
+}
+
+void CPictureEx::OnDestroy() 
+{
+ Stop(); 
+ CStatic::OnDestroy();
+}
+
+void CPictureEx::SetBkColor(COLORREF clr)
+{
+ if (!m_bIsInitialized) return;
+
+ m_clrBackground = clr;
+
+ HBRUSH hBrush = CreateSolidBrush(clr);
+ if (hBrush)
+ {
+  RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
+  FillRect(m_hMemDC,&rect,hBrush);
+  DeleteObject(hBrush);
+ };
+}
+
+#ifdef GIF_TRACING
+void CPictureEx::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize)
+{
+ CFile file;
+
+ if (!file.Open(szFileName,
+   CFile::modeCreate |
+   CFile::modeWrite |
+   CFile::shareDenyNone))
+ {
+  TRACE(_T("WriteData: Error creating file %s\n"),szFileName);
+  return;
+ };
+
+ char *pData = reinterpret_cast<char *> (GlobalLock(hData));
+ if (!pData)
+ {
+  TRACE(_T("WriteData: Error locking memory\n"));
+  return;
+ };
+
+ TRY
+ {
+  file.Write(pData,dwSize);
+ }
+ CATCH(CFileException, e);                                          
+ {
+  TRACE(_T("WriteData: An exception occured while writing to the file %s\n"),
+   szFileName);
+  e->Delete();
+  GlobalUnlock(hData);
+  file.Close();
+  return;
+ }
+ END_CATCH
+ 
+ GlobalUnlock(hData);
+ file.Close();
+}
+
+void CPictureEx::EnumGIFBlocks()
+{
+ enum GIFBlockTypes nBlock;
+
+ ResetDataPointer();
+ while(m_nCurrOffset < m_nDataSize)
+ {
+  nBlock = GetNextBlock();
+  switch(nBlock)
+  {
+  case BLOCK_UNKNOWN:
+   TRACE(_T("- Unknown block\n"));
+   return;
+   break;
+
+  case BLOCK_TRAILER:
+   TRACE(_T("- Trailer block\n"));
+   break;
+
+  case BLOCK_APPEXT:
+   TRACE(_T("- Application extension block\n"));
+   break;
+
+  case BLOCK_COMMEXT:
+   TRACE(_T("- Comment extension block\n"));
+   break;
+
+  case BLOCK_CONTROLEXT:
+   {
+   TGIFControlExt *pControl = 
+    reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
+   TRACE(_T("- Graphic control extension block (delay %d, disposal %d)\n"),
+     pControl->m_wDelayTime, pControl->GetPackedValue(GCX_PACKED_DISPOSAL));
+   };
+   break;
+
+  case BLOCK_PLAINTEXT:
+   TRACE(_T("- Plain text extension block\n"));
+   break;
+
+  case BLOCK_IMAGE:
+   TGIFImageDescriptor *pIDescr = 
+    reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
+   TRACE(_T("- Image data block (%dx%d  %d,%d)\n"),
+     pIDescr->m_wWidth,
+     pIDescr->m_wHeight,
+     pIDescr->m_wLeftPos,
+     pIDescr->m_wTopPos);
+   break;
+  };
+
+  SkipNextBlock(); 
+ };
+
+ TRACE(_T("\n"));
+}
+#endif // GIF_TRACING
+
+BOOL CPictureEx::SetPaintRect(const RECT *lpRect)
+{
+ return CopyRect(&m_PaintRect, lpRect);
+}
+
+BOOL CPictureEx::GetPaintRect(RECT *lpRect)
+{
+ return CopyRect(lpRect, &m_PaintRect);
+}

+ 305 - 0
SATHelper/UI/PictureEx.h

@@ -0,0 +1,305 @@
+//////////////////////////////////////////////////////////////////////
+// PictureEx.cpp: implementation of the CPictureEx class.
+//
+// Picture displaying control with support for the following formats:
+// GIF (including animated GIF87a and GIF89a), JPEG, BMP, WMF, ICO, CUR
+// 
+// Written by Oleg Bykov (oleg_bykoff@rsdn.ru)
+// Copyright (c) 2001
+//
+// To use CPictureEx, follow these steps:
+//   - place a static control on your dialog (either a text or a bitmap)
+//   - change its identifier to something else (e.g. IDC_MYPIC)
+//   - associate a CStatic with it using ClassWizard
+//   - in your dialog's header file replace CStatic with CPictureEx
+//     (don't forget to #include "PictureEx.h" and add 
+//     PictureEx.h and PictureEx.cpp to your project)
+//   - call one of the overloaded CPictureEx::Load() functions somewhere
+//     (OnInitDialog is a good place to start)
+//   - if the preceding Load() succeeded call Draw()
+//  
+// You can also add the control by defining a member variable of type 
+// CPictureEx, calling CPictureEx::Create (derived from CStatic), then 
+// CPictureEx::Load and CPictureEx::Draw.
+//
+// By default, the control initializes its background to COLOR_3DFACE
+// (see CPictureEx::PrepareDC()). You can change the background by
+// calling CPictureEx::SetBkColor(COLORREF) after CPictureEx::Load().
+//
+// I decided to leave in the class the functions to write separate frames from 
+// animated GIF to disk. If you want to use them, uncomment #define GIF_TRACING 
+// and an appropriate section in CPictureEx::Load(HGLOBAL, DWORD). These functions 
+// won't be compiled and linked to your project unless you uncomment #define GIF_TRACING,
+// so you don't have to worry.
+// 
+// Warning: this code hasn't been subject to a heavy testing, so
+// use it on your own risk. The author accepts no liability for the 
+// possible damage caused by this code.
+//
+// Version 1.0  7 Aug 2001
+//              Initial release
+//
+// Version 1.1  6 Sept 2001
+//              ATL version of the class
+//
+// Version 1.2  14 Oct 2001
+//              - Fixed a problem with loading GIFs from resources
+//                in MFC-version of the class for multi-modules apps.
+//                Thanks to Ruben Avila-Carretero for finding this out.
+//
+//              - Got rid of waitable timer in ThreadAnimation()
+//                Now CPictureEx[Wnd] works in Win95 too.
+//                Thanks to Alex Egiazarov and Wayne King for the idea.
+//
+//              - Fixed a visual glitch of using SetBkColor.
+//                Thanks to Kwangjin Lee for finding this out.
+//
+// Version 1.3  10 Nov 2001
+//              - Fixed a DC leak. One DC leaked per each UnLoad()
+//                (forgot to put a ReleaseDC() in the end of 
+//                CPictureExWnd::PrepareDC() function).
+//
+//              - Now it is possible to set a clipping rectangle using
+//                CPictureEx[Wnd]::SetPaintRect(const LPRECT) function.
+//                The LPRECT parameter tells the class what portion of
+//                a picture should it display. If the clipping rect is 
+//                not set, the whole picture is shown.
+//                Thanks to Fabrice Rodriguez for the idea.
+//
+//              - Added support for Stop/Draw. Now you can Stop() an
+//                animated GIF, then Draw() it again, it will continue
+//                animation from the frame it was stopped on. You can 
+//                also know if a GIF is currently playing with the 
+//                IsPlaying() function.
+//             
+//              - Got rid of math.h and made m_bExitThread volatile. 
+//                Thanks to Piotr Sawicki for the suggestion.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)
+#define AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <vector>
+
+//#define GIF_TRACING  // uncomment it if you want detailed TRACEs
+
+class CPictureEx : public CStatic
+{
+public:
+
+	struct TFrame    // structure that keeps a single frame info
+	{
+		IPicture *m_pPicture;  // pointer to the interface used for drawing
+		SIZE     m_frameSize;
+		SIZE     m_frameOffset;
+		UINT     m_nDelay;     // delay (in 1/100s of a second)
+		UINT     m_nDisposal;  // disposal method
+	};
+
+#pragma pack(1)   // turn byte alignment on
+
+	enum GIFBlockTypes
+	{
+		BLOCK_UNKNOWN,
+		BLOCK_APPEXT,
+		BLOCK_COMMEXT,
+		BLOCK_CONTROLEXT,
+		BLOCK_PLAINTEXT,
+		BLOCK_IMAGE,
+		BLOCK_TRAILER
+	};
+
+	enum ControlExtValues // graphic control extension packed field values
+	{
+		GCX_PACKED_DISPOSAL,  // disposal method
+		GCX_PACKED_USERINPUT,
+		GCX_PACKED_TRANSPCOLOR
+	};
+
+	enum LSDPackedValues  // logical screen descriptor packed field values
+	{
+		LSD_PACKED_GLOBALCT,
+		LSD_PACKED_CRESOLUTION,
+		LSD_PACKED_SORT,
+		LSD_PACKED_GLOBALCTSIZE
+	};
+
+	enum IDPackedValues   // image descriptor packed field values
+	{
+		ID_PACKED_LOCALCT,
+		ID_PACKED_INTERLACE,
+		ID_PACKED_SORT,
+		ID_PACKED_LOCALCTSIZE
+	};
+
+	struct TGIFHeader       // GIF header  
+	{
+		char m_cSignature[3]; // Signature - Identifies the GIF Data Stream
+		// This field contains the fixed value 'GIF'
+		char m_cVersion[3]; // Version number. May be one of the following:
+		// "87a" or "89a"
+	};
+
+	struct TGIFLSDescriptor // Logical Screen Descriptor
+	{
+		WORD m_wWidth; // 2 bytes. Logical screen width
+		WORD m_wHeight; // 2 bytes. Logical screen height
+
+		unsigned char m_cPacked;      // packed field 
+
+		unsigned char m_cBkIndex;     // 1 byte. Background color index
+		unsigned char m_cPixelAspect; // 1 byte. Pixel aspect ratio
+		inline int GetPackedValue(enum LSDPackedValues Value);
+	};
+
+	struct TGIFAppExtension // application extension block
+	{
+		unsigned char m_cExtIntroducer; // extension introducer (0x21)
+		unsigned char m_cExtLabel; // app. extension label (0xFF)
+		unsigned char m_cBlockSize; // fixed value of 11
+		char m_cAppIdentifier[8];   // application identifier
+		char m_cAppAuth[3];  // application authentication code
+	};
+
+	struct TGIFControlExt // graphic control extension block
+	{
+		unsigned char m_cExtIntroducer; // extension introducer (0x21)
+		unsigned char m_cControlLabel;  // control extension label (0xF9)
+		unsigned char m_cBlockSize; // fixed value of 4
+		unsigned char m_cPacked;    // packed field
+		WORD m_wDelayTime; // delay time
+		unsigned char m_cTColorIndex; // transparent color index
+		unsigned char m_cBlockTerm;   // block terminator (0x00)
+	public:
+		inline int GetPackedValue(enum ControlExtValues Value);
+	};
+
+	struct TGIFCommentExt  // comment extension block
+	{
+		unsigned char m_cExtIntroducer; // extension introducer (0x21)
+		unsigned char m_cCommentLabel;  // comment extension label (0xFE)
+	};
+
+	struct TGIFPlainTextExt // plain text extension block
+	{
+		unsigned char m_cExtIntroducer;  // extension introducer (0x21)
+		unsigned char m_cPlainTextLabel; // text extension label (0x01)
+		unsigned char m_cBlockSize; // fixed value of 12
+		WORD m_wLeftPos;    // text grid left position
+		WORD m_wTopPos;     // text grid top position
+		WORD m_wGridWidth;  // text grid width
+		WORD m_wGridHeight; // text grid height
+		unsigned char m_cCellWidth;  // character cell width
+		unsigned char m_cCellHeight; // character cell height
+		unsigned char m_cFgColor; // text foreground color index
+		unsigned char m_cBkColor; // text background color index
+	};
+
+	struct TGIFImageDescriptor // image descriptor block
+	{
+		unsigned char m_cImageSeparator; // image separator byte (0x2C)
+		WORD m_wLeftPos; // image left position
+		WORD m_wTopPos;  // image top position
+		WORD m_wWidth;   // image width
+		WORD m_wHeight;  // image height
+		unsigned char m_cPacked; // packed field
+		inline int GetPackedValue(enum IDPackedValues Value);
+	};
+
+#pragma pack() // turn byte alignment off
+
+public:
+	BOOL GetPaintRect(RECT *lpRect);
+	BOOL SetPaintRect(const RECT *lpRect);
+	CPictureEx();
+	virtual ~CPictureEx();
+	void Stop();   // stops animation
+	void UnLoad(); // stops animation plus releases all resources
+
+	BOOL IsGIF() const;
+	BOOL IsPlaying() const;
+	BOOL IsAnimatedGIF() const;
+	SIZE GetSize() const;
+	int GetFrameCount() const;
+	COLORREF GetBkColor() const;
+	void SetBkColor(COLORREF clr);
+
+	// draws the picture (starts an animation thread if needed)
+	// if an animation was previously stopped by Stop(),
+	// continues it from the last displayed frame
+	BOOL Draw();
+
+	// loads a picture from a file
+	// i.e. Load(_T("mypic.gif"));
+	BOOL Load(LPCTSTR szFileName);
+
+	// loads a picture from a global memory block (allocated by GlobalAlloc)
+	// Warning: this function DOES NOT free the global memory, pointed to by hGlobal
+	BOOL Load(HGLOBAL hGlobal, DWORD dwSize);
+
+	// loads a picture from a program resource
+	// i.e. Load(MAKEINTRESOURCE(IDR_MYPIC),_T("GIFTYPE"));
+	BOOL Load(LPCTSTR szResourceName,LPCTSTR szResourceType);
+
+protected:
+
+#ifdef GIF_TRACING
+	void EnumGIFBlocks();
+	void WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize);
+#endif // GIF_TRACING
+
+	RECT m_PaintRect;
+	SIZE m_PictureSize;
+	COLORREF m_clrBackground;
+	UINT m_nCurrFrame;
+	UINT m_nDataSize;
+	UINT m_nCurrOffset;
+	UINT m_nGlobalCTSize;
+	BOOL m_bIsGIF;
+	BOOL m_bIsPlaying;
+	volatile BOOL m_bExitThread;
+	BOOL m_bIsInitialized;
+	HDC m_hMemDC;
+
+	HDC m_hDispMemDC;
+	HBITMAP m_hDispMemBM;
+	HBITMAP m_hDispOldBM;
+
+	HBITMAP m_hBitmap;
+	HBITMAP m_hOldBitmap;
+	HANDLE m_hThread;
+	HANDLE m_hExitEvent;
+	IPicture * m_pPicture;
+	TGIFHeader * m_pGIFHeader;
+	unsigned char * m_pRawData;
+	TGIFLSDescriptor * m_pGIFLSDescriptor;
+	std::vector<TFrame> m_arrFrames;
+
+	void ThreadAnimation();
+	static UINT WINAPI _ThreadAnimation(LPVOID pParam);
+
+	int GetNextBlockLen() const;
+	BOOL SkipNextBlock();
+	BOOL SkipNextGraphicBlock();
+	BOOL PrepareDC(int nWidth, int nHeight);
+	void ResetDataPointer();
+	enum GIFBlockTypes GetNextBlock() const;
+	UINT GetSubBlocksLen(UINT nStartingOffset) const;
+	HGLOBAL GetNextGraphicBlock(UINT *pBlockLen, UINT *pDelay, 
+		SIZE *pBlockSize, SIZE *pBlockOffset, UINT *pDisposal);
+
+	// Generated message map functions
+	//{{AFX_MSG(CPictureEx)
+	afx_msg void OnDestroy();
+	afx_msg void OnPaint();
+	//}}AFX_MSG
+
+	DECLARE_MESSAGE_MAP()
+};
+
+#endif // !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)

+ 1364 - 0
SATHelper/UI/SubLabel.cpp

@@ -0,0 +1,1364 @@
+// Label.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "SubLabel.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+BEGIN_MESSAGE_MAP(CLabel, CStatic)
+	//{{AFX_MSG_MAP(CLabel)
+	ON_WM_TIMER()
+	ON_WM_LBUTTONDOWN()
+	ON_WM_SETCURSOR()
+	ON_WM_SYSCOLORCHANGE()
+	ON_WM_PAINT()
+	ON_WM_ERASEBKGND()
+	//}}AFX_MSG_MAP
+	ON_WM_CTLCOLOR_REFLECT()
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CLabel Version 1.2
+//
+// From now on I'll try to keep a log of fixes and enhancements...
+// 
+// The new feature were added due to the response of people.
+// All I ask is to all you programmers out there, is if you add, fix or
+// enhance this code, sent me a copy and I'll send the copy on to www.codeproject.com
+//
+// Happy Software Engineer :)
+// 
+// New features include:
+//
+// A. Support for 3D Fonts
+// B. Support for background transparency
+// C. More comments provided
+// D. If alignment is 'centered' and the window text is seperated by '\r\n'
+//	  the will be centered accordingly - requested by someone @ nasa ;)
+// E. Support for font rotation.
+// F. Respond to System Color Change
+// G. OnPaint improved performance - using Double Buffering Technique
+//
+// Thanks to:
+// Mark McDowell	- For suggestion on 'Increasing the flexibility of "hypertext" setting...'
+// Erich Ruth		- For suggestion on 'Font Rotation'
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// CLabel Version 1.3
+//
+// A. Added SS_LEFTNOWORDWRAP to include wordwrap
+// B. Fix repainting problem 
+// C. Fix SetBkColor
+// D. Added SS_CENTER
+
+// Thanks to:
+// Marius						- Added styling problem.
+// Azing Vondeling & Broker		- Spotting painting Problem.
+// Mel Stober					- Back Color & SS_CENTER
+// 
+/////////////////////////////////////////////////////////////////////////////
+// CLabel Version 1.4
+//
+// A. Fix to transparency mode
+// B. Added new SetText3DHiliteColor to change the 3D Font face color - default is white.
+// 
+// Thanks to:
+// michael.groeger				- Spotting Transparency with other controls bug.
+//
+//
+/////////////////////////////////////////////////////////////////////////////
+// CLabel Version 1.5
+//
+// A. Sanity handle check
+// B. Support Interface Charset
+// C. Check compilition with _UNICODE
+// D. Fix hyperlink feature
+// E. Support default Dialog Font
+// F. Inclusion of SS_OWNERDRAW via control creation and subclassing
+// G. Modification to Text aligmnent code
+// H. New background gradient fill function
+// 
+// Thanks to:
+// Steve Kowald				- Using null handles 
+// Alan Chan				- Supporting International Windows
+// Dieter Fauth				- Request for default Dialog font
+// Herb Illfelder			- Text Alignment code
+// 
+/////////////////////////////////////////////////////////////////////////////
+// CLabel Version 1.6
+// Jeroen Roosendaal		- SetFont suggestion
+// Laurent					- Spotting SelectObject bugs
+// Bernie					- Fix PreCreateWindow bug
+// Jignesh I. Patel			- Added expanded tabs feature
+// Jim Farmelant 			- Fix SetText crash
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::CLabel
+//
+// Description:		Default contructor
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel::CLabel()
+{
+	m_crText = GetSysColor(COLOR_WINDOWTEXT);
+
+	// 1.1
+	m_hBackBrush = NULL;
+
+
+	m_crHiColor =		0;
+	m_crLoColor	=		0;
+
+	m_bTimer =			FALSE;
+	m_bState =			FALSE;
+	m_bTransparent =	FALSE;
+	m_Link =			LinkNone;
+	m_hCursor =			NULL;
+	m_Type =			None;
+	m_bFont3d =			FALSE;
+	m_bNotifyParent =	FALSE;
+	m_bToolTips =		FALSE;
+	m_bRotation =		FALSE;
+	m_fillmode =		Normal;
+	m_cr3DHiliteColor =	RGB(255,255,255);
+	m_strText =			_T("");
+	m_hwndBrush = ::CreateSolidBrush(GetSysColor(COLOR_3DFACE));
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::~CLabel
+//
+// Description:		
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+// NT ALMOND				15092000	1.5		Handle Check
+//////////////////////////////////////////////////////////////////////////
+CLabel::~CLabel()
+{
+	// Clean up
+	m_font.DeleteObject();
+	::DeleteObject(m_hwndBrush);
+
+	// Stop Checking complaining
+	if (m_hBackBrush)
+		::DeleteObject(m_hBackBrush);
+
+}
+
+void CLabel::UpdateSurface()
+{
+	CRect (rc);
+	GetWindowRect(rc);
+	RedrawWindow();
+
+	GetParent()->ScreenToClient(rc);
+	GetParent()->InvalidateRect(rc,TRUE);
+	GetParent()->UpdateWindow();
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::ReconstructFont
+//
+// Description:		Helper function to build font after it was changed
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			PROTECTED
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+void CLabel::ReconstructFont()
+{
+	m_font.DeleteObject();
+	BOOL bCreated = m_font.CreateFontIndirect(&m_lf);
+
+	ASSERT(bCreated);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::OnPaint
+//
+// Description:		Handles all the drawing code for the label
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			Called by Windows... not by USER
+//					Probably needs tiding up a some point.
+//					Different states will require this code to be reworked.
+//
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				22/10/98    1.0     Origin
+// NT ALMOND				15092000	1.5		Handle Check
+// NT ALMOND				15092000	1.5		Alignment mods
+// NT ALMOND				15092000	1.5		Gradient Fill Mode
+// NT ALMOND				02072002	1.6		Fill SelectObject bugs
+// NT ALMOND				02072002	1.6		Added to expand tabs
+//////////////////////////////////////////////////////////////////////////
+
+void CLabel::OnPaint() 
+{
+	CPaintDC dc(this); // device context for painting
+
+	DWORD dwFlags = 0;
+
+	CRect rc;
+	GetClientRect(rc);
+	if (m_strText.IsEmpty())
+		GetWindowText(m_strText);
+	CBitmap bmp;
+
+
+	///////////////////////////////////////////////////////
+	//
+	// Set up for double buffering...
+	//
+	CDC* pDCMem;
+	CBitmap*	pOldBitmap = NULL;
+
+	if (!m_bTransparent)
+	{
+		pDCMem = new CDC;
+		pDCMem->CreateCompatibleDC(&dc);
+		bmp.CreateCompatibleBitmap(&dc,rc.Width(),rc.Height());
+		pOldBitmap = pDCMem->SelectObject(&bmp);
+	}
+	else
+	{
+		pDCMem = &dc;
+	}
+
+	UINT nMode = pDCMem->SetBkMode(TRANSPARENT);
+
+
+	COLORREF crText = pDCMem->SetTextColor(m_crText);
+	CFont *pOldFont = pDCMem->SelectObject(&m_font);
+
+
+	// Fill in backgound if not transparent
+	if (!m_bTransparent)
+	{
+		if (m_fillmode == Normal)
+		{
+			CBrush br;
+
+			if (m_hBackBrush != NULL)
+				br.Attach(m_hBackBrush);
+			else
+				br.Attach(m_hwndBrush);
+
+			pDCMem->FillRect(rc,&br);
+
+			br.Detach();
+		}
+		else // Gradient Fill
+		{
+			DrawGradientFill(pDCMem, &rc, m_crLoColor, m_crHiColor, 100);
+		}
+
+	}
+
+
+	// If the text is flashing turn the text color on
+	// then to the color of the window background.
+
+	LOGBRUSH lb;
+	ZeroMemory(&lb,sizeof(lb));
+
+	// Stop Checking complaining
+	if (m_hBackBrush)
+		::GetObject(m_hBackBrush,sizeof(lb),&lb);
+
+
+	// Something to do with flashing
+	if (!m_bState && m_Type == Text)
+	{
+		//ZeroMemory(&lb,sizeof(lb));
+		lb.lbColor = RGB(255,0,255);
+		pDCMem->SetTextColor(lb.lbColor);
+	}
+
+	DWORD style = GetStyle();
+
+	switch (style & SS_TYPEMASK)
+	{
+	case SS_RIGHT: 
+		dwFlags = DT_RIGHT | DT_WORDBREAK; 
+		break; 
+
+	case SS_CENTER: 
+		dwFlags = SS_CENTER | DT_WORDBREAK;
+		break;
+
+	case SS_LEFTNOWORDWRAP: 
+		dwFlags = DT_LEFT; 
+		break;
+
+	default: // treat other types as left
+	case SS_LEFT: 
+		dwFlags = DT_LEFT | DT_WORDBREAK; 
+		break;
+	}	
+
+
+	// Added to expand tabs...
+	if(m_strText.Find(_T('\t')) != -1)
+		dwFlags |= DT_EXPANDTABS;
+
+	// If the text centered make an assumtion that
+	// the will want to center verticly as well
+	if (style & SS_CENTERIMAGE)
+	{
+		dwFlags = DT_CENTER;
+
+		// Apply 
+		if (m_strText.Find(_T("\r\n")) == -1)
+		{
+			dwFlags |= DT_VCENTER;
+
+			// And because DT_VCENTER only works with single lines
+			dwFlags |= DT_SINGLELINE; 
+		}
+
+	}
+
+	//
+	// 3333   DDDDD
+	//     3  D    D
+	//   33   D     D    E F X 
+	//     3  D    D
+	// 3333   DDDDD
+	//
+	//
+	if (m_bRotation)
+	{
+		int nAlign = pDCMem->SetTextAlign (TA_BASELINE);
+
+		CPoint pt;
+		GetViewportOrgEx (pDCMem->m_hDC,&pt) ;
+		SetViewportOrgEx (pDCMem->m_hDC,rc.Width() / 2, rc.Height() / 2, NULL) ;
+		pDCMem->TextOut(0, 0, m_strText);
+		SetViewportOrgEx (pDCMem->m_hDC,pt.x / 2, pt.y / 2, NULL) ;
+		pDCMem->SetTextAlign (nAlign);
+	}
+	else
+	{
+		pDCMem->DrawText(m_strText, rc, dwFlags);
+		if (m_bFont3d)
+		{
+			pDCMem->SetTextColor(m_cr3DHiliteColor);
+
+			if (m_3dType == Raised)
+				rc.OffsetRect(-1,-1);
+			else
+				rc.OffsetRect(1,1);
+
+			pDCMem->DrawText(m_strText, rc, dwFlags);
+			m_3dType;
+
+		}
+	}
+
+	// Restore DC's State
+	pDCMem->SetBkMode(nMode);
+	pDCMem->SelectObject(pOldFont);
+	pDCMem->SetTextColor(crText);
+
+	if (!m_bTransparent)
+	{
+		dc.BitBlt(0, 0, rc.Width(), rc.Height(), pDCMem, 0, 0, SRCCOPY);
+		// continue DC restore 
+		pDCMem->SelectObject ( pOldBitmap ) ;
+		delete pDCMem;
+	}
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::OnTimer
+//
+// Description:		Used in conjunction with 'FLASH' functions
+//
+// INPUTS:          Windows API
+// 
+// RETURNS:         Windows API
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+void CLabel::OnTimer(UINT nIDEvent) 
+{
+
+	m_bState = !m_bState;
+
+	UpdateSurface();
+
+	CStatic::OnTimer(nIDEvent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::OnSetCursor
+//
+// Description:		Used in conjunction with 'LINK' function
+//
+// INPUTS:          Windows API
+// 
+// RETURNS:         Windows API
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+BOOL CLabel::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
+{
+
+	if (m_hCursor)
+	{
+		::SetCursor(m_hCursor);
+		return TRUE;
+	}
+
+	return CStatic::OnSetCursor(pWnd, nHitTest, message);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::OnLButtonDown
+//
+// Description:		Called when a link is click on
+//
+// INPUTS:          Windows API
+// 
+// RETURNS:         Windows API
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+// NT ALMOND				02072002    1.6     Added Mail support
+//////////////////////////////////////////////////////////////////////////
+void CLabel::OnLButtonDown(UINT nFlags, CPoint point) 
+{
+
+	if (!m_bNotifyParent) // Fix
+	{
+		CString strLink;
+
+		GetWindowText(strLink);
+		if (m_Link == HyperLink)
+		{
+			ShellExecute(NULL,_T("open"),m_sLink.IsEmpty() ? strLink : m_sLink,NULL,NULL,SW_SHOWNORMAL);
+		}
+		if (m_Link == MailLink)
+		{
+			strLink = _T("mailto:") + strLink;
+			ShellExecute( NULL, NULL,  strLink,  NULL, NULL, SW_SHOWNORMAL );
+		}
+	}
+	else
+	{
+		// To use notification in parent window
+		// Respond to a OnNotify in parent and disassemble the message
+		//
+		NMHDR nm;
+
+		nm.hwndFrom = GetSafeHwnd();
+		nm.idFrom  = GetDlgCtrlID();
+		nm.code = NM_LINKCLICK;
+		GetParent()->SendMessage(WM_NOTIFY,nm.idFrom,(LPARAM) &nm);
+	}
+
+	CStatic::OnLButtonDown(nFlags, point);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+// THE FUNCTIONS START HERE :----
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetText
+//
+// Description:		Short cut to set window text - caption - label
+//
+// INPUTS:          Text to use
+// 
+// RETURNS:         Reference to this
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26081998    1.0     Origin
+// NT ALMOND				02072002    1.6     Crash Fix
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetText(const CString& strText)
+{
+	if(IsWindow(this->GetSafeHwnd())) 
+	{
+		//SetWindowText(strText);
+		m_strText = strText;
+		UpdateSurface();
+	}
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetTextColor
+//
+// Description:		Sets the text color 
+//
+// INPUTS:          True or false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				22/10/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetTextColor(COLORREF crText)
+{
+
+	m_crText = crText;
+
+	UpdateSurface();
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetFontBold
+//
+// Description:		Sets the font ot bold 
+//
+// INPUTS:          True or false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				22/10/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetFontBold(BOOL bBold)
+{	
+
+	m_lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
+	ReconstructFont();
+	UpdateSurface();
+	return *this;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetFontUnderline
+//
+// Description:		Sets font underline attribue
+//
+// INPUTS:          True of false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetFontUnderline(BOOL bSet)
+{	
+	m_lf.lfUnderline = bSet;
+	ReconstructFont();
+	UpdateSurface();
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetFontItalic
+//
+// Description:		Sets font italic attribute
+//
+// INPUTS:          True of false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetFontItalic(BOOL bSet)
+{
+
+	m_lf.lfItalic = bSet;
+	ReconstructFont();
+	UpdateSurface();
+
+	return *this;	
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetSunken
+//
+// Description:		Sets sunken effect on border
+//
+// INPUTS:          True of false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetSunken(BOOL bSet)
+{
+
+	if (!bSet)
+		ModifyStyleEx(WS_EX_STATICEDGE,0,SWP_DRAWFRAME);
+	else
+		ModifyStyleEx(0,WS_EX_STATICEDGE,SWP_DRAWFRAME);
+
+	return *this;	
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetBorder
+//
+// Description:		Toggles the border on/off
+//
+// INPUTS:          True of false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetBorder(BOOL bSet)
+{
+
+	if (!bSet)
+		ModifyStyle(WS_BORDER,0,SWP_DRAWFRAME);
+	else
+		ModifyStyle(0,WS_BORDER,SWP_DRAWFRAME);
+
+	return *this;	
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetFontSize
+//
+// Description:		Sets the font size
+//
+// INPUTS:          True of false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetFontSize(int nSize)
+{
+
+	CFont cf;
+	LOGFONT lf;
+
+	cf.CreatePointFont(nSize * 10, m_lf.lfFaceName);
+	cf.GetLogFont(&lf);
+
+	m_lf.lfHeight = lf.lfHeight;
+	m_lf.lfWidth  = lf.lfWidth;
+
+	//	nSize*=-1;
+	//	m_lf.lfHeight = nSize;
+	ReconstructFont();
+	UpdateSurface();
+
+	return *this;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetBkColor
+//
+// Description:		Sets background color
+//
+// INPUTS:          Colorref of background color
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetBkColor(COLORREF crBkgnd, COLORREF crBkgndHigh , BackFillMode mode)
+{
+
+	m_crLoColor = crBkgnd;
+	m_crHiColor = crBkgndHigh;
+
+	m_fillmode = mode;
+
+	if (m_hBackBrush)
+		::DeleteObject(m_hBackBrush);
+
+
+	if (m_fillmode == Normal)
+		m_hBackBrush = ::CreateSolidBrush(crBkgnd);
+
+	UpdateSurface();
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetFontName
+//
+// Description:		Sets the fonts face name
+//
+// INPUTS:          String containing font name
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+// NT ALMOND				15092000	1.5		Support internation windows
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetFontName(const CString& strFont, BYTE byCharSet /* Default = ANSI_CHARSET */)
+{	
+
+	m_lf.lfCharSet = byCharSet;
+
+	_tcscpy_s(m_lf.lfFaceName,strFont);
+	ReconstructFont();
+	UpdateSurface();
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::FlashText
+//
+// Description:		As the function states
+//
+// INPUTS:          True or false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::FlashText(BOOL bActivate)
+{
+
+	if (m_bTimer)
+		KillTimer(1);
+
+	if (bActivate)
+	{
+		m_bState = FALSE;
+
+		m_bTimer = TRUE;
+
+		SetTimer(1,500,NULL);
+
+		m_Type = Text;
+	}
+	else
+		m_Type = None; // Fix
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::FlashBackground
+//
+// Description:		As the function states
+//
+// INPUTS:          True or false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::FlashBackground(BOOL bActivate)
+{
+
+	if (m_bTimer)
+		KillTimer(1);
+
+	if (bActivate)
+	{
+		m_bState = FALSE;
+
+		m_bTimer = TRUE;
+		SetTimer(1,500,NULL);
+
+		m_Type = Background;
+	}
+
+	return *this;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetLink
+//
+// Description:		Indicates the string is a link
+//
+// INPUTS:          True or false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+// NT ALMOND				26/08/99	1.2		Added flexbility of
+//												Sending Click meessage to parent
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetLink(BOOL bLink,BOOL bNotifyParent)
+{
+
+	if (bLink)
+		m_Link = HyperLink;
+	else
+		m_Link = LinkNone;
+
+	m_bNotifyParent = bNotifyParent;
+
+	if (m_Link != LinkNone)
+		ModifyStyle(0,SS_NOTIFY);
+	else
+		ModifyStyle(SS_NOTIFY,0);
+
+
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetLinkCursor
+//
+// Description:		Sets the internet browers link
+//
+// INPUTS:          Handle of cursor
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				26/08/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetLinkCursor(HCURSOR hCursor)
+{
+
+	m_hCursor = hCursor;
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetTransparent
+//
+// Description:		Sets the Label window to be transpaent
+//
+// INPUTS:          True or false
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				22/10/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetTransparent(BOOL bSet)
+{
+
+	m_bTransparent = bSet;
+	ModifyStyleEx(0,WS_EX_TRANSPARENT); // Fix for transparency
+	UpdateSurface();
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetFont3D
+//
+// Description:		Sets the 3D attribute of the font.
+//
+// INPUTS:          True or false, Raised or Sunken
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				22/10/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetFont3D(BOOL bSet,Type3D type)
+{
+
+	m_bFont3d = bSet;
+	m_3dType = type;
+	UpdateSurface();
+
+
+	return *this;
+}
+
+void CLabel::OnSysColorChange() 
+{
+
+	if (m_hwndBrush)
+		::DeleteObject(m_hwndBrush);
+
+	m_hwndBrush = ::CreateSolidBrush(GetSysColor(COLOR_3DFACE));
+
+	UpdateSurface();
+
+
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetRotationAngle
+//
+// Description:		Sets the rotation angle for the current font.
+//
+// INPUTS:          Angle in Degress
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				22/10/98    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetRotationAngle(UINT nAngle,BOOL bRotation)
+{
+	// Arrrrh...
+	// Your looking in here why the font is rotating, aren't you?
+	// Well try setting the font name to 'Arial' or 'Times New Roman'
+	// Make the Angle 180 and set bRotation to true.
+	//
+	// Font rotation _ONLY_ works with TrueType fonts...
+	//
+	// 
+	m_lf.lfEscapement = m_lf.lfOrientation = (nAngle * 10);
+	m_bRotation = bRotation;
+
+	ReconstructFont();
+
+	UpdateSurface();
+
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetText3DHiliteColor
+//
+// Description:		Sets the 3D font hilite color
+//
+// INPUTS:          Color 
+// 
+// RETURNS:         Reference to 'this' object
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				17/07/00    1.0     Origin
+//
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetText3DHiliteColor(COLORREF cr3DHiliteColor)
+{
+	m_cr3DHiliteColor = cr3DHiliteColor;
+	UpdateSurface();
+
+
+	return *this;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::PreSubclassWindow
+//
+// Description:		Assigns default dialog font
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				15092000    1.5     Origin
+// NT ALMOND				02072002    1.6     Fix crash when GetFont returns NULL
+//////////////////////////////////////////////////////////////////////////
+void CLabel::PreSubclassWindow() 
+{
+
+	CStatic::PreSubclassWindow();
+
+	CFont* cf = GetFont();
+	if(cf !=NULL)
+	{
+		cf->GetObject(sizeof(m_lf),&m_lf);
+	}
+	else
+	{
+		GetObject(GetStockObject(SYSTEM_FONT),sizeof(m_lf),&m_lf);
+	}
+
+	ReconstructFont();
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::PreCreateWindow
+//
+// Description:		
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				15092000    1.5     Origin
+//////////////////////////////////////////////////////////////////////////
+BOOL CLabel::PreCreateWindow(CREATESTRUCT& cs) 
+{	
+	return CStatic::PreCreateWindow(cs);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::DrawGradientFill
+//
+// Description:		Internal help function to gradient fill background
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				15092000    1.5     Origin
+//////////////////////////////////////////////////////////////////////////
+void CLabel::DrawGradientFill(CDC* pDC, CRect* pRect, COLORREF crStart, COLORREF crEnd, int nSegments)
+{
+	// Get the starting RGB values and calculate the incremental
+	// changes to be applied.
+
+	COLORREF cr;
+	int nR = GetRValue(crStart);
+	int nG = GetGValue(crStart);
+	int nB = GetBValue(crStart);
+
+	int neB = GetBValue(crEnd);
+	int neG = GetGValue(crEnd);
+	int neR = GetRValue(crEnd);
+
+	if(nSegments > pRect->Width())
+		nSegments = pRect->Width();
+
+	int nDiffR = (neR - nR);
+	int nDiffG = (neG - nG);
+	int nDiffB = (neB - nB);
+
+	int ndR = 256 * (nDiffR) / (max(nSegments,1));
+	int ndG = 256 * (nDiffG) / (max(nSegments,1));
+	int ndB = 256 * (nDiffB) / (max(nSegments,1));
+
+	nR *= 256;
+	nG *= 256;
+	nB *= 256;
+
+	neR *= 256;
+	neG *= 256;
+	neB *= 256;
+
+	int nCX = pRect->Width() / max(nSegments,1), nLeft = pRect->left, nRight;
+	pDC->SelectStockObject(NULL_PEN);
+
+	for (int i = 0; i < nSegments; i++, nR += ndR, nG += ndG, nB += ndB)
+	{
+		// Use special code for the last segment to avoid any problems
+		// with integer division.
+
+		if (i == (nSegments - 1))
+			nRight = pRect->right;
+		else
+			nRight = nLeft + nCX;
+
+		cr = RGB(nR / 256, nG / 256, nB / 256);
+
+		{
+			CBrush br(cr);
+			CBrush* pbrOld = pDC->SelectObject(&br);
+			pDC->Rectangle(nLeft, pRect->top, nRight + 1, pRect->bottom);
+			pDC->SelectObject(pbrOld);
+		}
+
+		// Reset the left side of the drawing rectangle.
+
+		nLeft = nRight;
+	}
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetFont
+//
+// Description:		Sets font with LOGFONT structure
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				02072002    1.6     Origin
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetFont(LOGFONT lf)
+{
+	CopyMemory(&m_lf, &lf, sizeof(m_lf));
+	ReconstructFont();
+	UpdateSurface();
+	return *this;
+
+}
+
+BOOL CLabel::OnEraseBkgnd(CDC* pDC) 
+{
+	// TODO: Add your message handler code here and/or call default
+
+	return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetMailLink
+//
+// Description:		Sets the label so it becomes Mail enabled
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				02072002    1.6     Origin
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetMailLink(BOOL bEnable, BOOL bNotifyParent)
+{
+	if (bEnable)
+		m_Link = MailLink;
+	else
+		m_Link = LinkNone;
+
+
+	m_bNotifyParent = bNotifyParent;
+
+	if (m_Link != LinkNone)
+		ModifyStyle(0,SS_NOTIFY);
+	else
+		ModifyStyle(SS_NOTIFY,0);
+
+	return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Function:		CLabel::SetHyperLink
+//
+// Description:		Sets the label so it becomes hyperlink enabled
+//
+// INPUTS:          
+// 
+// RETURNS:         
+//
+// NOTES:			
+// 
+// MODIFICATIONS:
+//
+// Name                     Date        Version Comments
+// NT ALMOND				02072002    1.6     Origin
+//////////////////////////////////////////////////////////////////////////
+CLabel& CLabel::SetHyperLink(const CString& sLink)
+{
+	m_sLink = sLink;
+
+	return *this;
+}
+
+/*************************************************************
+*add at  : 2014-08-11  by唐太闲
+*describe: 设置背景透明,返回空画刷                  
+*************************************************************/
+HBRUSH CLabel::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
+{
+	// TODO:  在此更改 DC 的任何属性
+
+	// TODO:  如果不应调用父级的处理程序,则返回非 null 画笔
+	//return NULL;
+	pDC->SetBkMode(TRANSPARENT);  
+	return (HBRUSH)GetStockObject(NULL_BRUSH);
+}

+ 117 - 0
SATHelper/UI/SubLabel.h

@@ -0,0 +1,117 @@
+#if !defined(AFX_LABEL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)
+#define AFX_LABEL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+// Label.h : header file
+//
+
+#define	NM_LINKCLICK	WM_APP + 0x200
+
+/////////////////////////////////////////////////////////////////////////////
+// CLabel window
+
+class CLabel : public CStatic
+{
+	// Construction
+public:
+
+
+	enum LinkStyle { LinkNone, HyperLink, MailLink };
+	enum FlashType {None, Text, Background };
+	enum Type3D { Raised, Sunken};
+	enum BackFillMode { Normal, Gradient };
+
+	CLabel();
+	virtual CLabel& SetBkColor(COLORREF crBkgnd, COLORREF crBkgndHigh = 0, BackFillMode mode = Normal);
+	virtual CLabel& SetTextColor(COLORREF crText);
+	virtual CLabel& SetText(const CString& strText);
+	virtual CLabel& SetFontBold(BOOL bBold = TRUE);
+	virtual CLabel& SetFontName(const CString& strFont, BYTE byCharSet = ANSI_CHARSET);
+	virtual CLabel& SetFontUnderline(BOOL bSet = TRUE);
+	virtual CLabel& SetFontItalic(BOOL bSet = TRUE);
+	virtual CLabel& SetFontSize(int nSize);
+	virtual CLabel& SetSunken(BOOL bSet );
+	virtual CLabel& SetBorder(BOOL bSet = TRUE);
+	virtual CLabel& SetTransparent(BOOL bSet);
+	virtual CLabel& FlashText(BOOL bActivate);
+	virtual CLabel& FlashBackground(BOOL bActivate);
+	virtual CLabel& SetLink(BOOL bLink,BOOL bNotifyParent);
+	virtual CLabel& SetLinkCursor(HCURSOR hCursor);
+	virtual CLabel& SetFont3D(BOOL bSet,Type3D type=Raised);
+	virtual CLabel& SetRotationAngle(UINT nAngle,BOOL bRotation);
+	virtual CLabel& SetText3DHiliteColor(COLORREF cr3DHiliteColor);
+	virtual CLabel& SetFont(LOGFONT lf);
+	virtual CLabel& SetMailLink(BOOL bEnable, BOOL bNotifyParent);
+	virtual CLabel& SetHyperLink(const CString& sLink);
+
+	// Attributes
+public:
+protected:
+	void UpdateSurface();
+	void ReconstructFont();
+	void DrawGradientFill(CDC* pDC, CRect* pRect, COLORREF crStart, COLORREF crEnd, int nSegments);
+	COLORREF		m_crText;
+	COLORREF		m_cr3DHiliteColor;
+	HBRUSH			m_hwndBrush;
+	HBRUSH			m_hBackBrush;
+	LOGFONT			m_lf;
+	CFont			m_font;
+	BOOL			m_bState;
+	BOOL			m_bTimer;
+	LinkStyle		m_Link;
+	BOOL			m_bTransparent;
+	BOOL			m_bFont3d;
+	BOOL			m_bToolTips;
+	BOOL			m_bNotifyParent;
+	BOOL			m_bRotation;
+	FlashType		m_Type;
+	HCURSOR			m_hCursor;
+	Type3D			m_3dType;
+	BackFillMode	m_fillmode;
+	COLORREF		m_crHiColor;
+	COLORREF		m_crLoColor;
+	CString			m_sLink;
+	CString			m_strText;
+	CDC* m_pMemDC;
+	//CDC* pDCMem;
+
+	// Operations
+public:
+
+	// Overrides
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CLabel)
+protected:
+	virtual void PreSubclassWindow();
+	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
+	//}}AFX_VIRTUAL
+
+	// Implementation
+public:
+	virtual ~CLabel();
+
+	// Generated message map functions
+protected:
+	//{{AFX_MSG(CLabel)
+	afx_msg void OnTimer(UINT nIDEvent);
+	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+	afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
+	afx_msg void OnSysColorChange();
+	afx_msg void OnPaint();
+	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+	//}}AFX_MSG
+
+	DECLARE_MESSAGE_MAP()
+public:
+	//tc:这就是新添加的ON_WM_CTLCOLOR_REFLECT()消息的响应函数
+	afx_msg HBRUSH CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_LABEL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)

+ 244 - 0
SATHelper/UI/TreeComboBox.cpp

@@ -0,0 +1,244 @@
+// TreeComboBox.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "TreeComboBox.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CTreeComboBox
+
+CTreeComboBox::CTreeComboBox()
+	:m_bTree(FALSE)
+	, m_bAlertBkg(FALSE)
+	, m_bAlertText(FALSE)
+	, m_droppedHeight(250)
+	, m_droppedWidth(100)
+{
+	m_hBrushAlert = CreateSolidBrush(COLOR_ALERT);
+}
+
+CTreeComboBox::~CTreeComboBox()
+{
+	DeleteObject(m_hBrushAlert);
+}
+
+
+BEGIN_MESSAGE_MAP(CTreeComboBox, CComboBox)
+	//{{AFX_MSG_MAP(CTreeComboBox)
+	ON_WM_LBUTTONDOWN()
+	ON_WM_LBUTTONDBLCLK()
+	ON_WM_CTLCOLOR_REFLECT()
+	//}}AFX_MSG_MAP
+	ON_MESSAGE(WMU_CLOSE_CONTROL, OnCloseControl)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CTreeComboBox message handlers
+void CTreeComboBox::PreSubclassWindow()
+{
+	// TODO: Add your specialized code here and/or call the base class
+	CComboBox::PreSubclassWindow();
+	CRect rect(0, 0, 0, 0);
+
+	DWORD dwStyle = WS_BORDER | TVS_DISABLEDRAGDROP | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_FULLROWSELECT | WS_POPUPWINDOW;
+	((CWnd&)m_Tree).CreateEx(0, WC_TREEVIEW, NULL, dwStyle, rect, GetParent(), NULL);
+	m_Tree.Init(this);
+
+	GetClientRect(rect);
+	SetDroppedWidth(rect.Width());
+	SetDroppedHeight(m_droppedHeight);
+}
+
+BOOL CTreeComboBox::PreTranslateMessage(MSG* pMsg)
+{
+	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DOWN)
+	{
+		DisplayTree();
+		return TRUE;
+	}
+
+	return CComboBox::PreTranslateMessage(pMsg);
+}
+
+void CTreeComboBox::OnLButtonDown(UINT nFlags, CPoint point)
+{
+	m_bTree = !m_bTree;
+	if (m_bTree)
+		DisplayTree();
+}
+
+void CTreeComboBox::OnLButtonDblClk(UINT nFlags, CPoint point)
+{
+	OnLButtonDown(nFlags, point);
+}
+
+void CTreeComboBox::TreeCtrlDone()
+{
+	CWnd* pParent = GetParent();
+	if (pParent != NULL)
+	{
+		CString str; GetWindowText(str);
+		if (m_pretext != str)
+		{
+			m_pretext = str;
+			WPARAM wParam = MAKEWPARAM(GetDlgCtrlID(), CBN_CLOSEUP);
+			pParent->SendMessage(WM_COMMAND, wParam, (LPARAM)m_hWnd);
+		}
+	}
+}
+
+LRESULT CTreeComboBox::OnCloseControl(WPARAM wParam, LPARAM lParam)
+{
+	m_Tree.ShowWindow(SW_HIDE);
+	m_bTree = FALSE;
+	HTREEITEM hItemChanged = (HTREEITEM)lParam;
+	if (hItemChanged)
+	{
+		SetTitle(m_Tree.GetItemText(hItemChanged));
+	}
+	AlertBkg(FALSE);
+	TreeCtrlDone();
+	return 1;
+}
+
+HBRUSH CTreeComboBox::CtlColor(CDC* pDC, UINT nCtlColor)
+{
+	HBRUSH hbr = NULL;
+	if (nCtlColor == CTLCOLOR_EDIT)
+	{
+		if (!m_bAlertText)pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
+		else pDC->SetTextColor(COLOR_RED);
+		if (!m_bAlertBkg)pDC->SetBkColor(GetSysColor(COLOR_WINDOW));
+		else pDC->SetBkColor(COLOR_ALERT);
+		if (m_bAlertText || m_bAlertBkg)hbr = m_hBrushAlert;
+		pDC->SetBkMode(TRANSPARENT);
+	}
+
+	return hbr;
+}
+
+void CTreeComboBox::DisplayTree()
+{
+	CRect rc;
+	GetWindowRect(rc);
+	rc.top = rc.bottom + 1;
+	rc.right = GetDroppedWidth();
+	rc.bottom = GetDroppedHeight();
+
+	m_Tree.Display(rc);
+}
+
+void CTreeComboBox::SetTitle(CString sTitle)
+{
+	ResetContent();
+	AddString(sTitle);
+	SetCurSel(0);
+}
+
+void CTreeComboBox::RefDroppedWidth()
+{
+	CRect rect;
+	GetClientRect(rect);
+	SetDroppedWidth(rect.Width());
+}
+
+/************************************************************************/
+/*  函数:InsertMyItem[12/7/2016 IT];
+/*  描述:;
+/*  参数:;
+/*  	[IN] :;
+/*  	[IN] :;
+/*  返回:void;
+/*  注意:只适合二层目录;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void CTreeComboBox::InsertMyItem(IN CString strRoot, IN CString strItem)
+{
+	BOOL bExist = FALSE;
+	HTREEITEM hRoot = m_Tree.GetRootItem();
+	if (hRoot == NULL)
+	{
+		hRoot = m_Tree.InsertItem(strRoot, NULL);
+		m_Tree.InsertItem(strItem, hRoot);
+		return;
+	}
+
+	CString strName = m_Tree.GetItemText(hRoot);
+	if (strName == strRoot)
+	{
+		HTREEITEM hSubItem = m_Tree.GetChildItem(hRoot);
+		// 遍历所有子项;
+		while (hSubItem)
+		{
+			// 当前子项是否是要查找的子项;
+			if (strItem == m_Tree.GetItemText(hSubItem))
+			{
+				bExist = TRUE;
+				break;
+			}
+
+			// 若未到,查找下一个兄弟项;
+			hSubItem = m_Tree.GetNextSiblingItem(hSubItem);
+		}
+
+		if (!bExist)
+		{
+			// 插入新项;
+			m_Tree.InsertItem(strItem, hRoot);
+		}
+	}
+	else
+	{
+		hRoot = m_Tree.GetNextSiblingItem(hRoot);
+		while (hRoot)
+		{
+			// 当前兄弟项是否是要查找的子项;
+			if (strRoot == m_Tree.GetItemText(hRoot))
+			{
+				HTREEITEM hSubItem = m_Tree.GetChildItem(hRoot);
+				// 遍历所有子项;
+				while (hSubItem)
+				{
+					// 当前子项是否是要查找的子项;
+					if (strItem == m_Tree.GetItemText(hSubItem))
+					{
+						bExist = TRUE;
+						break;
+					}
+
+					// 若未到,查找下一个兄弟项;
+					hSubItem = m_Tree.GetNextSiblingItem(hSubItem);
+				}
+
+				if (!bExist)
+				{
+					// 插入新项;
+					bExist = TRUE;
+					m_Tree.InsertItem(strItem, hRoot);
+				}
+
+				break;
+			}
+
+			// 若未到,查找下一个兄弟项;
+			hRoot = m_Tree.GetNextSiblingItem(hRoot);
+		}
+
+		if (!bExist)
+		{
+			// 插入新项;
+			hRoot = m_Tree.InsertItem(strRoot, NULL);
+			m_Tree.InsertItem(strItem, hRoot);
+		}
+	}
+}

+ 90 - 0
SATHelper/UI/TreeComboBox.h

@@ -0,0 +1,90 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [IT], 保留所有权利;
+/*  模 块 名:;
+/*  描    述:;
+/*
+/*  版    本:[V];
+/*  作    者:[IT];
+/*  日    期:[11/4/2016];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[IT];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+#ifndef __TREECOMBOBOX__
+#define __TREECOMBOBOX__
+
+#include "ComboTreeCtrl.h"
+
+#pragma once
+
+#ifndef	COLOR_RED
+#define COLOR_RED			RGB(220,0,0)
+#endif	//COLOR_RED
+#ifndef	COLOR_ALERT
+#define COLOR_ALERT			RGB(255,180,180)
+#endif	//COLOR_ALERT
+/////////////////////////////////////////////////////////////////////////////
+// CTreeComboBox  window
+
+class CTreeComboBox : public CComboBox
+{
+	CString m_pretext;
+	// Construction
+public:
+	CTreeComboBox();
+
+	// Attributes
+public:
+	BOOL m_bTree;
+
+	// Operations
+public:
+	void DisplayTree();
+	void SetTitle(CString sTitle);
+	BOOL IsAlertBkg(){ return m_bAlertBkg; }
+	BOOL IsAlertText(){ return m_bAlertText; }
+	CTreeCtrl& GetTreeCtrl(){ return m_Tree; }
+	int GetDroppedWidth(){ return m_droppedWidth; };
+	int GetDroppedHeight(){ return m_droppedHeight; };
+	void SetDroppedWidth(int nWidth){ m_droppedWidth = nWidth; };
+	void SetDroppedHeight(int nHeight){ m_droppedHeight = nHeight; };
+	void AlertBkg(BOOL bAlert = TRUE){ m_bAlertBkg = bAlert; Invalidate(); };
+	void AlertText(BOOL bAlert = TRUE){ m_bAlertText = bAlert; Invalidate(); };
+
+public:
+	virtual BOOL PreTranslateMessage(MSG* pMsg);
+protected:
+	virtual void PreSubclassWindow();
+
+public:
+	void RefDroppedWidth();
+	UINT GetTreeCount();
+	void TreeCtrlDone();
+	virtual ~CTreeComboBox();
+
+protected:
+	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+	afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
+	afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
+	afx_msg LRESULT OnCloseControl(WPARAM wParam, LPARAM lParam);
+	DECLARE_MESSAGE_MAP()
+
+private:
+	BOOL m_bAlertBkg;
+	BOOL m_bAlertText;
+	int m_droppedWidth;
+	int m_droppedHeight;
+	HBRUSH m_hBrushAlert;
+	CComboTreeCtrl m_Tree;
+
+public:
+	void InsertMyItem(IN CString strRoot, IN CString strItem);
+};
+
+
+#endif // __TREECOMBOBOX__;

+ 260 - 0
SATHelper/UI/XColorStatic.cpp

@@ -0,0 +1,260 @@
+#include "stdafx.h"
+#include "XColorStatic.h"
+#include "FontSize.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/***************************************************************************************/
+// CXColorStatic
+BEGIN_MESSAGE_MAP(CXColorStatic, CStatic)
+    // {{ AFX_MSG_MAP
+    ON_WM_PAINT()
+    ON_WM_ERASEBKGND()
+    // }} AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/***************************************************************************************/
+// CXColorStatic
+CXColorStatic::CXColorStatic()
+{
+    m_rgbText           = GetSysColor(COLOR_BTNTEXT);
+    m_rgbBackground     = GetSysColor(COLOR_BTNFACE);
+    m_pBrush            = new CBrush(m_rgbBackground);
+    m_bBold             = FALSE;
+    m_hIcon             = NULL;
+    m_iXMargin          = 0;
+    m_iYMargin          = 0;
+}
+
+/***************************************************************************************/
+// ~CXColorStatic
+CXColorStatic::~CXColorStatic()
+{
+    TRACE(_T("in CXColorStatic::!CXColorStatic\n"));
+
+    if(m_font.GetSafeHandle())
+        m_font.DeleteObject();
+
+    if(m_pBrush)
+    {
+        m_pBrush->DeleteObject();
+        delete m_pBrush;
+    }
+    m_pBrush = NULL;
+}
+
+/***************************************************************************************/
+// PreSubclassWindow
+void CXColorStatic::PreSubclassWindow()
+{
+    TRACE(_T("in CXColorStatic::PreSubclassWindow\n"));
+
+    // 获取字体
+    CFont* pFont = GetFont();
+    if(!pFont)
+    {
+        HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+        if(hFont == NULL)
+            hFont = (HFONT)GetStockObject(ANSI_VAR_FONT);
+        if(hFont)
+            pFont = CFont::FromHandle(hFont);
+    }
+    ASSERT(pFont);
+    ASSERT(pFont->GetSafeHandle());
+
+    // 为这个控件创建一个字体
+    LOGFONT lf;
+    pFont->GetLogFont(&lf);
+    m_font.CreateFontIndirect(&lf);
+}
+
+/***************************************************************************************/
+// OnPaint
+void CXColorStatic::OnPaint()
+{
+    CPaintDC dc(this);  // 获取到绘制对象的设备上下文
+
+    dc.SaveDC();
+
+    dc.SetTextColor(m_rgbText);
+    dc.SetBkColor(m_rgbBackground);
+    dc.SetBkMode(OPAQUE);
+    dc.SelectObject(m_pBrush);
+
+    CRect rect;
+    GetClientRect(rect);
+
+    // 不能同时绘制图标和文本
+    if(m_hIcon)
+    {
+        int iIconX = GetSystemMetrics(SM_CXICON);
+        int iIconY = GetSystemMetrics(SM_CYICON);
+
+        rect.left = rect.left + (rect.Width() - iIconX) / 2;
+        rect.top = rect.top + (rect.Height() - iIconY) / 2;
+		 
+        dc.DrawIcon(rect.left, rect.top, m_hIcon);
+    }
+    else
+    {
+        dc.SelectObject(&m_font);
+
+        // 获取静态文本
+        CString strText = _T("");
+        GetWindowText(strText);
+
+        UINT uiFormat = 0;
+        DWORD dwStyle = GetStyle();
+
+        // 重新绘制静态文本
+        if(dwStyle && SS_CENTER)
+            uiFormat |= DT_CENTER;
+        else if(dwStyle && SS_LEFT)
+            uiFormat |= DT_LEFT;
+        else if(dwStyle && SS_RIGHT)
+            uiFormat |= DT_RIGHT;
+
+        if(dwStyle & SS_CENTERIMAGE)    // 垂直居中 ==> 仅对单行
+            uiFormat |= DT_VCENTER | DT_SINGLELINE;
+        else
+            uiFormat |= DT_WORDBREAK;
+		 
+		uiFormat  = DT_LEFT;
+	
+
+        rect.left += m_iXMargin;
+
+        rect.top += m_iYMargin;
+
+        dc.DrawText(strText, rect, uiFormat);
+    }
+
+    dc.RestoreDC(-1);
+}
+
+/***************************************************************************************/
+// OnEraseBkgnd
+BOOL CXColorStatic::OnEraseBkgnd(CDC *pDC)
+{
+    CRect cr;
+    GetClientRect(cr);
+    pDC->FillRect(&cr, m_pBrush);
+
+    return TRUE;
+}
+
+/***************************************************************************************/
+// SetFont
+void CXColorStatic::SetFont(LOGFONT *pLogFont, BOOL bRedraw /* = TRUE */)
+{
+    ASSERT(pLogFont);
+    if(!pLogFont)
+        return;
+
+    if(m_font.GetSafeHandle())
+        m_font.DeleteObject();
+
+    LOGFONT lf = *pLogFont;
+    lf.lfWeight = m_bBold ? FW_BOLD : FW_NORMAL;
+
+    m_font.CreateFontIndirect(&lf);
+
+    if(bRedraw)
+        RedrawWindow();
+}
+
+/***************************************************************************************/
+// SetFont
+void CXColorStatic::SetFont(LPCTSTR lpszFaceName, int iPointSize, BOOL bRedraw /* = TRUE */)
+{
+    // 如果指定的字体名字不存在或未指定, 将使用默认的字体
+    LOGFONT lf;
+    memset(&lf, 0, sizeof(lf));
+
+    if(lpszFaceName == NULL)
+    {
+        CFont* pFont = GetFont();
+        ASSERT(pFont);
+        pFont->GetLogFont(&lf);
+    }
+    else
+    {
+        _tcsncpy_s(lf.lfFaceName, lpszFaceName, sizeof(lf.lfFaceName) / sizeof(TCHAR) - 1);
+    }
+
+    lf.lfHeight = GetFontHeight(iPointSize);
+    SetFont(&lf, bRedraw);
+}
+
+/***************************************************************************************/
+// SetFont
+void CXColorStatic::SetFont(CFont *pFont, BOOL bRedraw /* = TRUE */)
+{
+    ASSERT(pFont);
+    if(!pFont)
+        return;
+
+    LOGFONT lf;
+    memset(&lf, 0, sizeof(lf));
+
+    pFont->GetLogFont(&lf);
+
+    SetFont(&lf, bRedraw);
+}
+
+/***************************************************************************************/
+// SetTextColor
+void CXColorStatic::SetTextColor(COLORREF rgb, BOOL bRedraw /* = TRUE */)
+{
+    m_rgbText = rgb;
+    if(bRedraw)
+        RedrawWindow();
+}
+
+/***************************************************************************************/
+// SetBold
+void CXColorStatic::SetBold(BOOL bFlag, BOOL bRedraw /* = TRUE */)
+{
+    m_bBold = bFlag;
+
+    LOGFONT lf;
+    memset(&lf, 0, sizeof(lf));
+
+    CFont* pFont = GetFont();
+    ASSERT(pFont);
+    pFont->GetLogFont(&lf);
+
+    lf.lfWeight = m_bBold ? FW_BOLD : FW_NORMAL;
+	
+	SetFont(&lf, bRedraw);
+}
+
+/***************************************************************************************/
+// SetBackgroundColor
+void CXColorStatic::SetBackgroundColor(COLORREF rgb, BOOL bRedraw /* = TRUE */)
+{
+	m_rgbBackground = rgb;
+	if(m_pBrush)
+	{
+		m_pBrush->DeleteObject();
+		delete m_pBrush;
+	}
+	m_pBrush = new CBrush(m_rgbBackground);
+	if(bRedraw)
+		RedrawWindow();
+}
+
+/***************************************************************************************/
+// SetIcon
+void CXColorStatic::SetIcon(HICON hIcon, BOOL bRedraw /* = TRUE */)
+{
+	ASSERT(hIcon);
+	
+	m_hIcon = hIcon;
+	if(bRedraw)
+		RedrawWindow();
+}

+ 65 - 0
SATHelper/UI/XColorStatic.h

@@ -0,0 +1,65 @@
+/*************************************************************************************
+ * FontSize.h
+ *
+ * 作者: 雷登似
+ * 公司: TCL多媒体科技控股有限公司
+ * 版本: 0.01
+ * 编程日期: 2010-04-21
+ * 联系方式: wp2.leids@tcl.com / 0752-3270423(81423)
+ *
+ * 功能: 设置字体样式
+ * 说明: 本软件释放给TCL公司, 只要你喜欢, 在TCL公司内部可以免费使用, 但在使用过程中
+ *       你必须保证此软件的说明信息完整!
+ *
+ * 此软件的最终解释权归原作者拥有, 作者不对软件的分发和使用负责, 如果你因使用此软
+ * 件而造成的任何损失或商业风险均由使用者自己承担所有责任.
+ *
+ *************************************************************************************/
+
+#ifndef __X_COLOR_STATIC_H__
+#define __X_COLOR_STATIC_H__
+
+class CXColorStatic : public CStatic
+{
+// 构造函数和析构函数声明
+public:
+    CXColorStatic();
+    virtual ~CXColorStatic();
+
+//成员函数声明
+public:
+    void SetBackgroundColor(COLORREF rgb, BOOL bRedraw = TRUE);
+    void SetTextColor(COLORREF rgb, BOOL bRedraw = TRUE);
+    void SetBold(BOOL bFlag, BOOL bRedraw = TRUE);
+    void SetFont(LPCTSTR lpszFaceName, int iPointSize, BOOL bRedraw = TRUE);
+    void SetFont(LOGFONT* pLogFont, BOOL bRedraw = TRUE);
+    void SetFont(CFont* pFont, BOOL bRedraw = TRUE);
+    void SetIcon(HICON hIcon, BOOL bRedraw = TRUE);
+    void SetMargins(int x, int y) {m_iXMargin = x; m_iYMargin = y;}
+
+// 方法覆盖
+// {{AFX_VIRTUAL
+protected:
+    virtual void PreSubclassWindow();
+// }}AFX_VIRTUAL
+
+// 数据成员
+protected:
+    CFont       m_font;
+    COLORREF    m_rgbText;
+    COLORREF    m_rgbBackground;
+    CBrush*     m_pBrush;
+    BOOL        m_bBold;
+    int         m_iXMargin, m_iYMargin;
+    HICON       m_hIcon;
+
+// 消息映射方法
+protected:
+    // {{AFX_MSG
+    afx_msg void OnPaint();
+    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+    // }}AFX_MSG
+    DECLARE_MESSAGE_MAP();
+};
+
+#endif

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

+ 136 - 0
SATHelper/filehelper/filehelper.cpp

@@ -0,0 +1,136 @@
+#include "stdafx.h"
+#include "filehelper.h"
+#include <shlwapi.h>
+
+// 排序和乱序使用stl;
+#include <cstdlib>
+#include <ctime>
+#include <algorithm>
+// _mkdir头文件;
+#include <direct.h>
+
+int myrandom(int i) { return std::rand() % i; }
+
+filehelper::filehelper(void)
+{
+	
+}
+
+filehelper::~filehelper(void)
+{
+
+}
+
+/************************************************************************/
+/*  函数:[2/21/2017 Jeff];
+/*  描述:;
+/*  参数:;
+/*  	[IN] :;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void filehelper::random(IN STR_VEC& vtContent, IN BOOL bSort /* = TRUE */)
+{
+	if (bSort)
+	{//排序;
+		std::sort(vtContent.begin(), vtContent.end());
+	}
+	else
+	{//乱序;
+		//设置随即数生成器的种子;
+		std::srand(unsigned(std::time(0)));
+		std::random_shuffle(vtContent.begin(), vtContent.end(), myrandom);
+	}
+}
+
+/************************************************************************/
+/*  函数:[2/21/2017 Jeff];
+/*  描述:;
+/*  参数:;
+/*  	[IN] :;
+/*  	[OUT] :;
+/*  	[IN/OUT] :;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void filehelper::split(OUT vector<TString>& vtSplit, IN const TString str, IN const TString strSplit)
+{
+	if (str.size() == 0 || strSplit.size() == 0)
+		return;
+
+	INT nIndex = 0;
+	vtSplit.clear();
+	TString strtmp = str;
+	TString strtmp2;
+	do 
+	{
+		if (TString::npos != (nIndex = strtmp.find_first_of(strSplit)))
+		{
+			strtmp2 = strtmp.substr(0, nIndex);
+			if (strtmp2.size())vtSplit.push_back(strtmp2);
+			strtmp = strtmp.substr(nIndex + strSplit.size());
+		}
+	} while (strtmp.find_first_of(strSplit) != TString::npos);
+
+	if (strtmp.size())
+		vtSplit.push_back(strtmp);
+}
+
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::getsubfolder(LPCTSTR lpfolder, STR_VEC *pvtfolders)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfolders == NULL) return FALSE;
+	m_pvtfolders = pvtfolders;
+
+	findsubfolder(lpfolder);
+
+	return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+BOOL filehelper::getallfiles(LPCTSTR lpfolder, LPCTSTR lpfindext, STR_VEC *pvtfiles)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfiles == NULL) return FALSE;
+	m_pvtfiles = pvtfiles;
+
+	findfiles_findin_subfolder(lpfolder);
+
+	return TRUE;
+}
+
+BOOL filehelper::getfolderfiles(LPCTSTR lpfolder, LPCTSTR lpfindext, STR_VEC *pvtfiles)
+{
+	// 路径不存在;
+	if (!PathFileExists(lpfolder))
+		return FALSE;
+
+	// 指针空;
+	if (pvtfiles == NULL) return FALSE;
+	m_pvtfiles = pvtfiles;
+
+	findfiles_findout_subfolder(lpfolder);
+
+	return TRUE;
+}

+ 61 - 0
SATHelper/filehelper/filehelper.h

@@ -0,0 +1,61 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [IT], 保留所有权利;
+/*  模 块 名:;
+/*  描    述:;
+/*
+/*  版    本:[V];	
+/*  作    者:[IT];
+/*  日    期:[12/22/2016];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[IT];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+#ifndef __FILEHELPING_20150818__
+#define __FILEHELPING_20150818__
+
+#pragma once
+
+#include "findfile.h"
+
+#define USE_IMGEXT 0
+
+class filehelper :public findfile
+{
+public:
+	filehelper(void);
+	~filehelper(void);
+
+public:
+	// 排序或乱序;
+	static void random(IN STR_VEC& vtContent, IN BOOL bSort = TRUE);
+	// 分隔数组;
+	static void split(OUT vector<TString>& vtSplit, IN const TString str, IN const TString strSplit);
+
+public:
+	// 只获取当前要目录的子文件夹,不获取子文件夹中的文件夹;
+	BOOL getsubfolder(
+		LPCTSTR lpfolder,								// 要查找的目录;
+		STR_VEC *pvtfolders								// 返回子文件夹;
+		);
+
+	// 查找当前目录及子目录下的所有文件;
+	BOOL getallfiles(
+		LPCTSTR lpfolder,								// 要查找的目录;
+		LPCTSTR lpfindext,								// 要查找的扩展名;
+		STR_VEC *pvtfiles								// 返回要查找的文件全名;
+		);
+
+	// 只查找当前目录的文件,不查找子目录;
+	BOOL getfolderfiles(
+		LPCTSTR lpfolder,								// 要查找的目录;
+		LPCTSTR lpfindext,								// 要查找的扩展名;
+		STR_VEC *pvtfiles							// 返回要查找的文件全名;
+		);
+};
+
+#endif

+ 327 - 0
SATHelper/filehelper/findfile.cpp

@@ -0,0 +1,327 @@
+#include "stdafx.h"
+#include "findfile.h"
+#include <algorithm>
+
+// 查找文件的最大数量限制在nMaxLimit;
+CONST size_t nMaxLimit = 0xFFFFFFFF;	//--4294967295;
+// 查找文件的默认数量限制在nDefLimit;
+CONST size_t nDefLimit = 0xFFFFF;	//--1048575
+
+// 路径分隔符;
+CONST TCHAR		c_pathSeparator = _T('\\');
+CONST TString	s_pathSeparator = _T("\\");
+CONST TString	g_sVertical = _T("|");
+
+// 正确的扩展名及格式样列;
+// 这个值最好可以从配置文件ini或xml中获取,不用在代码里写死;
+CONST TString	g_sCorrectExt = _T("*.jpg|*.jpeg|*.png|*.bmp|*.cr2|*.nef|*.raw");
+
+
+findfile::findfile(void):
+m_nlimit(nDefLimit),
+m_pvtfiles(NULL),
+m_pvtnames(NULL),
+m_pvtfolders(NULL),
+m_pvtfiles_sth(NULL),
+m_pvtfiles_mth(NULL)
+{
+	
+}
+
+findfile::~findfile(void)
+{
+}
+
+/************************************************************************/
+/*
+函数:match
+描述:判断指定的文件命名是否匹配指定的扩展名;
+参数:
+IN	sFileExt	扩展名;
+IN	sFile		文件名;
+返回:匹配返回非0值,否则返回0;
+注意:
+*/
+/************************************************************************/
+void findfile::setlimit(IN CONST INT &nLimit)
+{
+	if (nLimit < 1)
+		m_nlimit = nDefLimit;
+	if (nLimit < nMaxLimit)
+		m_nlimit = nLimit;
+}
+
+// 获取文件名;
+TString findfile::getfilename(IN CONST TString &file)
+{
+	TString name;
+	TString strfile = file;
+	int nIndex = strfile.find_last_of(_T('\\'));	// 如果file不包含 '\\' ,得不到文件名;
+	if (nIndex == TString::npos)
+	{
+		nIndex = strfile.find_last_of(_T('.'));
+		if ( nIndex == TString::npos )
+			return _T("");
+
+		return strfile.substr(0, nIndex);
+	}
+
+	name = strfile.substr(nIndex+1);
+	nIndex = name.find_last_of(_T('.'));
+	if (nIndex == TString::npos)
+		return _T("");
+
+	return name.substr(0, nIndex);
+}
+
+void findfile::find1st(TString folder, TString &file)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString _file = _T("*");
+	TString s = path + _file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) != FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					file = path + fileinfo.cFileName;
+					break;
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+// 全部;
+void findfile::findall(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+					findall(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit())
+					{
+						m_pvtnames->push_back(fileinfo.cFileName);
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findsubfolder(IN CONST TString& folder)	// 查找子目录;
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			if (_T('.') != fileinfo.cFileName[0])// '.'和 '..'的系统文件去除;
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findallsubfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			if (_T('.') != fileinfo.cFileName[0])// '.'和 '..'的系统文件去除;
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+					findallsubfolder(path + fileinfo.cFileName); // 不入子目录查找;
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+void findfile::findfiles_findin_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					findfiles_findin_subfolder(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit())
+					{
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findfiles_findout_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) != FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					if (!checklimit())
+					{
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}
+
+void findfile::findfiles_within_subfolder(IN CONST TString& folder)
+{
+	TString path = folder;
+	if (path.size() > 0 && c_pathSeparator != path[path.size() - 1])
+		path.append(s_pathSeparator);
+
+	TString file = _T("*");
+	TString s = path + file;
+
+	WIN32_FIND_DATA fileinfo = { 0 };
+	HANDLE handle = FindFirstFile(s.c_str(), &fileinfo);
+
+	if (NULL != handle && INVALID_HANDLE_VALUE != handle)
+	{
+		do
+		{
+			// 检查是否超过最大数;
+			if (checklimit()) break;
+
+			// '.'和 '..'的系统文件去除;
+			if (_T('.') != fileinfo.cFileName[0])
+			{
+				if ((FILE_ATTRIBUTE_DIRECTORY & fileinfo.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)	// 目录;
+				{
+					m_pvtfolders->push_back(path + fileinfo.cFileName);
+					findfiles_within_subfolder(path + fileinfo.cFileName);
+				}
+				else
+				{
+					if (!checklimit())
+					{
+						m_pvtfiles->push_back(path + fileinfo.cFileName);
+					}
+				}
+			}
+		} while (FindNextFile(handle, &fileinfo));
+
+		FindClose(handle);
+	}
+}

+ 86 - 0
SATHelper/filehelper/findfile.h

@@ -0,0 +1,86 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [IT], 保留所有权利;
+/*  模 块 名:;
+/*  描    述:;
+/*
+/*  版    本:[V];	
+/*  作    者:[IT];
+/*  日    期:[12/22/2016];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[IT];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+#ifndef __FIND_FILE_20151003__
+#define __FIND_FILE_20151003__
+
+#include <string>
+#include <vector>
+using namespace std;
+
+#ifndef _UNICODE
+typedef string TString;
+#else
+typedef wstring TString;
+#endif
+
+typedef vector<TString> STR_VEC;
+
+#pragma once
+
+class findfile
+{
+public:
+	findfile(void);
+	virtual ~findfile(void);
+
+//protected:
+public:
+	INT m_nlimit;
+	STR_VEC m_vtEffctExt;
+	STR_VEC *m_pvtfiles;
+	STR_VEC *m_pvtnames;
+	STR_VEC *m_pvtfolders;
+
+	STR_VEC *m_pvtfiles_sth;	// s小图;
+	STR_VEC *m_pvtfiles_mth;	// m小图;	
+public:
+	static inline int findfile::match(IN CONST TString &strExt, IN CONST TString &strfile)
+	{
+		int pos = strfile.find_last_of(_T('.'));
+		if (TString::npos != pos)
+			return !_tcsicmp(strExt.c_str(), strfile.substr(pos).c_str());
+
+		return FALSE;
+	}
+	
+	static TString getfilename(IN CONST TString &file);
+protected:
+	INT  getlimit() const { return m_nlimit; }
+	void setlimit(IN CONST INT &nlimit);
+
+	inline INT checklimit() { return m_pvtfiles->size() == getlimit(); }
+	BOOL IsaDirectory(CONST TString &sDirectory) { return (FILE_ATTRIBUTE_DIRECTORY == GetFileAttributes(sDirectory.c_str())); }
+
+public:
+	void find1st(TString folder, TString &file);
+	// 查找所有文件,包括子文件夹名、文件名、文件路径;
+	void findall(IN CONST TString& folder);
+	// 只返回2级子目录名(测试ok);
+	void findsubfolder(IN CONST TString& folder);
+	// 只返回所有子目录名(测试ok);
+	void findallsubfolder(IN CONST TString& folder);
+
+	// 查找文件路径,以及查找子目录的文件路径,但不获取子目录名(测试ok);
+	void findfiles_findin_subfolder(IN CONST TString& folder);
+	// 查找文件路径,不查找子目录的文件(测试ok);
+	void findfiles_findout_subfolder(IN CONST TString& folder);
+	// 查找文件路径,以及查找子目录的文件路径,同时获取子目录名(测试ok);
+	void findfiles_within_subfolder(IN CONST TString& folder);
+};
+
+#endif