Merge changes from the upstream version:

- cast is implemented as a foreign function now
- On Windows, it is now possible to access functions exported by ordinal only
This commit is contained in:
Thomas Heller 2006-03-17 15:52:58 +00:00
parent f4b066084a
commit b03cb602e8
7 changed files with 191 additions and 92 deletions

View File

@ -304,10 +304,11 @@ class CDLL(object):
raise AttributeError, name raise AttributeError, name
return self.__getitem__(name) return self.__getitem__(name)
def __getitem__(self, name): def __getitem__(self, name_or_ordinal):
func = self._FuncPtr(name, self) func = self._FuncPtr((name_or_ordinal, self))
func.__name__ = name if not isinstance(name_or_ordinal, (int, long)):
setattr(self, name, func) func.__name__ = name_or_ordinal
setattr(self, name_or_ordinal, func)
return func return func
class PyDLL(CDLL): class PyDLL(CDLL):
@ -384,21 +385,29 @@ if _os.name in ("nt", "ce"):
_pointer_type_cache[None] = c_void_p _pointer_type_cache[None] = c_void_p
# functions
from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, cast
if sizeof(c_uint) == sizeof(c_void_p): if sizeof(c_uint) == sizeof(c_void_p):
c_size_t = c_uint c_size_t = c_uint
elif sizeof(c_ulong) == sizeof(c_void_p): elif sizeof(c_ulong) == sizeof(c_void_p):
c_size_t = c_ulong c_size_t = c_ulong
# functions
from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr
## void *memmove(void *, const void *, size_t); ## void *memmove(void *, const void *, size_t);
memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
## void *memset(void *, int, size_t) ## void *memset(void *, int, size_t)
memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr) memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
def PYFUNCTYPE(restype, *argtypes):
class CFunctionType(_CFuncPtr):
_argtypes_ = argtypes
_restype_ = restype
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
return CFunctionType
cast = PYFUNCTYPE(py_object, c_void_p, py_object)(_cast_addr)
_string_at = CFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) _string_at = CFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
def string_at(ptr, size=0): def string_at(ptr, size=0):
"""string_at(addr[, size]) -> string """string_at(addr[, size]) -> string

View File

@ -2,6 +2,7 @@ import sys, unittest, struct, math
from binascii import hexlify from binascii import hexlify
from ctypes import * from ctypes import *
from ctypes.test import is_resource_enabled
def bin(s): def bin(s):
return hexlify(buffer(s)).upper() return hexlify(buffer(s)).upper()
@ -149,7 +150,7 @@ class Test(unittest.TestCase):
self.failUnless(c_char.__ctype_le__ is c_char) self.failUnless(c_char.__ctype_le__ is c_char)
self.failUnless(c_char.__ctype_be__ is c_char) self.failUnless(c_char.__ctype_be__ is c_char)
def test_struct_fields(self): def test_struct_fields_1(self):
if sys.byteorder == "little": if sys.byteorder == "little":
base = BigEndianStructure base = BigEndianStructure
else: else:
@ -198,17 +199,20 @@ class Test(unittest.TestCase):
pass pass
self.assertRaises(TypeError, setattr, S, "_fields_", [("s", T)]) self.assertRaises(TypeError, setattr, S, "_fields_", [("s", T)])
# crashes on solaris with a core dump. def test_struct_fields_2(self):
def X_test_struct_fields(self): # standard packing in struct uses no alignment.
# So, we have to align using pad bytes.
#
# Unaligned accesses will crash Python (on those platforms that
# don't allow it, like sparc solaris).
if sys.byteorder == "little": if sys.byteorder == "little":
base = BigEndianStructure base = BigEndianStructure
fmt = ">bhid" fmt = ">bxhid"
else: else:
base = LittleEndianStructure base = LittleEndianStructure
fmt = "<bhid" fmt = "<bxhid"
class S(base): class S(base):
_pack_ = 1 # struct with '<' or '>' uses standard alignment.
_fields_ = [("b", c_byte), _fields_ = [("b", c_byte),
("h", c_short), ("h", c_short),
("i", c_int), ("i", c_int),
@ -218,5 +222,54 @@ class Test(unittest.TestCase):
s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14) s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
self.failUnlessEqual(bin(s1), bin(s2)) self.failUnlessEqual(bin(s1), bin(s2))
if is_resource_enabled("unaligned_access"):
def test_unaligned_nonnative_struct_fields(self):
if sys.byteorder == "little":
base = BigEndianStructure
fmt = ">b h xi xd"
else:
base = LittleEndianStructure
fmt = "<b h xi xd"
class S(base):
_pack_ = 1
_fields_ = [("b", c_byte),
("h", c_short),
("_1", c_byte),
("i", c_int),
("_2", c_byte),
("d", c_double)]
s1 = S(0x12, 0x1234, 0, 0x12345678, 0, 3.14)
s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
self.failUnlessEqual(bin(s1), bin(s2))
def test_unaligned_native_struct_fields(self):
if sys.byteorder == "little":
fmt = "<b h xi xd"
else:
base = LittleEndianStructure
fmt = ">b h xi xd"
class S(Structure):
_pack_ = 1
_fields_ = [("b", c_byte),
("h", c_short),
("_1", c_byte),
("i", c_int),
("_2", c_byte),
("d", c_double)]
s1 = S(0x12, 0x1234, 0, 0x12345678, 0, 3.14)
s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
self.failUnlessEqual(bin(s1), bin(s2))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -179,7 +179,7 @@ else:
def __getattr__(self, name): def __getattr__(self, name):
if name[:2] == '__' and name[-2:] == '__': if name[:2] == '__' and name[-2:] == '__':
raise AttributeError, name raise AttributeError, name
func = self._FuncPtr("s_" + name, self) func = self._FuncPtr(("s_" + name, self))
setattr(self, name, func) setattr(self, name, func)
return func return func

View File

@ -17,8 +17,11 @@ class LoaderTest(unittest.TestCase):
name = "libc.so" name = "libc.so"
elif sys.platform == "sunos5": elif sys.platform == "sunos5":
name = "libc.so" name = "libc.so"
elif sys.platform.startswith("netbsd"):
name = "libc.so"
else: else:
name = "libc.so.6" name = "libc.so.6"
## print (sys.platform, os.name)
cdll.load(name) cdll.load(name)
self.assertRaises(OSError, cdll.load, self.unknowndll) self.assertRaises(OSError, cdll.load, self.unknowndll)
@ -37,5 +40,31 @@ class LoaderTest(unittest.TestCase):
cdll.find(name) cdll.find(name)
self.assertRaises(OSError, cdll.find, self.unknowndll) self.assertRaises(OSError, cdll.find, self.unknowndll)
def test_load_library(self):
if os.name == "nt":
windll.load_library("kernel32").GetModuleHandleW
windll.LoadLibrary("kernel32").GetModuleHandleW
WinDLL("kernel32").GetModuleHandleW
elif os.name == "ce":
windll.load_library("coredll").GetModuleHandleW
windll.LoadLibrary("coredll").GetModuleHandleW
WinDLL("coredll").GetModuleHandleW
def test_load_ordinal_functions(self):
if os.name in ("nt", "ce"):
import _ctypes_test
dll = WinDLL(_ctypes_test.__file__)
# We load the same function both via ordinal and name
func_ord = dll[2]
func_name = dll.GetString
# addressof gets the address where the function pointer is stored
a_ord = addressof(func_ord)
a_name = addressof(func_name)
f_ord_addr = c_void_p.from_address(a_ord).value
f_name_addr = c_void_p.from_address(a_name).value
self.failUnlessEqual(hex(f_ord_addr), hex(f_name_addr))
self.failUnlessRaises(AttributeError, dll.__getitem__, 1234)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -20,5 +20,8 @@ class SizesTestCase(unittest.TestCase):
self.failUnlessEqual(8, sizeof(c_int64)) self.failUnlessEqual(8, sizeof(c_int64))
self.failUnlessEqual(8, sizeof(c_uint64)) self.failUnlessEqual(8, sizeof(c_uint64))
def test_size_t(self):
self.failUnlessEqual(sizeof(c_void_p), sizeof(c_size_t))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -2388,6 +2388,11 @@ static PPROC FindAddress(void *handle, char *name, PyObject *type)
address = (PPROC)GetProcAddress(handle, name); address = (PPROC)GetProcAddress(handle, name);
if (address) if (address)
return address; return address;
if (((size_t)name & ~0xFFFF) == 0) {
return NULL;
}
/* It should not happen that dict is NULL, but better be safe */ /* It should not happen that dict is NULL, but better be safe */
if (dict==NULL || dict->flags & FUNCFLAG_CDECL) if (dict==NULL || dict->flags & FUNCFLAG_CDECL)
return address; return address;
@ -2493,6 +2498,28 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags)
return 1; return 1;
} }
static int
_get_name(PyObject *obj, char **pname)
{
#ifdef MS_WIN32
if (PyInt_Check(obj) || PyLong_Check(obj)) {
/* We have to use MAKEINTRESOURCEA for Windows CE.
Works on Windows as well, of course.
*/
*pname = MAKEINTRESOURCEA(PyInt_AsUnsignedLongMask(obj) & 0xFFFF);
return 1;
}
#endif
if (PyString_Check(obj) || PyUnicode_Check(obj)) {
*pname = PyString_AsString(obj);
return pname ? 1 : 0;
}
PyErr_SetString(PyExc_TypeError,
"function name must be string or integer");
return 0;
}
static PyObject * static PyObject *
CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
{ {
@ -2504,7 +2531,7 @@ CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
void *handle; void *handle;
PyObject *paramflags = NULL; PyObject *paramflags = NULL;
if (!PyArg_ParseTuple(args, "sO|O", &name, &dll, &paramflags)) if (!PyArg_ParseTuple(args, "(O&O)|O", _get_name, &name, &dll, &paramflags))
return NULL; return NULL;
if (paramflags == Py_None) if (paramflags == Py_None)
paramflags = NULL; paramflags = NULL;
@ -2529,9 +2556,14 @@ CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
#ifdef MS_WIN32 #ifdef MS_WIN32
address = FindAddress(handle, name, (PyObject *)type); address = FindAddress(handle, name, (PyObject *)type);
if (!address) { if (!address) {
PyErr_Format(PyExc_AttributeError, if ((size_t)name & ~0xFFFF)
"function '%s' not found", PyErr_Format(PyExc_AttributeError,
name); "function '%s' not found",
name);
else
PyErr_Format(PyExc_AttributeError,
"function ordinal %d not found",
name);
return NULL; return NULL;
} }
#else #else
@ -2608,8 +2640,9 @@ CFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds)
"O" - must be a callable, creates a C callable function "O" - must be a callable, creates a C callable function
two or more argument forms (the third argument is a paramflags tuple) two or more argument forms (the third argument is a paramflags tuple)
"sO|O" - function name, dll object (with an integer handle) "(sO)|..." - (function name, dll object (with an integer handle)), paramflags
"is|O" - vtable index, method name, creates callable calling COM vtbl "(iO)|..." - (function ordinal, dll object (with an integer handle)), paramflags
"is|..." - vtable index, method name, creates callable calling COM vtbl
*/ */
static PyObject * static PyObject *
CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
@ -2622,14 +2655,13 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (PyTuple_GET_SIZE(args) == 0) if (PyTuple_GET_SIZE(args) == 0)
return GenericCData_new(type, args, kwds); return GenericCData_new(type, args, kwds);
/* Shouldn't the following better be done in __init__? */ if (1 <= PyTuple_GET_SIZE(args) && PyTuple_Check(PyTuple_GET_ITEM(args, 0)))
if (2 <= PyTuple_GET_SIZE(args)) {
#ifdef MS_WIN32
if (PyInt_Check(PyTuple_GET_ITEM(args, 0)))
return CFuncPtr_FromVtblIndex(type, args, kwds);
#endif
return CFuncPtr_FromDll(type, args, kwds); return CFuncPtr_FromDll(type, args, kwds);
}
#ifdef MS_WIN32
if (2 <= PyTuple_GET_SIZE(args) && PyInt_Check(PyTuple_GET_ITEM(args, 0)))
return CFuncPtr_FromVtblIndex(type, args, kwds);
#endif
if (1 == PyTuple_GET_SIZE(args) if (1 == PyTuple_GET_SIZE(args)
&& (PyInt_Check(PyTuple_GET_ITEM(args, 0)) && (PyInt_Check(PyTuple_GET_ITEM(args, 0))
@ -4351,6 +4383,42 @@ string_at(const char *ptr, Py_ssize_t size)
return PyString_FromStringAndSize(ptr, size); return PyString_FromStringAndSize(ptr, size);
} }
static int
cast_check_pointertype(PyObject *arg)
{
StgDictObject *dict;
if (PointerTypeObject_Check(arg))
return 1;
dict = PyType_stgdict(arg);
if (dict) {
if (PyString_Check(dict->proto)
&& (strchr("sPzUZXO", PyString_AS_STRING(dict->proto)[0]))) {
/* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */
return 1;
}
}
PyErr_Format(PyExc_TypeError,
"cast() argument 2 must be a pointer type, not %s",
PyType_Check(arg)
? ((PyTypeObject *)arg)->tp_name
: arg->ob_type->tp_name);
return 0;
}
static PyObject *
cast(void *ptr, PyObject *ctype)
{
CDataObject *result;
if (0 == cast_check_pointertype(ctype))
return NULL;
result = (CDataObject *)PyObject_CallFunctionObjArgs(ctype, NULL);
if (result == NULL)
return NULL;
/* Should we assert that result is a pointer type? */
memcpy(result->b_ptr, &ptr, sizeof(void *));
return (PyObject *)result;
}
#ifdef CTYPES_UNICODE #ifdef CTYPES_UNICODE
static PyObject * static PyObject *
@ -4486,6 +4554,7 @@ init_ctypes(void)
PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove)); PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove));
PyModule_AddObject(m, "_memset_addr", PyLong_FromVoidPtr(memset)); PyModule_AddObject(m, "_memset_addr", PyLong_FromVoidPtr(memset));
PyModule_AddObject(m, "_string_at_addr", PyLong_FromVoidPtr(string_at)); PyModule_AddObject(m, "_string_at_addr", PyLong_FromVoidPtr(string_at));
PyModule_AddObject(m, "_cast_addr", PyLong_FromVoidPtr(cast));
#ifdef CTYPES_UNICODE #ifdef CTYPES_UNICODE
PyModule_AddObject(m, "_wstring_at_addr", PyLong_FromVoidPtr(wstring_at)); PyModule_AddObject(m, "_wstring_at_addr", PyLong_FromVoidPtr(wstring_at));
#endif #endif

View File

@ -1423,71 +1423,7 @@ set_conversion_mode(PyObject *self, PyObject *args)
} }
#endif #endif
static char cast_doc[] =
"cast(cobject, ctype) -> ctype-instance\n\
\n\
Create an instance of ctype, and copy the internal memory buffer\n\
of cobject to the new instance. Should be used to cast one type\n\
of pointer to another type of pointer.\n\
Doesn't work correctly with ctypes integers.\n";
static int cast_check_pointertype(PyObject *arg, PyObject **pobj)
{
StgDictObject *dict;
if (PointerTypeObject_Check(arg)) {
*pobj = arg;
return 1;
}
dict = PyType_stgdict(arg);
if (dict) {
if (PyString_Check(dict->proto)
&& (strchr("sPzUZXO", PyString_AS_STRING(dict->proto)[0]))) {
/* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */
*pobj = arg;
return 1;
}
}
if (PyType_Check(arg)) {
PyErr_Format(PyExc_TypeError,
"cast() argument 2 must be a pointer type, not %s",
((PyTypeObject *)arg)->tp_name);
} else {
PyErr_Format(PyExc_TypeError,
"cast() argument 2 must be a pointer type, not a %s",
arg->ob_type->tp_name);
}
return 0;
}
static PyObject *cast(PyObject *self, PyObject *args)
{
PyObject *obj, *ctype;
struct argument a;
CDataObject *result;
/* We could and should allow array types for the second argument
also, but we cannot use the simple memcpy below for them. */
if (!PyArg_ParseTuple(args, "OO&:cast", &obj, &cast_check_pointertype, &ctype))
return NULL;
if (-1 == ConvParam(obj, 1, &a))
return NULL;
result = (CDataObject *)PyObject_CallFunctionObjArgs(ctype, NULL);
if (result == NULL) {
Py_XDECREF(a.keep);
return NULL;
}
// result->b_size
// a.ffi_type->size
memcpy(result->b_ptr, &a.value,
min(result->b_size, (int)a.ffi_type->size));
Py_XDECREF(a.keep);
return (PyObject *)result;
}
PyMethodDef module_methods[] = { PyMethodDef module_methods[] = {
{"cast", cast, METH_VARARGS, cast_doc},
#ifdef CTYPES_UNICODE #ifdef CTYPES_UNICODE
{"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