#include "StdAfx.h"
#include "Global.h"

// 获取文件版本号函数头文件;
#include <WinVer.h>		
#pragma comment(lib,"version.lib")
using namespace std;
#include <psapi.h>
#pragma comment(lib,"Psapi.lib")
#include <locale.h>
#include <io.h>//_access头文件;

namespace GLOBAL
{
	CIOCPPipe g_IOCP;
	CONFIG g_config;
	TCHAR g_szModulePath[MAX_PATH] = _T("");			// 软件目录;
	TCHAR g_szModuleFileName[MAX_PATH] = _T("");		// 软件名称;
	TCHAR g_szConfigFile[MAX_PATH] = _T("");
	PROC_WND_INFO g_procWndInfo;
	BOOL g_bHijacted = FALSE;
#define TRACE4(sz, p1, p2, p3, p4)  TRACE(_T(sz), p1, p2, p3, p4)

	BOOL GetConfigInfo(LPCTSTR lpIniDir /* = NULL */, LPCTSTR lpConfigName /* = NULL */)
	{
		TCHAR szDrive[_MAX_DRIVE] = { 0 };
		TCHAR szDir[_MAX_DIR] = { 0 };
		TCHAR szFna[_MAX_DIR] = { 0 };
		TCHAR szExt[_MAX_DIR] = { 0 };
		::GetModuleFileName(NULL, g_szModulePath, sizeof(g_szModulePath) / sizeof(TCHAR));
		_stprintf_s(g_szModuleFileName, _T("%s"), g_szModulePath);

		_tsplitpath_s(g_szModulePath, szDrive, szDir, szFna, szExt);
		_tcscpy_s(g_szModulePath, szDrive);
		_tcscat_s(g_szModulePath, szDir);

		if (lpIniDir != NULL && lpConfigName != NULL)
			_stprintf_s(g_szConfigFile, _T("%s%s"), lpIniDir, lpConfigName);
		else
			_stprintf_s(g_szConfigFile, _T("%s%s.ini"), g_szModulePath, szFna);

		HANDLE hFile = CreateFile(g_szConfigFile, 0/*GENERIC_READ*/, 0, NULL, OPEN_EXISTING, 0, NULL);

		if (ERROR_FILE_NOT_FOUND == GetLastError())
		{
			return FALSE;
		}

		CloseHandle(hFile);
		hFile = NULL;

#pragma region 获取配置文件信息;
		TCHAR szValue[MAX_PATH] = {0};
		// LOGIN;
		GetPrivateProfileString(_T("LOGIN"), _T("account"), _T(""), g_config.szAccount, MAX_PATH, g_szConfigFile);
		GetPrivateProfileString(_T("LOGIN"), _T("password"), _T(""), g_config.szPassword, MAX_PATH, g_szConfigFile);
		GetPrivateProfileString(_T("LOGIN"), _T("line"), _T(""), g_config.szLine, MAX_PATH, g_szConfigFile);
		g_config.nOffline = GetPrivateProfileInt(_T("LOGIN"), _T("offline"), 0, g_szConfigFile);
		// SYSTEM;
		g_config.nStdOut = GetPrivateProfileInt(_T("SYSTEM"), _T("stdprint"), 0, g_szConfigFile);
		GetPrivateProfileString(_T("SYSTEM"), _T("OGCTool"), _T(""), g_config.szOGCToolProgram, MAX_PATH, g_szConfigFile);
		// BATCH;
		g_config.nDeltaEType = GetPrivateProfileInt(_T("BATCH"), _T("EType"), 0, g_szConfigFile);
		GetPrivateProfileString(_T("BATCH"), _T("EValue"), _T(""), szValue, MAX_PATH, g_szConfigFile);
		g_config.dDeltaEValue = _tstof(szValue);
		GetPrivateProfileString(_T("BATCH"), _T("batchNumber"), _T(""), g_config.szBatchNumber, MAX_PATH, g_szConfigFile);
#pragma endregion

		if ( g_config.nStdOut ) {
			AllocConsole();                             // 开辟控制台;
			SetConsoleTitle(_T("调试输出"));			// 设置控制台窗口标题;
			freopen("CONOUT$", "w+t", stdout);			// 重定向输出;
			freopen("CONIN$", "r+t", stdin);			// 重定向输入;

			HWND hWnd = NULL;
again:
			hWnd = ::FindWindow(NULL, _T("调试输出"));
			if( hWnd ) {
				if (!::SetWindowPos(hWnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE)) {
					_tprintf_s(_T("前置设置失败\n"));
				} else {
					_tprintf_s(_T("前置设置成功\n"));
				}
			} else {
				goto again;
			}
		}

		return TRUE;
	}

	DWORD FindProcess(LPCTSTR lpProName)
	{
		ASSERT(lpProName!=NULL);

		DWORD dwProcId = 0;
		PROCESSENTRY32 pe32 = { 0 };

		HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
		if (hSnapshot == NULL)
		{
			return 0;
		}
		pe32.dwSize = sizeof(PROCESSENTRY32);

		if (Process32First(hSnapshot, &pe32))
		{
			do {
				if (_tcsicmp(lpProName, pe32.szExeFile) == 0)
				{
					dwProcId = pe32.th32ProcessID;
					break;
				}
			} while (Process32Next(hSnapshot, &pe32));
		}
		CloseHandle(hSnapshot);

		return dwProcId;
	}

	vector<DWORD> FindAllProcess(LPCTSTR lpProName)
	{
		ASSERT(lpProName!=NULL);

		vector<DWORD> vtPID;
		PROCESSENTRY32 pe32 = { 0 };

		HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
		if (hSnapshot == NULL)
			return vector<DWORD>();

		pe32.dwSize = sizeof(PROCESSENTRY32);
		if (Process32First(hSnapshot, &pe32))
		{
			do {
				if (_tcsicmp(lpProName, pe32.szExeFile) == 0)
				{
					vtPID.push_back(pe32.th32ProcessID);
				}
			} while (Process32Next(hSnapshot, &pe32));
		}
		CloseHandle(hSnapshot);

		return vtPID;
	}

	void FindAllProcess(std::vector<PROC_INFO> &vtProInfo)
	{
		PROCESSENTRY32 pe32 = { 0 };
		HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
		if (hSnapshot == NULL)
			return;

		pe32.dwSize = sizeof(PROCESSENTRY32);
		if (!Process32First(hSnapshot, &pe32))
			goto end;

		do 
		{
			PROC_INFO proinfo;
			proinfo.dwProcId = pe32.th32ProcessID;
			proinfo.strProcName = pe32.szExeFile;
			vtProInfo.push_back(proinfo);
		} while (Process32Next(hSnapshot, &pe32));

end:
		CloseHandle(hSnapshot);
	}

	HANDLE FindModule(LPCTSTR lpModuleName, DWORD dwProcId)
	{
		ASSERT(lpModuleName!=NULL);

		DWORD dwMID = 0;
		MODULEENTRY32 me32 = { 0 };
		HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, dwProcId);
		if (hSnapshot == NULL)
			return NULL;

		me32.dwSize = sizeof(MODULEENTRY32);
		if (Module32First(hSnapshot, &me32))
		{
			do {
				if (_tcsicmp(lpModuleName, me32.szModule) == 0)
				{
					break;
				}
			} while (Module32Next(hSnapshot, &me32));
		}
		CloseHandle(hSnapshot);

		return me32.hModule;
	}

	LPCTSTR GetModulePath(LPCTSTR lpModuleName, DWORD dwProcId)
	{
		ASSERT(lpModuleName!=NULL);

		DWORD dwMID = 0;
		MODULEENTRY32 me32 = { 0 };
		HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, dwProcId);
		if (hSnapshot == NULL)
			return NULL;

		BOOL bFound = FALSE;
		me32.dwSize = sizeof(MODULEENTRY32);
		if (Module32First(hSnapshot, &me32)) {
			do 
			{
				if (_tcsicmp(lpModuleName, me32.szModule) == 0) {
					bFound = TRUE;
					break;
				}
			} while (Module32Next(hSnapshot, &me32));
		}
		CloseHandle(hSnapshot);

		return bFound ? me32.szExePath : NULL;
	}

	HANDLE FindModuleEx(LPCTSTR lpModuleName, DWORD dwProcId)
	{
		HMODULE hMods[1024] = {0};
		DWORD cbNeeded = 0;
		TCHAR szModName[MAX_PATH];
		BOOL Wow64Process;

		HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ|PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcId);
		IsWow64Process(hProcess, &Wow64Process); //判断是32位还是64位进程
		if ( EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, Wow64Process?LIST_MODULES_32BIT:LIST_MODULES_64BIT) )
		{
			for (UINT i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ )
			{
				GetModuleFileNameEx(hProcess, hMods[i], szModName, _countof(szModName));
#ifdef _DEBUG
				WriteTextLog(_T("目标=%s, 原始=%s, 地址=%p"), szModName, lpModuleName, hMods[i]);
#endif
				if (_tcsicmp(lpModuleName, szModName) == 0)
				{
					CloseHandle(hProcess);
					WriteTextLog(_T("【目标=%s, 原始=%s, 地址=%p】"), szModName, lpModuleName, hMods[i]);
					return hMods[i];
				}
			}
		}

		CloseHandle(hProcess);

		return NULL;
	}

	// WINDOWS NT 以上的内核需要提权,才能对系统进行高级管理;
	BOOL GetDebugPriv()
	{
		// 返回的访问令牌指针;
		HANDLE	hToken;
		// 接收所返回的制定特权名称的信息;
		LUID	sedebugnameValue;
		// 新特权信息的指针(结构体);
		TOKEN_PRIVILEGES tkp;
		//DWORD	dwCurProcId = GetCurrentProcessId();
		// 要修改访问权限的进程句柄;
		HANDLE	hCurProc = ::GetCurrentProcess();
		//hCurProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwCurProcId);

		if (!::OpenProcessToken(hCurProc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		{
			return FALSE;
		}

		if (!::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
		{
			CloseHandle(hToken);
			return FALSE;
		}

		tkp.PrivilegeCount = 1;
		tkp.Privileges[0].Luid = sedebugnameValue;
		tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		if (!::AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
		{
			CloseHandle(hToken);
			return FALSE;
		}

		CloseHandle(hCurProc);
		CloseHandle(hToken);
		return TRUE;
	}

	/************************************************************************/
	/* 
	函数：GetFileVersion
	描述：获取可执行文件的文件版号;
	参数：
	hModule[IN]		可执行文件模块句柄;
	dwArray[OUT]	返回的文件版本号;
	返回：
	成功返回TRUE,失败返回FALSE;
	注意：
	当hModule为空时，表示要获取的可执行文件为本程序的文件版本号;
	*/
	/************************************************************************/
	BOOL GetFileVersion( IN HMODULE hModule, OUT DWORD (&dwArray)[4])
	{
		TCHAR fname[MAX_PATH];
		VS_FIXEDFILEINFO *pVi;
		DWORD dwHandle;

		if ( GetModuleFileName(hModule, fname, MAX_PATH))
		{
			INT nSize = GetFileVersionInfoSize(fname, &dwHandle);

			if (nSize > 0)
			{
				BYTE *pBuffer = new BYTE[nSize];
				memset(pBuffer, 0, nSize);

				if (GetFileVersionInfo(fname, dwHandle, nSize, pBuffer))
				{
					if (VerQueryValue(pBuffer, _T("\\"), (LPVOID *)&pVi, (PUINT)&nSize))
					{
						dwArray[0] = HIWORD(pVi->dwFileVersionMS);
						dwArray[1] = LOWORD(pVi->dwFileVersionMS);
						dwArray[2] = HIWORD(pVi->dwFileVersionLS);
						dwArray[3] = LOWORD(pVi->dwFileVersionLS);

						delete[]pBuffer;
						return TRUE;
					}
				}

				if ( pBuffer )
					delete[]pBuffer;
			}
		}

		return FALSE;
	}

	/************************************************************************/
	/* 
	函数：GetFileVersion
	描述：获取可执行文件的文件版号;
	参数：
	lpFileName[IN]	可执行文件名全路径;
	dwArray[OUT]	返回的文件版本号;
	返回：
	成功返回TRUE,失败返回FALSE;
	注意：
	*/
	/************************************************************************/
	BOOL GetFileVersionEx( IN LPCTSTR lpFileName, IN DWORD (&dwArray)[4] )
	{
		if ( lpFileName == NULL || !PathFileExists(lpFileName) )
		{
			OutputDebugString(_T("文件名错误或文件不存在\n"));
			return FALSE;
		}

		DWORD dwHandle = 0;
		VS_FIXEDFILEINFO *pVi = NULL;
		INT nSize = GetFileVersionInfoSize(lpFileName, &dwHandle);
		if ( nSize > 0 )
		{
			BYTE *pBuffer = new BYTE[nSize];
			memset(pBuffer, 0, nSize);

			if ( GetFileVersionInfo(lpFileName, dwHandle, nSize, pBuffer) )
			{
				if (VerQueryValue(pBuffer, _T("\\"), (LPVOID *)&pVi, (PUINT)&nSize))
				{
					dwArray[0] = HIWORD(pVi->dwFileVersionMS);
					dwArray[1] = LOWORD(pVi->dwFileVersionMS);
					dwArray[2] = HIWORD(pVi->dwFileVersionLS);
					dwArray[3] = LOWORD(pVi->dwFileVersionLS);

					if (pBuffer)
						delete[]pBuffer;
					return TRUE;
				}
			}

			if (pBuffer)
				delete[]pBuffer;
		}

		return FALSE;
	}

	/************************************************************************/
	/* 
	函数：GetProductVersion
	描述：获取可执行文件的产品版号;
	参数：
	hModule[IN]		可执行文件模块句柄;
	dwArray[OUT]	返回的产品版本号;
	返回：
	成功返回TRUE,失败返回FALSE;
	注意：
	当hModule为空时，表示要获取的可执行文件为本程序的产品版本号;
	*/
	/************************************************************************/
	BOOL GetProductVersion(IN HMODULE hModule, IN DWORD (&dwArray)[4] )
	{
		TCHAR fname[MAX_PATH];
		VS_FIXEDFILEINFO *pVi;
		DWORD dwHandle;

		if (::GetModuleFileName(hModule, fname, MAX_PATH))
		{
			INT nSize = GetFileVersionInfoSize(fname, &dwHandle);

			if (nSize > 0)
			{
				BYTE *pBuffer = new BYTE[nSize];
				memset(pBuffer, 0, nSize);

				if (GetFileVersionInfo(fname, dwHandle, nSize, pBuffer))
				{
					if (VerQueryValue(pBuffer, _T("\\"), (LPVOID *)&pVi, (PUINT)&nSize))
					{
						dwArray[0] = HIWORD(pVi->dwProductVersionMS);
						dwArray[1] = LOWORD(pVi->dwProductVersionMS);
						dwArray[2] = HIWORD(pVi->dwProductVersionLS);
						dwArray[3] = LOWORD(pVi->dwProductVersionLS);

						if(pBuffer)
							delete[]pBuffer;
						return TRUE;
					}
				}
				if(pBuffer)
					delete[]pBuffer;
			}
		}

		return FALSE;
	}

	/************************************************************************/
	/* 
	函数：GetProductVersion
	描述：获取可执行文件的产品版号;
	参数：
	lpFileName[IN]	可执行文件名全路径;
	dwArray[OUT]	返回的产品版本号;
	返回：
	成功返回TRUE,失败返回FALSE;
	注意：
	*/
	/************************************************************************/
	BOOL GetProductVersionEx( IN LPCTSTR lpFileName, IN DWORD (&dwArray)[4] )
	{
		if ( lpFileName == NULL || !PathFileExists(lpFileName) )
		{
			OutputDebugString(_T("文件名错误或文件不存在\n"));
			return FALSE;
		}

		DWORD dwHandle = 0;
		VS_FIXEDFILEINFO *pVi = NULL;
		INT nSize = GetFileVersionInfoSize(lpFileName, &dwHandle);
		if ( nSize > 0 )
		{
			BYTE *pBuffer = new BYTE[nSize];
			memset(pBuffer, 0, nSize);

			if ( GetFileVersionInfo(lpFileName, dwHandle, nSize, pBuffer) )
			{
				if (VerQueryValue(pBuffer, _T("\\"), (LPVOID *)&pVi, (PUINT)&nSize))
				{
					dwArray[0] = HIWORD(pVi->dwProductVersionMS);
					dwArray[1] = LOWORD(pVi->dwProductVersionMS);
					dwArray[2] = HIWORD(pVi->dwProductVersionLS);
					dwArray[3] = LOWORD(pVi->dwProductVersionLS);

					if (pBuffer)
						delete[]pBuffer;
					return TRUE;
				}
			}

			if (pBuffer)
				delete[]pBuffer;
		}

		return FALSE;
	}

	/************************************************************************/
	/*  函数：WriteTextLog[7/28/2016 IT];
	/*  描述：写文本日志;
	/*  参数：;
	/*  	[IN] ：;
	/*  返回：void;
	/*  注意：;
	/*  示例：;
	/*
	/*  修改：;
	/*  日期：;
	/*  内容：;
	/************************************************************************/
	void WriteTextLog(const TCHAR *format, ...)
	{
#if 0
		try
		{
			//static ThreadSection _critSection;
			//AutoThreadSection aSection(&_critSection);
			// 解析出日志路径;
			TCHAR szlogpath[MAX_PATH] = {0};
			static TCHAR szModulePath[MAX_PATH] = {0};
			static TCHAR szFna[_MAX_DIR] = { 0 };
			if ( szModulePath[0] == _T('\0') )
			{
				TCHAR szDrive[_MAX_DRIVE] = { 0 };
				TCHAR szDir[_MAX_DIR] = { 0 };
				TCHAR szExt[_MAX_DIR] = { 0 };
				::GetModuleFileName(NULL, szModulePath, sizeof(szModulePath) / sizeof(TCHAR));
				_tsplitpath_s(szModulePath, szDrive, szDir, szFna, szExt);
				_tcscpy_s(szModulePath, szDrive);
				_tcscat_s(szModulePath, szDir);
			}

			_stprintf_s(szlogpath, _T("%s日志\\%s%s.txt"), szModulePath, szFna, CTime::GetCurrentTime().Format("[%Y-%m-%d]"));

			// 打开或创建文件;
			CStdioFile fp;
			if (PathFileExists(szlogpath))
			{
				if (fp.Open(szlogpath, CFile::modeWrite) == FALSE)
				{
					return;
				}
				fp.SeekToEnd();
			}
			else
			{
				fp.Open(szlogpath, CFile::modeCreate | CFile::modeWrite);
			}

			// 格式化前设置语言区域;
			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 ); // C4996
			// Note: vsprintf is deprecated; consider using vsprintf_s instead

			// 将日志内容输入到文件中;
			fp.WriteString( CTime::GetCurrentTime().Format(_T("%Y-%m-%d %H:%M:%S ")) );
			fp.WriteString(buffer);
			fp.WriteString(_T("\n"));

			// 关闭文件，释放资源并设置回原语言区域;
			free( buffer );
			_tsetlocale(LC_CTYPE, old_locale);
			free(old_locale);//还原区域设定;
			fp.Close();
		}
		catch (CException *e)
		{
			e->ReportError();
			e->Delete();
		}
#else
		// 解析出日志路径;
		TCHAR szlogpath[MAX_PATH] = { 0 };
		static TCHAR szModulePath[MAX_PATH] = { 0 };
		static TCHAR szFna[MAX_PATH] = { 0 };
		if (szModulePath[0] == _T('\0'))
		{
			TCHAR szDrive[MAX_PATH] = { 0 };
			TCHAR szDir[MAX_PATH] = { 0 };
			TCHAR szExt[MAX_PATH] = { 0 };
			::GetModuleFileName(NULL, szModulePath, sizeof(szModulePath) / sizeof(TCHAR));
			_tsplitpath_s(szModulePath, szDrive, szDir, szFna, szExt);
			_tcscpy_s(szModulePath, szDrive);
			_tcscat_s(szModulePath, szDir);
		}

		_stprintf_s(szlogpath, _T("%s%s.txt"), szModulePath, szFna);
		// 打开或创建文件;
		FILE *fp = NULL;
		//if (_taccess(szlogpath, 0) != -1)
#ifndef UNICODE
		if (_access(szlogpath, 0) != -1)
#else
		if (_taccess(szlogpath, 0) != -1)
#endif
		{// 存在;
			fp = _tfopen(szlogpath, _T("a+"));
			// 移动到末尾;
			fseek(fp, 0, SEEK_END);
		}
		else
		{// 不存在;
			fp = _tfopen(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);//还原区域设定;
#endif
	}


	//---------------------------------------------------------------------
	// add by Jeff 2014.10.27
	// 函数：全局函数IsDirectoryLegitimate,多字节版本，非UNICODE
	// 描述：判断一个目录路径字符串，是否属于合法的、可创建的目录路径。
	// 参数：strDirectory 被验证的路径字符串;
	//		
	// 返回：合法路径返回TRUE;
	//---------------------------------------------------------------------
	BOOL IsDirectoryLegitimate(const CString &strDirectory)
	{
		if (strDirectory.Find('/') != -1 ||
			strDirectory.Find('\\') != -1 ||
			strDirectory.Find(':') != -1 ||
			strDirectory.Find('*') != -1 ||
			strDirectory.Find('?') != -1 ||
			strDirectory.Find('\"') != -1 ||
			strDirectory.Find('>') != -1 ||
			strDirectory.Find('<') != -1 ||
			strDirectory.Find('|') != -1
			)
			return FALSE;

		return TRUE;
	}


	//--------------------------------------------------------------------------------
	// Jeff add 2014.06.23;
	// 函数：ErrorExit
	// 描述：
	// 参数:
	//     lpszFunction：函数名;
	//     dwError：错误码; 
	//
	//--------------------------------------------------------------------------------
	void ShowSystemErrorInfo(CString strDescription, const DWORD &dwError)
	{
#if 1
		LPVOID lpMsgBuf;

		BOOL fOk = FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER |
			FORMAT_MESSAGE_FROM_SYSTEM |
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			dwError,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			(LPTSTR)&lpMsgBuf,
			0, NULL);

		if (!fOk)
		{
			// Is it a network-related error?
			HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL, DONT_RESOLVE_DLL_REFERENCES);

			if (hDll != NULL)
			{
				FormatMessage(
					FORMAT_MESSAGE_FROM_HMODULE |
					FORMAT_MESSAGE_FROM_SYSTEM |
					FORMAT_MESSAGE_IGNORE_INSERTS,
					hDll,
					dwError,
					MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
					(LPTSTR)&lpMsgBuf,
					0,
					NULL);

				FreeLibrary(hDll);
			}
		}

		if (lpMsgBuf != NULL)
		{
			CString strDisplay;
			strDisplay.Format(_T("%s.错误码=%d,Windows描述:%s"), strDescription, dwError, (PCTSTR)LocalLock(lpMsgBuf));
			//WriteLog(strDisplay);
			LocalFree(lpMsgBuf);
		}
		else
		{
			//WriteLog(strDescription);
		}
#else
		HLOCAL hlocal = NULL;   // Buffer that gets the error message string

		// Get the error code's textual description
		BOOL fOk = FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM |
			FORMAT_MESSAGE_ALLOCATE_BUFFER |
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			dwError,
			MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
			(PTSTR)&hlocal,
			0,
			NULL);

		if (!fOk)
		{
			// Is it a network-related error?
			HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL, DONT_RESOLVE_DLL_REFERENCES);

			if (hDll != NULL)
			{
				FormatMessage(
					FORMAT_MESSAGE_FROM_HMODULE |
					FORMAT_MESSAGE_FROM_SYSTEM |
					FORMAT_MESSAGE_IGNORE_INSERTS,
					hDll,
					dwError,
					MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
					(PTSTR)&hlocal,
					0,
					NULL);
				FreeLibrary(hDll);
			}
		}

		if (hlocal != NULL)
		{
			CString strDisplay;
			strDisplay.Format("%s 失败错误码=%d,Windows系统描述:%s", strDescription, dwError, (PCTSTR)LocalLock(hlocal));
			//WriteLog(strDisplay);
			LocalFree(hlocal);
		}
		else
		{
			//WriteLog("Error number not found.");
		}
#endif

	}

	// The system displays a dialog box with a custom message and a message to the user to close applications within the specified time-out interval (30 seconds). 
	// After the time-out interval elapses, the system is restarted.
	//The application must enable the SE_SHUTDOWN_NAME privilege before calling InitiateSystemShutdown
	BOOL MySystemShutdown(LPTSTR lpMsg)
	{
		HANDLE hToken;              // handle to process token 
		TOKEN_PRIVILEGES tkp;       // pointer to token structure 

		BOOL fResult;               // system shutdown flag 

		// Get the current process token handle so we can get shutdown 
		// privilege. 

		if (!OpenProcessToken(GetCurrentProcess(),
			TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
			return FALSE;

		// Get the LUID for shutdown privilege. 

		LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
			&tkp.Privileges[0].Luid);

		tkp.PrivilegeCount = 1;  // one privilege to set    
		tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		// Get shutdown privilege for this process. 

		AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
			(PTOKEN_PRIVILEGES)NULL, 0);

		// Cannot test the return value of AdjustTokenPrivileges. 

		if (GetLastError() != ERROR_SUCCESS)
			return FALSE;

		// Display the shutdown dialog box and start the countdown. 

		fResult = InitiateSystemShutdown(
			NULL,    // shut down local computer 
			lpMsg,   // message for user
			30,      // time-out period, in seconds 
			FALSE,   // ask user to close apps 
			TRUE);   // reboot after shutdown 

		if (!fResult)
			return FALSE;

		// Disable shutdown privilege. 

		tkp.Privileges[0].Attributes = 0;
		AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
			(PTOKEN_PRIVILEGES)NULL, 0);

		return TRUE;
	}

	// If the AbortSystemShutdown function is executed in the time-out period specified by InitiateSystemShutdown, 
	// the system does not shut down. For example, if PreventSystemShutdown is called after MySystemShutdown, 
	// the system closes the dialog box and does not restart the system.
	BOOL PreventSystemShutdown()
	{
		HANDLE hToken;              // handle to process token 
		TOKEN_PRIVILEGES tkp;       // pointer to token structure 

		// Get the current process token handle  so we can get shutdown 
		// privilege. 

		if (!OpenProcessToken(GetCurrentProcess(),
			TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
			return FALSE;

		// Get the LUID for shutdown privilege. 

		LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
			&tkp.Privileges[0].Luid);

		tkp.PrivilegeCount = 1;  // one privilege to set    
		tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		// Get shutdown privilege for this process. 

		AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
			(PTOKEN_PRIVILEGES)NULL, 0);

		if (GetLastError() != ERROR_SUCCESS)
			return FALSE;

		// Prevent the system from shutting down. 

		if (!AbortSystemShutdown(NULL))
			return FALSE;

		// Disable shutdown privilege. 

		tkp.Privileges[0].Attributes = 0;
		AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
			(PTOKEN_PRIVILEGES)NULL, 0);

		return TRUE;
	}

	// Shutting down flushes file buffers to disk and brings the system to a condition in which it is safe to turn off the computer
	// The application must first enable the SE_SHUTDOWN_NAME privilege. 
	// The final parameter in the call to ExitWindowsEx indicates that the system was shut down for a planning update of the operating system.
	BOOL MySystemShutdown()
	{
		HANDLE hToken;
		TOKEN_PRIVILEGES tkp;

		// Get a token for this process. 

		if (!OpenProcessToken(GetCurrentProcess(),
			TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
			return(FALSE);

		// Get the LUID for the shutdown privilege. 

		LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
			&tkp.Privileges[0].Luid);

		tkp.PrivilegeCount = 1;  // one privilege to set    
		tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		// Get the shutdown privilege for this process. 

		AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
			(PTOKEN_PRIVILEGES)NULL, 0);

		if (GetLastError() != ERROR_SUCCESS)
			return FALSE;

		// Shut down the system and force all applications to close. 

		if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE,
			SHTDN_REASON_MAJOR_OPERATINGSYSTEM |
			SHTDN_REASON_MINOR_UPGRADE |
			SHTDN_REASON_FLAG_PLANNED))
			return FALSE;

		return TRUE;
	}

	HWND GetProcessMainWnd(const DWORD& dwTagetProcessId, LPCTSTR lpTagetWndName)
	{
		DWORD dwCurPorcessId = 0;
		HWND hTagetProcessWnd = NULL;
		TCHAR szWndName[MAX_PATH] = { 0 };
		TCHAR szClassName[MAX_PATH] = { 0 };

		// 取得第一个窗口句柄;
		for (HWND hCurWnd = ::GetTopWindow(NULL); hCurWnd != NULL; hCurWnd = ::GetNextWindow(hCurWnd, GW_HWNDNEXT)) {
			// 重置为0;
			dwCurPorcessId = 0;
			// 通过窗口句柄反查进程pid;
			DWORD dwThreadId = ::GetWindowThreadProcessId(hCurWnd, &dwCurPorcessId);
			if (dwThreadId != 0) {
				// 判断当前进程id是否和目标进程id相同;
				if (dwCurPorcessId == dwTagetProcessId) {
					if (lpTagetWndName == NULL) {
						hTagetProcessWnd = hCurWnd;
						break;
					}
					else {
						// 获取窗口名称;
						::GetWindowText(hCurWnd, szWndName, sizeof(szWndName) / sizeof(TCHAR));
						// 获取窗口类名;
						::GetClassName(hCurWnd, szClassName, sizeof(szClassName) / sizeof(TCHAR));
#ifdef _DEBUG
						TCHAR szLogMsg[MAX_PATH] = { 0 };
						_stprintf_s(szLogMsg, _T("类名：%s， 窗口名：%s，窗口地址：%p \n"), szClassName, szWndName, hCurWnd);
						OutputDebugString(szLogMsg);
#endif
						if (_tcsstr(szWndName, lpTagetWndName) != NULL) {
							hTagetProcessWnd = hCurWnd;
							break;
						}
					}
				}
			}
		}

		// 当前窗口有可能不是进程父窗口;
		HWND hParentWnd = hTagetProcessWnd;
		while (hParentWnd) {
			hParentWnd = ::GetParent(hTagetProcessWnd);
			if (hParentWnd == NULL)
				break;
			hTagetProcessWnd = hParentWnd;
		}

		return hTagetProcessWnd;
	}

	BOOL CALLBACK EnumChildWindowCallBack(HWND hWnd, LPARAM lParam)  
	{  
		DWORD dwPid = 0;  
		LPPROC_WND_INFO lpWndData = (LPPROC_WND_INFO)lParam;
		GetWindowThreadProcessId(hWnd, &dwPid); // 获得找到窗口所属的进程  
		if(dwPid == lpWndData->dwProcId) // 判断是否是目标进程的窗口  
		{  
			WND_INFO wndInfo;
			wndInfo.hWnd=hWnd;
			SendMessage(hWnd, WM_GETTEXT, MAX_PATH, (LPARAM)wndInfo.szWndTitle);  
			GetClassName(hWnd, wndInfo.szClassName, MAX_PATH);
			wndInfo.dwCtrlId = ::GetDlgCtrlID(hWnd);
			lpWndData->AddWnd(wndInfo);
			// 输出窗口信息  
			TRACE4("A-0x%08X, %ld, %s, %s\n", hWnd, wndInfo.dwCtrlId, wndInfo.szClassName, wndInfo.szWndTitle);  
			// 此处如果再递归,会导致重复查找2次子窗口;
			//EnumChildWindows(hWnd, EnumChildWindowCallBack, lParam);    // 递归查找子窗口 
			return TRUE;
		}  

		return FALSE;  
	}  

	BOOL CALLBACK EnumWindowCallBack(HWND hWnd, LPARAM lParam)  
	{  
		DWORD dwPid = 0; 
		LPPROC_WND_INFO lpWndData = (LPPROC_WND_INFO)lParam;
		GetWindowThreadProcessId(hWnd, &dwPid); // 获得找到窗口所属的进程  
		if(dwPid == lpWndData->dwProcId) // 判断是否是目标进程的窗口  
		{  
			WND_INFO wndInfo;
			wndInfo.hWnd=hWnd;
			SendMessage(hWnd, WM_GETTEXT, MAX_PATH, (LPARAM)wndInfo.szWndTitle);  
			GetClassName(hWnd, wndInfo.szClassName, MAX_PATH);
			wndInfo.dwCtrlId = ::GetDlgCtrlID(hWnd);
			lpWndData->AddWnd(wndInfo);
			// 输出窗口信息  
			TRACE4("A-0x%08X, %ld, %s, %s\n", hWnd, wndInfo.dwCtrlId, wndInfo.szClassName, wndInfo.szWndTitle);  
			EnumChildWindows(hWnd, EnumChildWindowCallBack, lParam);    // 继续查找子窗口  
		}

		return TRUE;  
	} 

	BOOL EnumProcessAllWnd(LPPROC_WND_INFO lpProcWndInfo)
	{
		lpProcWndInfo->vtWndInfo.clear();
		return EnumWindows(EnumWindowCallBack, (LPARAM)lpProcWndInfo);
	}

	BOOL StartProcess(LPCTSTR lpPath, BOOL bShowWnd, BOOL bSuspend)
	{
		STARTUPINFO si;
		PROCESS_INFORMATION pi;
		ZeroMemory(&si, sizeof(si));
		si.cb = sizeof(si);
		ZeroMemory(&pi, sizeof(pi));

		CString strDir = lpPath;
		strDir = strDir.Left(strDir.ReverseFind(_T('\\')));
		si.dwFlags = STARTF_USESHOWWINDOW;							// 指定wShowWindow成员有效
		si.wShowWindow = bShowWnd ? SW_SHOW : SW_HIDE;				// 设置创建进程时，窗口不显示，
		// 为FALSE的话则不显示
		BOOL bRet = ::CreateProcess (
			lpPath,				// 不在此指定可执行文件的文件名
			NULL,				// 命令行参数
			NULL,				// 默认进程安全性
			NULL,				// 默认线程安全性
			FALSE,				// 指定当前进程内的句柄不可以被子进程继承
			bSuspend ? CREATE_SUSPENDED : NULL,
			NULL,				// 使用本进程的环境变量
			strDir,				// 使用本进程的驱动器和目录
			&si,
			&pi);

		if(bRet)
		{
			// 进程挂起后，仍能成功注入dll;
			// 		TCHAR szDllPath[MAX_PATH];
			// 		ZeroMemory(szDllPath,MAX_PATH);
			// 		_stprintf_s(szDllPath, _T("%shook.dll"), g_szModulePath);
			// 		for (int i = 0; i < 10; i++)
			// 		{
			// 			CInjection inject(pi.dwProcessId,szDllPath);
			// 			inject.InjectDynamicLibrary();
			// 			inject.EjectDynamicLibrary();
			// 		}
			g_procWndInfo.dwProcId = pi.dwProcessId;
			// 既然我们不使用两个句柄，最好是立刻将它们关闭
			::CloseHandle (pi.hThread);
			::CloseHandle (pi.hProcess);
		}

		return bRet;
	}
};