Issue #1798: Add ctypes calling convention that allows safe access to

errno (and LastError, on Windows).

ctypes maintains a module-global, but thread-local, variable that
contains an error number; called 'ctypes_errno' for this discussion.
This variable is a private copy of the systems 'errno' value; the copy
is swapped with the 'errno' variable on several occasions.

Foreign functions created with CDLL(..., use_errno=True), when called,
swap the values 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.

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).

Two new ctypes functions are provided to access the 'ctypes_errno'
value from Python:

- ctypes.set_errno(value) sets ctypes_errno to 'value', the previous
  ctypes_errno value is returned.

- ctypes.get_errno() returns the current ctypes_errno value.

---

On Windows, the same scheme is implemented for the error value which
is managed by the GetLastError() and SetLastError() windows api calls.

The ctypes functions are 'ctypes.set_last_error(value)' and
'ctypes.get_last_error()', the CDLL and WinDLL optional parameter is
named 'use_last_error', defaults to False.

---

On Windows, TlsSetValue and TlsGetValue calls are used to provide
thread local storage for the variables; ctypes compiled with __GNUC__
uses __thread variables.
This commit is contained in:
Thomas Heller 2008-06-04 18:59:03 +00:00
parent a2b34b87a9
commit e70c3378c0
7 changed files with 310 additions and 27 deletions

View File

@ -33,7 +33,9 @@ if _os.name == "posix" and _sys.platform == "darwin":
DEFAULT_MODE = RTLD_GLOBAL DEFAULT_MODE = RTLD_GLOBAL
from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \ 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 WINOLEAPI -> HRESULT
@ -73,8 +75,9 @@ def c_buffer(init, size=None):
return create_string_buffer(init, size) return create_string_buffer(init, size)
_c_functype_cache = {} _c_functype_cache = {}
def CFUNCTYPE(restype, *argtypes): def CFUNCTYPE(restype, *argtypes, **kw):
"""CFUNCTYPE(restype, *argtypes) -> function prototype. """CFUNCTYPE(restype, *argtypes,
use_errno=False, use_last_error=False) -> function prototype.
restype: the result type restype: the result type
argtypes: a sequence specifying the argument types 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((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal
prototype((function name, dll object)[, paramflags]) -> foreign function exported by name 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: try:
return _c_functype_cache[(restype, argtypes)] return _c_functype_cache[(restype, argtypes, flags)]
except KeyError: except KeyError:
class CFunctionType(_CFuncPtr): class CFunctionType(_CFuncPtr):
_argtypes_ = argtypes _argtypes_ = argtypes
_restype_ = restype _restype_ = restype
_flags_ = _FUNCFLAG_CDECL _flags_ = flags
_c_functype_cache[(restype, argtypes)] = CFunctionType _c_functype_cache[(restype, argtypes, flags)] = CFunctionType
return CFunctionType return CFunctionType
if _os.name in ("nt", "ce"): if _os.name in ("nt", "ce"):
@ -106,16 +116,23 @@ if _os.name in ("nt", "ce"):
_FUNCFLAG_STDCALL = _FUNCFLAG_CDECL _FUNCFLAG_STDCALL = _FUNCFLAG_CDECL
_win_functype_cache = {} _win_functype_cache = {}
def WINFUNCTYPE(restype, *argtypes): def WINFUNCTYPE(restype, *argtypes, **kw):
# docstring set later (very similar to CFUNCTYPE.__doc__) # 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: try:
return _win_functype_cache[(restype, argtypes)] return _win_functype_cache[(restype, argtypes, flags)]
except KeyError: except KeyError:
class WinFunctionType(_CFuncPtr): class WinFunctionType(_CFuncPtr):
_argtypes_ = argtypes _argtypes_ = argtypes
_restype_ = restype _restype_ = restype
_flags_ = _FUNCFLAG_STDCALL _flags_ = flags
_win_functype_cache[(restype, argtypes)] = WinFunctionType _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
return WinFunctionType return WinFunctionType
if WINFUNCTYPE.__doc__: if WINFUNCTYPE.__doc__:
WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE") WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
@ -124,6 +141,7 @@ elif _os.name == "posix":
from _ctypes import dlopen as _dlopen from _ctypes import dlopen as _dlopen
from _ctypes import sizeof, byref, addressof, alignment, resize from _ctypes import sizeof, byref, addressof, alignment, resize
from _ctypes import get_errno, set_errno
from _ctypes import _SimpleCData from _ctypes import _SimpleCData
def _check_size(typ, typecode=None): def _check_size(typ, typecode=None):
@ -313,12 +331,24 @@ class CDLL(object):
Calling the functions releases the Python GIL during the call and Calling the functions releases the Python GIL during the call and
reacquires it afterwards. reacquires it afterwards.
""" """
class _FuncPtr(_CFuncPtr): _func_flags_ = _FUNCFLAG_CDECL
_flags_ = _FUNCFLAG_CDECL _func_restype_ = c_int
_restype_ = c_int # default, can be overridden in instances
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 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: if handle is None:
self._handle = _dlopen(self._name, mode) self._handle = _dlopen(self._name, mode)
else: else:
@ -348,9 +378,7 @@ class PyDLL(CDLL):
access Python API functions. The GIL is not released, and access Python API functions. The GIL is not released, and
Python exceptions are handled correctly. Python exceptions are handled correctly.
""" """
class _FuncPtr(_CFuncPtr): _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
_restype_ = c_int # default, can be overridden in instances
if _os.name in ("nt", "ce"): 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 """This class represents a dll exporting functions using the
Windows stdcall calling convention. Windows stdcall calling convention.
""" """
class _FuncPtr(_CFuncPtr): _func_flags_ = _FUNCFLAG_STDCALL
_flags_ = _FUNCFLAG_STDCALL
_restype_ = c_int # default, can be overridden in instances
# XXX Hm, what about HRESULT as normal parameter? # XXX Hm, what about HRESULT as normal parameter?
# Mustn't it derive from c_long then? # 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 HRESULT error values are automatically raised as WindowsError
exceptions. exceptions.
""" """
class _FuncPtr(_CFuncPtr): _func_flags_ = _FUNCFLAG_STDCALL
_flags_ = _FUNCFLAG_STDCALL _func_restype_ = HRESULT
_restype_ = HRESULT
class LibraryLoader(object): class LibraryLoader(object):
def __init__(self, dlltype): def __init__(self, dlltype):
@ -424,6 +449,7 @@ if _os.name in ("nt", "ce"):
GetLastError = windll.kernel32.GetLastError GetLastError = windll.kernel32.GetLastError
else: else:
GetLastError = windll.coredll.GetLastError GetLastError = windll.coredll.GetLastError
from _ctypes import get_last_error, set_last_error
def WinError(code=None, descr=None): def WinError(code=None, descr=None):
if code is None: if code is None:

View File

@ -0,0 +1,76 @@
import unittest, os, errno
from ctypes import *
from ctypes.util import find_library
import threading
class Test(unittest.TestCase):
def test_open(self):
libc_name = find_library("c")
if libc_name is not None:
libc = CDLL(libc_name, use_errno=True)
if os.name == "nt":
libc_open = libc._open
else:
libc_open = libc.open
libc_open.argtypes = c_char_p, c_int
self.failUnlessEqual(libc_open("", 0), -1)
self.failUnlessEqual(get_errno(), errno.ENOENT)
self.failUnlessEqual(set_errno(32), errno.ENOENT)
self.failUnlessEqual(get_errno(), 32)
def _worker():
set_errno(0)
libc = CDLL(libc_name, use_errno=False)
if os.name == "nt":
libc_open = libc._open
else:
libc_open = libc.open
libc_open.argtypes = c_char_p, c_int
self.failUnlessEqual(libc_open("", 0), -1)
self.failUnlessEqual(get_errno(), 0)
t = threading.Thread(target=_worker)
t.start()
t.join()
self.failUnlessEqual(get_errno(), 32)
set_errno(0)
if os.name == "nt":
def test_GetLastError(self):
dll = WinDLL("kernel32", use_last_error=True)
GetModuleHandle = dll.GetModuleHandleA
GetModuleHandle.argtypes = [c_wchar_p]
self.failUnlessEqual(0, GetModuleHandle("foo"))
self.failUnlessEqual(get_last_error(), 126)
self.failUnlessEqual(set_last_error(32), 126)
self.failUnlessEqual(get_last_error(), 32)
def _worker():
set_last_error(0)
dll = WinDLL("kernel32", use_last_error=False)
GetModuleHandle = dll.GetModuleHandleW
GetModuleHandle.argtypes = [c_wchar_p]
GetModuleHandle("bar")
self.failUnlessEqual(get_last_error(), 0)
t = threading.Thread(target=_worker)
t.start()
t.join()
self.failUnlessEqual(get_last_error(), 32)
set_last_error(0)
if __name__ == "__main__":
unittest.main()

View File

@ -72,6 +72,9 @@ Extension Modules
Library Library
------- -------
- Issue #1798: Add ctypes calling convention that allows safe access
to errno.
- Patch #2125: Add GetInteger and GetString methods for - Patch #2125: Add GetInteger and GetString methods for
msilib.Record objects. msilib.Record objects.

View File

@ -3271,7 +3271,7 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
thunk = AllocFunctionCallback(callable, thunk = AllocFunctionCallback(callable,
dict->argtypes, dict->argtypes,
dict->restype, dict->restype,
dict->flags & FUNCFLAG_CDECL); dict->flags);
if (!thunk) if (!thunk)
return NULL; return NULL;
@ -5273,6 +5273,17 @@ init_ctypes(void)
if (!m) if (!m)
return; return;
#ifdef MS_WIN32
dwTlsIndex_LastError = TlsAlloc();
dwTlsIndex_errno = TlsAlloc();
if (dwTlsIndex_LastError == TLS_OUT_OF_INDEXES
|| dwTlsIndex_errno == TLS_OUT_OF_INDEXES) {
PyErr_SetString(PyExc_MemoryError,
"Could not allocate TLSIndex for LastError value");
return;
}
#endif
_pointer_type_cache = PyDict_New(); _pointer_type_cache = PyDict_New();
if (_pointer_type_cache == NULL) if (_pointer_type_cache == NULL)
return; return;
@ -5394,6 +5405,8 @@ init_ctypes(void)
PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyInt_FromLong(FUNCFLAG_STDCALL)); PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyInt_FromLong(FUNCFLAG_STDCALL));
#endif #endif
PyModule_AddObject(m, "FUNCFLAG_CDECL", PyInt_FromLong(FUNCFLAG_CDECL)); 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_AddObject(m, "FUNCFLAG_PYTHONAPI", PyInt_FromLong(FUNCFLAG_PYTHONAPI));
PyModule_AddStringConstant(m, "__version__", "1.1.0"); PyModule_AddStringConstant(m, "__version__", "1.1.0");

View File

@ -189,6 +189,7 @@ static void _CallPythonObject(void *mem,
SETFUNC setfunc, SETFUNC setfunc,
PyObject *callable, PyObject *callable,
PyObject *converters, PyObject *converters,
int flags,
void **pArgs) void **pArgs)
{ {
Py_ssize_t i; Py_ssize_t i;
@ -271,8 +272,22 @@ static void _CallPythonObject(void *mem,
#define CHECK(what, x) \ #define CHECK(what, x) \
if (x == NULL) _AddTraceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print() if (x == NULL) _AddTraceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
if (flags & FUNCFLAG_USE_ERRNO)
_swap_errno();
#ifdef MS_WIN32
if (flags & FUNCFLAG_USE_LASTERROR)
_swap_last_error();
#endif
result = PyObject_CallObject(callable, arglist); result = PyObject_CallObject(callable, arglist);
CHECK("'calling callback function'", result); CHECK("'calling callback function'", result);
#ifdef MS_WIN32
if (flags & FUNCFLAG_USE_LASTERROR)
_swap_last_error();
#endif
if (flags & FUNCFLAG_USE_ERRNO)
_swap_errno();
if ((restype != &ffi_type_void) && result) { if ((restype != &ffi_type_void) && result) {
PyObject *keep; PyObject *keep;
assert(setfunc); assert(setfunc);
@ -322,6 +337,7 @@ static void closure_fcn(ffi_cif *cif,
p->setfunc, p->setfunc,
p->callable, p->callable,
p->converters, p->converters,
p->flags,
args); args);
} }
@ -351,7 +367,7 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs)
CThunkObject *AllocFunctionCallback(PyObject *callable, CThunkObject *AllocFunctionCallback(PyObject *callable,
PyObject *converters, PyObject *converters,
PyObject *restype, PyObject *restype,
int is_cdecl) int flags)
{ {
int result; int result;
CThunkObject *p; CThunkObject *p;
@ -371,6 +387,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable,
goto error; goto error;
} }
p->flags = flags;
for (i = 0; i < nArgs; ++i) { for (i = 0; i < nArgs; ++i) {
PyObject *cnv = PySequence_GetItem(converters, i); PyObject *cnv = PySequence_GetItem(converters, i);
if (cnv == NULL) if (cnv == NULL)
@ -398,7 +415,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable,
cc = FFI_DEFAULT_ABI; cc = FFI_DEFAULT_ABI;
#if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64) #if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64)
if (is_cdecl == 0) if ((flags & FUNCFLAG_CDECL) == 0)
cc = FFI_STDCALL; cc = FFI_STDCALL;
#endif #endif
result = ffi_prep_cif(&p->cif, cc, result = ffi_prep_cif(&p->cif, cc,

View File

@ -83,6 +83,131 @@
#define DONT_USE_SEH #define DONT_USE_SEH
#endif #endif
/*
ctypes maintains a module-global, but thread-local, variable that contains
an error number; called 'ctypes_errno' for this discussion. This variable
is a private copy of the systems 'errno' value; the copy is swapped with the
'errno' variable on several occasions.
Foreign functions created with CDLL(..., use_errno=True), when called, swap
the values 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.
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).
Two new ctypes functions are provided to access the 'ctypes_errno' value
from Python:
- ctypes.set_errno(value) sets ctypes_errno to 'value', the previous
ctypes_errno value is returned.
- ctypes.get_errno() returns the current ctypes_errno value.
---
On Windows, the same scheme is implemented for the error value which is
managed by the GetLastError() and SetLastError() windows api calls.
The ctypes functions are 'ctypes.set_last_error(value)' and
'ctypes.get_last_error()', the CDLL and WinDLL optional parameter is named
'use_last_error', defaults to False.
---
On Windows, TlsSetValue and TlsGetValue calls are used to provide thread
local storage for the variables; ctypes compiled with __GNUC__ uses __thread
variables.
*/
#if defined(MS_WIN32)
DWORD dwTlsIndex_LastError;
DWORD dwTlsIndex_errno;
void
_swap_last_error(void)
{
DWORD temp = GetLastError();
SetLastError((DWORD)TlsGetValue(dwTlsIndex_LastError));
TlsSetValue(dwTlsIndex_LastError, (void *)temp);
}
static PyObject *
get_last_error(PyObject *self, PyObject *args)
{
return PyInt_FromLong((DWORD)TlsGetValue(dwTlsIndex_LastError));
}
static PyObject *
set_last_error(PyObject *self, PyObject *args)
{
DWORD new_value, prev_value;
if (!PyArg_ParseTuple(args, "i", &new_value))
return NULL;
prev_value = (DWORD)TlsGetValue(dwTlsIndex_LastError);
TlsSetValue(dwTlsIndex_LastError, (void *)new_value);
return PyInt_FromLong(prev_value);
}
void
_swap_errno(void)
{
int temp = errno;
errno = (int)TlsGetValue(dwTlsIndex_errno);
TlsSetValue(dwTlsIndex_errno, (void *)temp);
}
static PyObject *
get_errno(PyObject *self, PyObject *args)
{
return PyInt_FromLong((int)TlsGetValue(dwTlsIndex_errno));
}
static PyObject *
set_errno(PyObject *self, PyObject *args)
{
int new_value, prev_value;
if (!PyArg_ParseTuple(args, "i", &new_value))
return NULL;
prev_value = (int)TlsGetValue(dwTlsIndex_errno);
TlsSetValue(dwTlsIndex_errno, (void *)new_value);
return PyInt_FromLong(prev_value);
}
#elif defined(__GNUC__)
static __thread int ctypes_errno;
void
_swap_errno(void)
{
int temp = errno;
errno = ctypes_errno;
ctypes_errno = temp;
}
static PyObject *
get_errno(PyObject *self, PyObject *args)
{
return PyInt_FromLong(ctypes_errno);
}
static PyObject *
set_errno(PyObject *self, PyObject *args)
{
int new_errno;
if (!PyArg_ParseTuple(args, "i", &new_errno))
return NULL;
return PyInt_FromLong(_save_errno(new_errno));
}
#else
#error "TLS not implemented in this configuration"
#endif
#ifdef MS_WIN32 #ifdef MS_WIN32
PyObject *ComError; PyObject *ComError;
@ -660,7 +785,11 @@ static int _call_function_pointer(int flags,
if ((flags & FUNCFLAG_PYTHONAPI) == 0) if ((flags & FUNCFLAG_PYTHONAPI) == 0)
Py_UNBLOCK_THREADS Py_UNBLOCK_THREADS
#endif #endif
if (flags & FUNCFLAG_USE_ERRNO)
_swap_errno();
#ifdef MS_WIN32 #ifdef MS_WIN32
if (flags & FUNCFLAG_USE_LASTERROR)
_swap_last_error();
#ifndef DONT_USE_SEH #ifndef DONT_USE_SEH
__try { __try {
#endif #endif
@ -675,7 +804,11 @@ static int _call_function_pointer(int flags,
; ;
} }
#endif #endif
if (flags & FUNCFLAG_USE_LASTERROR)
_swap_last_error();
#endif #endif
if (flags & FUNCFLAG_USE_ERRNO)
_swap_errno();
#ifdef WITH_THREAD #ifdef WITH_THREAD
if ((flags & FUNCFLAG_PYTHONAPI) == 0) if ((flags & FUNCFLAG_PYTHONAPI) == 0)
Py_BLOCK_THREADS Py_BLOCK_THREADS
@ -1667,6 +1800,8 @@ pointer(PyObject *self, PyObject *arg)
} }
PyMethodDef module_methods[] = { PyMethodDef module_methods[] = {
{"get_errno", get_errno, METH_NOARGS},
{"set_errno", set_errno, METH_VARARGS},
{"POINTER", POINTER, METH_O }, {"POINTER", POINTER, METH_O },
{"pointer", pointer, METH_O }, {"pointer", pointer, METH_O },
{"_unpickle", unpickle, METH_VARARGS }, {"_unpickle", unpickle, METH_VARARGS },
@ -1675,6 +1810,8 @@ PyMethodDef module_methods[] = {
{"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc}, {"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc},
#endif #endif
#ifdef MS_WIN32 #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}, {"CopyComPointer", copy_com_pointer, METH_VARARGS, copy_com_pointer_doc},
{"FormatError", format_error, METH_VARARGS, format_error_doc}, {"FormatError", format_error, METH_VARARGS, format_error_doc},
{"LoadLibrary", load_library, METH_VARARGS, load_library_doc}, {"LoadLibrary", load_library, METH_VARARGS, load_library_doc},

View File

@ -87,6 +87,7 @@ typedef struct {
PyObject_VAR_HEAD PyObject_VAR_HEAD
ffi_closure *pcl; /* the C callable */ ffi_closure *pcl; /* the C callable */
ffi_cif cif; ffi_cif cif;
int flags;
PyObject *converters; PyObject *converters;
PyObject *callable; PyObject *callable;
PyObject *restype; PyObject *restype;
@ -185,7 +186,7 @@ extern PyMethodDef module_methods[];
extern CThunkObject *AllocFunctionCallback(PyObject *callable, extern CThunkObject *AllocFunctionCallback(PyObject *callable,
PyObject *converters, PyObject *converters,
PyObject *restype, PyObject *restype,
int stdcall); int flags);
/* a table entry describing a predefined ctypes type */ /* a table entry describing a predefined ctypes type */
struct fielddesc { struct fielddesc {
char code; char code;
@ -303,6 +304,8 @@ PyObject *_CallProc(PPROC pProc,
#define FUNCFLAG_CDECL 0x1 #define FUNCFLAG_CDECL 0x1
#define FUNCFLAG_HRESULT 0x2 #define FUNCFLAG_HRESULT 0x2
#define FUNCFLAG_PYTHONAPI 0x4 #define FUNCFLAG_PYTHONAPI 0x4
#define FUNCFLAG_USE_ERRNO 0x8
#define FUNCFLAG_USE_LASTERROR 0x10
#define TYPEFLAG_ISPOINTER 0x100 #define TYPEFLAG_ISPOINTER 0x100
#define TYPEFLAG_HASPOINTER 0x200 #define TYPEFLAG_HASPOINTER 0x200
@ -421,8 +424,16 @@ extern int IsSimpleSubType(PyObject *obj);
extern PyObject *_pointer_type_cache; extern PyObject *_pointer_type_cache;
extern void _swap_errno(void);
#ifdef MS_WIN32 #ifdef MS_WIN32
extern void _swap_last_error(void);
extern PyObject *ComError; extern PyObject *ComError;
extern DWORD dwTlsIndex_LastError;
extern DWORD dwTlsIndex_errno;
#endif #endif
/* /*