diff --git a/BUILDING.md b/BUILDING.md
index a28759e1c7cc1e..84cd38a336b481 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -111,6 +111,9 @@ Prerequisites:
* [Visual Studio 2015 Update 3](https://www.visualstudio.com/), all editions
including the Community edition (remember to select
"Common Tools for Visual C++ 2015" feature during installation).
+ * [Visual Studio 2017 RC](https://www.visualstudio.com/vs/visual-studio-2017-rc/),
+ all editions. Required are "VC++ 2017 v141 toolset" and one of the
+ "Windows 10 SDKS" components.
* Basic Unix tools required for some tests,
[Git for Windows](http://git-scm.com/download/win) includes Git Bash
and tools which can be included in the global `PATH`.
diff --git a/tools/comtypes/LICENSE.txt b/tools/comtypes/LICENSE.txt
new file mode 100644
index 00000000000000..51bfc346ac073a
--- /dev/null
+++ b/tools/comtypes/LICENSE.txt
@@ -0,0 +1,24 @@
+This software is OSI Certified Open Source Software.
+OSI Certified is a certification mark of the Open Source Initiative.
+
+Copyright (c) 2006-2013, Thomas Heller.
+Copyright (c) 2014, Comtypes Developers.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/tools/comtypes/MANIFEST.in b/tools/comtypes/MANIFEST.in
new file mode 100644
index 00000000000000..ab30e9aceec166
--- /dev/null
+++ b/tools/comtypes/MANIFEST.in
@@ -0,0 +1 @@
+include *.txt
diff --git a/tools/comtypes/README b/tools/comtypes/README
new file mode 100644
index 00000000000000..5dad588b6381ae
--- /dev/null
+++ b/tools/comtypes/README
@@ -0,0 +1,31 @@
+comtypes
+========
+
+**comtypes** is a lightweight Python COM package, based on the ctypes_
+FFI library, in less than 10000 lines of code (not counting the
+tests).
+
+**comtypes** allows to define, call, and implement custom and
+dispatch-based COM interfaces in pure Python. It works on Windows,
+64-bit Windows, and Windows CE.
+
+Documentation:
+
+ https://pythonhosted.org/comtypes
+
+ Contribute using the `source repository and issue tracker
+ `_ on GitHub.
+
+Mailing list:
+
+ http://gmane.org/info.php?group=gmane.comp.python.comtypes.user
+
+ https://lists.sourceforge.net/lists/listinfo/comtypes-users/
+
+Download:
+
+ Releases can be downloaded in the PyPI page:
+
+ https://pypi.python.org/pypi/comtypes
+
+.. _ctypes: http://docs.python.org/lib/module-ctypes.html
diff --git a/tools/comtypes/comtypes/GUID.py b/tools/comtypes/comtypes/GUID.py
new file mode 100644
index 00000000000000..198526aa5c14e2
--- /dev/null
+++ b/tools/comtypes/comtypes/GUID.py
@@ -0,0 +1,101 @@
+from ctypes import *
+import sys
+
+if sys.version_info >= (2, 6):
+ def binary(obj):
+ return bytes(obj)
+else:
+ def binary(obj):
+ return buffer(obj)
+
+BYTE = c_byte
+WORD = c_ushort
+DWORD = c_ulong
+
+_ole32 = oledll.ole32
+
+_StringFromCLSID = _ole32.StringFromCLSID
+_CoTaskMemFree = windll.ole32.CoTaskMemFree
+_ProgIDFromCLSID = _ole32.ProgIDFromCLSID
+_CLSIDFromString = _ole32.CLSIDFromString
+_CLSIDFromProgID = _ole32.CLSIDFromProgID
+_CoCreateGuid = _ole32.CoCreateGuid
+
+# Note: Comparing GUID instances by comparing their buffers
+# is slightly faster than using ole32.IsEqualGUID.
+
+class GUID(Structure):
+ _fields_ = [("Data1", DWORD),
+ ("Data2", WORD),
+ ("Data3", WORD),
+ ("Data4", BYTE * 8)]
+
+ def __init__(self, name=None):
+ if name is not None:
+ _CLSIDFromString(unicode(name), byref(self))
+
+ def __repr__(self):
+ return u'GUID("%s")' % unicode(self)
+
+ def __unicode__(self):
+ p = c_wchar_p()
+ _StringFromCLSID(byref(self), byref(p))
+ result = p.value
+ _CoTaskMemFree(p)
+ return result
+ __str__ = __unicode__
+
+ def __cmp__(self, other):
+ if isinstance(other, GUID):
+ return cmp(binary(self), binary(other))
+ return -1
+
+ def __nonzero__(self):
+ return self != GUID_null
+
+ def __eq__(self, other):
+ return isinstance(other, GUID) and \
+ binary(self) == binary(other)
+
+ def __hash__(self):
+ # We make GUID instances hashable, although they are mutable.
+ return hash(binary(self))
+
+ def copy(self):
+ return GUID(unicode(self))
+
+ def from_progid(cls, progid):
+ """Get guid from progid, ...
+ """
+ if hasattr(progid, "_reg_clsid_"):
+ progid = progid._reg_clsid_
+ if isinstance(progid, cls):
+ return progid
+ elif isinstance(progid, basestring):
+ if progid.startswith("{"):
+ return cls(progid)
+ inst = cls()
+ _CLSIDFromProgID(unicode(progid), byref(inst))
+ return inst
+ else:
+ raise TypeError("Cannot construct guid from %r" % progid)
+ from_progid = classmethod(from_progid)
+
+ def as_progid(self):
+ "Convert a GUID into a progid"
+ progid = c_wchar_p()
+ _ProgIDFromCLSID(byref(self), byref(progid))
+ result = progid.value
+ _CoTaskMemFree(progid)
+ return result
+
+ def create_new(cls):
+ "Create a brand new guid"
+ guid = cls()
+ _CoCreateGuid(byref(guid))
+ return guid
+ create_new = classmethod(create_new)
+
+GUID_null = GUID()
+
+__all__ = ["GUID"]
diff --git a/tools/comtypes/comtypes/__init__.py b/tools/comtypes/comtypes/__init__.py
new file mode 100644
index 00000000000000..774c8b647d917d
--- /dev/null
+++ b/tools/comtypes/comtypes/__init__.py
@@ -0,0 +1,1390 @@
+import types
+import sys
+import os
+
+# comtypes version numbers follow semver (http://semver.org/) and PEP 440
+__version__ = "1.1.3"
+
+import logging
+class NullHandler(logging.Handler):
+ """A Handler that does nothing."""
+ def emit(self, record):
+ pass
+
+logger = logging.getLogger(__name__)
+
+# Add a NULL handler to the comtypes logger. This prevents getting a
+# message like this:
+# No handlers could be found for logger "comtypes"
+# when logging is not configured and logger.error() is called.
+logger.addHandler(NullHandler())
+
+from ctypes import *
+from _ctypes import COMError
+from comtypes import patcher
+
+def _check_version(actual):
+ from comtypes.tools.codegenerator import version as required
+ if actual != required:
+ raise ImportError("Wrong version")
+ if not hasattr(sys, "frozen"):
+ g = sys._getframe(1).f_globals
+ mod_path = g.get("__file__")
+ tlb_path = g.get("typelib_path")
+ try:
+ mod_mtime = os.stat(mod_path).st_mtime
+ tlib_mtime = os.stat(tlb_path).st_mtime
+ except (OSError, TypeError):
+ return
+ if mod_mtime < tlib_mtime:
+ raise ImportError("Typelib newer than module")
+
+try:
+ COMError()
+except TypeError:
+ pass
+else:
+ # Python 2.5 and 2.5.1 have a bug in the COMError implementation:
+ # The type has no __init__ method, and no hresult, text, and
+ # details instance vars. Work around this bug by monkeypatching
+ # COMError.
+ def monkeypatch_COMError():
+ def __init__(self, hresult, text, details):
+ self.hresult = hresult
+ self.text = text
+ self.details = details
+ super(COMError, self).__init__(hresult, text, details)
+ COMError.__init__ = __init__
+ monkeypatch_COMError()
+ del monkeypatch_COMError
+
+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)
+
+ Return a hresult code from a COM method implementation
+ without logging an error.
+ """
+
+##class IDLWarning(UserWarning):
+## "Warn about questionable type information"
+
+from comtypes.GUID import GUID
+_GUID = GUID
+IID = GUID
+DWORD = c_ulong
+
+wireHWND = c_ulong
+
+################################################################
+# About COM apartments:
+# http://blogs.msdn.com/larryosterman/archive/2004/04/28/122240.aspx
+################################################################
+
+################################################################
+# constants for object creation
+CLSCTX_INPROC_SERVER = 1
+CLSCTX_INPROC_HANDLER = 2
+CLSCTX_LOCAL_SERVER = 4
+
+CLSCTX_INPROC = 3
+CLSCTX_SERVER = 5
+CLSCTX_ALL = 7
+
+CLSCTX_INPROC_SERVER16 = 8
+CLSCTX_REMOTE_SERVER = 16
+CLSCTX_INPROC_HANDLER16 = 32
+CLSCTX_RESERVED1 = 64
+CLSCTX_RESERVED2 = 128
+CLSCTX_RESERVED3 = 256
+CLSCTX_RESERVED4 = 512
+CLSCTX_NO_CODE_DOWNLOAD = 1024
+CLSCTX_RESERVED5 = 2048
+CLSCTX_NO_CUSTOM_MARSHAL = 4096
+CLSCTX_ENABLE_CODE_DOWNLOAD = 8192
+CLSCTX_NO_FAILURE_LOG = 16384
+CLSCTX_DISABLE_AAA = 32768
+CLSCTX_ENABLE_AAA = 65536
+CLSCTX_FROM_DEFAULT_CONTEXT = 131072
+
+tagCLSCTX = c_int # enum
+CLSCTX = tagCLSCTX
+
+# Constants for security setups
+SEC_WINNT_AUTH_IDENTITY_UNICODE = 0x2
+RPC_C_AUTHN_WINNT = 10
+RPC_C_AUTHZ_NONE = 0
+RPC_C_AUTHN_LEVEL_CONNECT = 2
+RPC_C_IMP_LEVEL_IMPERSONATE = 3
+EOAC_NONE = 0
+
+
+
+################################################################
+# 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
+COINIT_DISABLE_OLE1DDE = 0x4
+COINIT_SPEED_OVER_MEMORY = 0x8
+
+def CoInitialize():
+ return CoInitializeEx(COINIT_APARTMENTTHREADED)
+
+def CoInitializeEx(flags=None):
+ if flags is None:
+ if os.name == "ce":
+ flags = getattr(sys, "coinit_flags", COINIT_MULTITHREADED)
+ else:
+ flags = getattr(sys, "coinit_flags", COINIT_APARTMENTTHREADED)
+ logger.debug("CoInitializeEx(None, %s)", flags)
+ _ole32.CoInitializeEx(None, flags)
+
+# COM is initialized automatically for the thread that imports this
+# module for the first time. sys.coinit_flags is passed as parameter
+# to CoInitializeEx, if defined, otherwise COINIT_APARTMENTTHREADED
+# (COINIT_MULTITHREADED on Windows CE) is used.
+#
+# A shutdown function is registered with atexit, so that
+# CoUninitialize is called when Python is shut down.
+CoInitializeEx()
+
+# We need to have CoUninitialize for multithreaded model where we have
+# to initialize and uninitialize COM for every new thread (except main)
+# in which we are using COM
+def CoUninitialize():
+ logger.debug("CoUninitialize()")
+ _ole32_nohresult.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.
+ _exc_clear()
+ # Sometimes, CoUnititialize, running at Python shutdown,
+ # raises an exception. We suppress this when __debug__ is
+ # False.
+ _debug("Calling CoUnititialize()")
+ if __debug__:
+ func()
+ else:
+ try: func()
+ except WindowsError: pass
+ # Set the flag which means that calling obj.Release() is no longer
+ # needed.
+ if _cominterface_meta is not None:
+ _cominterface_meta._com_shutting_down = True
+ _debug("CoUnititialize() done.")
+
+import atexit
+atexit.register(shutdown)
+del shutdown
+
+################################################################
+# global registries.
+
+# allows to find interface classes by guid strings (iid)
+com_interface_registry = {}
+
+# allows to find coclasses by guid strings (clsid)
+com_coclass_registry = {}
+
+def _is_object(obj):
+ """This function determines if the argument is a COM object. It
+ is used in several places to determine whether propputref or
+ propput setters have to be used."""
+ from comtypes.automation import VARIANT
+ # A COM pointer is an 'Object'
+ if isinstance(obj, POINTER(IUnknown)):
+ return True
+ # A COM pointer in a VARIANT is an 'Object', too
+ elif isinstance(obj, VARIANT) and isinstance(obj.value, POINTER(IUnknown)):
+ return True
+ # It may be a dynamic dispatch object.
+ return hasattr(obj, "_comobj")
+
+################################################################
+# The metaclasses...
+
+class _cominterface_meta(type):
+ """Metaclass for COM interfaces. Automatically creates high level
+ methods from COMMETHOD lists.
+ """
+
+ # This flag is set to True by the atexit handler which calls
+ # CoUnititialize.
+ _com_shutting_down = False
+
+ # Creates also a POINTER type for the newly created class.
+ def __new__(self, name, bases, namespace):
+ methods = namespace.pop("_methods_", None)
+ dispmethods = namespace.pop("_disp_methods_", None)
+ cls = type.__new__(self, name, bases, namespace)
+
+ if methods is not None:
+ cls._methods_ = methods
+ if dispmethods is not None:
+ cls._disp_methods_ = dispmethods
+
+ # If we sublass a COM interface, for example:
+ #
+ # class IDispatch(IUnknown):
+ # ....
+ #
+ # then we need to make sure that POINTER(IDispatch) is a
+ # subclass of POINTER(IUnknown) because of the way ctypes
+ # typechecks work.
+ if bases == (object,):
+ _ptr_bases = (cls, _compointer_base)
+ else:
+ _ptr_bases = (cls, POINTER(bases[0]))
+
+ # The interface 'cls' is used as a mixin.
+ p = type(_compointer_base)("POINTER(%s)" % cls.__name__,
+ _ptr_bases,
+ {"__com_interface__": cls,
+ "_needs_com_addref_": None})
+
+ from ctypes import _pointer_type_cache
+ _pointer_type_cache[cls] = p
+
+ if cls._case_insensitive_:
+
+ @patcher.Patch(p)
+ class CaseInsensitive(object):
+ # case insensitive attributes for COM methods and properties
+ def __getattr__(self, name):
+ """Implement case insensitive access to methods and properties"""
+ try:
+ fixed_name = self.__map_case__[name.lower()]
+ except KeyError:
+ raise AttributeError(name)
+ if fixed_name != name: # prevent unbounded recursion
+ return getattr(self, fixed_name)
+ raise AttributeError(name)
+
+ # __setattr__ is pretty heavy-weight, because it is called for
+ # EVERY attribute assignment. Settings a non-com attribute
+ # through this function takes 8.6 usec, while without this
+ # function it takes 0.7 sec - 12 times slower.
+ #
+ # How much faster would this be if implemented in C?
+ def __setattr__(self, name, value):
+ """Implement case insensitive access to methods and properties"""
+ object.__setattr__(self,
+ self.__map_case__.get(name.lower(), name),
+ value)
+
+ @patcher.Patch(POINTER(p))
+ class ReferenceFix(object):
+ def __setitem__(self, index, value):
+ # We override the __setitem__ method of the
+ # POINTER(POINTER(interface)) type, so that the COM
+ # reference count is managed correctly.
+ #
+ # This is so that we can implement COM methods that have to
+ # return COM pointers more easily and consistent. Instead of
+ # using CopyComPointer in the method implementation, we can
+ # simply do:
+ #
+ # def GetTypeInfo(self, this, ..., pptinfo):
+ # if not pptinfo: return E_POINTER
+ # pptinfo[0] = a_com_interface_pointer
+ # return S_OK
+ if index != 0:
+ # CopyComPointer, which is in _ctypes, does only
+ # handle an index of 0. This code does what
+ # CopyComPointer should do if index != 0.
+ if bool(value):
+ value.AddRef()
+ super(POINTER(p), self).__setitem__(index, value)
+ return
+ from _ctypes import CopyComPointer
+ CopyComPointer(value, self)
+
+ return cls
+
+ def __setattr__(self, name, value):
+ if name == "_methods_":
+ # XXX I'm no longer sure why the code generator generates
+ # "_methods_ = []" in the interface definition, and later
+ # overrides this by "Interface._methods_ = [...]
+## assert self.__dict__.get("_methods_", None) is None
+ self._make_methods(value)
+ self._make_specials()
+ elif name == "_disp_methods_":
+ assert self.__dict__.get("_disp_methods_", None) is None
+ self._make_dispmethods(value)
+ self._make_specials()
+ type.__setattr__(self, name, value)
+
+ def _make_specials(self):
+ # This call installs methods that forward the Python protocols
+ # to COM protocols.
+
+ def has_name(name):
+ # Determine whether a property or method named 'name'
+ # exists
+ if self._case_insensitive_:
+ return name.lower() in self.__map_case__
+ return hasattr(self, name)
+
+ # XXX These special methods should be generated by the code generator.
+ if has_name("Count"):
+ @patcher.Patch(self)
+ class _(object):
+ def __len__(self):
+ "Return the the 'self.Count' property."
+ return self.Count
+
+ if has_name("Item"):
+ @patcher.Patch(self)
+ class _(object):
+ # 'Item' is the 'default' value. Make it available by
+ # calling the instance (Not sure this makes sense, but
+ # win32com does this also).
+ def __call__(self, *args, **kw):
+ "Return 'self.Item(*args, **kw)'"
+ return self.Item(*args, **kw)
+
+ # does this make sense? It seems that all standard typelibs I've
+ # seen so far that support .Item also support ._NewEnum
+ @patcher.no_replace
+ def __getitem__(self, index):
+ "Return 'self.Item(index)'"
+ # Handle tuples and all-slice
+ if isinstance(index, tuple):
+ args = index
+ elif index == _all_slice:
+ args = ()
+ else:
+ args = (index,)
+
+ try:
+ result = self.Item(*args)
+ except COMError, err:
+ (hresult, text, details) = err.args
+ if hresult == -2147352565: # DISP_E_BADINDEX
+ raise IndexError("invalid index")
+ else:
+ raise
+
+ # Note that result may be NULL COM pointer. There is no way
+ # to interpret this properly, so it is returned as-is.
+
+ # Hm, should we call __ctypes_from_outparam__ on the
+ # result?
+ return result
+
+ @patcher.no_replace
+ def __setitem__(self, index, value):
+ "Attempt 'self.Item[index] = value'"
+ try:
+ self.Item[index] = value
+ except COMError, err:
+ (hresult, text, details) = err.args
+ if hresult == -2147352565: # DISP_E_BADINDEX
+ raise IndexError("invalid index")
+ else:
+ raise
+ except TypeError:
+ msg = "%r object does not support item assignment"
+ raise TypeError(msg % type(self))
+
+ if has_name("_NewEnum"):
+ @patcher.Patch(self)
+ class _(object):
+ def __iter__(self):
+ "Return an iterator over the _NewEnum collection."
+ # This method returns a pointer to _some_ _NewEnum interface.
+ # It relies on the fact that the code generator creates next()
+ # methods for them automatically.
+ #
+ # Better would maybe to return an object that
+ # implements the Python iterator protocol, and
+ # forwards the calls to the COM interface.
+ enum = self._NewEnum
+ if isinstance(enum, types.MethodType):
+ # _NewEnum should be a propget property, with dispid -4.
+ #
+ # Sometimes, however, it is a method.
+ enum = enum()
+ if hasattr(enum, "Next"):
+ return enum
+ # _NewEnum returns an IUnknown pointer, QueryInterface() it to
+ # IEnumVARIANT
+ from comtypes.automation import IEnumVARIANT
+ return enum.QueryInterface(IEnumVARIANT)
+
+ def _make_case_insensitive(self):
+ # The __map_case__ dictionary maps lower case names to the
+ # names in the original spelling to enable case insensitive
+ # method and attribute access.
+ try:
+ self.__dict__["__map_case__"]
+ except KeyError:
+ d = {}
+ d.update(getattr(self, "__map_case__", {}))
+ self.__map_case__ = d
+
+ def _make_dispmethods(self, methods):
+ if self._case_insensitive_:
+ self._make_case_insensitive()
+
+ # create dispinterface methods and properties on the interface 'self'
+ properties = {}
+ for m in methods:
+ what, name, idlflags, restype, argspec = m
+
+ # is it a property set or property get?
+ is_prop = False
+
+ # argspec is a sequence of tuples, each tuple is:
+ # ([paramflags], type, name)
+ try:
+ memid = [x for x in idlflags if isinstance(x, int)][0]
+ except IndexError:
+ 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)
+ is_prop = True
+ setattr(self, name, accessor)
+ elif what == "DISPMETHOD": # DISPMETHOD
+ # argspec is a tuple of (idlflags, type, name[,
+ # defval]) items.
+ method = self._disp_method(memid, name, idlflags, restype, argspec)
+## not in 2.3 method.__name__ = name
+ if 'propget' in idlflags:
+ nargs = len(argspec)
+ properties.setdefault((name, nargs), [None, None, None])[0] = method
+ is_prop = True
+ elif 'propput' in idlflags:
+ nargs = len(argspec)-1
+ properties.setdefault((name, nargs), [None, None, None])[1] = method
+ is_prop = True
+ elif 'propputref' in idlflags:
+ nargs = len(argspec)-1
+ properties.setdefault((name, nargs), [None, None, None])[2] = method
+ is_prop = True
+ else:
+ setattr(self, name, method)
+ # COM is case insensitive.
+ #
+ # For a method, this is the real name. For a property,
+ # this is the name WITHOUT the _set_ or _get_ prefix.
+ if self._case_insensitive_:
+ self.__map_case__[name.lower()] = name
+ if is_prop:
+ self.__map_case__[name[5:].lower()] = name[5:]
+
+ for (name, nargs), methods in properties.items():
+ # methods contains [propget or None, propput or None, propputref or None]
+ if methods[1] is not None and methods[2] is not None:
+ # both propput and propputref.
+ #
+ # Create a setter method that examines the argument type
+ # and calls 'propputref' if it is an Object (in the VB
+ # sense), or call 'propput' otherwise.
+ propput = methods[1]
+ propputref = methods[2]
+ def put_or_putref(self, *args):
+ if _is_object(args[-1]):
+ return propputref(self, *args)
+ else:
+ return propput(self, *args)
+ methods[1] = put_or_putref
+ del methods[2]
+ elif methods[2] is not None:
+ # use propputref
+ del methods[1]
+ else:
+ # use propput (if any)
+ del methods[2]
+ if nargs:
+ setattr(self, name, named_property("%s.%s" % (self.__name__, name), *methods))
+ else:
+ assert len(methods) <= 2
+ setattr(self, name, property(*methods))
+
+ # COM is case insensitive
+ if self._case_insensitive_:
+ self.__map_case__[name.lower()] = name
+
+ # Some ideas, (not only) related to disp_methods:
+ #
+ # Should the functions/methods we create have restype and/or
+ # argtypes attributes?
+
+ def _disp_method(self, memid, name, idlflags, restype, argspec):
+ if 'propget' in idlflags:
+ def getfunc(obj, *args, **kw):
+ return self.Invoke(obj, memid, _invkind=2, *args, **kw) # DISPATCH_PROPERTYGET
+ return getfunc
+ elif 'propput' in idlflags:
+ def putfunc(obj, *args, **kw):
+ return self.Invoke(obj, memid, _invkind=4, *args, **kw) # DISPATCH_PROPERTYPUT
+ return putfunc
+ elif 'propputref' in idlflags:
+ def putfunc(obj, *args, **kw):
+ return self.Invoke(obj, memid, _invkind=8, *args, **kw) # DISPATCH_PROPERTYPUTREF
+ return putfunc
+ # a first attempt to make use of the restype. Still, support
+ # for named arguments and default argument values should be
+ # added.
+ if hasattr(restype, "__com_interface__"):
+ interface = restype.__com_interface__
+ def func(s, *args, **kw):
+ result = self.Invoke(s, memid, _invkind=1, *args, **kw)
+ if result is None:
+ return
+ return result.QueryInterface(interface)
+ else:
+ def func(obj, *args, **kw):
+ return self.Invoke(obj, memid, _invkind=1, *args, **kw) # DISPATCH_METHOD
+ return func
+
+ def _disp_property(self, memid, idlflags):
+ # XXX doc string missing in property
+ def _get(obj):
+ return obj.Invoke(memid, _invkind=2) # DISPATCH_PROPERTYGET
+ if "readonly" in idlflags:
+ return property(_get)
+ def _set(obj, value):
+ # Detect whether to use DISPATCH_PROPERTYPUT or
+ # DISPATCH_PROPERTYPUTREF
+ invkind = 8 if _is_object(value) else 4
+ return obj.Invoke(memid, value, _invkind=invkind)
+ return property(_get, _set)
+
+ def __get_baseinterface_methodcount(self):
+ "Return the number of com methods in the base interfaces"
+ try:
+ 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
+
+ def _fix_inout_args(self, func, argtypes, paramflags):
+ # This function provides a workaround for a bug in ctypes.
+ # [in, out] parameters must be converted with the argtype's
+ # .from_param() method BEFORE they are passed to the _ctypes
+ # build_callargs() function in Modules/_ctypes/_ctypes.c.
+ #
+ # For details see below.
+ #
+ # TODO: The workaround should be disabled when a ctypes
+ # version is used where the bug is fixed.
+ SIMPLETYPE = type(c_int)
+ BYREFTYPE = type(byref(c_int()))
+ def call_with_inout(self_, *args, **kw):
+ args = list(args)
+ # Indexed by order in the output
+ outargs = {}
+ outnum = 0
+ for i, info in enumerate(paramflags):
+ direction = info[0]
+ if direction & 3 == 3:
+ # This is an [in, out] parameter.
+ #
+ # Determine name and required type of the parameter.
+ name = info[1]
+ # [in, out] parameters are passed as pointers,
+ # this is the pointed-to type:
+ atyp = argtypes[i]._type_
+
+ # Get the actual parameter, either as positional or
+ # keyword arg.
+ try:
+ try:
+ v = args[i]
+ except IndexError:
+ v = kw[name]
+ except KeyError:
+ # no parameter was passed, make an empty one
+ # of the required type
+ v = atyp()
+ else:
+ # parameter was passed, call .from_param() to
+ # convert it to a ctypes type.
+ 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
+ # it will be wrapped in a pointer. Simply
+ # call the constructor with the argument
+ # in that case.
+ v = atyp(v)
+ else:
+ v = atyp.from_param(v)
+ assert not isinstance(v, BYREFTYPE)
+ outargs[outnum] = v
+ outnum += 1
+ if len(args) > i:
+ args[i] = v
+ else:
+ kw[name] = v
+ elif direction & 2 == 2:
+ outnum += 1
+
+ rescode = func(self_, *args, **kw)
+ # If there is only a single output value, then do not expect it to
+ # be iterable.
+ if len(outargs) == 1: # rescode is not iterable
+ return rescode.__ctypes_from_outparam__()
+
+ rescode = list(rescode)
+ for outnum, o in outargs.items():
+ rescode[outnum] = o.__ctypes_from_outparam__()
+ return rescode
+ return call_with_inout
+
+ def _make_methods(self, methods):
+ if self._case_insensitive_:
+ self._make_case_insensitive()
+
+ # we insist on an _iid_ in THIS class!
+ try:
+ iid = self.__dict__["_iid_"]
+ except KeyError:
+ raise AttributeError("this class must define an _iid_")
+ else:
+ iid = str(iid)
+## if iid in com_interface_registry:
+## # Warn when multiple interfaces are defined with identical iids.
+## # This would also trigger if we reload() a module that contains
+## # 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)
+## warnings.warn(text, UserWarning)
+ com_interface_registry[iid] = self
+ del iid
+ vtbl_offset = self.__get_baseinterface_methodcount()
+
+ properties = {}
+
+ # create private low level, and public high level methods
+ for i, item in enumerate(methods):
+ restype, name, argtypes, paramflags, idlflags, doc = item
+ # the function prototype
+ prototype = WINFUNCTYPE(restype, *argtypes)
+
+ # a low level unbound method calling the com method.
+ # attach it with a private name (__com_AddRef, for example),
+ # so that custom method implementations can call it.
+
+ # If the method returns a HRESULT, we pass the interface iid,
+ # so that we can request error info for the interface.
+ if restype == HRESULT:
+## print "%s.%s" % (self.__name__, name)
+ raw_func = prototype(i + vtbl_offset, name, None, self._iid_)
+ func = prototype(i + vtbl_offset, name, paramflags, self._iid_)
+ else:
+ raw_func = prototype(i + vtbl_offset, name, None, None)
+ func = prototype(i + vtbl_offset, name, paramflags, None)
+ setattr(self,
+ "_%s__com_%s" % (self.__name__, name),
+ instancemethod(raw_func, None, self))
+
+ if paramflags:
+ # see comment in the _fix_inout_args method
+ dirflags = [(p[0]&3) for p in paramflags]
+ if 3 in dirflags:
+## fullname = "%s::%s" % (self.__name__, name)
+## print "FIX %s" % fullname
+ func = self._fix_inout_args(func, argtypes, paramflags)
+
+ # 'func' is a high level function calling the COM method
+ func.__doc__ = doc
+ 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)
+
+ # is it a property set or property get?
+ is_prop = False
+
+ # XXX Hm. What, when paramflags is None?
+ # Or does have '0' values?
+ # Seems we loose then, at least for properties...
+
+ # The following code assumes that the docstrings for
+ # propget and propput are identical.
+ if "propget" in idlflags:
+ assert name.startswith("_get_")
+ nargs = len([flags for flags in paramflags
+ if flags[0] & 7 in (0, 1)])
+ # XXX or should we do this?
+ # nargs = len([flags for flags in paramflags
+ # if (flags[0] & 1) or (flags[0] == 0)])
+ propname = name[len("_get_"):]
+ properties.setdefault((propname, doc, nargs), [None, None, None])[0] = func
+ is_prop = True
+ elif "propput" in idlflags:
+ assert name.startswith("_set_")
+ nargs = len([flags for flags in paramflags
+ if flags[0] & 7 in (0, 1)]) - 1
+ propname = name[len("_set_"):]
+ properties.setdefault((propname, doc, nargs), [None, None, None])[1] = func
+ is_prop = True
+ elif "propputref" in idlflags:
+ assert name.startswith("_setref_")
+ nargs = len([flags for flags in paramflags
+ if flags[0] & 7 in (0, 1)]) - 1
+ propname = name[len("_setref_"):]
+ properties.setdefault((propname, doc, nargs), [None, None, None])[2] = func
+ is_prop = True
+
+ # We install the method in the class, except when it's a
+ # property accessor. And we make sure we don't overwrite
+ # a property that's already present in the class.
+ if not is_prop:
+ if hasattr(self, name):
+ setattr(self, "_" + name, mth)
+ else:
+ setattr(self, name, mth)
+
+ # COM is case insensitive.
+ #
+ # For a method, this is the real name. For a property,
+ # this is the name WITHOUT the _set_ or _get_ prefix.
+ if self._case_insensitive_:
+ self.__map_case__[name.lower()] = name
+ if is_prop:
+ self.__map_case__[name[5:].lower()] = name[5:]
+
+ # create public properties / attribute accessors
+ for (name, doc, nargs), methods in properties.items():
+ # methods contains [propget or None, propput or None, propputref or None]
+ if methods[1] is not None and methods[2] is not None:
+ # both propput and propputref.
+ #
+ # Create a setter method that examines the argument type
+ # and calls 'propputref' if it is an Object (in the VB
+ # sense), or call 'propput' otherwise.
+ propput = methods[1]
+ propputref = methods[2]
+ def put_or_putref(self, *args):
+ if _is_object(args[-1]):
+ return propputref(self, *args)
+ else:
+ return propput(self, *args)
+ methods[1] = put_or_putref
+ del methods[2]
+ elif methods[2] is not None:
+ # use propputref
+ del methods[1]
+ else:
+ # use propput (if any)
+ del methods[2]
+ if nargs == 0:
+ prop = property(*methods + [None, doc])
+ else:
+ # Hm, must be a descriptor where the __get__ method
+ # returns a bound object having __getitem__ and
+ # __setitem__ methods.
+ 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):
+ setattr(self, "_" + name, prop)
+ else:
+ setattr(self, name, prop)
+
+ # COM is case insensitive
+ if self._case_insensitive_:
+ self.__map_case__[name.lower()] = name
+
+
+################################################################
+# helper classes for COM propget / propput
+# Should they be implemented in C for speed?
+
+_all_slice = slice(None, None, None)
+
+
+class bound_named_property(object):
+ def __init__(self, name, getter, setter, im_inst):
+ self.name = name
+ self.im_inst = im_inst
+ self.getter = getter
+ self.setter = setter
+
+ def __getitem__(self, index):
+ if self.getter is None:
+ raise TypeError("unsubscriptable object")
+ if isinstance(index, tuple):
+ return self.getter(self.im_inst, *index)
+ elif index == _all_slice:
+ return self.getter(self.im_inst)
+ else:
+ return self.getter(self.im_inst, index)
+
+ def __call__(self, *args):
+ if self.getter is None:
+ raise TypeError("object is not callable")
+ return self.getter(self.im_inst, *args)
+
+ def __setitem__(self, index, value):
+ if self.setter is None:
+ raise TypeError("object does not support item assignment")
+ if isinstance(index, tuple):
+ self.setter(self.im_inst, *(index + (value,)))
+ elif index == _all_slice:
+ self.setter(self.im_inst, value)
+ else:
+ self.setter(self.im_inst, index, value)
+
+ def __repr__(self):
+ return "" % (self.name, id(self))
+
+ def __iter__(self):
+ """ Explicitly disallow iteration. """
+ msg = "%r is not iterable" % self.name
+ raise TypeError(msg)
+
+
+
+class named_property(object):
+ 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.name, self.getter, self.setter, im_inst)
+
+ # Make this a data descriptor
+ def __set__(self, obj):
+ raise AttributeError("Unsettable attribute")
+
+ def __repr__(self):
+ return "" % (self.name, id(self))
+
+################################################################
+
+class _compointer_meta(type(c_void_p), _cominterface_meta):
+ "metaclass for COM interface pointer classes"
+ # no functionality, but needed to avoid a metaclass conflict
+
+class _compointer_base(c_void_p):
+ "base class for COM interface pointer classes"
+ __metaclass__ = _compointer_meta
+ def __del__(self, _debug=logger.debug):
+ "Release the COM refcount we own."
+ if self:
+ # comtypes calls CoUnititialize() when the atexit handlers
+ # runs. CoUninitialize() cleans up the COM objects that
+ # are still alive. Python COM pointers may still be
+ # 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 type(self)._com_shutting_down:
+ _debug("Release %s", self)
+ self.Release()
+
+ def __cmp__(self, other):
+ """Compare pointers to COM interfaces."""
+ # COM identity rule
+ #
+ # XXX To compare COM interface pointers, should we
+ # automatically QueryInterface for IUnknown on both items, and
+ # compare the pointer values?
+ if not isinstance(other, _compointer_base):
+ return 1
+
+ # 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
+ return hash(super(_compointer_base, self).value)
+
+ # redefine the .value property; return the object itself.
+ def __get_value(self):
+ return self
+ value = property(__get_value, doc="""Return self.""")
+
+ def __repr__(self):
+ ptr = super(_compointer_base, self).value
+ return "<%s ptr=0x%x at %x>" % (self.__class__.__name__, ptr or 0, id(self))
+
+ # This fixes the problem when there are multiple python interface types
+ # wrapping the same COM interface. This could happen because some interfaces
+ # are contained in multiple typelibs.
+ #
+ # It also allows to pass a CoClass instance to an api
+ # expecting a COM interface.
+ def from_param(klass, value):
+ """Convert 'value' into a COM pointer to the interface.
+
+ This method accepts a COM pointer, or a CoClass instance
+ which is QueryInterface()d."""
+ if value is None:
+ return None
+ # CLF: 2013-01-18
+ # A default value of 0, meaning null, can pass through to here.
+ if value == 0:
+ return None
+ if isinstance(value, klass):
+ return value
+ # multiple python interface types for the same COM interface.
+ # Do we need more checks here?
+ if klass._iid_ == getattr(value, "_iid_", None):
+ return value
+ # Accept an CoClass instance which exposes the interface required.
+ try:
+ table = value._com_pointers_
+ except AttributeError:
+ pass
+ else:
+ try:
+ # a kind of QueryInterface
+ return table[klass._iid_]
+ except KeyError:
+ raise TypeError("Interface %s not supported" % klass._iid_)
+ return value.QueryInterface(klass.__com_interface__)
+ from_param = classmethod(from_param)
+
+################################################################
+
+from ctypes import _SimpleCData
+
+class BSTR(_SimpleCData):
+ "The windows BSTR data type"
+ _type_ = "X"
+ _needsfree = False
+ def __repr__(self):
+ return "%s(%r)" % (self.__class__.__name__, self.value)
+
+ def __ctypes_from_outparam__(self):
+ self._needsfree = True
+ return self.value
+
+ def __del__(self, _free=windll.oleaut32.SysFreeString):
+ # Free the string if self owns the memory
+ # or if instructed by __ctypes_from_outparam__.
+ if self._b_base_ is None \
+ or self._needsfree:
+ _free(self)
+
+ def from_param(cls, value):
+ """Convert into a foreign function call parameter."""
+ if isinstance(value, cls):
+ return value
+ # Although the builtin SimpleCData.from_param call does the
+ # right thing, it doesn't ensure that SysFreeString is called
+ # on destruction.
+ return cls(value)
+ from_param = classmethod(from_param)
+
+################################################################
+# IDL stuff
+
+class helpstring(unicode):
+ "Specifies the helpstring for a COM method or property."
+
+class defaultvalue(object):
+ "Specifies the default value for parameters marked optional."
+ def __init__(self, value):
+ self.value = value
+
+class dispid(int):
+ "Specifies the DISPID of a method or property."
+
+# XXX STDMETHOD, COMMETHOD, DISPMETHOD, and DISPPROPERTY should return
+# instances with methods, or at least accessors instead of tuple.
+
+def STDMETHOD(restype, name, argtypes=()):
+ "Specifies a COM method slot without idlflags"
+ # restype, name, argtypes, paramflags, idlflags, docstring
+ return restype, name, argtypes, None, (), None
+
+def DISPMETHOD(idlflags, restype, name, *argspec):
+ "Specifies a method of a dispinterface"
+ return "DISPMETHOD", name, idlflags, restype, argspec
+
+def DISPPROPERTY(idlflags, proptype, name):
+ "Specifies a property of a dispinterface"
+ return "DISPPROPERTY", name, idlflags, proptype, ()#, argspec
+
+# COMMETHOD returns:
+# restype, methodname, tuple(argtypes), tuple(paramflags), tuple(idlflags), helptext
+#
+# paramflags is a sequence of (flags (integer), paramname (string)
+# tuple(idlflags) is for the method itself: (dispid, 'readonly')
+#
+# Example: (HRESULT, 'Width', (c_long,), (2, 'rhs'), (4, 'readonly'), None)
+
+## sample generated code:
+## DISPPROPERTY([5, 'readonly'], OLE_YSIZE_HIMETRIC, 'Height'),
+## DISPMETHOD([6], None, 'Render',
+## ( [], c_int, 'hdc' ),
+## ( [], c_int, 'x' ),
+## ( [], c_int, 'y' ))
+
+################################################################
+
+_PARAMFLAGS = {
+ "in": 1,
+ "out": 2,
+ "lcid": 4,
+ "retval": 8,
+ "optional": 16,
+ }
+
+def _encode_idl(names):
+ # sum up all values found in _PARAMFLAGS, ignoring all others.
+ return sum([_PARAMFLAGS.get(n, 0) for n in names])
+
+_NOTHING = object()
+def _unpack_argspec(idl, typ, name=None, defval=_NOTHING):
+ return idl, typ, name, defval
+
+def COMMETHOD(idlflags, restype, methodname, *argspec):
+ """Specifies a COM method slot with idlflags.
+
+ XXX should explain the sematics of the arguments.
+ """
+ paramflags = []
+ argtypes = []
+
+ # collect all helpstring instances
+ # We should suppress docstrings when Python is started with -OO
+ helptext = [t for t in idlflags if isinstance(t, helpstring)]
+ # join them together(does this make sense?) and replace by None if empty.
+ helptext = "".join(helptext) or None
+
+ from comtypes.automation import VARIANT
+
+ for item in argspec:
+ idl, typ, argname, defval = _unpack_argspec(*item)
+ pflags = _encode_idl(idl)
+ if "optional" in idl:
+ if defval is _NOTHING:
+ if typ is VARIANT:
+ defval = VARIANT.missing
+ elif typ is POINTER(VARIANT):
+ defval = pointer(VARIANT.missing)
+ else:
+## 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:
+ paramflags.append((pflags, argname))
+ else:
+ paramflags.append((pflags, argname, defval))
+ argtypes.append(typ)
+ if "propget" in idlflags:
+ methodname = "_get_%s" % methodname
+ elif "propput" in idlflags:
+ methodname = "_set_%s" % methodname
+ elif "propputref" in idlflags:
+ methodname = "_setref_%s" % methodname
+ return restype, methodname, tuple(argtypes), tuple(paramflags), tuple(idlflags), helptext
+
+################################################################
+# IUnknown, the root of all evil...
+
+class IUnknown(object):
+ """The most basic COM interface.
+
+ Each subclasses of IUnknown must define these class attributes:
+
+ _iid_ - a GUID instance defining the identifier of this interface
+
+ _methods_ - a list of methods for this interface.
+
+ The _methods_ list must in VTable order. Methods are specified
+ with STDMETHOD or COMMETHOD calls.
+ """
+ _case_insensitive_ = False
+ __metaclass__ = _cominterface_meta
+ _iid_ = GUID("{00000000-0000-0000-C000-000000000046}")
+
+ _methods_ = [
+ STDMETHOD(HRESULT, "QueryInterface",
+ [POINTER(GUID), POINTER(c_void_p)]),
+ STDMETHOD(c_ulong, "AddRef"),
+ STDMETHOD(c_ulong, "Release")
+ ]
+
+ def QueryInterface(self, interface, iid=None):
+ "QueryInterface(interface) -> instance"
+ p = POINTER(interface)()
+ if iid is None:
+ iid = interface._iid_
+ self.__com_QueryInterface(byref(iid), byref(p))
+ clsid = self.__dict__.get('__clsid')
+ if clsid is not None:
+ p.__dict__['__clsid'] = clsid
+ return p
+
+ # these are only so that they get a docstring.
+ # XXX There should be other ways to install a docstring.
+ def AddRef(self):
+ "Increase the internal refcount by one and return it."
+ return self.__com_AddRef()
+
+ def Release(self):
+ "Decrease the internal refcount by one and return it."
+ return self.__com_Release()
+
+# IPersist is a trivial interface, which allows to ask an object about
+# its clsid.
+class IPersist(IUnknown):
+ _iid_ = GUID('{0000010C-0000-0000-C000-000000000046}')
+ _idlflags_ = []
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'GetClassID',
+ ( ['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
+ identified by the moniker."""
+ if interface is None:
+ interface = IUnknown
+ punk = POINTER(interface)()
+ # Do we need a way to specify the BIND_OPTS parameter?
+ _ole32.CoGetObject(unicode(displayname),
+ None,
+ byref(interface._iid_),
+ byref(punk))
+ return punk
+
+def CoCreateInstance(clsid, interface=None, clsctx=None, punkouter=None):
+ """The basic windows api to create a COM class object and return a
+ pointer to an interface.
+ """
+ if clsctx is None:
+ clsctx = CLSCTX_SERVER
+ if interface is None:
+ interface = IUnknown
+ p = POINTER(interface)()
+ iid = interface._iid_
+ _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)()
+ oledll.oleaut32.GetActiveObject(byref(clsid), None, byref(p))
+ if interface is not None:
+ p = p.QueryInterface(interface)
+ return p
+
+class MULTI_QI(Structure):
+ _fields_ = [("pIID", POINTER(GUID)),
+ ("pItf", POINTER(c_void_p)),
+ ("hr", HRESULT)]
+
+class _COAUTHIDENTITY(Structure):
+ _fields_ = [
+ ('User', POINTER(c_ushort)),
+ ('UserLength', c_ulong),
+ ('Domain', POINTER(c_ushort)),
+ ('DomainLength', c_ulong),
+ ('Password', POINTER(c_ushort)),
+ ('PasswordLength', c_ulong),
+ ('Flags', c_ulong),
+ ]
+COAUTHIDENTITY = _COAUTHIDENTITY
+
+class _COAUTHINFO(Structure):
+ _fields_ = [
+ ('dwAuthnSvc', c_ulong),
+ ('dwAuthzSvc', c_ulong),
+ ('pwszServerPrincName', c_wchar_p),
+ ('dwAuthnLevel', c_ulong),
+ ('dwImpersonationLevel', c_ulong),
+ ('pAuthIdentityData', POINTER(_COAUTHIDENTITY)),
+ ('dwCapabilities', c_ulong),
+ ]
+COAUTHINFO = _COAUTHINFO
+
+class _COSERVERINFO(Structure):
+ _fields_ = [
+ ('dwReserved1', c_ulong),
+ ('pwszName', c_wchar_p),
+ ('pAuthInfo', POINTER(_COAUTHINFO)),
+ ('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_ = [
+ ('cbStruct', c_ulong),
+ ('grfFlags', c_ulong),
+ ('grfMode', c_ulong),
+ ('dwTickCountDeadline', c_ulong)
+ ]
+# XXX Add __init__ which sets cbStruct?
+BIND_OPTS = tagBIND_OPTS
+
+class tagBIND_OPTS2(Structure):
+ _fields_ = [
+ ('cbStruct', c_ulong),
+ ('grfFlags', c_ulong),
+ ('grfMode', c_ulong),
+ ('dwTickCountDeadline', c_ulong),
+ ('dwTrackFlags', c_ulong),
+ ('dwClassContext', c_ulong),
+ ('locale', c_ulong),
+ ('pServerInfo', POINTER(_COSERVERINFO)),
+ ]
+# XXX Add __init__ which sets cbStruct?
+BINDOPTS2 = tagBIND_OPTS2
+
+#Structures for security setups
+#########################################
+class _SEC_WINNT_AUTH_IDENTITY(Structure):
+ _fields_ = [
+ ('User', POINTER(c_ushort)),
+ ('UserLength', c_ulong),
+ ('Domain', POINTER(c_ushort)),
+ ('DomainLength', c_ulong),
+ ('Password', POINTER(c_ushort)),
+ ('PasswordLength', c_ulong),
+ ('Flags', c_ulong),
+ ]
+SEC_WINNT_AUTH_IDENTITY = _SEC_WINNT_AUTH_IDENTITY
+
+class _SOLE_AUTHENTICATION_INFO(Structure):
+ _fields_ = [
+ ('dwAuthnSvc', c_ulong),
+ ('dwAuthzSvc', c_ulong),
+ ('pAuthInfo', POINTER(_SEC_WINNT_AUTH_IDENTITY)),
+ ]
+SOLE_AUTHENTICATION_INFO = _SOLE_AUTHENTICATION_INFO
+
+class _SOLE_AUTHENTICATION_LIST(Structure):
+ _fields_ = [
+ ('cAuthInfo', c_ulong),
+ ('pAuthInfo', POINTER(_SOLE_AUTHENTICATION_INFO)),
+ ]
+SOLE_AUTHENTICATION_LIST = _SOLE_AUTHENTICATION_LIST
+
+def CoCreateInstanceEx(clsid, interface=None,
+ clsctx=None,
+ machine=None,
+ pServerInfo=None):
+ """The basic windows api to create a COM class object and return a
+ pointer to an interface, possibly on another machine.
+
+ Passing both "machine" and "pServerInfo" results in a ValueError.
+
+ """
+ if clsctx is None:
+ clsctx=CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER
+
+ if pServerInfo is not None:
+ if machine is not None:
+ msg = "Can not specify both machine name and server info"
+ raise ValueError(msg)
+ elif machine is not None:
+ serverinfo = COSERVERINFO()
+ serverinfo.pwszName = machine
+ pServerInfo = byref(serverinfo)
+
+ if interface is None:
+ interface = IUnknown
+ multiqi = MULTI_QI()
+ multiqi.pIID = pointer(interface._iid_)
+ _ole32.CoCreateInstanceEx(byref(clsid),
+ None,
+ clsctx,
+ pServerInfo,
+ 1,
+ byref(multiqi))
+ return cast(multiqi.pItf, POINTER(interface))
+
+
+################################################################
+from comtypes._comobject import COMObject
+
+# 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):
+ __metaclass__ = _coclass_meta
+################################################################
diff --git a/tools/comtypes/comtypes/_comobject.py b/tools/comtypes/comtypes/_comobject.py
new file mode 100644
index 00000000000000..d3fca710447604
--- /dev/null
+++ b/tools/comtypes/comtypes/_comobject.py
@@ -0,0 +1,774 @@
+from ctypes import (
+ FormatError, POINTER, Structure, WINFUNCTYPE, byref, c_long, c_void_p,
+ oledll, pointer, windll
+)
+from _ctypes import CopyComPointer
+import logging
+import os
+
+from comtypes import COMError, ReturnHRESULT, instancemethod, _encode_idl
+from comtypes.errorinfo import ISupportErrorInfo, ReportException, ReportError
+from comtypes import IPersist
+from comtypes.hresult import (
+ DISP_E_BADINDEX, DISP_E_MEMBERNOTFOUND, E_FAIL, E_NOINTERFACE,
+ E_INVALIDARG, E_NOTIMPL, RPC_E_CHANGED_MODE, S_FALSE, S_OK
+)
+from comtypes.typeinfo import IProvideClassInfo, IProvideClassInfo2
+
+
+logger = logging.getLogger(__name__)
+_debug = logger.debug
+_warning = logger.warning
+_error = logger.error
+
+################################################################
+# COM object implementation
+
+# so we don't have to import comtypes.automation
+DISPATCH_METHOD = 1
+DISPATCH_PROPERTYGET = 2
+DISPATCH_PROPERTYPUT = 4
+DISPATCH_PROPERTYPUTREF = 8
+
+
+class E_NotImplemented(Exception):
+ """COM method is not implemented"""
+
+
+def HRESULT_FROM_WIN32(errcode):
+ "Convert a Windows error code into a HRESULT value."
+ if errcode is None:
+ return 0x80000000
+ if errcode & 0x80000000:
+ return errcode
+ return (errcode & 0xFFFF) | 0x80070000
+
+
+def winerror(exc):
+ """Return the windows error code from a WindowsError or COMError
+ instance."""
+ try:
+ code = exc[0]
+ if isinstance(code, (int, long)):
+ return code
+ except IndexError:
+ pass
+ # Sometimes, a WindowsError instance has no error code. An access
+ # violation raised by ctypes has only text, for example. In this
+ # cases we return a generic error code.
+ return E_FAIL
+
+
+def _do_implement(interface_name, method_name):
+ def _not_implemented(*args):
+ """Return E_NOTIMPL because the method is not implemented."""
+ _debug("unimplemented method %s_%s called", interface_name,
+ method_name)
+ return E_NOTIMPL
+ return _not_implemented
+
+
+def catch_errors(obj, mth, paramflags, interface, mthname):
+ clsid = getattr(obj, "_reg_clsid_", None)
+
+ def call_with_this(*args, **kw):
+ try:
+ result = mth(*args, **kw)
+ 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)
+ return HRESULT_FROM_WIN32(winerror(details))
+ except E_NotImplemented:
+ _warning("Unimplemented method %s.%s called", interface.__name__,
+ mthname)
+ return E_NOTIMPL
+ except:
+ _error("Exception in %s.%s implementation:", interface.__name__,
+ mthname, exc_info=True)
+ return ReportException(E_FAIL, interface._iid_, clsid=clsid)
+ if result is None:
+ return S_OK
+ return result
+ if paramflags is 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, paramflags, interface, mthname)
+ code = mth.func_code
+ if code.co_varnames[1:2] == ("this",):
+ 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
+ # direction flag is either exactly '0' or has the '1' bit set:
+ # 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)
+
+ clsid = getattr(inst, "_reg_clsid_", None)
+
+ def call_without_this(this, *args):
+ # 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(*inargs)
+ if args_out == 1:
+ args[args_out_idx[0]][0] = result
+ elif args_out != 0:
+ if len(result) != args_out:
+ msg = "Method should have returned a %s-tuple" % args_out
+ raise ValueError(msg)
+ for i, value in enumerate(result):
+ 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, 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
+ except (ValueError, TypeError):
+ msg = str(details)
+ else:
+ msg = "%s: %s" % (source, descr)
+ hr = HRESULT_FROM_WIN32(hr)
+ return ReportError(msg, iid=interface._iid_, clsid=clsid,
+ hresult=hr)
+ except WindowsError, details:
+ _error("Exception in %s.%s implementation:", interface.__name__,
+ mthname, exc_info=True)
+ hr = HRESULT_FROM_WIN32(winerror(details))
+ return ReportException(hr, interface._iid_, clsid=clsid)
+ except E_NotImplemented:
+ _warning("Unimplemented method %s.%s called", interface.__name__,
+ mthname)
+ return E_NOTIMPL
+ except:
+ _error("Exception in %s.%s implementation:", interface.__name__,
+ mthname, exc_info=True)
+ return ReportException(E_FAIL, interface._iid_, clsid=clsid)
+ return S_OK
+ if args_out:
+ call_without_this.has_outargs = True
+ return call_without_this
+
+
+class _MethodFinder(object):
+ def __init__(self, inst):
+ self.inst = inst
+ # map lower case names to names with correct spelling.
+ self.names = dict([(n.lower(), n) for n in dir(inst)])
+
+ def get_impl(self, interface, mthname, paramflags, idlflags):
+ mth = self.find_impl(interface, mthname, paramflags, idlflags)
+ if mth is None:
+ return _do_implement(interface.__name__, mthname)
+ return hack(self.inst, mth, paramflags, interface, mthname)
+
+ def find_method(self, fq_name, mthname):
+ # Try to find a method, first with the fully qualified name
+ # ('IUnknown_QueryInterface'), if that fails try the simple
+ # name ('QueryInterface')
+ try:
+ return getattr(self.inst, fq_name)
+ except AttributeError:
+ pass
+ return getattr(self.inst, mthname)
+
+ def find_impl(self, interface, mthname, paramflags, idlflags):
+ fq_name = "%s_%s" % (interface.__name__, mthname)
+ if interface._case_insensitive_:
+ # simple name, like 'QueryInterface'
+ mthname = self.names.get(mthname.lower(), mthname)
+ # qualified name, like 'IUnknown_QueryInterface'
+ fq_name = self.names.get(fq_name.lower(), fq_name)
+
+ try:
+ return self.find_method(fq_name, mthname)
+ except AttributeError:
+ pass
+ propname = mthname[5:] # strip the '_get_' or '_set' prefix
+ if interface._case_insensitive_:
+ propname = self.names.get(propname.lower(), propname)
+ # propput and propget is done with 'normal' attribute access,
+ # but only for COM properties that do not take additional
+ # arguments:
+
+ if "propget" in idlflags and len(paramflags) == 1:
+ return self.getter(propname)
+ if "propput" in idlflags and len(paramflags) == 1:
+ return self.setter(propname)
+ _debug("%r: %s.%s not implemented", self.inst, interface.__name__,
+ mthname)
+ return None
+
+ def setter(self, propname):
+ #
+ def set(self, value):
+ try:
+ # XXX this may not be correct is the object implements
+ # _get_PropName but not _set_PropName
+ setattr(self, propname, value)
+ except AttributeError:
+ raise E_NotImplemented()
+ return instancemethod(set, self.inst, type(self.inst))
+
+ def getter(self, propname):
+ def get(self):
+ try:
+ return getattr(self, propname)
+ except AttributeError:
+ raise E_NotImplemented()
+ return instancemethod(get, self.inst, type(self.inst))
+
+
+def _create_vtbl_type(fields, itf):
+ try:
+ return _vtbl_types[fields]
+ except KeyError:
+ class Vtbl(Structure):
+ _fields_ = fields
+ Vtbl.__name__ = "Vtbl_%s" % itf.__name__
+ _vtbl_types[fields] = Vtbl
+ return Vtbl
+
+# Ugh. Another type cache to avoid leaking types.
+_vtbl_types = {}
+
+################################################################
+
+try:
+ if os.name == "ce":
+ _InterlockedIncrement = windll.coredll.InterlockedIncrement
+ _InterlockedDecrement = windll.coredll.InterlockedDecrement
+ else:
+ _InterlockedIncrement = windll.kernel32.InterlockedIncrement
+ _InterlockedDecrement = windll.kernel32.InterlockedDecrement
+except AttributeError:
+ import threading
+ _lock = threading.Lock()
+ _acquire = _lock.acquire
+ _release = _lock.release
+ # win 64 doesn't have these functions
+
+ def _InterlockedIncrement(ob):
+ _acquire()
+ refcnt = ob.value + 1
+ ob.value = refcnt
+ _release()
+ return refcnt
+
+ def _InterlockedDecrement(ob):
+ _acquire()
+ refcnt = ob.value - 1
+ ob.value = refcnt
+ _release()
+ return refcnt
+else:
+ _InterlockedIncrement.argtypes = [POINTER(c_long)]
+ _InterlockedDecrement.argtypes = [POINTER(c_long)]
+ _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_ = {}
+
+ def __new__(cls, *args, **kw):
+ self = super(COMObject, cls).__new__(cls)
+ if isinstance(self, c_void_p):
+ # We build the VTables only for direct instances of
+ # CoClass, not for POINTERs to CoClass.
+ return self
+ if hasattr(self, "_com_interfaces_"):
+ self.__prepare_comobject()
+ return self
+
+ def __prepare_comobject(self):
+ # When a CoClass instance is created, COM pointers to all
+ # interfaces are created. Also, the CoClass must be kept alive as
+ # until the COM reference count drops to zero, even if no Python
+ # code keeps a reference to the object.
+ #
+ # The _com_pointers_ instance variable maps string interface iids
+ # to C compatible COM pointers.
+ self._com_pointers_ = {}
+ # COM refcount starts at zero.
+ self._refcnt = c_long(0)
+
+ # Some interfaces have a default implementation in COMObject:
+ # - ISupportErrorInfo
+ # - IPersist (if the subclass has a _reg_clsid_ attribute)
+ # - IProvideClassInfo (if the subclass has a _reg_clsid_ attribute)
+ # - IProvideClassInfo2 (if the subclass has a _outgoing_interfaces_
+ # attribute)
+ #
+ # Add these if they are not listed in _com_interfaces_.
+ interfaces = tuple(self._com_interfaces_)
+ if ISupportErrorInfo not in interfaces:
+ interfaces += (ISupportErrorInfo,)
+ if hasattr(self, "_reg_typelib_"):
+ from comtypes.typeinfo import LoadRegTypeLib
+ self._COMObject__typelib = LoadRegTypeLib(*self._reg_typelib_)
+ if hasattr(self, "_reg_clsid_"):
+ if IProvideClassInfo not in interfaces:
+ interfaces += (IProvideClassInfo,)
+ if hasattr(self, "_outgoing_interfaces_") and \
+ IProvideClassInfo2 not in interfaces:
+ interfaces += (IProvideClassInfo2,)
+ if hasattr(self, "_reg_clsid_"):
+ if IPersist not in interfaces:
+ interfaces += (IPersist,)
+ for itf in interfaces[::-1]:
+ self.__make_interface_pointer(itf)
+
+ def __make_interface_pointer(self, itf):
+ methods = [] # method implementations
+ fields = [] # (name, prototype) for virtual function table
+ iids = [] # interface identifiers.
+ # iterate over interface inheritance in reverse order to build the
+ # virtual function table, and leave out the 'object' base class.
+ finder = self._get_method_finder_(itf)
+ for interface in itf.__mro__[-2::-1]:
+ iids.append(interface._iid_)
+ for m in interface._methods_:
+ restype, mthname, argtypes, paramflags, idlflags, helptext = m
+ proto = WINFUNCTYPE(restype, c_void_p, *argtypes)
+ fields.append((mthname, proto))
+ mth = finder.get_impl(interface, mthname, paramflags, idlflags)
+ methods.append(proto(mth))
+ Vtbl = _create_vtbl_type(tuple(fields), itf)
+ vtbl = Vtbl(*methods)
+ for iid in iids:
+ self._com_pointers_[iid] = pointer(pointer(vtbl))
+ if hasattr(itf, "_disp_methods_"):
+ self._dispimpl_ = {}
+ for m in itf._disp_methods_:
+ what, mthname, idlflags, restype, argspec = m
+ #################
+ # What we have:
+ #
+ # restypes is a ctypes type or None
+ # argspec is seq. of (['in'], paramtype, paramname) tuples (or
+ # lists?)
+ #################
+ # What we need:
+ #
+ # idlflags must contain 'propget', 'propset' and so on:
+ # Must be constructed by converting disptype
+ #
+ # paramflags must be a sequence
+ # of (F_IN|F_OUT|F_RETVAL, paramname[, default-value]) tuples
+ #
+ # comtypes has this function which helps:
+ # def _encode_idl(names):
+ # # convert to F_xxx and sum up "in", "out",
+ # # "retval" values found in _PARAMFLAGS, ignoring
+ # # other stuff.
+ # return sum([_PARAMFLAGS.get(n, 0) for n in names])
+ #################
+
+ if what == "DISPMETHOD":
+ if 'propget' in idlflags:
+ invkind = 2 # DISPATCH_PROPERTYGET
+ mthname = "_get_" + mthname
+ elif 'propput' in idlflags:
+ invkind = 4 # DISPATCH_PROPERTYPUT
+ mthname = "_set_" + mthname
+ elif 'propputref' in idlflags:
+ invkind = 8 # DISPATCH_PROPERTYPUTREF
+ mthname = "_setref_" + mthname
+ else:
+ invkind = 1 # DISPATCH_METHOD
+ if restype:
+ argspec = argspec + ((['out'], restype, ""),)
+ self.__make_dispentry(finder, interface, mthname,
+ idlflags, argspec, invkind)
+ elif what == "DISPPROPERTY":
+ # DISPPROPERTY have implicit "out"
+ if restype:
+ argspec += ((['out'], restype, ""),)
+ self.__make_dispentry(finder, interface,
+ "_get_" + mthname,
+ idlflags, argspec,
+ 2 # DISPATCH_PROPERTYGET
+ )
+ if not 'readonly' in idlflags:
+ self.__make_dispentry(finder, interface,
+ "_set_" + mthname,
+ idlflags, argspec,
+ 4) # DISPATCH_PROPERTYPUT
+ # Add DISPATCH_PROPERTYPUTREF also?
+
+ def __make_dispentry(self,
+ finder, interface, mthname,
+ idlflags, argspec, invkind):
+ # We build a _dispmap_ entry now that maps invkind and
+ # dispid to implementations that the finder finds;
+ # IDispatch_Invoke will later call it.
+ paramflags = [((_encode_idl(x[0]), x[1]) + tuple(x[3:]))
+ for x in argspec]
+ # XXX can the dispid be at a different index? Check codegenerator.
+ dispid = idlflags[0]
+ impl = finder.get_impl(interface, mthname, paramflags, idlflags)
+ self._dispimpl_[(dispid, invkind)] = impl
+ # invkind is really a set of flags; we allow both
+ # DISPATCH_METHOD and DISPATCH_PROPERTYGET (win32com uses
+ # this, maybe other languages too?)
+ if invkind in (1, 2):
+ self._dispimpl_[(dispid, 3)] = impl
+
+ def _get_method_finder_(self, itf):
+ # This method can be overridden to customize how methods are
+ # found.
+ return _MethodFinder(self)
+
+ ################################################################
+ # LocalServer / InprocServer stuff
+ __server__ = None
+
+ @staticmethod
+ def __run_inprocserver__():
+ if COMObject.__server__ is None:
+ COMObject.__server__ = InprocServer()
+ elif isinstance(COMObject.__server__, InprocServer):
+ pass
+ else:
+ raise RuntimeError("Wrong server type")
+
+ @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
+
+ @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()
+
+ @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()
+ #
+ ################################################################
+
+ #########################################################
+ # IUnknown methods implementations
+ def IUnknown_AddRef(self, this,
+ __InterlockedIncrement=_InterlockedIncrement,
+ _debug=_debug):
+ result = __InterlockedIncrement(self._refcnt)
+ if result == 1:
+ 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):
+ # If this is called at COM shutdown, _InterlockedDecrement()
+ # must still be available, although module level variables may
+ # have been deleted already - so we supply it as default
+ # argument.
+ result = __InterlockedDecrement(self._refcnt)
+ _debug("%r.Release() -> %s", self, result)
+ if result == 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, _debug=_debug):
+ # XXX This is probably too slow.
+ # riid[0].hashcode() alone takes 33 us!
+ iid = riid[0]
+ ptr = self._com_pointers_.get(iid, None)
+ if ptr is not None:
+ # CopyComPointer(src, dst) calls AddRef!
+ _debug("%r.QueryInterface(%s) -> S_OK", self, iid)
+ return CopyComPointer(ptr, ppvObj)
+ _debug("%r.QueryInterface(%s) -> E_NOINTERFACE", self, iid)
+ return E_NOINTERFACE
+
+ def QueryInterface(self, interface):
+ "Query the object for an interface pointer"
+ # This method is NOT the implementation of
+ # IUnknown::QueryInterface, instead it is supposed to be
+ # called on an COMObject by user code. It allows to get COM
+ # interface pointers from COMObject instances.
+ ptr = self._com_pointers_.get(interface._iid_, None)
+ if ptr is None:
+ raise COMError(E_NOINTERFACE, FormatError(E_NOINTERFACE),
+ (None, None, 0, None, None))
+ # CopyComPointer(src, dst) calls AddRef!
+ result = POINTER(interface)()
+ CopyComPointer(ptr, byref(result))
+ return result
+
+ ################################################################
+ # ISupportErrorInfo::InterfaceSupportsErrorInfo implementation
+ def ISupportErrorInfo_InterfaceSupportsErrorInfo(self, this, riid):
+ if riid[0] in self._com_pointers_:
+ return S_OK
+ return S_FALSE
+
+ ################################################################
+ # IProvideClassInfo::GetClassInfo implementation
+ def IProvideClassInfo_GetClassInfo(self):
+ try:
+ self.__typelib
+ except AttributeError:
+ raise WindowsError(E_NOTIMPL)
+ return self.__typelib.GetTypeInfoOfGuid(self._reg_clsid_)
+
+ ################################################################
+ # IProvideClassInfo2::GetGUID implementation
+
+ def IProvideClassInfo2_GetGUID(self, dwGuidKind):
+ # GUIDKIND_DEFAULT_SOURCE_DISP_IID = 1
+ if dwGuidKind != 1:
+ raise WindowsError(E_INVALIDARG)
+ return self._outgoing_interfaces_[0]._iid_
+
+ ################################################################
+ # IDispatch methods
+ @property
+ def __typeinfo(self):
+ # XXX Looks like this better be a static property, set by the
+ # code that sets __typelib also...
+ iid = self._com_interfaces_[0]._iid_
+ return self.__typelib.GetTypeInfoOfGuid(iid)
+
+ def IDispatch_GetTypeInfoCount(self):
+ try:
+ self.__typelib
+ except AttributeError:
+ return 0
+ else:
+ return 1
+
+ def IDispatch_GetTypeInfo(self, this, itinfo, lcid, ptinfo):
+ if itinfo != 0:
+ return DISP_E_BADINDEX
+ try:
+ ptinfo[0] = self.__typeinfo
+ return S_OK
+ except AttributeError:
+ return E_NOTIMPL
+
+ def IDispatch_GetIDsOfNames(self, this, riid, rgszNames, cNames, lcid,
+ rgDispId):
+ # This call uses windll instead of oledll so that a failed
+ # call to DispGetIDsOfNames will return a HRESULT instead of
+ # raising an error.
+ try:
+ tinfo = self.__typeinfo
+ except AttributeError:
+ return E_NOTIMPL
+ return windll.oleaut32.DispGetIDsOfNames(tinfo,
+ rgszNames, cNames, rgDispId)
+
+ def IDispatch_Invoke(self, this, dispIdMember, riid, lcid, wFlags,
+ pDispParams, pVarResult, pExcepInfo, puArgErr):
+ try:
+ self._dispimpl_
+ except AttributeError:
+ try:
+ tinfo = self.__typeinfo
+ except AttributeError:
+ # Hm, we pretend to implement IDispatch, but have no
+ # typeinfo, and so cannot fulfill the contract. Should we
+ # better return E_NOTIMPL or DISP_E_MEMBERNOTFOUND? Some
+ # clients call IDispatch_Invoke with 'known' DISPID_...'
+ # values, without going through GetIDsOfNames first.
+ return DISP_E_MEMBERNOTFOUND
+ # This call uses windll instead of oledll so that a failed
+ # call to DispInvoke will return a HRESULT instead of raising
+ # an error.
+ interface = self._com_interfaces_[0]
+ ptr = self._com_pointers_[interface._iid_]
+ return windll.oleaut32.DispInvoke(
+ ptr, tinfo, dispIdMember, wFlags, pDispParams, pVarResult,
+ pExcepInfo, puArgErr
+ )
+
+ try:
+ # XXX Hm, wFlags should be considered a SET of flags...
+ mth = self._dispimpl_[(dispIdMember, wFlags)]
+ except KeyError:
+ return DISP_E_MEMBERNOTFOUND
+
+ # Unpack the parameters: It would be great if we could use the
+ # DispGetParam function - but we cannot since it requires that
+ # we pass a VARTYPE for each argument and we do not know that.
+ #
+ # Seems that n arguments have dispids (0, 1, ..., n-1).
+ # Unnamed arguments are packed into the DISPPARAMS array in
+ # reverse order (starting with the highest dispid), named
+ # arguments are packed in the order specified by the
+ # rgdispidNamedArgs array.
+ #
+ params = pDispParams[0]
+
+ if wFlags & (4 | 8):
+ # DISPATCH_PROPERTYPUT
+ # DISPATCH_PROPERTYPUTREF
+ #
+ # How are the parameters unpacked for propertyput
+ # operations with additional parameters? Can propput
+ # have additional args?
+ args = [params.rgvarg[i].value
+ for i in reversed(range(params.cNamedArgs))]
+ # MSDN: pVarResult is ignored if DISPATCH_PROPERTYPUT or
+ # DISPATCH_PROPERTYPUTREF is specified.
+ return mth(this, *args)
+
+ else:
+ # 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
+ num_unnamed = params.cArgs - params.cNamedArgs
+ unnamed_indexes = list(reversed(range(num_unnamed)))
+ # It seems that this code calculates the indexes of the
+ # parameters in the params.rgvarg array correctly.
+ indexes = named_indexes + unnamed_indexes
+ args = [params.rgvarg[i].value for i in indexes]
+
+ if pVarResult and getattr(mth, "has_outargs", False):
+ args.append(pVarResult)
+ return mth(this, *args)
+
+ ################################################################
+ # IPersist interface
+ def IPersist_GetClassID(self):
+ return self._reg_clsid_
+
+__all__ = ["COMObject"]
diff --git a/tools/comtypes/comtypes/_meta.py b/tools/comtypes/comtypes/_meta.py
new file mode 100644
index 00000000000000..2d26b15d15648b
--- /dev/null
+++ b/tools/comtypes/comtypes/_meta.py
@@ -0,0 +1,61 @@
+# comtypes._meta helper module
+from ctypes import POINTER, c_void_p, cast
+import comtypes
+
+################################################################
+# metaclass for CoClass (in comtypes/__init__.py)
+
+def _wrap_coclass(self):
+ # We are an IUnknown pointer, represented as a c_void_p instance,
+ # but we really want this interface:
+ itf = self._com_interfaces_[0]
+ punk = cast(self, POINTER(itf))
+ result = punk.QueryInterface(itf)
+ 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:
+#
+# POINTER(App)
+# App
+# CoClass
+# c_void_p
+# _SimpleCData
+# _CData
+# object
+
+class _coclass_meta(type):
+ # metaclass for CoClass
+ #
+ # When a CoClass subclass is created, create a POINTER(...) type
+ # for that class, with bases 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.
+ def __new__(cls, name, bases, namespace):
+ klass = type.__new__(cls, name, bases, namespace)
+ if bases == (object,):
+ return klass
+ # XXX We should insist that a _reg_clsid_ is present.
+ if "_reg_clsid_" in namespace:
+ 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,
+ "from_param": classmethod(_coclass_from_param),
+ })
+ from ctypes import _pointer_type_cache
+ _pointer_type_cache[klass] = PTR
+
+ return klass
+
+# will not work if we change the order of the two base classes!
+class _coclass_pointer_meta(type(c_void_p), _coclass_meta):
+ pass
diff --git a/tools/comtypes/comtypes/_safearray.py b/tools/comtypes/comtypes/_safearray.py
new file mode 100644
index 00000000000000..76e41b9d79afc1
--- /dev/null
+++ b/tools/comtypes/comtypes/_safearray.py
@@ -0,0 +1,128 @@
+"""SAFEARRAY api functions, data types, and constants."""
+
+from ctypes import *
+from ctypes.wintypes import *
+from comtypes import HRESULT, GUID
+
+################################################################
+##if __debug__:
+## from ctypeslib.dynamic_module import include
+## include("""\
+## #define UNICODE
+## #define NO_STRICT
+## #include
+## """,
+## persist=True)
+
+################################################################
+
+VARTYPE = c_ushort
+PVOID = c_void_p
+USHORT = c_ushort
+
+_oleaut32 = WinDLL("oleaut32")
+
+class tagSAFEARRAYBOUND(Structure):
+ _fields_ = [
+ ('cElements', DWORD),
+ ('lLbound', LONG),
+]
+SAFEARRAYBOUND = tagSAFEARRAYBOUND
+
+class tagSAFEARRAY(Structure):
+ _fields_ = [
+ ('cDims', USHORT),
+ ('fFeatures', USHORT),
+ ('cbElements', DWORD),
+ ('cLocks', DWORD),
+ ('pvData', PVOID),
+ ('rgsabound', SAFEARRAYBOUND * 1),
+ ]
+SAFEARRAY = tagSAFEARRAY
+
+SafeArrayAccessData = _oleaut32.SafeArrayAccessData
+SafeArrayAccessData.restype = HRESULT
+# Last parameter manually changed from POINTER(c_void_p) to c_void_p:
+SafeArrayAccessData.argtypes = [POINTER(SAFEARRAY), c_void_p]
+
+SafeArrayCreateVectorEx = _oleaut32.SafeArrayCreateVectorEx
+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)]
+
+_SafeArrayGetVartype = _oleaut32.SafeArrayGetVartype
+_SafeArrayGetVartype.restype = HRESULT
+_SafeArrayGetVartype.argtypes = [POINTER(SAFEARRAY), POINTER(VARTYPE)]
+def SafeArrayGetVartype(pa):
+ result = VARTYPE()
+ _SafeArrayGetVartype(pa, result)
+ return result.value
+
+SafeArrayGetElement = _oleaut32.SafeArrayGetElement
+SafeArrayGetElement.restype = HRESULT
+SafeArrayGetElement.argtypes = [POINTER(SAFEARRAY), POINTER(LONG), c_void_p]
+
+SafeArrayDestroy = _oleaut32.SafeArrayDestroy
+SafeArrayDestroy.restype = HRESULT
+SafeArrayDestroy.argtypes = [POINTER(SAFEARRAY)]
+
+SafeArrayCreateVector = _oleaut32.SafeArrayCreateVector
+SafeArrayCreateVector.restype = POINTER(SAFEARRAY)
+SafeArrayCreateVector.argtypes = [VARTYPE, LONG, DWORD]
+
+SafeArrayDestroyData = _oleaut32.SafeArrayDestroyData
+SafeArrayDestroyData.restype = HRESULT
+SafeArrayDestroyData.argtypes = [POINTER(SAFEARRAY)]
+
+SafeArrayGetDim = _oleaut32.SafeArrayGetDim
+SafeArrayGetDim.restype = UINT
+SafeArrayGetDim.argtypes = [POINTER(SAFEARRAY)]
+
+_SafeArrayGetLBound = _oleaut32.SafeArrayGetLBound
+_SafeArrayGetLBound.restype = HRESULT
+_SafeArrayGetLBound.argtypes = [POINTER(SAFEARRAY), UINT, POINTER(LONG)]
+def SafeArrayGetLBound(pa, dim):
+ result = LONG()
+ _SafeArrayGetLBound(pa, dim, result)
+ return result.value
+
+_SafeArrayGetUBound = _oleaut32.SafeArrayGetUBound
+_SafeArrayGetUBound.restype = HRESULT
+_SafeArrayGetUBound.argtypes = [POINTER(SAFEARRAY), UINT, POINTER(LONG)]
+def SafeArrayGetUBound(pa, dim):
+ result = LONG()
+ _SafeArrayGetUBound(pa, dim, result)
+ return result.value
+
+
+SafeArrayLock = _oleaut32.SafeArrayLock
+SafeArrayLock.restype = HRESULT
+SafeArrayLock.argtypes = [POINTER(SAFEARRAY)]
+SafeArrayPtrOfIndex = _oleaut32.SafeArrayPtrOfIndex
+SafeArrayPtrOfIndex.restype = HRESULT
+# Last parameter manually changed from POINTER(c_void_p) to c_void_p:
+SafeArrayPtrOfIndex.argtypes = [POINTER(SAFEARRAY), POINTER(LONG), c_void_p]
+SafeArrayUnlock = _oleaut32.SafeArrayUnlock
+SafeArrayUnlock.restype = HRESULT
+SafeArrayUnlock.argtypes = [POINTER(SAFEARRAY)]
+_SafeArrayGetIID = _oleaut32.SafeArrayGetIID
+_SafeArrayGetIID.restype = HRESULT
+_SafeArrayGetIID.argtypes = [POINTER(SAFEARRAY), POINTER(GUID)]
+def SafeArrayGetIID(pa):
+ result = GUID()
+ _SafeArrayGetIID(pa, result)
+ return result
+SafeArrayDestroyDescriptor = _oleaut32.SafeArrayDestroyDescriptor
+SafeArrayDestroyDescriptor.restype = HRESULT
+SafeArrayDestroyDescriptor.argtypes = [POINTER(SAFEARRAY)]
diff --git a/tools/comtypes/comtypes/automation.py b/tools/comtypes/comtypes/automation.py
new file mode 100644
index 00000000000000..7b709826d2bf83
--- /dev/null
+++ b/tools/comtypes/comtypes/automation.py
@@ -0,0 +1,881 @@
+# comtypes.automation module
+import array
+import datetime
+import decimal
+
+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 *
+from comtypes.patcher import Patch
+from comtypes import npsupport
+try:
+ from comtypes import _safearray
+except (ImportError, AttributeError):
+ class _safearray(object):
+ tagSAFEARRAY = None
+
+from ctypes.wintypes import DWORD, LONG, UINT, VARIANT_BOOL, WCHAR, WORD
+
+
+LCID = DWORD
+DISPID = LONG
+SCODE = LONG
+
+VARTYPE = c_ushort
+
+DISPATCH_METHOD = 1
+DISPATCH_PROPERTYGET = 2
+DISPATCH_PROPERTYPUT = 4
+DISPATCH_PROPERTYPUTREF = 8
+
+tagINVOKEKIND = c_int
+INVOKE_FUNC = DISPATCH_METHOD
+INVOKE_PROPERTYGET = DISPATCH_PROPERTYGET
+INVOKE_PROPERTYPUT = DISPATCH_PROPERTYPUT
+INVOKE_PROPERTYPUTREF = DISPATCH_PROPERTYPUTREF
+INVOKEKIND = tagINVOKEKIND
+
+
+################################
+# helpers
+IID_NULL = GUID()
+riid_null = byref(IID_NULL)
+_byref_type = type(byref(c_int()))
+
+# 30. December 1899, midnight. For VT_DATE.
+_com_null_date = datetime.datetime(1899, 12, 30, 0, 0, 0)
+
+################################################################
+# VARIANT, in all it's glory.
+VARENUM = c_int # enum
+VT_EMPTY = 0
+VT_NULL = 1
+VT_I2 = 2
+VT_I4 = 3
+VT_R4 = 4
+VT_R8 = 5
+VT_CY = 6
+VT_DATE = 7
+VT_BSTR = 8
+VT_DISPATCH = 9
+VT_ERROR = 10
+VT_BOOL = 11
+VT_VARIANT = 12
+VT_UNKNOWN = 13
+VT_DECIMAL = 14
+VT_I1 = 16
+VT_UI1 = 17
+VT_UI2 = 18
+VT_UI4 = 19
+VT_I8 = 20
+VT_UI8 = 21
+VT_INT = 22
+VT_UINT = 23
+VT_VOID = 24
+VT_HRESULT = 25
+VT_PTR = 26
+VT_SAFEARRAY = 27
+VT_CARRAY = 28
+VT_USERDEFINED = 29
+VT_LPSTR = 30
+VT_LPWSTR = 31
+VT_RECORD = 36
+VT_INT_PTR = 37
+VT_UINT_PTR = 38
+VT_FILETIME = 64
+VT_BLOB = 65
+VT_STREAM = 66
+VT_STORAGE = 67
+VT_STREAMED_OBJECT = 68
+VT_STORED_OBJECT = 69
+VT_BLOB_OBJECT = 70
+VT_CF = 71
+VT_CLSID = 72
+VT_VERSIONED_STREAM = 73
+VT_BSTR_BLOB = 4095
+VT_VECTOR = 4096
+VT_ARRAY = 8192
+VT_BYREF = 16384
+VT_RESERVED = 32768
+VT_ILLEGAL = 65535
+VT_ILLEGALMASKED = 4095
+VT_TYPEMASK = 4095
+
+
+class tagCY(Structure):
+ _fields_ = [("int64", c_longlong)]
+CY = tagCY
+CURRENCY = CY
+
+
+class tagDEC(Structure):
+ _fields_ = [("wReserved", c_ushort),
+ ("scale", c_ubyte),
+ ("sign", c_ubyte),
+ ("Hi32", c_ulong),
+ ("Lo64", c_ulonglong)]
+
+ def as_decimal(self):
+ """ Convert a tagDEC struct to Decimal.
+
+ See http://msdn.microsoft.com/en-us/library/cc234586.aspx for the tagDEC
+ specification.
+
+ """
+ digits = (self.Hi32 << 64) + self.Lo64
+ decimal_str = "{0}{1}e-{2}".format(
+ '-' if self.sign else '',
+ digits,
+ self.scale,
+ )
+ return decimal.Decimal(decimal_str)
+
+
+DECIMAL = tagDEC
+
+
+# The VARIANT structure is a good candidate for implementation in a C
+# helper extension. At least the get/set methods.
+class tagVARIANT(Structure):
+ class U_VARIANT1(Union):
+ class __tagVARIANT(Structure):
+ # The C Header file defn of VARIANT is much more complicated, but
+ # this is the ctypes version - functional as well.
+ class U_VARIANT2(Union):
+ class _tagBRECORD(Structure):
+ _fields_ = [("pvRecord", c_void_p),
+ ("pRecInfo", POINTER(IUnknown))]
+ _fields_ = [
+ ("VT_BOOL", VARIANT_BOOL),
+ ("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),
+ ("VT_CY", c_longlong),
+ ("c_wchar_p", c_wchar_p),
+ ("c_void_p", c_void_p),
+ ("pparray", POINTER(POINTER(_safearray.tagSAFEARRAY))),
+
+ ("bstrVal", BSTR),
+ ("_tagBRECORD", _tagBRECORD),
+ ]
+ _anonymous_ = ["_tagBRECORD"]
+ _fields_ = [("vt", VARTYPE),
+ ("wReserved1", c_ushort),
+ ("wReserved2", c_ushort),
+ ("wReserved3", c_ushort),
+ ("_", U_VARIANT2)
+ ]
+ _fields_ = [("__VARIANT_NAME_2", __tagVARIANT),
+ ("decVal", DECIMAL)]
+ _anonymous_ = ["__VARIANT_NAME_2"]
+ _fields_ = [("__VARIANT_NAME_1", U_VARIANT1)]
+ _anonymous_ = ["__VARIANT_NAME_1"]
+
+ def __init__(self, *args):
+ if args:
+ self.value = args[0]
+
+ def __del__(self):
+ if self._b_needsfree_:
+ # XXX This does not work. _b_needsfree_ is never
+ # set because the buffer is internal to the object.
+ _VariantClear(self)
+
+ def __repr__(self):
+ if self.vt & VT_BYREF:
+ return "VARIANT(vt=0x%x, byref(%r))" % (self.vt, self[0])
+ return "VARIANT(vt=0x%x, %r)" % (self.vt, self.value)
+
+ def from_param(cls, value):
+ if isinstance(value, cls):
+ return value
+ return cls(value)
+ from_param = classmethod(from_param)
+
+ def __setitem__(self, index, value):
+ # This method allows to change the value of a
+ # (VT_BYREF|VT_xxx) variant in place.
+ if index != 0:
+ raise IndexError(index)
+ if not self.vt & VT_BYREF:
+ raise TypeError("set_byref requires a VT_BYREF VARIANT instance")
+ typ = _vartype_to_ctype[self.vt & ~VT_BYREF]
+ cast(self._.c_void_p, POINTER(typ))[0] = value
+
+ # see also c:/sf/pywin32/com/win32com/src/oleargs.cpp 54
+ def _set_value(self, value):
+ _VariantClear(self)
+ if value is None:
+ self.vt = VT_NULL
+ elif (hasattr(value, '__len__') and len(value) == 0
+ and not isinstance(value, basestring)):
+ self.vt = VT_NULL
+ # since bool is a subclass of int, this check must come before
+ # the check for int
+ elif isinstance(value, bool):
+ self.vt = VT_BOOL
+ self._.VT_BOOL = value
+ elif isinstance(value, (int, c_int)):
+ self.vt = VT_I4
+ self._.VT_I4 = value
+ elif isinstance(value, long):
+ u = self._
+ # try VT_I4 first.
+ u.VT_I4 = value
+ if u.VT_I4 == value:
+ # it did work.
+ self.vt = VT_I4
+ return
+ # try VT_UI4 next.
+ if value >= 0:
+ u.VT_UI4 = value
+ if u.VT_UI4 == value:
+ # 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)
+ 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(value, len(value))
+ elif isinstance(value, datetime.datetime):
+ delta = value - _com_null_date
+ # a day has 24 * 60 * 60 = 86400 seconds
+ com_days = delta.days + (delta.seconds + delta.microseconds * 1e-6) / 86400.
+ self.vt = VT_DATE
+ self._.VT_R8 = com_days
+ elif npsupport.isdatetime64(value):
+ com_days = value - npsupport.com_null_date64
+ com_days /= npsupport.numpy.timedelta64(1, 'D')
+ self.vt = VT_DATE
+ self._.VT_R8 = com_days
+ elif decimal is not None and isinstance(value, decimal.Decimal):
+ self._.VT_CY = int(round(value * 10000))
+ self.vt = VT_CY
+ elif isinstance(value, POINTER(IDispatch)):
+ CopyComPointer(value, byref(self._))
+ self.vt = VT_DISPATCH
+ elif isinstance(value, POINTER(IUnknown)):
+ CopyComPointer(value, byref(self._))
+ self.vt = VT_UNKNOWN
+ elif isinstance(value, (list, tuple)):
+ obj = _midlSAFEARRAY(VARIANT).create(value)
+ memmove(byref(self._), byref(obj), sizeof(obj))
+ self.vt = VT_ARRAY | obj._vartype_
+ elif isinstance(value, array.array):
+ vartype = _arraycode_to_vartype[value.typecode]
+ typ = _vartype_to_ctype[vartype]
+ obj = _midlSAFEARRAY(typ).create(value)
+ memmove(byref(self._), byref(obj), sizeof(obj))
+ self.vt = VT_ARRAY | obj._vartype_
+ elif npsupport.isndarray(value):
+ # Try to convert a simple array of basic types.
+ descr = value.dtype.descr[0][1]
+ typ = npsupport.numpy.ctypeslib._typecodes.get(descr)
+ if typ is None:
+ # Try for variant
+ obj = _midlSAFEARRAY(VARIANT).create(value)
+ else:
+ obj = _midlSAFEARRAY(typ).create(value)
+ memmove(byref(self._), byref(obj), sizeof(obj))
+ self.vt = VT_ARRAY | obj._vartype_
+ elif isinstance(value, Structure) and hasattr(value, "_recordinfo_"):
+ guids = value._recordinfo_
+ from comtypes.typeinfo import GetRecordInfoFromGuids
+ ri = GetRecordInfoFromGuids(*guids)
+ self.vt = VT_RECORD
+ # Assigning a COM pointer to a structure field does NOT
+ # call AddRef(), have to call it manually:
+ ri.AddRef()
+ self._.pRecInfo = ri
+ self._.pvRecord = ri.RecordCreateCopy(byref(value))
+ elif isinstance(getattr(value, "_comobj", None), POINTER(IDispatch)):
+ CopyComPointer(value._comobj, byref(self._))
+ self.vt = VT_DISPATCH
+ elif isinstance(value, VARIANT):
+ _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, c_int64):
+ self.vt = VT_I8
+ self._.VT_I8 = value
+ elif isinstance(value, c_uint64):
+ self.vt = VT_UI8
+ self._.VT_UI8 = 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 ?
+
+ # c:/sf/pywin32/com/win32com/src/oleargs.cpp 197
+ def _get_value(self, dynamic=False):
+ vt = self.vt
+ if vt in (VT_EMPTY, VT_NULL):
+ return None
+ elif vt == VT_I1:
+ return self._.VT_I1
+ elif vt == VT_I2:
+ 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:
+ return self._.VT_UI1
+ elif vt == VT_UI2:
+ return self._.VT_UI2
+ elif vt == VT_UI4:
+ return self._.VT_UI4
+ elif vt == VT_UINT:
+ return self._.VT_UINT
+ elif vt == VT_R4:
+ return self._.VT_R4
+ elif vt == VT_R8:
+ return self._.VT_R8
+ elif vt == VT_BOOL:
+ return self._.VT_BOOL
+ elif vt == VT_BSTR:
+ return self._.bstrVal
+ elif vt == VT_DATE:
+ days = self._.VT_R8
+ return datetime.timedelta(days=days) + _com_null_date
+ elif vt == VT_CY:
+ return self._.VT_CY / decimal.Decimal("10000")
+ elif vt == VT_UNKNOWN:
+ val = self._.c_void_p
+ if not val:
+ # We should/could return a NULL COM pointer.
+ # But the code generation must be able to construct one
+ # from the __repr__ of it.
+ return None # XXX?
+ ptr = cast(val, POINTER(IUnknown))
+ # cast doesn't call AddRef (it should, imo!)
+ ptr.AddRef()
+ return ptr.__ctypes_from_outparam__()
+ elif vt == VT_DECIMAL:
+ return self.decVal.as_decimal()
+ elif vt == VT_DISPATCH:
+ val = self._.c_void_p
+ if not val:
+ # See above.
+ return None # XXX?
+ ptr = cast(val, POINTER(IDispatch))
+ # cast doesn't call AddRef (it should, imo!)
+ ptr.AddRef()
+ if not dynamic:
+ return ptr.__ctypes_from_outparam__()
+ else:
+ from comtypes.client.dynamic import Dispatch
+ return Dispatch(ptr)
+ # see also c:/sf/pywin32/com/win32com/src/oleargs.cpp
+ elif self.vt & VT_BYREF:
+ return self
+ elif vt == VT_RECORD:
+ from comtypes.client import GetModule
+ from comtypes.typeinfo import IRecordInfo
+
+ # Retrieving a COM pointer from a structure field does NOT
+ # call AddRef(), have to call it manually:
+ punk = self._.pRecInfo
+ punk.AddRef()
+ ri = punk.QueryInterface(IRecordInfo)
+
+ # find typelib
+ tlib = ri.GetTypeInfo().GetContainingTypeLib()[0]
+
+ # load typelib wrapper module
+ mod = GetModule(tlib)
+ # retrive the type and create an instance
+ value = getattr(mod, ri.GetName())()
+ # copy data into the instance
+ ri.RecordCopy(self._.pvRecord, byref(value))
+
+ return value
+ elif self.vt & VT_ARRAY:
+ typ = _vartype_to_ctype[self.vt & ~VT_ARRAY]
+ return cast(self._.pparray, _midlSAFEARRAY(typ)).unpack()
+ else:
+ raise NotImplementedError("typecode %d = 0x%x)" % (vt, vt))
+
+ def __getitem__(self, index):
+ if index != 0:
+ raise IndexError(index)
+ if self.vt == VT_BYREF|VT_VARIANT:
+ v = VARIANT()
+ # apparently VariantCopyInd doesn't work always with
+ # VT_BYREF|VT_VARIANT, so do it manually.
+ v = cast(self._.c_void_p, POINTER(VARIANT))[0]
+ return v.value
+ else:
+ v = VARIANT()
+ _VariantCopyInd(v, self)
+ return v.value
+
+
+# these are missing:
+## getter[VT_ERROR]
+## getter[VT_ARRAY]
+## getter[VT_BYREF|VT_UI1]
+## getter[VT_BYREF|VT_I2]
+## getter[VT_BYREF|VT_I4]
+## getter[VT_BYREF|VT_R4]
+## getter[VT_BYREF|VT_R8]
+## getter[VT_BYREF|VT_BOOL]
+## getter[VT_BYREF|VT_ERROR]
+## getter[VT_BYREF|VT_CY]
+## getter[VT_BYREF|VT_DATE]
+## getter[VT_BYREF|VT_BSTR]
+## getter[VT_BYREF|VT_UNKNOWN]
+## getter[VT_BYREF|VT_DISPATCH]
+## getter[VT_BYREF|VT_ARRAY]
+## getter[VT_BYREF|VT_VARIANT]
+## getter[VT_BYREF]
+## getter[VT_BYREF|VT_DECIMAL]
+## getter[VT_BYREF|VT_I1]
+## getter[VT_BYREF|VT_UI2]
+## getter[VT_BYREF|VT_UI4]
+## getter[VT_BYREF|VT_INT]
+## getter[VT_BYREF|VT_UINT]
+
+ value = property(_get_value, _set_value)
+
+ def __ctypes_from_outparam__(self):
+ # XXX Manual resource management, because of the VARIANT bug:
+ result = self.value
+ self.value = None
+ return result
+
+ def ChangeType(self, 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()
+VARIANT.missing = v = VARIANT()
+v.vt = VT_ERROR
+v._.VT_I4 = 0x80020004L
+del v
+
+_carg_obj = type(byref(c_int()))
+from _ctypes import Array as _CArrayType
+
+@Patch(POINTER(VARIANT))
+class _(object):
+ # Override the default .from_param classmethod of POINTER(VARIANT).
+ # This allows to pass values which can be stored in VARIANTs as
+ # function parameters declared as POINTER(VARIANT). See
+ # InternetExplorer's Navigate2() method, or Word's Close() method, for
+ # examples.
+ def from_param(cls, arg):
+ # accept POINTER(VARIANT) instance
+ if isinstance(arg, POINTER(VARIANT)):
+ return arg
+ # accept byref(VARIANT) instance
+ if isinstance(arg, _carg_obj) and isinstance(arg._obj, VARIANT):
+ return arg
+ # accept VARIANT instance
+ if isinstance(arg, VARIANT):
+ return byref(arg)
+ if isinstance(arg, _CArrayType) and arg._type_ is VARIANT:
+ # accept array of VARIANTs
+ return arg
+ # anything else which can be converted to a VARIANT.
+ return byref(VARIANT(arg))
+ from_param = classmethod(from_param)
+
+ def __setitem__(self, index, value):
+ # This is to support the same sematics as a pointer instance:
+ # variant[0] = value
+ self[index].value = value
+
+################################################################
+# interfaces, structures, ...
+class IEnumVARIANT(IUnknown):
+ _iid_ = GUID('{00020404-0000-0000-C000-000000000046}')
+ _idlflags_ = ['hidden']
+ _dynamic = False
+ def __iter__(self):
+ return self
+
+ def next(self):
+ item, fetched = self.Next(1)
+ if fetched:
+ return item
+ raise StopIteration
+
+ def __getitem__(self, index):
+ self.Reset()
+ # Does not yet work.
+## if isinstance(index, slice):
+## self.Skip(index.start or 0)
+## return self.Next(index.stop or sys.maxint)
+ self.Skip(index)
+ item, fetched = self.Next(1)
+ if fetched:
+ return item
+ raise IndexError
+
+ def Next(self, celt):
+ fetched = c_ulong()
+ if celt == 1:
+ v = VARIANT()
+ self.__com_Next(celt, v, fetched)
+ return v._get_value(dynamic=self._dynamic), fetched.value
+ array = (VARIANT * celt)()
+ self.__com_Next(celt, array, fetched)
+ result = [v._get_value(dynamic=self._dynamic) for v in array[:fetched.value]]
+ for v in array:
+ v.value = None
+ return result
+
+IEnumVARIANT._methods_ = [
+ COMMETHOD([], HRESULT, 'Next',
+ ( ['in'], c_ulong, 'celt' ),
+ ( ['out'], POINTER(VARIANT), 'rgvar' ),
+ ( ['out'], POINTER(c_ulong), 'pceltFetched' )),
+ COMMETHOD([], HRESULT, 'Skip',
+ ( ['in'], c_ulong, 'celt' )),
+ COMMETHOD([], HRESULT, 'Reset'),
+ COMMETHOD([], HRESULT, 'Clone',
+ ( ['out'], POINTER(POINTER(IEnumVARIANT)), 'ppenum' )),
+]
+
+
+##from _ctypes import VARIANT_set
+##import new
+##VARIANT.value = property(VARIANT._get_value, new.instancemethod(VARIANT_set, None, VARIANT))
+
+
+class tagEXCEPINFO(Structure):
+ def __repr__(self):
+ return "" % \
+ ((self.wCode, self.bstrSource, self.bstrDescription, self.bstrHelpFile, self.dwHelpContext,
+ self.pfnDeferredFillIn, self.scode),)
+tagEXCEPINFO._fields_ = [
+ ('wCode', WORD),
+ ('wReserved', WORD),
+ ('bstrSource', BSTR),
+ ('bstrDescription', BSTR),
+ ('bstrHelpFile', BSTR),
+ ('dwHelpContext', DWORD),
+ ('pvReserved', c_void_p),
+## ('pfnDeferredFillIn', WINFUNCTYPE(HRESULT, POINTER(tagEXCEPINFO))),
+ ('pfnDeferredFillIn', c_void_p),
+ ('scode', SCODE),
+]
+EXCEPINFO = tagEXCEPINFO
+
+class tagDISPPARAMS(Structure):
+ _fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 696
+ ('rgvarg', POINTER(VARIANTARG)),
+ ('rgdispidNamedArgs', POINTER(DISPID)),
+ ('cArgs', UINT),
+ ('cNamedArgs', UINT),
+ ]
+ def __del__(self):
+ if self._b_needsfree_:
+ for i in range(self.cArgs):
+ self.rgvarg[i].value = None
+DISPPARAMS = tagDISPPARAMS
+
+DISPID_VALUE = 0
+DISPID_UNKNOWN = -1
+DISPID_PROPERTYPUT = -3
+DISPID_NEWENUM = -4
+DISPID_EVALUATE = -5
+DISPID_CONSTRUCTOR = -6
+DISPID_DESTRUCTOR = -7
+DISPID_COLLECT = -8
+
+class IDispatch(IUnknown):
+ _iid_ = GUID("{00020400-0000-0000-C000-000000000046}")
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'GetTypeInfoCount',
+ (['out'], POINTER(UINT) ) ),
+ COMMETHOD([], HRESULT, 'GetTypeInfo',
+ (['in'], UINT, 'index'),
+ (['in'], LCID, 'lcid', 0),
+## Normally, we would declare this parameter in this way:
+## (['out'], POINTER(POINTER(ITypeInfo)) ) ),
+## but we cannot import comtypes.typeinfo at the top level (recursive imports!).
+ (['out'], POINTER(POINTER(IUnknown)) ) ),
+ STDMETHOD(HRESULT, 'GetIDsOfNames', [POINTER(IID), POINTER(c_wchar_p),
+ UINT, LCID, POINTER(DISPID)]),
+ STDMETHOD(HRESULT, 'Invoke', [DISPID, POINTER(IID), LCID, WORD,
+ POINTER(DISPPARAMS), POINTER(VARIANT),
+ POINTER(EXCEPINFO), POINTER(UINT)]),
+ ]
+
+ def GetTypeInfo(self, index, lcid=0):
+ """Return type information. Index 0 specifies typeinfo for IDispatch"""
+ import comtypes.typeinfo
+ result = self._GetTypeInfo(index, lcid)
+ return result.QueryInterface(comtypes.typeinfo.ITypeInfo)
+
+ def GetIDsOfNames(self, *names, **kw):
+ """Map string names to integer ids."""
+ lcid = kw.pop("lcid", 0)
+ assert not kw
+ arr = (c_wchar_p * len(names))(*names)
+ ids = (DISPID * len(names))()
+ self.__com_GetIDsOfNames(riid_null, arr, len(names), lcid, ids)
+ return ids[:]
+
+ def _invoke(self, memid, invkind, lcid, *args):
+ var = VARIANT()
+ argerr = c_uint()
+ dp = DISPPARAMS()
+
+ if args:
+ array = (VARIANT * len(args))()
+
+ for i, a in enumerate(args[::-1]):
+ array[i].value = a
+
+ dp.cArgs = len(args)
+ if invkind in (DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF):
+ dp.cNamedArgs = 1
+ dp.rgdispidNamedArgs = pointer(DISPID(DISPID_PROPERTYPUT))
+ dp.rgvarg = array
+
+ self.__com_Invoke(memid, riid_null, lcid, invkind,
+ dp, var, None, argerr)
+ return var._get_value(dynamic=True)
+
+ def Invoke(self, dispid, *args, **kw):
+ """Invoke a method or property."""
+
+ # Memory management in Dispatch::Invoke calls:
+ # http://msdn.microsoft.com/library/en-us/automat/htm/chap5_4x2q.asp
+ # Quote:
+ # The *CALLING* code is responsible for releasing all strings and
+ # objects referred to by rgvarg[ ] or placed in *pVarResult.
+ #
+ # For comtypes this is handled in DISPPARAMS.__del__ and VARIANT.__del__.
+ _invkind = kw.pop("_invkind", 1) # DISPATCH_METHOD
+ _lcid = kw.pop("_lcid", 0)
+ if kw:
+ raise ValueError("named parameters not yet implemented")
+
+ result = VARIANT()
+ excepinfo = EXCEPINFO()
+ argerr = c_uint()
+
+ if _invkind in (DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF): # propput
+ array = (VARIANT * len(args))()
+
+ for i, a in enumerate(args[::-1]):
+ array[i].value = a
+
+ dp = DISPPARAMS()
+ dp.cArgs = len(args)
+ dp.cNamedArgs = 1
+ dp.rgvarg = array
+ dp.rgdispidNamedArgs = pointer(DISPID(DISPID_PROPERTYPUT))
+ else:
+ array = (VARIANT * len(args))()
+
+ for i, a in enumerate(args[::-1]):
+ array[i].value = a
+
+ dp = DISPPARAMS()
+ dp.cArgs = len(args)
+ dp.cNamedArgs = 0
+ dp.rgvarg = array
+
+ try:
+ self.__com_Invoke(dispid, riid_null, _lcid, _invkind, byref(dp),
+ byref(result), byref(excepinfo), byref(argerr))
+ except COMError, err:
+ (hresult, text, details) = err.args
+ if hresult == DISP_E_EXCEPTION:
+ details = (excepinfo.bstrDescription, excepinfo.bstrSource,
+ excepinfo.bstrHelpFile, excepinfo.dwHelpContext,
+ excepinfo.scode)
+ raise COMError(hresult, text, details)
+ elif hresult == DISP_E_PARAMNOTFOUND:
+ # MSDN says: You get the error DISP_E_PARAMNOTFOUND
+ # when you try to set a property and you have not
+ # initialized the cNamedArgs and rgdispidNamedArgs
+ # elements of your DISPPARAMS structure.
+ #
+ # So, this looks like a bug.
+ raise COMError(hresult, text, argerr.value)
+ elif hresult == DISP_E_TYPEMISMATCH:
+ # MSDN: One or more of the arguments could not be
+ # coerced.
+ #
+ # Hm, should we raise TypeError, or COMError?
+ raise COMError(hresult, text,
+ ("TypeError: Parameter %s" % (argerr.value + 1),
+ args))
+ raise
+ return result._get_value(dynamic=True)
+
+ # XXX Would separate methods for _METHOD, _PROPERTYGET and _PROPERTYPUT be better?
+
+
+################################################################
+# safearrays
+# XXX Only one-dimensional arrays are currently implemented
+
+# map ctypes types to VARTYPE values
+
+_arraycode_to_vartype = {
+ "d": VT_R8,
+ "f": VT_R4,
+ "l": VT_I4,
+ "i": VT_INT,
+ "h": VT_I2,
+ "b": VT_I1,
+ "I": VT_UINT,
+ "L": VT_UI4,
+ "H": VT_UI2,
+ "B": VT_UI1,
+ }
+
+_ctype_to_vartype = {
+ c_byte: VT_I1,
+ c_ubyte: VT_UI1,
+
+ c_short: VT_I2,
+ c_ushort: VT_UI2,
+
+ c_long: VT_I4,
+ c_ulong: VT_UI4,
+
+ 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,
+
+ # This is needed to import Esri ArcObjects (esriSystem.olb).
+ POINTER(BSTR): VT_BYREF|VT_BSTR,
+
+ # These are not yet implemented:
+## POINTER(IUnknown): VT_UNKNOWN,
+## POINTER(IDispatch): VT_DISPATCH,
+ }
+
+_vartype_to_ctype = {}
+for c, v in _ctype_to_vartype.iteritems():
+ _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:
+ from comtypes.safearray import _midlSAFEARRAY
+except (ImportError, AttributeError):
+ pass
diff --git a/tools/comtypes/comtypes/client/__init__.py b/tools/comtypes/comtypes/client/__init__.py
new file mode 100644
index 00000000000000..d9216ae77d75ce
--- /dev/null
+++ b/tools/comtypes/comtypes/client/__init__.py
@@ -0,0 +1,266 @@
+'''comtypes.client - High level client level COM support package.
+'''
+
+################################################################
+#
+# TODO:
+#
+# - refactor some code into modules
+#
+################################################################
+
+import sys, os
+import ctypes
+
+import comtypes
+from comtypes.hresult import *
+import comtypes.automation
+import comtypes.typeinfo
+import comtypes.client.dynamic
+
+from comtypes.client._events import GetEvents, ShowEvents, PumpEvents
+from comtypes.client._generate import GetModule
+
+import logging
+logger = logging.getLogger(__name__)
+
+__all__ = ["CreateObject", "GetActiveObject", "CoGetObject",
+ "GetEvents", "ShowEvents", "PumpEvents", "GetModule",
+ "GetClassObject"]
+
+from comtypes.client._code_cache import _find_gen_dir
+
+gen_dir = _find_gen_dir()
+import comtypes.gen
+
+### for testing
+##gen_dir = None
+
+def wrap_outparam(punk):
+ logger.debug("wrap_outparam(%s)", punk)
+ if not punk:
+ return None
+ if punk.__com_interface__ == comtypes.automation.IDispatch:
+ return GetBestInterface(punk)
+ return punk
+
+def GetBestInterface(punk):
+ """Try to QueryInterface a COM pointer to the 'most useful'
+ interface.
+
+ Get type information for the provided object, either via
+ IDispatch.GetTypeInfo(), or via IProvideClassInfo.GetClassInfo().
+ Generate a wrapper module for the typelib, and QI for the
+ interface found.
+ """
+ if not punk: # NULL COM pointer
+ return punk # or should we return None?
+ # find the typelib and the interface name
+ logger.debug("GetBestInterface(%s)", punk)
+ try:
+ 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()
+ for index in range(ta.cImplTypes):
+ if tinfo.GetImplTypeFlags(index) == 1:
+ break
+ else:
+ if ta.cImplTypes != 1:
+ # Hm, should we use dynamic now?
+ raise TypeError("No default interface found")
+ # Only one interface implemented, use that (even if
+ # not marked as default).
+ index = 0
+ href = tinfo.GetRefTypeOfImplType(index)
+ tinfo = tinfo.GetRefTypeInfo(href)
+ except comtypes.COMError:
+ logger.debug("Does NOT implement IProvideClassInfo/IProvideClassInfo2")
+ try:
+ pdisp = punk.QueryInterface(comtypes.automation.IDispatch)
+ except comtypes.COMError:
+ logger.debug("No Dispatch interface: %s", punk)
+ return punk
+ try:
+ tinfo = pdisp.GetTypeInfo(0)
+ except comtypes.COMError:
+ pdisp = comtypes.client.dynamic.Dispatch(pdisp)
+ logger.debug("IDispatch.GetTypeInfo(0) failed: %s" % pdisp)
+ return pdisp
+ typeattr = tinfo.GetTypeAttr()
+ logger.debug("Default interface is %s", typeattr.guid)
+ try:
+ punk.QueryInterface(comtypes.IUnknown, typeattr.guid)
+ except comtypes.COMError:
+ logger.debug("Does not implement default interface, returning dynamic object")
+ return comtypes.client.dynamic.Dispatch(punk)
+
+ itf_name = tinfo.GetDocumentation(-1)[0] # interface name
+ tlib = tinfo.GetContainingTypeLib()[0] # typelib
+
+ # import the wrapper, generating it on demand
+ mod = GetModule(tlib)
+ # Python interface class
+ interface = getattr(mod, itf_name)
+ logger.debug("Implements default interface from typeinfo %s", interface)
+ # QI for this interface
+ # XXX
+ # What to do if this fails?
+ # In the following example the engine.Eval() call returns
+ # such an object.
+ #
+ # engine = CreateObject("MsScriptControl.ScriptControl")
+ # engine.Language = "JScript"
+ # engine.Eval("[1, 2, 3]")
+ #
+ # Could the above code, as an optimization, check that QI works,
+ # *before* generating the wrapper module?
+ result = punk.QueryInterface(interface)
+ logger.debug("Final result is %s", result)
+ return result
+# backwards compatibility:
+wrap = GetBestInterface
+
+# Should we do this for POINTER(IUnknown) also?
+ctypes.POINTER(comtypes.automation.IDispatch).__ctypes_from_outparam__ = wrap_outparam
+
+################################################################
+#
+# Typelib constants
+#
+class Constants(object):
+ """This class loads the type library from the supplied object,
+ then exposes constants in the type library as attributes."""
+ def __init__(self, obj):
+ obj = obj.QueryInterface(comtypes.automation.IDispatch)
+ tlib, index = obj.GetTypeInfo(0).GetContainingTypeLib()
+ self.tcomp = tlib.GetTypeComp()
+
+ def __getattr__(self, name):
+ try:
+ kind, desc = self.tcomp.Bind(name)
+ except (WindowsError, comtypes.COMError):
+ raise AttributeError(name)
+ if kind != "variable":
+ raise AttributeError(name)
+ return desc._.lpvarValue[0].value
+
+ def _bind_type(self, name):
+ return self.tcomp.BindType(name)
+
+################################################################
+#
+# Object creation
+#
+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 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.__dict__['__clsid'] = str(clsid)
+ if interface is None:
+ 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 a pointer to a comtypes.COSERVERINFO instance
+ '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
+ machine=None, # where to create the object
+ interface=None, # the interface we want
+ dynamic=False, # use dynamic dispatch
+ pServerInfo=None): # server info struct for remoting
+ """Create a COM object from 'progid', and try to QueryInterface()
+ it to the most useful interface, generating typelib support on
+ demand. A pointer to this interface is returned.
+
+ 'progid' may be a string like "InternetExplorer.Application",
+ a string specifying a clsid, a GUID instance, or an object with
+ 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
+ 'pServerInfo', if used, must be a pointer to a comtypes.COSERVERINFO instance
+ This supercedes 'machine'.
+
+ You can also later request to receive events with GetEvents().
+ """
+ clsid = comtypes.GUID.from_progid(progid)
+ logger.debug("%s -> %s", progid, clsid)
+ if dynamic:
+ if interface:
+ raise ValueError("interface and dynamic are mutually exclusive")
+ interface = comtypes.automation.IDispatch
+ elif interface is None:
+ interface = getattr(progid, "_com_interfaces_", [None])[0]
+ if machine is None and pServerInfo is None:
+ logger.debug("CoCreateInstance(%s, clsctx=%s, interface=%s)",
+ clsid, clsctx, interface)
+ obj = comtypes.CoCreateInstance(clsid, clsctx=clsctx, interface=interface)
+ else:
+ logger.debug("CoCreateInstanceEx(%s, clsctx=%s, interface=%s, machine=%s,\
+ pServerInfo=%s)",
+ clsid, clsctx, interface, machine, pServerInfo)
+ if machine is not None and pServerInfo is not None:
+ msg = "You can notset both the machine name and server info."
+ raise ValueError(msg)
+ obj = comtypes.CoCreateInstanceEx(clsid, clsctx=clsctx,
+ interface=interface, machine=machine, pServerInfo=pServerInfo)
+ if dynamic:
+ return comtypes.client.dynamic.Dispatch(obj)
+ return _manage(obj, clsid, interface=interface)
+
+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)
diff --git a/tools/comtypes/comtypes/client/_code_cache.py b/tools/comtypes/comtypes/client/_code_cache.py
new file mode 100644
index 00000000000000..0275381924ab2a
--- /dev/null
+++ b/tools/comtypes/comtypes/client/_code_cache.py
@@ -0,0 +1,130 @@
+"""comtypes.client._code_cache helper module.
+
+The main function is _find_gen_dir(), which on-demand creates the
+comtypes.gen package and returns a directory where generated code can
+be written to.
+"""
+import ctypes, logging, os, sys, tempfile, types
+logger = logging.getLogger(__name__)
+
+def _find_gen_dir():
+ """Create, if needed, and return a directory where automatically
+ generated modules will be created.
+
+ Usually, this is the directory 'Lib/site-packages/comtypes/gen'.
+
+ If the above directory cannot be created, or if it is not a
+ directory in the file system (when comtypes is imported from a
+ zip-archive or a zipped egg), or if the current user cannot create
+ files in this directory, an additional directory is created and
+ appended to comtypes.gen.__path__ .
+
+ For a Python script using comtypes, the additional directory is
+ '%APPDATA%\\Python\Python25\comtypes_cache'.
+
+ For an executable frozen with py2exe, the additional directory is
+ '%TEMP%\comtypes_cache\-25'.
+ """
+ _create_comtypes_gen_package()
+ from comtypes import gen
+ if not _is_writeable(gen.__path__):
+ # check type of executable image to determine a subdirectory
+ # where generated modules are placed.
+ ftype = getattr(sys, "frozen", None)
+ version_str = "%d%d" % sys.version_info[:2]
+ if ftype == None:
+ # Python script
+ subdir = r"Python\Python%s\comtypes_cache" % version_str
+ basedir = _get_appdata_dir()
+
+ elif ftype == "dll":
+ # dll created with py2exe
+ path = _get_module_filename(sys.frozendllhandle)
+ base = os.path.splitext(os.path.basename(path))[0]
+ subdir = r"comtypes_cache\%s-%s" % (base, version_str)
+ basedir = tempfile.gettempdir()
+
+ else: # ftype in ('windows_exe', 'console_exe')
+ # exe created by py2exe
+ base = os.path.splitext(os.path.basename(sys.executable))[0]
+ subdir = r"comtypes_cache\%s-%s" % (base, version_str)
+ basedir = tempfile.gettempdir()
+
+ gen_dir = os.path.join(basedir, subdir)
+ if not os.path.exists(gen_dir):
+ logger.info("Creating writeable comtypes cache directory: '%s'", gen_dir)
+ os.makedirs(gen_dir)
+ gen.__path__.append(gen_dir)
+ result = os.path.abspath(gen.__path__[-1])
+ logger.info("Using writeable comtypes cache directory: '%s'", result)
+ return result
+
+################################################################
+
+if os.name == "ce":
+ SHGetSpecialFolderPath = ctypes.OleDLL("coredll").SHGetSpecialFolderPath
+ GetModuleFileName = ctypes.WinDLL("coredll").GetModuleFileNameW
+else:
+ SHGetSpecialFolderPath = ctypes.OleDLL("shell32.dll").SHGetSpecialFolderPathW
+ GetModuleFileName = ctypes.WinDLL("kernel32.dll").GetModuleFileNameW
+SHGetSpecialFolderPath.argtypes = [ctypes.c_ulong, ctypes.c_wchar_p,
+ ctypes.c_int, ctypes.c_int]
+GetModuleFileName.restype = ctypes.c_ulong
+GetModuleFileName.argtypes = [ctypes.c_ulong, ctypes.c_wchar_p, ctypes.c_ulong]
+
+CSIDL_APPDATA = 26
+MAX_PATH = 260
+
+def _create_comtypes_gen_package():
+ """Import (creating it if needed) the comtypes.gen package."""
+ try:
+ import comtypes.gen
+ logger.info("Imported existing %s", comtypes.gen)
+ except ImportError:
+ import comtypes
+ logger.info("Could not import comtypes.gen, trying to create it.")
+ try:
+ comtypes_path = os.path.abspath(os.path.join(comtypes.__path__[0], "gen"))
+ if not os.path.isdir(comtypes_path):
+ os.mkdir(comtypes_path)
+ logger.info("Created comtypes.gen directory: '%s'", comtypes_path)
+ comtypes_init = os.path.join(comtypes_path, "__init__.py")
+ if not os.path.exists(comtypes_init):
+ logger.info("Writing __init__.py file: '%s'", comtypes_init)
+ ofi = open(comtypes_init, "w")
+ ofi.write("# comtypes.gen package, directory for generated files.\n")
+ ofi.close()
+ except (OSError, IOError), details:
+ logger.info("Creating comtypes.gen package failed: %s", details)
+ module = sys.modules["comtypes.gen"] = types.ModuleType("comtypes.gen")
+ comtypes.gen = module
+ comtypes.gen.__path__ = []
+ logger.info("Created a memory-only package.")
+
+def _is_writeable(path):
+ """Check if the first part, if any, on path is a directory in
+ which we can create files."""
+ if not path:
+ return False
+ try:
+ tempfile.TemporaryFile(dir=path[0])
+ except (OSError, IOError), details:
+ logger.debug("Path is unwriteable: %s", details)
+ return False
+ return True
+
+def _get_module_filename(hmodule):
+ """Call the Windows GetModuleFileName function which determines
+ the path from a module handle."""
+ path = ctypes.create_unicode_buffer(MAX_PATH)
+ if GetModuleFileName(hmodule, path, MAX_PATH):
+ return path.value
+ raise ctypes.WinError()
+
+def _get_appdata_dir():
+ """Return the 'file system directory that serves as a common
+ repository for application-specific data' - CSIDL_APPDATA"""
+ path = ctypes.create_unicode_buffer(MAX_PATH)
+ # get u'C:\\Documents and Settings\\\\Application Data'
+ SHGetSpecialFolderPath(0, path, CSIDL_APPDATA, True)
+ return path.value
diff --git a/tools/comtypes/comtypes/client/_events.py b/tools/comtypes/comtypes/client/_events.py
new file mode 100644
index 00000000000000..12264dc9a4dddd
--- /dev/null
+++ b/tools/comtypes/comtypes/client/_events.py
@@ -0,0 +1,285 @@
+import ctypes
+import traceback
+import comtypes
+import comtypes.hresult
+import comtypes.automation
+import comtypes.typeinfo
+import comtypes.connectionpoints
+from comtypes.client._generate import GetModule
+import logging
+logger = logging.getLogger(__name__)
+
+class _AdviseConnection(object):
+ def __init__(self, source, interface, receiver):
+ self.cp = None
+ self.cookie = None
+ self.receiver = None
+ self._connect(source, interface, receiver)
+
+ def _connect(self, source, interface, receiver):
+ cpc = source.QueryInterface(comtypes.connectionpoints.IConnectionPointContainer)
+ self.cp = cpc.FindConnectionPoint(ctypes.byref(interface._iid_))
+ logger.debug("Start advise %s", interface)
+ self.cookie = self.cp.Advise(receiver)
+ self.receiver = receiver
+
+ def disconnect(self):
+ if self.cookie:
+ self.cp.Unadvise(self.cookie)
+ logger.debug("Unadvised %s", self.cp)
+ self.cp = None
+ self.cookie = None
+ del self.receiver
+
+ def __del__(self):
+ try:
+ if self.cookie is not None:
+ self.cp.Unadvise(self.cookie)
+ except (comtypes.COMError, WindowsError):
+ # Are we sure we want to ignore errors here?
+ pass
+
+def FindOutgoingInterface(source):
+ """XXX Describe the strategy that is used..."""
+ # If the COM object implements IProvideClassInfo2, it is easy to
+ # find the default outgoing interface.
+ try:
+ pci = source.QueryInterface(comtypes.typeinfo.IProvideClassInfo2)
+ guid = pci.GetGUID(1)
+ except comtypes.COMError:
+ pass
+ else:
+ # another try: block needed?
+ try:
+ interface = comtypes.com_interface_registry[str(guid)]
+ except KeyError:
+ tinfo = pci.GetClassInfo()
+ tlib, index = tinfo.GetContainingTypeLib()
+ GetModule(tlib)
+ interface = comtypes.com_interface_registry[str(guid)]
+ logger.debug("%s using sinkinterface %s", source, interface)
+ return interface
+
+ # If we can find the CLSID of the COM object, we can look for a
+ # registered outgoing interface (__clsid has been set by
+ # comtypes.client):
+ clsid = source.__dict__.get('__clsid')
+ try:
+ interface = comtypes.com_coclass_registry[clsid]._outgoing_interfaces_[0]
+ except KeyError:
+ pass
+ else:
+ logger.debug("%s using sinkinterface from clsid %s", source, interface)
+ return interface
+
+## interface = find_single_connection_interface(source)
+## if interface:
+## return interface
+
+ raise TypeError("cannot determine source interface")
+
+def find_single_connection_interface(source):
+ # Enumerate the connection interfaces. If we find a single one,
+ # return it, if there are more, we give up since we cannot
+ # determine which one to use.
+ cpc = source.QueryInterface(comtypes.connectionpoints.IConnectionPointContainer)
+ enum = cpc.EnumConnectionPoints()
+ iid = enum.next().GetConnectionInterface()
+ try:
+ enum.next()
+ except StopIteration:
+ try:
+ interface = comtypes.com_interface_registry[str(iid)]
+ except KeyError:
+ return None
+ else:
+ logger.debug("%s using sinkinterface from iid %s", source, interface)
+ return interface
+ else:
+ logger.debug("%s has more than one connection point", source)
+
+ 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:
+ try:
+ return getattr(self.sink, fq_name)
+ except AttributeError:
+ return getattr(self.sink, mthname)
+
+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, handler)
+
+ 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
+ the 'sink' object. 'interface' is the source interface to use.
+ """
+ # When called from CreateObject, the sourceinterface has already
+ # been determined by the coclass. Otherwise, the only thing that
+ # makes sense is to use IProvideClassInfo2 to get the default
+ # source interface.
+ if interface is None:
+ interface = FindOutgoingInterface(source)
+
+ rcv = CreateEventReceiver(interface, sink)
+ return _AdviseConnection(source, interface, rcv)
+
+class EventDumper(object):
+ """Universal sink for COM events."""
+
+ def __getattr__(self, name):
+ "Create event handler methods on demand"
+ if name.startswith("__") and name.endswith("__"):
+ raise AttributeError(name)
+ print "# event found:", name
+ def handler(self, this, *args, **kw):
+ # 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, self, EventDumper)
+
+def ShowEvents(source, interface=None):
+ """Receive COM events from 'source'. A special event sink will be
+ used that first prints the names of events that are found in the
+ outgoing interface, and will also print out the events when they
+ are fired.
+ """
+ return comtypes.client.GetEvents(source, sink=EventDumper(), interface=interface)
+
+# This type is used inside 'PumpEvents', but if we create the type
+# afresh each time 'PumpEvents' is called we end up creating cyclic
+# garbage for each call. So we define it here instead.
+_handles_type = ctypes.c_void_p * 1
+
+def PumpEvents(timeout):
+ """This following code waits for 'timeout' seconds in the way
+ required for COM, internally doing the correct things depending
+ on the COM appartment of the current thread. It is possible to
+ terminate the message loop by pressing CTRL+C, which will raise
+ a KeyboardInterrupt.
+ """
+ # 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 = _handles_type(hevt)
+ RPC_S_CALLPENDING = -2147417835
+
+## @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)
+
+ try:
+ try:
+ res = ctypes.oledll.ole32.CoWaitForMultipleHandles(0,
+ int(timeout * 1000),
+ len(handles), handles,
+ ctypes.byref(ctypes.c_ulong()))
+ except WindowsError, details:
+ if details.args[0] != RPC_S_CALLPENDING: # timeout expired
+ raise
+ else:
+ raise KeyboardInterrupt
+ finally:
+ ctypes.windll.kernel32.CloseHandle(hevt)
+ ctypes.windll.kernel32.SetConsoleCtrlHandler(HandlerRoutine, 0)
diff --git a/tools/comtypes/comtypes/client/_generate.py b/tools/comtypes/comtypes/client/_generate.py
new file mode 100644
index 00000000000000..f4f5fcfda25017
--- /dev/null
+++ b/tools/comtypes/comtypes/client/_generate.py
@@ -0,0 +1,195 @@
+import types
+import os
+import sys
+import comtypes.client
+import comtypes.tools.codegenerator
+
+import logging
+logger = logging.getLogger(__name__)
+
+__verbose__ = __debug__
+
+if os.name == "ce":
+ # Windows CE has a hard coded PATH
+ # XXX Additionally there's an OEM path, plus registry settings.
+ # We don't currently use the latter.
+ PATH = ["\\Windows", "\\"]
+else:
+ PATH = os.environ["PATH"].split(os.pathsep)
+
+def _my_import(fullname):
+ # helper function to import dotted modules
+ import comtypes.gen
+ if comtypes.client.gen_dir \
+ and comtypes.client.gen_dir not in comtypes.gen.__path__:
+ comtypes.gen.__path__.append(comtypes.client.gen_dir)
+ return __import__(fullname, globals(), locals(), ['DUMMY'])
+
+def _name_module(tlib):
+ # Determine the name of a typelib wrapper module.
+ libattr = tlib.GetLibAttr()
+ modname = "_%s_%s_%s_%s" % \
+ (str(libattr.guid)[1:-1].replace("-", "_"),
+ libattr.lcid,
+ libattr.wMajorVerNum,
+ libattr.wMinorVerNum)
+ return "comtypes.gen." + modname
+
+def GetModule(tlib):
+ """Create a module wrapping a COM typelibrary on demand.
+
+ 'tlib' must be an ITypeLib COM pointer instance, the pathname of a
+ type library, or a tuple/list specifying the arguments to a
+ comtypes.typeinfo.LoadRegTypeLib call:
+
+ (libid, wMajorVerNum, wMinorVerNum, lcid=0)
+
+ Or it can be an object with _reg_libid_ and _reg_version_
+ attributes.
+
+ A relative pathname is interpreted as relative to the callers
+ __file__, if this exists.
+
+ This function determines the module name from the typelib
+ attributes, then tries to import it. If that fails because the
+ module doesn't exist, the module is generated into the
+ comtypes.gen package.
+
+ It is possible to delete the whole comtypes\gen directory to
+ remove all generated modules, the directory and the __init__.py
+ file in it will be recreated when needed.
+
+ If comtypes.gen __path__ is not a directory (in a frozen
+ executable it lives in a zip archive), generated modules are only
+ created in memory without writing them to the file system.
+
+ Example:
+
+ GetModule("shdocvw.dll")
+
+ would create modules named
+
+ comtypes.gen._EAB22AC0_30C1_11CF_A7EB_0000C05BAE0B_0_1_1
+ comtypes.gen.SHDocVw
+
+ containing the Python wrapper code for the type library used by
+ Internet Explorer. The former module contains all the code, the
+ latter is a short stub loading the former.
+ """
+ pathname = None
+ if isinstance(tlib, basestring):
+ # pathname of type library
+ if not os.path.isabs(tlib):
+ # If a relative pathname is used, we try to interpret
+ # this pathname as relative to the callers __file__.
+ frame = sys._getframe(1)
+ _file_ = frame.f_globals.get("__file__", None)
+ if _file_ is not None:
+ directory = os.path.dirname(os.path.abspath(_file_))
+ abspath = os.path.normpath(os.path.join(directory, tlib))
+ # If the file does exist, we use it. Otherwise it may
+ # still be that the file is on Windows search path for
+ # typelibs, and we leave the pathname alone.
+ if os.path.isfile(abspath):
+ tlib = abspath
+ logger.debug("GetModule(%s)", tlib)
+ pathname = tlib
+ tlib = comtypes.typeinfo.LoadTypeLibEx(tlib)
+ elif isinstance(tlib, (tuple, list)):
+ # sequence containing libid and version numbers
+ logger.debug("GetModule(%s)", (tlib,))
+ tlib = comtypes.typeinfo.LoadRegTypeLib(comtypes.GUID(tlib[0]), *tlib[1:])
+ elif hasattr(tlib, "_reg_libid_"):
+ # a COMObject implementation
+ logger.debug("GetModule(%s)", tlib)
+ tlib = comtypes.typeinfo.LoadRegTypeLib(comtypes.GUID(tlib._reg_libid_),
+ *tlib._reg_version_)
+ else:
+ # an ITypeLib pointer
+ logger.debug("GetModule(%s)", tlib.GetLibAttr())
+
+ # create and import the module
+ mod = _CreateWrapper(tlib, pathname)
+ try:
+ modulename = tlib.GetDocumentation(-1)[0]
+ except comtypes.COMError:
+ return mod
+ if modulename is None:
+ return mod
+ if sys.version_info < (3, 0):
+ modulename = modulename.encode("mbcs")
+
+ # create and import the friendly-named module
+ try:
+ mod = _my_import("comtypes.gen." + modulename)
+ except Exception, details:
+ logger.info("Could not import comtypes.gen.%s: %s", modulename, details)
+ else:
+ return mod
+ # the module is always regenerated if the import fails
+ if __verbose__:
+ print "# Generating comtypes.gen.%s" % modulename
+ # determine the Python module name
+ fullname = _name_module(tlib)
+ modname = fullname.split(".")[-1]
+ 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 = types.ModuleType("comtypes.gen." + modulename)
+ mod.__file__ = os.path.join(os.path.abspath(comtypes.gen.__path__[0]),
+ "")
+ exec code in mod.__dict__
+ sys.modules["comtypes.gen." + modulename] = mod
+ setattr(comtypes.gen, modulename, mod)
+ return mod
+ # create in file system, and import it
+ ofi = open(os.path.join(comtypes.client.gen_dir, modulename + ".py"), "w")
+ ofi.write(code)
+ ofi.close()
+ return _my_import("comtypes.gen." + modulename)
+
+def _CreateWrapper(tlib, pathname=None):
+ # helper which creates and imports the real typelib wrapper module.
+ fullname = _name_module(tlib)
+ try:
+ return sys.modules[fullname]
+ except KeyError:
+ pass
+
+ modname = fullname.split(".")[-1]
+
+ try:
+ return _my_import(fullname)
+ except Exception, details:
+ logger.info("Could not import %s: %s", fullname, details)
+
+ # generate the module since it doesn't exist or is out of date
+ from comtypes.tools.tlbparser import generate_module
+ if comtypes.client.gen_dir is None:
+ import cStringIO
+ ofi = cStringIO.StringIO()
+ else:
+ ofi = open(os.path.join(comtypes.client.gen_dir, modname + ".py"), "w")
+ # XXX use logging!
+ if __verbose__:
+ print "# Generating comtypes.gen.%s" % modname
+ generate_module(tlib, ofi, pathname)
+
+ if comtypes.client.gen_dir is None:
+ code = ofi.getvalue()
+ mod = types.ModuleType(fullname)
+ mod.__file__ = os.path.join(os.path.abspath(comtypes.gen.__path__[0]),
+ "")
+ exec code in mod.__dict__
+ sys.modules[fullname] = mod
+ setattr(comtypes.gen, modname, mod)
+ else:
+ ofi.close()
+ mod = _my_import(fullname)
+ return mod
+
+################################################################
+
+if __name__ == "__main__":
+ # When started as script, generate typelib wrapper from .tlb file.
+ GetModule(sys.argv[1])
diff --git a/tools/comtypes/comtypes/client/dynamic.py b/tools/comtypes/comtypes/client/dynamic.py
new file mode 100644
index 00000000000000..4ca216f49e2e63
--- /dev/null
+++ b/tools/comtypes/comtypes/client/dynamic.py
@@ -0,0 +1,165 @@
+import ctypes
+import comtypes.automation
+import comtypes.typeinfo
+import comtypes.client
+import comtypes.client.lazybind
+
+from comtypes import COMError, IUnknown, _is_object
+import comtypes.hresult as hres
+
+# These errors generally mean the property or method exists,
+# but can't be used in this context - eg, property instead of a method, etc.
+# Used to determine if we have a real error or not.
+ERRORS_BAD_CONTEXT = [
+ hres.DISP_E_MEMBERNOTFOUND,
+ hres.DISP_E_BADPARAMCOUNT,
+ hres.DISP_E_PARAMNOTOPTIONAL,
+ hres.DISP_E_TYPEMISMATCH,
+ hres.E_INVALIDARG,
+]
+
+def Dispatch(obj):
+ # Wrap an object in a Dispatch instance, exposing methods and properties
+ # via fully dynamic dispatch
+ if isinstance(obj, _Dispatch):
+ return obj
+ if isinstance(obj, ctypes.POINTER(comtypes.automation.IDispatch)):
+ try:
+ tinfo = obj.GetTypeInfo(0)
+ except (comtypes.COMError, WindowsError):
+ return _Dispatch(obj)
+ return comtypes.client.lazybind.Dispatch(obj, tinfo)
+ return obj
+
+class MethodCaller:
+ # Wrong name: does not only call methods but also handle
+ # property accesses.
+ def __init__(self, _id, _obj):
+ self._id = _id
+ self._obj = _obj
+
+ def __call__(self, *args):
+ return self._obj._comobj.Invoke(self._id, *args)
+
+ def __getitem__(self, *args):
+ return self._obj._comobj.Invoke(self._id, *args,
+ **dict(_invkind=comtypes.automation.DISPATCH_PROPERTYGET))
+
+ def __setitem__(self, *args):
+ if _is_object(args[-1]):
+ self._obj._comobj.Invoke(self._id, *args,
+ **dict(_invkind=comtypes.automation.DISPATCH_PROPERTYPUTREF))
+ else:
+ self._obj._comobj.Invoke(self._id, *args,
+ **dict(_invkind=comtypes.automation.DISPATCH_PROPERTYPUT))
+
+class _Dispatch(object):
+ # Expose methods and properties via fully dynamic dispatch
+ 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 e.QueryInterface(comtypes.automation.IEnumVARIANT)
+
+ def __cmp__(self, other):
+ if not isinstance(other, _Dispatch):
+ 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:
+ if 0 != enum.Skip(index):
+ raise IndexError("index out of range")
+ item, fetched = enum.Next(1)
+ if not fetched:
+ raise IndexError("index out of range")
+ return item
+
+ def QueryInterface(self, *args):
+ "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)
+ if not dispid:
+ 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)
+ except COMError, err:
+ (hresult, text, details) = err.args
+ if hresult in ERRORS_BAD_CONTEXT:
+ result = MethodCaller(dispid, self)
+ self.__dict__[name] = result
+ 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
+
+ def __setattr__(self, name, value):
+ dispid = self._ids.get(name)
+ if not dispid:
+ dispid = self._comobj.GetIDsOfNames(name)[0]
+ self._ids[name] = dispid
+ # Detect whether to use DISPATCH_PROPERTYPUT or
+ # DISPATCH_PROPERTYPUTREF
+ flags = 8 if _is_object(value) else 4
+ return self._comobj.Invoke(dispid, value, _invkind=flags)
+
+ def __iter__(self):
+ return _Collection(self.__enum())
+
+## def __setitem__(self, index, value):
+## self._comobj.Invoke(-3, index, value,
+## _invkind=comtypes.automation.DISPATCH_PROPERTYPUT|comtypes.automation.DISPATCH_PROPERTYPUTREF)
+
+class _Collection(object):
+ def __init__(self, enum):
+ self.enum = enum
+
+ def next(self):
+ item, fetched = self.enum.Next(1)
+ if fetched:
+ return item
+ raise StopIteration
+
+ def __iter__(self):
+ return self
+
+__all__ = ["Dispatch"]
diff --git a/tools/comtypes/comtypes/client/lazybind.py b/tools/comtypes/comtypes/client/lazybind.py
new file mode 100644
index 00000000000000..4705e4a74a8275
--- /dev/null
+++ b/tools/comtypes/comtypes/client/lazybind.py
@@ -0,0 +1,267 @@
+import comtypes
+import comtypes.automation
+
+from comtypes.automation import IEnumVARIANT
+from comtypes.automation import DISPATCH_METHOD
+from comtypes.automation import DISPATCH_PROPERTYGET
+from comtypes.automation import DISPATCH_PROPERTYPUT
+from comtypes.automation import DISPATCH_PROPERTYPUTREF
+
+from comtypes.automation import DISPID_VALUE
+from comtypes.automation import DISPID_NEWENUM
+
+from comtypes.typeinfo import FUNC_PUREVIRTUAL, FUNC_DISPATCH
+
+
+class FuncDesc(object):
+ """Stores important FUNCDESC properties by copying them from a
+ real FUNCDESC instance.
+ """
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+# What is missing?
+#
+# Should NamedProperty support __call__()?
+
+_all_slice = slice(None, None, None)
+
+
+class NamedProperty(object):
+ def __init__(self, disp, get, put, putref):
+ self.get = get
+ self.put = put
+ self.putref = putref
+ self.disp = disp
+
+ def __getitem__(self, arg):
+ if self.get is None:
+ raise TypeError("unsubscriptable object")
+ if isinstance(arg, tuple):
+ return self.disp._comobj._invoke(self.get.memid,
+ self.get.invkind,
+ 0,
+ *arg)
+ elif arg == _all_slice:
+ return self.disp._comobj._invoke(self.get.memid,
+ self.get.invkind,
+ 0)
+ return self.disp._comobj._invoke(self.get.memid,
+ self.get.invkind,
+ 0,
+ *[arg])
+
+ def __call__(self, *args):
+ if self.get is None:
+ raise TypeError("object is not callable")
+ return self.disp._comobj._invoke(self.get.memid,
+ self.get.invkind,
+ 0,
+ *args)
+
+ def __setitem__(self, name, value):
+ # See discussion in Dispatch.__setattr__ below.
+ if self.put is None and self.putref is None:
+ raise TypeError("object does not support item assignment")
+ if comtypes._is_object(value):
+ descr = self.putref or self.put
+ else:
+ descr = self.put or self.putref
+ if isinstance(name, tuple):
+ self.disp._comobj._invoke(descr.memid,
+ descr.invkind,
+ 0,
+ *(name + (value,)))
+ elif name == _all_slice:
+ self.disp._comobj._invoke(descr.memid,
+ descr.invkind,
+ 0,
+ value)
+ else:
+ self.disp._comobj._invoke(descr.memid,
+ descr.invkind,
+ 0,
+ name,
+ value)
+
+ def __iter__(self):
+ """ Explicitly disallow iteration. """
+ msg = "%r is not iterable" % self.disp
+ raise TypeError(msg)
+
+
+# The following 'Dispatch' class, returned from
+# CreateObject(progid, dynamic=True)
+# differ in behaviour from objects created with
+# CreateObject(progid, dynamic=False)
+# (let us call the latter 'Custom' objects for this discussion):
+#
+#
+# 1. Dispatch objects support __call__(), custom objects do not
+#
+# 2. Custom objects method support named arguments, Dispatch
+# objects do not (could be added, would probably be expensive)
+
+class Dispatch(object):
+ """Dynamic dispatch for an object the exposes type information.
+ Binding at runtime is done via ITypeComp::Bind calls.
+ """
+ def __init__(self, comobj, tinfo):
+ self.__dict__["_comobj"] = comobj
+ self.__dict__["_tinfo"] = tinfo
+ self.__dict__["_tcomp"] = tinfo.GetTypeComp()
+ self.__dict__["_tdesc"] = {}
+## self.__dict__["_iid"] = tinfo.GetTypeAttr().guid
+
+ def __bind(self, name, invkind):
+ """Bind (name, invkind) and return a FuncDesc instance or
+ None. Results (even unsuccessful ones) are cached."""
+ # We could cache the info in the class instead of the
+ # instance, but we would need an additional key for that:
+ # self._iid
+ try:
+ return self._tdesc[(name, invkind)]
+ except KeyError:
+ try:
+ descr = self._tcomp.Bind(name, invkind)[1]
+ except comtypes.COMError:
+ info = None
+ else:
+ # Using a separate instance to store interesting
+ # attributes of descr avoids that the typecomp instance is
+ # kept alive...
+ info = FuncDesc(memid=descr.memid,
+ invkind=descr.invkind,
+ cParams=descr.cParams,
+ funckind=descr.funckind)
+ self._tdesc[(name, invkind)] = info
+ return info
+
+ def QueryInterface(self, *args):
+ "QueryInterface is forwarded to the real com object."
+ return self._comobj.QueryInterface(*args)
+
+ def __cmp__(self, other):
+ if not isinstance(other, Dispatch):
+ return 1
+ return cmp(self._comobj, other._comobj)
+
+ def __eq__(self, other):
+ 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("__"):
+ raise AttributeError(name)
+ # check for propget or method
+ descr = self.__bind(name, DISPATCH_METHOD | DISPATCH_PROPERTYGET)
+ if descr is None:
+ raise AttributeError(name)
+ if descr.invkind == DISPATCH_PROPERTYGET:
+ # DISPATCH_PROPERTYGET
+ if descr.funckind == FUNC_DISPATCH:
+ if descr.cParams == 0:
+ return self._comobj._invoke(descr.memid, descr.invkind, 0)
+ elif descr.funckind == FUNC_PUREVIRTUAL:
+ # FUNC_PUREVIRTUAL descriptions contain the property
+ # itself as a parameter.
+ if descr.cParams == 1:
+ return self._comobj._invoke(descr.memid, descr.invkind, 0)
+ else:
+ raise RuntimeError("funckind %d not yet implemented" % descr.funckind)
+ put = self.__bind(name, DISPATCH_PROPERTYPUT)
+ putref = self.__bind(name, DISPATCH_PROPERTYPUTREF)
+ return NamedProperty(self, descr, put, putref)
+ else:
+ # DISPATCH_METHOD
+ def caller(*args):
+ return self._comobj._invoke(descr.memid, descr.invkind, 0, *args)
+ try:
+ caller.__name__ = name
+ except TypeError:
+ # In Python 2.3, __name__ is readonly
+ pass
+ return caller
+
+ def __setattr__(self, name, value):
+ # Hm, this can be a propput, a propputref, or 'both' property.
+ # (Or nothing at all.)
+ #
+ # Whether propput or propputref is called will depend on what
+ # is available, and on the type of 'value' as determined by
+ # comtypes._is_object(value).
+ #
+ # I think that the following table MAY be correct; although I
+ # have no idea whether the cases marked (?) are really valid.
+ #
+ # invkind available | _is_object(value) | invkind we should use
+ # ---------------------------------------------------------------
+ # put | True | put (?)
+ # put | False | put
+ # putref | True | putref
+ # putref | False | putref (?)
+ # put, putref | True | putref
+ # put, putref | False | put
+ put = self.__bind(name, DISPATCH_PROPERTYPUT)
+ putref = self.__bind(name, DISPATCH_PROPERTYPUTREF)
+ if not put and not putref:
+ raise AttributeError(name)
+ if comtypes._is_object(value):
+ descr = putref or put
+ else:
+ descr = put or putref
+ if descr.cParams == 1:
+ self._comobj._invoke(descr.memid, descr.invkind, 0, value)
+ return
+ raise AttributeError(name)
+
+ def __call__(self, *args):
+ return self._comobj._invoke(DISPID_VALUE,
+ DISPATCH_METHOD | DISPATCH_PROPERTYGET,
+ 0,
+ *args)
+
+ def __getitem__(self, arg):
+ if isinstance(arg, tuple):
+ args = arg
+ elif arg == _all_slice:
+ args = ()
+ else:
+ args = (arg,)
+
+ try:
+ return self._comobj._invoke(DISPID_VALUE,
+ DISPATCH_METHOD | DISPATCH_PROPERTYGET,
+ 0,
+ *args)
+ except comtypes.COMError:
+ return iter(self)[arg]
+
+ def __setitem__(self, name, value):
+ if comtypes._is_object(value):
+ invkind = DISPATCH_PROPERTYPUTREF
+ else:
+ invkind = DISPATCH_PROPERTYPUT
+
+ if isinstance(name, tuple):
+ args = name + (value,)
+ elif name == _all_slice:
+ args = (value,)
+ else:
+ args = (name, value)
+ return self._comobj._invoke(DISPID_VALUE,
+ invkind,
+ 0,
+ *args)
+
+ def __iter__(self):
+ punk = self._comobj._invoke(DISPID_NEWENUM,
+ DISPATCH_METHOD | DISPATCH_PROPERTYGET,
+ 0)
+ enum = punk.QueryInterface(IEnumVARIANT)
+ enum._dynamic = True
+ return enum
diff --git a/tools/comtypes/comtypes/connectionpoints.py b/tools/comtypes/comtypes/connectionpoints.py
new file mode 100644
index 00000000000000..bed8d0daa342ed
--- /dev/null
+++ b/tools/comtypes/comtypes/connectionpoints.py
@@ -0,0 +1,94 @@
+from ctypes import *
+from comtypes import IUnknown, COMMETHOD, GUID, HRESULT, dispid
+_GUID = GUID
+
+class tagCONNECTDATA(Structure):
+ _fields_ = [
+ ('pUnk', POINTER(IUnknown)),
+ ('dwCookie', c_ulong),
+ ]
+CONNECTDATA = tagCONNECTDATA
+
+################################################################
+
+class IConnectionPointContainer(IUnknown):
+ _iid_ = GUID('{B196B284-BAB4-101A-B69C-00AA00341D07}')
+ _idlflags_ = []
+
+class IConnectionPoint(IUnknown):
+ _iid_ = GUID('{B196B286-BAB4-101A-B69C-00AA00341D07}')
+ _idlflags_ = []
+
+class IEnumConnections(IUnknown):
+ _iid_ = GUID('{B196B287-BAB4-101A-B69C-00AA00341D07}')
+ _idlflags_ = []
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ cp, fetched = self.Next(1)
+ if fetched == 0:
+ raise StopIteration
+ return cp
+
+class IEnumConnectionPoints(IUnknown):
+ _iid_ = GUID('{B196B285-BAB4-101A-B69C-00AA00341D07}')
+ _idlflags_ = []
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ cp, fetched = self.Next(1)
+ if fetched == 0:
+ raise StopIteration
+ return cp
+
+################################################################
+
+IConnectionPointContainer._methods_ = [
+ COMMETHOD([], HRESULT, 'EnumConnectionPoints',
+ ( ['out'], POINTER(POINTER(IEnumConnectionPoints)), 'ppEnum' )),
+ COMMETHOD([], HRESULT, 'FindConnectionPoint',
+ ( ['in'], POINTER(_GUID), 'riid' ),
+ ( ['out'], POINTER(POINTER(IConnectionPoint)), 'ppCP' )),
+]
+
+IConnectionPoint._methods_ = [
+ COMMETHOD([], HRESULT, 'GetConnectionInterface',
+ ( ['out'], POINTER(_GUID), 'pIID' )),
+ COMMETHOD([], HRESULT, 'GetConnectionPointContainer',
+ ( ['out'], POINTER(POINTER(IConnectionPointContainer)), 'ppCPC' )),
+ COMMETHOD([], HRESULT, 'Advise',
+ ( ['in'], POINTER(IUnknown), 'pUnkSink' ),
+ ( ['out'], POINTER(c_ulong), 'pdwCookie' )),
+ COMMETHOD([], HRESULT, 'Unadvise',
+ ( ['in'], c_ulong, 'dwCookie' )),
+ COMMETHOD([], HRESULT, 'EnumConnections',
+ ( ['out'], POINTER(POINTER(IEnumConnections)), 'ppEnum' )),
+]
+
+IEnumConnections._methods_ = [
+ COMMETHOD([], HRESULT, 'Next',
+ ( ['in'], c_ulong, 'cConnections' ),
+ ( ['out'], POINTER(tagCONNECTDATA), 'rgcd' ),
+ ( ['out'], POINTER(c_ulong), 'pcFetched' )),
+ COMMETHOD([], HRESULT, 'Skip',
+ ( ['in'], c_ulong, 'cConnections' )),
+ COMMETHOD([], HRESULT, 'Reset'),
+ COMMETHOD([], HRESULT, 'Clone',
+ ( ['out'], POINTER(POINTER(IEnumConnections)), 'ppEnum' )),
+]
+
+IEnumConnectionPoints._methods_ = [
+ COMMETHOD([], HRESULT, 'Next',
+ ( ['in'], c_ulong, 'cConnections' ),
+ ( ['out'], POINTER(POINTER(IConnectionPoint)), 'ppCP' ),
+ ( ['out'], POINTER(c_ulong), 'pcFetched' )),
+ COMMETHOD([], HRESULT, 'Skip',
+ ( ['in'], c_ulong, 'cConnections' )),
+ COMMETHOD([], HRESULT, 'Reset'),
+ COMMETHOD([], HRESULT, 'Clone',
+ ( ['out'], POINTER(POINTER(IEnumConnectionPoints)), 'ppEnum' )),
+]
diff --git a/tools/comtypes/comtypes/errorinfo.py b/tools/comtypes/comtypes/errorinfo.py
new file mode 100644
index 00000000000000..992cb4de5995ff
--- /dev/null
+++ b/tools/comtypes/comtypes/errorinfo.py
@@ -0,0 +1,105 @@
+import sys
+from ctypes import *
+from comtypes import IUnknown, HRESULT, COMMETHOD, GUID, BSTR
+from comtypes.hresult import *
+
+LPCOLESTR = c_wchar_p
+DWORD = c_ulong
+
+class ICreateErrorInfo(IUnknown):
+ _iid_ = GUID("{22F03340-547D-101B-8E65-08002B2BD119}")
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'SetGUID',
+ (['in'], POINTER(GUID), "rguid")),
+ COMMETHOD([], HRESULT, 'SetSource',
+ (['in'], LPCOLESTR, "szSource")),
+ COMMETHOD([], HRESULT, 'SetDescription',
+ (['in'], LPCOLESTR, "szDescription")),
+ COMMETHOD([], HRESULT, 'SetHelpFile',
+ (['in'], LPCOLESTR, "szHelpFile")),
+ COMMETHOD([], HRESULT, 'SetHelpContext',
+ (['in'], DWORD, "dwHelpContext"))
+ ]
+
+class IErrorInfo(IUnknown):
+ _iid_ = GUID("{1CF2B120-547D-101B-8E65-08002B2BD119}")
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'GetGUID',
+ (['out'], POINTER(GUID), "pGUID")),
+ COMMETHOD([], HRESULT, 'GetSource',
+ (['out'], POINTER(BSTR), "pBstrSource")),
+ COMMETHOD([], HRESULT, 'GetDescription',
+ (['out'], POINTER(BSTR), "pBstrDescription")),
+ COMMETHOD([], HRESULT, 'GetHelpFile',
+ (['out'], POINTER(BSTR), "pBstrHelpFile")),
+ COMMETHOD([], HRESULT, 'GetHelpContext',
+ (['out'], POINTER(DWORD), "pdwHelpContext")),
+ ]
+
+class ISupportErrorInfo(IUnknown):
+ _iid_ = GUID("{DF0B3D60-548F-101B-8E65-08002B2BD119}")
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'InterfaceSupportsErrorInfo',
+ (['in'], POINTER(GUID), 'riid'))
+ ]
+
+################################################################
+_oleaut32 = oledll.oleaut32
+
+def CreateErrorInfo():
+ cei = POINTER(ICreateErrorInfo)()
+ _oleaut32.CreateErrorInfo(byref(cei))
+ return cei
+
+def GetErrorInfo():
+ """Get the error information for the current thread."""
+ errinfo = POINTER(IErrorInfo)()
+ if S_OK == _oleaut32.GetErrorInfo(0, byref(errinfo)):
+ return errinfo
+ return None
+
+def SetErrorInfo(errinfo):
+ """Set error information for the current thread."""
+ return _oleaut32.SetErrorInfo(0, errinfo)
+
+def ReportError(text, iid,
+ clsid=None, helpfile=None, helpcontext=0, hresult=DISP_E_EXCEPTION):
+ """Report a COM error. Returns the passed in hresult value."""
+ ei = CreateErrorInfo()
+ ei.SetDescription(text)
+ ei.SetGUID(iid)
+ if helpfile is not None:
+ ei.SetHelpFile(helpfile)
+ if helpcontext is not None:
+ ei.SetHelpContext(helpcontext)
+ if clsid is not None:
+ if isinstance(clsid, basestring):
+ clsid = GUID(clsid)
+ try:
+ progid = clsid.as_progid()
+ except WindowsError:
+ pass
+ else:
+ ei.SetSource(progid) # progid for the class or application that created the error
+ _oleaut32.SetErrorInfo(0, ei)
+ return hresult
+
+def ReportException(hresult, iid, clsid=None, helpfile=None, helpcontext=None,
+ stacklevel=None):
+ """Report a COM exception. Returns the passed in hresult value."""
+ 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)
+
+__all__ = ["ICreateErrorInfo", "IErrorInfo", "ISupportErrorInfo",
+ "ReportError", "ReportException",
+ "SetErrorInfo", "GetErrorInfo", "CreateErrorInfo"]
diff --git a/tools/comtypes/comtypes/gen/__init__.py b/tools/comtypes/comtypes/gen/__init__.py
new file mode 100644
index 00000000000000..40bf6c8d50d2be
--- /dev/null
+++ b/tools/comtypes/comtypes/gen/__init__.py
@@ -0,0 +1 @@
+# comtypes.gen package, directory for generated files.
diff --git a/tools/comtypes/comtypes/git.py b/tools/comtypes/comtypes/git.py
new file mode 100644
index 00000000000000..ef9f60ab2ec7d0
--- /dev/null
+++ b/tools/comtypes/comtypes/git.py
@@ -0,0 +1,65 @@
+"""comtypes.git - access the process wide global interface table
+
+The global interface table provides a way to marshal interface pointers
+between different threading appartments.
+"""
+from ctypes import *
+from comtypes import IUnknown, STDMETHOD, COMMETHOD, \
+ GUID, HRESULT, CoCreateInstance, CLSCTX_INPROC_SERVER
+
+DWORD = c_ulong
+
+class IGlobalInterfaceTable(IUnknown):
+ _iid_ = GUID("{00000146-0000-0000-C000-000000000046}")
+ _methods_ = [
+ STDMETHOD(HRESULT, "RegisterInterfaceInGlobal",
+ [POINTER(IUnknown), POINTER(GUID), POINTER(DWORD)]),
+ STDMETHOD(HRESULT, "RevokeInterfaceFromGlobal", [DWORD]),
+ STDMETHOD(HRESULT, "GetInterfaceFromGlobal",
+ [DWORD, POINTER(GUID), POINTER(POINTER(IUnknown))]),
+ ]
+
+ def RegisterInterfaceInGlobal(self, obj, interface=IUnknown):
+ cookie = DWORD()
+ self.__com_RegisterInterfaceInGlobal(obj, interface._iid_, cookie)
+ return cookie.value
+
+ def GetInterfaceFromGlobal(self, cookie, interface=IUnknown):
+ ptr = POINTER(interface)()
+ self.__com_GetInterfaceFromGlobal(cookie, interface._iid_, ptr)
+ return ptr
+
+ def RevokeInterfaceFromGlobal(self, cookie):
+ self.__com_RevokeInterfaceFromGlobal(cookie)
+
+
+# It was a pain to get this CLSID: it's neither in the registry, nor
+# in any header files. I had to compile a C program, and find it out
+# with the debugger. Apparently it is in uuid.lib.
+CLSID_StdGlobalInterfaceTable = GUID("{00000323-0000-0000-C000-000000000046}")
+
+git = CoCreateInstance(CLSID_StdGlobalInterfaceTable,
+ interface=IGlobalInterfaceTable,
+ clsctx=CLSCTX_INPROC_SERVER)
+
+RevokeInterfaceFromGlobal = git.RevokeInterfaceFromGlobal
+RegisterInterfaceInGlobal = git.RegisterInterfaceInGlobal
+GetInterfaceFromGlobal = git.GetInterfaceFromGlobal
+
+__all__ = ["RegisterInterfaceInGlobal", "RevokeInterfaceFromGlobal", "GetInterfaceFromGlobal"]
+
+if __name__ == "__main__":
+ from comtypes.typeinfo import CreateTypeLib, ICreateTypeLib
+
+ tlib = CreateTypeLib("foo.bar") # we don not save it later
+ assert (tlib.AddRef(), tlib.Release()) == (2, 1)
+
+ cookie = RegisterInterfaceInGlobal(tlib)
+ assert (tlib.AddRef(), tlib.Release()) == (3, 2)
+
+ GetInterfaceFromGlobal(cookie, ICreateTypeLib)
+ GetInterfaceFromGlobal(cookie, ICreateTypeLib)
+ GetInterfaceFromGlobal(cookie)
+ assert (tlib.AddRef(), tlib.Release()) == (3, 2)
+ RevokeInterfaceFromGlobal(cookie)
+ assert (tlib.AddRef(), tlib.Release()) == (2, 1)
diff --git a/tools/comtypes/comtypes/hresult.py b/tools/comtypes/comtypes/hresult.py
new file mode 100644
index 00000000000000..85f75ceb137651
--- /dev/null
+++ b/tools/comtypes/comtypes/hresult.py
@@ -0,0 +1,76 @@
+# comtypes.hresult
+# COM success and error codes
+#
+# Note that the codes should be written in decimal notation!
+
+S_OK = 0
+S_FALSE = 1
+
+E_UNEXPECTED = -2147418113 #0x8000FFFFL
+
+E_NOTIMPL = -2147467263 #0x80004001L
+E_NOINTERFACE = -2147467262 #0x80004002L
+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
+
+CO_E_CLASSSTRING = -2147221005 #0x800401F3L
+
+# connection point error codes
+CONNECT_E_CANNOTCONNECT = -2147220990
+CONNECT_E_ADVISELIMIT = -2147220991
+CONNECT_E_NOCONNECTION = -2147220992
+
+TYPE_E_ELEMENTNOTFOUND = -2147352077 #0x8002802BL
+
+TYPE_E_REGISTRYACCESS = -2147319780 #0x8002801CL
+TYPE_E_CANTLOADLIBRARY = -2147312566 #0x80029C4AL
+
+# all the DISP_E_ values from windows.h
+DISP_E_BUFFERTOOSMALL = -2147352557
+DISP_E_DIVBYZERO = -2147352558
+DISP_E_NOTACOLLECTION = -2147352559
+DISP_E_BADCALLEE = -2147352560
+DISP_E_PARAMNOTOPTIONAL = -2147352561 #0x8002000F
+DISP_E_BADPARAMCOUNT = -2147352562 #0x8002000E
+DISP_E_ARRAYISLOCKED = -2147352563 #0x8002000D
+DISP_E_UNKNOWNLCID = -2147352564 #0x8002000C
+DISP_E_BADINDEX = -2147352565 #0x8002000B
+DISP_E_OVERFLOW = -2147352566 #0x8002000A
+DISP_E_EXCEPTION = -2147352567 #0x80020009
+DISP_E_BADVARTYPE = -2147352568 #0x80020008
+DISP_E_NONAMEDARGS = -2147352569 #0x80020007
+DISP_E_UNKNOWNNAME = -2147352570 #0x80020006
+DISP_E_TYPEMISMATCH = -2147352571 #0800020005
+DISP_E_PARAMNOTFOUND = -2147352572 #0x80020004
+DISP_E_MEMBERNOTFOUND = -2147352573 #0x80020003
+DISP_E_UNKNOWNINTERFACE = -2147352575 #0x80020001
+
+RPC_E_CHANGED_MODE = -2147417850 # 0x80010106
+RPC_E_SERVERFAULT = -2147417851 # 0x80010105
+
+# 'macros' and constants to create your own HRESULT values:
+
+def MAKE_HRESULT(sev, fac, code):
+ # A hresult is SIGNED in comtypes
+ from ctypes import c_long
+ return c_long((sev << 31 | fac << 16 | code)).value
+
+SEVERITY_ERROR = 1
+SEVERITY_SUCCESS = 0
+
+FACILITY_ITF = 4
+FACILITY_WIN32 = 7
+
+def HRESULT_FROM_WIN32(x):
+ # make signed
+ from ctypes import c_long
+ x = c_long(x).value
+ if x < 0:
+ return x
+ # 0x80000000 | FACILITY_WIN32 << 16 | x & 0xFFFF
+ return c_long(0x80070000 | (x & 0xFFFF)).value
diff --git a/tools/comtypes/comtypes/logutil.py b/tools/comtypes/comtypes/logutil.py
new file mode 100644
index 00000000000000..3cb799aafcd565
--- /dev/null
+++ b/tools/comtypes/comtypes/logutil.py
@@ -0,0 +1,51 @@
+# logutil.py
+import logging, ctypes
+
+class NTDebugHandler(logging.Handler):
+ def emit(self, record,
+ writeA=ctypes.windll.kernel32.OutputDebugStringA,
+ writeW=ctypes.windll.kernel32.OutputDebugStringW):
+ text = self.format(record)
+ if isinstance(text, str):
+ writeA(text + "\n")
+ else:
+ writeW(text + u"\n")
+logging.NTDebugHandler = NTDebugHandler
+
+def setup_logging(*pathnames):
+ import ConfigParser
+
+ parser = ConfigParser.ConfigParser()
+ parser.optionxform = str # use case sensitive option names!
+
+ parser.read(pathnames)
+
+ DEFAULTS = {"handler": "StreamHandler()",
+ "format": "%(levelname)s:%(name)s:%(message)s",
+ "level": "WARNING"}
+
+ def get(section, option):
+ try:
+ return parser.get(section, option, True)
+ except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+ return DEFAULTS[option]
+
+ levelname = get("logging", "level")
+ format = get("logging", "format")
+ handlerclass = get("logging", "handler")
+
+ # convert level name to level value
+ level = getattr(logging, levelname)
+ # create the handler instance
+ handler = eval(handlerclass, vars(logging))
+ formatter = logging.Formatter(format)
+ handler.setFormatter(formatter)
+ logging.root.addHandler(handler)
+ logging.root.setLevel(level)
+
+ try:
+ for name, value in parser.items("logging.levels", True):
+ value = getattr(logging, value)
+ logging.getLogger(name).setLevel(value)
+ except ConfigParser.NoSectionError:
+ pass
diff --git a/tools/comtypes/comtypes/messageloop.py b/tools/comtypes/comtypes/messageloop.py
new file mode 100644
index 00000000000000..a0d58a28a76eba
--- /dev/null
+++ b/tools/comtypes/comtypes/messageloop.py
@@ -0,0 +1,50 @@
+import ctypes
+from ctypes import WinDLL, byref, WinError
+from ctypes.wintypes import MSG
+_user32 = WinDLL("user32")
+
+GetMessage = _user32.GetMessageA
+GetMessage.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_void_p,
+ ctypes.c_uint,
+ ctypes.c_uint,
+]
+TranslateMessage = _user32.TranslateMessage
+DispatchMessage = _user32.DispatchMessageA
+
+
+class _MessageLoop(object):
+
+ def __init__(self):
+ self._filters = []
+
+ def insert_filter(self, obj, index=-1):
+ self._filters.insert(index, obj)
+
+ def remove_filter(self, obj):
+ self._filters.remove(obj)
+
+ def run(self):
+ msg = MSG()
+ lpmsg = byref(msg)
+ while 1:
+ ret = GetMessage(lpmsg, 0, 0, 0)
+ if ret == -1:
+ raise WinError()
+ elif ret == 0:
+ return # got WM_QUIT
+ if not self.filter_message(lpmsg):
+ TranslateMessage(lpmsg)
+ DispatchMessage(lpmsg)
+
+ def filter_message(self, lpmsg):
+ return any(filter(lpmsg) for filter in self._filters)
+
+_messageloop = _MessageLoop()
+
+run = _messageloop.run
+insert_filter = _messageloop.insert_filter
+remove_filter = _messageloop.remove_filter
+
+__all__ = ["run", "insert_filter", "remove_filter"]
diff --git a/tools/comtypes/comtypes/npsupport.py b/tools/comtypes/comtypes/npsupport.py
new file mode 100644
index 00000000000000..3a362fb9e75208
--- /dev/null
+++ b/tools/comtypes/comtypes/npsupport.py
@@ -0,0 +1,104 @@
+""" Consolidation of numpy support utilities. """
+import sys
+
+try:
+ import numpy
+except ImportError:
+ numpy = None
+
+
+HAVE_NUMPY = numpy is not None
+
+is_64bits = sys.maxsize > 2**32
+
+
+def _make_variant_dtype():
+ """ Create a dtype for VARIANT. This requires support for Unions, which is
+ available in numpy version 1.7 or greater.
+
+ This does not support the decimal type.
+
+ Returns None if the dtype cannot be created.
+
+ """
+
+ # pointer typecode
+ ptr_typecode = '>> class MyClass:
+ ... def __init__(self, param):
+ ... self.param = param
+ ... def bar(self):
+ ... print("orig bar")
+
+ To add attributes to MyClass, you can use Patch:
+
+ >>> @Patch(MyClass)
+ ... class JustANamespace:
+ ... def print_param(self):
+ ... print(self.param)
+ >>> ob = MyClass('foo')
+ >>> ob.print_param()
+ foo
+
+ The namespace is assigned None, so there's no mistaking the purpose
+ >>> JustANamespace
+
+ The patcher will replace the existing methods:
+
+ >>> @Patch(MyClass)
+ ... class SomeNamespace:
+ ... def bar(self):
+ ... print("replaced bar")
+ >>> ob = MyClass('foo')
+ >>> ob.bar()
+ replaced bar
+
+ But it will not replace methods if no_replace is indicated.
+
+ >>> @Patch(MyClass)
+ ... class AnotherNamespace:
+ ... @no_replace
+ ... def bar(self):
+ ... print("candy bar")
+ >>> ob = MyClass('foo')
+ >>> ob.bar()
+ replaced bar
+
+ """
+
+ def __init__(self, target):
+ self.target = target
+
+ def __call__(self, patches):
+ for name, value in vars(patches).items():
+ if name in vars(ReferenceEmptyClass):
+ continue
+ no_replace = getattr(value, '__no_replace', False)
+ if no_replace and hasattr(self.target, name):
+ continue
+ setattr(self.target, name, value)
+
+def no_replace(f):
+ """
+ Method decorator to indicate that a method definition shall
+ silently be ignored if it already exists in the target class.
+ """
+ f.__no_replace = True
+ return f
+
+class ReferenceEmptyClass(object):
+ """
+ This empty class will serve as a reference for attributes present on
+ any class.
+ """
diff --git a/tools/comtypes/comtypes/persist.py b/tools/comtypes/comtypes/persist.py
new file mode 100644
index 00000000000000..77fae34728e13c
--- /dev/null
+++ b/tools/comtypes/comtypes/persist.py
@@ -0,0 +1,212 @@
+"""This module defines the following interfaces:
+
+ IErrorLog
+ IPropertyBag
+ IPersistPropertyBag
+ IPropertyBag2
+ IPersistPropertyBag2
+
+The 'DictPropertyBag' class is a class implementing the IPropertyBag
+interface, useful in client code.
+"""
+from ctypes import *
+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
+
+# XXX Replace by canonical solution!!!
+WSTRING = c_wchar_p
+
+class IErrorLog(IUnknown):
+ _iid_ = GUID('{3127CA40-446E-11CE-8135-00AA004BB851}')
+ _idlflags_ = []
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'AddError',
+ ( ['in'], WSTRING, 'pszPropName' ),
+ ( ['in'], POINTER(tagEXCEPINFO), 'pExcepInfo' )),
+ ]
+
+class IPropertyBag(IUnknown):
+ _iid_ = GUID('{55272A00-42CB-11CE-8135-00AA004BB851}')
+ _idlflags_ = []
+ _methods_ = [
+ # XXX Note: According to MSDN, pVar and pErrorLog are ['in', 'out'] parameters.
+ #
+ # XXX ctypes does NOT yet accept POINTER(IErrorLog) as 'out' parameter:
+ # TypeError: 'out' parameter 3 must be a pointer type, not POINTER(IErrorLog)
+ COMMETHOD([], HRESULT, 'Read',
+ ( ['in'], WSTRING, 'pszPropName' ),
+ ( ['in', 'out'], POINTER(VARIANT), 'pVar' ),
+ ( ['in'], POINTER(IErrorLog), 'pErrorLog' )),
+## ( ['in', 'out'], POINTER(IErrorLog), 'pErrorLog' )),
+ COMMETHOD([], HRESULT, 'Write',
+ ( ['in'], WSTRING, 'pszPropName' ),
+ ( ['in'], POINTER(VARIANT), 'pVar' )),
+ ]
+
+class IPersistPropertyBag(IPersist):
+ _iid_ = GUID('{37D84F60-42CB-11CE-8135-00AA004BB851}')
+ _idlflags_ = []
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'InitNew'),
+ COMMETHOD([], HRESULT, 'Load',
+ ( ['in'], POINTER(IPropertyBag), 'pPropBag' ),
+ ( ['in'], POINTER(IErrorLog), 'pErrorLog' )),
+ COMMETHOD([], HRESULT, 'Save',
+ ( ['in'], POINTER(IPropertyBag), 'pPropBag' ),
+ ( ['in'], c_int, 'fClearDirty' ),
+ ( ['in'], c_int, 'fSaveAllProperties' )),
+ ]
+
+
+CLIPFORMAT = WORD
+
+PROPBAG2_TYPE_UNDEFINED = 0
+PROPBAG2_TYPE_DATA = 1
+PROPBAG2_TYPE_URL = 2
+PROPBAG2_TYPE_OBJECT = 3
+PROPBAG2_TYPE_STREAM = 4
+PROPBAG2_TYPE_STORAGE = 5
+PROPBAG2_TYPE_MONIKER = 6
+
+class tagPROPBAG2(Structure):
+ _fields_ = [
+ ('dwType', c_ulong),
+ ('vt', c_ushort),
+ ('cfType', CLIPFORMAT),
+ ('dwHint', c_ulong),
+ ('pstrName', WSTRING),
+ ('clsid', GUID),
+ ]
+
+class IPropertyBag2(IUnknown):
+ _iid_ = GUID('{22F55882-280B-11D0-A8A9-00A0C90C2004}')
+ _idlflags_ = []
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'Read',
+ ( ['in'], c_ulong, 'cProperties' ),
+ ( ['in'], POINTER(tagPROPBAG2), 'pPropBag' ),
+ ( ['in'], POINTER(IErrorLog), 'pErrLog' ),
+ ( ['out'], POINTER(VARIANT), 'pvarValue' ),
+ ( ['out'], POINTER(HRESULT), 'phrError' )),
+ COMMETHOD([], HRESULT, 'Write',
+ ( ['in'], c_ulong, 'cProperties' ),
+ ( ['in'], POINTER(tagPROPBAG2), 'pPropBag' ),
+ ( ['in'], POINTER(VARIANT), 'pvarValue' )),
+ COMMETHOD([], HRESULT, 'CountProperties',
+ ( ['out'], POINTER(c_ulong), 'pcProperties' )),
+ COMMETHOD([], HRESULT, 'GetPropertyInfo',
+ ( ['in'], c_ulong, 'iProperty' ),
+ ( ['in'], c_ulong, 'cProperties' ),
+ ( ['out'], POINTER(tagPROPBAG2), 'pPropBag' ),
+ ( ['out'], POINTER(c_ulong), 'pcProperties' )),
+ COMMETHOD([], HRESULT, 'LoadObject',
+ ( ['in'], WSTRING, 'pstrName' ),
+ ( ['in'], c_ulong, 'dwHint' ),
+ ( ['in'], POINTER(IUnknown), 'punkObject' ),
+ ( ['in'], POINTER(IErrorLog), 'pErrLog' )),
+ ]
+
+class IPersistPropertyBag2(IPersist):
+ _iid_ = GUID('{22F55881-280B-11D0-A8A9-00A0C90C2004}')
+ _idlflags_ = []
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'InitNew'),
+ COMMETHOD([], HRESULT, 'Load',
+ ( ['in'], POINTER(IPropertyBag2), 'pPropBag' ),
+ ( ['in'], POINTER(IErrorLog), 'pErrLog' )),
+ COMMETHOD([], HRESULT, 'Save',
+ ( ['in'], POINTER(IPropertyBag2), 'pPropBag' ),
+ ( ['in'], c_int, 'fClearDirty' ),
+ ( ['in'], c_int, 'fSaveAllProperties' )),
+ COMMETHOD([], HRESULT, 'IsDirty'),
+ ]
+
+
+# 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):
+ """An object implementing the IProperty interface on a dictionary.
+
+ Pass named values in the constructor for the client to Read(), or
+ retrieve from the .values instance variable after the client has
+ called Load().
+ """
+ _com_interfaces_ = [IPropertyBag]
+
+ def __init__(self, **kw):
+ super(DictPropertyBag, self).__init__()
+ self.values = kw
+
+ def Read(self, this, name, pVar, errorlog):
+ try:
+ val = self.values[name]
+ except KeyError:
+ return E_INVALIDARG
+ # The caller did provide info about the type that is expected
+ # with the pVar[0].vt typecode, except when this is VT_EMPTY.
+ var = pVar[0]
+ typecode = var.vt
+ var.value = val
+ if typecode:
+ var.ChangeType(typecode)
+ return S_OK
+
+ def Write(self, this, name, var):
+ val = var[0].value
+ self.values[name] = val
+ return S_OK
diff --git a/tools/comtypes/comtypes/safearray.py b/tools/comtypes/comtypes/safearray.py
new file mode 100644
index 00000000000000..bef101377a9e9d
--- /dev/null
+++ b/tools/comtypes/comtypes/safearray.py
@@ -0,0 +1,397 @@
+import threading
+import array
+from ctypes import (POINTER, Structure, byref, cast, c_long, memmove, pointer,
+ sizeof)
+from comtypes import _safearray, IUnknown, com_interface_registry, npsupport
+from comtypes.patcher import Patch
+
+numpy = npsupport.numpy
+_safearray_type_cache = {}
+
+
+class _SafeArrayAsNdArrayContextManager(object):
+ '''Context manager allowing safe arrays to be extracted as ndarrays.
+
+ This is thread-safe.
+
+ Example
+ -------
+
+ This works in python >= 2.5
+ >>> with safearray_as_ndarray:
+ >>> my_arr = com_object.AsSafeArray
+ >>> type(my_arr)
+ numpy.ndarray
+
+ '''
+ thread_local = threading.local()
+
+ def __enter__(self):
+ try:
+ self.thread_local.count += 1
+ except AttributeError:
+ self.thread_local.count = 1
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.thread_local.count -= 1
+
+ def __nonzero__(self):
+ '''True if context manager is currently entered on given thread.
+
+ '''
+ return bool(getattr(self.thread_local, 'count', 0))
+
+
+# Global _SafeArrayAsNdArrayContextManager
+safearray_as_ndarray = _SafeArrayAsNdArrayContextManager()
+
+
+################################################################
+# This is THE PUBLIC function: the gateway to the SAFEARRAY functionality.
+def _midlSAFEARRAY(itemtype):
+ """This function mimics the 'SAFEARRAY(aType)' IDL idiom. It
+ returns a subtype of SAFEARRAY, instances will be built with a
+ typecode VT_... corresponding to the aType, which must be one of
+ the supported ctypes.
+ """
+ try:
+ return POINTER(_safearray_type_cache[itemtype])
+ except KeyError:
+ sa_type = _make_safearray_type(itemtype)
+ _safearray_type_cache[itemtype] = sa_type
+ return POINTER(sa_type)
+
+
+def _make_safearray_type(itemtype):
+ # Create and return a subclass of tagSAFEARRAY
+ from comtypes.automation import _ctype_to_vartype, VT_RECORD, \
+ VT_UNKNOWN, IDispatch, VT_DISPATCH
+
+ meta = type(_safearray.tagSAFEARRAY)
+ sa_type = meta.__new__(meta,
+ "SAFEARRAY_%s" % itemtype.__name__,
+ (_safearray.tagSAFEARRAY,), {})
+
+ try:
+ vartype = _ctype_to_vartype[itemtype]
+ extra = None
+ except KeyError:
+ if issubclass(itemtype, Structure):
+ try:
+ guids = itemtype._recordinfo_
+ except AttributeError:
+ extra = None
+ else:
+ from comtypes.typeinfo import GetRecordInfoFromGuids
+ extra = GetRecordInfoFromGuids(*guids)
+ vartype = VT_RECORD
+ elif issubclass(itemtype, POINTER(IDispatch)):
+ vartype = VT_DISPATCH
+ extra = pointer(itemtype._iid_)
+ elif issubclass(itemtype, POINTER(IUnknown)):
+ vartype = VT_UNKNOWN
+ extra = pointer(itemtype._iid_)
+ else:
+ raise TypeError(itemtype)
+
+ @Patch(POINTER(sa_type))
+ class _(object):
+ # Should explain the ideas how SAFEARRAY is used in comtypes
+ _itemtype_ = itemtype # a ctypes type
+ _vartype_ = vartype # a VARTYPE value: VT_...
+ _needsfree = False
+
+ @classmethod
+ def create(cls, value, extra=None):
+ """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 npsupport.isndarray(value):
+ return cls.create_from_ndarray(value, extra)
+
+ # 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.
+
+ # XXX How to specify the lbound (3. parameter to CreateVectorEx)?
+ # XXX How to write tests for lbound != 0?
+ pa = _safearray.SafeArrayCreateVectorEx(cls._vartype_,
+ 0,
+ len(value),
+ extra)
+ 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_)() # container for the values
+ _safearray.SafeArrayAccessData(pa, byref(ptr))
+ try:
+ 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
+
+ @classmethod
+ def create_from_ndarray(cls, value, extra, lBound=0):
+ from comtypes.automation import VARIANT
+ # If processing VARIANT, makes sure the array type is correct.
+ if cls._itemtype_ is VARIANT:
+ if value.dtype != npsupport.VARIANT_dtype:
+ value = _ndarray_to_variant_array(value)
+ else:
+ 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")
+
+ # SAFEARRAYs have Fortran order; convert the numpy array if needed
+ if not value.flags.f_contiguous:
+ value = numpy.array(value, order="F")
+
+ # 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
+
+ @classmethod
+ def from_param(cls, value):
+ if not isinstance(value, cls):
+ value = cls.create(value, extra)
+ value._needsfree = True
+ return value
+
+ def __getitem__(self, index):
+ # pparray[0] returns the whole array contents.
+ if index != 0:
+ raise IndexError("Only index 0 allowed")
+ return self.unpack()
+
+ def __setitem__(self, index, value):
+ # XXX Need this to implement [in, out] safearrays in COM servers!
+## print "__setitem__", index, value
+ raise TypeError("Setting items not allowed")
+
+ def __ctypes_from_outparam__(self):
+ self._needsfree = True
+ return self[0]
+
+ def __del__(self, _SafeArrayDestroy=_safearray.SafeArrayDestroy):
+ if self._needsfree:
+ _SafeArrayDestroy(self)
+
+ def _get_size(self, dim):
+ "Return the number of elements for dimension 'dim'"
+ ub = _safearray.SafeArrayGetUBound(self, dim) + 1
+ lb = _safearray.SafeArrayGetLBound(self, dim)
+ return ub - lb
+
+ def unpack(self):
+ """Unpack a POINTER(SAFEARRAY_...) into a Python tuple or ndarray."""
+ dim = _safearray.SafeArrayGetDim(self)
+
+ if dim == 1:
+ num_elements = self._get_size(1)
+ result = self._get_elements_raw(num_elements)
+ if safearray_as_ndarray:
+ import numpy
+ return numpy.asarray(result)
+ return tuple(result)
+ elif dim == 2:
+ # get the number of elements in each dimension
+ rows, cols = self._get_size(1), self._get_size(2)
+ # get all elements
+ result = self._get_elements_raw(rows * cols)
+ # this must be reshaped and transposed because it is
+ # flat, and in VB order
+ if safearray_as_ndarray:
+ import numpy
+ return numpy.asarray(result).reshape((cols, rows)).T
+ result = [tuple(result[r::rows]) for r in range(rows)]
+ return tuple(result)
+ else:
+ lowerbounds = [_safearray.SafeArrayGetLBound(self, d)
+ for d in range(1, dim+1)]
+ indexes = (c_long * dim)(*lowerbounds)
+ upperbounds = [_safearray.SafeArrayGetUBound(self, d)
+ for d in range(1, dim+1)]
+ row = self._get_row(0, indexes, lowerbounds, upperbounds)
+ if safearray_as_ndarray:
+ import numpy
+ return numpy.asarray(row)
+ return row
+
+ def _get_elements_raw(self, num_elements):
+ """Returns a flat list or ndarray containing ALL elements in
+ the safearray."""
+ from comtypes.automation import VARIANT
+ # XXX Not sure this is true:
+ # For VT_UNKNOWN and VT_DISPATCH, we should retrieve the
+ # interface iid by SafeArrayGetIID().
+ ptr = POINTER(self._itemtype_)() # container for the values
+ _safearray.SafeArrayAccessData(self, byref(ptr))
+ try:
+ if self._itemtype_ == VARIANT:
+ # We have to loop over each item, so we get no
+ # speedup by creating an ndarray here.
+ return [i.value for i in ptr[:num_elements]]
+ elif issubclass(self._itemtype_, POINTER(IUnknown)):
+ iid = _safearray.SafeArrayGetIID(self)
+ itf = com_interface_registry[str(iid)]
+ # COM interface pointers retrieved from array
+ # must be AddRef()'d if non-NULL.
+ elems = ptr[:num_elements]
+ result = []
+ # We have to loop over each item, so we get no
+ # speedup by creating an ndarray here.
+ for p in elems:
+ if bool(p):
+ p.AddRef()
+ result.append(p.QueryInterface(itf))
+ else:
+ # return a NULL-interface pointer.
+ result.append(POINTER(itf)())
+ return result
+ else:
+ # If the safearray element are NOT native python
+ # objects, the containing safearray must be kept
+ # alive until all the elements are destroyed.
+ if not issubclass(self._itemtype_, Structure):
+ # Create an ndarray if requested. This is where
+ # we can get the most speed-up.
+ # XXX Only try to convert types known to
+ # numpy.ctypeslib.
+ if (safearray_as_ndarray and self._itemtype_ in
+ numpy.ctypeslib._typecodes.values()):
+ arr = numpy.ctypeslib.as_array(ptr,
+ (num_elements,))
+ return arr.copy()
+ return ptr[:num_elements]
+
+ def keep_safearray(v):
+ v.__keepref = self
+ return v
+ return [keep_safearray(x) for x in ptr[:num_elements]]
+ finally:
+ _safearray.SafeArrayUnaccessData(self)
+
+ def _get_row(self, dim, indices, lowerbounds, upperbounds):
+ # loop over the index of dimension 'dim'
+ # we have to restore the index of the dimension we're looping over
+ restore = indices[dim]
+
+ result = []
+ obj = self._itemtype_()
+ pobj = byref(obj)
+ if dim+1 == len(indices):
+ # It should be faster to lock the array and get a whole row at once?
+ # How to calculate the pointer offset?
+ for i in range(indices[dim], upperbounds[dim]+1):
+ indices[dim] = i
+ _safearray.SafeArrayGetElement(self, indices, pobj)
+ result.append(obj.value)
+ else:
+ for i in range(indices[dim], upperbounds[dim]+1):
+ indices[dim] = i
+ result.append(self._get_row(dim+1, indices, lowerbounds, upperbounds))
+ indices[dim] = restore
+ return tuple(result) # for compatibility with pywin32.
+
+ @Patch(POINTER(POINTER(sa_type)))
+ class __(object):
+
+ @classmethod
+ def from_param(cls, value):
+ if isinstance(value, cls._type_):
+ return byref(value)
+ return byref(cls._type_.create(value, extra))
+
+ def __setitem__(self, index, value):
+ # create an LP_SAFEARRAY_... instance
+ 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)
+
+ return sa_type
+
+
+def _ndarray_to_variant_array(value):
+ """ Convert an ndarray to VARIANT_dtype array """
+ # Check that variant arrays are supported
+ if npsupport.VARIANT_dtype is None:
+ msg = "VARIANT ndarrays require NumPy 1.7 or newer."
+ raise RuntimeError(msg)
+
+ # special cases
+ if numpy.issubdtype(value.dtype, npsupport.datetime64):
+ return _datetime64_ndarray_to_variant_array(value)
+
+ from comtypes.automation import VARIANT
+ # Empty array
+ varr = numpy.zeros(value.shape, npsupport.VARIANT_dtype, order='F')
+ # Convert each value to a variant and put it in the array.
+ varr.flat = [VARIANT(v) for v in value.flat]
+ return varr
+
+
+def _datetime64_ndarray_to_variant_array(value):
+ """ Convert an ndarray of datetime64 to VARIANT_dtype array """
+ # The OLE automation date format is a floating point value, counting days
+ # since midnight 30 December 1899. Hours and minutes are represented as
+ # fractional days.
+ from comtypes.automation import VT_DATE
+ value = numpy.array(value, "datetime64[ns]")
+ value = value - npsupport.com_null_date64
+ # Convert to days
+ value = value / numpy.timedelta64(1, 'D')
+ varr = numpy.zeros(value.shape, npsupport.VARIANT_dtype, order='F')
+ varr['vt'] = VT_DATE
+ varr['_']['VT_R8'].flat = value.flat
+ return varr
diff --git a/tools/comtypes/comtypes/shelllink.py b/tools/comtypes/comtypes/shelllink.py
new file mode 100644
index 00000000000000..b512359a8ca2eb
--- /dev/null
+++ b/tools/comtypes/comtypes/shelllink.py
@@ -0,0 +1,217 @@
+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()
diff --git a/tools/comtypes/comtypes/tools/__init__.py b/tools/comtypes/comtypes/tools/__init__.py
new file mode 100644
index 00000000000000..a35f8aa391976f
--- /dev/null
+++ b/tools/comtypes/comtypes/tools/__init__.py
@@ -0,0 +1 @@
+# the comtypes.tools package
diff --git a/tools/comtypes/comtypes/tools/codegenerator.py b/tools/comtypes/comtypes/tools/codegenerator.py
new file mode 100644
index 00000000000000..be0f0566db60f2
--- /dev/null
+++ b/tools/comtypes/comtypes/tools/codegenerator.py
@@ -0,0 +1,1002 @@
+# Code generator to generate code for everything contained in COM type
+# libraries.
+import os
+import cStringIO
+import keyword
+from comtypes.tools import typedesc
+import comtypes.client
+import comtypes.client._generate
+
+version = "$Rev$"[6:-2]
+
+__warn_on_munge__ = __debug__
+
+
+class lcid(object):
+ def __repr__(self):
+ return "_lcid"
+lcid = lcid()
+
+class dispid(object):
+ def __init__(self, memid):
+ self.memid = memid
+
+ def __repr__(self):
+ return "dispid(%s)" % self.memid
+
+class helpstring(object):
+ def __init__(self, text):
+ self.text = text
+
+ def __repr__(self):
+ return "helpstring(%r)" % self.text
+
+
+# XXX Should this be in ctypes itself?
+ctypes_names = {
+ "unsigned char": "c_ubyte",
+ "signed char": "c_byte",
+ "char": "c_char",
+
+ "wchar_t": "c_wchar",
+
+ "short unsigned int": "c_ushort",
+ "short int": "c_short",
+
+ "long unsigned int": "c_ulong",
+ "long int": "c_long",
+ "long signed int": "c_long",
+
+ "unsigned int": "c_uint",
+ "int": "c_int",
+
+ "long long unsigned int": "c_ulonglong",
+ "long long int": "c_longlong",
+
+ "double": "c_double",
+ "float": "c_float",
+
+ # Hm...
+ "void": "None",
+}
+
+def get_real_type(tp):
+ if type(tp) is typedesc.Typedef:
+ return get_real_type(tp.typ)
+ elif isinstance(tp, typedesc.CvQualifiedType):
+ return get_real_type(tp.typ)
+ return tp
+
+ASSUME_STRINGS = True
+
+def _calc_packing(struct, fields, pack, isStruct):
+ # Try a certain packing, raise PackingError if field offsets,
+ # total size ot total alignment is wrong.
+ if struct.size is None: # incomplete struct
+ return -1
+ if struct.name in dont_assert_size:
+ return None
+ if struct.bases:
+ size = struct.bases[0].size
+ total_align = struct.bases[0].align
+ else:
+ size = 0
+ total_align = 8 # in bits
+ for i, f in enumerate(fields):
+ if f.bits: # this code cannot handle bit field sizes.
+## print "##XXX FIXME"
+ return -2 # XXX FIXME
+ s, a = storage(f.typ)
+ if pack is not None:
+ a = min(pack, a)
+ if size % a:
+ size += a - size % a
+ if isStruct:
+ if 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))
+ 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))
+
+def calc_packing(struct, fields):
+ # try several packings, starting with unspecified packing
+ isStruct = isinstance(struct, typedesc.Structure)
+ for pack in [None, 16*8, 8*8, 4*8, 2*8, 1*8]:
+ try:
+ _calc_packing(struct, fields, pack, isStruct)
+ except PackingError, details:
+ continue
+ else:
+ if pack is None:
+ return None
+ return pack/8
+ raise PackingError("PACKING FAILED: %s" % details)
+
+class PackingError(Exception):
+ pass
+
+try:
+ set
+except NameError:
+ # Python 2.3
+ from sets import Set as set
+
+# XXX These should be filtered out in gccxmlparser.
+dont_assert_size = set(
+ [
+ "__si_class_type_info_pseudo",
+ "__class_type_info_pseudo",
+ ]
+ )
+
+def storage(t):
+ # return the size and alignment of a type
+ if isinstance(t, typedesc.Typedef):
+ return storage(t.typ)
+ elif isinstance(t, typedesc.ArrayType):
+ s, a = storage(t.typ)
+ return s * (int(t.max) - int(t.min) + 1), a
+ return int(t.size), int(t.align)
+
+################################################################
+
+class Generator(object):
+
+ def __init__(self, ofi, known_symbols=None):
+ self._externals = {}
+ self.output = ofi
+ self.stream = cStringIO.StringIO()
+ self.imports = cStringIO.StringIO()
+## self.stream = self.imports = self.output
+ self.known_symbols = known_symbols or {}
+
+ self.done = set() # type descriptions that have been generated
+ self.names = set() # names that have been generated
+
+ def generate(self, item):
+ if item in self.done:
+ return
+ if isinstance(item, typedesc.StructureHead):
+ name = getattr(item.struct, "name", None)
+ else:
+ name = getattr(item, "name", None)
+ if name in self.known_symbols:
+ mod = self.known_symbols[name]
+ print >> self.imports, "from %s import %s" % (mod, name)
+ self.done.add(item)
+ if isinstance(item, typedesc.Structure):
+ self.done.add(item.get_head())
+ self.done.add(item.get_body())
+ return
+ mth = getattr(self, type(item).__name__)
+ # to avoid infinite recursion, we have to mark it as done
+ # before actually generating the code.
+ self.done.add(item)
+ mth(item)
+
+ def generate_all(self, items):
+ for item in items:
+ self.generate(item)
+
+ def _make_relative_path(self, path1, path2):
+ """path1 and path2 are pathnames.
+ Return path1 as a relative path to path2, if possible.
+ """
+ path1 = os.path.abspath(path1)
+ path2 = os.path.abspath(path2)
+ common = os.path.commonprefix([os.path.normcase(path1),
+ os.path.normcase(path2)])
+ if not os.path.isdir(common):
+ return path1
+ if not common.endswith("\\"):
+ return path1
+ if not os.path.isdir(path2):
+ path2 = os.path.dirname(path2)
+ # strip the common prefix
+ path1 = path1[len(common):]
+ path2 = path2[len(common):]
+
+ parts2 = path2.split("\\")
+ return "..\\" * len(parts2) + path1
+
+ 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
+ elif not os.path.dirname(filename) and not os.path.isfile(filename):
+ # no directory given, and not in current directory.
+ print >> self.output, "typelib_path = %r" % filename
+ else:
+ # relative path; make relative to comtypes.gen.
+ path = self._make_relative_path(filename, comtypes.gen.__path__[0])
+ print >> self.output, "import os"
+ print >> self.output, "typelib_path = os.path.normpath("
+ print >> self.output, " os.path.abspath(os.path.join(os.path.dirname(__file__),"
+ print >> self.output, " %r)))" % path
+
+ p = os.path.normpath(os.path.abspath(os.path.join(comtypes.gen.__path__[0],
+ path)))
+ assert os.path.isfile(p)
+ print >> self.imports, "_lcid = 0 # change this if required"
+ print >> self.imports, "from ctypes import *"
+ items = set(items)
+ loops = 0
+ while items:
+ loops += 1
+ self.more = set()
+ self.generate_all(items)
+
+ items |= self.more
+ items -= self.done
+
+ self.output.write(self.imports.getvalue())
+ self.output.write("\n\n")
+ self.output.write(self.stream.getvalue())
+
+ import textwrap
+ wrapper = textwrap.TextWrapper(subsequent_indent=" ",
+ break_long_words=False)
+ # XXX The space before '%s' is needed to make sure that the entire list
+ # does not get pushed to the next line when the first name is
+ # excessively long.
+ text = "__all__ = [ %s]" % ", ".join([repr(str(n)) for n in self.names])
+
+ for line in wrapper.wrap(text):
+ print >> self.output, line
+ print >> self.output, "from comtypes import _check_version; _check_version(%r)" % version
+ return loops
+
+ def type_name(self, t, generate=True):
+ # Return a string, containing an expression which can be used
+ # to refer to the type. Assumes the 'from ctypes import *'
+ # namespace is available.
+ if isinstance(t, typedesc.SAFEARRAYType):
+ return "_midlSAFEARRAY(%s)" % self.type_name(t.typ)
+## if isinstance(t, typedesc.CoClass):
+## return "%s._com_interfaces_[0]" % t.name
+ if isinstance(t, typedesc.Typedef):
+ return t.name
+ if isinstance(t, typedesc.PointerType):
+ if ASSUME_STRINGS:
+ x = get_real_type(t.typ)
+ if isinstance(x, typedesc.FundamentalType):
+ if x.name == "char":
+ self.need_STRING()
+ return "STRING"
+ elif x.name == "wchar_t":
+ self.need_WSTRING()
+ return "WSTRING"
+
+ result = "POINTER(%s)" % self.type_name(t.typ, generate)
+ # XXX Better to inspect t.typ!
+ if result.startswith("POINTER(WINFUNCTYPE"):
+ return result[len("POINTER("):-1]
+ if result.startswith("POINTER(CFUNCTYPE"):
+ return result[len("POINTER("):-1]
+ elif result == "POINTER(None)":
+ return "c_void_p"
+ return result
+ elif isinstance(t, typedesc.ArrayType):
+ return "%s * %s" % (self.type_name(t.typ, generate), int(t.max)+1)
+ elif isinstance(t, typedesc.FunctionType):
+ args = [self.type_name(x, generate) for x in [t.returns] + list(t.iterArgTypes())]
+ if "__stdcall__" in t.attributes:
+ return "WINFUNCTYPE(%s)" % ", ".join(args)
+ else:
+ return "CFUNCTYPE(%s)" % ", ".join(args)
+ elif isinstance(t, typedesc.CvQualifiedType):
+ # const and volatile are ignored
+ return "%s" % self.type_name(t.typ, generate)
+ elif isinstance(t, typedesc.FundamentalType):
+ return ctypes_names[t.name]
+ elif isinstance(t, typedesc.Structure):
+ return t.name
+ elif isinstance(t, typedesc.Enumeration):
+ if t.name:
+ return t.name
+ return "c_int" # enums are integers
+ return t.name
+
+ def need_VARIANT_imports(self, value):
+ text = repr(value)
+ if "Decimal(" in text:
+ print >> self.imports, "from decimal import Decimal"
+ if "datetime.datetime(" in text:
+ print >> self.imports, "import datetime"
+
+ _STRING_defined = False
+ def need_STRING(self):
+ if self._STRING_defined:
+ return
+ print >> self.imports, "STRING = c_char_p"
+ self._STRING_defined = True
+
+ _WSTRING_defined = False
+ def need_WSTRING(self):
+ if self._WSTRING_defined:
+ return
+ print >> self.imports, "WSTRING = c_wchar_p"
+ self._WSTRING_defined = True
+
+ _OPENARRAYS_defined = False
+ def need_OPENARRAYS(self):
+ if self._OPENARRAYS_defined:
+ return
+ print >> self.imports, "OPENARRAY = POINTER(c_ubyte) # hack, see comtypes/tools/codegenerator.py"
+ self._OPENARRAYS_defined = True
+
+ _arraytypes = 0
+ def ArrayType(self, tp):
+ self._arraytypes += 1
+ self.generate(get_real_type(tp.typ))
+ self.generate(tp.typ)
+
+ _enumvalues = 0
+ def EnumValue(self, tp):
+ value = int(tp.value)
+ if keyword.iskeyword(tp.name):
+ # XXX use logging!
+ if __warn_on_munge__:
+ print "# Fixing keyword as EnumValue for %s" % tp.name
+ tp.name += "_"
+ print >> self.stream, \
+ "%s = %d" % (tp.name, value)
+ self.names.add(tp.name)
+ self._enumvalues += 1
+
+ _enumtypes = 0
+ def Enumeration(self, tp):
+ self._enumtypes += 1
+ print >> self.stream
+ if tp.name:
+ print >> self.stream, "# values for enumeration '%s'" % tp.name
+ else:
+ print >> self.stream, "# values for unnamed enumeration"
+ # Some enumerations have the same name for the enum type
+ # and an enum value. Excel's XlDisplayShapes is such an example.
+ # Since we don't have separate namespaces for the type and the values,
+ # we generate the TYPE last, overwriting the value. XXX
+ for item in tp.values:
+ self.generate(item)
+ if tp.name:
+ print >> self.stream, "%s = c_int # enum" % tp.name
+ self.names.add(tp.name)
+
+ _GUID_defined = False
+ def need_GUID(self):
+ if self._GUID_defined:
+ return
+ self._GUID_defined = True
+ modname = self.known_symbols.get("GUID")
+ if modname:
+ print >> self.imports, "from %s import GUID" % modname
+
+ _typedefs = 0
+ def Typedef(self, tp):
+ self._typedefs += 1
+ if type(tp.typ) in (typedesc.Structure, typedesc.Union):
+ self.generate(tp.typ.get_head())
+ self.more.add(tp.typ)
+ else:
+ self.generate(tp.typ)
+ if self.type_name(tp.typ) in self.known_symbols:
+ stream = self.imports
+ else:
+ stream = self.stream
+ if tp.name != self.type_name(tp.typ):
+ print >> stream, "%s = %s" % \
+ (tp.name, self.type_name(tp.typ))
+ self.names.add(tp.name)
+
+ def FundamentalType(self, item):
+ pass # we should check if this is known somewhere
+
+ def StructureHead(self, head):
+ for struct in head.struct.bases:
+ self.generate(struct.get_head())
+ self.more.add(struct)
+ if head.struct.location:
+ print >> self.stream, "# %s %s" % head.struct.location
+ basenames = [self.type_name(b) for b in head.struct.bases]
+ if basenames:
+ self.need_GUID()
+ method_names = [m.name for m in head.struct.members if type(m) is typedesc.Method]
+ print >> self.stream, "class %s(%s):" % (head.struct.name, ", ".join(basenames))
+ print >> self.stream, " _iid_ = GUID('{}') # please look up iid and fill in!"
+ if "Enum" in method_names:
+ print >> self.stream, " def __iter__(self):"
+ print >> self.stream, " return self.Enum()"
+ elif method_names == "Next Skip Reset Clone".split():
+ print >> self.stream, " def __iter__(self):"
+ print >> self.stream, " return self"
+ print >> self.stream
+ print >> self.stream, " def next(self):"
+ print >> self.stream, " arr, fetched = self.Next(1)"
+ print >> self.stream, " if fetched == 0:"
+ print >> self.stream, " raise StopIteration"
+ print >> self.stream, " return arr[0]"
+ else:
+ methods = [m for m in head.struct.members if type(m) is typedesc.Method]
+ if methods:
+ # Hm. We cannot generate code for IUnknown...
+ print >> self.stream, "assert 0, 'cannot generate code for IUnknown'"
+ print >> self.stream, "class %s(_com_interface):" % head.struct.name
+ print >> self.stream, " pass"
+ elif type(head.struct) == typedesc.Structure:
+ print >> self.stream, "class %s(Structure):" % head.struct.name
+ if hasattr(head.struct, "_recordinfo_"):
+ print >> self.stream, " _recordinfo_ = %r" % (head.struct._recordinfo_,)
+ else:
+ print >> self.stream, " pass"
+ elif type(head.struct) == typedesc.Union:
+ print >> self.stream, "class %s(Union):" % head.struct.name
+ print >> self.stream, " pass"
+ self.names.add(head.struct.name)
+
+ _structures = 0
+ def Structure(self, struct):
+ self._structures += 1
+ self.generate(struct.get_head())
+ self.generate(struct.get_body())
+
+ Union = Structure
+
+ def StructureBody(self, body):
+ fields = []
+ methods = []
+ for m in body.struct.members:
+ if type(m) is typedesc.Field:
+ fields.append(m)
+ if type(m.typ) is typedesc.Typedef:
+ self.generate(get_real_type(m.typ))
+ self.generate(m.typ)
+ elif type(m) is typedesc.Method:
+ methods.append(m)
+ self.generate(m.returns)
+ self.generate_all(m.iterArgTypes())
+ elif type(m) is typedesc.Constructor:
+ pass
+
+ # we don't need _pack_ on Unions (I hope, at least), and not
+ # on COM interfaces:
+ if not methods:
+ try:
+ pack = calc_packing(body.struct, fields)
+ if pack is not None:
+ print >> self.stream, "%s._pack_ = %s" % (body.struct.name, pack)
+ except PackingError, details:
+ # if packing fails, write a warning comment to the output.
+ import warnings
+ message = "Structure %s: %s" % (body.struct.name, details)
+ warnings.warn(message, UserWarning)
+ print >> self.stream, "# WARNING: %s" % details
+
+ if fields:
+ if body.struct.bases:
+ assert len(body.struct.bases) == 1
+ self.generate(body.struct.bases[0].get_body())
+ # field definition normally span several lines.
+ # Before we generate them, we need to 'import' everything they need.
+ # So, call type_name for each field once,
+ for f in fields:
+ self.type_name(f.typ)
+ print >> self.stream, "%s._fields_ = [" % body.struct.name
+ if body.struct.location:
+ print >> self.stream, " # %s %s" % body.struct.location
+ # unnamed fields will get autogenerated names "_", "_1". "_2", "_3", ...
+ unnamed_index = 0
+ for f in fields:
+ if not f.name:
+ if unnamed_index:
+ fieldname = "_%d" % unnamed_index
+ else:
+ fieldname = "_"
+ unnamed_index += 1
+ print >> self.stream, " # Unnamed field renamed to '%s'" % fieldname
+ else:
+ fieldname = f.name
+ if f.bits is None:
+ print >> self.stream, " ('%s', %s)," % (fieldname, self.type_name(f.typ))
+ else:
+ print >> self.stream, " ('%s', %s, %s)," % (fieldname, self.type_name(f.typ), f.bits)
+ print >> self.stream, "]"
+
+ if body.struct.size is None:
+ msg = ("# The size provided by the typelib is incorrect.\n"
+ "# The size and alignment check for %s is skipped.")
+ print >> self.stream, msg % body.struct.name
+ elif body.struct.name not in dont_assert_size:
+ size = body.struct.size // 8
+ print >> self.stream, "assert sizeof(%s) == %s, sizeof(%s)" % \
+ (body.struct.name, size, body.struct.name)
+ align = body.struct.align // 8
+ print >> self.stream, "assert alignment(%s) == %s, alignment(%s)" % \
+ (body.struct.name, align, body.struct.name)
+
+ if methods:
+ self.need_COMMETHOD()
+ # method definitions normally span several lines.
+ # Before we generate them, we need to 'import' everything they need.
+ # So, call type_name for each field once,
+ for m in methods:
+ self.type_name(m.returns)
+ for a in m.iterArgTypes():
+ self.type_name(a)
+ print >> self.stream, "%s._methods_ = [" % body.struct.name
+ if body.struct.location:
+ print >> self.stream, "# %s %s" % body.struct.location
+
+ for m in methods:
+ if m.location:
+ print >> self.stream, " # %s %s" % m.location
+ print >> self.stream, " COMMETHOD([], %s, '%s'," % (
+ self.type_name(m.returns),
+ m.name)
+ for a in m.iterArgTypes():
+ print >> self.stream, \
+ " ( [], %s, )," % self.type_name(a)
+ print >> self.stream, " ),"
+ print >> self.stream, "]"
+
+ _midlSAFEARRAY_defined = False
+ def need_midlSAFEARRAY(self):
+ if self._midlSAFEARRAY_defined:
+ return
+ print >> self.imports, "from comtypes.automation import _midlSAFEARRAY"
+ self._midlSAFEARRAY_defined = True
+
+ _CoClass_defined = False
+ def need_CoClass(self):
+ if self._CoClass_defined:
+ return
+ print >> self.imports, "from comtypes import CoClass"
+ self._CoClass_defined = True
+
+ _dispid_defined = False
+ def need_dispid(self):
+ if self._dispid_defined:
+ return
+ print >> self.imports, "from comtypes import dispid"
+ self._dispid_defined = True
+
+ _COMMETHOD_defined = False
+ def need_COMMETHOD(self):
+ if self._COMMETHOD_defined:
+ return
+ print >> self.imports, "from comtypes import helpstring"
+ print >> self.imports, "from comtypes import COMMETHOD"
+ self._COMMETHOD_defined = True
+
+ _DISPMETHOD_defined = False
+ def need_DISPMETHOD(self):
+ if self._DISPMETHOD_defined:
+ return
+ print >> self.imports, "from comtypes import DISPMETHOD, DISPPROPERTY, helpstring"
+ self._DISPMETHOD_defined = True
+
+ ################################################################
+ # top-level typedesc generators
+ #
+ def TypeLib(self, lib):
+ # lib.name, lib.gui, lib.major, lib.minor, lib.doc
+
+ # Hm, in user code we have to write:
+ # class MyServer(COMObject, ...):
+ # _com_interfaces_ = [MyTypeLib.IInterface]
+ # _reg_typelib_ = MyTypeLib.Library._reg_typelib_
+ # ^^^^^^^
+ # Should the '_reg_typelib_' attribute be at top-level in the
+ # generated code, instead as being an attribute of the
+ # 'Library' symbol?
+ print >> self.stream, "class Library(object):"
+ if lib.doc:
+ print >> self.stream, " %r" % lib.doc
+ if lib.name:
+ print >> self.stream, " name = %r" % lib.name
+ print >> self.stream, " _reg_typelib_ = (%r, %r, %r)" % (lib.guid, lib.major, lib.minor)
+ print >> self.stream
+
+ def External(self, ext):
+ # ext.docs - docstring of typelib
+ # ext.symbol_name - symbol to generate
+ # ext.tlib - the ITypeLib pointer to the typelibrary containing the symbols definition
+ #
+ # ext.name filled in here
+
+ libdesc = str(ext.tlib.GetLibAttr()) # str(TLIBATTR) is unique for a given typelib
+ if libdesc in self._externals: # typelib wrapper already created
+ modname = self._externals[libdesc]
+ # we must fill in ext.name, it is used by self.type_name()
+ ext.name = "%s.%s" % (modname, ext.symbol_name)
+ return
+
+ modname = comtypes.client._generate._name_module(ext.tlib)
+ ext.name = "%s.%s" % (modname, ext.symbol_name)
+ self._externals[libdesc] = modname
+ print >> self.imports, "import", modname
+ comtypes.client.GetModule(ext.tlib)
+
+ def Constant(self, tp):
+ print >> self.stream, \
+ "%s = %r # Constant %s" % (tp.name,
+ tp.value,
+ self.type_name(tp.typ, False))
+ self.names.add(tp.name)
+
+ def SAFEARRAYType(self, sa):
+ self.generate(sa.typ)
+ self.need_midlSAFEARRAY()
+
+ _pointertypes = 0
+ def PointerType(self, tp):
+ self._pointertypes += 1
+ if type(tp.typ) is typedesc.ComInterface:
+ # this defines the class
+ self.generate(tp.typ.get_head())
+ # this defines the _methods_
+ self.more.add(tp.typ)
+ elif type(tp.typ) is typedesc.PointerType:
+ self.generate(tp.typ)
+ elif type(tp.typ) in (typedesc.Union, typedesc.Structure):
+ self.generate(tp.typ.get_head())
+ self.more.add(tp.typ)
+ elif type(tp.typ) is typedesc.Typedef:
+ self.generate(tp.typ)
+ else:
+ self.generate(tp.typ)
+
+ def CoClass(self, coclass):
+ self.need_GUID()
+ self.need_CoClass()
+ print >> self.stream, "class %s(CoClass):" % coclass.name
+ doc = getattr(coclass, "doc", None)
+ if doc:
+ print >> self.stream, " %r" % doc
+ print >> self.stream, " _reg_clsid_ = GUID(%r)" % coclass.clsid
+ print >> self.stream, " _idlflags_ = %s" % coclass.idlflags
+ if self.filename is not None:
+ print >> self.stream, " _typelib_path_ = typelib_path"
+##X print >> self.stream, "POINTER(%s).__ctypes_from_outparam__ = wrap" % coclass.name
+
+ libid = coclass.tlibattr.guid
+ wMajor, wMinor = coclass.tlibattr.wMajorVerNum, coclass.tlibattr.wMinorVerNum
+ print >> self.stream, " _reg_typelib_ = (%r, %s, %s)" % (str(libid), wMajor, wMinor)
+
+ for itf, idlflags in coclass.interfaces:
+ self.generate(itf.get_head())
+ implemented = []
+ sources = []
+ for item in coclass.interfaces:
+ # item is (interface class, impltypeflags)
+ if item[1] & 2: # IMPLTYPEFLAG_FSOURCE
+ # source interface
+ where = sources
+ else:
+ # sink interface
+ where = implemented
+ if item[1] & 1: # IMPLTYPEFLAG_FDEAULT
+ # The default interface should be the first item on the list
+ where.insert(0, item[0].name)
+ else:
+ where.append(item[0].name)
+ if implemented:
+ print >> self.stream, "%s._com_interfaces_ = [%s]" % (coclass.name, ", ".join(implemented))
+ if sources:
+ print >> self.stream, "%s._outgoing_interfaces_ = [%s]" % (coclass.name, ", ".join(sources))
+ print >> self.stream
+ self.names.add(coclass.name)
+
+ def ComInterface(self, itf):
+ self.generate(itf.get_head())
+ self.generate(itf.get_body())
+ self.names.add(itf.name)
+
+ def _is_enuminterface(self, itf):
+ # Check if this is an IEnumXXX interface
+ if not itf.name.startswith("IEnum"):
+ return False
+ member_names = [mth.name for mth in itf.members]
+ for name in ("Next", "Skip", "Reset", "Clone"):
+ if name not in member_names:
+ return False
+ return True
+
+ def ComInterfaceHead(self, head):
+ if head.itf.name in self.known_symbols:
+ return
+ base = head.itf.base
+ if head.itf.base is None:
+ # we don't beed to generate IUnknown
+ return
+ self.generate(base.get_head())
+ self.more.add(base)
+ basename = self.type_name(head.itf.base)
+
+ self.need_GUID()
+ print >> self.stream, "class %s(%s):" % (head.itf.name, basename)
+ print >> self.stream, " _case_insensitive_ = True"
+ doc = getattr(head.itf, "doc", None)
+ if doc:
+ print >> self.stream, " %r" % doc
+ print >> self.stream, " _iid_ = GUID(%r)" % head.itf.iid
+ print >> self.stream, " _idlflags_ = %s" % head.itf.idlflags
+
+ if self._is_enuminterface(head.itf):
+ print >> self.stream, " def __iter__(self):"
+ print >> self.stream, " return self"
+ print >> self.stream
+
+ print >> self.stream, " def next(self):"
+ print >> self.stream, " item, fetched = self.Next(1)"
+ print >> self.stream, " if fetched:"
+ print >> self.stream, " return item"
+ print >> self.stream, " raise StopIteration"
+ print >> self.stream
+
+ print >> self.stream, " def __getitem__(self, index):"
+ print >> self.stream, " self.Reset()"
+ print >> self.stream, " self.Skip(index)"
+ 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
+
+ def ComInterfaceBody(self, body):
+ # The base class must be fully generated, including the
+ # _methods_ list.
+ self.generate(body.itf.base)
+
+ # make sure we can generate the body
+ for m in body.itf.members:
+ for a in m.arguments:
+ self.generate(a[0])
+ self.generate(m.returns)
+
+ self.need_COMMETHOD()
+ self.need_dispid()
+ print >> self.stream, "%s._methods_ = [" % body.itf.name
+ for m in body.itf.members:
+ if isinstance(m, typedesc.ComMethod):
+ self.make_ComMethod(m, "dual" in body.itf.idlflags)
+ else:
+ raise TypeError("what's this?")
+
+ print >> self.stream, "]"
+ print >> self.stream, "################################################################"
+ print >> self.stream, "## code template for %s implementation" % body.itf.name
+ print >> self.stream, "##class %s_Impl(object):" % body.itf.name
+
+ methods = {}
+ for m in body.itf.members:
+ if isinstance(m, typedesc.ComMethod):
+ # m.arguments is a sequence of tuples:
+ # (argtype, argname, idlflags, docstring)
+ # Some typelibs have unnamed method parameters!
+ inargs = [a[1] or '' for a in m.arguments
+ if not 'out' in a[2]]
+ outargs = [a[1] or '' for a in m.arguments
+ if 'out' in a[2]]
+ if 'propget' in m.idlflags:
+ methods.setdefault(m.name, [0, inargs, outargs, m.doc])[0] |= 1
+ elif 'propput' in m.idlflags:
+ methods.setdefault(m.name, [0, inargs[:-1], inargs[-1:], m.doc])[0] |= 2
+ else:
+ methods[m.name] = [0, inargs, outargs, m.doc]
+
+ for name, (typ, inargs, outargs, doc) in methods.iteritems():
+ if typ == 0: # method
+ print >> self.stream, "## def %s(%s):" % (name, ", ".join(["self"] + inargs))
+ print >> self.stream, "## %r" % (doc or "-no docstring-")
+ print >> self.stream, "## #return %s" % (", ".join(outargs))
+ elif typ == 1: # propget
+ print >> self.stream, "## @property"
+ print >> self.stream, "## def %s(%s):" % (name, ", ".join(["self"] + inargs))
+ print >> self.stream, "## %r" % (doc or "-no docstring-")
+ print >> self.stream, "## #return %s" % (", ".join(outargs))
+ elif typ == 2: # propput
+ print >> self.stream, "## def _set(%s):" % ", ".join(["self"] + inargs + outargs)
+ print >> self.stream, "## %r" % (doc or "-no docstring-")
+ print >> self.stream, "## %s = property(fset = _set, doc = _set.__doc__)" % name
+ elif typ == 3: # propget + propput
+ print >> self.stream, "## def _get(%s):" % ", ".join(["self"] + inargs)
+ print >> self.stream, "## %r" % (doc or "-no docstring-")
+ print >> self.stream, "## #return %s" % (", ".join(outargs))
+ print >> self.stream, "## def _set(%s):" % ", ".join(["self"] + inargs + outargs)
+ print >> self.stream, "## %r" % (doc or "-no docstring-")
+ print >> self.stream, "## %s = property(_get, _set, doc = _set.__doc__)" % name
+ else:
+ raise RuntimeError("BUG")
+ print >> self.stream, "##"
+ print >> self.stream
+
+ def DispInterface(self, itf):
+ self.generate(itf.get_head())
+ self.generate(itf.get_body())
+ self.names.add(itf.name)
+
+ def DispInterfaceHead(self, head):
+ self.generate(head.itf.base)
+ basename = self.type_name(head.itf.base)
+
+ self.need_GUID()
+ print >> self.stream, "class %s(%s):" % (head.itf.name, basename)
+ print >> self.stream, " _case_insensitive_ = True"
+ doc = getattr(head.itf, "doc", None)
+ if doc:
+ print >> self.stream, " %r" % doc
+ print >> self.stream, " _iid_ = GUID(%r)" % head.itf.iid
+ print >> self.stream, " _idlflags_ = %s" % head.itf.idlflags
+ print >> self.stream, " _methods_ = []"
+
+ def DispInterfaceBody(self, body):
+ # make sure we can generate the body
+ for m in body.itf.members:
+ if isinstance(m, typedesc.DispMethod):
+ for a in m.arguments:
+ self.generate(a[0])
+ self.generate(m.returns)
+ elif isinstance(m, typedesc.DispProperty):
+ self.generate(m.typ)
+ else:
+ raise TypeError(m)
+
+ self.need_dispid()
+ self.need_DISPMETHOD()
+ print >> self.stream, "%s._disp_methods_ = [" % body.itf.name
+ for m in body.itf.members:
+ if isinstance(m, typedesc.DispMethod):
+ self.make_DispMethod(m)
+ elif isinstance(m, typedesc.DispProperty):
+ self.make_DispProperty(m)
+ else:
+ raise TypeError(m)
+ print >> self.stream, "]"
+
+ ################################################################
+ # non-toplevel method generators
+ #
+ def make_ComMethod(self, m, isdual):
+ # typ, name, idlflags, default
+ if isdual:
+ idlflags = [dispid(m.memid)] + m.idlflags
+ else:
+ # We don't include the dispid for non-dispatch COM interfaces
+ idlflags = m.idlflags
+ if __debug__ and m.doc:
+ idlflags.insert(1, helpstring(m.doc))
+ code = " COMMETHOD(%r, %s, '%s'" % (
+ idlflags,
+ self.type_name(m.returns),
+ m.name)
+
+ if not m.arguments:
+ print >> self.stream, "%s)," % code
+ else:
+ print >> self.stream, "%s," % code
+ self.stream.write(" ")
+ arglist = []
+ for typ, name, idlflags, default in m.arguments:
+ type_name = self.type_name(typ)
+ ###########################################################
+ # IDL files that contain 'open arrays' or 'conformant
+ # varying arrays' method parameters are strange.
+ # These arrays have both a 'size_is()' and
+ # 'length_is()' attribute, like this example from
+ # dia2.idl (in the DIA SDK):
+ #
+ # interface IDiaSymbol: IUnknown {
+ # ...
+ # HRESULT get_dataBytes(
+ # [in] DWORD cbData,
+ # [out] DWORD *pcbData,
+ # [out, size_is(cbData),
+ # length_is(*pcbData)] BYTE data[]
+ # );
+ #
+ # The really strange thing is that the decompiled type
+ # library then contains this declaration, which declares
+ # the interface itself as [out] method parameter:
+ #
+ # interface IDiaSymbol: IUnknown {
+ # ...
+ # HRESULT _stdcall get_dataBytes(
+ # [in] unsigned long cbData,
+ # [out] unsigned long* pcbData,
+ # [out] IDiaSymbol data);
+ #
+ # Of course, comtypes does not accept a COM interface
+ # as method parameter; so replace the parameter type
+ # with the comtypes spelling of 'unsigned char *', and
+ # mark the parameter as [in, out], so the IDL
+ # equivalent would be like this:
+ #
+ # interface IDiaSymbol: IUnknown {
+ # ...
+ # HRESULT _stdcall get_dataBytes(
+ # [in] unsigned long cbData,
+ # [out] unsigned long* pcbData,
+ # [in, out] BYTE data[]);
+ ###########################################################
+ if isinstance(typ, typedesc.ComInterface):
+ self.need_OPENARRAYS()
+ type_name = "OPENARRAY"
+ if 'in' not in idlflags:
+ idlflags.append('in')
+ if 'lcid' in idlflags:# and 'in' in idlflags:
+ default = lcid
+ if default is not None:
+ self.need_VARIANT_imports(default)
+ arglist.append("( %r, %s, '%s', %r )" % (
+ idlflags,
+ type_name,
+ name,
+ default))
+ else:
+ arglist.append("( %r, %s, '%s' )" % (
+ idlflags,
+ type_name,
+ name))
+ self.stream.write(",\n ".join(arglist))
+ print >> self.stream, "),"
+
+ def make_DispMethod(self, m):
+ idlflags = [dispid(m.dispid)] + m.idlflags
+ if __debug__ and m.doc:
+ idlflags.insert(1, helpstring(m.doc))
+ # typ, name, idlflags, default
+ code = " DISPMETHOD(%r, %s, '%s'" % (
+ idlflags,
+ self.type_name(m.returns),
+ m.name)
+
+ if not m.arguments:
+ print >> self.stream, "%s)," % code
+ else:
+ print >> self.stream, "%s," % code
+ self.stream.write(" ")
+ arglist = []
+ for typ, name, idlflags, default in m.arguments:
+ self.need_VARIANT_imports(default)
+ if default is not None:
+ arglist.append("( %r, %s, '%s', %r )" % (
+ idlflags,
+ self.type_name(typ),
+ name,
+ default))
+ else:
+ arglist.append("( %r, %s, '%s' )" % (
+ idlflags,
+ self.type_name(typ),
+ name,
+ ))
+ self.stream.write(",\n ".join(arglist))
+ print >> self.stream, "),"
+
+ def make_DispProperty(self, prop):
+ idlflags = [dispid(prop.dispid)] + prop.idlflags
+ if __debug__ and prop.doc:
+ idlflags.insert(1, helpstring(prop.doc))
+ print >> self.stream, " DISPPROPERTY(%r, %s, '%s')," % (
+ idlflags,
+ self.type_name(prop.typ),
+ prop.name)
+
+# shortcut for development
+if __name__ == "__main__":
+ import tlbparser
+ tlbparser.main()
diff --git a/tools/comtypes/comtypes/tools/tlbparser.py b/tools/comtypes/comtypes/tools/tlbparser.py
new file mode 100644
index 00000000000000..d9d118f93e2a40
--- /dev/null
+++ b/tools/comtypes/comtypes/tools/tlbparser.py
@@ -0,0 +1,752 @@
+import sys
+
+from comtypes import automation, typeinfo, COMError
+from comtypes.tools import typedesc
+from ctypes import c_void_p, sizeof, alignment
+
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+# Is the process 64-bit?
+is_64bits = sys.maxsize > 2**32
+
+
+################################
+
+def PTR(typ):
+ return typedesc.PointerType(typ,
+ sizeof(c_void_p)*8,
+ alignment(c_void_p)*8)
+
+# basic C data types, with size and alignment in bits
+char_type = typedesc.FundamentalType("char", 8, 8)
+uchar_type = typedesc.FundamentalType("unsigned char", 8, 8)
+wchar_t_type = typedesc.FundamentalType("wchar_t", 16, 16)
+short_type = typedesc.FundamentalType("short int", 16, 16)
+ushort_type = typedesc.FundamentalType("short unsigned int", 16, 16)
+int_type = typedesc.FundamentalType("int", 32, 32)
+uint_type = typedesc.FundamentalType("unsigned int", 32, 32)
+long_type = typedesc.FundamentalType("long int", 32, 32)
+ulong_type = typedesc.FundamentalType("long unsigned int", 32, 32)
+longlong_type = typedesc.FundamentalType("long long int", 64, 64)
+ulonglong_type = typedesc.FundamentalType("long long unsigned int", 64, 64)
+float_type = typedesc.FundamentalType("float", 32, 32)
+double_type = typedesc.FundamentalType("double", 64, 64)
+
+# basic COM data types
+BSTR_type = typedesc.Typedef("BSTR", PTR(wchar_t_type))
+SCODE_type = typedesc.Typedef("SCODE", int_type)
+VARIANT_BOOL_type = typedesc.Typedef("VARIANT_BOOL", short_type)
+HRESULT_type = typedesc.Typedef("HRESULT", ulong_type)
+
+VARIANT_type = typedesc.Structure("VARIANT",
+ align=alignment(automation.VARIANT)*8,
+ members=[], bases=[],
+ size=sizeof(automation.VARIANT)*8)
+IDISPATCH_type = typedesc.Typedef("IDispatch", None)
+IUNKNOWN_type = typedesc.Typedef("IUnknown", None)
+DECIMAL_type = typedesc.Structure("DECIMAL",
+ align=alignment(automation.DECIMAL)*8,
+ members=[], bases=[],
+ size=sizeof(automation.DECIMAL)*8)
+
+def midlSAFEARRAY(typ):
+ return typedesc.SAFEARRAYType(typ)
+
+# faked COM data types
+CURRENCY_type = longlong_type # slightly wrong; should be scaled by 10000 - use subclass of longlong?
+DATE_type = double_type # not *that* wrong...
+
+COMTYPES = {
+ automation.VT_I2: short_type, # 2
+ automation.VT_I4: int_type, # 3
+ automation.VT_R4: float_type, # 4
+ automation.VT_R8: double_type, # 5
+ automation.VT_CY: CURRENCY_type, # 6
+ automation.VT_DATE: DATE_type, # 7
+ automation.VT_BSTR: BSTR_type, # 8
+ automation.VT_DISPATCH: PTR(IDISPATCH_type), # 9
+ automation.VT_ERROR: SCODE_type, # 10
+ automation.VT_BOOL: VARIANT_BOOL_type, # 11
+ automation.VT_VARIANT: VARIANT_type, # 12
+ automation.VT_UNKNOWN: PTR(IUNKNOWN_type), # 13
+ automation.VT_DECIMAL: DECIMAL_type, # 14
+
+ automation.VT_I1: char_type, # 16
+ automation.VT_UI1: uchar_type, # 17
+ automation.VT_UI2: ushort_type, # 18
+ automation.VT_UI4: ulong_type, # 19
+ automation.VT_I8: longlong_type, # 20
+ automation.VT_UI8: ulonglong_type, # 21
+ automation.VT_INT: int_type, # 22
+ automation.VT_UINT: uint_type, # 23
+ automation.VT_VOID: typedesc.FundamentalType("void", 0, 0), # 24
+ automation.VT_HRESULT: HRESULT_type, # 25
+ automation.VT_LPSTR: PTR(char_type), # 30
+ automation.VT_LPWSTR: PTR(wchar_t_type), # 31
+}
+
+#automation.VT_PTR = 26 # below
+#automation.VT_SAFEARRAY = 27
+#automation.VT_CARRAY = 28 # below
+#automation.VT_USERDEFINED = 29 # below
+
+#automation.VT_RECORD = 36
+
+#automation.VT_ARRAY = 8192
+#automation.VT_BYREF = 16384
+
+################################################################
+
+class Parser(object):
+
+ def make_type(self, tdesc, tinfo):
+ try:
+ return COMTYPES[tdesc.vt]
+ except KeyError:
+ pass
+
+ if tdesc.vt == automation.VT_CARRAY:
+ typ = self.make_type(tdesc._.lpadesc[0].tdescElem, tinfo)
+ for i in range(tdesc._.lpadesc[0].cDims):
+ typ = typedesc.ArrayType(typ,
+ tdesc._.lpadesc[0].rgbounds[i].lLbound,
+ tdesc._.lpadesc[0].rgbounds[i].cElements-1)
+ return typ
+
+ elif tdesc.vt == automation.VT_PTR:
+ typ = self.make_type(tdesc._.lptdesc[0], tinfo)
+ return PTR(typ)
+
+ elif tdesc.vt == automation.VT_USERDEFINED:
+ try:
+ ti = tinfo.GetRefTypeInfo(tdesc._.hreftype)
+ except COMError, details:
+ type_name = "__error_hreftype_%d__" % tdesc._.hreftype
+ tlib_name = get_tlib_filename(self.tlib)
+ if tlib_name is None:
+ tlib_name = "unknown typelib"
+ message = "\n\tGetRefTypeInfo failed in %s: %s\n\tgenerating type '%s' instead" % \
+ (tlib_name, details, type_name)
+ import warnings
+ warnings.warn(message, UserWarning);
+ result = typedesc.Structure(type_name,
+ align=8,
+ members=[], bases=[],
+ size=0)
+ return result
+ result = self.parse_typeinfo(ti)
+ assert result is not None, ti.GetDocumentation(-1)[0]
+ return result
+
+ elif tdesc.vt == automation.VT_SAFEARRAY:
+ # SAFEARRAY(), see Don Box pp.331f
+ itemtype = self.make_type(tdesc._.lptdesc[0], tinfo)
+ return midlSAFEARRAY(itemtype)
+
+ raise NotImplementedError(tdesc.vt)
+
+ ################################################################
+
+ # TKIND_ENUM = 0
+ def ParseEnum(self, tinfo, ta):
+ ta = tinfo.GetTypeAttr()
+ enum_name = tinfo.GetDocumentation(-1)[0]
+ enum = typedesc.Enumeration(enum_name, 32, 32)
+ self._register(enum_name, enum)
+
+ for i in range(ta.cVars):
+ vd = tinfo.GetVarDesc(i)
+ name = tinfo.GetDocumentation(vd.memid)[0]
+ assert vd.varkind == typeinfo.VAR_CONST
+ num_val = vd._.lpvarValue[0].value
+ v = typedesc.EnumValue(name, num_val, enum)
+ enum.add_value(v)
+ return enum
+
+ # TKIND_RECORD = 1
+ def ParseRecord(self, tinfo, ta):
+ members = [] # will be filled later
+ struct_name, doc, helpcntext, helpfile = tinfo.GetDocumentation(-1)
+ struct = typedesc.Structure(struct_name,
+ align=ta.cbAlignment*8,
+ members=members,
+ bases=[],
+ size=ta.cbSizeInstance*8)
+ self._register(struct_name, struct)
+
+ tlib, _ = tinfo.GetContainingTypeLib()
+ tlib_ta = tlib.GetLibAttr()
+ # If this is a 32-bit typlib being loaded in a 64-bit process, then the
+ # size and alignment are incorrect. Set the size to None to disable
+ # size checks and correct the alignment.
+ if is_64bits and tlib_ta.syskind == typeinfo.SYS_WIN32:
+ struct.size = None
+ struct.align = 64
+
+ if ta.guid:
+ struct._recordinfo_ = (str(tlib_ta.guid),
+ tlib_ta.wMajorVerNum, tlib_ta.wMinorVerNum,
+ tlib_ta.lcid,
+ str(ta.guid))
+
+ for i in range(ta.cVars):
+ vd = tinfo.GetVarDesc(i)
+ name = tinfo.GetDocumentation(vd.memid)[0]
+ offset = vd._.oInst * 8
+ assert vd.varkind == typeinfo.VAR_PERINSTANCE
+ typ = self.make_type(vd.elemdescVar.tdesc, tinfo)
+ field = typedesc.Field(name,
+ typ,
+ None, # bits
+ offset)
+ members.append(field)
+ return struct
+
+ # TKIND_MODULE = 2
+ def ParseModule(self, tinfo, ta):
+ assert 0 == ta.cImplTypes
+ # functions
+ for i in range(ta.cFuncs):
+ # We skip all function definitions. There are several
+ # problems with these, and we can, for comtypes, ignore them.
+ continue
+ fd = tinfo.GetFuncDesc(i)
+ dllname, func_name, ordinal = tinfo.GetDllEntry(fd.memid, fd.invkind)
+ func_doc = tinfo.GetDocumentation(fd.memid)[1]
+ assert 0 == fd.cParamsOpt # XXX
+ returns = self.make_type(fd.elemdescFunc.tdesc, tinfo)
+
+ if fd.callconv == typeinfo.CC_CDECL:
+ attributes = "__cdecl__"
+ elif fd.callconv == typeinfo.CC_STDCALL:
+ attributes = "__stdcall__"
+ else:
+ raise ValueError("calling convention %d" % fd.callconv)
+
+ func = typedesc.Function(func_name, returns, attributes, extern=1)
+ if func_doc is not None:
+ func.doc = func_doc.encode("mbcs")
+ func.dllname = dllname
+ self._register(func_name, func)
+ for i in range(fd.cParams):
+ argtype = self.make_type(fd.lprgelemdescParam[i].tdesc, tinfo)
+ func.add_argument(argtype)
+
+ # constants
+ for i in range(ta.cVars):
+ vd = tinfo.GetVarDesc(i)
+ name, var_doc = tinfo.GetDocumentation(vd.memid)[0:2]
+ assert vd.varkind == typeinfo.VAR_CONST
+ typ = self.make_type(vd.elemdescVar.tdesc, tinfo)
+ var_value = vd._.lpvarValue[0].value
+ v = typedesc.Constant(name, typ, var_value)
+ self._register(name, v)
+ if var_doc is not None:
+ v.doc = var_doc
+
+ # TKIND_INTERFACE = 3
+ def ParseInterface(self, tinfo, ta):
+ itf_name, itf_doc = tinfo.GetDocumentation(-1)[0:2]
+ assert ta.cImplTypes <= 1
+ if ta.cImplTypes == 0 and itf_name != "IUnknown":
+ # Windows defines an interface IOleControlTypes in ocidl.idl.
+ # Don't known what artefact that is - we ignore it.
+ # It's an interface without methods anyway.
+ if itf_name != "IOleControlTypes":
+ message = "Ignoring interface %s which has no base interface" % itf_name
+ import warnings
+ warnings.warn(message, UserWarning);
+ return None
+
+ itf = typedesc.ComInterface(itf_name,
+ members=[],
+ base=None,
+ iid=str(ta.guid),
+ idlflags=self.interface_type_flags(ta.wTypeFlags))
+ if itf_doc:
+ itf.doc = itf_doc
+ self._register(itf_name, itf)
+
+ if ta.cImplTypes:
+ hr = tinfo.GetRefTypeOfImplType(0)
+ tibase = tinfo.GetRefTypeInfo(hr)
+ itf.base = self.parse_typeinfo(tibase)
+
+ assert ta.cVars == 0, "vars on an Interface?"
+
+ members = []
+ for i in range(ta.cFuncs):
+ fd = tinfo.GetFuncDesc(i)
+## func_name = tinfo.GetDocumentation(fd.memid)[0]
+ func_name, func_doc = tinfo.GetDocumentation(fd.memid)[:2]
+ assert fd.funckind == typeinfo.FUNC_PUREVIRTUAL
+ returns = self.make_type(fd.elemdescFunc.tdesc, tinfo)
+ names = tinfo.GetNames(fd.memid, fd.cParams+1)
+ names.append("rhs")
+ names = names[:fd.cParams + 1]
+ assert len(names) == fd.cParams + 1
+ flags = self.func_flags(fd.wFuncFlags)
+ flags += self.inv_kind(fd.invkind)
+ mth = typedesc.ComMethod(fd.invkind, fd.memid, func_name, returns, flags, func_doc)
+ mth.oVft = fd.oVft
+ for p in range(fd.cParams):
+ typ = self.make_type(fd.lprgelemdescParam[p].tdesc, tinfo)
+ name = names[p+1]
+ flags = fd.lprgelemdescParam[p]._.paramdesc.wParamFlags
+ if flags & typeinfo.PARAMFLAG_FHASDEFAULT:
+ # XXX should be handled by VARIANT itself
+ var = fd.lprgelemdescParam[p]._.paramdesc.pparamdescex[0].varDefaultValue
+ default = var.value
+ else:
+ default = None
+ mth.add_argument(typ, name, self.param_flags(flags), default)
+ members.append((fd.oVft, mth))
+ # Sort the methods by oVft (VTable offset): Some typeinfo
+ # don't list methods in VTable order.
+ members.sort()
+ itf.members.extend([m[1] for m in members])
+
+ return itf
+
+ # TKIND_DISPATCH = 4
+ def ParseDispatch(self, tinfo, ta):
+ itf_name, doc = tinfo.GetDocumentation(-1)[0:2]
+ assert ta.cImplTypes == 1
+
+ hr = tinfo.GetRefTypeOfImplType(0)
+ tibase = tinfo.GetRefTypeInfo(hr)
+ base = self.parse_typeinfo(tibase)
+ members = []
+ itf = typedesc.DispInterface(itf_name,
+ members=members,
+ base=base,
+ iid=str(ta.guid),
+ idlflags=self.interface_type_flags(ta.wTypeFlags))
+ if doc is not None:
+ itf.doc = str(doc.split("\0")[0])
+ self._register(itf_name, itf)
+
+ # This code can only handle pure dispinterfaces. Dual
+ # interfaces are parsed in ParseInterface().
+ assert ta.wTypeFlags & typeinfo.TYPEFLAG_FDUAL == 0
+
+ for i in range(ta.cVars):
+ vd = tinfo.GetVarDesc(i)
+ assert vd.varkind == typeinfo.VAR_DISPATCH
+ var_name, var_doc = tinfo.GetDocumentation(vd.memid)[0:2]
+ typ = self.make_type(vd.elemdescVar.tdesc, tinfo)
+ mth = typedesc.DispProperty(vd.memid, var_name, typ, self.var_flags(vd.wVarFlags), var_doc)
+ itf.members.append(mth)
+
+ # At least the EXCEL typelib lists the IUnknown and IDispatch
+ # methods even for this kind of interface. I didn't find any
+ # indication about these methods in the various flags, so we
+ # have to exclude them by name.
+ # CLF: 12/14/2012 Do this in a way that does not exclude other methods.
+ # I have encountered typlibs where only "QueryInterface", "AddRef"
+ # and "Release" are to be skipped.
+ ignored_names = set(["QueryInterface", "AddRef", "Release",
+ "GetTypeInfoCount", "GetTypeInfo",
+ "GetIDsOfNames", "Invoke"])
+
+ for i in range(ta.cFuncs):
+ fd = tinfo.GetFuncDesc(i)
+ func_name, func_doc = tinfo.GetDocumentation(fd.memid)[:2]
+ if func_name in ignored_names:
+ continue
+ assert fd.funckind == typeinfo.FUNC_DISPATCH
+
+ returns = self.make_type(fd.elemdescFunc.tdesc, tinfo)
+ names = tinfo.GetNames(fd.memid, fd.cParams+1)
+ names.append("rhs")
+ names = names[:fd.cParams + 1]
+ assert len(names) == fd.cParams + 1 # function name first, then parameter names
+ flags = self.func_flags(fd.wFuncFlags)
+ flags += self.inv_kind(fd.invkind)
+ mth = typedesc.DispMethod(fd.memid, fd.invkind, func_name, returns, flags, func_doc)
+ for p in range(fd.cParams):
+ typ = self.make_type(fd.lprgelemdescParam[p].tdesc, tinfo)
+ name = names[p+1]
+ flags = fd.lprgelemdescParam[p]._.paramdesc.wParamFlags
+ if flags & typeinfo.PARAMFLAG_FHASDEFAULT:
+ var = fd.lprgelemdescParam[p]._.paramdesc.pparamdescex[0].varDefaultValue
+ default = var.value
+ else:
+ default = None
+ mth.add_argument(typ, name, self.param_flags(flags), default)
+ itf.members.append(mth)
+
+ return itf
+
+ def inv_kind(self, invkind):
+ NAMES = {automation.DISPATCH_METHOD: [],
+ automation.DISPATCH_PROPERTYPUT: ["propput"],
+ automation.DISPATCH_PROPERTYPUTREF: ["propputref"],
+ automation.DISPATCH_PROPERTYGET: ["propget"]}
+ return NAMES[invkind]
+
+ def func_flags(self, flags):
+ # map FUNCFLAGS values to idl attributes
+ NAMES = {typeinfo.FUNCFLAG_FRESTRICTED: "restricted",
+ typeinfo.FUNCFLAG_FSOURCE: "source",
+ typeinfo.FUNCFLAG_FBINDABLE: "bindable",
+ typeinfo.FUNCFLAG_FREQUESTEDIT: "requestedit",
+ typeinfo.FUNCFLAG_FDISPLAYBIND: "displaybind",
+ typeinfo.FUNCFLAG_FDEFAULTBIND: "defaultbind",
+ typeinfo.FUNCFLAG_FHIDDEN: "hidden",
+ typeinfo.FUNCFLAG_FUSESGETLASTERROR: "usesgetlasterror",
+ typeinfo.FUNCFLAG_FDEFAULTCOLLELEM: "defaultcollelem",
+ typeinfo.FUNCFLAG_FUIDEFAULT: "uidefault",
+ typeinfo.FUNCFLAG_FNONBROWSABLE: "nonbrowsable",
+ # typeinfo.FUNCFLAG_FREPLACEABLE: "???",
+ typeinfo.FUNCFLAG_FIMMEDIATEBIND: "immediatebind"}
+ return [NAMES[bit] for bit in NAMES if bit & flags]
+
+ def param_flags(self, flags):
+ # map PARAMFLAGS values to idl attributes
+ NAMES = {typeinfo.PARAMFLAG_FIN: "in",
+ typeinfo.PARAMFLAG_FOUT: "out",
+ typeinfo.PARAMFLAG_FLCID: "lcid",
+ typeinfo.PARAMFLAG_FRETVAL: "retval",
+ typeinfo.PARAMFLAG_FOPT: "optional",
+ # typeinfo.PARAMFLAG_FHASDEFAULT: "",
+ # typeinfo.PARAMFLAG_FHASCUSTDATA: "",
+ }
+ return [NAMES[bit] for bit in NAMES if bit & flags]
+
+ def coclass_type_flags(self, flags):
+ # map TYPEFLAGS values to idl attributes
+ NAMES = {typeinfo.TYPEFLAG_FAPPOBJECT: "appobject",
+ # typeinfo.TYPEFLAG_FCANCREATE:
+ typeinfo.TYPEFLAG_FLICENSED: "licensed",
+ # typeinfo.TYPEFLAG_FPREDECLID:
+ typeinfo.TYPEFLAG_FHIDDEN: "hidden",
+ typeinfo.TYPEFLAG_FCONTROL: "control",
+ typeinfo.TYPEFLAG_FDUAL: "dual",
+ typeinfo.TYPEFLAG_FNONEXTENSIBLE: "nonextensible",
+ typeinfo.TYPEFLAG_FOLEAUTOMATION: "oleautomation",
+ typeinfo.TYPEFLAG_FRESTRICTED: "restricted",
+ typeinfo.TYPEFLAG_FAGGREGATABLE: "aggregatable",
+ # typeinfo.TYPEFLAG_FREPLACEABLE:
+ # typeinfo.TYPEFLAG_FDISPATCHABLE # computed, no flag for this
+ typeinfo.TYPEFLAG_FREVERSEBIND: "reversebind",
+ typeinfo.TYPEFLAG_FPROXY: "proxy",
+ }
+ NEGATIVE_NAMES = {typeinfo.TYPEFLAG_FCANCREATE: "noncreatable"}
+ return [NAMES[bit] for bit in NAMES if bit & flags] + \
+ [NEGATIVE_NAMES[bit] for bit in NEGATIVE_NAMES if not (bit & flags)]
+
+ def interface_type_flags(self, flags):
+ # map TYPEFLAGS values to idl attributes
+ NAMES = {typeinfo.TYPEFLAG_FAPPOBJECT: "appobject",
+ # typeinfo.TYPEFLAG_FCANCREATE:
+ typeinfo.TYPEFLAG_FLICENSED: "licensed",
+ # typeinfo.TYPEFLAG_FPREDECLID:
+ typeinfo.TYPEFLAG_FHIDDEN: "hidden",
+ typeinfo.TYPEFLAG_FCONTROL: "control",
+ typeinfo.TYPEFLAG_FDUAL: "dual",
+ typeinfo.TYPEFLAG_FNONEXTENSIBLE: "nonextensible",
+ typeinfo.TYPEFLAG_FOLEAUTOMATION: "oleautomation",
+ typeinfo.TYPEFLAG_FRESTRICTED: "restricted",
+ typeinfo.TYPEFLAG_FAGGREGATABLE: "aggregatable",
+ # typeinfo.TYPEFLAG_FREPLACEABLE:
+ # typeinfo.TYPEFLAG_FDISPATCHABLE # computed, no flag for this
+ typeinfo.TYPEFLAG_FREVERSEBIND: "reversebind",
+ typeinfo.TYPEFLAG_FPROXY: "proxy",
+ }
+ NEGATIVE_NAMES = {}
+ return [NAMES[bit] for bit in NAMES if bit & flags] + \
+ [NEGATIVE_NAMES[bit] for bit in NEGATIVE_NAMES if not (bit & flags)]
+
+ def var_flags(self, flags):
+ NAMES = {typeinfo.VARFLAG_FREADONLY: "readonly",
+ typeinfo.VARFLAG_FSOURCE: "source",
+ typeinfo.VARFLAG_FBINDABLE: "bindable",
+ typeinfo.VARFLAG_FREQUESTEDIT: "requestedit",
+ typeinfo.VARFLAG_FDISPLAYBIND: "displaybind",
+ typeinfo.VARFLAG_FDEFAULTBIND: "defaultbind",
+ typeinfo.VARFLAG_FHIDDEN: "hidden",
+ typeinfo.VARFLAG_FRESTRICTED: "restricted",
+ typeinfo.VARFLAG_FDEFAULTCOLLELEM: "defaultcollelem",
+ typeinfo.VARFLAG_FUIDEFAULT: "uidefault",
+ typeinfo.VARFLAG_FNONBROWSABLE: "nonbrowsable",
+ typeinfo.VARFLAG_FREPLACEABLE: "replaceable",
+ typeinfo.VARFLAG_FIMMEDIATEBIND: "immediatebind"
+ }
+ return [NAMES[bit] for bit in NAMES if bit & flags]
+
+
+ # TKIND_COCLASS = 5
+ def ParseCoClass(self, tinfo, ta):
+ # possible ta.wTypeFlags: helpstring, helpcontext, licensed,
+ # version, control, hidden, and appobject
+ coclass_name, doc = tinfo.GetDocumentation(-1)[0:2]
+ tlibattr = tinfo.GetContainingTypeLib()[0].GetLibAttr()
+ coclass = typedesc.CoClass(coclass_name,
+ str(ta.guid),
+ self.coclass_type_flags(ta.wTypeFlags),
+ tlibattr)
+ if doc is not None:
+ coclass.doc = doc
+ self._register(coclass_name, coclass)
+
+ for i in range(ta.cImplTypes):
+ hr = tinfo.GetRefTypeOfImplType(i)
+ ti = tinfo.GetRefTypeInfo(hr)
+ itf = self.parse_typeinfo(ti)
+ flags = tinfo.GetImplTypeFlags(i)
+ coclass.add_interface(itf, flags)
+ return coclass
+
+ # TKIND_ALIAS = 6
+ def ParseAlias(self, tinfo, ta):
+ name = tinfo.GetDocumentation(-1)[0]
+ typ = self.make_type(ta.tdescAlias, tinfo)
+ alias = typedesc.Typedef(name, typ)
+ self._register(name, alias)
+ return alias
+
+ # TKIND_UNION = 7
+ def ParseUnion(self, tinfo, ta):
+ union_name, doc, helpcntext, helpfile = tinfo.GetDocumentation(-1)
+ members = []
+ union = typedesc.Union(union_name,
+ align=ta.cbAlignment*8,
+ members=members,
+ bases=[],
+ size=ta.cbSizeInstance*8)
+ self._register(union_name, union)
+
+ tlib, _ = tinfo.GetContainingTypeLib()
+ tlib_ta = tlib.GetLibAttr()
+ # If this is a 32-bit typlib being loaded in a 64-bit process, then the
+ # size and alignment are incorrect. Set the size to None to disable
+ # size checks and correct the alignment.
+ if is_64bits and tlib_ta.syskind == typeinfo.SYS_WIN32:
+ union.size = None
+ union.align = 64
+
+ for i in range(ta.cVars):
+ vd = tinfo.GetVarDesc(i)
+ name = tinfo.GetDocumentation(vd.memid)[0]
+ offset = vd._.oInst * 8
+ assert vd.varkind == typeinfo.VAR_PERINSTANCE
+ typ = self.make_type(vd.elemdescVar.tdesc, tinfo)
+ field = typedesc.Field(name,
+ typ,
+ None, # bits
+ offset)
+ members.append(field)
+ return union
+
+ ################################################################
+
+ def _typelib_module(self, tlib=None):
+ if tlib is None:
+ tlib = self.tlib
+ # return a string that uniquely identifies a typelib.
+ # The string doesn't have any meaning outside this instance.
+ return str(tlib.GetLibAttr())
+
+ def _register(self, name, value, tlib=None):
+ modname = self._typelib_module(tlib)
+ fullname = "%s.%s" % (modname, name)
+ if fullname in self.items:
+ # XXX Can we really allow this? It happens, at least.
+ 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))
+ self.items[fullname] = value
+
+ def parse_typeinfo(self, tinfo):
+ name = tinfo.GetDocumentation(-1)[0]
+ modname = self._typelib_module()
+ try:
+ return self.items["%s.%s" % (modname, name)]
+ except KeyError:
+ pass
+
+ tlib = tinfo.GetContainingTypeLib()[0]
+ if tlib != self.tlib:
+ ta = tinfo.GetTypeAttr()
+ size = ta.cbSizeInstance * 8
+ align = ta.cbAlignment * 8
+ typ = typedesc.External(tlib,
+ name,
+ size,
+ align,
+ tlib.GetDocumentation(-1)[:2])
+ self._register(name, typ, tlib)
+ return typ
+
+ ta = tinfo.GetTypeAttr()
+ tkind = ta.typekind
+
+ if tkind == typeinfo.TKIND_ENUM: # 0
+ return self.ParseEnum(tinfo, ta)
+ elif tkind == typeinfo.TKIND_RECORD: # 1
+ return self.ParseRecord(tinfo, ta)
+ elif tkind == typeinfo.TKIND_MODULE: # 2
+ return self.ParseModule(tinfo, ta)
+ elif tkind == typeinfo.TKIND_INTERFACE: # 3
+ return self.ParseInterface(tinfo, ta)
+ elif tkind == typeinfo.TKIND_DISPATCH: # 4
+ try:
+ # GetRefTypeOfImplType(-1) returns the custom portion
+ # of a dispinterface, if it is dual
+ href = tinfo.GetRefTypeOfImplType(-1)
+ except COMError:
+ # no dual interface
+ return self.ParseDispatch(tinfo, ta)
+ tinfo = tinfo.GetRefTypeInfo(href)
+ ta = tinfo.GetTypeAttr()
+ assert ta.typekind == typeinfo.TKIND_INTERFACE
+ return self.ParseInterface(tinfo, ta)
+ elif tkind == typeinfo.TKIND_COCLASS: # 5
+ return self.ParseCoClass(tinfo, ta)
+ elif tkind == typeinfo.TKIND_ALIAS: # 6
+ return self.ParseAlias(tinfo, ta)
+ elif tkind == typeinfo.TKIND_UNION: # 7
+ return self.ParseUnion(tinfo, ta)
+ else:
+ print "NYI", tkind
+## raise "NYI", tkind
+
+ def parse_LibraryDescription(self):
+ la = self.tlib.GetLibAttr()
+ name, doc = self.tlib.GetDocumentation(-1)[:2]
+ desc = typedesc.TypeLib(name,
+ str(la.guid), la.wMajorVerNum, la.wMinorVerNum,
+ doc)
+ self._register(None, desc)
+
+ ################################################################
+
+ def parse(self):
+ self.parse_LibraryDescription()
+
+ for i in range(self.tlib.GetTypeInfoCount()):
+ tinfo = self.tlib.GetTypeInfo(i)
+ self.parse_typeinfo(tinfo)
+ return self.items
+
+class TlbFileParser(Parser):
+ "Parses a type library from a file"
+ def __init__(self, path):
+ # XXX DOESN'T LOOK CORRECT: We should NOT register the typelib.
+ self.tlib = typeinfo.LoadTypeLibEx(path)#, regkind=typeinfo.REGKIND_REGISTER)
+ self.items = {}
+
+class TypeLibParser(Parser):
+ def __init__(self, tlib):
+ self.tlib = tlib
+ self.items = {}
+
+################################################################
+# some interesting typelibs
+
+## these do NOT work:
+ # XXX infinite loop?
+## path = r"mshtml.tlb" # has propputref
+
+ # has SAFEARRAY
+ # HRESULT Run(BSTR, SAFEARRAY(VARIANT)*, VARIANT*)
+## path = "msscript.ocx"
+
+ # has SAFEARRAY
+ # HRESULT AddAddress(SAFEARRAY(BSTR)*, SAFEARRAY(BSTR)*)
+## path = r"c:\Programme\Microsoft Office\Office\MSWORD8.OLB" # has propputref
+
+ # has SAFEARRAY:
+ # SAFEARRAY(unsigned char) FileSignatureInfo(BSTR, long, MsiSignatureInfo)
+## path = r"msi.dll" # DispProperty
+
+ # fails packing IDLDESC
+## path = r"C:\Dokumente und Einstellungen\thomas\Desktop\tlb\win.tlb"
+ # fails packing WIN32_FIND_DATA
+## path = r"C:\Dokumente und Einstellungen\thomas\Desktop\tlb\win32.tlb"
+ # has a POINTER(IUnknown) as default parameter value
+## path = r"c:\Programme\Gemeinsame Dateien\Microsoft Shared\Speech\sapi.dll"
+
+
+## path = r"hnetcfg.dll"
+## path = r"simpdata.tlb"
+## path = r"nscompat.tlb"
+## path = r"stdole32.tlb"
+
+## path = r"shdocvw.dll"
+
+## path = r"c:\Programme\Microsoft Office\Office\MSO97.DLL"
+## path = r"PICCLP32.OCX" # DispProperty
+## path = r"MSHFLXGD.OCX" # DispProperty, propputref
+## path = r"scrrun.dll" # propput AND propputref on IDictionary::Item
+## path = r"C:\Dokumente und Einstellungen\thomas\Desktop\tlb\threadapi.tlb"
+
+## path = r"..\samples\BITS\bits2_0.tlb"
+
+## path = r"c:\vc98\include\activscp.tlb"
+
+def get_tlib_filename(tlib):
+ # seems if the typelib is not registered, there's no way to
+ # determine the filename.
+ from ctypes import windll, byref
+ from comtypes import BSTR
+ la = tlib.GetLibAttr()
+ name = BSTR()
+ try:
+ windll.oleaut32.QueryPathOfRegTypeLib
+ except AttributeError:
+ # Windows CE doesn't have this function
+ return None
+ if 0 == windll.oleaut32.QueryPathOfRegTypeLib(byref(la.guid),
+ la.wMajorVerNum,
+ la.wMinorVerNum,
+ 0, # lcid
+ byref(name)
+ ):
+ return name.value.split("\0")[0]
+ return None
+
+def _py2exe_hint():
+ # If the tlbparser is frozen, we need to include these
+ import comtypes.persist
+ import comtypes.typeinfo
+ import comtypes.automation
+
+def generate_module(tlib, ofi, pathname):
+ known_symbols = {}
+ for name in ("comtypes.persist",
+ "comtypes.typeinfo",
+ "comtypes.automation",
+ "comtypes._others",
+ "comtypes",
+ "ctypes.wintypes",
+ "ctypes"):
+ try:
+ mod = __import__(name)
+ except ImportError:
+ if name == "comtypes._others":
+ continue
+ raise
+ for submodule in name.split(".")[1:]:
+ mod = getattr(mod, submodule)
+ for name in mod.__dict__:
+ known_symbols[name] = mod.__name__
+ p = TypeLibParser(tlib)
+ if pathname is None:
+ pathname = get_tlib_filename(tlib)
+ items = p.parse()
+
+ from codegenerator import Generator
+
+ gen = Generator(ofi,
+ known_symbols=known_symbols,
+ )
+
+ gen.generate_code(items.values(), filename=pathname)
+
+# -eof-
diff --git a/tools/comtypes/comtypes/tools/typedesc.py b/tools/comtypes/comtypes/tools/typedesc.py
new file mode 100644
index 00000000000000..9665b62f33472a
--- /dev/null
+++ b/tools/comtypes/comtypes/tools/typedesc.py
@@ -0,0 +1,138 @@
+# More type descriptions from parsed COM typelibaries, extending those
+# in typedesc_base
+
+import ctypes
+from comtypes.tools.typedesc_base import *
+
+class TypeLib(object):
+ def __init__(self, name, guid, major, minor, doc=None):
+ self.name = name
+ self.guid = guid
+ self.major = major
+ self.minor = minor
+ self.doc = doc
+
+ def __repr__(self):
+ return "" % (self.name, self.guid, self.major, self.minor)
+
+class Constant(object):
+ def __init__(self, name, typ, value):
+ self.name = name
+ self.typ = typ
+ self.value = value
+
+class External(object):
+ def __init__(self, tlib, name, size, align, docs=None):
+ # the type library containing the symbol
+ self.tlib = tlib
+ # name of symbol
+ self.symbol_name = name
+ self.size = size
+ self.align = align
+ # type lib description
+ self.docs = docs
+
+ def get_head(self):
+ # codegen might call this
+ return self
+
+class SAFEARRAYType(object):
+ def __init__(self, typ):
+ self.typ = typ
+ self.align = self.size = ctypes.sizeof(ctypes.c_void_p) * 8
+
+class ComMethod(object):
+ # custom COM method, parsed from typelib
+ def __init__(self, invkind, memid, name, returns, idlflags, doc):
+ self.invkind = invkind
+ self.name = name
+ self.returns = returns
+ self.idlflags = idlflags
+ self.memid = memid
+ self.doc = doc
+ self.arguments = []
+
+ def add_argument(self, typ, name, idlflags, default):
+ self.arguments.append((typ, name, idlflags, default))
+
+class DispMethod(object):
+ # dispatchable COM method, parsed from typelib
+ def __init__(self, dispid, invkind, name, returns, idlflags, doc):
+ self.dispid = dispid
+ self.invkind = invkind
+ self.name = name
+ self.returns = returns
+ self.idlflags = idlflags
+ self.doc = doc
+ self.arguments = []
+
+ def add_argument(self, typ, name, idlflags, default):
+ self.arguments.append((typ, name, idlflags, default))
+
+class DispProperty(object):
+ # dispatchable COM property, parsed from typelib
+ def __init__(self, dispid, name, typ, idlflags, doc):
+ self.dispid = dispid
+ self.name = name
+ self.typ = typ
+ self.idlflags = idlflags
+ self.doc = doc
+
+class DispInterfaceHead(object):
+ def __init__(self, itf):
+ self.itf = itf
+
+class DispInterfaceBody(object):
+ def __init__(self, itf):
+ self.itf = itf
+
+class DispInterface(object):
+ def __init__(self, name, members, base, iid, idlflags):
+ self.name = name
+ self.members = members
+ self.base = base
+ self.iid = iid
+ self.idlflags = idlflags
+ self.itf_head = DispInterfaceHead(self)
+ self.itf_body = DispInterfaceBody(self)
+
+ def get_body(self):
+ return self.itf_body
+
+ def get_head(self):
+ return self.itf_head
+
+class ComInterfaceHead(object):
+ def __init__(self, itf):
+ self.itf = itf
+
+class ComInterfaceBody(object):
+ def __init__(self, itf):
+ self.itf = itf
+
+class ComInterface(object):
+ def __init__(self, name, members, base, iid, idlflags):
+ self.name = name
+ self.members = members
+ self.base = base
+ self.iid = iid
+ self.idlflags = idlflags
+ self.itf_head = ComInterfaceHead(self)
+ self.itf_body = ComInterfaceBody(self)
+
+ def get_body(self):
+ return self.itf_body
+
+ def get_head(self):
+ return self.itf_head
+
+class CoClass(object):
+ def __init__(self, name, clsid, idlflags, tlibattr):
+ self.name = name
+ self.clsid = clsid
+ self.idlflags = idlflags
+ self.tlibattr = tlibattr
+ self.interfaces = []
+
+ def add_interface(self, itf, idlflags):
+ self.interfaces.append((itf, idlflags))
diff --git a/tools/comtypes/comtypes/tools/typedesc_base.py b/tools/comtypes/comtypes/tools/typedesc_base.py
new file mode 100644
index 00000000000000..534de91e12d18a
--- /dev/null
+++ b/tools/comtypes/comtypes/tools/typedesc_base.py
@@ -0,0 +1,205 @@
+# typedesc.py - classes representing C type descriptions
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+class Argument(object):
+ "a Parameter in the argument list of a callable (Function, Method, ...)"
+ def __init__(self, atype, name):
+ self.atype = atype
+ self.name = name
+
+class _HasArgs(object):
+
+ def __init__(self):
+ self.arguments = []
+
+ def add_argument(self, arg):
+ assert isinstance(arg, Argument)
+ self.arguments.append(arg)
+
+ def iterArgTypes(self):
+ for a in self.arguments:
+ yield a.atype
+
+ def iterArgNames(self):
+ for a in self.arguments:
+ yield a.name
+
+ def fixup_argtypes(self, typemap):
+ for a in self.arguments:
+ a.atype = typemap[a.atype]
+
+
+################
+
+class Alias(object):
+ # a C preprocessor alias, like #define A B
+ def __init__(self, name, alias, typ=None):
+ self.name = name
+ self.alias = alias
+ self.typ = typ
+
+class Macro(object):
+ # a C preprocessor definition with arguments
+ def __init__(self, name, args, body):
+ # all arguments are strings, args is the literal argument list
+ # *with* the parens around it:
+ # Example: Macro("CD_INDRIVE", "(status)", "((int)status > 0)")
+ self.name = name
+ self.args = args
+ self.body = body
+
+class File(object):
+ def __init__(self, name):
+ self.name = name
+
+class Function(_HasArgs):
+ location = None
+ def __init__(self, name, returns, attributes, extern):
+ _HasArgs.__init__(self)
+ self.name = name
+ self.returns = returns
+ self.attributes = attributes # dllimport, __stdcall__, __cdecl__
+ self.extern = extern
+
+class Constructor(_HasArgs):
+ location = None
+ def __init__(self, name):
+ _HasArgs.__init__(self)
+ self.name = name
+
+class OperatorFunction(_HasArgs):
+ location = None
+ def __init__(self, name, returns):
+ _HasArgs.__init__(self)
+ self.name = name
+ self.returns = returns
+
+class FunctionType(_HasArgs):
+ location = None
+ def __init__(self, returns, attributes):
+ _HasArgs.__init__(self)
+ self.returns = returns
+ self.attributes = attributes
+
+class Method(_HasArgs):
+ location = None
+ def __init__(self, name, returns):
+ _HasArgs.__init__(self)
+ self.name = name
+ self.returns = returns
+
+class FundamentalType(object):
+ location = None
+ def __init__(self, name, size, align):
+ self.name = name
+ if name != "void":
+ self.size = int(size)
+ self.align = int(align)
+
+class PointerType(object):
+ location = None
+ def __init__(self, typ, size, align):
+ self.typ = typ
+ self.size = int(size)
+ self.align = int(align)
+
+class Typedef(object):
+ location = None
+ def __init__(self, name, typ):
+ self.name = name
+ self.typ = typ
+
+class ArrayType(object):
+ location = None
+ def __init__(self, typ, min, max):
+ self.typ = typ
+ self.min = min
+ self.max = max
+
+class StructureHead(object):
+ location = None
+ def __init__(self, struct):
+ self.struct = struct
+
+class StructureBody(object):
+ location = None
+ def __init__(self, struct):
+ self.struct = struct
+
+class _Struct_Union_Base(object):
+ location = None
+ def get_body(self):
+ return self.struct_body
+
+ def get_head(self):
+ return self.struct_head
+
+class Structure(_Struct_Union_Base):
+ def __init__(self, name, align, members, bases, size, artificial=None):
+ self.name = name
+ self.align = int(align)
+ self.members = members
+ self.bases = bases
+ self.artificial = artificial
+ if size is not None:
+ self.size = int(size)
+ else:
+ self.size = None
+ self.struct_body = StructureBody(self)
+ self.struct_head = StructureHead(self)
+
+class Union(_Struct_Union_Base):
+ def __init__(self, name, align, members, bases, size, artificial=None):
+ self.name = name
+ self.align = int(align)
+ self.members = members
+ self.bases = bases
+ self.artificial = artificial
+ if size is not None:
+ self.size = int(size)
+ else:
+ self.size = None
+ self.struct_body = StructureBody(self)
+ self.struct_head = StructureHead(self)
+
+class Field(object):
+ def __init__(self, name, typ, bits, offset):
+ self.name = name
+ self.typ = typ
+ self.bits = bits
+ self.offset = int(offset)
+
+class CvQualifiedType(object):
+ def __init__(self, typ, const, volatile):
+ self.typ = typ
+ self.const = const
+ self.volatile = volatile
+
+class Enumeration(object):
+ location = None
+ def __init__(self, name, size, align):
+ self.name = name
+ self.size = int(size)
+ self.align = int(align)
+ self.values = []
+
+ def add_value(self, v):
+ self.values.append(v)
+
+class EnumValue(object):
+ def __init__(self, name, value, enumeration):
+ self.name = name
+ self.value = value
+ self.enumeration = enumeration
+
+class Variable(object):
+ location = None
+ def __init__(self, name, typ, init=None):
+ self.name = name
+ self.typ = typ
+ self.init = init
+
+################################################################
diff --git a/tools/comtypes/comtypes/typeinfo.py b/tools/comtypes/comtypes/typeinfo.py
new file mode 100644
index 00000000000000..f72698df298256
--- /dev/null
+++ b/tools/comtypes/comtypes/typeinfo.py
@@ -0,0 +1,913 @@
+# XXX Should convert from STDMETHOD to COMMETHOD.
+
+# generated by 'xml2py'
+# flags '..\tools\windows.xml -m comtypes -m comtypes.automation -w -r .*TypeLibEx -r .*TypeLib -o typeinfo.py'
+# then hacked manually
+import os
+import sys
+import weakref
+
+from ctypes import *
+from ctypes.wintypes import ULONG
+from comtypes import STDMETHOD
+from comtypes import COMMETHOD
+from comtypes import _GUID, GUID
+# XXX should import more stuff from ctypes.wintypes...
+from comtypes.automation import BSTR
+from comtypes.automation import DISPID
+from comtypes.automation import DISPPARAMS
+from comtypes.automation import DWORD
+from comtypes.automation import EXCEPINFO
+from comtypes.automation import HRESULT
+from comtypes.automation import IID
+from comtypes.automation import IUnknown
+from comtypes.automation import LCID
+from comtypes.automation import LONG
+from comtypes.automation import SCODE
+from comtypes.automation import UINT
+from comtypes.automation import VARIANT
+from comtypes.automation import VARIANTARG
+from comtypes.automation import VARTYPE
+from comtypes.automation import WCHAR
+from comtypes.automation import WORD
+from comtypes.automation import tagVARIANT
+
+is_64_bit = sys.maxsize > 2**32
+
+BOOL = c_int
+HREFTYPE = DWORD
+INT = c_int
+MEMBERID = DISPID
+OLECHAR = WCHAR
+PVOID = c_void_p
+SHORT = c_short
+# See https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx#ULONG_PTR # noqa
+ULONG_PTR = c_uint64 if is_64_bit else c_ulong
+
+USHORT = c_ushort
+LPOLESTR = POINTER(OLECHAR)
+
+################################################################
+# enums
+tagSYSKIND = c_int # enum
+SYS_WIN16 = 0
+SYS_WIN32 = 1
+SYS_MAC = 2
+SYS_WIN64 = 3
+SYSKIND = tagSYSKIND
+
+tagREGKIND = c_int # enum
+REGKIND_DEFAULT = 0
+REGKIND_REGISTER = 1
+REGKIND_NONE = 2
+REGKIND = tagREGKIND
+
+tagTYPEKIND = c_int # enum
+TKIND_ENUM = 0
+TKIND_RECORD = 1
+TKIND_MODULE = 2
+TKIND_INTERFACE = 3
+TKIND_DISPATCH = 4
+TKIND_COCLASS = 5
+TKIND_ALIAS = 6
+TKIND_UNION = 7
+TKIND_MAX = 8
+TYPEKIND = tagTYPEKIND
+
+tagINVOKEKIND = c_int # enum
+INVOKE_FUNC = 1
+INVOKE_PROPERTYGET = 2
+INVOKE_PROPERTYPUT = 4
+INVOKE_PROPERTYPUTREF = 8
+INVOKEKIND = tagINVOKEKIND
+
+tagDESCKIND = c_int # enum
+DESCKIND_NONE = 0
+DESCKIND_FUNCDESC = 1
+DESCKIND_VARDESC = 2
+DESCKIND_TYPECOMP = 3
+DESCKIND_IMPLICITAPPOBJ = 4
+DESCKIND_MAX = 5
+DESCKIND = tagDESCKIND
+
+tagVARKIND = c_int # enum
+VAR_PERINSTANCE = 0
+VAR_STATIC = 1
+VAR_CONST = 2
+VAR_DISPATCH = 3
+VARKIND = tagVARKIND
+
+tagFUNCKIND = c_int # enum
+FUNC_VIRTUAL = 0
+FUNC_PUREVIRTUAL = 1
+FUNC_NONVIRTUAL = 2
+FUNC_STATIC = 3
+FUNC_DISPATCH = 4
+FUNCKIND = tagFUNCKIND
+
+tagCALLCONV = c_int # enum
+CC_FASTCALL = 0
+CC_CDECL = 1
+CC_MSCPASCAL = 2
+CC_PASCAL = 2
+CC_MACPASCAL = 3
+CC_STDCALL = 4
+CC_FPFASTCALL = 5
+CC_SYSCALL = 6
+CC_MPWCDECL = 7
+CC_MPWPASCAL = 8
+CC_MAX = 9
+CALLCONV = tagCALLCONV
+
+IMPLTYPEFLAG_FDEFAULT = 1
+IMPLTYPEFLAG_FSOURCE = 2
+IMPLTYPEFLAG_FRESTRICTED = 4
+IMPLTYPEFLAG_FDEFAULTVTABLE = 8
+
+tagTYPEFLAGS = c_int # enum
+TYPEFLAG_FAPPOBJECT = 1
+TYPEFLAG_FCANCREATE = 2
+TYPEFLAG_FLICENSED = 4
+TYPEFLAG_FPREDECLID = 8
+TYPEFLAG_FHIDDEN = 16
+TYPEFLAG_FCONTROL = 32
+TYPEFLAG_FDUAL = 64
+TYPEFLAG_FNONEXTENSIBLE = 128
+TYPEFLAG_FOLEAUTOMATION = 256
+TYPEFLAG_FRESTRICTED = 512
+TYPEFLAG_FAGGREGATABLE = 1024
+TYPEFLAG_FREPLACEABLE = 2048
+TYPEFLAG_FDISPATCHABLE = 4096
+TYPEFLAG_FREVERSEBIND = 8192
+TYPEFLAG_FPROXY = 16384
+TYPEFLAGS = tagTYPEFLAGS
+
+tagFUNCFLAGS = c_int # enum
+FUNCFLAG_FRESTRICTED = 1
+FUNCFLAG_FSOURCE = 2
+FUNCFLAG_FBINDABLE = 4
+FUNCFLAG_FREQUESTEDIT = 8
+FUNCFLAG_FDISPLAYBIND = 16
+FUNCFLAG_FDEFAULTBIND = 32
+FUNCFLAG_FHIDDEN = 64
+FUNCFLAG_FUSESGETLASTERROR = 128
+FUNCFLAG_FDEFAULTCOLLELEM = 256
+FUNCFLAG_FUIDEFAULT = 512
+FUNCFLAG_FNONBROWSABLE = 1024
+FUNCFLAG_FREPLACEABLE = 2048
+FUNCFLAG_FIMMEDIATEBIND = 4096
+FUNCFLAGS = tagFUNCFLAGS
+
+tagVARFLAGS = c_int # enum
+VARFLAG_FREADONLY = 1
+VARFLAG_FSOURCE = 2
+VARFLAG_FBINDABLE = 4
+VARFLAG_FREQUESTEDIT = 8
+VARFLAG_FDISPLAYBIND = 16
+VARFLAG_FDEFAULTBIND = 32
+VARFLAG_FHIDDEN = 64
+VARFLAG_FRESTRICTED = 128
+VARFLAG_FDEFAULTCOLLELEM = 256
+VARFLAG_FUIDEFAULT = 512
+VARFLAG_FNONBROWSABLE = 1024
+VARFLAG_FREPLACEABLE = 2048
+VARFLAG_FIMMEDIATEBIND = 4096
+VARFLAGS = tagVARFLAGS
+
+PARAMFLAG_NONE = 0
+PARAMFLAG_FIN = 1
+PARAMFLAG_FOUT = 2
+PARAMFLAG_FLCID = 4
+PARAMFLAG_FRETVAL = 8
+PARAMFLAG_FOPT = 16
+PARAMFLAG_FHASDEFAULT = 32
+PARAMFLAG_FHASCUSTDATA = 64
+
+################################################################
+# a helper
+def _deref_with_release(ptr, release):
+ # Given a POINTER instance, return the pointed to value.
+ # Call the 'release' function with 'ptr' to release resources
+ # when the value is no longer needed.
+ result = ptr[0]
+ result.__ref__ = weakref.ref(result, lambda dead: release(ptr))
+ return result
+
+# interfaces
+
+class ITypeLib(IUnknown):
+ _iid_ = GUID("{00020402-0000-0000-C000-000000000046}")
+
+ # Commented out methods use the default implementation that comtypes
+ # automatically creates for COM methods.
+
+## def GetTypeInfoCount(self):
+## "Return the number of type informations"
+
+## def GetTypeInfo(self, index):
+## "Load type info by index"
+
+## def GetTypeInfoType(self, index):
+## "Return the TYPEKIND of type information"
+
+## def GetTypeInfoOfGuid(self, guid):
+## "Return type information for a guid"
+
+ def GetLibAttr(self):
+ "Return type library attributes"
+ return _deref_with_release(self._GetLibAttr(), self.ReleaseTLibAttr)
+
+## def GetTypeComp(self):
+## "Return an ITypeComp pointer."
+
+## def GetDocumentation(self, index):
+## "Return documentation for a type description."
+
+ def IsName(self, name, lHashVal=0):
+ """Check if there is type information for this name.
+
+ Returns the name with capitalization found in the type
+ library, or None.
+ """
+ from ctypes import create_unicode_buffer
+ namebuf = create_unicode_buffer(name)
+ found = BOOL()
+ self.__com_IsName(namebuf, lHashVal, byref(found))
+ if found.value:
+ return namebuf[:].split("\0", 1)[0]
+ return None
+
+ def FindName(self, name, lHashVal=0):
+ # Hm...
+ # Could search for more than one name - should we support this?
+ found = c_ushort(1)
+ tinfo = POINTER(ITypeInfo)()
+ memid = MEMBERID()
+ self.__com_FindName(name, lHashVal, byref(tinfo), byref(memid), byref(found))
+ if found.value:
+ return memid.value, tinfo
+ return None
+
+## def ReleaseTLibAttr(self, ptla):
+## "Release TLIBATTR"
+
+################
+
+def fix_name(name):
+ # Some typelibs contain BSTR with embedded NUL characters,
+ # probably the len of the BSTR is wrong.
+ if name is None:
+ return name
+ return name.split("\0")[0]
+
+class ITypeInfo(IUnknown):
+ _iid_ = GUID("{00020401-0000-0000-C000-000000000046}")
+
+ def GetTypeAttr(self):
+ "Return the TYPEATTR for this type"
+ return _deref_with_release(self._GetTypeAttr(), self.ReleaseTypeAttr)
+
+## def GetTypeComp(self):
+## "Return ITypeComp pointer for this type"
+
+ def GetDocumentation(self, memid):
+ """Return name, docstring, helpcontext, and helpfile for 'memid'."""
+ name, doc, helpcontext, helpfile = self._GetDocumentation(memid)
+ return fix_name(name), fix_name(doc), helpcontext, fix_name(helpfile)
+
+ def GetFuncDesc(self, index):
+ "Return FUNCDESC for index"
+ return _deref_with_release(self._GetFuncDesc(index), self.ReleaseFuncDesc)
+
+ def GetVarDesc(self, index):
+ "Return VARDESC for index"
+ return _deref_with_release(self._GetVarDesc(index), self.ReleaseVarDesc)
+
+ def GetNames(self, memid, count=1):
+ "Return names for memid"
+ names = (BSTR * count)()
+ cnames = c_uint()
+ self.__com_GetNames(memid, names, count, byref(cnames))
+ return names[:cnames.value]
+
+## def GetRefTypeOfImplType(self, index):
+## "Get the reftype of an implemented type"
+
+## def GetImplTypeFlags(self, index):
+## "Get IMPLTYPEFLAGS"
+
+ def GetIDsOfNames(self, *names):
+ "Maps function and argument names to identifiers"
+ rgsznames = (c_wchar_p * len(names))(*names)
+ ids = (MEMBERID * len(names))()
+ self.__com_GetIDsOfNames(rgsznames, len(names), ids)
+ return ids[:]
+
+
+ # not yet wrapped
+## STDMETHOD(HRESULT, 'Invoke', [PVOID, MEMBERID, WORD, POINTER(DISPPARAMS), POINTER(VARIANT), POINTER(EXCEPINFO), POINTER(UINT)]),
+
+## def GetDllEntry(self, memid, invkind):
+## "Return the dll name, function name, and ordinal for a function and invkind."
+
+## def GetRefTypeInfo(self, href):
+## "Get type info for reftype"
+
+ def AddressOfMember(self, memid, invkind):
+ "Get the address of a function in a dll"
+ raise "Check Me"
+ p = c_void_p()
+ self.__com_AddressOfMember(memid, invkind, byref(p))
+ # XXX Would the default impl return the value of p?
+ return p.value
+
+ def CreateInstance(self, punkouter=None, interface=IUnknown, iid=None):
+ if iid is None:
+ iid = interface._iid_
+ return self._CreateInstance(punkouter, byref(interface._iid_))
+
+## def GetMops(self, index):
+## "Get marshalling opcodes (whatever that is...)"
+
+## def GetContainingTypeLib(self):
+## "Return index into and the containing type lib itself"
+
+## def ReleaseTypeAttr(self, pta):
+
+## def ReleaseFuncDesc(self, pfd):
+
+## def ReleaseVarDesc(self, pvd):
+
+################
+
+class ITypeComp(IUnknown):
+ _iid_ = GUID("{00020403-0000-0000-C000-000000000046}")
+
+ def Bind(self, name, flags=0, lHashVal=0):
+ "Bind to a name"
+ bindptr = BINDPTR()
+ desckind = DESCKIND()
+ ti = POINTER(ITypeInfo)()
+ self.__com_Bind(name, lHashVal, flags, byref(ti), byref(desckind), byref(bindptr))
+ kind = desckind.value
+ if kind == DESCKIND_FUNCDESC:
+ fd = bindptr.lpfuncdesc[0]
+ fd.__ref__ = weakref.ref(fd, lambda dead: ti.ReleaseFuncDesc(bindptr.lpfuncdesc))
+ return "function", fd
+ elif kind == DESCKIND_VARDESC:
+ vd = bindptr.lpvardesc[0]
+ vd.__ref__ = weakref.ref(vd, lambda dead: ti.ReleaseVarDesc(bindptr.lpvardesc))
+ return "variable", vd
+ elif kind == DESCKIND_TYPECOMP:
+ return "type", bindptr.lptcomp
+ elif kind == DESCKIND_IMPLICITAPPOBJ:
+ raise NotImplementedError
+ elif kind == DESCKIND_NONE:
+ 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."
+ ti = POINTER(ITypeInfo)()
+ tc = POINTER(ITypeComp)()
+ self.__com_BindType(name, lHashVal, byref(ti), byref(tc))
+ return ti, tc
+
+
+################
+
+class ICreateTypeLib(IUnknown):
+ _iid_ = GUID("{00020406-0000-0000-C000-000000000046}")
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 2149
+
+class ICreateTypeLib2(ICreateTypeLib):
+ _iid_ = GUID("{0002040F-0000-0000-C000-000000000046}")
+
+class ICreateTypeInfo(IUnknown):
+ _iid_ = GUID("{00020405-0000-0000-C000-000000000046}")
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 915
+
+ def SetFuncAndParamNames(self, index, *names):
+ rgszNames = (c_wchar_p * len(names))()
+ for i, n in enumerate(names):
+ rgszNames[i] = n
+ return self._SetFuncAndParamNames(index, rgszNames, len(names))
+
+class IRecordInfo(IUnknown):
+ # C:/vc98/include/OAIDL.H 5974
+ _iid_ = GUID("{0000002F-0000-0000-C000-000000000046}")
+
+ def GetFieldNames(self, *args):
+ count = c_ulong()
+ self.__com_GetFieldNames(count, None)
+ array = (BSTR * count.value)()
+ self.__com_GetFieldNames(count, array)
+ result = array[:]
+ # XXX Should SysFreeString the array contents. How to?
+ return result
+
+IRecordInfo. _methods_ = [
+ COMMETHOD([], HRESULT, 'RecordInit',
+ (['in'], c_void_p, 'pvNew')),
+ COMMETHOD([], HRESULT, 'RecordClear',
+ (['in'], c_void_p, 'pvExisting')),
+ COMMETHOD([], HRESULT, 'RecordCopy',
+ (['in'], c_void_p, 'pvExisting'),
+ (['in'], c_void_p, 'pvNew')),
+ COMMETHOD([], HRESULT, 'GetGuid',
+ (['out'], POINTER(GUID), 'pguid')),
+ COMMETHOD([], HRESULT, 'GetName',
+ (['out'], POINTER(BSTR), 'pbstrName')),
+ COMMETHOD([], HRESULT, 'GetSize',
+ (['out'], POINTER(c_ulong), 'pcbSize')),
+ COMMETHOD([], HRESULT, 'GetTypeInfo',
+ (['out'], POINTER(POINTER(ITypeInfo)), 'ppTypeInfo')),
+ COMMETHOD([], HRESULT, 'GetField',
+ (['in'], c_void_p, 'pvData'),
+ (['in'], c_wchar_p, 'szFieldName'),
+ (['out'], POINTER(VARIANT), 'pvarField')),
+ COMMETHOD([], HRESULT, 'GetFieldNoCopy',
+ (['in'], c_void_p, 'pvData'),
+ (['in'], c_wchar_p, 'szFieldName'),
+ (['out'], POINTER(VARIANT), 'pvarField'),
+ (['out'], POINTER(c_void_p), 'ppvDataCArray')),
+ COMMETHOD([], HRESULT, 'PutField',
+ (['in'], c_ulong, 'wFlags'),
+ (['in'], c_void_p, 'pvData'),
+ (['in'], c_wchar_p, 'szFieldName'),
+ (['in'], POINTER(VARIANT), 'pvarField')),
+ COMMETHOD([], HRESULT, 'PutFieldNoCopy',
+ (['in'], c_ulong, 'wFlags'),
+ (['in'], c_void_p, 'pvData'),
+ (['in'], c_wchar_p, 'szFieldName'),
+ (['in'], POINTER(VARIANT), 'pvarField')),
+ COMMETHOD([], HRESULT, 'GetFieldNames',
+ (['in', 'out'], POINTER(c_ulong), 'pcNames'),
+ (['in'], POINTER(BSTR), 'rgBstrNames')),
+ COMMETHOD([], BOOL, 'IsMatchingType',
+ (['in'], POINTER(IRecordInfo))),
+ COMMETHOD([], HRESULT, 'RecordCreate'),
+ COMMETHOD([], HRESULT, 'RecordCreateCopy',
+ (['in'], c_void_p, 'pvSource'),
+ (['out'], POINTER(c_void_p), 'ppvDest')),
+ COMMETHOD([], HRESULT, 'RecordDestroy',
+ (['in'], c_void_p, 'pvRecord'))]
+
+
+
+################################################################
+# functions
+_oleaut32 = oledll.oleaut32
+
+def GetRecordInfoFromTypeInfo(tinfo):
+ "Return an IRecordInfo pointer to the UDT described in tinfo"
+ ri = POINTER(IRecordInfo)()
+ _oleaut32.GetRecordInfoFromTypeInfo(tinfo, byref(ri))
+ return ri
+
+def GetRecordInfoFromGuids(rGuidTypeLib, verMajor, verMinor, lcid, rGuidTypeInfo):
+ ri = POINTER(IRecordInfo)()
+ _oleaut32.GetRecordInfoFromGuids(byref(GUID(rGuidTypeLib)),
+ verMajor, verMinor, lcid,
+ byref(GUID(rGuidTypeInfo)),
+ byref(ri))
+ return ri
+
+def LoadRegTypeLib(guid, wMajorVerNum, wMinorVerNum, lcid=0):
+ "Load a registered type library"
+ tlib = POINTER(ITypeLib)()
+ _oleaut32.LoadRegTypeLib(byref(GUID(guid)), wMajorVerNum, wMinorVerNum, lcid, byref(tlib))
+ return tlib
+
+if hasattr(_oleaut32, "LoadTypeLibEx"):
+ def LoadTypeLibEx(szFile, regkind=REGKIND_NONE):
+ "Load, and optionally register a type library file"
+ ptl = POINTER(ITypeLib)()
+ _oleaut32.LoadTypeLibEx(c_wchar_p(szFile), regkind, byref(ptl))
+ return ptl
+else:
+ def LoadTypeLibEx(szFile, regkind=REGKIND_NONE):
+ "Load, and optionally register a type library file"
+ ptl = POINTER(ITypeLib)()
+ _oleaut32.LoadTypeLib(c_wchar_p(szFile), byref(ptl))
+ return ptl
+
+def LoadTypeLib(szFile):
+ "Load and register a type library file"
+ tlib = POINTER(ITypeLib)()
+ _oleaut32.LoadTypeLib(c_wchar_p(szFile), byref(tlib))
+ return tlib
+
+def UnRegisterTypeLib(libID, wVerMajor, wVerMinor, lcid=0, syskind=SYS_WIN32):
+ "Unregister a registered type library"
+ return _oleaut32.UnRegisterTypeLib(byref(GUID(libID)), wVerMajor, wVerMinor, lcid, syskind)
+
+def RegisterTypeLib(tlib, fullpath, helpdir=None):
+ "Register a type library in the registry"
+ return _oleaut32.RegisterTypeLib(tlib, c_wchar_p(fullpath), c_wchar_p(helpdir))
+
+def CreateTypeLib(filename, syskind=SYS_WIN32):
+ "Return a ICreateTypeLib2 pointer"
+ ctlib = POINTER(ICreateTypeLib2)()
+ _oleaut32.CreateTypeLib2(syskind, c_wchar_p(filename), byref(ctlib))
+ return ctlib
+
+if os.name == "ce":
+ # See also:
+ # http://blogs.msdn.com/larryosterman/archive/2006/01/09/510856.aspx
+ #
+ # windows CE does not have QueryPathOfRegTypeLib. Emulate by reading the registry:
+ def QueryPathOfRegTypeLib(libid, wVerMajor, wVerMinor, lcid=0):
+ "Return the path of a registered type library"
+ import _winreg
+ try:
+ hkey = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, r"Typelib\%s\%s.%s\%x\win32" % (libid, wVerMajor, wVerMinor, lcid))
+ except WindowsError:
+ # On CE, some typelib names are not in the ..\win32 subkey:
+ hkey = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, r"Typelib\%s\%s.%s\%x" % (libid, wVerMajor, wVerMinor, lcid))
+ return _winreg.QueryValueEx(hkey, "")[0]
+else:
+ def QueryPathOfRegTypeLib(libid, wVerMajor, wVerMinor, lcid=0):
+ "Return the path of a registered type library"
+ pathname = BSTR()
+ _oleaut32.QueryPathOfRegTypeLib(byref(GUID(libid)), wVerMajor, wVerMinor, lcid, byref(pathname))
+ return pathname.value.split("\0")[0]
+
+################################################################
+# Structures
+
+class tagTLIBATTR(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 4437
+ def __repr__(self):
+ return "TLIBATTR(GUID=%s, Version=%s.%s, LCID=%s, FLags=0x%x)" % \
+ (self.guid, self.wMajorVerNum, self.wMinorVerNum, self.lcid, self.wLibFlags)
+TLIBATTR = tagTLIBATTR
+
+class tagTYPEATTR(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 672
+ def __repr__(self):
+ return "TYPEATTR(GUID=%s, typekind=%s, funcs=%s, vars=%s, impltypes=%s)" % \
+ (self.guid, self.typekind, self.cFuncs, self.cVars, self.cImplTypes)
+TYPEATTR = tagTYPEATTR
+
+class tagFUNCDESC(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 769
+ def __repr__(self):
+ return "FUNCDESC(memid=%s, cParams=%s, cParamsOpt=%s, callconv=%s, invkind=%s, funckind=%s)" % \
+ (self.memid, self.cParams, self.cParamsOpt, self.callconv, self.invkind, self.funckind)
+
+
+FUNCDESC = tagFUNCDESC
+class tagVARDESC(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 803
+ pass
+VARDESC = tagVARDESC
+
+class tagBINDPTR(Union):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 3075
+ pass
+BINDPTR = tagBINDPTR
+class tagTYPEDESC(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 582
+ pass
+TYPEDESC = tagTYPEDESC
+class tagIDLDESC(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 633
+ pass
+IDLDESC = tagIDLDESC
+
+class tagARRAYDESC(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 594
+ pass
+
+################################################################
+# interface vtbl definitions
+
+ICreateTypeLib._methods_ = [
+# C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 2149
+ COMMETHOD([], HRESULT, 'CreateTypeInfo',
+ (['in'], LPOLESTR, 'szName'),
+ (['in'], TYPEKIND, 'tkind'),
+ (['out'], POINTER(POINTER(ICreateTypeInfo)), 'ppCTInfo')),
+ STDMETHOD(HRESULT, 'SetName', [LPOLESTR]),
+ STDMETHOD(HRESULT, 'SetVersion', [WORD, WORD]),
+ STDMETHOD(HRESULT, 'SetGuid', [POINTER(GUID)]),
+ STDMETHOD(HRESULT, 'SetDocString', [LPOLESTR]),
+ STDMETHOD(HRESULT, 'SetHelpFileName', [LPOLESTR]),
+ STDMETHOD(HRESULT, 'SetHelpContext', [DWORD]),
+ STDMETHOD(HRESULT, 'SetLcid', [LCID]),
+ STDMETHOD(HRESULT, 'SetLibFlags', [UINT]),
+ STDMETHOD(HRESULT, 'SaveAllChanges', []),
+]
+
+ICreateTypeLib2._methods_ = [
+# C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 2444
+ STDMETHOD(HRESULT, 'DeleteTypeInfo', [POINTER(ITypeInfo)]),
+ STDMETHOD(HRESULT, 'SetCustData', [POINTER(GUID), POINTER(VARIANT)]),
+ STDMETHOD(HRESULT, 'SetHelpStringContext', [ULONG]),
+ STDMETHOD(HRESULT, 'SetHelpStringDll', [LPOLESTR]),
+ ]
+
+ITypeLib._methods_ = [
+# C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 4455
+ COMMETHOD([], UINT, 'GetTypeInfoCount'),
+ COMMETHOD([], HRESULT, 'GetTypeInfo',
+ (['in'], UINT, 'index'),
+ (['out'], POINTER(POINTER(ITypeInfo)))),
+ COMMETHOD([], HRESULT, 'GetTypeInfoType',
+ (['in'], UINT, 'index'),
+ (['out'], POINTER(TYPEKIND))),
+ COMMETHOD([], HRESULT, 'GetTypeInfoOfGuid',
+ (['in'], POINTER(GUID)),
+ (['out'], POINTER(POINTER(ITypeInfo)))),
+ COMMETHOD([], HRESULT, 'GetLibAttr',
+ (['out'], POINTER(POINTER(TLIBATTR)))),
+ COMMETHOD([], HRESULT, 'GetTypeComp',
+ (['out'], POINTER(POINTER(ITypeComp)))),
+ COMMETHOD([], HRESULT, 'GetDocumentation',
+ (['in'], INT, 'index'),
+ (['out'], POINTER(BSTR)),
+ (['out'], POINTER(BSTR)),
+ (['out'], POINTER(DWORD)),
+ (['out'], POINTER(BSTR))),
+ COMMETHOD([], HRESULT, 'IsName',
+ # IsName changes the casing of the passed in name to
+ # match that in the type library. In the automatically
+ # wrapped version of this method, ctypes would pass a
+ # Python unicode string which would then be changed -
+ # very bad. So we have (see above) to implement the
+ # IsName method manually.
+ (['in', 'out'], LPOLESTR, 'name'),
+ (['in', 'optional'], DWORD, 'lHashVal', 0),
+ (['out'], POINTER(BOOL))),
+ STDMETHOD(HRESULT, 'FindName', [LPOLESTR, DWORD, POINTER(POINTER(ITypeInfo)),
+ POINTER(MEMBERID), POINTER(USHORT)]),
+ COMMETHOD([], None, 'ReleaseTLibAttr',
+ (['in'], POINTER(TLIBATTR)))
+]
+
+ITypeInfo._methods_ = [
+# C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 3230
+ COMMETHOD([], HRESULT, 'GetTypeAttr',
+ (['out'], POINTER(POINTER(TYPEATTR)), 'ppTypeAttr')),
+ COMMETHOD([], HRESULT, 'GetTypeComp',
+ (['out'], POINTER(POINTER(ITypeComp)))),
+ COMMETHOD([], HRESULT, 'GetFuncDesc',
+ (['in'], UINT, 'index'),
+ (['out'], POINTER(POINTER(FUNCDESC)))),
+ COMMETHOD([], HRESULT, 'GetVarDesc',
+ (['in'], UINT, 'index'),
+ (['out'], POINTER(POINTER(VARDESC)))),
+ STDMETHOD(HRESULT, 'GetNames', [MEMBERID, POINTER(BSTR), UINT, POINTER(UINT)]),
+ COMMETHOD([], HRESULT, 'GetRefTypeOfImplType',
+ (['in'], UINT, 'index'),
+ (['out'], POINTER(HREFTYPE))),
+ COMMETHOD([], HRESULT, 'GetImplTypeFlags',
+ (['in'], UINT, 'index'),
+ (['out'], POINTER(INT))),
+## STDMETHOD(HRESULT, 'GetIDsOfNames', [POINTER(LPOLESTR), UINT, POINTER(MEMBERID)]),
+ # this one changed, to accept c_wchar_p array
+ STDMETHOD(HRESULT, 'GetIDsOfNames', [POINTER(c_wchar_p), UINT, POINTER(MEMBERID)]),
+ STDMETHOD(HRESULT, 'Invoke', [PVOID, MEMBERID, WORD, POINTER(DISPPARAMS), POINTER(VARIANT), POINTER(EXCEPINFO), POINTER(UINT)]),
+
+ COMMETHOD([], HRESULT, 'GetDocumentation',
+ (['in'], MEMBERID, 'memid'),
+ (['out'], POINTER(BSTR), 'pBstrName'),
+ (['out'], POINTER(BSTR), 'pBstrDocString'),
+ (['out'], POINTER(DWORD), 'pdwHelpContext'),
+ (['out'], POINTER(BSTR), 'pBstrHelpFile')),
+ COMMETHOD([], HRESULT, 'GetDllEntry',
+ (['in'], MEMBERID, 'index'),
+ (['in'], INVOKEKIND, 'invkind'),
+ (['out'], POINTER(BSTR), 'pBstrDllName'),
+ (['out'], POINTER(BSTR), 'pBstrName'),
+ (['out'], POINTER(WORD), 'pwOrdinal')),
+ COMMETHOD([], HRESULT, 'GetRefTypeInfo',
+ (['in'], HREFTYPE, 'hRefType'),
+ (['out'], POINTER(POINTER(ITypeInfo)))),
+ STDMETHOD(HRESULT, 'AddressOfMember', [MEMBERID, INVOKEKIND, POINTER(PVOID)]),
+ COMMETHOD([], HRESULT, 'CreateInstance',
+ (['in'], POINTER(IUnknown), 'pUnkOuter'),
+ (['in'], POINTER(IID), 'refiid'),
+ (['out'], POINTER(POINTER(IUnknown)))),
+ COMMETHOD([], HRESULT, 'GetMops',
+ (['in'], MEMBERID, 'memid'),
+ (['out'], POINTER(BSTR))),
+ COMMETHOD([], HRESULT, 'GetContainingTypeLib',
+ (['out'], POINTER(POINTER(ITypeLib))),
+ (['out'], POINTER(UINT))),
+ COMMETHOD([], None, 'ReleaseTypeAttr',
+ (['in'], POINTER(TYPEATTR))),
+ COMMETHOD([], None, 'ReleaseFuncDesc',
+ (['in'], POINTER(FUNCDESC))),
+ COMMETHOD([], None, 'ReleaseVarDesc',
+ (['in'], POINTER(VARDESC))),
+]
+
+ITypeComp._methods_ = [
+# C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 3090
+ STDMETHOD(HRESULT, 'Bind',
+ [LPOLESTR, DWORD, WORD, POINTER(POINTER(ITypeInfo)),
+ POINTER(DESCKIND), POINTER(BINDPTR)]),
+ STDMETHOD(HRESULT, 'BindType',
+ [LPOLESTR, DWORD, POINTER(POINTER(ITypeInfo)), POINTER(POINTER(ITypeComp))]),
+]
+
+ICreateTypeInfo._methods_ = [
+# C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 915
+ STDMETHOD(HRESULT, 'SetGuid', [POINTER(GUID)]),
+ STDMETHOD(HRESULT, 'SetTypeFlags', [UINT]),
+ STDMETHOD(HRESULT, 'SetDocString', [LPOLESTR]),
+ STDMETHOD(HRESULT, 'SetHelpContext', [DWORD]),
+ STDMETHOD(HRESULT, 'SetVersion', [WORD, WORD]),
+# STDMETHOD(HRESULT, 'AddRefTypeInfo', [POINTER(ITypeInfo), POINTER(HREFTYPE)]),
+ COMMETHOD([], HRESULT, 'AddRefTypeInfo',
+ (['in'], POINTER(ITypeInfo)),
+ (['out'], POINTER(HREFTYPE))),
+ STDMETHOD(HRESULT, 'AddFuncDesc', [UINT, POINTER(FUNCDESC)]),
+ STDMETHOD(HRESULT, 'AddImplType', [UINT, HREFTYPE]),
+ STDMETHOD(HRESULT, 'SetImplTypeFlags', [UINT, INT]),
+ STDMETHOD(HRESULT, 'SetAlignment', [WORD]),
+ STDMETHOD(HRESULT, 'SetSchema', [LPOLESTR]),
+ STDMETHOD(HRESULT, 'AddVarDesc', [UINT, POINTER(VARDESC)]),
+ STDMETHOD(HRESULT, 'SetFuncAndParamNames', [UINT, POINTER(c_wchar_p), UINT]),
+ STDMETHOD(HRESULT, 'SetVarName', [UINT, LPOLESTR]),
+ STDMETHOD(HRESULT, 'SetTypeDescAlias', [POINTER(TYPEDESC)]),
+ STDMETHOD(HRESULT, 'DefineFuncAsDllEntry', [UINT, LPOLESTR, LPOLESTR]),
+ STDMETHOD(HRESULT, 'SetFuncDocString', [UINT, LPOLESTR]),
+ STDMETHOD(HRESULT, 'SetVarDocString', [UINT, LPOLESTR]),
+ STDMETHOD(HRESULT, 'SetFuncHelpContext', [UINT, DWORD]),
+ STDMETHOD(HRESULT, 'SetVarHelpContext', [UINT, DWORD]),
+ STDMETHOD(HRESULT, 'SetMops', [UINT, BSTR]),
+ STDMETHOD(HRESULT, 'SetTypeIdldesc', [POINTER(IDLDESC)]),
+ STDMETHOD(HRESULT, 'LayOut', []),
+]
+
+class IProvideClassInfo(IUnknown):
+ _iid_ = GUID("{B196B283-BAB4-101A-B69C-00AA00341D07}")
+ _methods_ = [
+ # Returns the ITypeInfo interface for the object's coclass type information.
+ COMMETHOD([], HRESULT, "GetClassInfo",
+ ( ['out'], POINTER(POINTER(ITypeInfo)), "ppTI" ) )
+ ]
+
+class IProvideClassInfo2(IProvideClassInfo):
+ _iid_ = GUID("{A6BC3AC0-DBAA-11CE-9DE3-00AA004BB851}")
+ _methods_ = [
+ # Returns the GUID for the object's outgoing IID for its default event set.
+ COMMETHOD([], HRESULT, "GetGUID",
+ ( ['in'], DWORD, "dwGuidKind" ),
+ ( ['out', 'retval'], POINTER(GUID), "pGUID" ))
+ ]
+
+
+################################################################
+# Structure fields
+
+tagTLIBATTR._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 4437
+ ('guid', GUID),
+ ('lcid', LCID),
+ ('syskind', SYSKIND),
+ ('wMajorVerNum', WORD),
+ ('wMinorVerNum', WORD),
+ ('wLibFlags', WORD),
+]
+class N11tagTYPEDESC5DOLLAR_203E(Union):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 584
+ pass
+N11tagTYPEDESC5DOLLAR_203E._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 584
+ ('lptdesc', POINTER(tagTYPEDESC)),
+ ('lpadesc', POINTER(tagARRAYDESC)),
+ ('hreftype', HREFTYPE),
+]
+tagTYPEDESC._anonymous_ = ('_',)
+tagTYPEDESC._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 582
+ # Unnamed field renamed to '_'
+ ('_', N11tagTYPEDESC5DOLLAR_203E),
+ ('vt', VARTYPE),
+]
+tagIDLDESC._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 633
+ ('dwReserved', ULONG_PTR),
+ ('wIDLFlags', USHORT),
+]
+tagTYPEATTR._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 672
+ ('guid', GUID),
+ ('lcid', LCID),
+ ('dwReserved', DWORD),
+ ('memidConstructor', MEMBERID),
+ ('memidDestructor', MEMBERID),
+ ('lpstrSchema', LPOLESTR),
+ ('cbSizeInstance', DWORD),
+ ('typekind', TYPEKIND),
+ ('cFuncs', WORD),
+ ('cVars', WORD),
+ ('cImplTypes', WORD),
+ ('cbSizeVft', WORD),
+ ('cbAlignment', WORD),
+ ('wTypeFlags', WORD),
+ ('wMajorVerNum', WORD),
+ ('wMinorVerNum', WORD),
+ ('tdescAlias', TYPEDESC),
+ ('idldescType', IDLDESC),
+]
+class N10tagVARDESC5DOLLAR_205E(Union):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 807
+ pass
+N10tagVARDESC5DOLLAR_205E._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 807
+ ('oInst', DWORD),
+ ('lpvarValue', POINTER(VARIANT)),
+]
+class tagELEMDESC(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 661
+ pass
+class N11tagELEMDESC5DOLLAR_204E(Union):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 663
+ pass
+
+class tagPARAMDESC(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 609
+ pass
+
+class tagPARAMDESCEX(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 601
+ pass
+LPPARAMDESCEX = POINTER(tagPARAMDESCEX)
+
+tagPARAMDESC._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 609
+ ('pparamdescex', LPPARAMDESCEX),
+ ('wParamFlags', USHORT),
+]
+PARAMDESC = tagPARAMDESC
+
+N11tagELEMDESC5DOLLAR_204E._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 663
+ ('idldesc', IDLDESC),
+ ('paramdesc', PARAMDESC),
+]
+tagELEMDESC._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 661
+ ('tdesc', TYPEDESC),
+ # Unnamed field renamed to '_'
+ ('_', N11tagELEMDESC5DOLLAR_204E),
+]
+ELEMDESC = tagELEMDESC
+
+tagVARDESC._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 803
+ ('memid', MEMBERID),
+ ('lpstrSchema', LPOLESTR),
+ # Unnamed field renamed to '_'
+ ('_', N10tagVARDESC5DOLLAR_205E),
+ ('elemdescVar', ELEMDESC),
+ ('wVarFlags', WORD),
+ ('varkind', VARKIND),
+]
+tagBINDPTR._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 3075
+ ('lpfuncdesc', POINTER(FUNCDESC)),
+ ('lpvardesc', POINTER(VARDESC)),
+ ('lptcomp', POINTER(ITypeComp)),
+]
+
+tagFUNCDESC._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 769
+ ('memid', MEMBERID),
+ ('lprgscode', POINTER(SCODE)),
+ ('lprgelemdescParam', POINTER(ELEMDESC)),
+ ('funckind', FUNCKIND),
+ ('invkind', INVOKEKIND),
+ ('callconv', CALLCONV),
+ ('cParams', SHORT),
+ ('cParamsOpt', SHORT),
+ ('oVft', SHORT),
+ ('cScodes', SHORT),
+ ('elemdescFunc', ELEMDESC),
+ ('wFuncFlags', WORD),
+]
+
+tagPARAMDESCEX._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 601
+ ('cBytes', DWORD),
+ ('varDefaultValue', VARIANTARG),
+]
+
+class tagSAFEARRAYBOUND(Structure):
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 226
+ _fields_ = [
+ ('cElements', DWORD),
+ ('lLbound', LONG),
+ ]
+SAFEARRAYBOUND = tagSAFEARRAYBOUND
+
+tagARRAYDESC._fields_ = [
+ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 594
+ ('tdescElem', TYPEDESC),
+ ('cDims', USHORT),
+ ('rgbounds', SAFEARRAYBOUND * 1),
+]
diff --git a/tools/comtypes/comtypes/util.py b/tools/comtypes/comtypes/util.py
new file mode 100644
index 00000000000000..0eaf864db0947e
--- /dev/null
+++ b/tools/comtypes/comtypes/util.py
@@ -0,0 +1,96 @@
+"""This module defines the funtions byref_at(cobj, offset)
+and cast_field(struct, fieldname, fieldtype).
+"""
+from ctypes import *
+
+def _calc_offset():
+ # Internal helper function that calculates where the object
+ # returned by a byref() call stores the pointer.
+
+ # The definition of PyCArgObject in C code (that is the type of
+ # object that a byref() call returns):
+ class PyCArgObject(Structure):
+ class value(Union):
+ _fields_ = [("c", c_char),
+ ("h", c_short),
+ ("i", c_int),
+ ("l", c_long),
+ ("q", c_longlong),
+ ("d", c_double),
+ ("f", c_float),
+ ("p", c_void_p)]
+ #
+ # Thanks to Lenard Lindstrom for this tip:
+ # sizeof(PyObject_HEAD) is the same as object.__basicsize__.
+ #
+ _fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
+ ("pffi_type", c_void_p),
+ ("tag", c_char),
+ ("value", value),
+ ("obj", c_void_p),
+ ("size", c_int)]
+
+ _anonymous_ = ["value"]
+
+ # additional checks to make sure that everything works as expected
+
+ if sizeof(PyCArgObject) != type(byref(c_int())).__basicsize__:
+ raise RuntimeError("sizeof(PyCArgObject) invalid")
+
+ obj = c_int()
+ ref = byref(obj)
+
+ argobj = PyCArgObject.from_address(id(ref))
+
+ if argobj.obj != id(obj) or \
+ argobj.p != addressof(obj) or \
+ argobj.tag != 'P':
+ raise RuntimeError("PyCArgObject field definitions incorrect")
+
+ return PyCArgObject.p.offset # offset of the pointer field
+
+################################################################
+#
+# byref_at
+#
+def byref_at(obj, offset,
+ _byref=byref,
+ _c_void_p_from_address = c_void_p.from_address,
+ _byref_pointer_offset = _calc_offset()
+ ):
+ """byref_at(cobj, offset) behaves similar this C code:
+
+ (((char *)&obj) + offset)
+
+ In other words, the returned 'pointer' points to the address of
+ 'cobj' + 'offset'. 'offset' is in units of bytes.
+ """
+ ref = _byref(obj)
+ # Change the pointer field in the created byref object by adding
+ # 'offset' to it:
+ _c_void_p_from_address(id(ref)
+ + _byref_pointer_offset).value += offset
+ return ref
+
+
+################################################################
+#
+# cast_field
+#
+def cast_field(struct, fieldname, fieldtype, offset=0,
+ _POINTER=POINTER,
+ _byref_at=byref_at,
+ _byref=byref,
+ _divmod=divmod,
+ _sizeof=sizeof,
+ ):
+ """cast_field(struct, fieldname, fieldtype)
+
+ Return the contents of a struct field as it it were of type
+ 'fieldtype'.
+ """
+ fieldoffset = getattr(type(struct), fieldname).offset
+ return cast(_byref_at(struct, fieldoffset),
+ _POINTER(fieldtype))[0]
+
+__all__ = ["byref_at", "cast_field"]
diff --git a/tools/comtypes/comtypes/viewobject.py b/tools/comtypes/comtypes/viewobject.py
new file mode 100644
index 00000000000000..110c6a6f00230a
--- /dev/null
+++ b/tools/comtypes/comtypes/viewobject.py
@@ -0,0 +1,160 @@
+# XXX need to find out what the share from comtypes.dataobject.
+from ctypes import *
+from ctypes.wintypes import _RECTL, SIZEL, HDC, tagRECT, tagPOINT
+
+from comtypes import COMMETHOD
+from comtypes import GUID
+from comtypes import IUnknown
+
+class tagPALETTEENTRY(Structure):
+ _fields_ = [
+ ('peRed', c_ubyte),
+ ('peGreen', c_ubyte),
+ ('peBlue', c_ubyte),
+ ('peFlags', c_ubyte),
+ ]
+assert sizeof(tagPALETTEENTRY) == 4, sizeof(tagPALETTEENTRY)
+assert alignment(tagPALETTEENTRY) == 1, alignment(tagPALETTEENTRY)
+
+class tagLOGPALETTE(Structure):
+ _pack_ = 2
+ _fields_ = [
+ ('palVersion', c_ushort),
+ ('palNumEntries', c_ushort),
+ ('palPalEntry', POINTER(tagPALETTEENTRY)),
+ ]
+assert sizeof(tagLOGPALETTE) == 8, sizeof(tagLOGPALETTE)
+assert alignment(tagLOGPALETTE) == 2, alignment(tagLOGPALETTE)
+
+class tagDVTARGETDEVICE(Structure):
+ _fields_ = [
+ ('tdSize', c_ulong),
+ ('tdDriverNameOffset', c_ushort),
+ ('tdDeviceNameOffset', c_ushort),
+ ('tdPortNameOffset', c_ushort),
+ ('tdExtDevmodeOffset', c_ushort),
+ ('tdData', POINTER(c_ubyte)),
+ ]
+assert sizeof(tagDVTARGETDEVICE) == 16, sizeof(tagDVTARGETDEVICE)
+assert alignment(tagDVTARGETDEVICE) == 4, alignment(tagDVTARGETDEVICE)
+
+class tagExtentInfo(Structure):
+ _fields_ = [
+ ('cb', c_ulong),
+ ('dwExtentMode', c_ulong),
+ ('sizelProposed', SIZEL),
+ ]
+ def __init__(self, *args, **kw):
+ self.cb = sizeof(self)
+ super(tagExtentInfo, self).__init__(*args, **kw)
+ def __repr__(self):
+ size = (self.sizelProposed.cx, self.sizelProposed.cy)
+ return "" % (self.dwExtentMode,
+ size,
+ id(self))
+assert sizeof(tagExtentInfo) == 16, sizeof(tagExtentInfo)
+assert alignment(tagExtentInfo) == 4, alignment(tagExtentInfo)
+DVEXTENTINFO = tagExtentInfo
+
+IAdviseSink = IUnknown # fake the interface
+
+class IViewObject(IUnknown):
+ _case_insensitive_ = False
+ _iid_ = GUID('{0000010D-0000-0000-C000-000000000046}')
+ _idlflags_ = []
+
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'Draw',
+ ( ['in'], c_ulong, 'dwDrawAspect' ),
+ ( ['in'], c_int, 'lindex' ),
+ ( ['in'], c_void_p, 'pvAspect' ),
+ ( ['in'], POINTER(tagDVTARGETDEVICE), 'ptd' ),
+ ( ['in'], HDC, 'hdcTargetDev' ),
+ ( ['in'], HDC, 'hdcDraw' ),
+ ( ['in'], POINTER(_RECTL), 'lprcBounds' ),
+ ( ['in'], POINTER(_RECTL), 'lprcWBounds' ),
+ ( ['in'], c_void_p, 'pfnContinue' ), # a pointer to a callback function
+ ( ['in'], c_ulong, 'dwContinue')),
+ COMMETHOD([], HRESULT, 'GetColorSet',
+ ( ['in'], c_ulong, 'dwDrawAspect' ),
+ ( ['in'], c_int, 'lindex' ),
+ ( ['in'], c_void_p, 'pvAspect' ),
+ ( ['in'], POINTER(tagDVTARGETDEVICE), 'ptd' ),
+ ( ['in'], HDC, 'hicTargetDev' ),
+ ( ['out'], POINTER(POINTER(tagLOGPALETTE)), 'ppColorSet' )),
+ COMMETHOD([], HRESULT, 'Freeze',
+ ( ['in'], c_ulong, 'dwDrawAspect' ),
+ ( ['in'], c_int, 'lindex' ),
+ ( ['in'], c_void_p, 'pvAspect' ),
+ ( ['out'], POINTER(c_ulong), 'pdwFreeze' )),
+ COMMETHOD([], HRESULT, 'Unfreeze',
+ ( ['in'], c_ulong, 'dwFreeze' )),
+ COMMETHOD([], HRESULT, 'SetAdvise',
+ ( ['in'], c_ulong, 'dwAspect' ),
+ ( ['in'], c_ulong, 'advf' ),
+ ( ['in'], POINTER(IAdviseSink), 'pAdvSink' )),
+ COMMETHOD([], HRESULT, 'GetAdvise',
+ ( ['out'], POINTER(c_ulong), 'pdwAspect' ),
+ ( ['out'], POINTER(c_ulong), 'pAdvf' ),
+ ( ['out'], POINTER(POINTER(IAdviseSink)), 'ppAdvSink' )),
+ ]
+
+class IViewObject2(IViewObject):
+ _case_insensitive_ = False
+ _iid_ = GUID('{00000127-0000-0000-C000-000000000046}')
+ _idlflags_ = []
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'GetExtent',
+ ( ['in'], c_ulong, 'dwDrawAspect' ),
+ ( ['in'], c_int, 'lindex' ),
+ ( ['in'], POINTER(tagDVTARGETDEVICE), 'ptd' ),
+ ( ['out'], POINTER(SIZEL), 'lpsizel' )),
+ ]
+
+class IViewObjectEx(IViewObject2):
+ _case_insensitive_ = False
+ _iid_ = GUID('{3AF24292-0C96-11CE-A0CF-00AA00600AB8}')
+ _idlflags_ = []
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'GetRect',
+ ( ['in'], c_ulong, 'dwAspect' ),
+ ( ['out'], POINTER(_RECTL), 'pRect' )),
+ COMMETHOD([], HRESULT, 'GetViewStatus',
+ ( ['out'], POINTER(c_ulong), 'pdwStatus' )),
+ COMMETHOD([], HRESULT, 'QueryHitPoint',
+ ( ['in'], c_ulong, 'dwAspect' ),
+ ( ['in'], POINTER(tagRECT), 'pRectBounds' ),
+ ( ['in'], tagPOINT, 'ptlLoc' ),
+ ( ['in'], c_int, 'lCloseHint' ),
+ ( ['out'], POINTER(c_ulong), 'pHitResult' )),
+ COMMETHOD([], HRESULT, 'QueryHitRect',
+ ( ['in'], c_ulong, 'dwAspect' ),
+ ( ['in'], POINTER(tagRECT), 'pRectBounds' ),
+ ( ['in'], POINTER(tagRECT), 'pRectLoc' ),
+ ( ['in'], c_int, 'lCloseHint' ),
+ ( ['out'], POINTER(c_ulong), 'pHitResult' )),
+ COMMETHOD([], HRESULT, 'GetNaturalExtent',
+ ( ['in'], c_ulong, 'dwAspect' ),
+ ( ['in'], c_int, 'lindex' ),
+ ( ['in'], POINTER(tagDVTARGETDEVICE), 'ptd' ),
+ ( ['in'], HDC, 'hicTargetDev' ),
+ ( ['in'], POINTER(tagExtentInfo), 'pExtentInfo' ),
+ ( ['out'], POINTER(SIZEL), 'pSizel' )),
+ ]
+
+
+DVASPECT = c_int # enum
+DVASPECT_CONTENT = 1
+DVASPECT_THUMBNAIL = 2
+DVASPECT_ICON = 4
+DVASPECT_DOCPRINT = 8
+
+DVASPECT2 = c_int # enum
+DVASPECT_OPAQUE = 16
+DVASPECT_TRANSPARENT = 32
+
+DVEXTENTMODE = c_int # enum
+# Container asks the object how big it wants to be to exactly fit its content:
+DVEXTENT_CONTENT = 0
+# The container proposes a size to the object for its use in resizing:
+DVEXTENT_INTEGRAL = 1
diff --git a/tools/find_vs2017.py b/tools/find_vs2017.py
new file mode 100644
index 00000000000000..81b39348362104
--- /dev/null
+++ b/tools/find_vs2017.py
@@ -0,0 +1,173 @@
+import re
+import sys
+import os
+from ctypes import *
+
+root_dir = os.path.dirname(__file__)
+sys.path.insert(0, os.path.join(root_dir, 'comtypes'))
+
+from comtypes import IUnknown
+from comtypes import GUID
+from comtypes import COMMETHOD
+from comtypes import BSTR
+from comtypes import DWORD
+from comtypes.safearray import _midlSAFEARRAY
+from comtypes.client import CreateObject
+
+
+""" Find Visual Studio 2017 C/C++ compiler install location """
+
+class ISetupInstance(IUnknown):
+ _iid_ = GUID('{B41463C3-8866-43B5-BC33-2B0676F7F42E}')
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'GetInstanceId',
+ ( ['out'], POINTER(BSTR), 'pbstrInstanceId' ) ),
+ COMMETHOD([], HRESULT, 'GetInstallDate',
+ ( ['out'], POINTER(c_ulonglong), 'pInstallDate') ),
+ COMMETHOD([], HRESULT, 'GetInstallationName',
+ ( ['out'], POINTER(BSTR), 'pInstallationName') ),
+ COMMETHOD([], HRESULT, 'GetInstallationPath',
+ ( ['out'], POINTER(BSTR), 'pInstallationPath') ),
+ COMMETHOD([], HRESULT, 'GetInstallationVersion',
+ ( ['out'], POINTER(BSTR), 'pInstallationVersion') ),
+ COMMETHOD([], HRESULT, 'GetDisplayName',
+ ( ['in'], DWORD, 'lcid' ),
+ ( ['out'], POINTER(BSTR), 'pDisplayName') ),
+ COMMETHOD([], HRESULT, 'GetDescription',
+ ( ['in'], DWORD, 'lcid' ),
+ ( ['out'], POINTER(BSTR), 'pDescription') ),
+ COMMETHOD([], HRESULT, 'ResolvePath',
+ ( ['in'], c_wchar_p, 'pRelativePath' ),
+ ( ['out'], POINTER(BSTR), 'pAbsolutePath') ),
+ ]
+
+class ISetupPackageReference(IUnknown):
+ _iid_ = GUID('{da8d8a16-b2b6-4487-a2f1-594ccccd6bf5}')
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'GetId',
+ ( ['out'], POINTER(BSTR), 'pOut' ) ),
+ COMMETHOD([], HRESULT, 'GetVersion',
+ ( ['out'], POINTER(BSTR), 'pOut' ) ),
+ COMMETHOD([], HRESULT, 'GetChip',
+ ( ['out'], POINTER(BSTR), 'pOut' ) ),
+ COMMETHOD([], HRESULT, 'GetLanguage',
+ ( ['out'], POINTER(BSTR), 'pOut' ) ),
+ COMMETHOD([], HRESULT, 'GetBranch',
+ ( ['out'], POINTER(BSTR), 'pOut' ) ),
+ COMMETHOD([], HRESULT, 'GetType',
+ ( ['out'], POINTER(BSTR), 'pOut' ) ),
+ COMMETHOD([], HRESULT, 'GetUniqueId',
+ ( ['out'], POINTER(BSTR), 'pOut' ) )
+ ]
+
+class ISetupInstance2(ISetupInstance):
+ _iid_ = GUID('{89143C9A-05AF-49B0-B717-72E218A2185C}')
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'GetState',
+ ( ['out'], POINTER(DWORD), 'pState' ) ),
+ COMMETHOD([], HRESULT, 'GetPackages',
+ ( ['out'], POINTER(_midlSAFEARRAY(POINTER(ISetupPackageReference))), 'ppPackage' ) )
+ ]
+
+class IEnumSetupInstances(IUnknown):
+ _iid_ = GUID('{6380BCFF-41D3-4B2E-8B2E-BF8A6810C848}')
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'Next',
+ ( ['in'], c_ulong, 'celt'),
+ ( ['out'], POINTER(POINTER(ISetupInstance)), 'rgelt' ),
+ ( ['out'], POINTER(c_ulong), 'pceltFetched' ) ),
+ COMMETHOD([], HRESULT, 'Skip',
+ ( ['in'], c_ulong, 'celt' ) ),
+ COMMETHOD([], HRESULT, 'Reset'),
+ ]
+
+class ISetupConfiguration(IUnknown):
+ _iid_ = GUID('{42843719-DB4C-46C2-8E7C-64F1816EFD5B}')
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'EnumInstances',
+ ( ['out'], POINTER(POINTER(IEnumSetupInstances)), 'ppIESI' ) ),
+ COMMETHOD([], HRESULT, 'GetInstanceForCurrentProcess',
+ ( ['out'], POINTER(POINTER(ISetupInstance)), 'ppISI' ) ),
+ COMMETHOD([], HRESULT, 'GetInstanceForPath',
+ ( ['in'], c_wchar_p, 'wzPath'),
+ ( ['out'], POINTER(POINTER(ISetupInstance)), 'ppISI' ) )
+ ]
+
+class ISetupConfiguration2(ISetupConfiguration) :
+ _iid_ = GUID('{26AAB78C-4A60-49D6-AF3B-3C35BC93365D}')
+ _methods_ = [
+ COMMETHOD([], HRESULT, 'EnumAllInstances',
+ ( ['out'], POINTER(POINTER(IEnumSetupInstances)), 'ppIEnumSetupInstances' ) )
+ ]
+
+
+def GetVS2017CPPBasePath():
+ installs = []
+ iface = CreateObject(GUID('{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}'))
+ setupConfiguration = iface.QueryInterface(ISetupConfiguration2)
+ allInstances = setupConfiguration.EnumAllInstances()
+ while True:
+ result = allInstances.Next(1)
+ instance = result[0]
+ if not instance:
+ break
+ path = instance.GetInstallationPath()
+ version = instance.GetInstallationVersion()
+ instance2 = instance.QueryInterface(ISetupInstance2)
+ packages = instance2.GetPackages()
+ for package in packages:
+ packageId = package.GetId()
+ if packageId == 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64':
+ installs.append(path)
+ return installs
+
+def GetInstalledVS2017WinSDKs(vs_path):
+ sdks = []
+ has81sdk = False
+ win8preg = re.compile(r"Microsoft.VisualStudio.Component.Windows81SDK")
+ win10preg = re.compile(r"Microsoft.VisualStudio.Component.Windows10SDK.(\d+)")
+ iface = CreateObject(GUID('{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}'))
+ setupConfiguration = iface.QueryInterface(ISetupConfiguration2)
+ allInstances = setupConfiguration.EnumAllInstances()
+ while True:
+ result = allInstances.Next(1)
+ instance = result[0]
+ if not instance:
+ break
+ path = instance.GetInstallationPath()
+ if path != vs_path:
+ continue
+ instance2 = instance.QueryInterface(ISetupInstance2)
+ packages = instance2.GetPackages()
+ for package in packages:
+ packageId = package.GetId()
+ if win8preg.match(packageId):
+ has81sdk = True
+ else:
+ win10match = win10preg.match(packageId)
+ if win10match:
+ sdks.append('10.0.' + str(win10match.group(1)) + '.0')
+
+ sdks.sort(reverse = True)
+ if has81sdk:
+ sdks.append('8.1')
+ return sdks
+
+def main():
+ if len(sys.argv) == 1:
+ installs = GetVS2017CPPBasePath()
+ if len(installs) == 0:
+ return
+ for install in installs:
+ sdks = GetInstalledVS2017WinSDKs(install)
+ if len(sdks) > 0:
+ print install
+ return
+ print installs[0]
+ else:
+ sdks = GetInstalledVS2017WinSDKs(sys.argv[1])
+ if len(sdks) > 0:
+ print sdks[0]
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/gyp/pylib/gyp/MSVSVersion.py b/tools/gyp/pylib/gyp/MSVSVersion.py
index d9bfa684fa30c2..f6cff664166bd2 100644
--- a/tools/gyp/pylib/gyp/MSVSVersion.py
+++ b/tools/gyp/pylib/gyp/MSVSVersion.py
@@ -84,6 +84,10 @@ def SetupScript(self, target_arch):
# vcvars32, which it can only find if VS??COMNTOOLS is set, which it
# isn't always.
if target_arch == 'x86':
+ if self.short_name == '2017':
+ return [os.path.normpath(
+ ps.path.join(self.path, 'Common7/Tools/VsDevCmd.bat')), '/no_logo',
+ '/arch=x86']
if self.short_name >= '2013' and self.short_name[-1] != 'e' and (
os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
@@ -96,6 +100,10 @@ def SetupScript(self, target_arch):
os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
else:
assert target_arch == 'x64'
+ if self.short_name == '2017':
+ return [os.path.normpath(
+ ps.path.join(self.path, 'Common7/Tools/VsDevCmd.bat')), '/no_logo',
+ '/arch=x64']
arg = 'x86_amd64'
# Use the 64-on-64 compiler if we're not using an express
# edition and we're running on a 64bit OS.
@@ -226,6 +234,15 @@ def _CreateVersion(name, path, sdk_based=False):
if path:
path = os.path.normpath(path)
versions = {
+ '2017': VisualStudioVersion('2017',
+ 'Visual Studio 2017',
+ solution_version='12.00',
+ project_version='14.0',
+ flat_sln=False,
+ uses_vcxproj=True,
+ path=path,
+ sdk_based=sdk_based,
+ default_toolset='v141'),
'2015': VisualStudioVersion('2015',
'Visual Studio 2015',
solution_version='12.00',
@@ -346,6 +363,7 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
2012(e) - Visual Studio 2012 (11)
2013(e) - Visual Studio 2013 (12)
2015 - Visual Studio 2015 (14)
+ 2017 - Visual Studio 2017 (15)
Where (e) is e for express editions of MSVS and blank otherwise.
"""
version_to_year = {
@@ -355,6 +373,7 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
'11.0': '2012',
'12.0': '2013',
'14.0': '2015',
+ '15.0': '2017'
}
versions = []
for version in versions_to_check:
@@ -395,6 +414,17 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
versions.append(_CreateVersion(version_to_year[version] + 'e',
os.path.join(path, '..'), sdk_based=True))
+ if version == '15.0':
+ # The VC++ 2017 install location needs to be located using COM instead of
+ # the registry. For details see:
+ # https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
+ # For now we use a hardcoded default with an environment variable
+ # override.
+ path = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional'
+ path = os.environ.get('vs2017_install', path)
+ if os.path.exists(path):
+ versions.append(_CreateVersion('2017', path))
+
return versions
@@ -410,7 +440,7 @@ def SelectVisualStudioVersion(version='auto', allow_fallback=True):
if version == 'auto':
version = os.environ.get('GYP_MSVS_VERSION', 'auto')
version_map = {
- 'auto': ('14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
+ 'auto': ('15.0', '14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
'2005': ('8.0',),
'2005e': ('8.0',),
'2008': ('9.0',),
@@ -422,6 +452,7 @@ def SelectVisualStudioVersion(version='auto', allow_fallback=True):
'2013': ('12.0',),
'2013e': ('12.0',),
'2015': ('14.0',),
+ '2017': ('15.0',),
}
override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
if override_path:
diff --git a/tools/gyp/pylib/gyp/generator/msvs.py b/tools/gyp/pylib/gyp/generator/msvs.py
index 44cc1304a2e8ed..7dd24f68403652 100644
--- a/tools/gyp/pylib/gyp/generator/msvs.py
+++ b/tools/gyp/pylib/gyp/generator/msvs.py
@@ -2622,7 +2622,7 @@ def _GetMSBuildProjectConfigurations(configurations):
return [group]
-def _GetMSBuildGlobalProperties(spec, guid, gyp_file_name):
+def _GetMSBuildGlobalProperties(spec, version, guid, gyp_file_name):
namespace = os.path.splitext(gyp_file_name)[0]
properties = [
['PropertyGroup', {'Label': 'Globals'},
@@ -2662,6 +2662,15 @@ def _GetMSBuildGlobalProperties(spec, guid, gyp_file_name):
else:
properties[0].append(['ApplicationType', 'Windows Store'])
+ msvs_windows_sdk_version = None
+ if msvs_windows_sdk_version == None and version.ShortName() == '2017':
+ vs2017_sdk = '10.0.14393.0'
+ vs2017_sdk = os.environ.get('vs2017_sdk', vs2017_sdk)
+ if vs2017_sdk:
+ msvs_windows_sdk_version = vs2017_sdk
+ if msvs_windows_sdk_version:
+ properties[0].append(['WindowsTargetPlatformVersion',
+ str(msvs_windows_sdk_version)])
return properties
def _GetMSBuildConfigurationDetails(spec, build_file):
@@ -3295,7 +3304,8 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
}]
content += _GetMSBuildProjectConfigurations(configurations)
- content += _GetMSBuildGlobalProperties(spec, project.guid, project_file_name)
+ content += _GetMSBuildGlobalProperties(spec, version, project.guid,
+ project_file_name)
content += import_default_section
content += _GetMSBuildConfigurationDetails(spec, project.build_file)
if spec.get('msvs_enable_winphone'):
diff --git a/vcbuild.bat b/vcbuild.bat
index bc578c8f1d2269..69fb745ff41322 100644
--- a/vcbuild.bat
+++ b/vcbuild.bat
@@ -49,6 +49,7 @@ if /i "%1"=="ia32" set target_arch=x86&goto arg-ok
if /i "%1"=="x86" set target_arch=x86&goto arg-ok
if /i "%1"=="x64" set target_arch=x64&goto arg-ok
if /i "%1"=="vc2015" set target_env=vc2015&goto arg-ok
+if /i "%1"=="vc2017" set target_env=vc2017&goto arg-ok
if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok
if /i "%1"=="nobuild" set nobuild=1&goto arg-ok
if /i "%1"=="nosign" set "sign="&goto arg-ok
@@ -135,7 +136,29 @@ if defined noprojgen if defined nobuild if not defined sign if not defined msi g
@rem Set environment for msbuild
+@rem Look for Visual Studio 2017
+:vc-set-2017
+if defined target_env if "%target_env%" NEQ "vc2017" goto vc-set-2015
+echo Looking for Visual Studio 2017
+for /f "delims=" %%i in ('python "%~dp0tools\find_vs2017.py"') do set VS2017_INSTALL=%%i
+if not exist "%VS2017_INSTALL%\Common7\Tools\VsDevCmd.bat" goto vc-set-2015
+for /f "delims=" %%i in ('python "%~dp0tools\find_vs2017.py" "%VS2017_INSTALL%"') do set VS2017_SDK=%%i
+echo Found Visual Studio 2017
+if defined msi (
+ echo Cannot build the MSI with Visual Studio 2017 - it is not yet supported by WiX
+ goto vc-set-2015
+)
+if "%VCVARS_VER%" NEQ "150" (
+ call "%VS2017_INSTALL%\Common7\Tools\VsDevCmd.bat" /no_logo
+ set VCVARS_VER=150
+)
+set GYP_MSVS_VERSION=2017
+set PLATFORM_TOOLSET=v141
+goto msbuild-found
+
@rem Look for Visual Studio 2015
+:vc-set-2015
+if defined target_env if "%target_env%" NEQ "vc2015" goto msbuild-not-found
echo Looking for Visual Studio 2015
if not defined VS140COMNTOOLS goto msbuild-not-found
if not exist "%VS140COMNTOOLS%\..\..\vc\vcvarsall.bat" goto msbuild-not-found
@@ -365,7 +388,7 @@ echo Failed to create vc project files.
goto exit
:help
-echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-inspector/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [small-icu/full-icu/without-intl] [nobuild] [sign] [x86/x64] [vc2015] [download-all] [enable-vtune]
+echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-inspector/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [small-icu/full-icu/without-intl] [nobuild] [sign] [x86/x64] [vc2015/vc2017] [download-all] [enable-vtune]
echo Examples:
echo vcbuild.bat : builds release build
echo vcbuild.bat debug : builds debug build