diff options
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_ctypes/_ctypes.c | 4 | ||||
-rw-r--r-- | Modules/_ctypes/callbacks.c | 42 | ||||
-rw-r--r-- | Modules/_ctypes/callproc.c | 154 | ||||
-rw-r--r-- | Modules/_ctypes/ctypes.h | 6 |
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; |