[Python-checkins] r62368 - in ctypes/trunk/comtypes/comtypes: _comobject.py automation.py client/_events.py test/test_ie.py

thomas.heller python-checkins at python.org
Thu Apr 17 20:36:43 CEST 2008


Author: thomas.heller
Date: Thu Apr 17 20:36:42 2008
New Revision: 62368

Log:
More changes from upstream svn: Add support for implementing
(non-dual) dispinterfaces.


Modified:
   ctypes/trunk/comtypes/comtypes/_comobject.py
   ctypes/trunk/comtypes/comtypes/automation.py
   ctypes/trunk/comtypes/comtypes/client/_events.py
   ctypes/trunk/comtypes/comtypes/test/test_ie.py

Modified: ctypes/trunk/comtypes/comtypes/_comobject.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/_comobject.py	(original)
+++ ctypes/trunk/comtypes/comtypes/_comobject.py	Thu Apr 17 20:36:42 2008
@@ -18,6 +18,12 @@
 from comtypes.typeinfo import IProvideClassInfo, IProvideClassInfo2
 from comtypes import IPersist
 
+# so we don't have to import comtypes.automation
+DISPATCH_METHOD = 1
+DISPATCH_PROPERTYGET = 2
+DISPATCH_PROPERTYPUT = 4
+DISPATCH_PROPERTYPUTREF = 8
+
 class E_NotImplemented(Exception):
     """COM method is not implemented"""
 
@@ -77,10 +83,10 @@
     if code.co_varnames[1:2] == ("this",):
         return catch_errors(inst, mth, interface, mthname)
     dirflags = [f[0] for f in paramflags]
-    # An argument is [IN] if it is not [OUT] !
-    # This handles the case where no direction is defined in the IDL file.
-    # number of input arguments:
-    args_in = len([f for f in dirflags if (f & 2) == 0])
+    # An argument is an input arg either if flags are NOT set in the
+    # idl file, or if the flags contain 'in'. In other words, the
+    # direction flag is either exactly '0' or has the '1' bit set:
+    args_in = len([f for f in dirflags if (f == 0) or (f & 1)])
     # number of output arguments:
     args_out = len([f for f in dirflags if f & 2])
     if args_in != code.co_argcount - 1:
@@ -92,9 +98,12 @@
     clsid = getattr(inst, "_reg_clsid_", None)
     def wrapper(this, *args):
         outargs = args[len(args)-args_out:]
-        for a in outargs:
-            if not a:
-                return E_POINTER
+        # Method implementations could check for and return E_POINTER
+        # themselves.  Or an error will be raised when
+        # 'outargs[i][0] = value' is executed.
+##        for a in outargs:
+##            if not a:
+##                return E_POINTER
         try:
             result = mth(*args[:args_in])
             if args_out == 1:
@@ -136,7 +145,7 @@
         # map lower case names to names with correct spelling.
         self.names = dict([(n.lower(), n) for n in dir(inst)])
 
-    def get_impl(self, instance, interface, mthname, paramflags, idlflags):
+    def get_impl(self, interface, mthname, paramflags, idlflags):
         mth = self.find_impl(interface, mthname, paramflags, idlflags)
         if mth is None:
             return _do_implement(interface.__name__, mthname)
@@ -226,6 +235,7 @@
             return self
         if hasattr(self, "_com_interfaces_"):
             self.__prepare_comobject()
+        self._dispimpl_ = {}
         return self
 
     def __prepare_comobject(self):
@@ -278,7 +288,7 @@
                 restype, mthname, argtypes, paramflags, idlflags, helptext = m
                 proto = WINFUNCTYPE(restype, c_void_p, *argtypes)
                 fields.append((mthname, proto))
-                mth = finder.get_impl(self, interface, mthname, paramflags, idlflags)
+                mth = finder.get_impl(interface, mthname, paramflags, idlflags)
                 methods.append(proto(mth))
         Vtbl = _create_vtbl_type(tuple(fields), itf)
         vtbl = Vtbl(*methods)
@@ -391,44 +401,112 @@
         else:
             return 1
 
-    def IDispatch_GetTypeInfo(self, itinfo, lcid):
+    def IDispatch_GetTypeInfo(self, this, itinfo, lcid, ptinfo):
         if itinfo != 0:
-            raise WindowsError(DISP_E_BADINDEX)
+            return DISP_E_BADINDEX
         try:
-            self.__typelib
+            ptinfo[0] = self.__typeinfo
+            return S_OK
         except AttributeError:
-            raise WindowsError(E_NOTIMPL)
-        else:
-            return self.__typeinfo
+            return E_NOTIMPL
 
     def IDispatch_GetIDsOfNames(self, this, riid, rgszNames, cNames, lcid, rgDispId):
-        # Use windll to let DispGetIDsOfNames return a HRESULT instead
-        # of raising an error:
+        # This call uses windll instead of oledll so that a failed
+        # call to DispGetIDsOfNames will return a HRESULT instead of
+        # raising an error.
         try:
-            self.__typeinfo
+            tinfo = self.__typeinfo
         except AttributeError:
             return E_NOTIMPL
-        return windll.oleaut32.DispGetIDsOfNames(self.__typeinfo,
+        return windll.oleaut32.DispGetIDsOfNames(tinfo,
                                                  rgszNames, cNames, rgDispId)
 
     def IDispatch_Invoke(self, this, dispIdMember, riid, lcid, wFlags,
                          pDispParams, pVarResult, pExcepInfo, puArgErr):
+        interface = self._com_interfaces_[0]
         try:
-            self.__typeinfo
+            # We cannot use DispInvoke on pure dispinterfaces; it
+            # would return DISP_E_MEMBERNOTFOUND.
+            #
+            # XXX Could we check for !TYPEFLAG_DUAL and TYPEFLAG_FDISPATCHABLE instead?
+            methods = interface._disp_methods_
         except AttributeError:
-            # Hm, we pretend to implement IDispatch, but have no
-            # typeinfo, and so cannot fulfill the contract.  Should we
-            # better return E_NOTIMPL or DISP_E_MEMBERNOTFOUND?  Some
-            # clients call IDispatch_Invoke with 'known' DISPID_...'
-            # values, without going through GetIDsOfNames first.
+            try:
+                tinfo = self.__typeinfo
+            except AttributeError:
+                # Hm, we pretend to implement IDispatch, but have no
+                # typeinfo, and so cannot fulfill the contract.  Should we
+                # better return E_NOTIMPL or DISP_E_MEMBERNOTFOUND?  Some
+                # clients call IDispatch_Invoke with 'known' DISPID_...'
+                # values, without going through GetIDsOfNames first.
+                return DISP_E_MEMBERNOTFOUND
+            # This call uses windll instead of oledll so that a failed
+            # call to DispInvoke will return a HRESULT instead of raising
+            # an error.
+            ptr = self._com_pointers_[interface._iid_]
+            return windll.oleaut32.DispInvoke(ptr,
+                                              tinfo,
+                                              dispIdMember, wFlags, pDispParams,
+                                              pVarResult, pExcepInfo, puArgErr)
+
+        impl = self._find_impl(dispIdMember, wFlags, bool(pVarResult))
+        if isinstance(impl, int):
+            return impl
+
+        params = pDispParams[0]
+        args = [params.rgvarg[i].value for i in range(params.cArgs)[::-1]]
+
+        if pVarResult:
+            args += [pVarResult]
+
+        return impl(this, *args)
+
+    def _find_impl(self, dispid, wFlags, expects_result,
+                   finder=None):
+        try:
+            return self._dispimpl_[(dispid, wFlags)]
+        except KeyError:
+            pass
+
+        interface = self._com_interfaces_[0]
+
+        methods = interface._disp_methods_
+        # XXX This uses a linear search
+        descr = [m for m in methods
+                 if m[2][0] == dispid]
+        if not descr:
             return DISP_E_MEMBERNOTFOUND
-        impl = self._com_pointers_[self._com_interfaces_[0]._iid_]
-        # Use windll to let DispInvoke return a HRESULT instead
-        # of raising an error:
-        return windll.oleaut32.DispInvoke(impl,
-                                          self.__typeinfo,
-                                          dispIdMember, wFlags, pDispParams,
-                                          pVarResult, pExcepInfo, puArgErr)
+        disptype, name, idlflags, restype, argspec = descr[0]
+
+        if disptype == "DISPMETHOD":
+            if (wFlags & DISPATCH_METHOD) == 0:
+                return DISP_E_MEMBERNOTFOUND
+
+        elif disptype == "DISPPROPERTY":
+
+            if wFlags & DISPATCH_PROPERTYGET:
+                name = "_get_" + name
+            elif wFlags & DISPATCH_PROPERTYPUT:
+                name = "_set_" + name
+            elif wFlags & DISPATCH_PROPERTYPUTREF:
+                name = "_setref_" + name
+            else:
+                return DISP_E_MEMBERNOTFOUND
+
+        else:
+            # this should not happen at all: it is a bug in comtypes
+            return E_FAIL
+
+        from comtypes import _encode_idl
+        paramflags = [(_encode_idl(m[0]),) + m[1:] for m in argspec]
+        if expects_result:
+            paramflags += [[2]]
+            
+        if finder is None:
+            finder = _MethodFinder(self)
+        impl = finder.get_impl(interface, name, paramflags, [])
+        self._dispimpl_[(dispid, wFlags)] = impl
+        return impl
 
     ################################################################
     # IPersist interface

Modified: ctypes/trunk/comtypes/comtypes/automation.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/automation.py	(original)
+++ ctypes/trunk/comtypes/comtypes/automation.py	Thu Apr 17 20:36:42 2008
@@ -168,6 +168,14 @@
         return cls(value)
     from_param = classmethod(from_param)
 
+    def _set_byref(self, value):
+        # This method allows to change the value of a
+        # (VT_BYREF|VT_xxx) variant in place.
+        if not self.vt & VT_BYREF:
+            raise TypeError("set_byref requires a VT_BYREF VARIANT instance")
+        typ = _vartype_to_ctype[self.vt & ~VT_BYREF]
+        cast(self._.c_void_p, POINTER(typ))[0] = value
+
     # see also c:/sf/pywin32/com/win32com/src/oleargs.cpp 54
     def _set_value(self, value):
         _VariantClear(byref(self))

Modified: ctypes/trunk/comtypes/comtypes/client/_events.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/client/_events.py	(original)
+++ ctypes/trunk/comtypes/comtypes/client/_events.py	Thu Apr 17 20:36:42 2008
@@ -185,10 +185,27 @@
     rcv._com_pointers_[interface._iid_] = rcv._com_pointers_[comtypes.automation.IDispatch._iid_]
     return rcv
 
+# New implementation of GetDispEventReceiver; not yet enabled.
+#
+# The 'this'-calling convention seems to work, the 'this-less'
+# convention not yet (severe changes to VARIANT are required).
+def X_GetDispEventReceiver(interface, sink):
+
+    class Sink(comtypes.COMObject):
+        _com_interfaces_ = [interface]
+
+        def _find_impl(self, dispid, wFlags, expects_result):
+            from comtypes._comobject import _MethodFinder
+            return super(Sink, self)._find_impl(dispid, wFlags, expects_result,
+                                                finder=_MethodFinder(sink))
+
+    return Sink()
+
 def GetCustomEventReceiver(interface, sink):
     class EventReceiver(comtypes.COMObject):
         _com_interfaces_ = [interface]
 
+    # XXX should use a mechanism similar to _find_impl() above.
     for itf in interface.mro()[:-2]: # skip object and IUnknown
         for info in itf._methods_:
             restype, name, argtypes, paramflags, idlflags, docstring = info

Modified: ctypes/trunk/comtypes/comtypes/test/test_ie.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/test/test_ie.py	(original)
+++ ctypes/trunk/comtypes/comtypes/test/test_ie.py	Thu Apr 17 20:36:42 2008
@@ -78,8 +78,9 @@
         ie.Visible = False
         ie.Quit()
 
-        self.failUnlessEqual(sink._events[:2], ['OnVisible', 'BeforeNavigate2'])
-        self.failUnlessEqual(sink._events[-1], 'OnVisible')
+        self.failUnlessEqual(sink._events, ['OnVisible', 'BeforeNavigate2',
+                                            'NavigateComplete2', 'DocumentComplete',
+                                            'OnVisible'])
 
         del ie
         del conn


More information about the Python-checkins mailing list