#include "StdAfx.h" #include "ScriptExecutor.h" #include #include #include CScriptExecutor::CScriptExecutor(void) { m_hWorkThread = NULL; m_hWorkEvent = NULL; m_hLogThread = NULL; m_hLogEvent = NULL; m_bStatus = FALSE; m_nRunType = 0; m_hStdOutRead = NULL; m_hStdOutWrite = NULL; m_hStdErrorWrite = NULL; m_hOldStdOutWrite = 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_bRuned = FALSE; } CScriptExecutor::~CScriptExecutor(void) { } DWORD CScriptExecutor::_WorkerThread(LPVOID lpParam) { CScriptExecutor* that = (CScriptExecutor*)lpParam; if ( !that ) { printf("Error:参数失效\n"); return -1; } if ( that->m_nRunType == EMBEDDED ) { that->RedirectProcessStdout(); ResumeThread(that->m_hLogThread); that->RunEmbeddedScript(); } else if (that->m_nRunType == SUBPROCESS) { that->RunScripProcess(); } CloseHandle(that->m_hWorkThread); that->m_hWorkThread = NULL; // 结束日志线程; that->m_bStopLogExport = TRUE; return 0; } DWORD CScriptExecutor::_LogExportThread(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; Global::WritePythonLog(that->m_szLogPath, chBuf); 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; Global::WritePythonLog(that->m_szLogPath, chBuf); memset(chBuf, 0, BUFSIZE); } while (!that->m_bStopLogExport); } // 关闭线程句柄; CloseHandle(that->m_hLogThread); that->m_hLogThread = NULL; // 结束重定向; that->EndSubprocessStdOut(); return 0; } int CScriptExecutor::RedirectSubprocessStdout(LPSTARTUPINFO si /*=NULL*/) { #ifdef _DEBUG OutputDebugString("RedirectSubprocessStdout\n"); #endif // Python脚本中,必须使用sys.__stdout__() if (m_nRunType == SUBPROCESS) { 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; } // 创建stderr的管道,由于stderr一般就是stdout,直接复制句柄; if (!DuplicateHandle(GetCurrentProcess(), m_hStdOutWrite, GetCurrentProcess(), &m_hStdErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) { OutputDebugString("创建stderr管道失败\n"); return -2; } si->dwFlags |= STARTF_USESTDHANDLES; // 将子进程的stdout输出到句柄hStdOutWrite; si->hStdOutput = m_hStdOutWrite; // 将子进程的stderr输出到句柄hStdErrWrite; si->hStdError = m_hStdErrorWrite; return 0; } // 异常类型; OutputDebugString("Error:异常类型\n"); return -5; } int CScriptExecutor::RedirectProcessStdout() { #ifdef _DEBUG OutputDebugString("RedirectProcessStdout\n"); #endif if (m_nRunType == EMBEDDED) { // 创建stdout的管道; if (!CreatePipe(&m_hStdOutRead, &m_hStdOutWrite, NULL, 0)) { printf("Error:创建stdout管道失败\n"); return -1; } // 将 C 运行时文件描述符与现有操作系统文件句柄关联; int wpfd = _open_osfhandle((intptr_t)m_hStdOutWrite, _O_TEXT); // 为stdout分配新文件描述符; if (_dup2(wpfd, _fileno(stdout)) != 0) { printf("Error:dup2调用失败"); return -2; } _close(wpfd); // 备份进程原始标准输出; m_hOldStdOutWrite = GetStdHandle(STD_OUTPUT_HANDLE); // 设备进程标准输出,重定向到管道中; if (!SetStdHandle(STD_OUTPUT_HANDLE, m_hStdOutWrite)) { printf("Error:重定向失败\n"); return -3; } // 设置标准流不使用缓冲,即时写入; setvbuf(stdout, NULL, _IONBF, 0); return 0; } // 异常类型; printf("Error:异常类型\n"); return -5; } void CScriptExecutor::CatchPythonException() { if (!Py_IsInitialized()) { printf("Error:未初始化Python环境\n"); return; } if (!PyErr_Occurred()) { printf("Error:没有异常产生\n"); return; } // 捕获异常; const char* pszErrMsg = NULL; TCHAR* pszTraceback = NULL; PyObject* pPyType = NULL; PyObject* pPyValue = NULL; PyObject* pPyTraceback = NULL; // 非控制台,使用PyErr_Fetch捕获异常; PyErr_Fetch(&pPyType, &pPyValue, &pPyTraceback); PyErr_NormalizeException(&pPyType, &pPyValue, &pPyTraceback); // 可有可无,不影响获取异常; if (pPyValue) { PyObject* pPyStr = PyObject_Str(pPyValue); if (PyString_Check(pPyStr)) { pszErrMsg = PyString_AsString(pPyStr); if (pszErrMsg) printf("Error Info=>%s\n", pszErrMsg); if (pPyTraceback) { PyObject* pPyTraceModule = PyImport_ImportModule("traceback"); if (!pPyTraceModule) { printf("Error:导入traceback模块失败\n"); return; } #if 1 // 细分出所有换行符; PyObject* pPyModuleDict = PyModule_GetDict(pPyTraceModule); if (pPyModuleDict) { PyObject* pPyFunc = PyDict_GetItemString(pPyModuleDict, "format_exception"); if (!pPyFunc || !PyCallable_Check(pPyFunc)) { printf("Error:加载format_exception失败\n"); return; } PyObject* pErroList = PyObject_CallFunctionObjArgs(pPyFunc, pPyType, pPyValue, pPyTraceback, NULL); if (pErroList) { int nSize = PyList_Size(pErroList); for (int i = 0; i < nSize; i++) { pszErrMsg = PyString_AsString(PyList_GetItem(pErroList, i)); printf("%s", pszErrMsg); } } Py_DECREF(pErroList); } Py_XDECREF(pPyTraceModule); #else // 不细分Item; PyObject* pPyFunc = PyObject_GetAttrString(pPyTraceModule, "format_exception"); if (!pPyFunc || !PyCallable_Check(pPyFunc)) { printf("Error:加载format_exception失败\n"); return; } PyObject* pPyResult = PyObject_CallFunctionObjArgs(pPyFunc, pPyType, pPyValue, pPyTraceback, NULL); pPyStr = PyObject_Str(pPyResult); pszErrMsg = PyString_AsString(pPyStr); if (pszErrMsg) printf("%s\n", pszErrMsg); Py_DECREF(pPyResult); Py_XDECREF(pPyTraceModule); #endif } } } Py_XDECREF(pPyType); Py_XDECREF(pPyValue); Py_XDECREF(pPyTraceback); } int CScriptExecutor::RunEmbeddedScript() { // 参数有效性判断; if (!PathFileExists(m_szScriptPath)) { printf("Error:脚本文件不存在\n"); return -1; } // 初始化Python环境; if (!Py_IsInitialized()) Py_Initialize(); if (!Py_IsInitialized()) { printf("Error:初始化Python环境失败\n"); return -2; } // 解析脚本路径和脚本名称; std::string scriptdir; TCHAR szDrive[_MAX_DRIVE] = { 0 }; TCHAR szDir[_MAX_DIR] = { 0 }; TCHAR szExt[_MAX_EXT] = { 0 }; TCHAR szFilename[_MAX_FNAME] = { 0 }; TCHAR szScriptDir[MAX_PATH] = { 0 }; _tsplitpath_s(m_szScriptPath, szDrive, szDir, szFilename, szExt); _stprintf_s(szScriptDir, _T("%s%s"), szDrive, szDir); // 缓存一份路径; scriptdir = szScriptDir; // 将'\'转换成'/', Python才设置运行目录成功; int len = _tcslen(szScriptDir); for (int i = 0; i < len; i++) { if (szScriptDir[i] == '\\') szScriptDir[i] = '/'; } //szScriptDir[len-1] = '\0'; TCHAR szExecuteDir[MAX_PATH] = { 0 }; _stprintf_s(szExecuteDir, _T("sys.path.append(\"%s\")"), szScriptDir); // 添加系统模块,并指定当前脚本路径为运行路径; PyRun_SimpleString("import sys"); PyRun_SimpleString(szExecuteDir); // 运行额外的语句,一般用于传递命令行参数; if (_tcslen(m_szExtraSentence)) PyRun_SimpleString(m_szExtraSentence); // 注意:脚本名称尽量不要与系统目录其他py文件相同; // 导入脚本,以脚本名称为模块名加载; PyObject* pModuleObj = PyImport_ImportModule(szFilename); if (!pModuleObj) { printf("Error:加载脚本模块失败\n"); Py_Finalize(); return -3; } // 获取脚本的主函数名称(规定所有脚本的主函数名为main) // 加载函数:注意,如果目录下有同名的脚本文件,可能会加载失败; PyObject* pFunction = PyObject_GetAttrString(pModuleObj, "main"); if (!pFunction || !PyCallable_Check(pFunction)) { printf("Error:加载函数失败\n"); Py_Finalize(); return -4; } m_bRuned = TRUE; // 执行main函数; PyObject* pResult = PyObject_CallObject(pFunction, NULL); if (!pResult) { printf("Error:执行函数失败\n"); CatchPythonException(); Py_Finalize(); return -5; } Py_DECREF(pResult); Py_Finalize(); return 0; } int CScriptExecutor::RunScripProcess() { 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_hLogThread); // 启动子进程; 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 ) { printf("Error:创建子进程失败 (%d).\n", GetLastError()); return -3; } m_bRuned = TRUE; m_dwSubprocessId = m_pi.dwProcessId; // 等待进程完成退出. WaitForSingleObject(m_pi.hProcess, INFINITE); Sleep(5000); // 结束日志线程; m_bStopLogExport = TRUE; // 关闭进程句柄. 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; } bool CScriptExecutor::StartWorkThread() { // 创建线程; m_hWorkThread = CreateThread(NULL, 0, _WorkerThread, this, 0, &m_dwThreadId); if (!m_hWorkThread) { printf("Error:创建线程失败\n"); return false; } return true; } bool CScriptExecutor::StartLogThread() { printf("StartLogThread\n"); // 创建线程事件:手动控制,无信号状态; m_hLogEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!m_hLogEvent) { printf("Error:无法为线程创建控制事件\n"); return false; } // 创建线程; m_hLogThread = CreateThread(NULL, 0, _LogExportThread, this, CREATE_SUSPENDED, NULL); if (!m_hLogThread) { printf("Error:创建线程失败\n"); CloseHandle(m_hLogEvent); m_hLogEvent = NULL; return false; } return true; } void CScriptExecutor::EndWorkThread() { // 等待线程结束; if (m_hWorkThread) { // 尝试5次结束行为; for (int i = 0; i < 5; i++) { if (TerminateThread(m_hWorkThread, 0)) break; } CloseHandle(m_hWorkThread); m_hWorkThread = NULL; } } void CScriptExecutor::EndLogThread() { // 设置事件有信号; if (m_hLogEvent) SetEvent(m_hLogEvent); // 等待线程结束; if (m_hLogThread) { WaitForSingleObject(m_hLogThread, INFINITE); CloseHandle(m_hLogThread); m_hLogThread = NULL; } // 关闭句柄; if (m_hLogEvent) CloseHandle(m_hLogEvent); m_hLogEvent = NULL; } void CScriptExecutor::EndThread(HANDLE hThread, HANDLE hEvent) { // 设置事件有信号; if (hEvent) SetEvent(hEvent); // 等待线程结束; if (hThread) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); hThread = NULL; } // 关闭句柄; if (hEvent) CloseHandle(hEvent); hEvent = NULL; } BOOL CScriptExecutor::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 CScriptExecutor::EndProcessStdOut() { OutputDebugString("--------------EndProcessStdOut----------------\n"); #if 0 // 恢复失败,代码有问题 // 恢复标准输出(嵌入进程脚本); if ( m_nRunType == EMBEDDED && m_hOldStdOutWrite) { int fn = _fileno(stdout); // 恢复进程默认标准输出; int fd = _open_osfhandle((intptr_t)m_hOldStdOutWrite, _O_RDWR); if ( _dup2(fd, _fileno(stdout)) == 0) { _close(fd); // 设置标准输出; SetStdHandle(STD_OUTPUT_HANDLE, m_hOldStdOutWrite); m_hOldStdOutWrite = NULL; // 设置标准流不使用缓冲,即时写入; setvbuf(stdout, NULL, _IONBF, 0); } else { printf("Error:_dup2分配文件描述符失败\n"); } } #endif // 关闭重定向句柄; 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; } void CScriptExecutor::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; } void CScriptExecutor::InitScript(std::string strScript, std::string strLogPath, std::string strScriptCmd, int nRunType /*= PY_RUN_TYPE::EMBEDDED*/) { // 判断脚本是否存在; if (!PathFileExists(strScript.c_str())) { printf("Error:脚本文件不存在\n"); return; } // 判断日志文件路径是否可创建; if (!PathFileExists(strLogPath.c_str())) { // 创建路径; if (!Global::MKDIR(strLogPath.c_str())) { printf("Error:创建目录失败\n"); return; } // 创建文件; std::ofstream flog(strLogPath.c_str()); if ( flog.bad() ) { printf("Error:创建文件失败\n"); return; } flog.close(); } // 赋值参数; m_bRuned = FALSE; m_nRunType = nRunType; 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()); } bool CScriptExecutor::StartScript() { if (StartWorkThread()) { // 开启日志导出线程; if (!StartLogThread()) { printf("Error:日志导出线程创建出错\n"); } Sleep(100); return true; } // 异常类型; printf("Error:异常类型脚本\n"); return false; } void CScriptExecutor::StopScript() { // 如果是子进程运行脚本,停止线程时kill进程; if (m_nRunType == SUBPROCESS) EndSubprocess(); // 结束线程; EndWorkThread(); } bool CScriptExecutor::IsScriptOver() { int i = 10; while (!m_bRuned) { Sleep(300); if ( --i == 0 ) break; } if (m_nRunType == EMBEDDED) { if ( m_hWorkThread == NULL ) return true; } else if (m_nRunType == SUBPROCESS) { if ( m_pi.hProcess == NULL && m_hWorkThread == NULL ) return true; } return false; }