65.9K
CodeProject 正在变化。 阅读更多。
Home

为 Windows Phone 8 编译 Python

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (8投票s)

2012 年 12 月 24 日

CPOL

3分钟阅读

viewsIcon

79127

downloadIcon

823

本文讨论了如何在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 的新函数替换此函数,该函数返回 PYTHONPATHPYTHONHOME 的变量。

首先,更改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;  
} 

编译并运行,结果将打印在输出窗口中。

© . All rights reserved.