为 Windows Phone 8 编译 Python
本文讨论了如何在WP8上编译Python源代码,对源代码所做的更改,以及在WP8原生应用程序中使用Python的示例。
引言
Windows Phone 8 (WP8) 支持原生代码。开发人员可以直接使用 C/C++ 编写应用程序,并将现有的成果移植到 Windows Phone。但 WP8 上的 API 与 Win32 上的 API 之间存在许多差异。这些差异存在于许多类别中,例如原生线程、同步方法、文件查找、库加载等,这为代码移植带来了大量工作。
Python 是一种动态脚本语言,拥有许多功能模块。它易于学习和使用。Python 可以提高应用程序的灵活性,并且现有的 Python 模块也可以用来加快开发过程。由于 Python 是一种动态语言,应用程序可以使用此功能来动态创建逻辑或控制某些应用程序功能。Python 解释器是用 C 编写的。因为 WP8 支持原生代码,我们可以在 WP8 上编译 Python 源代码。因此,可以在 WP8 上使用 Python。
由于 WP8 上 API 的限制,必须对 Python 的一些源代码进行更改才能成功编译。此外,某些 API(例如线程、Windows 注册表)不受支持,并非所有模块或功能都可以移植到 WP8。
本文讨论了如何在WP8上编译Python源代码,对源代码所做的更改,并给出了在WP8原生应用程序中使用Python的示例。
源代码中的主要更改
线程相关函数
由于WP8不支持原生线程,因此无法在WP8上编译线程相关函数。因此,我们从项目中删除这些源文件并更改pyconfig.h。
/* Define if you want to compile in rudimentary thread support */
#undef WITH_THREAD
这些源文件已从项目中删除
modules\posixmodule
modules\mmapmodule
modules\threadmodule
Python\thread.c
timemodule.c 中的更改
#if !defined(ENV_WP)
static PyObject *
time_sleep(PyObject *self, PyObject *args)
{
double secs;
if (!PyArg_ParseTuple(args, "d:sleep", &secs))
return NULL;
if (floatsleep(secs) != 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
#endif
#if !defined(ENV_WP)
{"sleep", time_sleep, METH_VARARGS, sleep_doc},
#endif
config.c 中的更改
#if !defined(ENV_WP)
{"nt", initnt}, /* Use the NT os functions, not posix */
#endif
#if !defined(ENV_WP)
{"mmap", initmmap},
#endif
#if !defined(ENV_WP)
{"_winreg", init_winreg},
#endif
#if !defined(ENV_WP)
{"_subprocess", init_subprocess},
#endif
由于不支持线程,OS Python模块无法正常工作,导致site.py无法正确导入。因此,默认情况下应导入site.py。
在pythonrun.c中
#if !defined(ENV_WP)
if (!Py_NoSiteFlag)
initsite(); /* Module site */
#endif
对 getenv 函数的更改
WP8 上的 getenv
函数不返回任何环境变量值,因此我们使用一个名为 getenv_win8
的新函数替换此函数,该函数返回 PYTHONPATH
和 PYTHONHOME
的变量。
首先,更改pydebug.h
extern char *getenv_win8(char *name);
#define Py_GETENV(s) (Py_IgnoreEnvironmentFlag ? NULL : getenv_win8(s))
创建一个名为 Python_wp8.cpp 的新源文件,并添加 function getenv_win8
。
bool GetInstall_Dir_win8(char *Buf,int BufSize)
{
if( BufSize == 0 || Buf == NULL )
return false;
WideCharToMultiByte( CP_ACP, 0, Windows::ApplicationModel::Package::Current->InstalledLocation ->
Path->Data(), -1, Buf, BufSize-1, NULL, NULL );
Buf[BufSize-1] = 0;
return true;
}
char *getenv_win8(char *name)
{
char Buf[1024];
static char RetBuf[1024];
GetInstall_Dir_win8(Buf,1024);
if( stricmp(name,"PYTHONPATH") == 0 ){
sprintf(RetBuf,"%s\\python\\lib",Buf);
return RetBuf;
}else if( stricmp(name,"PYTHONHOME") == 0 ){
sprintf(RetBuf,"%s\\python",Buf);
return RetBuf;
}else
return NULL;
}
注意:Python_wp8.cpp 文件应该使用 Windows 运行时进行编译。
对 loadlibraryex 函数的更改
首先,LoadLibraryEx
无法加载具有绝对路径的库,因此不应在 LoadLibraryEx
函数之前调用 GetFullPathName
。其次,应该使用 LoadPackagedLibrary
而不是 LoadLibraryEx
。
dynload_win.c 中的更改。
#if !defined(ENV_WP)
old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
if (GetFullPathName(pathname,
sizeof(pathbuf),
pathbuf,
&dummy)) {
ULONG_PTR cookie = _Py_ActivateActCtx();
/* XXX This call doesn't exist in Windows CE */
hDLL = LoadLibraryEx(pathname, NULL,
LOAD_WITH_ALTERED_SEARCH_PATH);
_Py_DeactivateActCtx(cookie);
}
/* restore old error mode settings */
SetErrorMode(old_mode);
#endif
#if defined(ENV_WP)
{
ULONG_PTR cookie = _Py_ActivateActCtx();
/* XXX This call doesn't exist in Windows CE */
MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, pathname, -1,wacModuleName, 512 );
//hDLL = (HINSTANCE)LoadPackagedLibrary(wacModuleName,0);
hDLL = (HINSTANCE)SRP_LoadPackage(wacModuleName);
_Py_DeactivateActCtx(cookie);
}
#endif
void *SRP_LoadPackage(wchar_t *wacModuleName)
{
return (void *)LoadPackagedLibrary(wacModuleName,0);
}
对 FindFirstFile 函数的更改
不支持 FindFirstFile
,应将 FindFirstFileEx
与 Unicode 字符串一起使用。因此,必须更改 import.c 中的函数。
static int
case_ok(char *buf, Py_ssize_t len, Py_ssize_t namelen, char *name)
{
/* Pick a platform-specific implementation; the sequence of #if's here should
* match the sequence just above.
*/
/* MS_WINDOWS */
#if defined(MS_WINDOWS)
#if !defined(ENV_WP)
WIN32_FIND_DATA data;
#else
wchar_t wacFileName[512] ;
char acFileName[512] ;
WIN32_FIND_DATAW data;
#endif
HANDLE h;
if (Py_GETENV("PYTHONCASEOK") != NULL)
return 1;
#if !defined(ENV_WP)
h = FindFirstFile(buf, &data);
#else
MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, buf, -1,wacFileName, 512 );
h = FindFirstFileExW(wacFileName,FindExInfoStandard,&data,FindExSearchNameMatch,NULL, 0);
#endif
if (h == INVALID_HANDLE_VALUE) {
PyErr_Format(PyExc_NameError,
"Can't find file for module %.100s\n(filename %.300s)",
name, buf);
return 0;
}
FindClose(h);
#if !defined(ENV_WP)
return strncmp(data.cFileName, name, namelen) == 0;
#else
WideCharToMultiByte( CP_ACP, 0, data.cFileName, -1, acFileName, 511, NULL, NULL );
return strncmp(acFileName, name, namelen) == 0;
#endif
…
对其他文件或函数的更改
源代码中还有一些其他更改,请检查源代码。
完整的源代码更改以及WP8项目可以从 http://code.google.com/p/c-python-for-windows-phone8 下载。
在 WP8 上的原生应用程序中使用 Python 的示例
1. 创建项目
打开 VS2012 并为 Windows Phone 8 创建一个原生项目,如下所示
2. 将 python27.dll 添加到项目,将其属性“content”设置为 Yes
3. 设置包含目录和库搜索路径
4. 添加 python27.lib
5. 创建 pytest.py,并将其添加到项目,将其属性 Content 设置为 Yes
def add(a,b):
return a + b
要测试的 Python 代码很简单。它计算两个数字的总和。
6. 创建 C++ 代码以调用 Python
#include <windows.h>
#if defined(_DEBUG)
#undef _DEBUG
#include "python.h"
#define _DEBUG
#else
#include "python.h"
#endif
void test()
{
PyObject *pName, *pModule, *pDict, *pFunc, *pArgs, *pRetVal;
Py_Initialize();
if ( !Py_IsInitialized() ){
return;
}
pName = PyString_FromString("pytest");
pModule = PyImport_Import(pName);
if ( !pModule ){
OutputDebugString(L"can't find pytest.py\n");
return;
}
pDict = PyModule_GetDict(pModule);
if ( !pDict ){
return;
}
pFunc = PyDict_GetItemString(pDict, "add");
if ( !pFunc || !PyCallable_Check(pFunc) ) {
OutputDebugString(L"can't find function [add]\n");
return;
}
pArgs = PyTuple_New(2);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",3));
PyTuple_SetItem(pArgs, 1, Py_BuildValue("l",4));
pRetVal = PyObject_CallObject(pFunc, pArgs);
char ResultBuf[512];
wchar_t ResultBufW[512];
sprintf_s(ResultBuf,512,"function return value : %ld\r\n", PyInt_AsLong(pRetVal));
MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, ResultBuf, -1,ResultBufW, 512 );
OutputDebugString(ResultBufW);
Py_DECREF(pName);
Py_DECREF(pArgs);
Py_DECREF(pModule);
Py_DECREF(pRetVal);
Py_Finalize();
return;
}
编译并运行,结果将打印在输出窗口中。