加载中

Introduction 

Windows phone 8(wp8) supports native code. Developers can write applications using c/c++ directly, and port existing achievements to windows phone. But there are many differences between APIs on wp8 and APIs on win32. These differences exist in many categories, such as native thread, synchronization method, file find, library load, etc, which brings a lot of workload for code porting.

介绍

Windows Phone 8(wp8) 支持本地编码,开发者可以直接用c或c++语言编写应用,并且有成功移植的案例.但是wp8和win32在应用程序接口方面也存在很多的不同。这些差异在许多分类中都存在,比如本地线程,同步(并行)函数,文件查找,库加载等等,这些差异为代码移植带来了很大的工作量。

Python is a dynamic script language and has many function modules. It is easy to learn and use. Python can improve the flexibility of the application and existing python module can also be used to speed up the development procedure. For python is dynamic language, Apps can uses this feature to create logic or controls some application functions dynamically. Python interpreter is written in c code. Because wp8 supports native code, we can compile python source code on wp8. Thus makes it possible to use python on wp8.

Python是一种动态脚本语言,有很多功能模块。它很容易学习和使用Python可以提高应用程序的灵活性而且现有的Python模块也可以用来加快研发过程由于Python动态语言,应用程序可以使用此功能来创建逻辑动态控制一些应用程序Python解释器由C编写由于WP8支持本机代码,我们可以在WP8上编译Python源代码使得在WP8使用Python的成为可能。

Because the limitations of APIs on wp8, changes have to be made in some source code of python to be compiled successfully. Furthermore, some APIs such as thread, windows registry are not supported, not all modules or features can be ported to wp8.

This article talks about how to compile python source code on wp8, the changes made on source code, and gives an example using python in wp8 native app.

由于WP8 API的限制python的一些源代码必须被改动使得编译成功。此外,一些APIs例如thread ,Windows的注册表并不支持不是所有的模块或功能,可以移植到WP8

本文讨论如何在WP8编译Python源代码源代码所做的改动给出了一个例子WP8本地应用程序中使用Python

Main changes in source code

thread related functions

For wp8 does not support native thread, thread related functions can not compiled on wp8. There, we remove these source code files from the project, and changes “pyconfig.h”

/* Define if you want to compile in rudimentary thread support */
#undef WITH_THREAD 
these source files are removed from the project
modules\posixmodule
modules\mmapmodule 
modules\threadmodule
Python\thread.c 
changes in 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 
changes in 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 

源代码的主要改动

线程相关函数

对于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 


because thread is not supported, os python module can not work correctly, which causes site.py can not import correctly. So site.py should not be imported by default.

In pythonrun.c

#if !defined(ENV_WP)
    if (!Py_NoSiteFlag)
        initsite(); /* Module site */
#endif 

changes getenv function

getenv function on wp8 returns no environment value, so we replace this function with a new one getenv_win8, which returns variables for PYTHONPATH and PYTHONHOME.

first, change pydebug.h

extern char *getenv_win8(char *name);
#define Py_GETENV(s) (Py_IgnoreEnvironmentFlag ? NULL : getenv_win8(s)) 
create a new source file named “Python_wp8.cpp”, add 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;
}  

note: Python_wp8.cpp file should be compiled with windows runtime.

由于不支持线程, 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”, 加入函数 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;
}  

note: Python_wp8.cpp 文件要用 windows runtime 编译.

changes loadlibraryex function

first, LoadLibraryEx can not load library with absolute path, so GetFullPathName should not be called before LoadLibraryEx function. second, LoadPackagedLibrary should be used other than LoadLibraryEx.

change in 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);
} 

修改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);
} 

changes FindFirstFile function

FindFirstFile is not supported, FindFirstFileEx should be used with unicode string. Therefore the function in import.c has to be changed.

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 

修改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 

changes other files or functions.

There are some other changes in source code, please check the source code.

The full source change with wp8 project can be download from http://code.google.com/p/c-python-for-windows-phone8

example of using python in native apps on wp8

1. create project

open vs2012, create a native project for windows phone 8, as follow:

2. Add python27.dll to the project, set it’s property “content” to Yes.

3. set include directories and library search path

4. Add python27.lib

5. Create pytest.py, and add it to the project, set it’s property content to yes.

def add(a,b):  
    return a + b

The python code to be tested is simple, It counts the sum of two number.

6. Create c++ code to call 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;  
} 
compile and run, the result will be printed in the output window.

修改其他文件或函数.

有一些另外的修改请查看源代码.

wp8工程的全部源代码修改能从这下载 http://code.google.com/p/c-python-for-windows-phone8

WP8原生APP种使用python的例子

1. 新建工程

打开vs2012, 新建一个原生 windows phone 8工程, 例如:

2. 添加python27.dll 到这个工程, 设定它的 “content” 属性到 Yes.

3. 设定include directories 和 library search 路径

4. 添加python27.lib

5. 新建pytest.py, 并加入工程,设定它的 “content” 属性到 Yes.

def add(a,b):  
    return a + b

将被测试的python代码很简单, 计算两数相加的结果.

6. 生成c++ 代码调用python.

#include 
#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;  
} 
编译运行, 结果就会在输出窗口输出.
返回顶部
顶部