Author: Antonio Cuni <anto.c...@gmail.com> Branch: Changeset: r45354:634a87b2adbf Date: 2011-07-05 11:50 +0200 http://bitbucket.org/pypy/pypy/changeset/634a87b2adbf/
Log: (theller) fix issue743, by Thomas Heller: https://bugs.pypy.org/issue743 I changed test_commethods.py from unittest to py.test. However, I don't have a windows box around where to try it, so I cross the fingers and I commit it blindly. Please review :-) diff --git a/lib_pypy/_ctypes/__init__.py b/lib_pypy/_ctypes/__init__.py --- a/lib_pypy/_ctypes/__init__.py +++ b/lib_pypy/_ctypes/__init__.py @@ -18,7 +18,16 @@ if _os.name in ("nt", "ce"): from _rawffi import FormatError from _rawffi import check_HRESULT as _check_HRESULT - CopyComPointer = None # XXX + + def CopyComPointer(src, dst): + from ctypes import c_void_p, cast + if src: + hr = src[0][0].AddRef(src) + if hr & 0x80000000: + return hr + dst[0] = cast(src, c_void_p).value + return 0 + LoadLibrary = dlopen from _rawffi import FUNCFLAG_STDCALL, FUNCFLAG_CDECL, FUNCFLAG_PYTHONAPI diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py --- a/lib_pypy/_ctypes/basics.py +++ b/lib_pypy/_ctypes/basics.py @@ -139,7 +139,10 @@ return buffer(self._buffer) def _get_b_base(self): - return self._base + try: + return self._base + except AttributeError: + return None _b_base_ = property(_get_b_base) _b_needsfree_ = False @@ -218,5 +221,7 @@ 'z' : _ffi.types.void_p, 'O' : _ffi.types.void_p, 'Z' : _ffi.types.void_p, + 'X' : _ffi.types.void_p, + 'v' : _ffi.types.sshort, } diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py --- a/lib_pypy/_ctypes/function.py +++ b/lib_pypy/_ctypes/function.py @@ -322,20 +322,18 @@ RuntimeWarning, stacklevel=2) if self._com_index: - assert False, 'TODO2' from ctypes import cast, c_void_p, POINTER if not args: raise ValueError( "native COM method call without 'this' parameter" ) - thisarg = cast(args[0], POINTER(POINTER(c_void_p))).contents - argtypes = [c_void_p] + list(argtypes) - args = list(args) - args[0] = args[0].value + thisarg = cast(args[0], POINTER(POINTER(c_void_p))) + newargs, argtypes, outargs = self._convert_args(argtypes, args[1:], kwargs) + newargs.insert(0, args[0].value) + argtypes.insert(0, c_void_p) else: thisarg = None - - newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs) + newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs) funcptr = self._getfuncptr(argtypes, self._restype_, thisarg) result = self._call_funcptr(funcptr, *newargs) @@ -343,6 +341,11 @@ if not outargs: return result + + simple_cdata = type(c_void_p()).__bases__[0] + outargs = [x.value if type(x).__bases__[0] is simple_cdata else x + for x in outargs] + if len(outargs) == 1: return outargs[0] return tuple(outargs) @@ -398,10 +401,10 @@ # extract the address from the object's virtual table if not thisarg: raise ValueError("COM method call without VTable") - ptr = thisarg[self._com_index - 0x1000] - argshapes = [arg._ffiargshape for arg in argtypes] - resshape = restype._ffiargshape - return _rawffi.FuncPtr(ptr, argshapes, resshape, self._flags_) + ptr = thisarg[0][self._com_index - 0x1000] + ffiargs = [argtype.get_ffi_argtype() for argtype in argtypes] + ffires = restype.get_ffi_argtype() + return _ffi.FuncPtr.fromaddr(ptr, '', ffiargs, ffires) cdll = self.dll._handle try: @@ -468,11 +471,7 @@ newargtypes = [] total = len(args) paramflags = self._paramflags - - if self._com_index: - inargs_idx = 1 - else: - inargs_idx = 0 + inargs_idx = 0 if not paramflags and total < len(argtypes): raise TypeError("not enough arguments") @@ -587,13 +586,7 @@ retval = None - if self._com_index: - if resbuffer[0] & 0x80000000: - raise get_com_error(resbuffer[0], - self._com_iid, argsandobjs[0]) - else: - retval = int(resbuffer[0]) - elif restype is not None: + if restype is not None: checker = getattr(self.restype, '_check_retval_', None) if checker: val = restype(result) @@ -601,7 +594,13 @@ # classes defining a new type, and their subclasses if '_type_' in restype.__dict__: val = val.value - retval = checker(val) + # XXX Raise a COMError when restype is HRESULT and + # checker(val) fails. How to check for restype == HRESULT? + if self._com_index: + if result & 0x80000000: + raise get_com_error(result, None, None) + else: + retval = checker(val) elif not isinstance(restype, _CDataMeta): retval = restype(result) else: diff --git a/lib_pypy/_ctypes/primitive.py b/lib_pypy/_ctypes/primitive.py --- a/lib_pypy/_ctypes/primitive.py +++ b/lib_pypy/_ctypes/primitive.py @@ -216,10 +216,15 @@ result.value = property(_getvalue, _setvalue) elif tp == 'X': - from ctypes import windll - SysAllocStringLen = windll.oleaut32.SysAllocStringLen - SysStringLen = windll.oleaut32.SysStringLen - SysFreeString = windll.oleaut32.SysFreeString + from ctypes import WinDLL + # Use WinDLL("oleaut32") instead of windll.oleaut32 + # because the latter is a shared (cached) object; and + # other code may set their own restypes. We need out own + # restype here. + oleaut32 = WinDLL("oleaut32") + SysAllocStringLen = oleaut32.SysAllocStringLen + SysStringLen = oleaut32.SysStringLen + SysFreeString = oleaut32.SysFreeString def _getvalue(self): addr = self._buffer[0] if addr == 0: diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py b/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py new file mode 100644 --- /dev/null +++ b/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py @@ -0,0 +1,82 @@ +# unittest for SOME ctypes com function calls. +# Can't resist from implementing some kind of mini-comtypes +# theller ;-) + +import py +import sys +if sys.platform != "win32": + py.test.skip('windows only test') + +import ctypes, new, unittest +from ctypes.wintypes import HRESULT +from _ctypes import COMError + +oleaut32 = ctypes.OleDLL("oleaut32") + +class UnboundMethod(object): + def __init__(self, func, index, name): + self.func = func + self.index = index + self.name = name + self.__doc__ = func.__doc__ + + def __repr__(self): + return "<Unbound COM method index %d: %s at %x>" % (self.index, self.name, id(self)) + + def __get__(self, instance, owner): + if instance is None: + return self + return new.instancemethod(self.func, instance, owner) + +def commethod(index, restype, *argtypes): + """A decorator that generates COM methods. The decorated function + itself is not used except for it's name.""" + def make_commethod(func): + comfunc = ctypes.WINFUNCTYPE(restype, *argtypes)(index, func.__name__) + comfunc.__name__ = func.__name__ + comfunc.__doc__ = func.__doc__ + return UnboundMethod(comfunc, index, func.__name__) + return make_commethod + +class ICreateTypeLib2(ctypes.c_void_p): + + @commethod(1, ctypes.c_long) + def AddRef(self): + pass + + @commethod(2, ctypes.c_long) + def Release(self): + pass + + @commethod(4, HRESULT, ctypes.c_wchar_p) + def SetName(self): + """Set the name of the library.""" + + @commethod(12, HRESULT) + def SaveAllChanges(self): + pass + + +CreateTypeLib2 = oleaut32.CreateTypeLib2 +CreateTypeLib2.argtypes = (ctypes.c_int, ctypes.c_wchar_p, ctypes.POINTER(ICreateTypeLib2)) + +################################################################ + +def test_basic_comtypes(): + punk = ICreateTypeLib2() + hr = CreateTypeLib2(0, "foobar.tlb", punk) + assert hr == 0 + + assert 2 == punk.AddRef() + assert 3 == punk.AddRef() + assert 4 == punk.AddRef() + + punk.SetName("TypeLib_ByPYPY") + py.test.raises(COMError, lambda: punk.SetName(None)) + + # This would save the typelib to disk. + ## punk.SaveAllChanges() + + assert 3 == punk.Release() + assert 2 == punk.Release() + assert 1 == punk.Release() _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit