Working with Sessions
Overview
One session wraps exactly one Windows Python process on top of Wine. The session essentially manages the process and takes care of communication between it and the “host” Unix Python process. ctypes
API calls are routed from the Unix Python process into the Windows Python process and executed there. Where applicable, memory content is being kept in sync between both processes, either automatically or via user defined directives.
zugbruecke offers two primary ways of working with sessions. If zugbruecke.ctypes
is being imported, it can be used directly as if regular ctypes
was imported instead. A default session is being created and configured automatically in the background. This also applies to importing zugbruecke.ctypes.util
. Alternatively, ctypes sessions, instances of zugbruecke.CtypesSession
, can also be created and configured on demand as needed.
Note
If you are just importing zugbruecke
with import zugbruecke
, no session is started.
Sessions are identified by a unique ID string. By default, this is a random hash value. However, it can also be set at the time of session creation. The ID is prominently shown in logging / debugging output of zugbruecke.
Sessions have a life-cycle and require termination routines to run before they can be dropped or deleted.
Note
By default, sessions terminate themselves automatically when the Unix Python interpreter quits.
Certain ctypes
functionality is bound to an individual session and its internal state.
Bound, Stable Functions and Classes
The following notable ctypes
functions and classes and their functionality are bound to individual sessions and therefore depend on their internal state:
zugbruecke.CtypesSession.CDLL()
andzugbruecke.CtypesSession.cdll()
zugbruecke.CtypesSession.WinDLL()
andzugbruecke.CtypesSession.windll()
zugbruecke.CtypesSession.CFUNCTYPE()
andzugbruecke.CtypesSession.WINFUNCTYPE()
In addition, the functions from zugbruecke.CtypesSession.util
are also bound to individual sessions:
find_library
find_msvcrt
Untested, Bound Functions and Classes
The following functions are bound to sessions and implemented, but untested and provided on a best-effort basis:
Bound Stubs and Unimplemented Functionality
zugbruecke currently does not support every ctypes feature. The following noteworthy functions and classes are only provided as stubs and raise NotImplementedError
if called:
Special APIs
Every zugbruecke session offers a number of special APIs for session management, prefixed with zb_
:
Session Lifecycle
Every zugbruecke session or, to be more precise, every Windows Python process on top of Wine, has a lifecycle and needs to be terminated.
Note
By default, sessions are automatically terminated when the Unix Python process quits.
Sessions are started by creating an instance of zugbruecke.CtypesSession
. The following example illustrates the life cycle of a session object:
from zugbruecke import CtypesSession
ctypes = CtypesSession() # session creation
kernel32 = ctypes.cdll.kernel32
# do stuff with "kernel32" ...
ctypes.zb_terminate() # session termination
Sessions can also be conveniently managed via Python’s context managers:
from zugbruecke import CtypesSession
with CtypesSession() as ctypes: # session creation AND termination
kernel32 = ctypes.cdll.kernel32
# do stuff with "kernel32" ...
The default session can of cause also be terminated:
from zugbruecke import ctypes # session creation
kernel32 = ctypes.cdll.kernel32
# do stuff with "kernel32" ...
ctypes.zb_terminate() # session termination
A session’s “health status” can be inspected via two of its properties:
from zugbruecke import CtypesSession
ctypes = CtypesSession() # session creation
assert ctypes.zb_client_up
assert ctypes.zb_server_up
ctypes.zb_terminate() # session termination
assert not ctypes.zb_client_up
assert not ctypes.zb_server_up
Both, zb_client_up
and zb_server_up
, are supposed to be True
if the session is up and running and should both be False
is the session has been correctly terminated.
Parallel Sessions
You may choose to run more than one session at a time for more advanced problems. For example, accessing 32 bit and 64 bit DLLs simultaneously requires different session configurations for win32
and win64
architectures. Another use case may be multithreading, where you might want to operate one session per thread.
Note
The number of sessions is only (theoretically) limited by the amount of available memory and by the number of available network ports on the host system (two ports per instance are required).
If you are using functions or classes, which are bound to a session, always use those connected to the relevant session, e.g.
from zugbruecke import CtypesSession
ctypes_a = CtypesSession() # session creation
ctypes_b = CtypesSession() # session creation
assert ctypes_a.zb_id != ctypes_b.zb_id
kernel32 = ctypes_a.cdll.kernel32
msvcrt = ctypes_b.cdll.msvcrt
# do stuff with "kernel32" through "ctypes_a"
# do stuff with "msvcrt" through "ctypes_b"
ctypes_a.zb_terminate() # session termination
ctypes_b.zb_terminate() # session termination
Configuring Sessions
Session configuration parameters can be altered in multiple ways. For details on configuration options, see chapter on configuration.
At Time of Creation
First, a session can be configured at the time of its creation by passing valid configuration parameters as keyword arguments into it:
from zugbruecke import CtypesSession
ctypes_32bit = CtypesSession(arch = "win32") # for 32 bit DLLs
ctypes_64bit = CtypesSession(arch = "win64") # for 64 bit DLLs
At Run-Time
Second, a session can be reconfigured during run-time.
Warning
Only very few parameters can actually be changed once a session has been created.
Consider the following example:
from zugbruecke import CtypesSession
from logging import DEBUG, NOTSET
ctypes = CtypesSession()
assert ctypes.zb_get_parameter('log_level') == NOTSET
ctypes.zb_set_parameter('log_level', DEBUG)
assert ctypes.zb_get_parameter('log_level') == DEBUG