ソースを参照

1、完成Python嵌入C++,能正常捕获Python异常信息;
2、待完善其他。

scbc.sat2 5 年 前
コミット
24ccb442d8

+ 209 - 3
RunPython/RunPython/RunPython.cpp

@@ -32,11 +32,11 @@ int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
 	{
 		// TODO: 在此处为应用程序的行为编写代码。
 		//RunPython("E:\\bin\\ScbcCopyKey\\test.py"); // 加载失败,但改成test2.py后,就能加载成功
-		//RunPython("E:\\bin\\ScbcCopyKey\\ScbcTest.py", NULL);
-		//RunPython("D:\\SAT\\runner\\btc_runner_se\\runner\\output\\ODF_NPI_RT2841\\20191119172310094\\192.168.1.119_5555\\cases\\RT_2841\\ODF_NPI_RT2841\\picture\\22.py", NULL);
+		//RunPythonEx("E:\\bin\\ScbcCopyKey\\ScbcTest.py", NULL);
+		RunPythonEx("D:\\SAT\\runner\\btc_runner_se\\runner\\output\\ODF_NPI_RT2841\\20191119172310094\\192.168.1.119_5555\\cases\\RT_2841\\ODF_NPI_RT2841\\picture\\22.py", NULL);
 		//Sleep(10000);
 		printf("\n\n\n\n=================================================================\n\n");
-		CallPythonEx("D:\\SAT\\runner\\btc_runner_se\\runner\\output\\ODF_NPI_RT2841\\20191119172310094\\192.168.1.119_5555\\cases\\RT_2841\\ODF_NPI_RT2841\\picture\\22.py", NULL);
+		//CallPythonEx("D:\\SAT\\runner\\btc_runner_se\\runner\\output\\ODF_NPI_RT2841\\20191119172310094\\192.168.1.119_5555\\cases\\RT_2841\\ODF_NPI_RT2841\\picture\\22.py", NULL);
 		//CallPythonEx("E:\\bin\\ScbcCopyKey\\ScbcTest.py", NULL);
 		printf("\n\n\n\n=================================================================\n\n");
 	}
@@ -45,6 +45,93 @@ int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
 	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)
 {
@@ -142,6 +229,125 @@ RUNPYTHON_API int RunPython(LPCTSTR lpScriptFile, LPCTSTR lpExtraSentence)
 	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)

+ 2 - 0
RunPython/RunPython/RunPython.h

@@ -21,6 +21,8 @@ extern RUNPYTHON_API int nRunPython;
 
 // 直接C++运行Python脚本;
 RUNPYTHON_API int RunPython(LPCTSTR lpScriptFile, LPCTSTR lpExtraSentence);
+// 实现标准错误输出;
+RUNPYTHON_API int RunPythonEx(LPCTSTR lpScriptFile, LPCTSTR lpExtraSentence);
 // 调用Python.exe来运行脚本;
 RUNPYTHON_API int CallPython(LPCTSTR lpScriptFile, LPCTSTR lpCommand);
 // 调用Python.exe来运行脚本,同时重定向输出流;

+ 8 - 0
RunPython/RunPython/RunPython.vcproj

@@ -249,6 +249,10 @@
 			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
 			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
 			>
+			<File
+				RelativePath=".\PythonHelper.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\RunPython.cpp"
 				>
@@ -287,6 +291,10 @@
 			Filter="h;hpp;hxx;hm;inl;inc;xsd"
 			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
 			>
+			<File
+				RelativePath=".\PythonHelper.h"
+				>
+			</File>
 			<File
 				RelativePath=".\Resource.h"
 				>