Browse Source

完成进程自身重定向printf功能(包含嵌入的Python脚本的print,以及自身的printf和cout)
关键代码段(setvbuf也很重要,不缓冲解决了由于缓冲导致的不及时输出问题):
// 创建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调用失败");
}
_close(wpfd);

if (!SetStdHandle(STD_OUTPUT_HANDLE, m_hStdOutWrite))
{
printf("Error:重定向失败\n");
}

// 设置标准流不使用缓冲,即时写入;
setvbuf(stdout, NULL, _IONBF, 0);

scbc.sat2 5 years ago
parent
commit
5d4fbae3dc
2 changed files with 29 additions and 526 deletions
  1. 1 524
      RunPython/RunPython/RunPython.cpp
  2. 28 2
      RunPython/RunPython/ScriptExecutor.cpp

+ 1 - 524
RunPython/RunPython/RunPython.cpp

@@ -45,529 +45,6 @@ int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
 	}
 
 	system("pause");
-	return nRetCode;
-}
-
-void CatchPythonException()
-{
-	if ( !Py_IsInitialized() )
-	{
-		printf("未初始化Python环境\n");
-		return;
-	}
-
-	if ( !PyErr_Occurred() )
-	{
-		printf("没有异常产生\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("导入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("加载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_XDECREF(pPyTraceModule);
-#else // 不细分Item;
-				PyObject *pPyFunc = PyObject_GetAttrString(pPyTraceModule, "format_exception");
-				if ( !pPyFunc || !PyCallable_Check(pPyFunc) )
-				{
-					printf("加载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
-			}
-		}
-	}
-}
-
-// 从注册表获取 Python27路径;
-BOOL Python27Dir(LPTSTR lpPython27Dir, int nBufferLen)
-{
-	HKEY hKey;
-	int ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\WOW6432Node\\Python\\PythonCore\\2.7\\InstallPath"), 0, KEY_QUERY_VALUE, &hKey);
-	if (ret != ERROR_SUCCESS)
-		return FALSE;
-
-	//读取KEY
-	DWORD dwType = REG_SZ; //数据类型
-	DWORD cbData = nBufferLen;
-	ret = RegQueryValueEx(hKey, _T(""), NULL, &dwType, (LPBYTE)lpPython27Dir, &cbData);
-	if (ret != ERROR_SUCCESS)
-	{
-		RegCloseKey(hKey);
-		return FALSE;
-	}
-	RegCloseKey(hKey);
-
-	return TRUE;
-}
-
-// 运行Python脚本;
-RUNPYTHON_API int RunPython(LPCTSTR lpScriptFile, LPCTSTR lpExtraSentence)
-{
-	// 参数有效性判断;
-	if ( !lpScriptFile || !PathFileExists(lpScriptFile) )
-		return -1;
-
-	// 初始化Python环境;
-	Py_Initialize();
-	if ( !Py_IsInitialized() )
-		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(lpScriptFile, 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 ( lpExtraSentence )
-		PyRun_SimpleString(lpExtraSentence);
-
-	// 注意:脚本名称尽量不要与系统目录其他py文件相同;
-	// 导入脚本,以脚本名称为模块名加载;
-	PyObject *pModuleObj = PyImport_ImportModule(szFilename);
-	if ( !pModuleObj )
-	{
-		printf("=>加载模块失败\n");
-		Py_Finalize();
-		return -3;
-	}
-	
-	// 获取脚本的主函数名称(规定所有脚本的主函数名为main)
-	// 加载函数:注意,如果目录下有同名的脚本文件,可能会加载失败;
-	PyObject *pFunction = PyObject_GetAttrString(pModuleObj, "main");
-	if ( !pFunction || !PyCallable_Check(pFunction) )
-	{
-		printf("=>加载函数失败\n");
-		Py_Finalize();
-		return -4;
-	}
-	
-	// 执行main函数;
-	PyObject *pResult = PyObject_CallObject(pFunction, NULL);
-	if ( !pResult )
-	{
-		printf("=>运行函数失败\n");
-		Py_Finalize();
-		return -5;
-	}
-
-	Py_Finalize();
-
-	return 0;
-}
-
-RUNPYTHON_API int RunPythonEx(LPCTSTR lpScriptFile, LPCTSTR lpExtraSentence)
-{
-	// 参数有效性判断;
-	if ( !lpScriptFile || !PathFileExists(lpScriptFile) )
-		return -1;
-
-	// 初始化Python环境;
-	Py_Initialize();
-	if ( !Py_IsInitialized() )
-		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(lpScriptFile, 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 ( lpExtraSentence )
-		PyRun_SimpleString(lpExtraSentence);
-
-	/*
-	PyObject* main = PyModule_GetDict(PyImport_AddModule("__main__"));
-	PyObject *res = PyRun_String(由文件内容, Py_file_input, main, main);
-	*/
-	// 注意:脚本名称尽量不要与系统目录其他py文件相同;
-	// 导入脚本,以脚本名称为模块名加载;
-	PyObject *pModuleObj = PyImport_ImportModule(szFilename);
-	if ( !pModuleObj )
-	{
-		printf("=>加载模块失败\n");
-		Py_Finalize();
-		return -3;
-	}
-
-	// 获取脚本的主函数名称(规定所有脚本的主函数名为main)
-	// 加载函数:注意,如果目录下有同名的脚本文件,可能会加载失败;
-	PyObject *pFunction = PyObject_GetAttrString(pModuleObj, "main");
-	if ( !pFunction || !PyCallable_Check(pFunction) )
-	{
-		printf("=>加载函数失败\n");
-		Py_Finalize();
-		return -4;
-	}
-
-	// 执行main函数;
-	//PyObject *pResult = PyObject_CallFunctionObjArgs(pFunction, pType, pValue, pTraceback, NULL);
-	PyObject *pResult = PyObject_CallObject(pFunction, NULL);
-	if ( !pResult )
-	{
-		printf("=>运行函数失败\n");
-#if 0
-		// 捕获异常;
-		const char *pszErrMsg = NULL;
-		TCHAR *pszTraceback = NULL;
-		
-		PyObject *pType = NULL;
-		PyObject *pValue = NULL;
-		PyObject *pTraceback = NULL;
-		// 非控制台,使用PyErr_Fetch捕获异常;
-		PyErr_Fetch(&pType, &pValue, &pTraceback);
-		PyErr_NormalizeException(&pType,&pValue,&pTraceback);
-		if ( pValue )
-		{
-			PyObject *pStr = PyObject_Str(pValue);
-			if ( pStr )
-			{
-				//pszErrMsg = PyUnicode_AsUTF8String(pStr);
-
-				int line, offset;
-				TCHAR *pszMsg, *pszFile, *pszText;
-				int res = PyArg_ParseTuple(pValue, "s(siis)", &pszMsg, &pszFile, &line, &offset, &pszText);
-				//Q_UNUSED();
-
-				PyObject *line_no = PyObject_GetAttrString(pValue, "lineno");
-				PyObject *line_no_str = PyObject_Str(line_no);
-				PyObject *line_no_unicode = PyUnicode_AsEncodedString(line_no_str, "utf-8", "Error");
-
-				char *actual_line_no = PyBytes_AsString(line_no_unicode);
-				printf("actual_line_no --%s\n", actual_line_no);
-			}
-		}
-#else
-		CatchPythonException();
-#endif
-
-		PyErr_Print();
-		Py_Finalize();
-		return -5;
-	}
 
-	
-	Py_DECREF(pResult);
-
-	Py_Finalize();
-
-	return 0;
-}
-
-// lpScriptFile:脚本文件;
-// lpCommand:命令行参数;
-RUNPYTHON_API int CallPython(LPCTSTR lpScriptFile, LPCTSTR lpCommand)
-{
-	if ( !lpScriptFile || !PathFileExists(lpScriptFile) )
-	{
-		printf("参数无效\n");
-		return -1;
-	}
-
-	// 获取Python27进程目录;
-	TCHAR szPython27Dir[MAX_PATH] = {0};
-	TCHAR szPython27Exe[MAX_PATH] = {0};
-	if ( !Python27Dir(szPython27Dir, MAX_PATH) )
-	{
-		printf("获取Python27目录失败\n");
-		return -2;
-	}
-	_stprintf_s(szPython27Exe, _T("%spython.exe"), szPython27Dir);
-
-	STARTUPINFO si;
-	::memset(&si, 0 ,sizeof(si));
-	si.cb = sizeof(si);
-
-	PROCESS_INFORMATION pi;
-	::memset(&pi, 0 ,sizeof(pi));
-
-	TCHAR szCommandLine[MAX_PATH] = {0};
-	if ( lpCommand )
-		_stprintf_s(szCommandLine, _T("python -W ignore %s %s"), lpScriptFile, lpCommand);
-	else
-		_stprintf_s(szCommandLine, _T("python -W ignore %s"), lpScriptFile);
-
-	// 启动子进程;
-	if( !CreateProcess( 
-		NULL,			// No module name (use command line)
-		szCommandLine,			// Command line
-		NULL,					// Process handle not inheritable
-		NULL,					// Thread handle not inheritable
-		FALSE,					// Set handle inheritance to TRUE
-		0,						// No creation flags
-		NULL,					// Use parent's environment block
-		NULL,					// Use parent's starting directory 
-		&si,					// Pointer to STARTUPINFO structure
-		&pi )					// Pointer to PROCESS_INFORMATION structure
-		) 
-	{
-		printf( "CreateProcess failed (%d).\n", GetLastError() );
-		return -3;
-	}
-
-	// Wait until child process exits.
-	WaitForSingleObject( pi.hProcess, INFINITE );
-
-	// Close process and thread handles. 
-	CloseHandle( pi.hProcess );
-	CloseHandle( pi.hThread );
-
-	return 0;
-}
-
-// 读出stdout  
-BOOL ReadFromPipe(HANDLE hStdOutRead)  
-{  
-	char out_buffer[BUFSIZE] = {0};  
-	DWORD dwRead;    
-	BOOL bSuccess = FALSE;  
-
-	//用WriteFile,从hStdOutRead读出子进程stdout输出的数据,数据结果在out_buffer中,长度为dwRead  
-	bSuccess = ReadFile( hStdOutRead, out_buffer, BUFSIZE, &dwRead, NULL);  
-	if ((bSuccess) && (dwRead!=0))  //如果成功了,且长度>0  
-	{  
-		// 此处加入你自己的代码  
-		// 比如:将数据写入文件或显示到窗口中  
-		OutputDebugString(out_buffer);
-	}  
-	return bSuccess;  
-}  
-
-bool GetOutput( HANDLE hStdOutRead, int timeout )
-{
-	if( NULL == hStdOutRead )
-	{
-		return false;
-	}
-
-	char buffer[4096] = {0};
-	DWORD readBytes = 0;
-	while( timeout > 0 )
-	{
-		//对管道数据进行读,但不会删除管道里的数据,如果没有数据,就立即返回
-		if( FALSE == PeekNamedPipe( hStdOutRead, buffer, sizeof(buffer) - 1, &readBytes, 0, NULL ) )
-		{
-			return false;
-		}
-
-		//检测是否读到数据,如果没有数据,继续等待
-		if( 0 == readBytes )
-		{
-			::Sleep(200);
-			timeout -= 200;
-			continue;
-		}
-
-		readBytes = 0;
-		if( ::ReadFile( hStdOutRead, buffer, sizeof(buffer) - 1, &readBytes, NULL) )
-		{
-			OutputDebugString(buffer);
-			return true;
-		}
-		else
-		{
-			return false;
-		}
-	}
-
-	return false;
+	return nRetCode;
 }
-
-
-// lpScriptFile:脚本文件;
-// lpCommand:命令行参数;
-RUNPYTHON_API int CallPythonEx(LPCTSTR lpScriptFile, LPCTSTR lpCommand)
-{
-	if ( !lpScriptFile || !PathFileExists(lpScriptFile) )
-	{
-		printf("参数无效\n");
-		return -1;
-	}
-
-	// 获取Python27进程目录;
-	TCHAR szPython27Dir[MAX_PATH] = {0};
-	TCHAR szPython27Exe[MAX_PATH] = {0};
-	if ( !Python27Dir(szPython27Dir, MAX_PATH) )
-	{
-		printf("获取Python27目录失败\n");
-		return -2;
-	}
-	_stprintf_s(szPython27Exe, _T("%spython.exe"), szPython27Dir);
-
-	STARTUPINFO si;
-	::memset(&si, 0 ,sizeof(si));
-	si.cb = sizeof(si);
-	GetStartupInfo(&si); 
-
-	PROCESS_INFORMATION pi;
-	::memset(&pi, 0 ,sizeof(pi));
-
-	TCHAR szCommandLine[MAX_PATH] = {0};
-	if ( lpCommand )
-		_stprintf_s(szCommandLine, _T("python -W ignore %s %s"), lpScriptFile, lpCommand);
-	else
-		_stprintf_s(szCommandLine, _T("python -W ignore %s"), lpScriptFile);
-
-	SECURITY_ATTRIBUTES   sa;   
-	sa.bInheritHandle = TRUE;   
-	sa.lpSecurityDescriptor = NULL;   
-	sa.nLength = sizeof(sa);
-
-	// 创建stdout的管道;
-	HANDLE  hStdOutRead, hStdOutWrite;     
-	if ( !CreatePipe(&hStdOutRead, &hStdOutWrite, &sa, 0) )
-	{
-		printf("创建stdout管道失败\n");
-		return -3;
-	}
-
-	// 创建stderr的管道,由于stderr一般就是stdout,直接复制句柄;
-	HANDLE  hStdErrWrite;     
-	if ( !DuplicateHandle(GetCurrentProcess(), hStdOutWrite, GetCurrentProcess(), &hStdErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS) )
-	{
-		printf("创建stderr管道失败\n");
-		return -3;
-	}
-
-	si.dwFlags |= STARTF_USESTDHANDLES;  
-	// 将子进程的stdout输出到句柄hStdOutWrite;
-	si.hStdOutput = hStdOutWrite;
-	// 将子进程的stderr输出到句柄hStdErrWrite;
-	si.hStdError = hStdErrWrite;
-
-	// 启动子进程;
-	if( !CreateProcess( 
-		NULL,			// No module name (use command line)
-		szCommandLine,			// Command line
-		NULL,					// Process handle not inheritable
-		NULL,					// Thread handle not inheritable
-		TRUE,					// 如果不设置为TRUE,重定向无效;
-		0,						// No creation flags
-		NULL,					// Use parent's environment block
-		NULL,					// Use parent's starting directory 
-		&si,					// Pointer to STARTUPINFO structure
-		&pi )					// Pointer to PROCESS_INFORMATION structure
-		) 
-	{
-		printf( "CreateProcess failed (%d).\n", GetLastError() );
-		return -3;
-	}
-
-#ifdef _DEBUG
-	DWORD process_exit_code = 0;
-	while (GetExitCodeProcess(pi.hProcess, &process_exit_code))
-	{
-		// 读取stdout、stderr;
-		//GetOutput(hStdOutRead, 3000);
-		ReadFromPipe(hStdOutRead);
-		if ( process_exit_code != STILL_ACTIVE )
-			break;
-	}
-#endif
-
-	// Wait until child process exits.
-	WaitForSingleObject( pi.hProcess, INFINITE );
-
-	// Close process and thread handles. 
-	CloseHandle( pi.hProcess );
-	CloseHandle( pi.hThread );
-
-	return 0;
-}

+ 28 - 2
RunPython/RunPython/ScriptExecutor.cpp

@@ -79,7 +79,7 @@ DWORD __stdcall CScriptExecutor::_LogExportThread(LPVOID lpParam)
 		do
 		{
 			bSuccess = ReadFile(that->m_hStdOutRead, chBuf, BUFSIZE, &dwRead, NULL);
-			if (!bSuccess || dwRead == 0) 
+			if (!bSuccess || dwRead == 0 || !_tcslen(chBuf)) 
 				continue;
 
 			Global::WritePythonLog(that->m_szLogPath, chBuf);
@@ -95,7 +95,7 @@ DWORD __stdcall CScriptExecutor::_LogExportThread(LPVOID lpParam)
 		do
 		{	
 			bSuccess = ReadFile(that->m_hStdOutRead, chBuf, BUFSIZE, &dwRead, NULL);
-			if (!bSuccess || dwRead == 0) 
+			if (!bSuccess || dwRead == 0 || !_tcslen(chBuf)) 
 				continue;
 
 			Global::WritePythonLog(that->m_szLogPath, chBuf);
@@ -173,11 +173,36 @@ int CScriptExecutor::RedirectStdout(LPSTARTUPINFO si /*=NULL*/)
 			return -1;
 		}
 
+		// 将 C 运行时文件描述符与现有操作系统文件句柄关联;
 		int wpfd = _open_osfhandle((intptr_t)m_hStdOutWrite, _O_TEXT);
+#if 0
 		FILE* pOutFile = _fdopen(wpfd, "w");
 		*stdout = *pOutFile;
 		setvbuf(stdout, NULL, _IONBF, 0);
+
+		if (!SetStdHandle(STD_OUTPUT_HANDLE, m_hStdOutWrite))
+		{
+			printf("Error:重定向失败\n");
+		}
+#else
+		// 为stdout分配新文件描述符;
+		if (_dup2(wpfd, _fileno(stdout)) != 0)
+		{
+			printf("Error:dup2调用失败");
+		}
+		_close(wpfd);
+
+		if (!SetStdHandle(STD_OUTPUT_HANDLE, m_hStdOutWrite))
+		{
+			printf("Error:重定向失败\n");
+		}
+
+		// 设置标准流不使用缓冲,即时写入;
+		setvbuf(stdout, NULL, _IONBF, 0);
 #endif
+#endif
+
+		return 0;
 	}
 
 	// 异常类型;
@@ -188,6 +213,7 @@ int CScriptExecutor::RedirectStdout(LPSTARTUPINFO si /*=NULL*/)
 
 void CScriptExecutor::CatchPythonException()
 {
+	//std::cout << "fdfdfd" << endl;
 	if (!Py_IsInitialized())
 	{
 		printf("Error:未初始化Python环境\n");