Commits

Jason Scheirer committed 60972bc

0.5.3

Comments (0)

Files changed (35)

+2008-12-12  Thomas Heller  <theller@python.net>
+
+	* Bumped version number to 0.5.3.
+
+	* Added VARIANT support for VT_I8 and VT_UI8 typecodes.
+
+2008-12-11  Thomas Heller  <theller@python.net>
+
+	* Workaround for Python bug: Python 3 cannot handle a distutils
+	installscript in the setup script
+
+	* Merged the py3-branch:
+
+	Various changes for py3 compatibility.  The setup script now uses
+	distutils.command.build_py.build_py_2to3 when run with Python 3.x,
+	and converts the sources into py3 syntax on the fly (in the build
+	directory).
+
+2008-11-26  Thomas Heller  <theller@python.net>
+
+	* Added untested code to comtypes.server: RegisterActiveObject()
+	and RevokeActiveObject(), plus some flags.
+
+	* Applied a patch from Torbjřrn Tyridal.  This allows to
+	high-level implement methods in COM servers, and event handler
+	methods that have [in] and [out] arguments in mixed order.
+
+2008-11-05  Thomas Heller  <theller@python.net>
+
+	* Add the IPersistFile interface to the comtypes.persist module.
+	Add comtypes.shelllink module which contains IShellLinkA and
+	IShellLinkW interfaces, plus the ShellLink coclass.
+
+2008-10-29  Thomas Heller  <theller@python.net>
+
+	* Handle coclass pointers as arguments in com interface methods
+	correctly.  Method calls will now accept pointers to the default
+	interface of this coclass.
+
+	* The Fire_Event() method in comtypes.server.connectionpoints now
+	returns a list of results.
+
+2008-10-10  Thomas Heller  <theller@python.net>
+
+	* Import cStringIO, which should always be available on Windows,
+	instead of first trying cStringIO and then StringIO.
+
+	* Python 2.6 compatibility: use 'types.MethodType' instead of
+	'new.instancemethod' to fix a -3 warning.
+
+	Use 'raise Exception(details)' instead of
+	'raise Exception, details'.
+
+	Don't use tuple unpacking in exception handlers:
+	  'except COMError, err:
+	      (hresult, text, details) = err'
+	instead of
+	  'except COMError, (hresult, text, details)'
+
+	* Python 2.4 compatibility: os.stat() raises OSError instead of
+	WindowsError when a file is not found.
+
 2008-09-19  Thomas Heller  <theller@python.net>
 
+	* Bump version number to 0.5.3dev.
+
 	* Bump version number to 0.5.2.
 	Released comtypes-0.5.2.
 

clear_comtypes_cache.py

             fullpath = os.path.join(directory, f)
             os.remove(fullpath)
         os.rmdir(directory)
-        print "Removed directory %s" % directory
+        print("Removed directory %s" % directory)
     else:
-        print "Directory %s NOT removed" % directory
+        print("Directory %s NOT removed" % directory)
 from ctypes import *
+import sys
+
+if sys.version_info > (3,):
+    def binary(obj):
+        return bytes(obj)
+else:
+    def binary(obj):
+        return buffer(obj)
 
 BYTE = c_byte
 WORD = c_ushort
 
     def __cmp__(self, other):
         if isinstance(other, GUID):
-            return cmp(buffer(self), buffer(other))
+            return cmp(binary(self), binary(other))
         return -1
 
     def __nonzero__(self):
-        return str(buffer(self)) != "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        return self != GUID_null
 
     def __eq__(self, other):
         return isinstance(other, GUID) and \
-               buffer(self) == buffer(other)
+               binary(self) == binary(other)
 
     def __hash__(self):
         # We make GUID instances hashable, although they are mutable.
-        return hash(buffer(self))
+        return hash(binary(self))
 
     def copy(self):
         return GUID(unicode(self))
         return guid
     create_new = classmethod(create_new)
 
+GUID_null = GUID()
+
 __all__ = ["GUID"]

comtypes/__init__.py

-import new, types, sys, os
+import types, sys, os
 
 # comtypes version numbers follow the setuptools convention:
 # http://peak.telecommunity.com/DevCenter/setuptools#specifying-your-project-s-version
 # 0.6.0dev < 0.6.0a < 0.6.0.b < 0.6.0c < 0.6.0
-__version__ = "0.5.2"
+__version__ = "0.5.3"
 
 import logging
 class NullHandler(logging.Handler):
         try:
             mod_mtime = os.stat(mod_path).st_mtime
             tlib_mtime = os.stat(tlb_path).st_mtime
-        except (WindowsError, TypeError):
+        except (OSError, TypeError):
             return
         if mod_mtime < tlib_mtime:
             raise ImportError("Typelib newer than module")
     monkeypatch_COMError()
     del monkeypatch_COMError
 
-class COMError(partial.partial, COMError):
-    @partial.replace
-    def __str__(self):
-        return "%s(0x%X, %r, %r)" % (self.__class__.__name__,
-                                     self.hresult & 0xFFFFFFFF,
-                                     self.text,
-                                     self.details)
+if sys.version_info >= (3, 0):
+    pythonapi.PyInstanceMethod_New.argtypes = [py_object]
+    pythonapi.PyInstanceMethod_New.restype = py_object
+    PyInstanceMethod_Type = type(pythonapi.PyInstanceMethod_New(id))
+
+    def instancemethod(func, inst, cls):
+        mth = PyInstanceMethod_Type(func)
+        if inst is None:
+            return mth
+        return mth.__get__(inst)
+else:
+    def instancemethod(func, inst, cls):
+        return types.MethodType(func, inst, cls)
 
 class ReturnHRESULT(Exception):
     """ReturnHRESULT(hresult, text)
 
 def shutdown(func=_ole32.CoUninitialize,
              _debug=logger.debug,
-             _exc_clear=sys.exc_clear):
+             _exc_clear=getattr(sys, "exc_clear", lambda: None)):
     # Make sure no COM pointers stay in exception frames.
     _exc_clear()
     # Sometimes, CoUnititialize, running at Python shutdown,
     # A COM pointer in a VARIANT is an 'Object', too
     elif isinstance(obj, VARIANT) and isinstance(obj.value, POINTER(IUnknown)):
         return True
-    try:
-        # a comtypes.client.dynamic.Dispatch object
-        _comobj = obj._comobj
-    except AttributeError:
-        return False
-    return True
-
+    # It may be a dynamic dispatch object.
+    return hasattr(obj, "_comobj")
 
 ################################################################
 # The metaclasses...
                     "Return 'self.Item(index)'"
                     try:
                         result = self.Item(index)
-                    except COMError, (hresult, text, details):
+                    except COMError, err:
+                        (hresult, text, details) = err.args
                         if hresult == -2147352565: # DISP_E_BADINDEX
-                            raise IndexError, "invalid index"
+                            raise IndexError("invalid index")
                         else:
                             raise
                     # Hm, this doesn't look correct...
                     if not result: # we got a NULL com pointer
-                        raise IndexError, "invalid index"
+                        raise IndexError("invalid index")
                     # Hm, should we call __ctypes_from_outparam__ on the result?
                     return result
 
             try:
                 memid = [x for x in idlflags if isinstance(x, int)][0]
             except IndexError:
-                raise TypeError, "no dispid found in idlflags"
+                raise TypeError("no dispid found in idlflags")
             if what == "DISPPROPERTY": # DISPPROPERTY
                 assert not argspec # XXX does not yet work for properties with parameters
                 accessor = self._disp_property(memid, idlflags)
     def __get_baseinterface_methodcount(self):
         "Return the number of com methods in the base interfaces"
         try:
-            return sum([len(itf.__dict__["_methods_"])
-                        for itf in self.mro()[1:-1]])
-        except KeyError, (name,):
+            result = 0
+            for itf in self.mro()[1:-1]:
+                result += len(itf.__dict__["_methods_"])
+            return result
+        except KeyError, err:
+            (name,) = err.args
             if name == "_methods_":
-                raise TypeError, "baseinterface '%s' has no _methods_" % itf.__name__
+                raise TypeError("baseinterface '%s' has no _methods_" % itf.__name__)
             raise
 
     def _fix_inout_args(self, func, argtypes, paramflags):
         try:
             iid = self.__dict__["_iid_"]
         except KeyError:
-            raise AttributeError, "this class must define an _iid_"
+            raise AttributeError("this class must define an _iid_")
         else:
             iid = str(iid)
 ##            if iid in com_interface_registry:
                 func = prototype(i + vtbl_offset, name, paramflags, None)
             setattr(self,
                     "_%s__com_%s" % (self.__name__, name),
-                    new.instancemethod(raw_func, None, self))
+                    instancemethod(raw_func, None, self))
 
             if paramflags:
                 # see comment in the _fix_inout_args method
             func.__doc__ = doc
             func.__name__ = name # for pyhelp
             # make it an unbound method.  Remember, 'self' is a type here.
-            mth = new.instancemethod(func, None, self)
+            mth = instancemethod(func, None, self)
 
             # is it a property set or property get?
             is_prop = False
             # present but we can no longer call Release() on them -
             # this may give a protection fault.  So we need the
             # _com_shutting_down flag.
-            if not self.__metaclass__._com_shutting_down:
+            #
+            if not type(self)._com_shutting_down:
                 _debug("Release %s", self)
                 self.Release()
 
         # get the value property of the c_void_p baseclass, this is the pointer value
         return cmp(super(_compointer_base, self).value, super(_compointer_base, other).value)
 
+    def __eq__(self, other):
+        if not isinstance(other, _compointer_base):
+            return False
+        # get the value property of the c_void_p baseclass, this is the pointer value
+        return super(_compointer_base, self).value == super(_compointer_base, other).value
+
     def __hash__(self):
         """Return the hash value of the pointer."""
         # hash the pointer values
 # What's a coclass?
 # a POINTER to a coclass is allowed as parameter in a function declaration:
 # http://msdn.microsoft.com/library/en-us/midl/midl/oleautomation.asp
+
+from comtypes._meta import _coclass_meta
+
 class CoClass(COMObject):
-    from comtypes._meta import _coclass_meta as __metaclass__
-
+    __metaclass__ = _coclass_meta
 ################################################################

comtypes/_comobject.py

 from comtypes.hresult import *
 
 import os
-import new
 import logging
 logger = logging.getLogger(__name__)
 _debug = logger.debug
 # COM object implementation
 from _ctypes import CopyComPointer
 
-from comtypes import COMError, ReturnHRESULT
+from comtypes import COMError, ReturnHRESULT, instancemethod
 from comtypes.errorinfo import ISupportErrorInfo, ReportException, ReportError
 from comtypes.typeinfo import IProvideClassInfo, IProvideClassInfo2
 from comtypes import IPersist
     def call_with_this(*args, **kw):
         try:
             result = mth(*args, **kw)
-        except ReturnHRESULT, (hresult, text):
+        except ReturnHRESULT, err:
+            (hresult, text) = err.args
             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)
     # 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])
+    # Output arguments have flag '2'
+
+    args_out_idx=[]
+    args_in_idx=[]
+    for i,a in enumerate(dirflags):
+        if a&2:
+            args_out_idx.append(i)
+        if a&1 or a==0:
+            args_in_idx.append(i)
+    args_out = len(args_out_idx)
+
     ## XXX Remove this:
 ##    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 call_without_this(this, *args):
-        outargs = args[len(args)-args_out:]
         # 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
+
+        #make argument list for handler by index array built above
+        inargs=[]
+        for a in args_in_idx:
+            inargs.append(args[a])
         try:
-            result = mth(*args[:args_in])
+            result = mth(*inargs)
             if args_out == 1:
-                outargs[0][0] = result
+                args[args_out_idx[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):
+                    args[args_out_idx[i]][0] = value
+        except ReturnHRESULT, err:
+            (hresult, text) = err.args
             return ReportError(text, iid=interface._iid_, clsid=clsid, hresult=hresult)
-        except COMError, (hr, text, details):
+        except COMError, err:
+            (hr, text, details) = err.args
             _error("Exception in %s.%s implementation:", interface.__name__, mthname, exc_info=True)
             try:
                 descr, source, helpfile, helpcontext, progid = details
                 setattr(self, propname, value)
             except AttributeError:
                 raise E_NotImplemented()
-        return new.instancemethod(set, self.inst, type(self.inst))
+        return instancemethod(set, self.inst, type(self.inst))
 
     def getter(self, propname):
         #
                 return getattr(self, propname)
             except AttributeError:
                 raise E_NotImplemented()
-        return new.instancemethod(get, self.inst, type(self.inst))
+        return instancemethod(get, self.inst, type(self.inst))
 
 def _create_vtbl_type(fields, itf):
     try:
             # How are the parameters unpacked for propertyput
             # operations with additional parameters?  Can propput
             # have additional args?
-            args = [params.rgvarg[i].value for i in range(params.cNamedArgs)[::-1]]
+            #
+            # 2to3 has problems to translate 'range(...)[::-1]'
+            # correctly, so use 'list(range)[::-1]' instead (will be
+            # fixed in Python 3.1, probably):
+            args = [params.rgvarg[i].value for i in list(range(params.cNamedArgs))[::-1]]
             # MSDN: pVarResult is ignored if DISPATCH_PROPERTYPUT or
             # DISPATCH_PROPERTYPUTREF is specified.
             return mth(this, *args)
             # DISPATCH_METHOD
             # DISPATCH_PROPERTYGET
             # the positions of named arguments
+            #
+            # 2to3 has problems to translate 'range(...)[::-1]'
+            # correctly, so use 'list(range)[::-1]' instead (will be
+            # fixed in Python 3.1, probably):
             named_indexes = [params.rgdispidNamedArgs[i] for i in range(params.cNamedArgs)]
             # the positions of unnamed arguments
-            unnamed_indexes = range(params.cArgs - params.cNamedArgs)[::-1]
+            unnamed_indexes = list(range(params.cArgs - params.cNamedArgs))[::-1]
             # It seems that this code calculates the indexes of the
             # parameters in the params.rgvarg array correctly.
             indexes = named_indexes + unnamed_indexes

comtypes/_meta.py

     result.__dict__["__clsid"] = str(self._reg_clsid_)
     return result
 
+def _coclass_from_param(cls, obj):
+    if isinstance(obj, (cls._com_interfaces_[0], cls)):
+        return obj
+    raise TypeError(obj)
+
 #
 # The mro() of a POINTER(App) type, where class App is a subclass of CoClass:
 #
 class _coclass_meta(type):
     # metaclass for CoClass
     #
-    # If a CoClass subclass is created, create a POINTER(...) type for
-    # that class, with bases <coclass> and c_void_p.  Also, the
+    # When a CoClass subclass is created, create a POINTER(...) type
+    # for that class, with bases <coclass> and c_void_p.  Also, the
     # POINTER(...) type gets a __ctypes_from_outparam__ method which
     # will QueryInterface for the default interface: the first one on
     # the coclass' _com_interfaces_ list.
             clsid = namespace["_reg_clsid_"]
             comtypes.com_coclass_registry[str(clsid)] = klass
         PTR = _coclass_pointer_meta("POINTER(%s)" % klass.__name__,
-                                  (klass, c_void_p),
-                                  {"__ctypes_from_outparam__": _wrap_coclass})
+                                    (klass, c_void_p),
+                                    {"__ctypes_from_outparam__": _wrap_coclass,
+                                     "from_param": classmethod(_coclass_from_param),
+                                     })
         from ctypes import _pointer_type_cache
         _pointer_type_cache[klass] = PTR
 

comtypes/automation.py

             ("VT_I1", c_byte),
             ("VT_I2", c_short),
             ("VT_I4", c_long),
+            ("VT_I8", c_longlong),
             ("VT_INT", c_int),
             ("VT_UI1", c_ubyte),
             ("VT_UI2", c_ushort),
             ("VT_UI4", c_ulong),
+            ("VT_UI8", c_ulonglong),
             ("VT_UINT", c_uint),
             ("VT_R4", c_float),
             ("VT_R8", c_double),
                     # did work.
                     self.vt = VT_UI4
                     return
+            # try VT_I8 next.
+            if value >= 0:
+                u.VT_I8 = value
+                if u.VT_I8 == value:
+                    # did work.
+                    self.vt = VT_I8
+                    return
+            # try VT_UI8 next.
+            if value >= 0:
+                u.VT_UI8 = value
+                if u.VT_UI8 == value:
+                    # did work.
+                    self.vt = VT_UI8
+                    return
             # VT_R8 is last resort.
             self.vt = VT_R8
             u.VT_R8 = float(value)
             return self._.VT_I2
         elif vt == VT_I4:
             return self._.VT_I4
+        elif vt == VT_I8:
+            return self._.VT_I8
+        elif vt == VT_UI8:
+            return self._.VT_UI8
         elif vt == VT_INT:
             return self._.VT_INT
         elif vt == VT_UI1:
     def GetTypeInfo(self, index, lcid=0):
         """Return type information.  Index 0 specifies typeinfo for IDispatch"""
         import comtypes.typeinfo
-        # we could also use cast instead of QueryInterface.  Does it matter?
         result = self._GetTypeInfo(index, lcid)
         return result.QueryInterface(comtypes.typeinfo.ITypeInfo)
 
         arr = (c_wchar_p * len(names))(*names)
         ids = (DISPID * len(names))()
         self.__com_GetIDsOfNames(riid_null, arr, len(names), lcid, ids)
-        return [i for i in ids]
+        return ids[:]
 
     def Invoke(self, dispid, *args, **kw):
         """Invoke a method or property."""
         _invkind = kw.pop("_invkind", 1) # DISPATCH_METHOD
         _lcid = kw.pop("_lcid", 0)
         if kw:
-            raise ValueError, "named parameters not yet implemented"
+            raise ValueError("named parameters not yet implemented")
 
         result = VARIANT()
         excepinfo = EXCEPINFO()
         try:
             self.__com_Invoke(dispid, riid_null, _lcid, _invkind, byref(dp),
                               byref(result), byref(excepinfo), byref(argerr))
-        except COMError, (hresult, text, details):
+        except COMError, err:
+            (hresult, text, details) = err.args
             if hresult == DISP_E_EXCEPTION:
                 details = (excepinfo.bstrDescription, excepinfo.bstrSource,
                            excepinfo.bstrHelpFile, excepinfo.dwHelpContext,

comtypes/client/__init__.py

         else:
             if ta.cImplTypes != 1:
                 # Hm, should we use dynamic now?
-                raise TypeError, "No default interface found"
+                raise TypeError("No default interface found")
             # Only one interface implemented, use that (even if
             # not marked as default).
             index = 0
     logger.debug("Default interface is %s", typeattr.guid)
     try:
         punk.QueryInterface(comtypes.IUnknown, typeattr.guid)
-    except comtypes.COMError, details:
+    except comtypes.COMError:
         logger.debug("Does not implement default interface, returning dynamic object")
         return comtypes.client.dynamic.Dispatch(punk)
 

comtypes/client/_code_cache.py

 comtypes.gen package and returns a directory where generated code can
 be written to.
 """
-import ctypes, logging, new, os, sys, tempfile
+import ctypes, logging, os, sys, tempfile
 logger = logging.getLogger(__name__)
 
 def _find_gen_dir():
                 ofi.close()
         except (OSError, IOError), details:
             logger.info("Creating comtypes.gen package failed: %s", details)
-            module = sys.modules["comtypes.gen"] = new.module("comtypes.gen")
+            module = sys.modules["comtypes.gen"] = types.ModuleType("comtypes.gen")
             comtypes.gen = module
             comtypes.gen.__path__ = []
             logger.info("Created a memory-only package.")

comtypes/client/_events.py

             # XXX handler is called with 'this'.  Should we really print "None" instead?
             args = (None,) + args
             print "Event %s(%s)" % (name, ", ".join([repr(a) for a in args]))
-        import new
-        return new.instancemethod(handler, EventDumper, self)
+        return comtypes.instancemethod(handler, EventDumper, self)
 
 def ShowEvents(source, interface=None):
     """Receive COM events from 'source'.  A special event sink will be
     """
     # XXX Should there be a way to pass additional event handles which
     # can terminate this function?
+
+    # XXX XXX XXX
+    #
+    # It may be that I misunderstood the CoWaitForMultipleHandles
+    # function.  Is a message loop required in a STA?  Seems so...
+    #
+    # MSDN says:
+    #
+    # If the caller resides in a single-thread apartment,
+    # CoWaitForMultipleHandles enters the COM modal loop, and the
+    # thread's message loop will continue to dispatch messages using
+    # the thread's message filter. If no message filter is registered
+    # for the thread, the default COM message processing is used.
+    #
+    # If the calling thread resides in a multithread apartment (MTA),
+    # CoWaitForMultipleHandles calls the Win32 function
+    # MsgWaitForMultipleObjects.
+    
     hevt = ctypes.windll.kernel32.CreateEventA(None, True, False, None)
     handles = (ctypes.c_void_p * 1)(hevt)
     RPC_S_CALLPENDING = -2147417835
                                                                len(handles), handles,
                                                                ctypes.byref(ctypes.c_ulong()))
         except WindowsError, details:
-            if details[0] != RPC_S_CALLPENDING: # timeout expired
+            if details.args[0] != RPC_S_CALLPENDING: # timeout expired
                 raise
         else:
             raise KeyboardInterrupt

comtypes/client/_generate.py

-import new
+import types
 import os
 import sys
 import comtypes.client
         return mod
     if modulename is None:
         return mod
-    modulename = modulename.encode("mbcs")
+    if sys.version_info < (3, 0):
+        modulename = modulename.encode("mbcs")
 
     # create and import the friendly-named module
     try:
     code = "from comtypes.gen import %s\nglobals().update(%s.__dict__)\n" % (modname, modname)
     code += "__name__ = 'comtypes.gen.%s'" % modulename
     if comtypes.client.gen_dir is None:
-        mod = new.module("comtypes.gen." + modulename)
+        mod = types.ModuleType("comtypes.gen." + modulename)
         mod.__file__ = os.path.join(os.path.abspath(comtypes.gen.__path__[0]),
                                     "<memory>")
         exec code in mod.__dict__
 
     if comtypes.client.gen_dir is None:
         code = ofi.getvalue()
-        mod = new.module(fullname)
+        mod = types.ModuleType(fullname)
         mod.__file__ = os.path.join(os.path.abspath(comtypes.gen.__path__[0]),
                                     "<memory>")
         exec code in mod.__dict__

comtypes/client/dynamic.py

         enum = self.__enum()
         if index > 0:
             if 0 != enum.Skip(index):
-                raise IndexError, "index out of range"
+                raise IndexError("index out of range")
         item, fetched = enum.Next(1)
         if not fetched:
-            raise IndexError, "index out of range"
+            raise IndexError("index out of range")
         return item
 
     def QueryInterface(self, *args):
         flags = comtypes.automation.DISPATCH_PROPERTYGET
         try:
             result = self._comobj.Invoke(dispid, _invkind=flags)
-        except COMError, (hresult, text, details):
+        except COMError, err:
+            (hresult, text, details) = err.args
             if hresult in ERRORS_BAD_CONTEXT:
                 result = MethodCaller(dispid, self)
                 self.__dict__[name] = result
-            else: raise
-        except: raise
+            else:
+                # The line break is important for 2to3 to work correctly
+                raise
+        except:
+            # The line break is important for 2to3 to work correctly
+            raise
 
         return result
 
         flags = comtypes.automation.DISPATCH_PROPERTYPUT
         try:
             return self._comobj.Invoke(dispid, value, _invkind=flags)
-        except COMError, (hresult, text, details):
+        except COMError, err:
+            (hresult, text, details) = err.args
             if hresult == hres.DISP_E_MEMBERNOTFOUND: pass
             else: raise
         flags = comtypes.automation.DISPATCH_PROPERTYPUTREF

comtypes/partial.py

             # It is the class partial itself
             return type.__new__(cls, name, bases, dict)
         if len(bases) != 2:
-            raise TypeError, "A partial class definition must have only one base class to extend"
+            raise TypeError("A partial class definition must have only one base class to extend")
         base = bases[1]
         for k, v in dict.items():
             if k == '__module__':
                 if hasattr(v, '__noreplace'):
                     continue
                 if not hasattr(v, '__replace'):
-                    raise TypeError, "%s already has %s" % (repr(base), k)
+                    raise TypeError("%r already has %s" % (base, k))
             setattr(base, k, v)
         # Return the original class
         return base

comtypes/persist.py

 interface, useful in client code.
 """
 from ctypes import *
-from ctypes.wintypes import WORD
+from ctypes.wintypes import WORD, DWORD, BOOL
 from comtypes import GUID, IUnknown, COMMETHOD, HRESULT, dispid
 from comtypes import IPersist
 from comtypes.automation import VARIANT, tagEXCEPINFO
         ]
 
 
+# STGM constants
+# Access
+STGM_READ = 0x00000000
+STGM_WRITE = 0x00000001
+STGM_READWRITE = 0x00000002
+
+# Sharing
+STGM_SHARE_EXCLUSIVE = 0x00000010
+STGM_SHARE_DENY_WRITE = 0x00000020
+STGM_SHARE_DENY_READ = 0x00000030
+STGM_SHARE_DENY_NONE = 0x00000040
+STGM_PRIORITY = 0x00040000
+
+# Creation
+STGM_FAILIFTHERE = 0x00000000
+STGM_CREATE = 0x00001000
+STGM_CONVERT = 0x00020000
+
+# Transactioning
+STGM_DIRECT = 0x00000000
+STGM_TRANSACTED = 0x00010000
+
+# Transactioning Performance
+STGM_NOSCRATCH = 0x00100000
+STGM_NOSNAPSHOT = 0x00200000
+
+# Direct SWMR and Simple
+STGM_SIMPLE = 0x08000000
+STGM_DIRECT_SWMR = 0x00400000
+
+# Delete on release
+STGM_DELETEONRELEASE = 0x04000000
+
+LPOLESTR = LPCOLESTR = c_wchar_p
+
+class IPersistFile(IPersist):
+    _iid_ = GUID('{0000010B-0000-0000-C000-000000000046}')
+    _idlflags_ = []
+    _methods_ = [
+        COMMETHOD([], HRESULT, 'IsDirty'),
+        COMMETHOD([], HRESULT, 'Load',
+                  ( ['in'], LPCOLESTR, 'pszFileName' ),
+                  ( ['in'], DWORD, 'dwMode' )),
+        COMMETHOD([], HRESULT, 'Save',
+                  ( ['in'], LPCOLESTR, 'pszFileName' ),
+                  ( ['in'], BOOL, 'fRemember' )),
+        COMMETHOD([], HRESULT, 'SaveCompleted',
+                  ( ['in'], LPCOLESTR, 'pszFileName' )),
+        COMMETHOD([], HRESULT, 'GetCurFile',
+                  ( ['out'], POINTER(LPOLESTR), 'ppszFileName' ))
+        ]
+
+
 from comtypes import COMObject
 from comtypes.hresult import *
 class DictPropertyBag(COMObject):

comtypes/server/__init__.py

 ##    _methods_ = [
 ##        STDMETHOD(HRESULT, "AddConnection", [c_ulong, c_ulong]),
 ##        STDMETHOD(HRESULT, "ReleaseConnection", [c_ulong, c_ulong, c_ulong])]
+
+# The following code is untested:
+
+ACTIVEOBJECT_STRONG = 0x0
+ACTIVEOBJECT_WEAK   = 0x1
+
+oleaut32 = ctypes.oledll.oleaut32
+
+def RegisterActiveObject(comobj, weak=True):
+    punk = comobj._com_pointers_[comtypes.IUnknown._iid_]
+    clsid = comobj._reg_clsid_
+    if weak:
+        flags = ACTIVEOBJECT_WEAK
+    else:
+        flags = ACTIVEOBJECT_STRONG
+    handle = ctypes.c_ulong()
+    oleaut32.RegisterActiveObject(punk,
+                                  ctypes.byref(clsid),
+                                  flags,
+                                  ctypes.byref(handle))
+    return handle.value
+
+def RevokeActiveObject(handle):
+    oleaut32.RevokeActiveObject(handle, None)

comtypes/server/connectionpoints.py

         return E_NOTIMPL
 
     def _call_sinks(self, name, *args, **kw):
+        results = []
         logger.debug("_call_sinks(%s, %s, *%s, **%s)", self, name, args, kw)
         # Is it an IDispatch derived interface?  Then, events have to be delivered
         # via Invoke calls (even if it is a dual interface).
             dispid = self._typeinfo.GetIDsOfNames(name)[0]
             for key, p in self._connections.items():
                 try:
-                    p.Invoke(dispid, *args, **kw)
+                    result = p.Invoke(dispid, *args, **kw)
+                except COMError, details:
+                    if details.hresult == -2147023174:
+                        logger.warning("_call_sinks(%s, %s, *%s, **%s) failed; removing connection",
+                                       self, name, args, kw,
+                                       exc_info=True)
+                        try:
+                            del self._connections[key]
+                        except KeyError:
+                            pass # connection already gone
+                    else:
+                        logger.warning("_call_sinks(%s, %s, *%s, **%s)", self, name, args, kw,
+                                       exc_info=True)
+                else:
+                    results.append(result)
+        else:
+            for p in self._connections.values():
+                try:
+                    result = getattr(p, name)(*args, **kw)
                 except COMError, details:
                     if details.hresult == -2147023174:
                         logger.warning("_call_sinks(%s, %s, *%s, **%s) failed; removing connection",
                     else:
                         logger.warning("_call_sinks(%s, %s, *%s, **%s)", self, name, args, kw,
                                        exc_info=True)
-        else:
-            for p in self._connections.values():
-                try:
-                    getattr(p, name)(*args, **kw)
-                except COMError, details:
-                    if details.hresult == -2147023174:
-                        logger.warning("_call_sinks(%s, %s, *%s, **%s) failed; removing connection",
-                                       self, name, args, kw,
-                                       exc_info=True)
-                        del self._connections[key]
-                    else:
-                        logger.warning("_call_sinks(%s, %s, *%s, **%s)", self, name, args, kw,
-                                       exc_info=True)
+                else:
+                    results.append(result)
+        return results
 
 class ConnectableObjectMixin(object):
     """Mixin which implements IConnectionPointContainer.
     def Fire_Event(self, itf, name, *args, **kw):
         # Fire event 'name' with arguments *args and **kw.
         # Accepts either an interface index or an interface as first argument.
+        # Returns a list of results.
         logger.debug("Fire_Event(%s, %s, *%s, **%s)", itf, name, args, kw)
         if isinstance(itf, int):
             itf = self._outgoing_interfaces_[itf]
-        self.__connections[itf]._call_sinks(name, *args, **kw)
+        return self.__connections[itf]._call_sinks(name, *args, **kw)
+

comtypes/server/w_getopt.py

                 try:
                     opts.append((arg, args[1]))
                 except IndexError:
-                    raise GetoptError, "option '%s' requires an argument" % args[0]
+                    raise GetoptError("option '%s' requires an argument" % args[0])
                 args = args[1:]
             elif arg in options:
                 opts.append((arg, ''))
             else:
-                raise GetoptError, "invalid option '%s'" % args[0]
+                raise GetoptError("invalid option '%s'" % args[0])
             args = args[1:]
         else:
             arguments.append(args[0])

comtypes/shelllink.py

+from ctypes import *
+from ctypes.wintypes import DWORD, WIN32_FIND_DATAA, WIN32_FIND_DATAW, MAX_PATH
+from comtypes import IUnknown, GUID, COMMETHOD, HRESULT, CoClass
+
+# for GetPath
+SLGP_SHORTPATH = 0x1
+SLGP_UNCPRIORITY = 0x2
+SLGP_RAWPATH = 0x4
+
+# for SetShowCmd, GetShowCmd
+##SW_SHOWNORMAL
+##SW_SHOWMAXIMIZED
+##SW_SHOWMINNOACTIVE
+
+
+# for Resolve
+##SLR_INVOKE_MSI
+##SLR_NOLINKINFO
+##SLR_NO_UI
+##SLR_NOUPDATE
+##SLR_NOSEARCH
+##SLR_NOTRACK
+##SLR_UPDATE
+
+# fake these...
+ITEMIDLIST = c_int
+LPITEMIDLIST = LPCITEMIDLIST = POINTER(ITEMIDLIST)
+
+class IShellLinkA(IUnknown):
+    _iid_ = GUID('{000214EE-0000-0000-C000-000000000046}')
+    _methods_ = [
+        COMMETHOD([], HRESULT, 'GetPath',
+                  ( ['in', 'out'], c_char_p, 'pszFile' ),
+                  ( ['in'], c_int, 'cchMaxPath' ),
+                  ( ['in', 'out'], POINTER(WIN32_FIND_DATAA), 'pfd' ),
+                  ( ['in'], DWORD, 'fFlags' )),
+        COMMETHOD([], HRESULT, 'GetIDList',
+                  ( ['retval', 'out'], POINTER(LPITEMIDLIST), 'ppidl' )),
+        COMMETHOD([], HRESULT, 'SetIDList',
+                  ( ['in'], LPCITEMIDLIST, 'pidl' )),
+        COMMETHOD([], HRESULT, 'GetDescription',
+                  ( ['in', 'out'], c_char_p, 'pszName' ),
+                  ( ['in'], c_int, 'cchMaxName' )),
+        COMMETHOD([], HRESULT, 'SetDescription',
+                  ( ['in'], c_char_p, 'pszName' )),
+        COMMETHOD([], HRESULT, 'GetWorkingDirectory',
+                  ( ['in', 'out'], c_char_p, 'pszDir' ),
+                  ( ['in'], c_int, 'cchMaxPath' )),
+        COMMETHOD([], HRESULT, 'SetWorkingDirectory',
+                  ( ['in'], c_char_p, 'pszDir' )),
+        COMMETHOD([], HRESULT, 'GetArguments',
+                  ( ['in', 'out'], c_char_p, 'pszArgs' ),
+                  ( ['in'], c_int, 'cchMaxPath' )),
+        COMMETHOD([], HRESULT, 'SetArguments',
+                  ( ['in'], c_char_p, 'pszArgs' )),
+        COMMETHOD(['propget'], HRESULT, 'Hotkey',
+                  ( ['retval', 'out'], POINTER(c_short), 'pwHotkey' )),
+        COMMETHOD(['propput'], HRESULT, 'Hotkey',
+                  ( ['in'], c_short, 'pwHotkey' )),
+        COMMETHOD(['propget'], HRESULT, 'ShowCmd',
+                  ( ['retval', 'out'], POINTER(c_int), 'piShowCmd' )),
+        COMMETHOD(['propput'], HRESULT, 'ShowCmd',
+                  ( ['in'], c_int, 'piShowCmd' )),
+        COMMETHOD([], HRESULT, 'GetIconLocation',
+                  ( ['in', 'out'], c_char_p, 'pszIconPath' ),
+                  ( ['in'], c_int, 'cchIconPath' ),
+                  ( ['in', 'out'], POINTER(c_int), 'piIcon' )),
+        COMMETHOD([], HRESULT, 'SetIconLocation',
+                  ( ['in'], c_char_p, 'pszIconPath' ),
+                  ( ['in'], c_int, 'iIcon' )),
+        COMMETHOD([], HRESULT, 'SetRelativePath',
+                  ( ['in'], c_char_p, 'pszPathRel' ),
+                  ( ['in'], DWORD, 'dwReserved' )),
+        COMMETHOD([], HRESULT, 'Resolve',
+                  ( ['in'], c_int, 'hwnd' ),
+                  ( ['in'], DWORD, 'fFlags' )),
+        COMMETHOD([], HRESULT, 'SetPath',
+                  ( ['in'], c_char_p, 'pszFile' )),
+        ]
+
+    def GetPath(self, flags=SLGP_SHORTPATH):
+        buf = create_string_buffer(MAX_PATH)
+        # We're not interested in WIN32_FIND_DATA
+        self.__com_GetPath(buf, MAX_PATH, None, flags)
+        return buf.value
+
+    def GetDescription(self):
+        buf = create_string_buffer(1024)
+        self.__com_GetDescription(buf, 1024)
+        return buf.value
+
+    def GetWorkingDirectory(self):
+        buf = create_string_buffer(MAX_PATH)
+        self.__com_GetWorkingDirectory(buf, MAX_PATH)
+        return buf.value
+
+    def GetArguments(self):
+        buf = create_string_buffer(1024)
+        self.__com_GetArguments(buf, 1024)
+        return buf.value
+
+    def GetIconLocation(self):
+        iIcon = c_int()
+        buf = create_string_buffer(MAX_PATH)
+        self.__com_GetIconLocation(buf, MAX_PATH, byref(iIcon))
+        return buf.value, iIcon.value
+
+class IShellLinkW(IUnknown):
+    _iid_ = GUID('{000214F9-0000-0000-C000-000000000046}')
+    _methods_ = [
+        COMMETHOD([], HRESULT, 'GetPath',
+                  ( ['in', 'out'], c_wchar_p, 'pszFile' ),
+                  ( ['in'], c_int, 'cchMaxPath' ),
+                  ( ['in', 'out'], POINTER(WIN32_FIND_DATAW), 'pfd' ),
+                  ( ['in'], DWORD, 'fFlags' )),
+        COMMETHOD([], HRESULT, 'GetIDList',
+                  ( ['retval', 'out'], POINTER(LPITEMIDLIST), 'ppidl' )),
+        COMMETHOD([], HRESULT, 'SetIDList',
+                  ( ['in'], LPCITEMIDLIST, 'pidl' )),
+        COMMETHOD([], HRESULT, 'GetDescription',
+                  ( ['in', 'out'], c_wchar_p, 'pszName' ),
+                  ( ['in'], c_int, 'cchMaxName' )),
+        COMMETHOD([], HRESULT, 'SetDescription',
+                  ( ['in'], c_wchar_p, 'pszName' )),
+        COMMETHOD([], HRESULT, 'GetWorkingDirectory',
+                  ( ['in', 'out'], c_wchar_p, 'pszDir' ),
+                  ( ['in'], c_int, 'cchMaxPath' )),
+        COMMETHOD([], HRESULT, 'SetWorkingDirectory',
+                  ( ['in'], c_wchar_p, 'pszDir' )),
+        COMMETHOD([], HRESULT, 'GetArguments',
+                  ( ['in', 'out'], c_wchar_p, 'pszArgs' ),
+                  ( ['in'], c_int, 'cchMaxPath' )),
+        COMMETHOD([], HRESULT, 'SetArguments',
+                  ( ['in'], c_wchar_p, 'pszArgs' )),
+        COMMETHOD(['propget'], HRESULT, 'Hotkey',
+                  ( ['retval', 'out'], POINTER(c_short), 'pwHotkey' )),
+        COMMETHOD(['propput'], HRESULT, 'Hotkey',
+                  ( ['in'], c_short, 'pwHotkey' )),
+        COMMETHOD(['propget'], HRESULT, 'ShowCmd',
+                  ( ['retval', 'out'], POINTER(c_int), 'piShowCmd' )),
+        COMMETHOD(['propput'], HRESULT, 'ShowCmd',
+                  ( ['in'], c_int, 'piShowCmd' )),
+        COMMETHOD([], HRESULT, 'GetIconLocation',
+                  ( ['in', 'out'], c_wchar_p, 'pszIconPath' ),
+                  ( ['in'], c_int, 'cchIconPath' ),
+                  ( ['in', 'out'], POINTER(c_int), 'piIcon' )),
+        COMMETHOD([], HRESULT, 'SetIconLocation',
+                  ( ['in'], c_wchar_p, 'pszIconPath' ),
+                  ( ['in'], c_int, 'iIcon' )),
+        COMMETHOD([], HRESULT, 'SetRelativePath',
+                  ( ['in'], c_wchar_p, 'pszPathRel' ),
+                  ( ['in'], DWORD, 'dwReserved' )),
+        COMMETHOD([], HRESULT, 'Resolve',
+                  ( ['in'], c_int, 'hwnd' ),
+                  ( ['in'], DWORD, 'fFlags' )),
+        COMMETHOD([], HRESULT, 'SetPath',
+                  ( ['in'], c_wchar_p, 'pszFile' )),
+        ]
+
+    def GetPath(self, flags=SLGP_SHORTPATH):
+        buf = create_unicode_buffer(MAX_PATH)
+        # We're not interested in WIN32_FIND_DATA
+        self.__com_GetPath(buf, MAX_PATH, None, flags)
+        return buf.value
+
+    def GetDescription(self):
+        buf = create_unicode_buffer(1024)
+        self.__com_GetDescription(buf, 1024)
+        return buf.value
+
+    def GetWorkingDirectory(self):
+        buf = create_unicode_buffer(MAX_PATH)
+        self.__com_GetWorkingDirectory(buf, MAX_PATH)
+        return buf.value
+
+    def GetArguments(self):
+        buf = create_unicode_buffer(1024)
+        self.__com_GetArguments(buf, 1024)
+        return buf.value
+
+    def GetIconLocation(self):
+        iIcon = c_int()
+        buf = create_unicode_buffer(MAX_PATH)
+        self.__com_GetIconLocation(buf, MAX_PATH, byref(iIcon))
+        return buf.value, iIcon.value
+
+class ShellLink(CoClass):
+    u'ShellLink class'
+    _reg_clsid_ = GUID('{00021401-0000-0000-C000-000000000046}')
+    _idlflags_ = []
+    _com_interfaces_ = [IShellLinkW, IShellLinkA]
+
+
+if __name__ == "__main__":
+
+    import sys
+    import comtypes
+    from comtypes.client import CreateObject
+    from comtypes.persist import IPersistFile
+
+
+
+    shortcut = CreateObject(ShellLink)
+    print shortcut
+    ##help(shortcut)
+
+    shortcut.SetPath(sys.executable)
+
+    shortcut.SetDescription("Python %s" % sys.version)
+    shortcut.SetIconLocation(sys.executable, 1)
+
+    print shortcut.GetPath(2)
+    print shortcut.GetIconLocation()
+
+    pf = shortcut.QueryInterface(IPersistFile)
+    pf.Save("foo.lnk", True)
+    print pf.GetCurFile()

comtypes/test/TestComServer.idl

 	[id(17), helpstring("execute a statement")]
 	HRESULT Exec2([in] BSTR what);
 
+	[id(18), helpstring("a method with [in] and [out] args in mixed order")]
+	HRESULT MixedInOut([in] int a, [out] int *b, [in] int c, [out] int *d);
 };
 
 [

comtypes/test/TestComServer.py

         self._name = name
         return S_OK
 
+##    [id(18), helpstring("a method with [in] and [out] args in mixed order")]
+##    HRESULT MixedInOut([in] int a, [out] int *b, [in] int c, [out] int *d);
+    def MixedInOut(self, a, c):
+        return a+1, c+1
+    
 if __name__ == "__main__":
     try:
         from comtypes.server.register import UseCommandLine

comtypes/test/TestComServer.tlb

Binary file modified.

comtypes/test/test_comserver.py

-import unittest
+import unittest, sys
 from ctypes import *
 from ctypes.wintypes import *
 from comtypes.client import CreateObject, GetEvents, ShowEvents
         bytes = find_memleak(func)
         self.failIf(bytes, "Leaks %d bytes" % bytes)
 
+    def test_mixedinout(self):
+        o = self.create_object()
+        self.failUnlessEqual(o.MixedInOut(2, 4), (3, 5))
+
     def test_getname(self):
         from ctypes import byref, pointer
         from comtypes import BSTR
         def test_getname(self):
             pass
 
+        def test_mixedinout(self):
+            # Not sure about this; it raise 'Invalid Number of parameters'
+            # Is mixed [in], [out] args not compatible with IDispatch???
+            pass
+
     if is_resource_enabled("ui"):
         class TestLocalServer_win32com(TestInproc_win32com):
             def create_object(self):
 
     # The following functions are never called, they only contain doctests:
 
-    def ShowEvents(self):
-        '''
-        >>> from comtypes.client import CreateObject, ShowEvents
-        >>>
-        >>> o = CreateObject("TestComServerLib.TestComServer")
-        >>> con = ShowEvents(o)
-        # event found: ITestComServerEvents_EvalStarted
-        # event found: ITestComServerEvents_EvalCompleted
-        >>> result = o.eval("10. / 4")
-        Event ITestComServerEvents_EvalStarted(None, u'10. / 4')
-        Event ITestComServerEvents_EvalCompleted(None, u'10. / 4', VARIANT(vt=0x5, 2.5))
-        >>> result
-        2.5
-        >>>
-        '''
+    if sys.version_info >= (3, 0):
+        def ShowEvents(self):
+            '''
+            >>> from comtypes.client import CreateObject, ShowEvents
+            >>>
+            >>> o = CreateObject("TestComServerLib.TestComServer")
+            >>> con = ShowEvents(o)
+            # event found: ITestComServerEvents_EvalStarted
+            # event found: ITestComServerEvents_EvalCompleted
+            >>> result = o.eval("10. / 4")
+            Event ITestComServerEvents_EvalStarted(None, '10. / 4')
+            Event ITestComServerEvents_EvalCompleted(None, '10. / 4', VARIANT(vt=0x5, 2.5))
+            >>> result
+            2.5
+            >>>
+            '''
+    else:
+        def ShowEvents(self):
+            '''
+            >>> from comtypes.client import CreateObject, ShowEvents
+            >>>
+            >>> o = CreateObject("TestComServerLib.TestComServer")
+            >>> con = ShowEvents(o)
+            # event found: ITestComServerEvents_EvalStarted
+            # event found: ITestComServerEvents_EvalCompleted
+            >>> result = o.eval("10. / 4")
+            Event ITestComServerEvents_EvalStarted(None, u'10. / 4')
+            Event ITestComServerEvents_EvalCompleted(None, u'10. / 4', VARIANT(vt=0x5, 2.5))
+            >>> result
+            2.5
+            >>>
+            '''
 
         # The following test, if enabled, works but the testsuit
         # crashes elsewhere.  Is there s problem with SAFEARRAYs?
         >>> o =  CreateObject("TestComServerLib.TestComServer")
         >>> class EventHandler(object):
         ...     def EvalStarted(self, this, what):
-        ...         print "EvalStarted:", what
+        ...         print("EvalStarted: %s" % what)
         ...         return 0
         ...     def EvalCompleted(self, this, what, result):
-        ...         print "EvalCompleted:", what, "=", result.value
+        ...         print("EvalCompleted: %s = %s" % (what, result.value))
         ...         return 0
         ...
         >>>

comtypes/test/test_findgendir.py

-import new, os, unittest, sys, tempfile
+import types, os, unittest, sys, tempfile
+
+if sys.version_info >= (3, 0):
+    from imp import reload
 
 import comtypes
 import comtypes.client
 
 from comtypes.client._code_cache import _get_appdata_dir
 
+imgbase = os.path.splitext(os.path.basename(sys.executable))[0]
+
 class Test(unittest.TestCase):
     """Test the comtypes.client._find_gen_dir() function in several
     simulated environments.
         self.orig_comtypesgen = sys.modules["comtypes.gen"]
         del sys.modules["comtypes.gen"]
         del comtypes.gen
-        mod = sys.modules["comtypes.gen"] = new.module("comtypes.gen")
+        mod = sys.modules["comtypes.gen"] = types.ModuleType("comtypes.gen")
         mod.__path__ = []
         comtypes.gen = mod
 
         # %TEMP%\comtypes_cache\<imagebasename>-25
         # the image is python25.dll
         path = os.path.join(tempfile.gettempdir(),
-                            r"comtypes_cache\python%d%d-%d%d" % (ma, mi, ma, mi))
+                            r"comtypes_cache\%s%d%d-%d%d" % (imgbase, ma, mi, ma, mi))
         gen_dir = comtypes.client._find_gen_dir()
         self.failUnlessEqual(path, gen_dir)
 
         sys.frozen = "console_exe"
         # %TEMP%\comtypes_cache\<imagebasename>-25
         path = os.path.join(tempfile.gettempdir(),
-                            r"comtypes_cache\python-%d%d" % sys.version_info[:2])
+                            r"comtypes_cache\%s-%d%d" % (
+            imgbase, sys.version_info[0], sys.version_info[1]))
         gen_dir = comtypes.client._find_gen_dir()
         self.failUnlessEqual(path, gen_dir)
 
         sys.frozen = "windows_exe"
         # %TEMP%\comtypes_cache\<imagebasename>-25
         path = os.path.join(tempfile.gettempdir(),
-                            r"comtypes_cache\python-%d%d" % sys.version_info[:2])
+                            r"comtypes_cache\%s-%d%d" % (
+            imgbase, sys.version_info[0], sys.version_info[1]))
         gen_dir = comtypes.client._find_gen_dir()
         self.failUnlessEqual(path, gen_dir)
 

comtypes/test/test_showevents.py

         doctest.testmod(comtypes.test.test_showevents, optionflags=doctest.ELLIPSIS)
 
     # These methods are never called, they only contain doctests.
-    def IE_ShowEvents(self):
-        '''
-        >>> from comtypes.client import CreateObject, ShowEvents, PumpEvents
-        >>>
-        >>> o = CreateObject("InternetExplorer.Application")
-        >>> con = ShowEvents(o)
-        # event found: DWebBrowserEvents2_StatusTextChange
-        # event found: DWebBrowserEvents2_ProgressChange
-        # event found: DWebBrowserEvents2_CommandStateChange
-        # event found: DWebBrowserEvents2_DownloadBegin
-        # event found: DWebBrowserEvents2_DownloadComplete
-        # event found: DWebBrowserEvents2_TitleChange
-        # event found: DWebBrowserEvents2_PropertyChange
-        # event found: DWebBrowserEvents2_BeforeNavigate2
-        # event found: DWebBrowserEvents2_NewWindow2
-        # event found: DWebBrowserEvents2_NavigateComplete2
-        # event found: DWebBrowserEvents2_DocumentComplete
-        # event found: DWebBrowserEvents2_OnQuit
-        # event found: DWebBrowserEvents2_OnVisible
-        # event found: DWebBrowserEvents2_OnToolBar
-        # event found: DWebBrowserEvents2_OnMenuBar
-        # event found: DWebBrowserEvents2_OnStatusBar
-        # event found: DWebBrowserEvents2_OnFullScreen
-        # event found: DWebBrowserEvents2_OnTheaterMode
-        # event found: DWebBrowserEvents2_WindowSetResizable
-        # event found: DWebBrowserEvents2_WindowSetLeft
-        # event found: DWebBrowserEvents2_WindowSetTop
-        # event found: DWebBrowserEvents2_WindowSetWidth
-        # event found: DWebBrowserEvents2_WindowSetHeight
-        # event found: DWebBrowserEvents2_WindowClosing
-        # event found: DWebBrowserEvents2_ClientToHostWindow
-        # event found: DWebBrowserEvents2_SetSecureLockIcon
-        # event found: DWebBrowserEvents2_FileDownload
-        # event found: DWebBrowserEvents2_NavigateError
-        # event found: DWebBrowserEvents2_PrintTemplateInstantiation
-        # event found: DWebBrowserEvents2_PrintTemplateTeardown
-        # event found: DWebBrowserEvents2_UpdatePageStatus
-        # event found: DWebBrowserEvents2_PrivacyImpactedStateChange
-        # event found: DWebBrowserEvents2_NewWindow3
-        >>> res = o.Navigate2("http://www.python.org")
-        Event DWebBrowserEvents2_PropertyChange(None, u'{265b75c1-4158-11d0-90f6-00c04fd497ea}')
-        Event DWebBrowserEvents2_BeforeNavigate2(None, <POINTER(IWebBrowser2) ...>, VARIANT(vt=0x400c, byref(u'http://www.python.org/')), VARIANT(vt=0x400c, byref(0)), VARIANT(vt=0x400c, byref(None)), VARIANT(vt=0x400c, byref(VARIANT(vt=0x400c, byref(None)))), VARIANT(vt=0x400c, byref(None)), VARIANT(vt=0x400b, byref(False)))
-        Event DWebBrowserEvents2_DownloadBegin(None)
-        Event DWebBrowserEvents2_PropertyChange(None, u'{D0FCA420-D3F5-11CF-B211-00AA004AE837}')
-        >>> res = PumpEvents(0.01)
-        Event DWebBrowserEvents2_CommandStateChange(None, 2, False)
-        Event DWebBrowserEvents2_CommandStateChange(None, 1, False)
-        >>> res = o.Quit()
-        >>> res = PumpEvents(0.01)
-        Event DWebBrowserEvents2_OnQuit(None)
-        >>>
-        '''
-
+    if sys.version_info >= (3, 0):
+        def IE_ShowEvents(self):
+            '''
+            >>> from comtypes.client import CreateObject, ShowEvents, PumpEvents
+            >>>
+            >>> o = CreateObject("InternetExplorer.Application")
+            >>> con = ShowEvents(o)
+            # event found: DWebBrowserEvents2_StatusTextChange
+            # event found: DWebBrowserEvents2_ProgressChange
+            # event found: DWebBrowserEvents2_CommandStateChange
+            # event found: DWebBrowserEvents2_DownloadBegin
+            # event found: DWebBrowserEvents2_DownloadComplete
+            # event found: DWebBrowserEvents2_TitleChange
+            # event found: DWebBrowserEvents2_PropertyChange
+            # event found: DWebBrowserEvents2_BeforeNavigate2
+            # event found: DWebBrowserEvents2_NewWindow2
+            # event found: DWebBrowserEvents2_NavigateComplete2
+            # event found: DWebBrowserEvents2_DocumentComplete
+            # event found: DWebBrowserEvents2_OnQuit
+            # event found: DWebBrowserEvents2_OnVisible
+            # event found: DWebBrowserEvents2_OnToolBar
+            # event found: DWebBrowserEvents2_OnMenuBar
+            # event found: DWebBrowserEvents2_OnStatusBar
+            # event found: DWebBrowserEvents2_OnFullScreen
+            # event found: DWebBrowserEvents2_OnTheaterMode
+            # event found: DWebBrowserEvents2_WindowSetResizable
+            # event found: DWebBrowserEvents2_WindowSetLeft
+            # event found: DWebBrowserEvents2_WindowSetTop
+            # event found: DWebBrowserEvents2_WindowSetWidth
+            # event found: DWebBrowserEvents2_WindowSetHeight
+            # event found: DWebBrowserEvents2_WindowClosing
+            # event found: DWebBrowserEvents2_ClientToHostWindow
+            # event found: DWebBrowserEvents2_SetSecureLockIcon
+            # event found: DWebBrowserEvents2_FileDownload
+            # event found: DWebBrowserEvents2_NavigateError
+            # event found: DWebBrowserEvents2_PrintTemplateInstantiation
+            # event found: DWebBrowserEvents2_PrintTemplateTeardown
+            # event found: DWebBrowserEvents2_UpdatePageStatus
+            # event found: DWebBrowserEvents2_PrivacyImpactedStateChange
+            # event found: DWebBrowserEvents2_NewWindow3
+            >>> res = o.Navigate2("http://www.python.org")
+            Event DWebBrowserEvents2_PropertyChange(None, '{265b75c1-4158-11d0-90f6-00c04fd497ea}')
+            Event DWebBrowserEvents2_BeforeNavigate2(None, <POINTER(IWebBrowser2) ...>, VARIANT(vt=0x400c, byref('http://www.python.org/')), VARIANT(vt=0x400c, byref(0)), VARIANT(vt=0x400c, byref(None)), VARIANT(vt=0x400c, byref(VARIANT(vt=0x400c, byref(None)))), VARIANT(vt=0x400c, byref(None)), VARIANT(vt=0x400b, byref(False)))
+            Event DWebBrowserEvents2_DownloadBegin(None)
+            Event DWebBrowserEvents2_PropertyChange(None, '{D0FCA420-D3F5-11CF-B211-00AA004AE837}')
+            >>> res = PumpEvents(0.01)
+            Event DWebBrowserEvents2_CommandStateChange(None, 2, False)
+            Event DWebBrowserEvents2_CommandStateChange(None, 1, False)
+            >>> res = o.Quit()
+            >>> res = PumpEvents(0.01)
+            Event DWebBrowserEvents2_OnQuit(None)
+            >>>
+            '''
+    else:
+        def IE_ShowEvents(self):
+            '''
+            >>> from comtypes.client import CreateObject, ShowEvents, PumpEvents
+            >>>
+            >>> o = CreateObject("InternetExplorer.Application")
+            >>> con = ShowEvents(o)
+            # event found: DWebBrowserEvents2_StatusTextChange
+            # event found: DWebBrowserEvents2_ProgressChange
+            # event found: DWebBrowserEvents2_CommandStateChange
+            # event found: DWebBrowserEvents2_DownloadBegin
+            # event found: DWebBrowserEvents2_DownloadComplete
+            # event found: DWebBrowserEvents2_TitleChange
+            # event found: DWebBrowserEvents2_PropertyChange
+            # event found: DWebBrowserEvents2_BeforeNavigate2
+            # event found: DWebBrowserEvents2_NewWindow2
+            # event found: DWebBrowserEvents2_NavigateComplete2
+            # event found: DWebBrowserEvents2_DocumentComplete
+            # event found: DWebBrowserEvents2_OnQuit
+            # event found: DWebBrowserEvents2_OnVisible
+            # event found: DWebBrowserEvents2_OnToolBar
+            # event found: DWebBrowserEvents2_OnMenuBar
+            # event found: DWebBrowserEvents2_OnStatusBar
+            # event found: DWebBrowserEvents2_OnFullScreen
+            # event found: DWebBrowserEvents2_OnTheaterMode
+            # event found: DWebBrowserEvents2_WindowSetResizable
+            # event found: DWebBrowserEvents2_WindowSetLeft
+            # event found: DWebBrowserEvents2_WindowSetTop
+            # event found: DWebBrowserEvents2_WindowSetWidth
+            # event found: DWebBrowserEvents2_WindowSetHeight
+            # event found: DWebBrowserEvents2_WindowClosing
+            # event found: DWebBrowserEvents2_ClientToHostWindow
+            # event found: DWebBrowserEvents2_SetSecureLockIcon
+            # event found: DWebBrowserEvents2_FileDownload
+            # event found: DWebBrowserEvents2_NavigateError
+            # event found: DWebBrowserEvents2_PrintTemplateInstantiation
+            # event found: DWebBrowserEvents2_PrintTemplateTeardown
+            # event found: DWebBrowserEvents2_UpdatePageStatus
+            # event found: DWebBrowserEvents2_PrivacyImpactedStateChange
+            # event found: DWebBrowserEvents2_NewWindow3
+            >>> res = o.Navigate2("http://www.python.org")
+            Event DWebBrowserEvents2_PropertyChange(None, u'{265b75c1-4158-11d0-90f6-00c04fd497ea}')
+            Event DWebBrowserEvents2_BeforeNavigate2(None, <POINTER(IWebBrowser2) ...>, VARIANT(vt=0x400c, byref(u'http://www.python.org/')), VARIANT(vt=0x400c, byref(0)), VARIANT(vt=0x400c, byref(None)), VARIANT(vt=0x400c, byref(VARIANT(vt=0x400c, byref(None)))), VARIANT(vt=0x400c, byref(None)), VARIANT(vt=0x400b, byref(False)))
+            Event DWebBrowserEvents2_DownloadBegin(None)
+            Event DWebBrowserEvents2_PropertyChange(None, u'{D0FCA420-D3F5-11CF-B211-00AA004AE837}')
+            >>> res = PumpEvents(0.01)
+            Event DWebBrowserEvents2_CommandStateChange(None, 2, False)
+            Event DWebBrowserEvents2_CommandStateChange(None, 1, False)
+            >>> res = o.Quit()
+            >>> res = PumpEvents(0.01)
+            Event DWebBrowserEvents2_OnQuit(None)
+            >>>
+            '''
+        
     def IE_GetEvents():
         """
         >>> from comtypes.client import CreateObject, GetEvents, PumpEvents
         >>> o =  CreateObject("InternetExplorer.Application")
         >>> class EventHandler(object):
         ...     def DWebBrowserEvents2_PropertyChange(self, this, what):
-        ...         print "PropertyChange:", what
+        ...         print("PropertyChange: %s" % what)
         ...         return 0
         ...
         >>>
         >>> o = CreateObject("Excel.Application")
         >>> class Sink(object):
         ...    def AppEvents_NewWorkbook(self, this, workbook):
-        ...        print "AppEvents_NewWorkbook", workbook
+        ...        print("AppEvents_NewWorkbook %s" % workbook)
         ...
         >>>
         >>> con = GetEvents(o, Sink())
         >>>
         >>> class Sink(object):
         ...    def AppEvents_NewWorkbook(self, workbook):
-        ...        print "AppEvents_NewWorkbook(no this)", workbook
+        ...        print("AppEvents_NewWorkbook(no this) %s" % workbook)
         ...
         >>>
         >>> con = GetEvents(o, Sink())

comtypes/test/test_variant.py

         import sys
         v = VARIANT()
 
-        v.value = sys.maxint
-        self.failUnlessEqual(v.value, sys.maxint)
-        self.failUnlessEqual(type(v.value), int)
+        if (hasattr(sys, "maxint")):
+            # this test doesn't work in Python 3000
+            v.value = sys.maxint
+            self.failUnlessEqual(v.value, sys.maxint)
+            self.failUnlessEqual(type(v.value), int)
 
-        v.value += 1
-        self.failUnlessEqual(v.value, sys.maxint+1)
-        self.failUnlessEqual(type(v.value), long)
+            v.value += 1
+            self.failUnlessEqual(v.value, sys.maxint+1)
+            self.failUnlessEqual(type(v.value), long)
 
         v.value = 1L
         self.failUnlessEqual(v.value, 1)

comtypes/tools/codegenerator.py

 # Code generator to generate code for everything contained in COM type
 # libraries.
 import os
+import cStringIO
 from comtypes.tools import typedesc
 import comtypes.client
 import comtypes.client._generate
 
-version = "$Rev: 411 $"[6:-2]
+version = "$Rev: 482 $"[6:-2]
 
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
 
 class lcid(object):
     def __repr__(self):
             size += a - size % a
         if isStruct:
             if size != f.offset:
-                raise PackingError, "field %s offset (%s/%s)" % (f.name, size, f.offset)
+                raise PackingError("field %s offset (%s/%s)" % (f.name, size, f.offset))
             size += s
         else:
             size = max(size, s)
         total_align = max(total_align, a)
     if total_align != struct.align:
-        raise PackingError, "total alignment (%s/%s)" % (total_align, struct.align)
+        raise PackingError("total alignment (%s/%s)" % (total_align, struct.align))
     a = total_align
     if pack is not None:
         a = min(pack, a)
     if size % a:
         size += a - size % a
     if size != struct.size:
-        raise PackingError, "total size (%s/%s)" % (size, struct.size)
+        raise PackingError("total size (%s/%s)" % (size, struct.size))
 
 def calc_packing(struct, fields):
     # try several packings, starting with unspecified packing
             if pack is None:
                 return None
             return pack/8
-    raise PackingError, "PACKING FAILED: %s" % details
+    raise PackingError("PACKING FAILED: %s" % details)
 
 class PackingError(Exception):
     pass
     def __init__(self, ofi, known_symbols=None):
         self._externals = {}
         self.output = ofi
-        self.stream = StringIO.StringIO()
-        self.imports = StringIO.StringIO()
+        self.stream = cStringIO.StringIO()
+        self.imports = cStringIO.StringIO()
 ##        self.stream = self.imports = self.output
         self.known_symbols = known_symbols or {}
 
         for item in items:
             self.generate(item)
 
-    def cmpitems(a, b):
-        a = getattr(a, "location", None)
-        b = getattr(b, "location", None)
-        if a is None: return -1
-        if b is None: return 1
-        return cmp(a[0],b[0]) or cmp(int(a[1]),int(b[1]))
-    cmpitems = staticmethod(cmpitems)
-
     def _make_relative_path(self, path1, path2):
         """path1 and path2 are pathnames.
         Return path1 as a relative path to path2, if possible.
     def generate_code(self, items, filename=None):
         self.filename = filename
         if filename is not None:
+            # Hm, what is the CORRECT encoding?
+            print >> self.output, "# -*- coding: mbcs -*-"
             if os.path.isabs(filename):
                 # absolute path
                 print >> self.output, "typelib_path = %r" % filename
         while items:
             loops += 1
             self.more = set()
-            self.generate_all(sorted(items, self.cmpitems))
+            self.generate_all(items)
 
             items |= self.more
             items -= self.done
             print >> self.stream, "        item, fetched = self.Next(1)"
             print >> self.stream, "        if fetched:"
             print >> self.stream, "            return item"
-            print >> self.stream, "        raise IndexError, index"
+            print >> self.stream, "        raise IndexError(index)"
             print >> self.stream
 
     def ComInterfaceBody(self, body):
             if isinstance(m, typedesc.ComMethod):
                 self.make_ComMethod(m, "dual" in body.itf.idlflags)
             else:
-                raise TypeError, "what's this?"
+                raise TypeError("what's this?")
 
         print >> self.stream, "]"
         print >> self.stream, "################################################################"
             elif isinstance(m, typedesc.DispProperty):
                 self.generate(m.typ)
             else:
-                raise TypeError, m
+                raise TypeError(m)
 
         self.need_dispid()
         self.need_DISPMETHOD()
             elif isinstance(m, typedesc.DispProperty):
                 self.make_DispProperty(m)
             else:
-                raise TypeError, m
+                raise TypeError(m)
         print >> self.stream, "]"
 
     ################################################################

comtypes/tools/tlbparser.py

             itemtype = self.make_type(tdesc._.lptdesc[0], tinfo)
             return midlSAFEARRAY(itemtype)
 
-        raise "NYI", tdesc.vt
+        raise NotImplementedError(tdesc.vt)
 
     ################################################################
 
             elif fd.callconv == typeinfo.CC_STDCALL:
                 attributes = "__stdcall__"
             else:
-                raise ValueError, "calling convention %d" % fd.callconv
+                raise ValueError("calling convention %d" % fd.callconv)
 
             func = typedesc.Function(func_name, returns, attributes, extern=1)
             if func_doc is not None:
             if isinstance(value, typedesc.External):
                 return
             # BUG: We try to register an item that's already registered.
-            raise ValueError, "Bug: Multiple registered name '%s': %r" % (name, value)
+            raise ValueError("Bug: Multiple registered name '%s': %r" % (name, value))
         self.items[fullname] = value
 
     def parse_typeinfo(self, tinfo):

comtypes/typeinfo.py

         elif kind == DESCKIND_IMPLICITAPPOBJ:
             raise NotImplementedError
         elif kind == DESCKIND_NONE:
-            raise NameError, "Name %s not found" % name
+            raise NameError("Name %s not found" % name)
 
     def BindType(self, name, lHashVal=0):
         "Bind a type, and return both the typeinfo and typecomp for it."
 
 .. _ctypes: http://docs.python.org/lib/module-ctypes.html
 """
-import comtypes
-
 import sys, os
 from distutils.core import setup, Command, DistutilsOptionError
 
+try:
+    from distutils.command.build_py import build_py_2to3 as build_py
+except ImportError:
+    from distutils.command.build_py import build_py
+
 class test(Command):
     # Original version of this class posted
     # by Berthold Hoellmann to distutils-sig@python.org
     boolean_options = ["refcounts"]
 
     def initialize_options(self):
-        self.build_base = 'build'
         self.use_resources = ""
         self.refcounts = False
         self.tests = "comtypes.test"
     # finalize_options()
 
     def run(self):
-        self.run_command('build')
+        build = self.reinitialize_command('build')
+        build.run()
+        if build.build_lib is not None:
+            sys.path.insert(0, build.build_lib)
 
         import comtypes.test
         comtypes.test.use_resources.extend(self.use_resources)
 
         for name in self.tests:
             package = __import__(name, globals(), locals(), ['*'])
-            print "Testing package", name, (sys.version, sys.platform, os.name)
+            sys.stdout.write("Testing package %s %s\n"
+                             % (name, (sys.version, sys.platform, os.name)))
             comtypes.test.run_tests(package,
                                     "test_*.py",
                                     self.verbose,
     'Intended Audience :: Developers',
     'License :: OSI Approved :: MIT License',
     'Operating System :: Microsoft :: Windows',
-##    'Operating System :: Microsoft :: Windows CE', # pypi doesn't have this classifier
+    'Operating System :: Microsoft :: Windows :: Windows CE',
     'Programming Language :: Python',
+    'Programming Language :: Python :: 2'
+    'Programming Language :: Python :: 2.4'
+    'Programming Language :: Python :: 2.5'
+    'Programming Language :: Python :: 2.6'
+    'Programming Language :: Python :: 3'
+    'Programming Language :: Python :: 3.0'
     'Topic :: Software Development :: Libraries :: Python Modules',
     ]
 
+def read_version():
+    # Determine the version number by reading it from the file
+    # 'comtypes\__init__.py'.  We cannot import this file (with py3,
+    # at least) because it is in py2.x syntax.
+    ns = {}
+    for line in open("comtypes\__init__.py"):
+        if line.startswith("__version__ = "):
+            exec(line, ns)
+            break
+    return ns["__version__"]
+
+if sys.version_info >= (3, 0):
+    # install_script does not work in Python 3 (python bug)
+    # Another distutils bug: it doesn't accept an empty options dict
+    options = {"foo": {}}
+##    options = {}
+else:
+    options={"bdist_wininst": {"install_script": "clear_comtypes_cache.py"}}
+
 setup(name="comtypes",
       description="Pure Python COM package",
       long_description = __doc__,
       classifiers=classifiers,
 
       scripts=["clear_comtypes_cache.py"],
-      options={"bdist_wininst": {"install_script": "clear_comtypes_cache.py"}},
+      options=options,
 
-      cmdclass = {'test': test},
+      cmdclass = {'test': test,
+                  'build_py': build_py},
 
-      version=comtypes.__version__,
+      version=read_version(),
       packages=["comtypes",
                 "comtypes.client",
                 "comtypes.server",
 
 This document describes |comtypes| version 0.4.1.
 
+NEW: The beginning of the documentation for implementing COM servers in
+comtypes is here: comtypes_server_
+
 .. contents::
 
 The comtypes.client package
 of the object when something happens.  The standard COM mechanism is
 based on so-called *connection points*.
 
+Note: For the rules that you should observe when implementing event
+handlers you should read the implementing_COM_methods_ section in the
+comtypes server document.
+
 ``GetEvents(source, sink, interface=None)``
     This functions connects an event sink to the COM object
     ``source``.
    WorkbookOpened <POINTER(_Workbook) ptr=0x291944 at 1853120>
    >>>
 
+Note that event handler methods support the same calling convention as
+COM method implementations in comtypes.  So the remarks about
+implementing_COM_methods_ should be observed.
 
 Typelibraries
 +++++++++++++
 
 .. _sourceforge: http://sourceforge.net/projects/comtypes
 
-.. _files: http://sourceforge.net/project/showfiles.php?group_id=115265
+.. _files: http://sourceforge.net/project/showfiles.php?group_id=115265
+
+.. _comtypes_server: server.html
+
+.. _implementing_COM_methods: server.html#implementing-com-methods
 set CMD=c:\python25\python -u rst2html.py %OPTIONS% %CSS%
 
 %CMD% index.rst index.html
+%CMD% server.rst server.html
 %CMD% scriptcontrol.rst scriptcontrol.html
 
 if not "%1" == "" start %1.html
+import comtypes
+import comtypes.server.localserver
+
+from comtypes.client import GetModule
+GetModule("mytypelib.tlb")
+
+from comtypes.gen.MyTypeLib import MyObject
+
+class MyObjectImpl(MyObject):
+    # registry entries
+    _reg_threading_ = "Both"
+    _reg_progid_ = "MyTypeLib.MyObject.1"
+    _reg_novers_progid_ = "MyTypeLib.MyObject"
+    _reg_desc_ = "Simple COM server for testing"
+    _reg_clsctx_ = comtypes.CLSCTX_INPROC_SERVER | comtypes.CLSCTX_LOCAL_SERVER
+    _regcls_ = comtypes.server.localserver.REGCLS_MULTIPLEUSE
+
+    def MyMethod(self, a, b):
+        return a + b
+
+if __name__ == "__main__":
+    from comtypes.server.register import UseCommandLine
+    UseCommandLine(MyObjectImpl)

web/mytypelib.idl

+import "oaidl.idl";
+import "ocidl.idl";
+
+[
+        uuid(11C65963-BD45-4B56-A06E-1610532B8613),
+        dual,
+        oleautomation
+]
+interface IMyInterface : IDispatch {
+        HRESULT MyMethod([in] INT a, [in] INT b, [out, retval] INT *presult);
+}
+
+[
+	uuid(F0D8338A-BDC1-45D7-A14F-27D64E7BCA18)
+]
+library MyTypeLib
+{
+        importlib("stdole2.tlb");
+	
+        [uuid(FBA0A6D0-B775-40D7-924A-B593B6EFA091)]
+		coclass MyObject {
+		[default] interface IMyInterface;
+        };
+};
+#########################
+COM servers with comtypes
+#########################
+
+|comtypes| is a *pure Python* COM package based on the ctypes_ ffi
+foreign function library.  **ctypes** is included in Python 2.5 and
+later, it is also available for Python 2.4 as separate download.
+
+The |comtypes| package makes it easy to access and implement both
+custom and dispatch based COM interfaces.
+
+This document describes |comtypes| version 0.5.
+
+.. contents::
+
+Implementing a simple COM object
+********************************
+
+To implement a COM server object in comtypes you need to write a type
+library describing the coclass, the interface(s) that the object
+implements, and (optional) the event interface that the object
+supports.  Also you have to write a Python module that defines a class
+which implements the object itself.
+
+We will present a short example here that does actually work.
+
+Define the COM interface
+++++++++++++++++++++++++
+
+Start writing an IDL file.  It is a good idea to define ``dual``
+interfaces, and only use automation compatible data types.
+
+.. sourcecode:: c
+
+  import "oaidl.idl";
+  import "ocidl.idl";
+
+  [
+          uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
+          dual,
+          oleautomation
+  ]
+  interface IMyInterface : IDispatch {
+          HRESULT MyMethod([in] INT a, [in] INT b, [out, retval] INT *presult);
+  }
+
+  [
+  	uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
+  ]
+  library MyTypeLib
+  {
+          importlib("stdole2.tlb");
+  	
+          [uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)]
+  		coclass MyObject {
+  		[default] interface IMyInterface;
+          };
+  };
+
+Please note that you must replace the 'xxxx' placeholders in the
+section above with separate GUIDs that you must generate yourself.
+You can use Python to generate unique GUIDs by running this in a
+windows console:
+
+.. sourcecode:: shell
+