]> granicus.if.org Git - python/commitdiff
Issue #1798: Add ctypes calling convention that allows safe access of errno.
authorThomas Heller <theller@ctypes.org>
Fri, 6 Jun 2008 08:33:46 +0000 (08:33 +0000)
committerThomas Heller <theller@ctypes.org>
Fri, 6 Jun 2008 08:33:46 +0000 (08:33 +0000)
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.

Lib/ctypes/__init__.py
Lib/ctypes/test/test_errno.py [new file with mode: 0644]
Misc/NEWS
Modules/_ctypes/_ctypes.c
Modules/_ctypes/callbacks.c
Modules/_ctypes/callproc.c
Modules/_ctypes/ctypes.h

index 41d39dc93f12ef0c51bdd13c866250856078b880..d3e11dc663ac52d51e019a35fe8cf3ba91ca21fa 100644 (file)
@@ -33,7 +33,9 @@ if _os.name == "posix" and _sys.platform == "darwin":
         DEFAULT_MODE = RTLD_GLOBAL
 
 from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
-     FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI
+     FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \
+     FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
+     FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR
 
 """
 WINOLEAPI -> HRESULT
@@ -73,8 +75,9 @@ def c_buffer(init, size=None):
     return create_string_buffer(init, size)
 
 _c_functype_cache = {}
-def CFUNCTYPE(restype, *argtypes):
-    """CFUNCTYPE(restype, *argtypes) -> function prototype.
+def CFUNCTYPE(restype, *argtypes, **kw):
+    """CFUNCTYPE(restype, *argtypes,
+                 use_errno=False, use_last_error=False) -> function prototype.
 
     restype: the result type
     argtypes: a sequence specifying the argument types
@@ -88,14 +91,21 @@ def CFUNCTYPE(restype, *argtypes):
     prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal
     prototype((function name, dll object)[, paramflags]) -> foreign function exported by name
     """
+    flags = _FUNCFLAG_CDECL
+    if kw.pop("use_errno", False):
+        flags |= _FUNCFLAG_USE_ERRNO
+    if kw.pop("use_last_error", False):
+        flags |= _FUNCFLAG_USE_LASTERROR
+    if kw:
+        raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
     try:
-        return _c_functype_cache[(restype, argtypes)]
+        return _c_functype_cache[(restype, argtypes, flags)]
     except KeyError:
         class CFunctionType(_CFuncPtr):
             _argtypes_ = argtypes
             _restype_ = restype
-            _flags_ = _FUNCFLAG_CDECL
-        _c_functype_cache[(restype, argtypes)] = CFunctionType
+            _flags_ = flags
+        _c_functype_cache[(restype, argtypes, flags)] = CFunctionType
         return CFunctionType
 
 if _os.name in ("nt", "ce"):
@@ -106,16 +116,23 @@ if _os.name in ("nt", "ce"):
         _FUNCFLAG_STDCALL = _FUNCFLAG_CDECL
 
     _win_functype_cache = {}
-    def WINFUNCTYPE(restype, *argtypes):
+    def WINFUNCTYPE(restype, *argtypes, **kw):
         # docstring set later (very similar to CFUNCTYPE.__doc__)
+        flags = _FUNCFLAG_STDCALL
+        if kw.pop("use_errno", False):
+            flags |= _FUNCFLAG_USE_ERRNO
+        if kw.pop("use_last_error", False):
+            flags |= _FUNCFLAG_USE_LASTERROR
+        if kw:
+            raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
         try:
-            return _win_functype_cache[(restype, argtypes)]
+            return _win_functype_cache[(restype, argtypes, flags)]
         except KeyError:
             class WinFunctionType(_CFuncPtr):
                 _argtypes_ = argtypes
                 _restype_ = restype
-                _flags_ = _FUNCFLAG_STDCALL
-            _win_functype_cache[(restype, argtypes)] = WinFunctionType
+                _flags_ = flags
+            _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
             return WinFunctionType
     if WINFUNCTYPE.__doc__:
         WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
@@ -124,6 +141,7 @@ elif _os.name == "posix":
     from _ctypes import dlopen as _dlopen
 
 from _ctypes import sizeof, byref, addressof, alignment, resize
+from _ctypes import get_errno, set_errno
 from _ctypes import _SimpleCData
 
 def _check_size(typ, typecode=None):
@@ -313,12 +331,24 @@ class CDLL(object):
     Calling the functions releases the Python GIL during the call and
     reacquires it afterwards.
     """
-    class _FuncPtr(_CFuncPtr):
-        _flags_ = _FUNCFLAG_CDECL
-        _restype_ = c_int # default, can be overridden in instances
+    _func_flags_ = _FUNCFLAG_CDECL
+    _func_restype_ = c_int
 
-    def __init__(self, name, mode=DEFAULT_MODE, handle=None):
+    def __init__(self, name, mode=DEFAULT_MODE, handle=None,
+                 use_errno=False,
+                 use_last_error=False):
         self._name = name
+        flags = self._func_flags_
+        if use_errno:
+            flags |= _FUNCFLAG_USE_ERRNO
+        if use_last_error:
+            flags |= _FUNCFLAG_USE_LASTERROR
+
+        class _FuncPtr(_CFuncPtr):
+            _flags_ = flags
+            _restype_ = self._func_restype_
+        self._FuncPtr = _FuncPtr
+
         if handle is None:
             self._handle = _dlopen(self._name, mode)
         else:
@@ -348,9 +378,7 @@ class PyDLL(CDLL):
     access Python API functions.  The GIL is not released, and
     Python exceptions are handled correctly.
     """
-    class _FuncPtr(_CFuncPtr):
-        _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
-        _restype_ = c_int # default, can be overridden in instances
+    _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
 
 if _os.name in ("nt", "ce"):
 
@@ -358,9 +386,7 @@ if _os.name in ("nt", "ce"):
         """This class represents a dll exporting functions using the
         Windows stdcall calling convention.
         """
-        class _FuncPtr(_CFuncPtr):
-            _flags_ = _FUNCFLAG_STDCALL
-            _restype_ = c_int # default, can be overridden in instances
+        _func_flags_ = _FUNCFLAG_STDCALL
 
     # XXX Hm, what about HRESULT as normal parameter?
     # Mustn't it derive from c_long then?
@@ -384,9 +410,8 @@ if _os.name in ("nt", "ce"):
         HRESULT error values are automatically raised as WindowsError
         exceptions.
         """
-        class _FuncPtr(_CFuncPtr):
-            _flags_ = _FUNCFLAG_STDCALL
-            _restype_ = HRESULT
+        _func_flags_ = _FUNCFLAG_STDCALL
+        _func_restype_ = HRESULT
 
 class LibraryLoader(object):
     def __init__(self, dlltype):
@@ -424,6 +449,7 @@ if _os.name in ("nt", "ce"):
         GetLastError = windll.kernel32.GetLastError
     else:
         GetLastError = windll.coredll.GetLastError
+    from _ctypes import get_last_error, set_last_error
 
     def WinError(code=None, descr=None):
         if code is None:
diff --git a/Lib/ctypes/test/test_errno.py b/Lib/ctypes/test/test_errno.py
new file mode 100644 (file)
index 0000000..ce1cfad
--- /dev/null
@@ -0,0 +1,76 @@
+import unittest, os, errno\r
+from ctypes import *\r
+from ctypes.util import find_library\r
+import threading\r
+\r
+class Test(unittest.TestCase):\r
+    def test_open(self):\r
+        libc_name = find_library("c")\r
+        if libc_name is not None:\r
+            libc = CDLL(libc_name, use_errno=True)\r
+            if os.name == "nt":\r
+                libc_open = libc._open\r
+            else:\r
+                libc_open = libc.open\r
+\r
+            libc_open.argtypes = c_char_p, c_int\r
+\r
+            self.failUnlessEqual(libc_open("", 0), -1)\r
+            self.failUnlessEqual(get_errno(), errno.ENOENT)\r
+\r
+            self.failUnlessEqual(set_errno(32), errno.ENOENT)\r
+            self.failUnlessEqual(get_errno(), 32)\r
+\r
+\r
+            def _worker():\r
+                set_errno(0)\r
+\r
+                libc = CDLL(libc_name, use_errno=False)\r
+                if os.name == "nt":\r
+                    libc_open = libc._open\r
+                else:\r
+                    libc_open = libc.open\r
+                libc_open.argtypes = c_char_p, c_int\r
+                self.failUnlessEqual(libc_open("", 0), -1)\r
+                self.failUnlessEqual(get_errno(), 0)\r
+\r
+            t = threading.Thread(target=_worker)\r
+            t.start()\r
+            t.join()\r
+\r
+            self.failUnlessEqual(get_errno(), 32)\r
+            set_errno(0)\r
+\r
+    if os.name == "nt":\r
+\r
+        def test_GetLastError(self):\r
+            dll = WinDLL("kernel32", use_last_error=True)\r
+            GetModuleHandle = dll.GetModuleHandleA\r
+            GetModuleHandle.argtypes = [c_wchar_p]\r
+\r
+            self.failUnlessEqual(0, GetModuleHandle("foo"))\r
+            self.failUnlessEqual(get_last_error(), 126)\r
+\r
+            self.failUnlessEqual(set_last_error(32), 126)\r
+            self.failUnlessEqual(get_last_error(), 32)\r
+\r
+            def _worker():\r
+                set_last_error(0)\r
+\r
+                dll = WinDLL("kernel32", use_last_error=False)\r
+                GetModuleHandle = dll.GetModuleHandleW\r
+                GetModuleHandle.argtypes = [c_wchar_p]\r
+                GetModuleHandle("bar")\r
+\r
+                self.failUnlessEqual(get_last_error(), 0)\r
+\r
+            t = threading.Thread(target=_worker)\r
+            t.start()\r
+            t.join()\r
+\r
+            self.failUnlessEqual(get_last_error(), 32)\r
+\r
+            set_last_error(0)\r
+\r
+if __name__ == "__main__":\r
+    unittest.main()\r
index 9083bbcabe6c642b79a053cfd65dc87d6a887999..c0f25cc81bc7d5e206cc8917a9a04d2db6f5588c 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -72,6 +72,9 @@ Extension Modules
 Library
 -------
 
+- Issue #1798: Add ctypes calling convention that allows safe access
+  to errno.
+
 - Issue #2404: ctypes objects support the new pep3118 buffer interface
 
 - Patch #2125: Add GetInteger and GetString methods for 
index 30e981a7f942b3fb91d038d9e3e913901cd00136..34ec9f2580c7227ee296e49f2492b2392d265a84 100644 (file)
@@ -3412,7 +3412,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;
 
@@ -5535,6 +5535,8 @@ init_ctypes(void)
        PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyInt_FromLong(FUNCFLAG_STDCALL));
 #endif
        PyModule_AddObject(m, "FUNCFLAG_CDECL", PyInt_FromLong(FUNCFLAG_CDECL));
+       PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyInt_FromLong(FUNCFLAG_USE_ERRNO));
+       PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyInt_FromLong(FUNCFLAG_USE_LASTERROR));
        PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyInt_FromLong(FUNCFLAG_PYTHONAPI));
        PyModule_AddStringConstant(m, "__version__", "1.1.0");
 
index b78c5282c88891e3c5b58a053b76bc9baca88d1c..aa021545f88f41294201ebfd9faadd8cce1c1f10 100644 (file)
@@ -189,12 +189,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
@@ -271,8 +274,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);
@@ -322,6 +358,7 @@ static void closure_fcn(ffi_cif *cif,
                          p->setfunc,
                          p->callable,
                          p->converters,
+                         p->flags,
                          args);
 }
 
@@ -351,7 +388,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;
@@ -371,6 +408,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)
@@ -398,7 +436,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,
index 052017652f9ff2318205989b2c790e5a636b08d1..89a4d682502fc81605ee37a2a913ede6158854c4 100644 (file)
 #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 = PyInt_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 PyInt_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)
@@ -625,6 +747,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
@@ -656,11 +780,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
@@ -675,7 +814,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
@@ -1692,6 +1842,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 },
@@ -1702,6 +1854,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},
index d068ea5eecdecb67bfac4235b9b8a077611e8487..96db12f25595dde9d4786300f7cd4b31485ba592 100644 (file)
@@ -87,6 +87,7 @@ typedef struct {
        PyObject_VAR_HEAD
        ffi_closure *pcl; /* the C callable */
        ffi_cif cif;
+       int flags;
        PyObject *converters;
        PyObject *callable;
        PyObject *restype;
@@ -185,7 +186,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;
@@ -311,6 +312,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
@@ -429,6 +432,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;