summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_ctypes/_ctypes.c4
-rw-r--r--Modules/_ctypes/callbacks.c42
-rw-r--r--Modules/_ctypes/callproc.c154
-rw-r--r--Modules/_ctypes/ctypes.h6
4 files changed, 202 insertions, 4 deletions
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index b652319..fe598d7 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -3340,7 +3340,7 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
thunk = AllocFunctionCallback(callable,
dict->argtypes,
dict->restype,
- dict->flags & FUNCFLAG_CDECL);
+ dict->flags);
if (!thunk)
return NULL;
@@ -5333,6 +5333,8 @@ init_ctypes(void)
PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyLong_FromLong(FUNCFLAG_STDCALL));
#endif
PyModule_AddObject(m, "FUNCFLAG_CDECL", PyLong_FromLong(FUNCFLAG_CDECL));
+ PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
+ PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
PyModule_AddStringConstant(m, "__version__", "1.1.0");
diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c
index 87e3df7..80b4cde 100644
--- a/Modules/_ctypes/callbacks.c
+++ b/Modules/_ctypes/callbacks.c
@@ -185,12 +185,15 @@ static void _CallPythonObject(void *mem,
SETFUNC setfunc,
PyObject *callable,
PyObject *converters,
+ int flags,
void **pArgs)
{
Py_ssize_t i;
PyObject *result;
PyObject *arglist = NULL;
Py_ssize_t nArgs;
+ PyObject *error_object = NULL;
+ int *space;
#ifdef WITH_THREAD
PyGILState_STATE state = PyGILState_Ensure();
#endif
@@ -267,8 +270,41 @@ static void _CallPythonObject(void *mem,
#define CHECK(what, x) \
if (x == NULL) _AddTraceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
+ if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
+ error_object = get_error_object(&space);
+ if (error_object == NULL)
+ goto Done;
+ if (flags & FUNCFLAG_USE_ERRNO) {
+ int temp = space[0];
+ space[0] = errno;
+ errno = temp;
+ }
+#ifdef MS_WIN32
+ if (flags & FUNCFLAG_USE_LASTERROR) {
+ int temp = space[1];
+ space[1] = GetLastError();
+ SetLastError(temp);
+ }
+#endif
+ }
+
result = PyObject_CallObject(callable, arglist);
CHECK("'calling callback function'", result);
+
+#ifdef MS_WIN32
+ if (flags & FUNCFLAG_USE_LASTERROR) {
+ int temp = space[1];
+ space[1] = GetLastError();
+ SetLastError(temp);
+ }
+#endif
+ if (flags & FUNCFLAG_USE_ERRNO) {
+ int temp = space[0];
+ space[0] = errno;
+ errno = temp;
+ }
+ Py_XDECREF(error_object);
+
if ((restype != &ffi_type_void) && result) {
PyObject *keep;
assert(setfunc);
@@ -319,6 +355,7 @@ static void closure_fcn(ffi_cif *cif,
p->setfunc,
p->callable,
p->converters,
+ p->flags,
args);
}
@@ -348,7 +385,7 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs)
CThunkObject *AllocFunctionCallback(PyObject *callable,
PyObject *converters,
PyObject *restype,
- int is_cdecl)
+ int flags)
{
int result;
CThunkObject *p;
@@ -368,6 +405,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable,
goto error;
}
+ p->flags = flags;
for (i = 0; i < nArgs; ++i) {
PyObject *cnv = PySequence_GetItem(converters, i);
if (cnv == NULL)
@@ -395,7 +433,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable,
cc = FFI_DEFAULT_ABI;
#if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64)
- if (is_cdecl == 0)
+ if ((flags & FUNCFLAG_CDECL) == 0)
cc = FFI_STDCALL;
#endif
result = ffi_prep_cif(&p->cif, cc,
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
index fd66216..ffdc3bb 100644
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -78,7 +78,129 @@
#define DONT_USE_SEH
#endif
+/*
+ ctypes maintains thread-local storage that has space for two error numbers:
+ private copies of the system 'errno' value and, on Windows, the system error code
+ accessed by the GetLastError() and SetLastError() api functions.
+
+ Foreign functions created with CDLL(..., use_errno=True), when called, swap
+ the system 'errno' value with the private copy just before the actual
+ function call, and swapped again immediately afterwards. The 'use_errno'
+ parameter defaults to False, in this case 'ctypes_errno' is not touched.
+
+ On Windows, foreign functions created with CDLL(..., use_last_error=True) or
+ WinDLL(..., use_last_error=True) swap the system LastError value with the
+ ctypes private copy.
+
+ The values are also swapped immeditately before and after ctypes callback
+ functions are called, if the callbacks are constructed using the new
+ optional use_errno parameter set to True: CFUNCTYPE(..., use_errno=TRUE) or
+ WINFUNCTYPE(..., use_errno=True).
+
+ New ctypes functions are provided to access the ctypes private copies from
+ Python:
+
+ - ctypes.set_errno(value) and ctypes.set_last_error(value) store 'value' in
+ the private copy and returns the previous value.
+
+ - ctypes.get_errno() and ctypes.get_last_error() returns the current ctypes
+ private copies value.
+*/
+
+/*
+ This function creates and returns a thread-local Python object that has
+ space to store two integer error numbers; once created the Python object is
+ kept alive in the thread state dictionary as long as the thread itself.
+*/
+PyObject *
+get_error_object(int **pspace)
+{
+ PyObject *dict = PyThreadState_GetDict();
+ PyObject *errobj;
+ if (dict == 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "cannot get thread state");
+ return NULL;
+ }
+ errobj = PyDict_GetItemString(dict, "ctypes.error_object");
+ if (errobj)
+ Py_INCREF(errobj);
+ else {
+ void *space = PyMem_Malloc(sizeof(int) * 2);
+ if (space == NULL)
+ return NULL;
+ memset(space, 0, sizeof(int) * 2);
+ errobj = PyCObject_FromVoidPtr(space, PyMem_Free);
+ if (errobj == NULL)
+ return NULL;
+ if (-1 == PyDict_SetItemString(dict, "ctypes.error_object",
+ errobj)) {
+ Py_DECREF(errobj);
+ return NULL;
+ }
+ }
+ *pspace = (int *)PyCObject_AsVoidPtr(errobj);
+ return errobj;
+}
+
+static PyObject *
+get_error_internal(PyObject *self, PyObject *args, int index)
+{
+ int *space;
+ PyObject *errobj = get_error_object(&space);
+ PyObject *result;
+
+ if (errobj == NULL)
+ return NULL;
+ result = PyLong_FromLong(space[index]);
+ Py_DECREF(errobj);
+ return result;
+}
+
+static PyObject *
+set_error_internal(PyObject *self, PyObject *args, int index)
+{
+ int new_errno, old_errno;
+ PyObject *errobj;
+ int *space;
+
+ if (!PyArg_ParseTuple(args, "i", &new_errno))
+ return NULL;
+ errobj = get_error_object(&space);
+ if (errobj == NULL)
+ return NULL;
+ old_errno = space[index];
+ space[index] = new_errno;
+ Py_DECREF(errobj);
+ return PyLong_FromLong(old_errno);
+}
+
+static PyObject *
+get_errno(PyObject *self, PyObject *args)
+{
+ return get_error_internal(self, args, 0);
+}
+
+static PyObject *
+set_errno(PyObject *self, PyObject *args)
+{
+ return set_error_internal(self, args, 0);
+}
+
#ifdef MS_WIN32
+
+static PyObject *
+get_last_error(PyObject *self, PyObject *args)
+{
+ return get_error_internal(self, args, 1);
+}
+
+static PyObject *
+set_last_error(PyObject *self, PyObject *args)
+{
+ return set_error_internal(self, args, 1);
+}
+
PyObject *ComError;
static TCHAR *FormatError(DWORD code)
@@ -614,6 +736,8 @@ static int _call_function_pointer(int flags,
#ifdef WITH_THREAD
PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
#endif
+ PyObject *error_object = NULL;
+ int *space;
ffi_cif cif;
int cc;
#ifdef MS_WIN32
@@ -645,11 +769,26 @@ static int _call_function_pointer(int flags,
return -1;
}
+ if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
+ error_object = get_error_object(&space);
+ if (error_object == NULL)
+ return -1;
+ }
#ifdef WITH_THREAD
if ((flags & FUNCFLAG_PYTHONAPI) == 0)
Py_UNBLOCK_THREADS
#endif
+ if (flags & FUNCFLAG_USE_ERRNO) {
+ int temp = space[0];
+ space[0] = errno;
+ errno = temp;
+ }
#ifdef MS_WIN32
+ if (flags & FUNCFLAG_USE_LASTERROR) {
+ int temp = space[1];
+ space[1] = GetLastError();
+ SetLastError(temp);
+ }
#ifndef DONT_USE_SEH
__try {
#endif
@@ -664,7 +803,18 @@ static int _call_function_pointer(int flags,
;
}
#endif
+ if (flags & FUNCFLAG_USE_LASTERROR) {
+ int temp = space[1];
+ space[1] = GetLastError();
+ SetLastError(temp);
+ }
#endif
+ if (flags & FUNCFLAG_USE_ERRNO) {
+ int temp = space[0];
+ space[0] = errno;
+ errno = temp;
+ }
+ Py_XDECREF(error_object);
#ifdef WITH_THREAD
if ((flags & FUNCFLAG_PYTHONAPI) == 0)
Py_BLOCK_THREADS
@@ -1658,6 +1808,8 @@ buffer_info(PyObject *self, PyObject *arg)
}
PyMethodDef module_methods[] = {
+ {"get_errno", get_errno, METH_NOARGS},
+ {"set_errno", set_errno, METH_VARARGS},
{"POINTER", POINTER, METH_O },
{"pointer", pointer, METH_O },
{"_unpickle", unpickle, METH_VARARGS },
@@ -1667,6 +1819,8 @@ PyMethodDef module_methods[] = {
{"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc},
#endif
#ifdef MS_WIN32
+ {"get_last_error", get_last_error, METH_NOARGS},
+ {"set_last_error", set_last_error, METH_VARARGS},
{"CopyComPointer", copy_com_pointer, METH_VARARGS, copy_com_pointer_doc},
{"FormatError", format_error, METH_VARARGS, format_error_doc},
{"LoadLibrary", load_library, METH_VARARGS, load_library_doc},
diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
index 0982f76..7bbe439 100644
--- a/Modules/_ctypes/ctypes.h
+++ b/Modules/_ctypes/ctypes.h
@@ -56,6 +56,7 @@ typedef struct {
PyObject_VAR_HEAD
ffi_closure *pcl; /* the C callable */
ffi_cif cif;
+ int flags;
PyObject *converters;
PyObject *callable;
PyObject *restype;
@@ -154,7 +155,7 @@ extern PyMethodDef module_methods[];
extern CThunkObject *AllocFunctionCallback(PyObject *callable,
PyObject *converters,
PyObject *restype,
- int stdcall);
+ int flags);
/* a table entry describing a predefined ctypes type */
struct fielddesc {
char code;
@@ -280,6 +281,8 @@ PyObject *_CallProc(PPROC pProc,
#define FUNCFLAG_CDECL 0x1
#define FUNCFLAG_HRESULT 0x2
#define FUNCFLAG_PYTHONAPI 0x4
+#define FUNCFLAG_USE_ERRNO 0x8
+#define FUNCFLAG_USE_LASTERROR 0x10
#define TYPEFLAG_ISPOINTER 0x100
#define TYPEFLAG_HASPOINTER 0x200
@@ -356,6 +359,7 @@ extern char *alloc_format_string(const char *prefix, const char *suffix);
extern int IsSimpleSubType(PyObject *obj);
extern PyObject *_pointer_type_cache;
+PyObject *get_error_object(int **pspace);
#ifdef MS_WIN32
extern PyObject *ComError;