Re: How do you develop on the PocketPC?

"Luke Dunstan" <[email protected]>
Newsgroups gmane.comp.python.windows-ce
Message-ID <[email protected]>
----- Original Message ----- 
From: "Thomas Heller" <[email protected]>
To: <[email protected]>
Sent: Monday, January 09, 2006 10:42 PM
Subject: Re: [PythonCE] How do you develop on the PocketPC?


> "Luke Dunstan" <[email protected]> writes:
>
>> I'm sorry to say that I've seen the NULL bug before but have been too
>> distracted by the task at hand to fix it, but here is a patch that
>> fixes it.
>>
>> By the way, I've recently tried running the remote part of "winpdb"
>> (http://www.digitalpeers.com/pythondebugger/) on Windows CE and
>> connecting to it over TCP/IP from the GUI running on the PC, and after
>> a trivial change (to handle a lack of PATH environment variable) it
>> worked fine. Personally I would rather have the ability to communicate
>> to the remote debugger using ActiveSync rather than only TCP/IP
>> because it is more convenient especially since I only have Bluetooth
>> not WiFi, so I have been investigating using the Remote API (RAPI).
>
> In case you are talking about the scripts I posted: Although the client
> and server connect via TCP/IP, the only connection between the pockect
> PC and the desktop computer I have is the cradle with an USB cable, plus
> activesync (3.8, IIRC) running.  It seems activesync emulates internet
> connection on the pocket pc.
>
> Thomas

I have noticed that IE works on the PDA when cradled, but I know that 
ActiveSync is not providing a real network adapter on the PC side because an 
extra IP does not appear in the output of 'ipconfig'. This prevents running 
TCP listen servers on the PDA but it is true that your method works nicely 
in this case. As a demonstration of how we could make it even more 
convenient, I have modified it slightly so that the server starts the client 
script on the PDA remotely, using RAPI via ctypes. I have put the 
experimental RAPI interfacing code in a separate module "wincerapi".

We should probably consider putting all these scripts in CVS somewhere.

Luke

_______________________________________________
PythonCE mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pythonce
pyceserver.py (text/plain, 958 B)
# Thomas Heller 20060109
# Start this script on the PC, then start client.py on the Pocket PC.
import socket, sys, threading
import wincerapi

# Initialise RAPI
wincerapi.CeRapiInitTimeout()
# Start the client program on the device
DEVICE_PYTHON_EXE = '\\Program Files\\Python\\python.exe'
DEVICE_SCRIPT = '\\my documents\\python\\pyceclient.py'
wincerapi.RunProcess(DEVICE_PYTHON_EXE, '"' + DEVICE_SCRIPT + '"')

HOST = ''                 # Symbolic name meaning the local host
PORT = 20000              # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()

def get_input():
    while 1:
        data = raw_input()
        conn.send(data + '\n')

t = threading.Thread(target=get_input)
t.setDaemon(True)
t.start()

while 1:
    data = conn.recv(1024)
    if not data:
        print "SystemExit, hit return to end."
        raise SystemExit
    sys.stderr.write(data)
wincerapi.py (text/plain, 7.5 KB)
# vi:ts=4:sw=4:et

import ctypes
from ctypes.com import STDMETHOD
from ctypes.com.storage import IStream

_rapi = ctypes.windll.rapi


###########################################################
#
# Internal helpers
#

_DWORD = ctypes.c_ulong
_WCHAR = ctypes.c_wchar
_BOOL = ctypes.c_int

def _SUCCEEDED(hr):
    return hr >= 0

def _FAILED(hr):
    return hr < 0


###########################################################
#
# Error handling
#

CeRapiGetError = _rapi.CeRapiGetError
CeRapiGetError.argtypes = []
CeRapiGetError.restype = _DWORD # really a HRESULT

CeGetLastError = _rapi.CeGetLastError
CeGetLastError.argtypes = []
CeGetLastError.restype = _DWORD

class RapiError(Exception):
    """This error is raised by some functions in this module when an error occurs"""

    def __init__(self, message):
        self.rapi_errno = CeRapiGetError()
        self.ce_errno = CeGetLastError()
        if self.rapi_errno == 0:
            if self.ce_errno == 0:
                msg = message + " (No error code)"
            else:
                msg = "%s (CeGetLastError: %d)" % (message, self.ce_errno)
        else:
            msg = "%s (CeRapiGetError: %d)" % (message, self.rapi_errno)
        Exception.__init__(self, msg)


###########################################################
#
# Initialisation
#

class RAPIINIT(ctypes.Structure):
    _fields_ = [
        ("cbSize", _DWORD),
        ("heRapiInit", _DWORD),
        ("hrRapiInit", ctypes.HRESULT),
        ]

    def __init__(self):
        self.cbSize = ctypes.sizeof(self)


CeRapiInitEx = _rapi.CeRapiInitEx
CeRapiInitEx.argtypes = [ ctypes.POINTER(RAPIINIT) ]
CeRapiInitEx.restype = ctypes.HRESULT

CeRapiUninit = _rapi.CeRapiUninit
CeRapiUninit.argtypes = []
CeRapiUninit.restype = ctypes.HRESULT

def CeRapiInitTimeout(timeout_ms = 5000):
    """Initialises the RAPI connection, failing if it does not succeed within the specified timeout (milliseconds).
    Implemented by calling CeRapiInitEx, then waiting on the returned event handle with a timeout.
    Raises RapiError on failure.
    """
    ri = RAPIINIT()
    CeRapiInitEx(ctypes.byref(ri))
    # FIXME: how do you get the address of a ctypes.Structure field?
    hEvent = _DWORD(ri.heRapiInit)
    WAIT_OBJECT_0 = 0
    rwait = ctypes.windll.user32.MsgWaitForMultipleObjects(1, ctypes.byref(hEvent), 0, timeout_ms, 0)
    if rwait == WAIT_OBJECT_0:
        if _FAILED(ri.hrRapiInit):
            raise RapiError("CeRapiInitEx failed: " + str(ri.hrRapiInit))
        # else success
    else:
        # The wait timed out or failed
        ex = RapiError("Timeout waiting for CeRapiInitEx")
        CeRapiUninit()
        raise ex


###########################################################
#
# File I/O
#

INVALID_HANDLE_VALUE = 0xffffffff   # HANDLE is an unsigned long
ERROR_NO_MORE_FILES = 18
MAX_PATH = 260

class HANDLE(ctypes.c_ulong):
    def _check_HANDLE(handle):
        if handle == INVALID_HANDLE_VALUE:
            raise RapiError("RAPI call returned invalid handle")
        return handle

    _check_retval_ = staticmethod(_check_HANDLE)

class BOOLRESULT(ctypes.c_int):
    def _check_BOOLRESULT(value):
        if not value:
            raise RapiError("RAPI call returned FALSE")
        return value

    _check_retval_ = staticmethod(_check_BOOLRESULT)

class FILETIME(ctypes.Structure):
    _fields_ = [
        ("dwLowDateTime", _DWORD),
        ("dwHighDateTime", _DWORD),
        ]

class CE_FIND_DATA(ctypes.Structure):
    _fields_ = [
        ("dwFileAttributes", _DWORD),
        ("ftCreationTime", FILETIME),
        ("ftLastAccessTime", FILETIME),
        ("ftLastWriteTime", FILETIME),
        ("nFileSizeHigh", _DWORD),
        ("nFileSizeLow", _DWORD),
        ("dwOID", _DWORD),
        ("cFileName", _WCHAR * MAX_PATH),
        ]

    _as_parameter_ = property(ctypes.byref) # always passed by reference

CeFindFirstFile = _rapi.CeFindFirstFile
CeFindFirstFile.argtypes = [ ctypes.c_wchar_p, CE_FIND_DATA ]
CeFindFirstFile.restype = HANDLE

CeFindNextFile = _rapi.CeFindNextFile
CeFindNextFile.argtypes = [ HANDLE, CE_FIND_DATA ]

CeFindClose = _rapi.CeFindClose # TODO: check for errors?
CeFindClose.argtypes = [ HANDLE ]

CeCopyFile = _rapi.CeCopyFile
CeCopyFile.argtypes = [ ctypes.c_wchar_p, ctypes.c_wchar_p, _BOOL ]

CeDeleteFile = _rapi.CeDeleteFile
CeDeleteFile.argtypes = [ ctypes.c_wchar_p ]
CeDeleteFile.restype = BOOLRESULT

def listdir(path):
    """This function is meant to emulate os.listdir but lists files on the WinCE device"""
    pattern = unicode(path)
    if pattern[-1:] != u'\\':
        pattern += u'\\'
    pattern += u'*'
    lst = []
    data = CE_FIND_DATA()
    try:
        hFind = CeFindFirstFile(pattern, data)
    except RapiError, ex:
        # A "no files" error is perfectly normal
        if ex.ce_errno == ERROR_NO_MORE_FILES:
            return lst
        raise
    try:
        lst.append(data.cFileName)
        while CeFindNextFile(hFind, data):
            lst.append(data.cFileName)
    finally:
        CeFindClose(hFind)
    return lst


###########################################################
#
# Miscellaneous
#

CeCloseHandle = _rapi.CeCloseHandle # TODO: check for errors?
CeCloseHandle.argtypes = [ HANDLE ]

RAPISTREAMFLAG = ctypes.c_int   # enum
STREAM_TIMEOUT_READ = 0

class IRAPIStream(IStream):
    #_iid_ = ?
    _methods_ = IStream._methods_ + [
        STDMETHOD(ctypes.HRESULT, "SetRapiStat", RAPISTREAMFLAG, _DWORD),
        STDMETHOD(ctypes.HRESULT, "GetRapiStat", RAPISTREAMFLAG, ctypes.POINTER(_DWORD)),
    ]

CeRapiInvoke = _rapi.CeRapiInvoke
CeRapiInvoke.argtypes = [ ctypes.c_wchar_p, ctypes.c_wchar_p,
    _DWORD, ctypes.c_void_p, ctypes.POINTER(_DWORD), ctypes.POINTER(ctypes.c_void_p),
    ctypes.POINTER(ctypes.POINTER(IRAPIStream)), _DWORD ]
CeRapiInvoke.restype = ctypes.HRESULT

LocalFree = ctypes.windll.kernel32.LocalFree
LocalFree.argtypes = [ ctypes.c_void_p ]

class PROCESS_INFORMATION(ctypes.Structure):
    _fields_ = [
        ("hProcess", HANDLE),
        ("hThread", HANDLE),
        ("dwProcessId", _DWORD),
        ("dwThreadId", _DWORD),
        ]

    _as_parameter_ = property(ctypes.byref) # always passed by reference

CeCreateProcess = _rapi.CeCreateProcess
CeCreateProcess.argtypes = [ ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_void_p,
    ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p,
    PROCESS_INFORMATION ]
CeCreateProcess.restype = BOOLRESULT

def RunProcess(program, command_line, flags=0):
    if command_line:
        command_line = unicode(command_line)
    else:
        command_line = None
    pi = PROCESS_INFORMATION()
    CeCreateProcess(unicode(program), command_line,
            None, None, 0, 0, None, None, None, pi)
    CeCloseHandle(pi.hProcess)
    CeCloseHandle(pi.hThread)
    return pi.dwProcessId


###########################################################
#
# Testing
#

def test():
    ri = RAPIINIT()
    print "Calling CeRapiInitEx"
    CeRapiInitEx(ctypes.byref(ri))
    print "CeRapiInitEx started..."
    # FIXME: how do you get the address of a ctypes.Structure field?
    hEvent = _DWORD(ri.heRapiInit)
    WAIT_OBJECT_0 = 0
    rwait = ctypes.windll.user32.MsgWaitForMultipleObjects(1, ctypes.byref(hEvent), 0, 5000, 0)
    if rwait == WAIT_OBJECT_0:
        if _SUCCEEDED(ri.hrRapiInit):
            print "CeRapiInitEx finished successfully"
        else:
            print "CeRapiInitEx failed: ", ri.hrRapiInit
            return
    else:
        print "CeRapiInitEx timed out: calling CeRapiUninit"
        CeRapiUninit()
        return
    # Init success
    # Clean up
    print "Calling CeRapiUninit"
    CeRapiUninit()

if __name__ == '__main__':
    test()
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.