You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
9.9 KiB
268 lines
9.9 KiB
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
|