C++嵌入Python:未安装Python时崩溃
我在Windows上开发,找了很多地方都没有人讨论过这种情况。
我在桌面上做了一个C++应用程序,里面嵌入了Python 3.1,使用的是MSVC。我链接了python31.lib,并把python31.dll放在应用程序的运行文件夹里,和可执行文件放在一起。运行得很好。我的扩展和嵌入代码确实有效,没有崩溃。
我把运行文件夹发给了一个没有安装Python的朋友,结果他在脚本设置阶段崩溃了。
几个小时前,我在一台安装了Python 2.6 的笔记本上试了这个应用,结果和我朋友的情况一样崩溃了。通过调试我发现是Py_Initialize()这个调用失败了。
我在笔记本上安装了Python 3.1,没有改动应用代码。运行后它完美运行。我卸载了Python 3.1,应用又崩溃了。我在应用中加入了代码,动态链接本地的python31.dll,以确保它在使用这个文件,但崩溃问题依然存在。
我不知道解释器启动时是否需要除了DLL以外的其他东西。我找不到相关的资料。Python的文档和其他指南似乎从来没有提到过如何分发使用Python嵌入的C/C++应用,而不让用户在本地安装Python。我知道在Windows上这个问题比在Unix上更常见,但我见过一些Windows的C/C++应用在本地嵌入Python,我不太确定他们是怎么做到的。
除了DLL,我还需要什么?为什么安装Python时可以运行,卸载后就不行了?这听起来应该很简单,也许这就是为什么没人讨论这个问题。不过,我真的不知道该怎么解决这个崩溃的问题。
非常感谢你的帮助。
4 个回答
我想给那些可能还在遇到麻烦的人提供一些额外的信息,因为我之前也遇到过类似的问题。最后,我按照用户sambha提议的方法让我的应用程序正常工作,具体是:
Program Files (x86)\ MyApplicationFolder\ MyApplication.exe python27.dll Python27\ DLLs\ (contents of DLLs folder) Lib\ (contents of Lib folder)
...不过有一个重要的补充:我还需要安装MSVCR90.DLL。我使用的是Python 2.7,而python27.dll显然需要MSVCR90.DLL(可能还需要其他MSVC*90.DLL文件)。
我通过下载、安装并运行来自http://www.microsoft.com/en-us/download/details.aspx?id=29的'vcredist_x86.exe'包来解决这个问题。我觉得,虽然我不太确定,但在Win7系统上你至少需要这样做,而不是像以前那样简单地把MSVC*90.DLL文件放在你的.exe文件旁边。因为微软的安装程序会以一种特殊的方式在Win7下放置和注册这些文件。
我也尝试过.zip文件的方法,但即使安装了MSVCR90.DLL,这个方法也没有成功。
很好。如果你不想打包,可以把 Python26\DLLs 和 Python26\lib 这两个文件夹复制到你的 exe 文件所在的目录,像这样:
.\myexe.exe
.\python26.dll
.\Python26\DLLs
.\Python26\lib
然后用 Py_SetPythonHome() 这个函数来设置 PYTHONHOME。显然,这个函数在 Py_Initialize() 之前是不能调用的。
下面的方法在我的 Windows 系统上有效(Python 没有 安装):
#include "stdafx.h"
#include <iostream>
#include "Python.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
char pySearchPath[] = "Python26";
Py_SetPythonHome(pySearchPath);
Py_Initialize();
PyRun_SimpleString("from time import time,ctime\n"
"print 'Today is',ctime(time())\n");
//cerr << Py_GetPath() << endl;
Py_Finalize();
return 0;
}
很高兴搜索路径是相对于 exe 文件的。你可以用 Py_GetPath 来查看它在寻找模块时会去哪里。
除了pythonxy.dll,你还需要整个Python库,也就是lib文件夹里的所有内容,还有扩展模块,也就是DLLs文件夹里的内容。如果没有标准库,Python根本无法启动,因为它会试图找到os.py(在3.x版本中)或者string.py(在2.x版本中)。在启动时,它会导入一些模块,特别是site.py。
Python会在多个地方寻找标准库;在你的情况下,它最终会在注册表中找到它。启动时,它会使用可执行文件的名称(通过Py_SetProgramName设置)来尝试找到标准库的位置;它还会检查是否有一个名为python31.zip的文件,这个文件应该是标准库的压缩副本。同时,它也会检查一个环境变量PYTHONHOME。
你可以自由地去掉那些你不需要的库;有很多工具可以静态计算依赖关系(特别是modulefinder)。
如果你想减少文件的数量,你可以:
- 将所有扩展模块静态链接到你的pythonxy.dll中,甚至可以将pythonxy.dll静态链接到你的应用程序中。
- 使用freeze工具;这个工具可以将标准库的字节码链接到你的pythonxy.dll中。
- (作为第2点的替代)使用pythonxy.zip作为标准库。