Bladeren bron

脚本执行器不使用ScriptExcutor,而是使用PythonExecutor;

scbc.sat2 5 jaren geleden
bovenliggende
commit
f806501af6

+ 733 - 0
SATService/SATService/PythonExecutor.cpp

@@ -0,0 +1,733 @@
+#include "StdAfx.h"
+#include "PythonExecutor.h"
+#include <fstream>
+#include <io.h>
+#include <fcntl.h>
+#include "Python.h"
+// 服务进程头文件;
+#include <Wtsapi32.h>
+#include <TlHelp32.h>
+#pragma comment(lib, "wtsapi32.lib")
+#include <Userenv.h>
+#pragma comment(lib,"userenv.lib")
+
+/*
+typedef struct _TOKEN_LINKED_TOKEN {
+HANDLE LinkedToken;
+} TOKEN_LINKED_TOKEN, *PTOKEN_LINKED_TOKEN;
+SERVICE_STATUS_HANDLE hServiceStatus;
+*/
+
+CPythonExecutor::CPythonExecutor(void)
+{
+	m_hWorkThread = NULL;
+	m_hStdoutLogThread = NULL;
+	m_bStatus = FALSE;
+
+	m_hStdOutRead = NULL;
+	m_hStdOutWrite = NULL;
+	m_hStdErrorRead = NULL;
+	m_hStdErrorWrite = NULL;
+
+	memset(m_szScriptPath, 0, MAX_PATH);
+	memset(m_szLogPath, 0, MAX_PATH);
+	memset(m_szExtraSentence, 0, MAX_PATH);
+
+	memset(&m_si, 0, sizeof(m_si));
+	memset(&m_pi, 0, sizeof(m_pi));
+	m_si.cb = sizeof(m_si);
+
+	m_bKillProcess = FALSE;
+	m_bStopLogExport = FALSE;
+	m_dwThreadId = 0;
+	m_dwSubprocessId = 0;
+
+	m_pCaseObj = NULL;
+	m_ulStartTickCount = 0;
+}
+
+CPythonExecutor::~CPythonExecutor(void)
+{
+	EndWorkThread();
+	EndLogThread();
+}
+
+DWORD CPythonExecutor::_WorkerThread(LPVOID lpParam)
+{
+	CPythonExecutor* that = (CPythonExecutor*)lpParam;
+	if ( !that ) {
+		printf("Error:参数失效\n");
+		return -1;
+	}
+
+#ifdef _DEBUG
+	that->RunScriptProcess();
+#else
+	that->ServiceRunScriptProcess();
+#endif
+
+	// 延时;
+	Sleep(2000);
+	// 结束日志线程;
+	that->m_bStopLogExport = TRUE;
+	GLOBAL::WriteTextLog(_T("脚本执行线程结束,用例名=%s"), that->m_pCaseObj->strCaseName.c_str());
+
+	return 0;
+}
+
+DWORD CPythonExecutor::_StdoutLogExportThread(LPVOID lpParam)
+{
+	CPythonExecutor* that = (CPythonExecutor*)lpParam;
+	if (!that) {
+		printf("Error:参数失效\n");
+		return -1;
+	}
+
+
+	DWORD dwRead;
+	CHAR chBuf[BUFSIZE] = {0};
+	BOOL bSuccess = FALSE;
+
+	do
+	{
+		bSuccess = ReadFile(that->m_hStdOutRead, chBuf, BUFSIZE, &dwRead, NULL);
+		if (!bSuccess || dwRead == 0 || !_tcslen(chBuf)) 
+			continue;
+
+		that->m_ulStartTickCount = GetTickCount64();
+		GLOBAL::WritePythonLog(that->m_szLogPath, chBuf);
+		// 更新日志时间;
+		if ( that->m_pCaseObj )
+			that->m_pCaseObj->_ulStartTickCount = GetTickCount64();
+		memset(chBuf, 0, BUFSIZE);
+	} while (!that->m_bStopLogExport);
+
+
+
+	// 结束重定向;
+	that->EndSubprocessStdOut();
+	GLOBAL::WriteTextLog(_T("脚本日志线程结束,用例名=%s"), that->m_pCaseObj->strCaseName.c_str());
+
+	return 0;
+}
+
+DWORD CPythonExecutor::_StderrLogExportThread(LPVOID lpParam)
+{
+	CPythonExecutor* that = (CPythonExecutor*)lpParam;
+	if (!that) {
+		printf("Error:参数失效\n");
+		return -1;
+	}
+
+
+	DWORD dwRead;
+	CHAR chBuf[BUFSIZE] = {0};
+	BOOL bSuccess = FALSE;
+
+	do
+	{
+		bSuccess = ReadFile(that->m_hStdOutRead, chBuf, BUFSIZE, &dwRead, NULL);
+		if (!bSuccess || dwRead == 0 || !_tcslen(chBuf)) 
+			continue;
+
+		that->m_ulStartTickCount = GetTickCount64();
+		GLOBAL::WritePythonLog(that->m_szLogPath, chBuf);
+		// 更新日志时间;
+		if ( that->m_pCaseObj )
+			that->m_pCaseObj->_ulStartTickCount = GetTickCount64();
+		memset(chBuf, 0, BUFSIZE);
+	} while (!that->m_bStopLogExport);
+
+	// 结束重定向;
+	that->EndSubprocessStdOut();
+	GLOBAL::WriteTextLog(_T("脚本日志线程结束,用例名=%s"), that->m_pCaseObj->strCaseName.c_str());
+
+	return 0;
+}
+
+int CPythonExecutor::RedirectSubprocessStdout(LPSTARTUPINFO si /*=NULL*/)
+{
+	// Python脚本中,必须使用sys.__stdout__()
+	SECURITY_ATTRIBUTES sa;
+	sa.bInheritHandle = TRUE;
+	sa.lpSecurityDescriptor = NULL;
+	sa.nLength = sizeof(sa);
+
+	// 创建stdout的管道;
+	if (!CreatePipe(&m_hStdOutRead, &m_hStdOutWrite, &sa, 0)) {
+		OutputDebugString("Error:创建stdout管道失败\n");
+		return -1;
+	}
+
+#if 0// 标准输出和标准错误都由同一个句柄重定向,此时只需要一个线程来读取管道内容;
+	// 创建stderr的管道,由于stderr一般就是stdout,直接复制句柄;
+	if (!DuplicateHandle(GetCurrentProcess(), m_hStdOutWrite, GetCurrentProcess(), &m_hStdErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
+		OutputDebugString("创建stderr管道失败\n");
+		return -2;
+	}
+#else
+	// 标准输出和标准错误分开,需要二个线程来读取管道内容;
+	if (!CreatePipe(&m_hStdErrorRead, &m_hStdErrorWrite, &sa, 0)) {
+		OutputDebugString("Error:创建stderr管道失败\n");
+		return -1;
+	}
+#endif
+
+	si->dwFlags |= STARTF_USESTDHANDLES;
+	// 将子进程的stdout输出到句柄hStdOutWrite;
+	si->hStdOutput = m_hStdOutWrite;
+	// 将子进程的stderr输出到句柄hStdErrWrite;
+	si->hStdError = m_hStdErrorWrite;
+
+	return 0;
+}
+
+int CPythonExecutor::RunScriptProcess()
+{
+	if (!PathFileExists(m_szScriptPath))
+	{
+		printf("Error:脚本路径无效\n");
+		return -1;
+	}
+
+	// 初始化参数;
+	::memset(&m_si, 0, sizeof(m_si));
+	::memset(&m_pi, 0, sizeof(m_pi));
+	m_si.cb = sizeof(m_si);
+	GetStartupInfo(&m_si); 
+
+	// 强制stdion, stdout和stderr完全无缓冲:python -u
+	TCHAR szCommandLine[MAX_PATH] = { 0 };
+	if (_tcslen(m_szExtraSentence))
+		_stprintf_s(szCommandLine, _T("python -W ignore -u %s %s"), m_szScriptPath, m_szExtraSentence);
+	else
+		_stprintf_s(szCommandLine, _T("python -W ignore -u %s"), m_szScriptPath);
+
+	// 重定向输出;
+	RedirectSubprocessStdout(&m_si);
+	// 恢复日志线程;
+	ResumeThread(m_hStdoutLogThread);
+
+	// 启动子进程;
+	if (!CreateProcess(
+		NULL,					// No module name (use command line)
+		szCommandLine,			// Command line
+		NULL,					// Process handle not inheritable
+		NULL,					// Thread handle not inheritable
+		TRUE,					// Set handle inheritance to TRUE
+		0,						// No creation flags
+		NULL,					// Use parent's environment block
+		NULL,					// Use parent's starting directory 
+		&m_si,					// Pointer to STARTUPINFO structure
+		&m_pi)					// Pointer to PROCESS_INFORMATION structure
+		)
+	{
+		GLOBAL::WriteTextLog("Error:创建子进程失败 (%d)", GetLastError());
+		return -3;
+	}
+
+	m_dwSubprocessId = m_pi.dwProcessId;
+	// 等待进程完成退出.
+	WaitForSingleObject(m_pi.hProcess, INFINITE);
+
+	Sleep(5000);
+	GLOBAL::WriteTextLog("脚本进程结束(%ld)",  m_pi.dwProcessId);
+	// 结束日志线程;
+	m_bStopLogExport = TRUE;
+	// 同时需要关闭输出的管道,否则ReadFile会阻塞;
+	CloseHandle(m_hStdOutWrite);
+	m_hStdOutWrite = NULL;
+	CloseHandle(m_hStdErrorWrite);
+	m_hStdErrorWrite = NULL;
+
+	// 关闭进程句柄. 
+	CloseHandle(m_pi.hProcess);
+	CloseHandle(m_pi.hThread);
+
+	// 重置;
+	memset(&m_si, 0, sizeof(m_si));
+	memset(&m_pi, 0, sizeof(m_pi));
+	m_si.cb = sizeof(m_si);
+
+	return 0;
+}
+
+int CPythonExecutor::ServiceRunScriptProcess()
+{
+	if (!PathFileExists(m_szScriptPath)) {
+		printf("Error:脚本路径无效\n");
+		return -1;
+	}
+
+	DWORD dwProcesses = 0;
+	BOOL bResult = FALSE;
+	DWORD dwRet = 0;
+	DWORD dwSid = GetActiveSessionID();
+
+	HANDLE hProcess = NULL, hPToken = NULL, hUserTokenDup = NULL;
+	if (!WTSQueryUserToken(dwSid, &hPToken)) {
+		PROCESSENTRY32 procEntry;
+		DWORD dwPid = 0;
+		HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+		if (hSnap == INVALID_HANDLE_VALUE) {
+			return FALSE;
+		}
+
+		procEntry.dwSize = sizeof(PROCESSENTRY32);
+		if (Process32First(hSnap, &procEntry)) {
+			do {
+				if (_tcsicmp(procEntry.szExeFile, _T("explorer.exe")) == 0) {
+					DWORD exeSessionId = 0;
+					if (ProcessIdToSessionId(procEntry.th32ProcessID, &exeSessionId) && exeSessionId == dwSid) {
+						dwPid = procEntry.th32ProcessID;
+						break;
+					}
+				}
+
+			} while (Process32Next(hSnap, &procEntry));
+		}
+		CloseHandle(hSnap);
+
+		// explorer进程不存在
+		if (dwPid == 0) {
+			return FALSE;
+		}
+
+		hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPid);
+		if (hProcess == NULL) {
+			return FALSE;
+		}
+
+		if(!::OpenProcessToken(hProcess, TOKEN_ALL_ACCESS_P,&hPToken)) {
+			CloseHandle(hProcess);
+			return FALSE;
+		}
+	}
+
+	if (hPToken == NULL)
+		return -1;
+
+	TOKEN_LINKED_TOKEN admin;
+	bResult = GetTokenInformation(hPToken, (TOKEN_INFORMATION_CLASS)19, &admin, sizeof(TOKEN_LINKED_TOKEN), &dwRet);
+
+	if (!bResult) {// vista 以前版本不支持TokenLinkedToken
+		TOKEN_PRIVILEGES tp;
+		LUID luid;
+		if (LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid)) {
+			tp.PrivilegeCount =1;
+			tp.Privileges[0].Luid =luid;
+			tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;
+		}
+		DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hUserTokenDup);
+	} else {
+		hUserTokenDup = admin.LinkedToken;
+	}
+
+	LPVOID pEnv =NULL;
+	DWORD dwCreationFlags = CREATE_PRESERVE_CODE_AUTHZ_LEVEL;
+
+	if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))
+	{
+		dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT;
+	}
+	else
+	{
+		pEnv = NULL;
+	}
+
+	ZeroMemory( &m_si, sizeof(m_si) );
+	m_si.cb = sizeof(m_si);
+	m_si.dwFlags = STARTF_USESHOWWINDOW;
+	m_si.wShowWindow = SW_HIDE;
+	ZeroMemory( &m_pi, sizeof(m_pi) );
+
+	// 重定向输出;
+	RedirectSubprocessStdout(&m_si);
+	// 恢复日志线程;
+	ResumeThread(m_hStdoutLogThread);
+
+	// 强制stdion, stdout和stderr完全无缓冲:python -u
+	TCHAR szCommandLine[MAX_PATH] = { 0 };
+	if (_tcslen(m_szExtraSentence))
+		_stprintf_s(szCommandLine, _T("python -W ignore -u %s %s"), m_szScriptPath, m_szExtraSentence);
+	else
+		_stprintf_s(szCommandLine, _T("python -W ignore -u %s"), m_szScriptPath);
+
+	bResult = CreateProcessAsUser(
+		hUserTokenDup,                     // client's access token
+		NULL,    // file to execute
+		szCommandLine,                 // command line
+		NULL,            // pointer to process SECURITY_ATTRIBUTES
+		NULL,               // pointer to thread SECURITY_ATTRIBUTES
+		TRUE,              // handles are not inheritable
+		dwCreationFlags,     // creation flags
+		pEnv,               // pointer to new environment block
+		NULL,               // name of current directory
+		&m_si,               // pointer to STARTUPINFO structure
+		&m_pi                // receives information about new process
+		);	
+	if ( !bResult )
+		return -1;
+
+	m_dwSubprocessId = m_pi.dwProcessId;
+
+	// 等待进程完成退出.
+	WaitForSingleObject(m_pi.hProcess, INFINITE);
+
+	Sleep(5000);
+	// 结束日志线程;
+	m_bStopLogExport = TRUE;
+
+	// 关闭进程句柄. 
+	CloseHandle(m_pi.hProcess);
+	CloseHandle(m_pi.hThread);
+	// 同时需要关闭输出的管道,否则ReadFile会阻塞;
+	CloseHandle(m_hStdOutWrite);
+	m_hStdOutWrite = NULL;
+	CloseHandle(m_hStdErrorWrite);
+	m_hStdErrorWrite = NULL;
+
+	// 重置;
+	memset(&m_si, 0, sizeof(m_si));
+	memset(&m_pi, 0, sizeof(m_pi));
+	m_si.cb = sizeof(m_si);
+
+	if (hUserTokenDup != NULL)
+		CloseHandle(hUserTokenDup);
+	if (hProcess != NULL)
+		CloseHandle(hProcess);
+	if (hPToken != NULL)
+		CloseHandle(hPToken);
+	if (pEnv != NULL)
+		DestroyEnvironmentBlock(pEnv);
+
+	return TRUE;
+}
+
+bool CPythonExecutor::StartThread()
+{
+	// 创建线程;
+	m_hWorkThread = CreateThread(NULL, 0, _WorkerThread, this, 0, &m_dwThreadId);
+	if (!m_hWorkThread) {
+		printf("Error:创建执行线程失败\n");
+		return false;
+	}
+
+	// 创建线程;
+	m_hStdoutLogThread = CreateThread(NULL, 0, _StdoutLogExportThread, this, CREATE_SUSPENDED, NULL);
+	if (!m_hStdoutLogThread) {
+		printf("Error:创建日志线程失败\n");
+		return false;
+	}
+
+	return true;
+}
+
+void CPythonExecutor::EndWorkThread()
+{
+	// 结束进程;
+	EndSubprocess();
+	// 等待3秒,是否能自主结束线程;
+	if (m_hWorkThread) {
+		if (WaitForSingleObject(m_hWorkThread, 3000) == WAIT_OBJECT_0) {
+			CloseHandle(m_hWorkThread);
+			m_hWorkThread = NULL;
+			return;
+		}
+	}
+
+	// 手动结束线程;
+	if ( m_hWorkThread ) {
+		// 尝试5次结束行为;
+		for (int i = 0; i < 5; i++) {
+			if (TerminateThread(m_hWorkThread, 0))
+				break;
+		}
+
+		CloseHandle(m_hWorkThread);
+		m_hWorkThread = NULL;
+	}
+}
+
+void CPythonExecutor::EndLogThread()
+{
+	m_bStopLogExport = TRUE;
+	// 等待3秒,是否能自主结束线程;
+	if ( m_hStdoutLogThread ) {
+		if ( WaitForSingleObject(m_hStdoutLogThread, 3000) == WAIT_OBJECT_0 ) {
+			CloseHandle(m_hStdoutLogThread);
+			m_hStdoutLogThread = NULL;
+			return;
+		}	
+	}
+
+	// 手动结束线程;
+	if (m_hStdoutLogThread) {
+		// 尝试5次结束行为;
+		for (int i = 0; i < 5; i++) {
+			if (TerminateThread(m_hStdoutLogThread, 0))
+				break;
+		}
+
+		CloseHandle(m_hStdoutLogThread);
+		m_hStdoutLogThread = NULL;
+	}
+}
+
+void CPythonExecutor::EndThread()
+{
+	EndWorkThread();
+	EndLogThread();
+}
+
+BOOL CPythonExecutor::EndSubprocess()
+{
+	BOOL ret = false;
+	if (m_pi.hProcess) {
+		// 尝试5次结束;
+		for (int i = 0; i < 5; i++) {
+			if ( (ret = TerminateProcess(m_pi.hProcess, 0)) )
+				break;
+		}
+	}
+
+	return ret;
+}
+
+void CPythonExecutor::EndSubprocessStdOut()
+{
+	OutputDebugString("--------------EndSubprocessStdOut----------------\n");
+	// 关闭重定向句柄;
+	if (m_hStdErrorWrite)
+		CloseHandle(m_hStdErrorWrite);
+	m_hStdErrorWrite = NULL;
+
+	// 只有子进程方式才关闭句柄;
+	if ( m_hStdOutWrite)
+		CloseHandle(m_hStdOutWrite);
+	m_hStdOutWrite = NULL;
+
+	if (m_hStdOutRead)
+		CloseHandle(m_hStdOutRead);
+	m_hStdOutRead = NULL;	
+}
+
+DWORD CPythonExecutor::GetActiveSessionID()
+{
+	DWORD dwSessionId = 0;
+	PWTS_SESSION_INFO pSessionInfo = NULL;
+	DWORD dwCount = 0;
+
+	WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);
+
+	for(DWORD i = 0; i < dwCount; i++) {
+		WTS_SESSION_INFO si = pSessionInfo[i];
+		if(WTSActive == si.State) {
+			dwSessionId = si.SessionId;
+			break;
+		}
+	}
+
+	WTSFreeMemory(pSessionInfo);
+
+	return dwSessionId;
+}
+
+BOOL CPythonExecutor::ServiceExecute(std::wstring wstrCmdLine, INT32& n32ExitResult)
+{
+	ofstream ofile("C:\\logEvent.txt");
+	ofile<<"start excute"<<std::endl;
+	DWORD dwProcesses = 0;
+	BOOL bResult = FALSE;
+
+	DWORD dwSid = GetActiveSessionID();
+
+	DWORD dwRet = 0;
+	PROCESS_INFORMATION pi;
+	STARTUPINFO si;
+	HANDLE hProcess = NULL, hPToken = NULL, hUserTokenDup = NULL;
+	if (!WTSQueryUserToken(dwSid, &hPToken)) {
+		PROCESSENTRY32 procEntry;
+		DWORD dwPid = 0;
+		HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+		if (hSnap == INVALID_HANDLE_VALUE) {
+			return FALSE;
+		}
+
+		procEntry.dwSize = sizeof(PROCESSENTRY32);
+		if (Process32First(hSnap, &procEntry)) {
+			do {
+				if (_tcsicmp(procEntry.szExeFile, _T("explorer.exe")) == 0) {
+					DWORD exeSessionId = 0;
+					if (ProcessIdToSessionId(procEntry.th32ProcessID, &exeSessionId) && exeSessionId == dwSid) {
+						dwPid = procEntry.th32ProcessID;
+						break;
+					}
+				}
+
+			} while (Process32Next(hSnap, &procEntry));
+		}
+		CloseHandle(hSnap);
+
+		// explorer进程不存在
+		if (dwPid == 0)
+		{
+			return FALSE;
+		}
+
+		hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPid);
+		if (hProcess == NULL) {
+			return FALSE;
+		}
+
+		if(!::OpenProcessToken(hProcess, TOKEN_ALL_ACCESS_P,&hPToken)) {
+			CloseHandle(hProcess);
+			return FALSE;
+		}
+	}
+
+	if (hPToken == NULL)
+		return FALSE;
+
+	TOKEN_LINKED_TOKEN admin;
+	bResult = GetTokenInformation(hPToken, (TOKEN_INFORMATION_CLASS)19, &admin, sizeof(TOKEN_LINKED_TOKEN), &dwRet);
+
+	if (!bResult)  {// vista 以前版本不支持TokenLinkedToken
+		TOKEN_PRIVILEGES tp;
+		LUID luid;
+		if (LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid)) {
+			tp.PrivilegeCount =1;
+			tp.Privileges[0].Luid =luid;
+			tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;
+		}
+		DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hUserTokenDup);
+	} else {
+		hUserTokenDup = admin.LinkedToken;
+		ofile<<"token: "<<hUserTokenDup<<std::endl;
+	}
+
+	LPVOID pEnv =NULL;
+	DWORD dwCreationFlags = CREATE_PRESERVE_CODE_AUTHZ_LEVEL;
+
+	if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE)) {
+		dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT;
+	} else {
+		pEnv = NULL;
+	}
+
+	ZeroMemory( &si, sizeof(si) );
+	si.cb = sizeof(si);
+	si.dwFlags = STARTF_USESHOWWINDOW;
+	si.wShowWindow = SW_SHOWNORMAL;
+	ZeroMemory( &pi, sizeof(pi) );
+
+	bResult = CreateProcessAsUser(
+		hUserTokenDup,                     // client's access token
+		NULL,							// file to execute
+		(LPTSTR) wstrCmdLine.c_str(),                 // command line
+		NULL,							// pointer to process SECURITY_ATTRIBUTES
+		NULL,							// pointer to thread SECURITY_ATTRIBUTES
+		FALSE,							// handles are not inheritable
+		dwCreationFlags,				// creation flags
+		pEnv,							// pointer to new environment block
+		NULL,							// name of current directory
+		&si,							// pointer to STARTUPINFO structure
+		&pi								// receives information about new process
+		);	
+
+	if(pi.hProcess) {
+		if(WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, 180000)) {
+			DWORD dwResult = 0;
+			if(GetExitCodeProcess(pi.hProcess,  &dwResult)) {
+				n32ExitResult = dwResult;
+			} else {
+				n32ExitResult = -1;
+			}
+
+			CloseHandle(pi.hThread);
+			CloseHandle(pi.hProcess);
+		} else {
+			CloseHandle(pi.hThread);
+			CloseHandle(pi.hProcess);
+			n32ExitResult = -1;
+		}
+	}
+
+	if (hUserTokenDup != NULL)
+		CloseHandle(hUserTokenDup);
+	if (hProcess != NULL)
+		CloseHandle(hProcess);
+	if (hPToken != NULL)
+		CloseHandle(hPToken);
+	if (pEnv != NULL)
+		DestroyEnvironmentBlock(pEnv);
+
+	return TRUE;
+
+}
+
+bool CPythonExecutor::InitScript(std::string strScript, std::string strLogPath, std::string strScriptCmd)
+{
+	// 判断脚本是否存在;
+	if (!PathFileExists(strScript.c_str())) {
+		printf("Error:脚本文件不存在\n");
+		return false;
+	}
+
+	// 判断日志文件路径是否可创建;
+	if (!PathFileExists(strLogPath.c_str())) {
+		// 创建路径;
+		if (!GLOBAL::MKDIR(strLogPath.c_str())) {
+			printf("Error:创建目录失败\n");
+			return false;
+		}
+
+		// 创建文件;
+		std::ofstream flog(strLogPath.c_str());
+		if ( flog.bad() ) {
+			printf("Error:创建文件失败\n");
+			return false;
+		}
+		flog.close();
+	}
+
+	// 赋值参数;
+	m_dwThreadId = m_dwSubprocessId = 0;
+	_tcscpy_s(m_szScriptPath, strScript.c_str());
+	_tcscpy_s(m_szLogPath, strLogPath.c_str());
+	_tcscpy_s(m_szExtraSentence, strScriptCmd.c_str());
+
+	return true;
+}
+
+bool CPythonExecutor::StartScript()
+{
+	if (StartThread()) {
+		Sleep(100);
+		return true;
+	}
+
+	// 异常类型;
+	printf("Error:异常类型脚本\n");
+
+	return false;
+}
+
+void CPythonExecutor::StopScript()
+{
+	// 如果是子进程运行脚本,停止线程时kill进程;
+	EndSubprocess();
+	// 结束线程;
+	EndWorkThread();
+}
+
+bool CPythonExecutor::IsScriptOver()
+{
+	if ( WaitForSingleObject(m_hStdoutLogThread, 0) == WAIT_OBJECT_0  && WaitForSingleObject(m_hWorkThread, 0) == WAIT_OBJECT_0)
+	{
+		GLOBAL::WriteTextLog(_T("====>脚本(%s)已完成"), m_pCaseObj->strCaseName.c_str());
+		return true;
+	}
+
+	return false;
+}

+ 95 - 0
SATService/SATService/PythonExecutor.h

@@ -0,0 +1,95 @@
+#ifndef __SCRIPT_EXECUTOR__
+#define __SCRIPT_EXECUTOR__
+
+#pragma once
+
+#define BUFSIZE 4096
+
+class CPythonExecutor
+{
+public:
+	CPythonExecutor(void);
+	~CPythonExecutor(void);
+
+protected:
+	SATHTTP::STCase *m_pCaseObj;						// 用例对象;
+	HANDLE			m_hWorkThread;						// 事件句柄;
+	HANDLE			m_hStdoutLogThread;					// 事件句柄;
+	BOOL			m_bStatus;							// 线程状态;
+	TCHAR			m_szScriptPath[MAX_PATH];			// 脚本路径;
+	TCHAR			m_szLogPath[MAX_PATH];				// 日志路径;	
+	TCHAR			m_szExtraSentence[MAX_PATH];		// 脚本命令行参数或额外要执行的语句;
+	// 重定向标准输出和标准错误;
+	HANDLE			m_hStdOutRead;
+	HANDLE			m_hStdOutWrite;
+	HANDLE			m_hStdErrorRead;
+	HANDLE			m_hStdErrorWrite;
+	// 子进程信息;
+	STARTUPINFO			m_si;
+	PROCESS_INFORMATION	m_pi;
+	// 进程结束标志;
+	BOOL			m_bKillProcess;
+	// 停止日志导出线程;
+	BOOL			m_bStopLogExport;
+	// 执行器执行时间;
+	CTime			m_tStartTime;
+	ULONGLONG		m_ulStartTickCount;
+	// 执行器结束时间;
+	CTime			m_tEndTime;
+	// 子进程ID;
+	DWORD			m_dwSubprocessId;
+	// 线程ID;
+	DWORD			m_dwThreadId;
+protected:
+	// 工作者线程函数;
+	static DWORD WINAPI _WorkerThread(LPVOID lpParam);
+	// 日志导出线程;
+	static DWORD WINAPI _StdoutLogExportThread(LPVOID lpParam);
+	static DWORD WINAPI _StderrLogExportThread(LPVOID lpParam);
+
+	// 重定子进程向标准输出;
+	int RedirectSubprocessStdout(LPSTARTUPINFO si = NULL);	
+
+	// 运行脚本进程;
+	int RunScriptProcess();
+	// 在服务进程中创建脚本进程;
+	int ServiceRunScriptProcess();
+	// 开启线程;
+	bool StartThread();
+	// 结束线程;
+	void EndWorkThread();
+	void EndLogThread();
+	void EndThread();
+	// 从管道读取日志;
+	void ReadFromPipe();
+	// 结束重定向句柄,恢复默认;
+	void EndSubprocessStdOut();
+	// 获取当前活动的会话ID;
+	DWORD GetActiveSessionID();
+	// 在服务进程下创建子进程;
+	BOOL ServiceExecute(std::wstring wstrCmdLine, INT32& n32ExitResult);
+public:
+	// 初始脚本运行参数;
+	bool InitScript(
+		std::string strScript, 
+		std::string strLogPath, 
+		std::string strScriptCmd);
+	// 运行脚本;
+	bool StartScript();
+	// 停止脚本;
+	void StopScript();
+	// 脚本是否结束;
+	bool IsScriptOver();
+	// 获取脚本Id;
+	DWORD GetScriptId() const {
+		return m_dwSubprocessId;
+	}
+	// 设置用例对象;
+	void SetCaseObje(SATHTTP::STCase *pObj){ m_pCaseObj = pObj;}
+	// 结束子进程;
+	BOOL EndSubprocess();
+	// 获取活动的CPU时间;
+	ULONGLONG GetActiveTickCount() const { return m_ulStartTickCount;}
+};
+
+#endif // __SCRIPT_EXECUTOR__

+ 6 - 6
SATService/SATService/SATExecutor.cpp

@@ -1,6 +1,6 @@
 #include "StdAfx.h"
 #include "SATExecutor.h"
-#include "ScriptExecutor.h"
+#include "PythonExecutor.h"
 #include "CharEncoding.h"
 
 CSATExecutor::CSATExecutor(void)
@@ -104,12 +104,12 @@ SATHTTP::STCase* CSATExecutor::ExecuteFreeCaseScript(SATHTTP::STTask* pTask)
 	SATHTTP::STCase* pCase = GetFreeCaseScript(pTask->Job.vtCases);
 	if (pCase) {		
 		if ( !pCase->_pExcutor ) {
-			CScriptExecutor *pExcutor = new CScriptExecutor(); 
+			CPythonExecutor *pExcutor = new CPythonExecutor(); 
 			if ( pExcutor ) {
 				pCase->_pExcutor = pExcutor;
 				// 用例的日志文件路径;
 				pCase->_strCaseLog = pCase->_strFileDir + "\\" + pCase->_strFileName + ".txt";
-				if ( pExcutor->InitScript(pCase->_strScriptPath, pCase->_strCaseLog, "", SUBPROCESS) ) {
+				if ( pExcutor->InitScript(pCase->_strScriptPath, pCase->_strCaseLog, "") ) {
 					// 设置用例对象;
 					pExcutor->SetCaseObje(pCase);
 					pExcutor->StartScript();
@@ -998,7 +998,7 @@ DWORD CSATExecutor::ExecuteScriptThread(LPVOID lpVoid)
 				// 是否有脚本用例在执行;
 				SATHTTP::STCase* pCase = that->IsCaseScriptProcess(pTask->Job.vtCases);
 				if ( pCase ) {
-					CScriptExecutor *pExcutor = (CScriptExecutor*)pCase->_pExcutor;
+					CPythonExecutor *pExcutor = (CPythonExecutor*)pCase->_pExcutor;
 					if ( pExcutor ) {
 						if ( pExcutor->IsScriptOver() ) {
 							pCase->_nExecutionState = 2;
@@ -1043,10 +1043,10 @@ DWORD CSATExecutor::ExecuteScriptThread(LPVOID lpVoid)
 					std::vector<SATHTTP::STCase>::iterator _case = pTask->Job.vtCases.begin();
 					for ( ; _case != pTask->Job.vtCases.end(); _case++) {
 						if (!_case->_pExcutor) {					
-							CScriptExecutor *pExcutor = new CScriptExecutor();
+							CPythonExecutor *pExcutor = new CPythonExecutor();
 							if ( pExcutor ) {
 								_case->_pExcutor = pExcutor;
-								pExcutor->InitScript(_case->_strScriptPath, _case->_strFileDir + "\\" + _case->_strFileName + ".txt", "", SUBPROCESS);
+								pExcutor->InitScript(_case->_strScriptPath, _case->_strFileDir + "\\" + _case->_strFileName + ".txt", "");
 								pExcutor->StartScript();
 								// 标记用例执行中;
 								_case->_nExecutionState = 1;

+ 6 - 6
SATService/SATService/SATService.vcproj

@@ -351,27 +351,27 @@
 				>
 			</File>
 			<File
-				RelativePath=".\SATDevices.cpp"
+				RelativePath=".\PythonExecutor.cpp"
 				>
 			</File>
 			<File
-				RelativePath=".\SATDevices.h"
+				RelativePath=".\PythonExecutor.h"
 				>
 			</File>
 			<File
-				RelativePath=".\SATExecutor.cpp"
+				RelativePath=".\SATDevices.cpp"
 				>
 			</File>
 			<File
-				RelativePath=".\SATExecutor.h"
+				RelativePath=".\SATDevices.h"
 				>
 			</File>
 			<File
-				RelativePath=".\ScriptExecutor.cpp"
+				RelativePath=".\SATExecutor.cpp"
 				>
 			</File>
 			<File
-				RelativePath=".\ScriptExecutor.h"
+				RelativePath=".\SATExecutor.h"
 				>
 			</File>
 		</Filter>

+ 83 - 20
SATService/SATService/ScriptExecutor.cpp

@@ -21,11 +21,12 @@ SERVICE_STATUS_HANDLE hServiceStatus;
 CScriptExecutor::CScriptExecutor(void)
 {
 	m_hWorkThread = NULL;
-	m_hLogThread = NULL;
+	m_hStdoutLogThread = NULL;
 	m_bStatus = FALSE;
 	m_nRunType = 0;
 	m_hStdOutRead = NULL;
 	m_hStdOutWrite = NULL;
+	m_hStdErrorRead = NULL;
 	m_hStdErrorWrite = NULL;
 	m_hOldStdOutWrite = NULL;
 
@@ -60,14 +61,12 @@ DWORD CScriptExecutor::_WorkerThread(LPVOID lpParam)
 		return -1;
 	}
 
-	if ( that->m_nRunType == EMBEDDED )
-	{
+	if ( that->m_nRunType == EMBEDDED ) {
 		that->RedirectProcessStdout();
-		ResumeThread(that->m_hLogThread);
+		ResumeThread(that->m_hStdoutLogThread);
 		that->RunEmbeddedScript();
 	}
-	else if (that->m_nRunType == SUBPROCESS)
-	{
+	else if (that->m_nRunType == SUBPROCESS) {
 #ifdef _DEBUG
 		that->RunScriptProcess();
 #else
@@ -84,7 +83,7 @@ DWORD CScriptExecutor::_WorkerThread(LPVOID lpParam)
 	return 0;
 }
 
-DWORD CScriptExecutor::_LogExportThread(LPVOID lpParam)
+DWORD CScriptExecutor::_StdoutLogExportThread(LPVOID lpParam)
 {
 	CScriptExecutor* that = (CScriptExecutor*)lpParam;
 	if (!that) {
@@ -140,6 +139,62 @@ DWORD CScriptExecutor::_LogExportThread(LPVOID lpParam)
 	return 0;
 }
 
+DWORD CScriptExecutor::_StderrLogExportThread(LPVOID lpParam)
+{
+	CScriptExecutor* that = (CScriptExecutor*)lpParam;
+	if (!that) {
+		printf("Error:参数失效\n");
+		return -1;
+	}
+
+	if (that->m_nRunType == SUBPROCESS)
+	{
+		DWORD dwRead;
+		CHAR chBuf[BUFSIZE] = {0};
+		BOOL bSuccess = FALSE;
+
+		do
+		{
+			bSuccess = ReadFile(that->m_hStdOutRead, chBuf, BUFSIZE, &dwRead, NULL);
+			if (!bSuccess || dwRead == 0 || !_tcslen(chBuf)) 
+				continue;
+
+			that->m_ulStartTickCount = GetTickCount64();
+			GLOBAL::WritePythonLog(that->m_szLogPath, chBuf);
+			// 更新日志时间;
+			if ( that->m_pCaseObj )
+				that->m_pCaseObj->_ulStartTickCount = GetTickCount64();
+			memset(chBuf, 0, BUFSIZE);
+		} while (!that->m_bStopLogExport);
+	}
+	else if ( that->m_nRunType == EMBEDDED)
+	{
+		DWORD dwRead;
+		CHAR chBuf[BUFSIZE] = {0};
+		BOOL bSuccess = FALSE;
+
+		do
+		{	
+			bSuccess = ReadFile(that->m_hStdOutRead, chBuf, BUFSIZE, &dwRead, NULL);
+			if (!bSuccess || dwRead == 0 || !_tcslen(chBuf)) 
+				continue;
+
+			that->m_ulStartTickCount = GetTickCount64();
+			GLOBAL::WritePythonLog(that->m_szLogPath, chBuf);
+			// 更新日志时间;
+			if ( that->m_pCaseObj )
+				that->m_pCaseObj->_ulStartTickCount = GetTickCount64();
+			memset(chBuf, 0, BUFSIZE);
+		} while (!that->m_bStopLogExport);
+	}
+
+	// 结束重定向;
+	that->EndSubprocessStdOut();
+	GLOBAL::WriteTextLog(_T("脚本日志线程结束,用例名=%s"), that->m_pCaseObj->strCaseName.c_str());
+
+	return 0;
+}
+
 int CScriptExecutor::RedirectSubprocessStdout(LPSTARTUPINFO si /*=NULL*/)
 {
 #ifdef _DEBUG
@@ -159,11 +214,19 @@ int CScriptExecutor::RedirectSubprocessStdout(LPSTARTUPINFO si /*=NULL*/)
 			return -1;
 		}
 
+#if 0// 标准输出和标准错误都由同一个句柄重定向,此时只需要一个线程来读取管道内容;
 		// 创建stderr的管道,由于stderr一般就是stdout,直接复制句柄;
 		if (!DuplicateHandle(GetCurrentProcess(), m_hStdOutWrite, GetCurrentProcess(), &m_hStdErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
 			OutputDebugString("创建stderr管道失败\n");
 			return -2;
 		}
+#else
+		// 标准输出和标准错误分开,需要二个线程来读取管道内容;
+		if (!CreatePipe(&m_hStdErrorRead, &m_hStdErrorWrite, &sa, 0)) {
+			OutputDebugString("Error:创建stderr管道失败\n");
+			return -1;
+		}
+#endif
 
 		si->dwFlags |= STARTF_USESTDHANDLES;
 		// 将子进程的stdout输出到句柄hStdOutWrite;
@@ -423,7 +486,7 @@ int CScriptExecutor::RunScriptProcess()
 	// 重定向输出;
 	RedirectSubprocessStdout(&m_si);
 	// 恢复日志线程;
-	ResumeThread(m_hLogThread);
+	ResumeThread(m_hStdoutLogThread);
 
 	// 启动子进程;
 	if (!CreateProcess(
@@ -562,7 +625,7 @@ int CScriptExecutor::ServiceRunScriptProcess()
 	// 重定向输出;
 	RedirectSubprocessStdout(&m_si);
 	// 恢复日志线程;
-	ResumeThread(m_hLogThread);
+	ResumeThread(m_hStdoutLogThread);
 
 	// 强制stdion, stdout和stderr完全无缓冲:python -u
 	TCHAR szCommandLine[MAX_PATH] = { 0 };
@@ -633,8 +696,8 @@ bool CScriptExecutor::StartThread()
 	}
 
 	// 创建线程;
-	m_hLogThread = CreateThread(NULL, 0, _LogExportThread, this, CREATE_SUSPENDED, NULL);
-	if (!m_hLogThread) {
+	m_hStdoutLogThread = CreateThread(NULL, 0, _StdoutLogExportThread, this, CREATE_SUSPENDED, NULL);
+	if (!m_hStdoutLogThread) {
 		printf("Error:创建日志线程失败\n");
 		return false;
 	}
@@ -672,24 +735,24 @@ void CScriptExecutor::EndLogThread()
 {
 	m_bStopLogExport = TRUE;
 	// 等待3秒,是否能自主结束线程;
-	if ( m_hLogThread ) {
-		if ( WaitForSingleObject(m_hLogThread, 3000) == WAIT_OBJECT_0 ) {
-			CloseHandle(m_hLogThread);
-			m_hLogThread = NULL;
+	if ( m_hStdoutLogThread ) {
+		if ( WaitForSingleObject(m_hStdoutLogThread, 3000) == WAIT_OBJECT_0 ) {
+			CloseHandle(m_hStdoutLogThread);
+			m_hStdoutLogThread = NULL;
 			return;
 		}	
 	}
 
 	// 手动结束线程;
-	if (m_hLogThread) {
+	if (m_hStdoutLogThread) {
 		// 尝试5次结束行为;
 		for (int i = 0; i < 5; i++) {
-			if (TerminateThread(m_hLogThread, 0))
+			if (TerminateThread(m_hStdoutLogThread, 0))
 				break;
 		}
 
-		CloseHandle(m_hLogThread);
-		m_hLogThread = NULL;
+		CloseHandle(m_hStdoutLogThread);
+		m_hStdoutLogThread = NULL;
 	}
 }
 
@@ -987,7 +1050,7 @@ void CScriptExecutor::StopScript()
 
 bool CScriptExecutor::IsScriptOver()
 {
-	if ( WaitForSingleObject(m_hLogThread, 0) == WAIT_OBJECT_0  && WaitForSingleObject(m_hWorkThread, 0) == WAIT_OBJECT_0)
+	if ( WaitForSingleObject(m_hStdoutLogThread, 0) == WAIT_OBJECT_0  && WaitForSingleObject(m_hWorkThread, 0) == WAIT_OBJECT_0)
 	{
 		GLOBAL::WriteTextLog(_T("====>脚本(%s)已完成"), m_pCaseObj->strCaseName.c_str());
 		return true;

+ 4 - 2
SATService/SATService/ScriptExecutor.h

@@ -19,7 +19,7 @@ public:
 protected:
 	SATHTTP::STCase *m_pCaseObj;						// 用例对象;
 	HANDLE			m_hWorkThread;						// 事件句柄;
-	HANDLE			m_hLogThread;						// 事件句柄;
+	HANDLE			m_hStdoutLogThread;					// 事件句柄;
 	BOOL			m_bStatus;							// 线程状态;
 	TCHAR			m_szScriptPath[MAX_PATH];			// 脚本路径;
 	TCHAR			m_szLogPath[MAX_PATH];				// 日志路径;	
@@ -28,6 +28,7 @@ protected:
 	// 重定向标准输出和标准错误;
 	HANDLE			m_hStdOutRead;
 	HANDLE			m_hStdOutWrite;
+	HANDLE			m_hStdErrorRead;
 	HANDLE			m_hStdErrorWrite;
 	HANDLE			m_hOldStdOutWrite;					// 用于本进程标准输出备份;
 	// 子进程信息;
@@ -52,7 +53,8 @@ protected:
 	// 工作者线程函数;
 	static DWORD WINAPI _WorkerThread(LPVOID lpParam);
 	// 日志导出线程;
-	static DWORD WINAPI _LogExportThread(LPVOID lpParam);
+	static DWORD WINAPI _StdoutLogExportThread(LPVOID lpParam);
+	static DWORD WINAPI _StderrLogExportThread(LPVOID lpParam);
 	// 重定向本进程标准输出;
 	int RedirectProcessStdout();
 	// 重定子进程向标准输出;