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
return self.__getitem__(name)
def __getitem__(self, name):
func = self._FuncPtr(name, self)
func.__name__ = name
setattr(self, name, func)
def __getitem__(self, name_or_ordinal):
func = self._FuncPtr((name_or_ordinal, self))
if not isinstance(name_or_ordinal, (int, long)):
func.__name__ = name_or_ordinal
setattr(self, name_or_ordinal, func)
return func
class PyDLL(CDLL):
@ -384,21 +385,29 @@ if _os.name in ("nt", "ce"):
_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):
c_size_t = c_uint
elif sizeof(c_ulong) == sizeof(c_void_p):
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);
memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
## void *memset(void *, int, size_t)
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)
def string_at(ptr, size=0):
"""string_at(addr[, size]) -> string

View File

@ -2,6 +2,7 @@ import sys, unittest, struct, math
from binascii import hexlify
from ctypes import *
from ctypes.test import is_resource_enabled
def bin(s):
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_be__ is c_char)
def test_struct_fields(self):
def test_struct_fields_1(self):
if sys.byteorder == "little":
base = BigEndianStructure
else:
@ -198,17 +199,20 @@ class Test(unittest.TestCase):
pass
self.assertRaises(TypeError, setattr, S, "_fields_", [("s", T)])
# crashes on solaris with a core dump.
def X_test_struct_fields(self):
def test_struct_fields_2(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":
base = BigEndianStructure
fmt = ">bhid"
fmt = ">bxhid"
else:
base = LittleEndianStructure
fmt = "<bhid"
fmt = "<bxhid"
class S(base):
_pack_ = 1 # struct with '<' or '>' uses standard alignment.
_fields_ = [("b", c_byte),
("h", c_short),
("i", c_int),
@ -218,5 +222,54 @@ class Test(unittest.TestCase):
s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
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__":
unittest.main()

View File

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

View File

@ -17,8 +17,11 @@ class LoaderTest(unittest.TestCase):
name = "libc.so"
elif sys.platform == "sunos5":
name = "libc.so"
elif sys.platform.startswith("netbsd"):
name = "libc.so"
else:
name = "libc.so.6"
## print (sys.platform, os.name)
cdll.load(name)
self.assertRaises(OSError, cdll.load, self.unknowndll)
@ -37,5 +40,31 @@ class LoaderTest(unittest.TestCase):
cdll.find(name)
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__":
unittest.main()

View File

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

View File

@ -2388,6 +2388,11 @@ static PPROC FindAddress(void *handle, char *name, PyObject *type)
address = (PPROC)GetProcAddress(handle, name);
if (address)
return address;
if (((size_t)name & ~0xFFFF) == 0) {
return NULL;
}
/* It should not happen that dict is NULL, but better be safe */
if (dict==NULL || dict->flags & FUNCFLAG_CDECL)
return address;
@ -2493,6 +2498,28 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags)
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 *
CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
@ -2504,7 +2531,7 @@ CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
void *handle;
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;
if (paramflags == Py_None)
paramflags = NULL;
@ -2529,9 +2556,14 @@ CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
#ifdef MS_WIN32
address = FindAddress(handle, name, (PyObject *)type);
if (!address) {
PyErr_Format(PyExc_AttributeError,
"function '%s' not found",
name);
if ((size_t)name & ~0xFFFF)
PyErr_Format(PyExc_AttributeError,
"function '%s' not found",
name);
else
PyErr_Format(PyExc_AttributeError,
"function ordinal %d not found",
name);
return NULL;
}
#else
@ -2608,8 +2640,9 @@ CFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds)
"O" - must be a callable, creates a C callable function
two or more argument forms (the third argument is a paramflags tuple)
"sO|O" - function name, dll object (with an integer handle)
"is|O" - vtable index, method name, creates callable calling COM vtbl
"(sO)|..." - (function name, dll object (with an integer handle)), paramflags
"(iO)|..." - (function ordinal, dll object (with an integer handle)), paramflags
"is|..." - vtable index, method name, creates callable calling COM vtbl
*/
static PyObject *
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)
return GenericCData_new(type, args, kwds);
/* Shouldn't the following better be done in __init__? */
if (2 <= PyTuple_GET_SIZE(args)) {
#ifdef MS_WIN32
if (PyInt_Check(PyTuple_GET_ITEM(args, 0)))
return CFuncPtr_FromVtblIndex(type, args, kwds);
#endif
if (1 <= PyTuple_GET_SIZE(args) && PyTuple_Check(PyTuple_GET_ITEM(args, 0)))
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)
&& (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);
}
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
static PyObject *
@ -4486,6 +4554,7 @@ init_ctypes(void)
PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove));
PyModule_AddObject(m, "_memset_addr", PyLong_FromVoidPtr(memset));
PyModule_AddObject(m, "_string_at_addr", PyLong_FromVoidPtr(string_at));
PyModule_AddObject(m, "_cast_addr", PyLong_FromVoidPtr(cast));
#ifdef CTYPES_UNICODE
PyModule_AddObject(m, "_wstring_at_addr", PyLong_FromVoidPtr(wstring_at));
#endif

View File

@ -1423,71 +1423,7 @@ set_conversion_mode(PyObject *self, PyObject *args)
}
#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[] = {
{"cast", cast, METH_VARARGS, cast_doc},
#ifdef CTYPES_UNICODE
{"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc},
#endif