Commits

Jason Scheirer committed e699c66

0.6.2 (trunk)

  • Participants
  • Parent commits 334efbf

Comments (0)

Files changed (39)

+2010-04-09  Thomas Heller  <theller@python.net>
+
+	* comtypes.client.dynamic: Added _FlagAsMethod(*names) method
+	to the _Dispatch class.
+
+	This allows to flag these attribute names as being methods.  Some
+	objects do not correctly differentiate methods and properties,
+	leading to problems when calling these methods.
+
+2010-02-23  Thomas Heller  <theller@python.net>
+
+	* pointers and byref() objects will now be stored in a VARIANT as
+	VT_BYREF type.  This allows to call dispinterface methods with
+	BYREF arguments.
+
+2010-01-21  Thomas Heller  <theller@python.net>
+
+	* Add a __repr__ method to named_property and bound_named_property
+	object.
+
+2010-01-15  Thomas Heller  <theller@python.net>
+
+	* Bump version number to 0.6.3dev: development version.
+
+2010-01-15  Thomas Heller  <theller@python.net>
+
+	* Bumped version number to 0.6.2.
+
+	* CoUninitialize() returns nothing, not HRESULT.
+
+2009-12-22  Thomas Heller  <theller@python.net>
+
+	* Bug fixes for event handlers implemented in Python.
+
+2009-12-11  Thomas Heller  <theller@python.net>
+
+	* COM servers implemented in Python can now fire events; it did
+	not work in some cases.
+
+	* When an exception occurs in a COM event handler, a traceback is
+	printed.
+
+2009-11-26  Thomas Heller  <theller@python.net>
+
+	* Allow typelib wrappers that contain 'SAFEARRAY(VARIANT*)'
+	parameter types to be imported.  Calling these methods will fail,
+	though.
+
+2009-11-13  Thomas Heller  <theller@python.net>
+
+	* To avoid a memory leak when PyInitialize()/PyUninitialize() are
+	called several times, return S_FALSE from inproc server's
+	DllCanUnloadNow().
+
+2009-11-05  Thomas Heller  <theller@python.net>
+
+	* COMObject subclasses can now implement a _final_release_()
+	method to free up resources, for example.  This method is called
+	when the COM reference count reaches zero.
+
+	* Implement __hash__ method in dynamic dispatch classes.  This
+	fixes a 'python -3' warning.
+
+	* comtypes\safearray.py: When numpy is not installed, creating
+	safearrays took a very long time.  This is fixed now.
+
+	* comtypes\test\test_server.py: New way to test COM client and
+	server.  Work in progress.
+
+2009-10-22  Thomas Heller  <theller@python.net>
+
+	* Support broken COM objects that provide IProvideClassInfo2, but
+	not IProvideClassInfo (although the latter is derived from the
+	former).  See
+	http://sourceforge.net/tracker/index.php?func=detail&aid=2829887&group_id=115265&atid=692942
+
+	* Fixed a regression from the 0.4 version, [out] parameters didn't
+	accept pointers or arrays any longer. Thanks again to Michael
+	Eddington.
+
+2009-10-19  Thomas Heller  <theller@python.net>
+
+	* Fix a memory leak in Python COM servers.  Thanks to Michael
+	Eddington for the patch.
+
+2009-10-02  Thomas Heller  <theller@python.net>
+
+	* comtypes\test\test_server.py: Start a better approach to test
+	both COM object calls and COM object implementations.
+
+2009-09-09  Thomas Heller  <theller@python.net>
+
+	* Fix returning SAFEARRAY of VT_RECORDs.  Based on a patch from
+	Eduardo Arias.
+
+	* New module comtypes.viewobject, contains the interfaces
+	IViewObject, IViewObject2, IViewObjectEx.  IAdviseSink is faked.
+
+	* Change version number to 0.6.2dev.
+
+2009-09-04  Thomas Heller  <theller@python.net>
+
+	* CoTaskMemFree does not return a HRESULT.  Patch from James Teh.
+
+2009-08-19  Thomas Heller  <theller@python.net>
+
+	* Bumped version number to 0.6.1.
+
+2009-08-07  Thomas Heller  <theller@python.net>
+
+	* When an interface was specified in the call to
+	IClassObject.CreateInstance, return that instead of calling
+	GetBestInterface.  Patch from James Teh.
+
+2009-08-04  Thomas Heller  <theller@python.net>
+
+	* Added comtypes.CoGetClassObject() low-level function,
+	comtypes.client.GetClassObject() high-level function, and
+	implemented a pythonic interface to IClassFactory's CreateInstance
+	method:
+
+	    def CreateInstance(self,
+			       punkouter=None,
+			       interface=None,
+			       dynamic=False)
+
+	* Added the 'dynamic=False' parameter to the
+	comtypes.client.CoGetObject and comtypes.client.GetActiveObject
+	functions.  Suggested by James Teh.
+
+2009-06-17  Thomas Heller  <theller@python.net>
+
+	* comtypes.automation: Support VT_I8 and VT_UI8 SAFEARRAYs.
+
+	* comtypes._comobject: Restore compatibility with Python 2.3.
+
+	* Add the comtypes.IServiceProvider interface.  Based on a patch
+	from Michael Curran.
+
+2009-04-30  Thomas Heller  <theller@python.net>
+
+	* Change version number in repository to 0.6.0.2dev.
+
+	* Replace the VARIANTEnumerator implementation class in
+	comtypes.server.automation with a new one which should actually be
+	usable.
+
+	* A completely new way how localserver and inproc server instances
+	are managed:
+
+		A comtypes.LocalServer or comtypes.InprocServer instance
+		is attached to the comtypes.COMObject class at runtime.
+
+		These changes keep localserver running as long as
+		COMObject instances are alive.
+
+2009-04-29  Thomas Heller  <theller@python.net>
+
+	* comtypes.errorinfo.ReportException now takes an additional
+	'stacklevel' named argument.
+
+	* Add E_OUTOFMEMORY hresult code.
+
+	* Register the InprocServer32 only when running as script or
+	py2exe dll, not when running as py2exe exe server.
+	
+2009-04-25  Thomas Heller  <theller@python.net>
+
+	* SAFEARRAYs can now also be created from multi-dimensional numpy
+	arrays.
+
+2009-04-23  Thomas Heller  <theller@python.net>
+
+	* Change version number in repository to 0.6.0.1dev.
+
+	* SAFEARRAYs can now also be created from array.array objects, and
+	from (one-dimensional) numpy arrays.  This is a lot faster than
+	creating them from Python lists or tuples, at least for large
+	arrays.
+
+	* ctypes instances like c_int, c_ubyte, and so on can now be
+	assigned to VARIANT().value.  This allows to force creation of
+	VARIANTs with the corresponding typecodes V_I4, VT_UI1 and alike.
+
+	* Accept typelibs that contain SAFEARRAY(char).
+
+2009-03-17  Thomas Heller  <theller@python.net>
+
+	* Fixed the return type of ITypeLib::ReleaseTLibAttr, which is
+	documented wrongly in MSDN.  The return type is void, not HRESULT.
+	Reported to cause crashes on Windows 7.
+
+2009-01-29  Thomas Heller  <theller@python.net>
+
+	* Restore compatibility with Python 2.3.
+	
+	* comtypes\client\_code_cache.py: Add missing 'import types' in
+	comtypes\client\_code_cache.py.
+
 2008-12-19  Thomas Heller  <theller@python.net>
 
 	* Bumped version number to 0.6.0.
+	comtypes 0.6.1 released.
+
+Summary of important changes:
+
+	- SAFEARRAYs can now be created from array.array objects, and
+	from multidimensional numpy arrays.
+
+	- Completely new implementation of the
+	comtypes.server.automation.VARIANTEnumerator class.
+
+	- Added comtypes.client.GetClassObject() function which
+	returns a factory able to create COM objects.
+
+	- Much improved IClassObject::CreateInstance() method.
+
+	- SAFEARRAYs now support the typecodes VT_I8 and VT_UI8.
+
+	- Added comtypes.IServiceProvider interface (unfortunately
+	there is no ChangleLog entry for this one).
+
+--------------------------------------------------------------------------
+
+Detailed changelog since version 0.6.0:
+
+
+
+2009-08-19  Thomas Heller  <theller@python.net>
+
+	* Bumped version number to 0.6.1.
+
+2009-08-07  Thomas Heller  <theller@python.net>
+
+	* When an interface was specified in the call to
+	IClassObject.CreateInstance, return that instead of calling
+	GetBestInterface.  Patch from James Teh.
+
+2009-08-04  Thomas Heller  <theller@python.net>
+
+	* Added comtypes.CoGetClassObject() low-level function,
+	comtypes.client.GetClassObject() high-level function, and
+	implemented a pythonic interface to IClassFactory's CreateInstance
+	method:
+
+	    def CreateInstance(self,
+			       punkouter=None,
+			       interface=None,
+			       dynamic=False)
+
+	* Added the 'dynamic=False' parameter to the
+	comtypes.client.CoGetObject and comtypes.client.GetActiveObject
+	functions.  Suggested by James Teh.
+
+2009-06-17  Thomas Heller  <theller@python.net>
+
+	* comtypes.automation: Support VT_I8 and VT_UI8 SAFEARRAYs.
+
+	* comtypes._comobject: Restore compatibility with Python 2.3.
+
+	* Add the comtypes.IServiceProvider interface.  Based on a patch
+	from Michael Curran.
+
+2009-04-30  Thomas Heller  <theller@python.net>
+
+	* Change version number in repository to 0.6.0.2dev.
+
+	* Replace the VARIANTEnumerator implementation class in
+	comtypes.server.automation with a new one which should actually be
+	usable.
+
+	* A completely new way how localserver and inproc server instances
+	are managed:
+
+		A comtypes.LocalServer or comtypes.InprocServer instance
+		is attached to the comtypes.COMObject class at runtime.
+
+		These changes keep localserver running as long as
+		COMObject instances are alive.
+
+2009-04-29  Thomas Heller  <theller@python.net>
+
+	* comtypes.errorinfo.ReportException now takes an additional
+	'stacklevel' named argument.
+
+	* Add E_OUTOFMEMORY hresult code.
+
+	* Register the InprocServer32 only when running as script or
+	py2exe dll, not when running as py2exe exe server.
+	
+2009-04-25  Thomas Heller  <theller@python.net>
+
+	* SAFEARRAYs can now also be created from multi-dimensional numpy
+	arrays.
+
+2009-04-23  Thomas Heller  <theller@python.net>
+
+	* Change version number in repository to 0.6.0.1dev.
+
+	* SAFEARRAYs can now also be created from array.array objects, and
+	from (one-dimensional) numpy arrays.  This is a lot faster than
+	creating them from Python lists or tuples, at least for large
+	arrays.
+
+	* ctypes instances like c_int, c_ubyte, and so on can now be
+	assigned to VARIANT().value.  This allows to force creation of
+	VARIANTs with the corresponding typecodes V_I4, VT_UI1 and alike.
+
+	* Accept typelibs that contain SAFEARRAY(char).
+
+2009-03-17  Thomas Heller  <theller@python.net>
+
+	* Fixed the return type of ITypeLib::ReleaseTLibAttr, which is
+	documented wrongly in MSDN.  The return type is void, not HRESULT.
+	Reported to cause crashes on Windows 7.
+
+2009-01-29  Thomas Heller  <theller@python.net>
+
+	* Restore compatibility with Python 2.3.
+	
+	* comtypes\client\_code_cache.py: Add missing 'import types' in
+	comtypes\client\_code_cache.py.
+
+	comtypes 0.6.2 released.
+
+Summary of important changes:
+
+	- Several bug fixes for COM event handlers implemented in Python.
+
+	- Allow typelib wrappers that (wrongly?) contain
+	'SAFEARRAY(VARIANT)*'.
+
+	- DllCanUnloadNow() always returns S_FALSE in comtypes inproc
+	COM servers.
+
+	- The COM interfaces IViewObject, IViewObject2, and
+	IViewObjectEx in the new module comtypes.viewobject.
+
+--------------------------------------------------------------------------
+
+Detailed changelog since version 0.6.0:
+
+2010-01-15  Thomas Heller  <theller@python.net>
+
+	* Bumped version number to 0.6.2.
+
+	* CoUninitialize() returns nothing, not HRESULT.
+
+2009-12-22  Thomas Heller  <theller@python.net>
+
+	* Bug fixes for event handlers implemented in Python.
+
+2009-12-11  Thomas Heller  <theller@python.net>
+
+	* COM servers implemented in Python can now fire events; it did
+	not work in some cases.
+
+	* When an exception occurs in a COM event handler, a traceback is
+	printed.
+
+2009-11-26  Thomas Heller  <theller@python.net>
+
+	* Allow typelib wrappers that contain 'SAFEARRAY(VARIANT*)'
+	parameter types to be imported.  Calling these methods will fail,
+	though.
+
+2009-11-13  Thomas Heller  <theller@python.net>
+
+	* To avoid a memory leak when PyInitialize()/PyUninitialize() are
+	called several times, return S_FALSE from inproc server's
+	DllCanUnloadNow().
+
+2009-11-05  Thomas Heller  <theller@python.net>
+
+	* COMObject subclasses can now implement a _final_release_()
+	method to free up resources, for example.  This method is called
+	when the COM reference count reaches zero.
+
+	* Implement __hash__ method in dynamic dispatch classes.  This
+	fixes a 'python -3' warning.
+
+	* comtypes\safearray.py: When numpy is not installed, creating
+	safearrays took a very long time.  This is fixed now.
+
+	* comtypes\test\test_server.py: New way to test COM client and
+	server.  Work in progress.
+
+2009-10-22  Thomas Heller  <theller@python.net>
+
+	* Support broken COM objects that provide IProvideClassInfo2, but
+	not IProvideClassInfo (although the latter is derived from the
+	former).  See
+	http://sourceforge.net/tracker/index.php?func=detail&aid=2829887&group_id=115265&atid=692942
+
+	* Fixed a regression from the 0.4 version, [out] parameters didn't
+	accept pointers or arrays any longer. Thanks again to Michael
+	Eddington.
+
+2009-10-19  Thomas Heller  <theller@python.net>
+
+	* Fix a memory leak in Python COM servers.  Thanks to Michael
+	Eddington for the patch.
+
+2009-10-02  Thomas Heller  <theller@python.net>
+
+	* comtypes\test\test_server.py: Start a better approach to test
+	both COM object calls and COM object implementations.
+
+2009-09-09  Thomas Heller  <theller@python.net>
+
+	* Fix returning SAFEARRAY of VT_RECORDs.  Based on a patch from
+	Eduardo Arias.
+
+	* New module comtypes.viewobject, contains the interfaces
+	IViewObject, IViewObject2, IViewObjectEx.  IAdviseSink is faked.
+
+	* Change version number to 0.6.2dev.
+
+2009-09-04  Thomas Heller  <theller@python.net>
+
+	* CoTaskMemFree does not return a HRESULT.  Patch from James Teh.
+
+2009-08-19  Thomas Heller  <theller@python.net>
+
+	* Bumped version number to 0.6.1.
+
+2009-08-07  Thomas Heller  <theller@python.net>
+
+	* When an interface was specified in the call to
+	IClassObject.CreateInstance, return that instead of calling
+	GetBestInterface.  Patch from James Teh.
+
+2009-08-04  Thomas Heller  <theller@python.net>
+
+	* Added comtypes.CoGetClassObject() low-level function,
+	comtypes.client.GetClassObject() high-level function, and
+	implemented a pythonic interface to IClassFactory's CreateInstance
+	method:
+
+	    def CreateInstance(self,
+			       punkouter=None,
+			       interface=None,
+			       dynamic=False)
+
+	* Added the 'dynamic=False' parameter to the
+	comtypes.client.CoGetObject and comtypes.client.GetActiveObject
+	functions.  Suggested by James Teh.
+
+2009-06-17  Thomas Heller  <theller@python.net>
+
+	* comtypes.automation: Support VT_I8 and VT_UI8 SAFEARRAYs.
+
+	* comtypes._comobject: Restore compatibility with Python 2.3.
+
+	* Add the comtypes.IServiceProvider interface.  Based on a patch
+	from Michael Curran.
+
+2009-04-30  Thomas Heller  <theller@python.net>
+
+	* Change version number in repository to 0.6.0.2dev.
+
+	* Replace the VARIANTEnumerator implementation class in
+	comtypes.server.automation with a new one which should actually be
+	usable.
+
+	* A completely new way how localserver and inproc server instances
+	are managed:
+
+		A comtypes.LocalServer or comtypes.InprocServer instance
+		is attached to the comtypes.COMObject class at runtime.
+
+		These changes keep localserver running as long as
+		COMObject instances are alive.
+
+2009-04-29  Thomas Heller  <theller@python.net>
+
+	* comtypes.errorinfo.ReportException now takes an additional
+	'stacklevel' named argument.
+
+	* Add E_OUTOFMEMORY hresult code.
+
+	* Register the InprocServer32 only when running as script or
+	py2exe dll, not when running as py2exe exe server.
+	
+2009-04-25  Thomas Heller  <theller@python.net>
+
+	* SAFEARRAYs can now also be created from multi-dimensional numpy
+	arrays.
+
+2009-04-23  Thomas Heller  <theller@python.net>
+
+	* Change version number in repository to 0.6.0.1dev.
+
+	* SAFEARRAYs can now also be created from array.array objects, and
+	from (one-dimensional) numpy arrays.  This is a lot faster than
+	creating them from Python lists or tuples, at least for large
+	arrays.
+
+	* ctypes instances like c_int, c_ubyte, and so on can now be
+	assigned to VARIANT().value.  This allows to force creation of
+	VARIANTs with the corresponding typecodes V_I4, VT_UI1 and alike.
+
+	* Accept typelibs that contain SAFEARRAY(char).
+
+2009-03-17  Thomas Heller  <theller@python.net>
+
+	* Fixed the return type of ITypeLib::ReleaseTLibAttr, which is
+	documented wrongly in MSDN.  The return type is void, not HRESULT.
+	Reported to cause crashes on Windows 7.
+
+2009-01-29  Thomas Heller  <theller@python.net>
+
+	* Restore compatibility with Python 2.3.
+	
+	* comtypes\client\_code_cache.py: Add missing 'import types' in
+	comtypes\client\_code_cache.py.
+
 Features planned
 ----------------
 
-Improve dynamic dispatch support, based on patches provided by Michael
-Curran.  See
-http://article.gmane.org/gmane.comp.python.comtypes.user/199
-
 Provide a .cab file for easy installation on Windows CE.

clear_comtypes_cache.py

-import os, sys
+import os
+import sys
+import shutil
 from ctypes import windll
 
 def is_cache():
 if directory:
     res = windll.user32.MessageBoxA(0, text, title, MB_YESNO|MB_ICONWARNING)
     if res == IDYES:
-        for f in os.listdir(directory):
-            fullpath = os.path.join(directory, f)
-            os.remove(fullpath)
-        os.rmdir(directory)
+        shutil.rmtree(directory)
         print("Removed directory %s" % directory)
     else:
         print("Directory %s NOT removed" % directory)
 from ctypes import *
 import sys
 
-if sys.version_info > (3,):
+if sys.version_info >= (2, 6):
     def binary(obj):
         return bytes(obj)
 else:
 _ole32 = oledll.ole32
 
 _StringFromCLSID = _ole32.StringFromCLSID
-_CoTaskMemFree = _ole32.CoTaskMemFree
+_CoTaskMemFree = windll.ole32.CoTaskMemFree
 _ProgIDFromCLSID = _ole32.ProgIDFromCLSID
 _CLSIDFromString = _ole32.CLSIDFromString
 _CLSIDFromProgID = _ole32.CLSIDFromProgID

comtypes/__init__.py

 # 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.6.0"
+__version__ = "0.6.3dev"
 
 import logging
 class NullHandler(logging.Handler):
 ################################################################
 # Initialization and shutdown
 _ole32 = oledll.ole32
+_ole32_nohresult = windll.ole32 # use this for functions that don't return a HRESULT
 
 COINIT_MULTITHREADED     = 0x0
 COINIT_APARTMENTTHREADED = 0x2
 # in which we are using COM
 def CoUninitialize():
     logger.debug("CoUninitialize()")
-    _ole32.CoUninitialize()
+    _ole32_nohresult.CoUninitialize()
 
-def shutdown(func=_ole32.CoUninitialize,
+def shutdown(func=_ole32_nohresult.CoUninitialize,
              _debug=logger.debug,
              _exc_clear=getattr(sys, "exc_clear", lambda: None)):
     # Make sure no COM pointers stay in exception frames.
                     "Return 'self.Item(*args, **kw)'"
                     return self.Item(*args, **kw)
 
-                @partial.noreplace
+##                @partial.noreplace
                 # does this make sense? It seems that all standard typelibs I've
                 # seen so far that support .Item also support ._NewEnum
                 def __getitem__(self, index):
                         raise IndexError("invalid index")
                     # Hm, should we call __ctypes_from_outparam__ on the result?
                     return result
+                __getitem__ = partial.noreplace(__getitem__)
 
         if has_name("_NewEnum"):
             class _(partial.partial, self):
                 # use propput (if any)
                 del methods[2]
             if nargs:
-                setattr(self, name, named_property(*methods))
+                setattr(self, name, named_property("%s.%s" % (self.__name__, name), *methods))
             else:
                 assert len(methods) <= 2
                 setattr(self, name, property(*methods))
                     else:
                         # parameter was passed, call .from_param() to
                         # convert it to a ctypes type.
-                        if type(atyp) is SIMPLETYPE:
+                        if getattr(v, "_type_", None) is atyp:
+                            # Array of or pointer to type 'atyp' was
+                            # passed, pointer to 'atyp' expected.
+                            pass
+                        elif type(atyp) is SIMPLETYPE:
                             # The from_param method of simple types
                             # (c_int, c_double, ...) returns a byref()
                             # object which we cannot use since later
 ##                # interface types, so suppress the warning in this case.
 ##                other = com_interface_registry[iid]
 ##                if self.__name__ != other.__name__ or self.__module__ != other.__module__:
-##                    text = "Multiple interface defn: %s, %s" % \
-##                           (self, other)
+##                    text = "Multiple interface defn: %s, %s" % (self, other)
 ##                    warnings.warn(text, UserWarning)
             com_interface_registry[iid] = self
             del iid
 
             # 'func' is a high level function calling the COM method
             func.__doc__ = doc
-            func.__name__ = name # for pyhelp
+            try:
+                func.__name__ = name # for pyhelp
+            except TypeError:
+                # In Python 2.3, __name__ is a readonly attribute
+                pass
             # make it an unbound method.  Remember, 'self' is a type here.
             mth = instancemethod(func, None, self)
 
                 # Hm, must be a descriptor where the __get__ method
                 # returns a bound object having __getitem__ and
                 # __setitem__ methods.
-                prop = named_property(*methods + [doc])
+                prop = named_property("%s.%s" % (self.__name__, name), *methods + [doc])
             # Again, we should not overwrite class attributes that are
             # already present.
             if hasattr(self, name):
 # Should they be implemented in C for speed?
 
 class bound_named_property(object):
-    def __init__(self, getter, setter, im_inst):
+    def __init__(self, name, getter, setter, im_inst):
+        self.name = name
         self.im_inst = im_inst
         self.getter = getter
         self.setter = setter
         else:
             self.setter(self.im_inst, index, value)
 
+    def __repr__(self):
+        return "<bound_named_property %r at %x>" % (self.name, id(self))
+
 class named_property(object):
-    def __init__(self, fget=None, fset=None, doc=None):
+    def __init__(self, name, fget=None, fset=None, doc=None):
+        self.name = name
         self.getter = fget
         self.setter = fset
         self.__doc__ = doc
     def __get__(self, im_inst, im_class=None):
         if im_inst is None:
             return self
-        return bound_named_property(self.getter, self.setter, im_inst)
+        return bound_named_property(self.name, self.getter, self.setter, im_inst)
 
     # Make this a data descriptor
     def __set__(self, obj):
         raise AttributeError("Unsettable attribute")
 
+    def __repr__(self):
+        return "<named_property %r at %x>" % (self.name, id(self))
+
 ################################################################
 
 class _compointer_meta(type(c_void_p), _cominterface_meta):
                 elif typ is POINTER(VARIANT):
                     defval = pointer(VARIANT.missing)
                 else:
-##                    msg = "'optional' only allowed for VARIANT and VARIANT*, not for %s" \
-##                                  % typ.__name__
+##                    msg = ("'optional' only allowed for VARIANT and VARIANT*, not for %s"
+##                           % typ.__name__)
 ##                    warnings.warn(msg, IDLWarning, stacklevel=2)
                     defval = typ()
         if defval is _NOTHING:
                   ( ['out'], POINTER(GUID), 'pClassID' )),
         ]
 
+class IServiceProvider(IUnknown):
+    _iid_ = GUID('{6D5140C1-7436-11CE-8034-00AA006009FA}')
+
+    # Overridden QueryService to make it nicer to use (passing it an
+    # interface and it returns a pointer to that interface)
+    def QueryService(self, serviceIID, interface):
+        p = POINTER(interface)()
+        self._QueryService(byref(serviceIID), byref(interface._iid_), byref(p))
+        return p
+
+    _methods_ = [
+        COMMETHOD([], HRESULT, 'QueryService',
+                  ( ['in'], POINTER(GUID), 'guidService' ),
+                  ( ['in'], POINTER(GUID), 'riid' ),
+                  ( ['in'], POINTER(c_void_p), 'ppvObject' ))
+        ]
+
 ################################################################
 def CoGetObject(displayname, interface):
     """Convert a displayname to a moniker, then bind and return the object
     _ole32.CoCreateInstance(byref(clsid), punkouter, clsctx, byref(iid), byref(p))
     return p
 
+def CoGetClassObject(clsid, clsctx=None, pServerInfo=None, interface=None):
+    if clsctx is None:
+        clsctx = CLSCTX_SERVER
+    if interface is None:
+        import comtypes.server
+        interface = comtypes.server.IClassFactory
+    p = POINTER(interface)()
+    _CoGetClassObject(clsid,
+                      clsctx,
+                      pServerInfo,
+                      interface._iid_,
+                      byref(p))
+    return p
+
 def GetActiveObject(clsid, interface=None):
     """Retrieves a pointer to a running object"""
     p = POINTER(IUnknown)()
         ('dwReserved2', c_ulong),
     ]
 COSERVERINFO = _COSERVERINFO
+_CoGetClassObject = _ole32.CoGetClassObject
+_CoGetClassObject.argtypes = [POINTER(GUID), DWORD, POINTER(COSERVERINFO),
+                              POINTER(GUID), POINTER(c_void_p)]
 
 class tagBIND_OPTS(Structure):
     _fields_ = [

comtypes/_comobject.py

         return E_NOTIMPL
     return _not_implemented
 
-def catch_errors(obj, mth, interface, mthname):
+def catch_errors(obj, mth, paramflags, interface, mthname):
     clsid = getattr(obj, "_reg_clsid_", None)
     def call_with_this(*args, **kw):
         try:
         if result is None:
             return S_OK
         return result
+    if paramflags == None:
+        has_outargs = False
+    else:
+        has_outargs = bool([x[0] for x in paramflags
+                            if x[0] & 2])
+    call_with_this.has_outargs = has_outargs
     return call_with_this
 
 ################################################################
 
 def hack(inst, mth, paramflags, interface, mthname):
     if paramflags is None:
-        return catch_errors(inst, mth, interface, mthname)
+        return catch_errors(inst, mth, paramflags, interface, mthname)
     code = mth.func_code
     if code.co_varnames[1:2] == ("this",):
-        return catch_errors(inst, mth, interface, mthname)
+        return catch_errors(inst, mth, paramflags, interface, mthname)
     dirflags = [f[0] for f in paramflags]
     # 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
             _error("Exception in %s.%s implementation:", interface.__name__, mthname, exc_info=True)
             return ReportException(E_FAIL, interface._iid_, clsid=clsid)
         return S_OK
-
+    if args_out:
+        call_without_this.has_outargs = True
     return call_without_this
 
 class _MethodFinder(object):
     _InterlockedIncrement.restype = c_long
     _InterlockedDecrement.restype = c_long
 
+class LocalServer(object):
+
+    _queue = None
+    def run(self, classobjects):
+        # Use windll instead of oledll so that we don't get an
+        # exception on a FAILED hresult:
+        result = windll.ole32.CoInitialize(None)
+        if RPC_E_CHANGED_MODE == result:
+            # we're running in MTA: no message pump needed
+            _debug("Server running in MTA")
+            self.run_mta()
+        else:
+            # we're running in STA: need a message pump
+            _debug("Server running in STA")
+            if result >= 0:
+                # we need a matching CoUninitialize() call for a successful CoInitialize().
+                windll.ole32.CoUninitialize()
+            self.run_sta()
+
+        for obj in classobjects:
+            obj._revoke_class()
+
+    def run_sta(self):
+        from comtypes import messageloop
+        messageloop.run()
+
+    def run_mta(self):
+        import Queue
+        self._queue = Queue.Queue()
+        self._queue.get()
+
+    def Lock(self):
+        oledll.ole32.CoAddRefServerProcess()
+
+    def Unlock(self):
+        rc = oledll.ole32.CoReleaseServerProcess()
+        if rc == 0:
+            if self._queue:
+                self._queue.put(42)
+            else:
+                windll.user32.PostQuitMessage(0)
+
+class InprocServer(object):
+
+    def __init__(self):
+        self.locks = c_long(0)
+
+    def Lock(self):
+        _InterlockedIncrement(self.locks)
+
+    def Unlock(self):
+        _InterlockedDecrement(self.locks)
+
+    def DllCanUnloadNow(self):
+        if self.locks.value:
+            return S_FALSE
+        if COMObject._instances_:
+            return S_FALSE
+        return S_OK
+
 class COMObject(object):
     _instances_ = {}
-    _factory = None
 
     def __new__(cls, *args, **kw):
         self = super(COMObject, cls).__new__(cls)
         # found.
         return _MethodFinder(self)
 
+    ################################################################
+    # LocalServer / InprocServer stuff
+    __server__ = None
+##2.3    @staticmethod
+    def __run_inprocserver__():
+        if COMObject.__server__ is None:
+            COMObject.__server__ = InprocServer()
+        elif isinstance(COMObject.__server__, InprocServer):
+            pass
+        else:
+            raise RuntimeError("Wrong server type")
+    __run_inprocserver__ = staticmethod(__run_inprocserver__)
+
+##2.3    @staticmethod
+    def __run_localserver__(classobjects):
+        assert COMObject.__server__ is None
+        # XXX Decide whether we are in STA or MTA
+        server = COMObject.__server__ = LocalServer()
+        server.run(classobjects)
+        COMObject.__server__ = None
+    __run_localserver__ = staticmethod(__run_localserver__)
+
+##2.3    @staticmethod
+    def __keep__(obj):
+        COMObject._instances_[obj] = None
+        _debug("%d active COM objects: Added   %r", len(COMObject._instances_), obj)
+        if COMObject.__server__:
+            COMObject.__server__.Lock()
+    __keep__ = staticmethod(__keep__)
+
+##2.3    @staticmethod
+    def __unkeep__(obj):
+        try:
+            del COMObject._instances_[obj]
+        except AttributeError:
+            _debug("? active COM objects: Removed %r", obj)
+        else:
+            _debug("%d active COM objects: Removed %r", len(COMObject._instances_), obj)
+        _debug("Remaining: %s", COMObject._instances_.keys())
+        if COMObject.__server__:
+            COMObject.__server__.Unlock()
+    __unkeep__ = staticmethod(__unkeep__)
+    #
+    ################################################################
+
     #########################################################
     # IUnknown methods implementations
     def IUnknown_AddRef(self, this,
                         _debug=_debug):
         result = __InterlockedIncrement(self._refcnt)
         if result == 1:
-            # keep reference to the object in a class variable.
-            COMObject._instances_[self] = None
-            _debug("%d active COM objects: Added   %r", len(COMObject._instances_), self)
+            self.__keep__(self)
         _debug("%r.AddRef() -> %s", self, result)
         return result
 
+    def _final_release_(self):
+        """This method may be overridden in subclasses
+        to free allocated resources or so."""
+        pass
+
     def IUnknown_Release(self, this,
                          __InterlockedDecrement=_InterlockedDecrement,
                         _debug=_debug):
         result = __InterlockedDecrement(self._refcnt)
         _debug("%r.Release() -> %s", self, result)
         if result == 0:
-            # For whatever reasons, at cleanup it may be that
-            # COMObject is already cleaned (set to None)
-            try:
-                del COMObject._instances_[self]
-            except AttributeError:
-                _debug("? active COM objects: Removed %r", self)
-            else:
-                _debug("%d active COM objects: Removed %r", len(COMObject._instances_), self)
-            if self._factory is not None:
-                self._factory.LockServer(None, 0)
+            self._final_release_()
+            self.__unkeep__(self)
+            # Hm, why isn't this cleaned up by the cycle gc?
+            self._com_pointers_ = {}
         return result
 
     def IUnknown_QueryInterface(self, this, riid, ppvObj,
 
     ################################################################
     # IDispatch methods
-    @property
+##2.3    @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)
+    __typeinfo = property(__typeinfo)
 
     def IDispatch_GetTypeInfoCount(self):
         try:
             indexes = named_indexes + unnamed_indexes
             args = [params.rgvarg[i].value for i in named_indexes + unnamed_indexes]
 
-            if pVarResult:
+            if pVarResult and getattr(mth, "has_outargs", False):
                 args.append(pVarResult)
             return mth(this, *args)
 

comtypes/_safearray.py

 SafeArrayCreateVectorEx.restype = POINTER(SAFEARRAY)
 SafeArrayCreateVectorEx.argtypes = [VARTYPE, LONG, DWORD, PVOID]
 
+SafeArrayCreateEx = _oleaut32.SafeArrayCreateEx
+SafeArrayCreateEx.restype = POINTER(SAFEARRAY)
+SafeArrayCreateEx.argtypes = [VARTYPE, c_uint, POINTER(SAFEARRAYBOUND), PVOID]
+
+SafeArrayCreate = _oleaut32.SafeArrayCreate
+SafeArrayCreate.restype = POINTER(SAFEARRAY)
+SafeArrayCreate.argtypes = [VARTYPE, c_uint, POINTER(SAFEARRAYBOUND)]
+
 SafeArrayUnaccessData = _oleaut32.SafeArrayUnaccessData
 SafeArrayUnaccessData.restype = HRESULT
 SafeArrayUnaccessData.argtypes = [POINTER(SAFEARRAY)]

comtypes/automation.py

 # comtypes.automation module
 from ctypes import *
+from ctypes import _Pointer
 from _ctypes import CopyComPointer
 from comtypes import IUnknown, GUID, IID, STDMETHOD, BSTR, COMMETHOD, COMError
 from comtypes.hresult import *
 # helpers
 IID_NULL = GUID()
 riid_null = byref(IID_NULL)
-_VariantClear = oledll.oleaut32.VariantClear
-_SysAllocStringLen = oledll.oleaut32.SysAllocStringLen
-_VariantCopyInd = oledll.oleaut32.VariantCopyInd
+_byref_type = type(byref(c_int()))
 
 # 30. December 1899, midnight.  For VT_DATE.
 _com_null_date = datetime.datetime(1899, 12, 30, 0, 0, 0)
         if self._b_needsfree_:
             # XXX This does not work.  _b_needsfree_ is never
             # set because the buffer is internal to the object.
-            _VariantClear(byref(self))
+            _VariantClear(self)
 
     def __repr__(self):
         if self.vt & VT_BYREF:
 
     # see also c:/sf/pywin32/com/win32com/src/oleargs.cpp 54
     def _set_value(self, value):
-        _VariantClear(byref(self))
+        _VariantClear(self)
         if value is None:
             self.vt = VT_NULL
         # since bool is a subclass of int, this check must come before
         elif isinstance(value, bool):
             self.vt = VT_BOOL
             self._.VT_BOOL = value
-        elif isinstance(value, int):
+        elif isinstance(value, (int, c_int)):
             self.vt = VT_I4
             self._.VT_I4 = value
         elif isinstance(value, long):
             # VT_R8 is last resort.
             self.vt = VT_R8
             u.VT_R8 = float(value)
-        elif isinstance(value, float):
+        elif isinstance(value, (float, c_double)):
             self.vt = VT_R8
             self._.VT_R8 = value
         elif isinstance(value, (str, unicode)):
             self.vt = VT_BSTR
             # do the c_wchar_p auto unicode conversion
-            self._.c_void_p = _SysAllocStringLen(c_wchar_p.from_param(value), len(value))
+            self._.c_void_p = _SysAllocStringLen(value, len(value))
         elif isinstance(value, datetime.datetime):
             delta = value - _com_null_date
             # a day has 24 * 60 * 60 = 86400 seconds
             CopyComPointer(value._comobj, byref(self._))
             self.vt = VT_DISPATCH
         elif isinstance(value, VARIANT):
-            windll.oleaut32.VariantCopy(byref(self), byref(value))
+            _VariantCopy(self, value)
+        elif isinstance(value, c_ubyte):
+            self._.VT_UI1 = value
+            self.vt = VT_UI1
+        elif isinstance(value, c_char):
+            self._.VT_UI1 = ord(value.value)
+            self.vt = VT_UI1
+        elif isinstance(value, c_byte):
+            self._.VT_I1 = value
+            self.vt = VT_I1
+        elif isinstance(value, c_ushort):
+            self._.VT_UI2 = value
+            self.vt = VT_UI2
+        elif isinstance(value, c_short):
+            self._.VT_I2 = value
+            self.vt = VT_I2
+        elif isinstance(value, c_uint):
+            self.vt = VT_UI4
+            self._.VT_UI4 = value
+        elif isinstance(value, c_float):
+            self.vt = VT_R4
+            self._.VT_R4 = value
+        elif isinstance(value, _byref_type):
+            ref = value._obj
+            self._.c_void_p = addressof(ref)
+            self.__keepref = value
+            self.vt = _ctype_to_vartype[type(ref)] | VT_BYREF
+        elif isinstance(value, _Pointer):
+            ref = value.contents
+            self._.c_void_p = addressof(ref)
+            self.__keepref = value
+            self.vt = _ctype_to_vartype[type(ref)] | VT_BYREF
         else:
             raise TypeError("Cannot put %r in VARIANT" % value)
         # buffer ->  SAFEARRAY of VT_UI1 ?
         if self.vt == VT_BYREF|VT_VARIANT:
             v = VARIANT()
             # apparently VariantCopyInd doesn't work always with
-            # VT_BREF|VT_VARIANT, so do it manually.
+            # VT_BYREF|VT_VARIANT, so do it manually.
             v = cast(self._.c_void_p, POINTER(VARIANT))[0]
             return v.value
         else:
             v = VARIANT()
-            _VariantCopyInd(byref(v), byref(self))
+            _VariantCopyInd(v, self)
             return v.value
 
 
         return result
 
     def ChangeType(self, typecode):
-        oledll.oleaut32.VariantChangeType(byref(self),
-                                          byref(self),
-                                          0,
-                                          typecode)
+        _VariantChangeType(self,
+                           self,
+                           0,
+                           typecode)
 
 VARIANT = tagVARIANT
 VARIANTARG = VARIANT
 
+_oleaut32 = OleDLL("oleaut32")
+
+_VariantChangeType = _oleaut32.VariantChangeType
+_VariantChangeType.argtypes = (POINTER(VARIANT), POINTER(VARIANT), c_ushort, VARTYPE)
+
+_VariantClear = _oleaut32.VariantClear
+_VariantClear.argtypes = (POINTER(VARIANT),)
+
+_SysAllocStringLen = windll.oleaut32.SysAllocStringLen
+_SysAllocStringLen.argtypes = c_wchar_p, c_uint
+_SysAllocStringLen.restype = c_void_p
+
+_VariantCopy = _oleaut32.VariantCopy
+_VariantCopy.argtypes = POINTER(VARIANT), POINTER(VARIANT)
+
+_VariantCopyInd = _oleaut32.VariantCopyInd
+_VariantCopyInd.argtypes = POINTER(VARIANT), POINTER(VARIANT)
+
 # some commonly used VARIANT instances
 VARIANT.null = VARIANT(None)
 VARIANT.empty = VARIANT()
     c_float: VT_R4,
     c_double: VT_R8,
 
+    c_longlong: VT_I8,
+    c_ulonglong: VT_UI8,
+
     VARIANT_BOOL: VT_BOOL,
 
     BSTR: VT_BSTR,
     VARIANT: VT_VARIANT,
 
+    # SAFEARRAY(VARIANT *)
+    #
+    # It is unlear to me if this is allowed or not.  Apparently there
+    # are typelibs that define such an argument type, but it may be
+    # that these are buggy.
+    #
+    # Point is that SafeArrayCreateEx(VT_VARIANT|VT_BYREF, ..) fails.
+    # The MSDN docs for SafeArrayCreate() have a notice that neither
+    # VT_ARRAY not VT_BYREF may be set, this notice is missing however
+    # for SafeArrayCreateEx().
+    #
+    # We have this code here to make sure that comtypes can import
+    # such a typelib, although calling ths method will fail because
+    # such an array cannot be created.
+    POINTER(VARIANT): VT_BYREF|VT_VARIANT,
+
     # These are not yet implemented:
 ##    POINTER(IUnknown): VT_UNKNOWN,
 ##    POINTER(IDispatch): VT_DISPATCH,
     _vartype_to_ctype[v] = c
 _vartype_to_ctype[VT_INT] = _vartype_to_ctype[VT_I4]
 _vartype_to_ctype[VT_UINT] = _vartype_to_ctype[VT_UI4]
+_ctype_to_vartype[c_char] = VT_UI1
 
 
 try:

comtypes/client/__init__.py

 logger = logging.getLogger(__name__)
 
 __all__ = ["CreateObject", "GetActiveObject", "CoGetObject",
-           "GetEvents", "ShowEvents", "PumpEvents", "GetModule"]
+           "GetEvents", "ShowEvents", "PumpEvents", "GetModule",
+           "GetClassObject"]
 
 from comtypes.client._code_cache import _find_gen_dir
 
     # find the typelib and the interface name
     logger.debug("GetBestInterface(%s)", punk)
     try:
-        pci = punk.QueryInterface(comtypes.typeinfo.IProvideClassInfo)
-        logger.debug("Does implement IProvideClassInfo")
+        try:
+            pci = punk.QueryInterface(comtypes.typeinfo.IProvideClassInfo)
+            logger.debug("Does implement IProvideClassInfo")
+        except comtypes.COMError:
+            # Some COM objects support IProvideClassInfo2, but not IProvideClassInfo.
+            # These objects are broken, but we support them anyway.
+            logger.debug("Does NOT implement IProvideClassInfo, trying IProvideClassInfo2")
+            pci = punk.QueryInterface(comtypes.typeinfo.IProvideClassInfo2)
+            logger.debug("Does implement IProvideClassInfo2")
         tinfo = pci.GetClassInfo() # TypeInfo for the CoClass
         # find the interface marked as default
         ta = tinfo.GetTypeAttr()
         href = tinfo.GetRefTypeOfImplType(index)
         tinfo = tinfo.GetRefTypeInfo(href)
     except comtypes.COMError:
-        logger.debug("Does NOT implement IProvideClassInfo")
+        logger.debug("Does NOT implement IProvideClassInfo/IProvideClassInfo2")
         try:
             pdisp = punk.QueryInterface(comtypes.automation.IDispatch)
         except comtypes.COMError:
 #
 # Object creation
 #
-def GetActiveObject(progid, interface=None):
+def GetActiveObject(progid, interface=None, dynamic=False):
+    """Return a pointer to a running COM object that has been
+    registered with COM.
+
+    'progid' may be a string like "Excel.Application",
+       a string specifying a clsid, a GUID instance, or an object with
+       a _clsid_ attribute which should be any of the above.
+    'interface' allows to force a certain interface.
+    'dynamic=True' will return a dynamic dispatch object.
+    """
     clsid = comtypes.GUID.from_progid(progid)
-    if interface is None:
+    if dynamic:
+        if interface is not None:
+            raise ValueError("interface and dynamic are mutually exclusive")
+        interface = comtypes.automation.IDispatch
+    elif interface is None:
         interface = getattr(progid, "_com_interfaces_", [None])[0]
     obj = comtypes.GetActiveObject(clsid, interface=interface)
+    if dynamic:
+        return comtypes.client.dynamic.Dispatch(obj)
     return _manage(obj, clsid, interface=interface)
 
 def _manage(obj, clsid, interface):
         obj = GetBestInterface(obj)
     return obj
 
+def GetClassObject(progid,
+                   clsctx=None,
+                   pServerInfo=None,
+                   interface=None):
+    """Create and return the class factory for a COM object.
+
+    'clsctx' specifies how to create the object, use the CLSCTX_... constants.
+    'pServerInfo', if used, must be an instance of comtypes.COSERVERINFO
+    'interface' may be used to request an interface other than IClassFactory
+    """
+    clsid = comtypes.GUID.from_progid(progid)
+    return comtypes.CoGetClassObject(clsid,
+                                     clsctx, pServerInfo, interface)
 
 def CreateObject(progid,                  # which object to create
                  clsctx=None,             # how to create the object
        a _clsid_ attribute which should be any of the above.
     'clsctx' specifies how to create the object, use the CLSCTX_... constants.
     'machine' allows to specify a remote machine to create the object on.
+    'interface' allows to force a certain interface
+    'dynamic=True' will return a dynamic dispatch object
 
     You can also later request to receive events with GetEvents().
     """
         return comtypes.client.dynamic.Dispatch(obj)
     return _manage(obj, clsid, interface=interface)
 
-def CoGetObject(displayname, interface=None):
+def CoGetObject(displayname, interface=None, dynamic=False):
     """Create an object by calling CoGetObject(displayname).
 
     Additional parameters have the same meaning as in CreateObject().
     """
+    if dynamic:
+        if interface is not None:
+            raise ValueError("interface and dynamic are mutually exclusive")
+        interface = comtypes.automation.IDispatch
     punk = comtypes.CoGetObject(displayname, interface)
+    if dynamic:
+        return comtypes.client.dynamic.Dispatch(punk)
     return _manage(punk,
                    clsid=None,
                    interface=interface)

comtypes/client/_code_cache.py

 comtypes.gen package and returns a directory where generated code can
 be written to.
 """
-import ctypes, logging, os, sys, tempfile
+import ctypes, logging, os, sys, tempfile, types
 logger = logging.getLogger(__name__)
 
 def _find_gen_dir():

comtypes/client/_events.py

 import ctypes
+import traceback
 import comtypes
-from comtypes.hresult import *
+import comtypes.hresult
 import comtypes.automation
 import comtypes.typeinfo
 import comtypes.connectionpoints
 def FindOutgoingInterface(source):
     """XXX Describe the strategy that is used..."""
     # If the COM object implements IProvideClassInfo2, it is easy to
-    # find the default autgoing interface.
+    # find the default outgoing interface.
     try:
         pci = source.QueryInterface(comtypes.typeinfo.IProvideClassInfo2)
         guid = pci.GetGUID(1)
 
     return None
 
+def report_errors(func):
+    # This decorator preserves parts of the decorated function
+    # signature, so that the comtypes special-casing for the 'this'
+    # parameter still works.
+    if func.func_code.co_varnames[:2] == ('self', 'this'):
+        def error_printer(self, this, *args, **kw):
+            try:
+                return func(self, this, *args, **kw)
+            except:
+                traceback.print_exc()
+                raise
+    else:
+        def error_printer(*args, **kw):
+            try:
+                return func(*args, **kw)
+            except:
+                traceback.print_exc()
+                raise
+    return error_printer
+
 from comtypes._comobject import _MethodFinder
 class _SinkMethodFinder(_MethodFinder):
+    """Special MethodFinder, for finding and decorating event handler
+    methods.  Looks for methods on two objects. Also decorates the
+    event handlers with 'report_errors' which will print exceptions in
+    event handlers.
+    """
     def __init__(self, inst, sink):
         super(_SinkMethodFinder, self).__init__(inst)
         self.sink = sink
 
     def find_method(self, fq_name, mthname):
+        impl = self._find_method(fq_name, mthname)
+        # Caller of this method catches AttributeError,
+        # so we need to be careful in the following code
+        # not to raise one...
+        try:
+            # impl is a bound method, dissect it...
+            im_self, im_func = impl.im_self, impl.im_func
+            # decorate it with an error printer...
+            method = report_errors(im_func)
+            # and make a new bound method from it again.
+            return comtypes.instancemethod(method,
+                                           im_self,
+                                           type(im_self))
+        except AttributeError, details:
+            raise RuntimeError(details)
+
+    def _find_method(self, fq_name, mthname):
         try:
             return super(_SinkMethodFinder, self).find_method(fq_name, mthname)
         except AttributeError:
             except AttributeError:
                 return getattr(self.sink, mthname)
 
-def CreateEventReceiver(interface, sink):
+def CreateEventReceiver(interface, handler):
 
     class Sink(comtypes.COMObject):
         _com_interfaces_ = [interface]
         def _get_method_finder_(self, itf):
             # Use a special MethodFinder that will first try 'self',
             # then the sink.
-            return _SinkMethodFinder(self, sink)
+            return _SinkMethodFinder(self, handler)
 
-    return Sink()
+    sink = Sink()
+
+    # Since our Sink object doesn't have typeinfo, it needs a
+    # _dispimpl_ dictionary to dispatch events received via Invoke.
+    if issubclass(interface, comtypes.automation.IDispatch) \
+           and not hasattr(sink, "_dispimpl_"):
+        finder = sink._get_method_finder_(interface)
+        dispimpl = sink._dispimpl_ = {}
+        for m in interface._methods_:
+            restype, mthname, argtypes, paramflags, idlflags, helptext = m
+            # Can dispid be at a different index? Should check code generator...
+            # ...but hand-written code should also work...
+            dispid = idlflags[0]
+            impl = finder.get_impl(interface, mthname, paramflags, idlflags)
+            # XXX Wouldn't work for 'propget', 'propput', 'propputref'
+            # methods - are they allowed on event interfaces?
+            dispimpl[(dispid, comtypes.automation.DISPATCH_METHOD)] = impl
+
+    return sink
 
 def GetEvents(source, sink, interface=None):
     """Receive COM events from 'source'.  Events will call methods on
             # 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]))
-        return comtypes.instancemethod(handler, EventDumper, self)
+        return comtypes.instancemethod(handler, self, EventDumper)
 
 def ShowEvents(source, interface=None):
     """Receive COM events from 'source'.  A special event sink will be
     handles = (ctypes.c_void_p * 1)(hevt)
     RPC_S_CALLPENDING = -2147417835
 
-    @ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_uint)
+##    @ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_uint)
     def HandlerRoutine(dwCtrlType):
         if dwCtrlType == 0: # CTRL+C
             ctypes.windll.kernel32.SetEvent(hevt)
             return 1
         return 0
+    HandlerRoutine = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_uint)(HandlerRoutine)
 
     ctypes.windll.kernel32.SetConsoleCtrlHandler(HandlerRoutine, 1)
 

comtypes/client/dynamic.py

     def __init__(self, comobj):
         self.__dict__["_comobj"] = comobj
         self.__dict__["_ids"] = {} # Tiny optimization: trying not to use GetIDsOfNames more than once
+        self.__dict__["_methods"] = set()
 
     def __enum(self):
         e = self._comobj.Invoke(-4) # DISPID_NEWENUM
             return 1 	 
         return cmp(self._comobj, other._comobj)
 
+    def __hash__(self):
+        return hash(self._comobj)
+
     def __getitem__(self, index):
         enum = self.__enum()
         if index > 0:
         "QueryInterface is forwarded to the real com object."
         return self._comobj.QueryInterface(*args)
 
+    def _FlagAsMethod(self, *names):
+        """Flag these attribute names as being methods.
+        Some objects do not correctly differentiate methods and
+        properties, leading to problems when calling these methods.
+
+        Specifically, trying to say: ob.SomeFunc()
+        may yield an exception "None object is not callable"
+        In this case, an attempt to fetch the *property*has worked
+        and returned None, rather than indicating it is really a method.
+        Calling: ob._FlagAsMethod("SomeFunc")
+        should then allow this to work.
+        """
+        self._methods.update(names)
+
     def __getattr__(self, name):
+        if name.startswith("__") and name.endswith("__"):
+            raise AttributeError(name)
 ##        tc = self._comobj.GetTypeInfo(0).QueryInterface(comtypes.typeinfo.ITypeComp)
 ##        dispid = tc.Bind(name)[1].memid
         dispid = self._ids.get(name)
             dispid = self._comobj.GetIDsOfNames(name)[0]
             self._ids[name] = dispid
 
+        if name in self._methods:
+            result = MethodCaller(dispid, self)
+            self.__dict__[name] = result
+            return result
+
         flags = comtypes.automation.DISPATCH_PROPERTYGET
         try:
             result = self._comobj.Invoke(dispid, _invkind=flags)

comtypes/client/lazybind.py

         return isinstance(other, Dispatch) and \
                self._comobj == other._comobj
 
+    def __hash__(self):
+        return hash(self._comobj)
+
     def __getattr__(self, name):
         """Get a COM attribute."""
         if name.startswith("__") and name.endswith("__"):
             # DISPATCH_METHOD
             def caller(*args):
                 return self._comobj._invoke(descr.memid, descr.invkind, 0, *args)
-            caller.__name__ = name
+            try:
+                caller.__name__ = name
+            except TypeError:
+                # In Python 2.3, __name__ is readonly
+                pass
             return caller
 
     def __setattr__(self, name, value):

comtypes/errorinfo.py

     _oleaut32.SetErrorInfo(0, ei)
     return hresult
 
-def ReportException(hresult, iid, clsid=None, helpfile=None, helpcontext=None):
+def ReportException(hresult, iid, clsid=None, helpfile=None, helpcontext=None,
+                    stacklevel=None):
     """Report a COM exception.  Returns the passed in hresult value."""
-    text = "%s: %s" % sys.exc_info()[:2]
+    typ, value, tb = sys.exc_info()
+    if stacklevel is not None:
+        for _ in range(stacklevel):
+            tb = tb.tb_next
+        line = tb.tb_frame.f_lineno
+        name = tb.tb_frame.f_globals["__name__"]
+        text = "%s: %s (%s, line %d)" % (typ, value, name, line)
+    else:
+        text = "%s: %s" % (typ, value)
     return ReportError(text, iid,
                        clsid=clsid, helpfile=helpfile, helpcontext=helpcontext,
                        hresult=hresult)

comtypes/hresult.py

 E_POINTER = -2147467261 #0x80004003L
 E_FAIL = -2147467259 #0x80004005L
 E_INVALIDARG = -2147024809 #0x80070057L
+E_OUTOFMEMORY = -2147024882 # 0x8007000EL
 
 CLASS_E_NOAGGREGATION = -2147221232 #0x80040110L
 CLASS_E_CLASSNOTAVAILABLE = -2147221231 #0x80040111L

comtypes/safearray.py

+import array, sys
 from ctypes import *
 from comtypes import _safearray, GUID, IUnknown, com_interface_registry
 from comtypes.partial import partial
-
 _safearray_type_cache = {}
 
 ################################################################
         _vartype_ = vartype # a VARTYPE value: VT_...
         _needsfree = False
 
-        @classmethod
+##        @classmethod
         def create(cls, value, extra=None):
-            """Create a one-dimensional POINTER(SAFEARRAY_...)
-            instance of the correct type; value is a sequence
-            containing the items to store."""
+            """Create a POINTER(SAFEARRAY_...) instance of the correct
+            type; value is an object containing the items to store.
+
+            Python lists, tuples, and array.array instances containing
+            compatible item types can be passed to create
+            one-dimensional arrays.  To create multidimensional arrys,
+            numpy arrays must be passed.
+            """
+
+            if "numpy" in sys.modules:
+                numpy = sys.modules["numpy"]
+                if isinstance(value, numpy.ndarray):
+                    return cls.create_from_ndarray(value, extra)
 
             # For VT_UNKNOWN or VT_DISPATCH, extra must be a pointer to
             # the GUID of the interface.
             ptr = POINTER(cls._itemtype_)() # container for the values
             _safearray.SafeArrayAccessData(pa, byref(ptr))
             try:
-                for index, item in enumerate(value):
-                    ptr[index] = item
+                if isinstance(value, array.array):
+                    addr, n = value.buffer_info()
+                    nbytes = len(value) * sizeof(cls._itemtype_)
+                    memmove(ptr, addr, nbytes)
+                else:
+                    for index, item in enumerate(value):
+                        ptr[index] = item
             finally:
                 _safearray.SafeArrayUnaccessData(pa)
             return pa
+        create = classmethod(create)
 
-        @classmethod
+##        @classmethod
+        def create_from_ndarray(cls, value, extra, lBound=0):
+            #c:/python25/lib/site-packages/numpy/ctypeslib.py
+            numpy = __import__("numpy.ctypeslib")
+
+            # SAFEARRAYs have Fortran order; convert the numpy array if needed
+            if not value.flags.f_contiguous:
+                value = numpy.array(value, order="F")
+
+            ai = value.__array_interface__
+            if ai["version"] != 3:
+                raise TypeError("only __array_interface__ version 3 supported")
+            if cls._itemtype_ != numpy.ctypeslib._typecodes[ai["typestr"]]:
+                raise TypeError("Wrong array item type")
+
+            # For VT_UNKNOWN or VT_DISPATCH, extra must be a pointer to
+            # the GUID of the interface.
+            #
+            # For VT_RECORD, extra must be a pointer to an IRecordInfo
+            # describing the record.
+            rgsa = (_safearray.SAFEARRAYBOUND * value.ndim)()
+            nitems = 1
+            for i, d in enumerate(value.shape):
+                nitems *= d
+                rgsa[i].cElements = d
+                rgsa[i].lBound = lBound
+            pa = _safearray.SafeArrayCreateEx(cls._vartype_,
+                                              value.ndim, # cDims
+                                              rgsa, # rgsaBound
+                                              extra) # pvExtra
+            if not pa:
+                if cls._vartype_ == VT_RECORD and extra is None:
+                    raise TypeError("Cannot create SAFEARRAY type VT_RECORD without IRecordInfo.")
+                # Hm, there may be other reasons why the creation fails...
+                raise MemoryError()
+            # We now have a POINTER(tagSAFEARRAY) instance which we must cast
+            # to the correct type:
+            pa = cast(pa, cls)
+            # Now, fill the data in:
+            ptr = POINTER(cls._itemtype_)() # pointer to the item values
+            _safearray.SafeArrayAccessData(pa, byref(ptr))
+            try:
+                nbytes = nitems * sizeof(cls._itemtype_)
+                memmove(ptr, value.ctypes.data, nbytes)
+            finally:
+                _safearray.SafeArrayUnaccessData(pa)
+            return pa
+        create_from_ndarray = classmethod(create_from_ndarray)
+
+##        @classmethod
         def from_param(cls, value):
             if not isinstance(value, cls):
                 value = cls.create(value, extra)
                 value._needsfree = True
             return value
+        from_param = classmethod(from_param)
 
         def __getitem__(self, index):
             # pparray[0] returns the whole array contents.
                     # objects, the containing safearray must be kept
                     # alive until all the elements are destroyed.
                     if not issubclass(self._itemtype_, Structure):
+                        # Creating and returning numpy arrays instead
+                        # of Python tuple from a safearray is a lot faster,
+                        # but only for large arrays because of a certain overhead.
+                        # Also, for backwards compatibility, some clients expect
+                        # a Python tuple - so there should be a way to select
+                        # what should be returned.  How could that work?
+##                        # A hack which would return numpy arrays
+##                        # instead of Python lists.  To be effective,
+##                        # the result must not converted into a tuple
+##                        # in the caller so there must be changes as
+##                        # well!
+##
+##                        # Crude hack to create and attach an
+##                        # __array_interface__ property to the
+##                        # pointer instance
+##                        array_type = ptr._type_ * num_elements
+##                        if not hasattr(array_type, "__array_interface__"):
+##                            import numpy.ctypeslib
+##                            numpy.ctypeslib.prep_array(array_type)
+##                        # use the array_type's __array_interface__, ...
+##                        aif = array_type.__array_interface__.__get__(ptr)
+##                        # overwrite the 'data' member so that it points to the
+##                        # address we want to use
+##                        aif["data"] = (cast(ptr, c_void_p).value, False)
+##                        ptr.__array_interface__ = aif
+##                        return numpy.array(ptr, copy=True)
                         return ptr[:num_elements]
                     def keep_safearray(v):
                         v.__keepref = self
 
     class _(partial, POINTER(POINTER(sa_type))):
 
-        @classmethod
+##        @classmethod
         def from_param(cls, value):
             if isinstance(value, cls._type_):
                 return byref(value)
-            return byref(cls._type_.create(value))
+            return byref(cls._type_.create(value, extra))
+        from_param = classmethod(from_param)
 
         def __setitem__(self, index, value):
             # create an LP_SAFEARRAY_... instance
-            pa = self._type_.create(value)
+            pa = self._type_.create(value, extra)
             # XXX Must we destroy the currently contained data?
             # fill it into self
             super(POINTER(POINTER(sa_type)), self).__setitem__(index, pa)

comtypes/server/__init__.py

-import comtypes, ctypes
+import comtypes.client, ctypes
 
 ################################################################
 # Interfaces
     _iid_ = comtypes.GUID("{00000001-0000-0000-C000-000000000046}")
     _methods_ = [
         comtypes.STDMETHOD(comtypes.HRESULT, "CreateInstance",
-                           [ctypes.c_int, ctypes.POINTER(comtypes.GUID), ctypes.POINTER(ctypes.c_ulong)]),
+                           [ctypes.POINTER(comtypes.IUnknown),
+                            ctypes.POINTER(comtypes.GUID),
+                            ctypes.POINTER(ctypes.c_void_p)]),
         comtypes.STDMETHOD(comtypes.HRESULT, "LockServer",
                            [ctypes.c_int])]
 
+    def CreateInstance(self, punkouter=None, interface=None, dynamic=False):
+        if dynamic:
+            if interface is not None:
+                raise ValueError("interface and dynamic are mutually exclusive")
+            realInterface = comtypes.automation.IDispatch
+        elif interface is None:
+            realInterface = comtypes.IUnknown
+        else:
+            realInterface = interface
+        obj = ctypes.POINTER(realInterface)()
+        self.__com_CreateInstance(punkouter, realInterface._iid_, ctypes.byref(obj))
+        if dynamic:
+            return comtypes.client.dynamic.Dispatch(obj)
+        elif interface is None:
+            # An interface was not specified, so return the best.
+            return comtypes.client.GetBestInterface(obj)
+        # An interface was specified and obj is already that interface.
+        return obj
+
 ##class IExternalConnection(IUnknown):
 ##    _iid_ = GUID("{00000019-0000-0000-C000-000000000046}")
 ##    _methods_ = [

comtypes/server/automation.py

 from comtypes.hresult import *
 
 from comtypes import COMObject, IUnknown
-from comtypes.automation import IEnumVARIANT
+from comtypes.automation import IDispatch, IEnumVARIANT
 
 logger = logging.getLogger(__name__)
 
+# XXX When the COMCollection class is ready, insert it into __all__
 __all__ = ["VARIANTEnumerator"]
 
+
 class VARIANTEnumerator(COMObject):
+    """A universal VARIANTEnumerator class.  Instantiate it with a
+    collection of items that support the IDispatch interface."""
     _com_interfaces_ = [IEnumVARIANT]
 
-    def __init__(self, itemtype, jobs):
-        self.jobs = jobs # keep, so that we can restore our iterator (in Reset, and Clone).
-        self.itemtype = itemtype
-        self.item_interface = itemtype._com_interfaces_[0]
-        self.seq = iter(self.jobs)
+    def __init__(self, items):
+        self.items = items # keep, so that we can restore our iterator (in Reset, and Clone).
+        self.seq = iter(self.items)
         super(VARIANTEnumerator, self).__init__()
 
     def Next(self, this, celt, rgVar, pCeltFetched):
         pCeltFetched[0] = 0
         try:
             for index in range(celt):
-                job = self.itemtype(self.seq.next())
-                p = POINTER(self.item_interface)()
-                job.IUnknown_QueryInterface(None,
-                                            pointer(p._iid_),
-                                            byref(p))
+                item = self.seq.next()
+                p = item.QueryInterface(IDispatch)
                 rgVar[index].value = p
                 pCeltFetched[0] += 1
         except StopIteration:
             pass
+##        except:
+##            # ReportException? return E_FAIL?
+##            import traceback
+##            traceback.print_exc()
+
         if pCeltFetched[0] == celt:
             return S_OK
         return S_FALSE
         return S_OK
 
     def Reset(self, this):
-        self.seq = iter(self.jobs)
+        self.seq = iter(self.items)
         return S_OK
 
-    # Clone
+    # Clone not implemented
 
 ################################################################
 
+# XXX Shouldn't this be a mixin class?
+# And isn't this class borked anyway?
+
 class COMCollection(COMObject):
     """Abstract base class which implements Count, Item, and _NewEnum."""
     def __init__(self, itemtype, collection):

comtypes/server/inprocserver.py

 
 ################################################################
 
-g_cLocks = 0
-
 class ClassFactory(COMObject):
     _com_interfaces_ = [IClassFactory]
 
 
     def IClassFactory_CreateInstance(self, this, punkOuter, riid, ppv):
         _debug("ClassFactory.CreateInstance(%s)", riid[0])
-
         result = self._cls().IUnknown_QueryInterface(None, riid, ppv)
         _debug("CreateInstance() -> %s", result)
         return result
 
     def IClassFactory_LockServer(self, this, fLock):
-        global g_cLocks
         if fLock:
-            g_cLocks += 1
+            COMObject.__server__.Lock()
         else:
-            g_cLocks -= 1
+            COMObject.__server__.Unlock()
         return S_OK
 
 # will be set by py2exe boot script 'from outside'
         logging.getLogger(name).setLevel(level)
 
 def DllGetClassObject(rclsid, riid, ppv):
+    COMObject.__run_inprocserver__()
+
     iid = GUID.from_address(riid)
     clsid = GUID.from_address(rclsid)
 
         _setup_logging(clsid)
 
     # This function is directly called by C code, and receives C
-    # integers as parameters. rcslid is a pointer to the CLSID for the
+    # integers as parameters. rclsid is a pointer to the CLSID for the
     # coclass we want to be created, riid is a pointer to the
     # requested interface.
     try:
         return E_FAIL
 
 def DllCanUnloadNow():
-    from comtypes._comobject import COMObject
-    result = S_OK
-    if g_cLocks:
-        result = S_FALSE
-    elif COMObject._instances_:
-        result = S_FALSE
-    _debug("DllCanUnloadNow %d locks, %d instances -> result %s",
-           g_cLocks, len(COMObject._instances_), result)
-    return result
+    COMObject.__run_inprocserver__()
+    result = COMObject.__server__.DllCanUnloadNow()
+    # To avoid a memory leak when PyInitialize()/PyUninitialize() are
+    # called several times, we refuse to unload the dll.
+    return S_FALSE

comtypes/server/localserver.py

 
 logger = logging.getLogger(__name__)
 _debug = logger.debug
-_critical = logger.critical
 
 REGCLS_SINGLEUSE = 0       # class object only generates one instance
 REGCLS_MULTIPLEUSE = 1     # same class object genereates multiple inst.
 REGCLS_SURROGATE      = 8  # must be used when a surrogate process
 
 def run(classes):
-    assert len(classes) == 1
-    cls = classes[0]
-    factory = ClassFactory(cls)
-    factory._run()
-
-g_cLocks = 0 # XXX We should use Interlocked access
+    classobjects = [ClassFactory(cls) for cls in classes]
+    comtypes.COMObject.__run_localserver__(classobjects)
 
 class ClassFactory(comtypes.COMObject):
     _com_interfaces_ = [IClassFactory]
 
     def __init__(self, cls, *args, **kw):
         super(ClassFactory, self).__init__()
-        cls._factory = self
         self._cls = cls
         self._register_class()
         self._args = args
         self._kw = kw
 
+    def IUnknown_AddRef(self, this):
+        return 2
+
+    def IUnknown_Release(self, this):
+        return 1
+
     def _register_class(self):
         regcls = getattr(self._cls, "_regcls_", self.regcls)
         cookie = c_ulong()
         ptr = self._com_pointers_[comtypes.IUnknown._iid_]
+        clsctx = self._cls._reg_clsctx_
+        clsctx &= ~comtypes.CLSCTX_INPROC # reset the inproc flags
         oledll.ole32.CoRegisterClassObject(byref(comtypes.GUID(self._cls._reg_clsid_)),
                                            ptr,
-                                           self._cls._reg_clsctx_,
+                                           clsctx,
                                            regcls,
                                            byref(cookie))
         self.cookie = cookie
 
     def _revoke_class(self):
-        oledll.ole32.CoRevokeClassObject(self,cookie)
+        oledll.ole32.CoRevokeClassObject(self.cookie)
 
     def CreateInstance(self, this, punkOuter, riid, ppv):
         _debug("ClassFactory.CreateInstance(%s)", riid[0])
-        self.LockServer(None, True)
         obj = self._cls(*self._args, **self._kw)
         result = obj.IUnknown_QueryInterface(None, riid, ppv)
         _debug("CreateInstance() -> %s", result)
         return result
 
     def LockServer(self, this, fLock):
-        global g_cLocks
         if fLock:
-            g_cLocks += 1
+            comtypes.COMObject.__server__.Lock()
         else:
-            g_cLocks -= 1
-        _debug("LockServer -> %d", g_cLocks)
-        if g_cLocks == 0:
-            if self._queue is not None:
-                self._queue.put(42)
-            else:
-                windll.user32.PostQuitMessage(0)
+            comtypes.COMObject.__server__.Unlock()
         return S_OK
-
-    def _run(self):
-        result = windll.ole32.CoInitialize(None)
-        if RPC_E_CHANGED_MODE == result:
-            # we're running in MTA: no message pump needed
-            _debug("Server running in MTA")
-            self.run_mta()
-        else:
-            # we're running in STA: need a message pump
-            _debug("Server running in STA")
-            if result >= 0:
-                # we need a matching CoUninitialize() call for a successful CoInitialize().
-                windll.ole32.CoUninitialize()
-            self.run_sta()
-
-    def run_sta(self):
-        "Can be overridden in subclasses, to install a custom message pump."
-        from comtypes import messageloop
-        messageloop.run()
-
-    def run_mta(self):
-        "Can be overridden in subclasses."
-        self._queue = Queue.Queue()
-        self._queue.get()