[Python-checkins] r62366 - in ctypes/trunk/comtypes: ChangeLog comtypes/__init__.py comtypes/_comobject.py comtypes/client/_events.py comtypes/server/automation.py comtypes/server/connectionpoints.py comtypes/server/register.py comtypes/test/TestComServer.py comtypes/test/runtests.py comtypes/test/test_collections.py comtypes/test/test_createwrappers.py

thomas.heller python-checkins at python.org
Thu Apr 17 19:56:29 CEST 2008


Author: thomas.heller
Date: Thu Apr 17 19:56:27 2008
New Revision: 62366

Log:
Merge in a lot of changes from the private upstream repository (rev
26394); mainly much better support for implementing comtypes servers.


Modified:
   ctypes/trunk/comtypes/ChangeLog
   ctypes/trunk/comtypes/comtypes/__init__.py
   ctypes/trunk/comtypes/comtypes/_comobject.py
   ctypes/trunk/comtypes/comtypes/client/_events.py
   ctypes/trunk/comtypes/comtypes/server/automation.py
   ctypes/trunk/comtypes/comtypes/server/connectionpoints.py
   ctypes/trunk/comtypes/comtypes/server/register.py
   ctypes/trunk/comtypes/comtypes/test/TestComServer.py
   ctypes/trunk/comtypes/comtypes/test/runtests.py
   ctypes/trunk/comtypes/comtypes/test/test_collections.py
   ctypes/trunk/comtypes/comtypes/test/test_createwrappers.py

Modified: ctypes/trunk/comtypes/ChangeLog
==============================================================================
--- ctypes/trunk/comtypes/ChangeLog	(original)
+++ ctypes/trunk/comtypes/ChangeLog	Thu Apr 17 19:56:27 2008
@@ -1,3 +1,9 @@
+2008-04-17  Thomas Heller  <theller at python.net>
+
+	* Merge in a lot of changes from the private upstream repository
+	26394; mainly much better support for implementing comtypes
+	servers.
+
 2008-04-09  Thomas Heller  <theller at python.net>
 
 	* Bump version number to 0.4.3a

Modified: ctypes/trunk/comtypes/comtypes/__init__.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/__init__.py	(original)
+++ ctypes/trunk/comtypes/comtypes/__init__.py	Thu Apr 17 19:56:27 2008
@@ -28,6 +28,13 @@
     monkeypatch_COMError()
     del monkeypatch_COMError
 
+class ReturnHRESULT(Exception):
+    """ReturnHRESULT(hresult, text)
+
+    Return a hresult code from a COM method implementation
+    without logging an error.
+    """
+
 import logging
 logger = logging.getLogger(__name__)
 

Modified: ctypes/trunk/comtypes/comtypes/_comobject.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/_comobject.py	(original)
+++ ctypes/trunk/comtypes/comtypes/_comobject.py	Thu Apr 17 19:56:27 2008
@@ -2,48 +2,133 @@
 from comtypes.hresult import *
 
 import os
+import new
 import logging
 logger = logging.getLogger(__name__)
 _debug = logger.debug
 _warning = logger.warning
+_error = logger.error
 
 ################################################################
 # COM object implementation
 from _ctypes import CopyComPointer
 
-def prepare_comobject(inst):
-    # When a CoClass instance is created, COM pointers to all
-    # interfaces are created.  Also, the CoClass must be kept alive as
-    # until the COM reference count drops to zero, even if no Python
-    # code keeps a reference to the object.
-    #
-    # The _com_pointers_ instance variable maps string interface iids
-    # to C compatible COM pointers.
-    inst._com_pointers_ = {}
-    # COM refcount starts at zero.
-    inst._refcnt = c_long(0)
-    for itf in inst._com_interfaces_[::-1]:
-        make_interface_pointer(inst, itf)
+from comtypes import COMError, ReturnHRESULT
+from comtypes.errorinfo import ISupportErrorInfo, ReportException, ReportError
+from comtypes.typeinfo import IProvideClassInfo, IProvideClassInfo2
+from comtypes import IPersist
+
+class E_NotImplemented(Exception):
+    """COM method is not implemented"""
+
+def HRESULT_FROM_WIN32(errcode):
+    "Convert a Windows error code into a HRESULT value."
+    if errcode is None:
+        return 0x80000000
+    if errcode & 0x80000000:
+        return errcode
+    return (errcode & 0xFFFF) | 0x80070000
+
+def winerror(exc):
+    """Return the windows error code from a WindowsError or COMError
+    instance."""
+    try:
+        code = exc[0]
+        if isinstance(code, (int, long)):
+            return code
+    except IndexError:
+        pass
+    # Sometimes, a WindowsError instance has no error code.  An access
+    # violation raised by ctypes has only text, for example.  In this
+    # cases we return a generic error code.
+    return E_FAIL
 
-from comtypes.errorinfo import ReportException
+def _do_implement(interface_name, method_name):
+    def _not_implemented(*args):
+        """Return E_NOTIMPL because the method is not implemented."""
+        _debug("unimplemented method %s_%s called", interface_name, method_name)
+        return E_NOTIMPL
+    return _not_implemented
 
-def catch_errors(obj, mth, interface):
-    iid = interface._iid_
+def catch_errors(obj, mth, interface, mthname):
     clsid = getattr(obj, "_reg_clsid_", None)
     def func(*args, **kw):
         try:
             return mth(*args, **kw)
-        except Exception:
-            _warning("%s", interface, exc_info=True)
-            return ReportException(E_FAIL, iid, clsid=clsid)
+        except ReturnHRESULT, (hresult, text):
+            return ReportError(text, iid=interface._iid_, clsid=clsid, hresult=hresult)
+        except (COMError, WindowsError), details:
+            _error("Exception in %s.%s implementation:", interface.__name__, mthname, exc_info=True)
+            return HRESULT_FROM_WIN32(winerror(details))
+        except E_NotImplemented:
+            _warning("Unimplemented method %s.%s called", interface.__name__, mthname)
+            return E_NOTIMPL
+        except:
+            _error("Exception in %s.%s implementation:", interface.__name__, mthname, exc_info=True)
+            return ReportException(E_FAIL, interface._iid_, clsid=clsid)
     return func
 
-def _do_implement(interface_name, method_name):
-    def _not_implemented(*args):
-        """Return E_NOTIMPL because the method is not implemented."""
-        _debug("unimplemented method %s_%s called", interface_name, method_name)
-        return E_NOTIMPL
-    return _not_implemented
+################################################################
+
+def hack(inst, mth, paramflags, interface, mthname):
+    if paramflags is None:
+        return catch_errors(inst, mth, interface, mthname)
+    code = mth.func_code
+    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])
+    # number of output arguments:
+    args_out = len([f for f in dirflags if f & 2])
+    if args_in != code.co_argcount - 1:
+        return catch_errors(inst, mth, interface, mthname)
+    # This code assumes that input args are always first, and output
+    # args are always last.  Have to check with the IDL docs if this
+    # is always correct.
+
+    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
+        try:
+            result = mth(*args[:args_in])
+            if args_out == 1:
+                outargs[0][0] = result
+            elif args_out != 0:
+                if len(result) != args_out:
+                    raise ValueError("Method should have returned a %s-tuple" % args_out)
+                for i, value in enumerate(result):
+                    outargs[i][0] = value
+        except ReturnHRESULT, (hresult, text):
+            return ReportError(text, iid=interface._iid_, clsid=clsid, hresult=hresult)
+        except COMError, (hr, text, details):
+            _error("Exception in %s.%s implementation:", interface.__name__, mthname, exc_info=True)
+            try:
+                descr, source, helpfile, helpcontext, progid = details
+            except (ValueError, TypeError):
+                msg = str(details)
+            else:
+                msg = "%s: %s" % (source, descr)
+            hr = HRESULT_FROM_WIN32(hr)
+            return ReportError(msg, iid=interface._iid_, clsid=clsid, hresult=hr)
+        except WindowsError, details:
+            _error("Exception in %s.%s implementation:", interface.__name__, mthname, exc_info=True)
+            hr = HRESULT_FROM_WIN32(winerror(details))
+            return ReportException(hr, interface._iid_, clsid=clsid)
+        except E_NotImplemented:
+            _warning("Unimplemented method %s.%s called", interface.__name__, mthname)
+            return E_NOTIMPL
+        except:
+            _error("Exception in %s.%s implementation:", interface.__name__, mthname, exc_info=True)
+            return ReportException(E_FAIL, interface._iid_, clsid=clsid)
+        return S_OK
+
+    return wrapper
 
 class _MethodFinder(object):
     def __init__(self, inst):
@@ -51,44 +136,61 @@
         # map lower case names to names with correct spelling.
         self.names = dict([(n.lower(), n) for n in dir(inst)])
 
-    def get_impl(self, interface, mthname):
+    def get_impl(self, instance, interface, mthname, paramflags, idlflags):
+        mth = self.find_impl(interface, mthname, paramflags, idlflags)
+        if mth is None:
+            return _do_implement(interface.__name__, mthname)
+        return hack(self.inst, mth, paramflags, interface, mthname)
+
+    def find_impl(self, interface, mthname, paramflags, idlflags):
         fq_name = "%s_%s" % (interface.__name__, mthname)
         if interface._case_insensitive_:
             mthname = self.names.get(mthname.lower(), mthname)
             fq_name = self.names.get(fq_name.lower(), fq_name)
+
         try:
-            # try the simple name, like 'QueryInterface'
-            return getattr(self.inst, mthname)
+            # qualified name, like 'IUnknown_QueryInterface'
+            return getattr(self.inst, fq_name)
         except AttributeError:
             pass
         try:
-            # qualified name, like 'IUnknown_QueryInterface'
-            return getattr(self.inst, fq_name)
+            # simple name, like 'QueryInterface'
+            return getattr(self.inst, mthname)
         except AttributeError:
-            # use method that returns E_NOTIMPL when called.
-            _debug("%r: %s.%s not implemented", self.inst, interface.__name__, mthname)
-            return _do_implement(interface.__name__, mthname)
+            pass
+        propname = mthname[5:]
+        if interface._case_insensitive_:
+            propname = self.names.get(propname.lower(), propname)
+        # propput and propget is done with 'normal' attribute access,
+        # but only for COM properties that do not take additional
+        # arguments:
+
+        if "propget" in idlflags and len(paramflags) == 1:
+            return self.getter(propname)
+        if "propput" in idlflags and len(paramflags) == 1:
+            return self.setter(propname)
+        _debug("%r: %s.%s not implemented", self.inst, interface.__name__, mthname)
+        return None
+
+    def setter(self, propname):
+        #
+        def set(self, value):
+            try:
+                # XXX this may not be correct is the object implements
+                # _get_PropName but not _set_PropName
+                setattr(self, propname, value)
+            except AttributeError:
+                raise E_NotImplemented()
+        return new.instancemethod(set, self.inst, type(self.inst))
 
-def make_interface_pointer(inst, itf,
-                           _debug=_debug):
-    methods = [] # method implementations
-    fields = [] # (name, prototype) for virtual function table
-    iids = [] # interface identifiers.
-    # iterate over interface inheritance in reverse order to build the
-    # virtual function table, and leave out the 'object' base class.
-    finder = _MethodFinder(inst)
-    for interface in itf.__mro__[-2::-1]:
-        iids.append(interface._iid_)
-        for m in interface._methods_:
-            restype, mthname, argtypes, paramflags, idlflags, helptext = m
-            proto = WINFUNCTYPE(restype, c_void_p, *argtypes)
-            fields.append((mthname, proto))
-            mth = finder.get_impl(interface, mthname)
-            methods.append(proto(mth))
-    Vtbl = _create_vtbl_type(tuple(fields), itf)
-    vtbl = Vtbl(*methods)
-    for iid in iids:
-        inst._com_pointers_[iid] = pointer(pointer(vtbl))
+    def getter(self, propname):
+        #
+        def get(self):
+            try:
+                return getattr(self, propname)
+            except AttributeError:
+                raise E_NotImplemented()
+        return new.instancemethod(get, self.inst, type(self.inst))
 
 def _create_vtbl_type(fields, itf):
     try:
@@ -109,39 +211,80 @@
     _InterlockedIncrement = windll.coredll.InterlockedIncrement
     _InterlockedDecrement = windll.coredll.InterlockedDecrement
 else:
-    try:
-        _InterlockedIncrement = windll.kernel32.InterlockedIncrement
-        _InterlockedDecrement = windll.kernel32.InterlockedDecrement
-    except AttributeError:
-        import thread
-        _lock = thread.allocate_lock()
-
-        def _InterlockedIncrement(obj):
-            _lock.acquire()
-            result = obj._obj.value = obj._obj.value + 1
-            _lock.release()
-            return result
-
-        def _InterlockedDecrement(obj):
-            _lock.acquire()
-            result = obj._obj.value = obj._obj.value - 1
-            _lock.release()
-            return result
+    _InterlockedIncrement = windll.kernel32.InterlockedIncrement
+    _InterlockedDecrement = windll.kernel32.InterlockedDecrement
 
 class COMObject(object):
     _instances_ = {}
     _factory = None
 
     def __new__(cls, *args, **kw):
-        self = super(COMObject, cls).__new__(cls)
+        self = super(COMObject, cls).__new__(cls, *args, **kw)
         if isinstance(self, c_void_p):
             # We build the VTables only for direct instances of
             # CoClass, not for POINTERs to CoClass.
             return self
         if hasattr(self, "_com_interfaces_"):
-            prepare_comobject(self)
+            self.__prepare_comobject()
         return self
 
+    def __prepare_comobject(self):
+        # When a CoClass instance is created, COM pointers to all
+        # interfaces are created.  Also, the CoClass must be kept alive as
+        # until the COM reference count drops to zero, even if no Python
+        # code keeps a reference to the object.
+        #
+        # The _com_pointers_ instance variable maps string interface iids
+        # to C compatible COM pointers.
+        self._com_pointers_ = {}
+        # COM refcount starts at zero.
+        self._refcnt = c_long(0)
+
+        # Some interfaces have a default implementation in COMObject:
+        # - ISupportErrorInfo
+        # - IPersist (if the subclass has a _reg_clsid_ attribute)
+        # - IProvideClassInfo (if the subclass has a _reg_clsid_ attribute)
+        # - IProvideClassInfo2 (if the subclass has a _outgoing_interfaces_ attribute)
+        #
+        # Add these if they are not listed in _com_interfaces_.
+        interfaces = tuple(self._com_interfaces_)
+        if ISupportErrorInfo not in interfaces:
+            interfaces += (ISupportErrorInfo,)
+        if hasattr(self, "_reg_typelib_"):
+            from comtypes.typeinfo import LoadRegTypeLib
+            self._COMObject__typelib = LoadRegTypeLib(*self._reg_typelib_)
+            if hasattr(self, "_reg_clsid_"):
+                if IProvideClassInfo not in interfaces:
+                    interfaces += (IProvideClassInfo,)
+                if hasattr(self, "_outgoing_interfaces_") and \
+                   IProvideClassInfo2 not in interfaces:
+                    interfaces += (IProvideClassInfo2,)
+        if hasattr(self, "_reg_clsid_"):
+                if IPersist not in interfaces:
+                    interfaces += (IPersist,)
+        for itf in interfaces[::-1]:
+            self.__make_interface_pointer(itf)
+
+    def __make_interface_pointer(self, itf):
+        methods = [] # method implementations
+        fields = [] # (name, prototype) for virtual function table
+        iids = [] # interface identifiers.
+        # iterate over interface inheritance in reverse order to build the
+        # virtual function table, and leave out the 'object' base class.
+        finder = _MethodFinder(self)
+        for interface in itf.__mro__[-2::-1]:
+            iids.append(interface._iid_)
+            for m in interface._methods_:
+                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)
+                methods.append(proto(mth))
+        Vtbl = _create_vtbl_type(tuple(fields), itf)
+        vtbl = Vtbl(*methods)
+        for iid in iids:
+            self._com_pointers_[iid] = pointer(pointer(vtbl))
+
     #########################################################
     # IUnknown methods implementations
     def IUnknown_AddRef(self, this,
@@ -191,11 +334,105 @@
         _debug("%r.QueryInterface(%s) -> E_NOINTERFACE", self, iid)
         return E_NOINTERFACE
 
+    def QueryInterface(self, interface):
+        "Query the object for an interface pointer"
+        # This method is NOT the implementation of
+        # IUnknown::QueryInterface, instead it is supposed to be
+        # called on an COMObject by user code.  It allows to get COM
+        # interface pointers from COMObject instances.
+        ptr = self._com_pointers_.get(interface._iid_, None)
+        if ptr is None:
+            raise COMError(E_NOINTERFACE, FormatError(E_NOINTERFACE),
+                           (None, None, 0, None, None))
+        # CopyComPointer(src, dst) calls AddRef!
+        result = POINTER(interface)()
+        CopyComPointer(ptr, byref(result))
+        return result
+
     ################################################################
-    # ISupportErrorInfo method implementation
+    # ISupportErrorInfo::InterfaceSupportsErrorInfo implementation
     def ISupportErrorInfo_InterfaceSupportsErrorInfo(self, this, riid):
         if riid[0] in self._com_pointers_:
             return S_OK
         return S_FALSE
 
+    ################################################################
+    # IProvideClassInfo::GetClassInfo implementation
+    def IProvideClassInfo_GetClassInfo(self):
+        try:
+            self.__typelib
+        except AttributeError:
+            raise WindowsError(E_NOTIMPL)
+        return self.__typelib.GetTypeInfoOfGuid(self._reg_clsid_)
+
+    ################################################################
+    # IProvideClassInfo2::GetGUID implementation
+
+    def IProvideClassInfo2_GetGUID(self, dwGuidKind):
+        # GUIDKIND_DEFAULT_SOURCE_DISP_IID = 1
+        if dwGuidKind != 1:
+            raise WindowsError(E_INVALIDARG)
+        return self._outgoing_interfaces_[0]._iid_
+
+    ################################################################
+    # IDispatch methods
+    @property
+    def __typeinfo(self):
+        # XXX Looks like this better be a static property, set by the
+        # code that sets __typelib also...
+        iid = self._com_interfaces_[0]._iid_
+        return self.__typelib.GetTypeInfoOfGuid(iid)
+
+    def IDispatch_GetTypeInfoCount(self):
+        try:
+            self.__typelib
+        except AttributeError:
+            return 0
+        else:
+            return 1
+
+    def IDispatch_GetTypeInfo(self, itinfo, lcid):
+        if itinfo != 0:
+            raise WindowsError(DISP_E_BADINDEX)
+        try:
+            self.__typelib
+        except AttributeError:
+            raise WindowsError(E_NOTIMPL)
+        else:
+            return self.__typeinfo
+
+    def IDispatch_GetIDsOfNames(self, this, riid, rgszNames, cNames, lcid, rgDispId):
+        # Use windll to let DispGetIDsOfNames return a HRESULT instead
+        # of raising an error:
+        try:
+            self.__typeinfo
+        except AttributeError:
+            return E_NOTIMPL
+        return windll.oleaut32.DispGetIDsOfNames(self.__typeinfo,
+                                                 rgszNames, cNames, rgDispId)
+
+    def IDispatch_Invoke(self, this, dispIdMember, riid, lcid, wFlags,
+                         pDispParams, pVarResult, pExcepInfo, puArgErr):
+        try:
+            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
+        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)
+
+    ################################################################
+    # IPersist interface
+    def IPersist_GetClassID(self):
+        return self._reg_clsid_
+
 __all__ = ["COMObject"]

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 19:56:27 2008
@@ -50,9 +50,18 @@
         self.cookie = self.cp.Advise(receiver)
         self.receiver = receiver
 
+    def disconnect(self):
+        if self.cookie:
+            self.cp.Unadvise(self.cookie)
+            logger.debug("Unadvised %s", self.cp)
+            self.cp = None
+            self.cookie = None
+            del self.receiver
+
     def __del__(self):
         try:
-            self.cp.Unadvise(self.cookie)
+            if self.cookie is not None:
+                self.cp.Unadvise(self.cookie)
         except (comtypes.COMError, WindowsError):
             # Are we sure we want to ignore errors here?
             pass

Modified: ctypes/trunk/comtypes/comtypes/server/automation.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/server/automation.py	(original)
+++ ctypes/trunk/comtypes/comtypes/server/automation.py	Thu Apr 17 19:56:27 2008
@@ -4,79 +4,11 @@
 from comtypes.hresult import *
 
 from comtypes import COMObject, IUnknown
-from comtypes.typeinfo import LoadRegTypeLib, IProvideClassInfo, IProvideClassInfo2
 from comtypes.automation import IEnumVARIANT
 
 logger = logging.getLogger(__name__)
 
-_oleaut32 = windll.oleaut32
-
-__all__ = ["DualDispImplMixin", "VARIANTEnumerator"]
-
-class DualDispImplMixin(object):
-    # a mixin class to implement a dual dispatch interface.
-    # Needs a _reg_typelib_ attribute in the subclass.
-    #
-    # Also implements IProvideClassInfo2.  XXX Where should this
-    # really go?  And: XXX Can we load the typelib in the CoClass
-    # baseclass?
-    def __init__(self):
-        super(DualDispImplMixin, self).__init__()
-        tlib = LoadRegTypeLib(*self._reg_typelib_)
-
-        # XXX This works only if the default dispatch interface is
-        # also the default interface.  We should either search for the
-        # first dispatch interface, or raise an error if the first is
-        # no default disp interface.
-        self.__dispatch_iid = self._com_interfaces_[0]._iid_
-        self.__tinfo = tlib.GetTypeInfoOfGuid(self.__dispatch_iid)
-        if hasattr(self, "_reg_clsid_"):
-            self.__coclass_tinfo = tlib.GetTypeInfoOfGuid(self._reg_clsid_)
-
-    def IDispatch_GetTypeInfoCount(self, this, pctinfo):
-        if not pctinfo:
-            return E_POINTER
-        pctinfo[0] = 1
-        return S_OK
-
-    def IDispatch_GetTypeInfo(self, this, itinfo, lcid, pptinfo):
-        if not pptinfo:
-            return E_POINTER
-        if itinfo != 0:
-            return DISP_E_BADINDEX
-        pptinfo[0] = self.__tinfo
-        return S_OK
-
-    def IDispatch_GetIDsOfNames(self, this, riid, rgszNames, cNames, lcid, rgDispId):
-        return _oleaut32.DispGetIDsOfNames(self.__tinfo, rgszNames, cNames, rgDispId)
-
-    def IDispatch_Invoke(self, this, dispIdMember, riid, lcid, wFlags,
-                         pDispParams, pVarResult, pExcepInfo, puArgErr):
-        impl = self._com_pointers_[self.__dispatch_iid]
-        return _oleaut32.DispInvoke(impl, self.__tinfo,
-                                    dispIdMember, wFlags, pDispParams,
-                                    pVarResult, pExcepInfo, puArgErr)
-
-    def IProvideClassInfo_GetClassInfo(self, this, ppTI):
-        if not ppTI:
-            return E_POINTER
-        logger.debug("GetClassInfo called for %s", self._reg_clsid_)
-        ppTI[0] = self.__coclass_tinfo
-        return S_OK
-
-    def IProvideClassInfo2_GetGUID(self, this, dwGuidKind, pGUID):
-        if not pGUID:
-            return E_POINTER
-        GUIDKIND_DEFAULT_SOURCE_DISP_IID = 1
-        if dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID:
-            return E_INVALIDARG
-        # XXX MSDN: The outgoing interface in question must be derived from IDispatch. 
-        iid = self._outgoing_interfaces_[0]._iid_
-        memmove(pGUID, byref(iid), sizeof(iid))
-        logger.debug("IProvideClassInfo2::GetGUID -> %s", iid)
-        return S_OK
-
-################################################################
+__all__ = ["VARIANTEnumerator"]
 
 class VARIANTEnumerator(COMObject):
     _com_interfaces_ = [IEnumVARIANT]

Modified: ctypes/trunk/comtypes/comtypes/server/connectionpoints.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/server/connectionpoints.py	(original)
+++ ctypes/trunk/comtypes/comtypes/server/connectionpoints.py	Thu Apr 17 19:56:27 2008
@@ -58,10 +58,20 @@
             # for better performance, we could cache the dispids.
             dispid = self._typeinfo.GetIDsOfNames(name)[0]
             for p in self._connections.values():
-                p.Invoke(dispid, *args, **kw)
+                try:
+                    p.Invoke(dispid, *args, **kw)
+                except COMError, details:
+                    # XXX for certain errors (server missing) we should unadvise the connection
+                    logger.warning("_call_sinks(%s, %s, *%s, **%s)", self, name, args, kw,
+                                   exc_info=True)
         else:
             for p in self._connections.values():
-                getattr(p, name)(*args, **kw)
+                try:
+                    getattr(p, name)(*args, **kw)
+                except COMError, details:
+                    # XXX for certain errors (server missing) we should unadvise the connection
+                    logger.warning("_call_sinks(%s, %s, *%s, **%s)", self, name, args, kw,
+                                   exc_info=True)
 
 class ConnectableObjectMixin(object):
     """Mixin which implements IConnectionPointContainer.

Modified: ctypes/trunk/comtypes/comtypes/server/register.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/server/register.py	(original)
+++ ctypes/trunk/comtypes/comtypes/server/register.py	Thu Apr 17 19:56:27 2008
@@ -286,6 +286,8 @@
             if " " in exe:
                 exe = '"%s"' % exe
             if not hasattr(sys, "frozen"):
+                if not __debug__:
+                    exe = "%s -O" % exe
                 script = os.path.abspath(sys.modules[cls.__module__].__file__)
                 if " " in script:
                     script = '"%s"' % script
@@ -328,7 +330,7 @@
     opts, args = w_getopt.w_getopt(sys.argv[1:],
                                    "regserver unregserver embedding l: f: nodebug")
     if not opts:
-        sys.stderr.write(usage)
+        sys.stderr.write(usage + "\n")
         return 0 # nothing for us to do
 
     levels = []

Modified: ctypes/trunk/comtypes/comtypes/test/TestComServer.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/test/TestComServer.py	(original)
+++ ctypes/trunk/comtypes/comtypes/test/TestComServer.py	Thu Apr 17 19:56:27 2008
@@ -12,7 +12,6 @@
 import comtypes.client
 import comtypes.errorinfo
 import comtypes.server
-import comtypes.server.automation
 import comtypes.server.connectionpoints
 import comtypes.typeinfo
 
@@ -40,7 +39,6 @@
 # class.
 class TestComServer(
     TestComServerLib.TestComServer, # the coclass from the typelib wrapper
-    comtypes.server.automation.DualDispImplMixin, # other mixins
     comtypes.server.connectionpoints.ConnectableObjectMixin,
     ):
 

Modified: ctypes/trunk/comtypes/comtypes/test/runtests.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/test/runtests.py	(original)
+++ ctypes/trunk/comtypes/comtypes/test/runtests.py	Thu Apr 17 19:56:27 2008
@@ -2,4 +2,4 @@
 import comtypes.test
 
 if __name__ == "__main__":
-    sys.exit(comtypes.test.run(sys.argv[1:]))
+    sys.exit(comtypes.test.main(comtypes.test))

Modified: ctypes/trunk/comtypes/comtypes/test/test_collections.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/test/test_collections.py	(original)
+++ ctypes/trunk/comtypes/comtypes/test/test_collections.py	Thu Apr 17 19:56:27 2008
@@ -2,6 +2,8 @@
 from comtypes.client import CreateObject, GetModule #, Constants
 from ctypes import ArgumentError
 
+from comtypes.test.find_memleak import find_memleak
+
 class Test(unittest.TestCase):
 
     def test_IEnumVARIANT(self):
@@ -59,5 +61,22 @@
         cv.Reset()
         self.failUnlessRaises(ArgumentError, lambda: cv[:])
 
+    def test_leaks(self):
+        # The XP firewall manager.
+        fwmgr = CreateObject('HNetCfg.FwMgr')
+        # apps has a _NewEnum property that implements IEnumVARIANT
+        apps = fwmgr.LocalPolicy.CurrentProfile.AuthorizedApplications
+
+        def doit():
+            for item in iter(apps):
+                item.ProcessImageFileName
+        bytes = find_memleak(doit, (2, 20))
+        self.failIf(bytes, "Leaks %d bytes" % bytes)
+
+        def doit():
+            iter(apps).Next(99)
+        bytes = find_memleak(doit, (2, 20))
+        self.failIf(bytes, "Leaks %d bytes" % bytes)
+
 if __name__ == "__main__":
     unittest.main()

Modified: ctypes/trunk/comtypes/comtypes/test/test_createwrappers.py
==============================================================================
--- ctypes/trunk/comtypes/comtypes/test/test_createwrappers.py	(original)
+++ ctypes/trunk/comtypes/comtypes/test/test_createwrappers.py	Thu Apr 17 19:56:27 2008
@@ -78,6 +78,8 @@
     print "ADD", path
     add_test(path)
 
+for fname in glob.glob(os.path.join(common_progdir, r"Microsoft Shared\Speech\*.dll")):
+    add_test(fname)
 
 for fname in glob.glob(os.path.join(sysdir, "*.dll")):
     # these typelibs give errors:


More information about the Python-checkins mailing list