#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>

TCHAR g_szModulePath[MAX_PATH] = _T("");			// 软件目录;
TCHAR g_szModuleFileName[MAX_PATH] = _T("");		// 软件名称;
TCHAR g_szIniFile[MAX_PATH] = _T("");

// 配置文件信息;
TCHAR g_szServAddress[MAX_PATH] = _T("");		
DWORD g_dwServPort = 0;		
TCHAR g_szAccount[MAX_PATH] = _T("");		
TCHAR g_szPassword[MAX_PATH] = _T("");	
TCHAR g_szWeChatPath[MAX_PATH] = _T("");		
TCHAR g_szCacheDir[MAX_PATH] = _T("");		
TCHAR g_szDynamicLibraryPath[MAX_PATH] = _T("");

// 控制台输出;
BOOL g_bStdOut = FALSE;

/************************************************************************/
/*  函数：[1/6/2019 Home];
/*  描述：;
/*  参数：;
/*  	[IN] ：;
/*  	[OUT] ：;
/*  	[IN/OUT] ：;
/*  返回：void;
/*  注意：;
/*  示例：;
/*
/*  修改：;
/*  日期：;
/*  内容：;
/************************************************************************/
int GetIniInfo(LPCTSTR lpIniDir /* = NULL */, LPCTSTR lpIniName /* = 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);

	// 动态库路径;
	_stprintf_s(g_szDynamicLibraryPath, _T("%shook.dll"), g_szModulePath);

	if (lpIniDir != NULL && lpIniName != NULL)
		_stprintf_s(g_szIniFile, _T("%s%s"), lpIniDir, lpIniName);
	else
		_stprintf_s(g_szIniFile, _T("%sconfig.ini"), g_szModulePath);

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

	if (ERROR_FILE_NOT_FOUND == GetLastError())
	{
		return -1;
	}

	CloseHandle(hFile);
	hFile = NULL;

	// 获取服务器端信息;
	GetPrivateProfileString(_T("ServerInfo"), _T("IP"), _T(""), g_szServAddress, MAX_PATH, g_szIniFile);
	g_dwServPort = GetPrivateProfileInt(_T("ServerInfo"), _T("Port"), 0, g_szIniFile);
	GetPrivateProfileString(_T("CustomerInfo"), _T("Account"), _T(""), g_szAccount, MAX_PATH, g_szIniFile);
	GetPrivateProfileString(_T("CustomerInfo"), _T("Password"), _T(""), g_szPassword, MAX_PATH, g_szIniFile);
	GetPrivateProfileString(_T("CustomerInfo"), _T("WeChat"), _T(""), g_szWeChatPath, MAX_PATH, g_szIniFile);
	GetPrivateProfileString(_T("CustomerInfo"), _T("Cache"), _T(""), g_szCacheDir, MAX_PATH, g_szIniFile);

	g_bStdOut = GetPrivateProfileInt(_T("CustomerInfo"), _T("StdOut"), 0, g_szIniFile);
	if ( g_bStdOut )
	{
		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 0;
}

/************************************************************************/
/*  函数：[1/6/2019 Home];
/*  描述：;
/*  参数：;
/*  	[IN] ：;
/*  	[OUT] ：;
/*  	[IN/OUT] ：;
/*  返回：void;
/*  注意：;
/*  示例：;
/*
/*  修改：;
/*  日期：;
/*  内容：;
/************************************************************************/
DWORD FindProcess(LPCTSTR lpProName)
{
	ASSERT(lpProName!=NULL);

	DWORD dwPID = 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)
			{
				dwPID = pe32.th32ProcessID;
				break;
			}
		} while (Process32Next(hSnapshot, &pe32));
	}
	CloseHandle(hSnapshot);

	return dwPID;
}

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;
}

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

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

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

	return me32.hModule;
}

HANDLE FindModuleEx(LPCTSTR lpModuleName, DWORD dwPid)
{
	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, dwPid);
	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));
			if (_tcsicmp(lpModuleName, szModName) == 0)
			{
				CloseHandle(hProcess);
				return hMods[i];
			}
		}
	}

	CloseHandle(hProcess);

	return NULL;
}

// WINDOWS NT 以上的内核需要提权,才能对系统进行高级管理;
/************************************************************************/
/*  函数：[1/6/2019 Home];
/*  描述：;
/*  参数：;
/*  	[IN] ：;
/*  	[OUT] ：;
/*  	[IN/OUT] ：;
/*  返回：void;
/*  注意：;
/*  示例：;
/*
/*  修改：;
/*  日期：;
/*  内容：;
/************************************************************************/
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;
}

BOOL getWeChatPath()
{
	// 通过注册表获取微信安装目录;
	HKEY hKey = NULL;
	if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Tencent\\WeChat"), &hKey))
	{
		return FALSE;
	}

	DWORD Type = REG_SZ;
	DWORD cbData = MAX_PATH*sizeof(WCHAR);
	if(ERROR_SUCCESS != RegQueryValueEx(hKey, _T("InstallPath"), 0, &Type, (LPBYTE)g_szWeChatPath, &cbData))
	{
		RegCloseKey(hKey);
		return FALSE;
	}

	PathAppend(g_szWeChatPath, _T("WeChat.exe"));

	return TRUE;
}

BOOL OpenWeChat()
{
#if 1
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&pi, sizeof(pi));

	si.dwFlags = STARTF_USESHOWWINDOW;  // 指定wShowWindow成员有效
	si.wShowWindow = TRUE;				// 此成员设为TRUE的话则显示新建进程的主窗口，
	// 为FALSE的话则不显示
	BOOL bRet = ::CreateProcess (
		g_szWeChatPath,		// 不在此指定可执行文件的文件名
		NULL,				// 命令行参数
		NULL,				// 默认进程安全性
		NULL,				// 默认线程安全性
		FALSE,				// 指定当前进程内的句柄不可以被子进程继承
		//CREATE_SUSPENDED,	// 挂起进程;CREATE_SUSPENDED
		//NORMAL_PRIORITY_CLASS,
		//CREATE_NEW_CONSOLE,
		NULL,
		NULL,				// 使用本进程的环境变量
		NULL,				// 使用本进程的驱动器和目录
		&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();
// 		}

		//不sleep就会出现读取不到的297错误
		//Sleep(5000);				

		//获取线程上下文
// 		CONTEXT ct = { 0 };
// 		ct.ContextFlags = CONTEXT_CONTROL;
// 		GetThreadContext(pi.hThread, &ct);
// 
// 		::ResumeThread(pi.hThread);	

		// 既然我们不使用两个句柄，最好是立刻将它们关闭
		::CloseHandle (pi.hThread);
		::CloseHandle (pi.hProcess);

		// 当进程挂起时,是无法修改关闭微信句柄;
		PatchWeChat();
	}
#else

	SHELLEXECUTEINFO sei;
	memset(&sei, 0, sizeof(SHELLEXECUTEINFO));
	sei.cbSize = sizeof(SHELLEXECUTEINFO);
	sei.hwnd = NULL;
	sei.lpVerb = _T("open");
	//sei.lpVerb = _T("runas");
	//sei.fMask = SEE_MASK_NOCLOSEPROCESS;
	sei.lpFile = g_szWeChatPath;
	sei.lpParameters = NULL;
	sei.lpDirectory = NULL;
	sei.nShow = SW_NORMAL;
	sei.hInstApp = NULL;

	if ( !ShellExecuteEx(&sei) )
	{
		DWORD dw = GetLastError();
		return FALSE;
	}
	PatchWeChat();
#endif

	return TRUE;
}