如何在C++中加载可执行文件目录下的Python模块?
问题描述
我的安装目录结构如下:
install_folder\ - app.exe - lib.dll - python\ - kitsu.py
我尝试在C应用程序中调用kitsu.py文件中的部分函数,C代码(main.cpp)如下:
TCHAR curr_path[MAX_PATH]; DWORD length = GetModuleFileName(nullptr, curr_path, MAX_PATH); PathRemoveFileSpec(curr_path, MAX_PATH); PyObject* pName, * pModule, * pDict, * pFunc, * pValue, * presult, * sys, * python_folder, * fromlist, * syspathres; // Initialize the Python Interpreter Py_Initialize(); // Build the name object pName = PyUnicode_FromString((char*)"kitsu"); python_folder = PyUnicode_FromString((char*)".python"); PyObject* py_curr_path = PyUnicode_FromString((char*)curr_path); char command[MAX_PATH]; sprintf(command, "sys.path.append(%s)", curr_path); PyRun_SimpleString("import sys"); PyRun_SimpleString(command); fromlist = PyTuple_Pack(1, python_folder); // Load the module object pModule = PyImport_ImportModuleEx((char*)"kitsu", nullptr, nullptr, fromlist); PyObject* init_kitsu = PyObject_GetAttrString(pModule, (char*)"init_kitsu"); presult = PyObject_CallObject(init_kitsu, nullptr);
(注:这是早期阶段代码,可能存在格式不规范问题)
应用程序运行时崩溃,原因是pModule为空指针,请问为何无法加载该模块?
问题分析与解决
核心原因
导致模块加载失败的关键问题有三个:
- 路径语句格式错误:
sprintf生成的sys.path.append(%s)没有给路径加引号,Python会把路径当作变量而非字符串,执行直接报错。 - 未添加目标子目录到Python路径:
kitsu.py在install_folder\python下,但你只把install_folder加入了sys.path,Python找不到存放模块的子目录。 fromlist参数滥用:PyImport_ImportModuleEx的fromlist参数用于指定要导入的子模块,你传入.python完全不符合该参数的使用逻辑,反而干扰了模块查找流程。
修复步骤
- 修正路径添加逻辑:
要么给路径字符串加上引号,确保Python能识别:
要么用Python C API的方式添加路径,避免字符串拼接的隐患:sprintf(command, "sys.path.append(r'%s')", curr_path);PyRun_SimpleString("import sys"); sys = PyImport_ImportModule("sys"); PyObject* path = PyObject_GetAttrString(sys, "path"); // 添加主目录 PyList_Append(path, py_curr_path); // 添加python子目录 PyObject* python_path = PyUnicode_FromFormat("%S\\python", py_curr_path); PyList_Append(path, python_path); // 释放资源 Py_DECREF(python_path); Py_DECREF(path); Py_DECREF(sys); - 简化模块导入:直接使用
PyImport_ImportModule导入kitsu即可,不需要错误使用PyImport_ImportModuleEx:pModule = PyImport_ImportModule("kitsu"); - 添加错误检查:每次调用Python API后检查返回值,避免空指针崩溃:
if (pModule == nullptr) { PyErr_Print(); // 打印Python原生错误信息,快速定位问题 return -1; } PyObject* init_kitsu = PyObject_GetAttrString(pModule, "init_kitsu"); if (init_kitsu == nullptr || !PyCallable_Check(init_kitsu)) { PyErr_Print(); Py_DECREF(pModule); return -1; }
额外注意事项
- 确保编译程序使用的Python版本和运行时的Python版本完全一致,否则会出现兼容性崩溃。
TCHAR是宽字符类型,直接强转char*会导致路径乱码,建议用PyUnicode_FromWideChar处理宽字符串:PyObject* py_curr_path = PyUnicode_FromWideChar(curr_path, wcslen(curr_path));
内容的提问来源于stack exchange,提问作者hi everybody
相关产品推荐
相关产品推荐

