123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- # This module exists to create the "best" dispatch object for a given
- # object. If "makepy" support for a given object is detected, it is
- # used, otherwise a dynamic dispatch object.
- # Note that if the unknown dispatch object then returns a known
- # dispatch object, the known class will be used. This contrasts
- # with dynamic.Dispatch behaviour, where dynamic objects are always used.
- import pythoncom
- from . import dynamic
- from . import gencache
- import sys
- import pywintypes
- _PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
- def __WrapDispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, \
- UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER,
- WrapperClass = None):
- """
- Helper function to return a makepy generated class for a CLSID if it exists,
- otherwise cope by using CDispatch.
- """
- assert UnicodeToString is None, "this is deprecated and will go away"
- if resultCLSID is None:
- try:
- typeinfo = dispatch.GetTypeInfo()
- if typeinfo is not None: # Some objects return NULL, some raise exceptions...
- resultCLSID = str(typeinfo.GetTypeAttr()[0])
- except (pythoncom.com_error, AttributeError):
- pass
- if resultCLSID is not None:
- from . import gencache
- # Attempt to load generated module support
- # This may load the module, and make it available
- klass = gencache.GetClassForCLSID(resultCLSID)
- if klass is not None:
- return klass(dispatch)
- # Return a "dynamic" object - best we can do!
- if WrapperClass is None: WrapperClass = CDispatch
- return dynamic.Dispatch(dispatch, userName, WrapperClass, typeinfo, clsctx=clsctx)
- def GetObject(Pathname = None, Class = None, clsctx = None):
- """
- Mimic VB's GetObject() function.
- ob = GetObject(Class = "ProgID") or GetObject(Class = clsid) will
- connect to an already running instance of the COM object.
- ob = GetObject(r"c:\blah\blah\foo.xls") (aka the COM moniker syntax)
- will return a ready to use Python wrapping of the required COM object.
- Note: You must specifiy one or the other of these arguments. I know
- this isn't pretty, but it is what VB does. Blech. If you don't
- I'll throw ValueError at you. :)
- This will most likely throw pythoncom.com_error if anything fails.
- """
- if clsctx is None:
- clsctx = pythoncom.CLSCTX_ALL
- if (Pathname is None and Class is None) or \
- (Pathname is not None and Class is not None):
- raise ValueError("You must specify a value for Pathname or Class, but not both.")
- if Class is not None:
- return GetActiveObject(Class, clsctx)
- else:
- return Moniker(Pathname, clsctx)
- def GetActiveObject(Class, clsctx = pythoncom.CLSCTX_ALL):
- """
- Python friendly version of GetObject's ProgID/CLSID functionality.
- """
- resultCLSID = pywintypes.IID(Class)
- dispatch = pythoncom.GetActiveObject(resultCLSID)
- dispatch = dispatch.QueryInterface(pythoncom.IID_IDispatch)
- return __WrapDispatch(dispatch, Class, resultCLSID = resultCLSID, clsctx = clsctx)
- def Moniker(Pathname, clsctx = pythoncom.CLSCTX_ALL):
- """
- Python friendly version of GetObject's moniker functionality.
- """
- moniker, i, bindCtx = pythoncom.MkParseDisplayName(Pathname)
- dispatch = moniker.BindToObject(bindCtx, None, pythoncom.IID_IDispatch)
- return __WrapDispatch(dispatch, Pathname, clsctx=clsctx)
- def Dispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER):
- """Creates a Dispatch based COM object.
- """
- assert UnicodeToString is None, "this is deprecated and will go away"
- dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
- return __WrapDispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx)
- def DispatchEx(clsid, machine=None, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=None, clsctx = None):
- """Creates a Dispatch based COM object on a specific machine.
- """
- assert UnicodeToString is None, "this is deprecated and will go away"
- # If InProc is registered, DCOM will use it regardless of the machine name
- # (and regardless of the DCOM config for the object.) So unless the user
- # specifies otherwise, we exclude inproc apps when a remote machine is used.
- if clsctx is None:
- clsctx = pythoncom.CLSCTX_SERVER
- if machine is not None: clsctx = clsctx & ~pythoncom.CLSCTX_INPROC
- if machine is None:
- serverInfo = None
- else:
- serverInfo = (machine,)
- if userName is None: userName = clsid
- dispatch = pythoncom.CoCreateInstanceEx(clsid, None, clsctx, serverInfo, (pythoncom.IID_IDispatch,))[0]
- return Dispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx)
- class CDispatch(dynamic.CDispatch):
- """
- The dynamic class used as a last resort.
- The purpose of this overriding of dynamic.CDispatch is to perpetuate the policy
- of using the makepy generated wrapper Python class instead of dynamic.CDispatch
- if/when possible.
- """
- def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
- assert UnicodeToString is None, "this is deprecated and will go away"
- return Dispatch(ob, userName, returnCLSID,None)
- def __dir__(self):
- return dynamic.CDispatch.__dir__(self)
- def CastTo(ob, target, typelib = None):
- """'Cast' a COM object to another interface"""
- # todo - should support target being an IID
- mod = None
- if typelib is not None: # caller specified target typelib (TypelibSpec). See e.g. selecttlb.EnumTlbs().
- mod = gencache.MakeModuleForTypelib(typelib.clsid, typelib.lcid, int(typelib.major, 16), int(typelib.minor, 16))
- if not hasattr(mod, target):
- raise ValueError("The interface name '%s' does not appear in the " \
- "specified library %r" % (target, typelib.ver_desc))
- elif hasattr(target, "index"): # string like
- # for now, we assume makepy for this to work.
- if "CLSID" not in ob.__class__.__dict__:
- # Eeek - no makepy support - try and build it.
- ob = gencache.EnsureDispatch(ob)
- if "CLSID" not in ob.__class__.__dict__:
- raise ValueError("Must be a makepy-able object for this to work")
- clsid = ob.CLSID
- # Lots of hoops to support "demand-build" - ie, generating
- # code for an interface first time it is used. We assume the
- # interface name exists in the same library as the object.
- # This is generally the case - only referenced typelibs may be
- # a problem, and we can handle that later. Maybe <wink>
- # So get the generated module for the library itself, then
- # find the interface CLSID there.
- mod = gencache.GetModuleForCLSID(clsid)
- # Get the 'root' module.
- mod = gencache.GetModuleForTypelib(mod.CLSID, mod.LCID,
- mod.MajorVersion, mod.MinorVersion)
- # Find the CLSID of the target
- target_clsid = mod.NamesToIIDMap.get(target)
- if target_clsid is None:
- raise ValueError("The interface name '%s' does not appear in the " \
- "same library as object '%r'" % (target, ob))
- mod = gencache.GetModuleForCLSID(target_clsid)
- if mod is not None:
- target_class = getattr(mod, target)
- # resolve coclass to interface
- target_class = getattr(target_class, "default_interface", target_class)
- return target_class(ob) # auto QI magic happens
- raise ValueError
- class Constants:
- """A container for generated COM constants.
- """
- def __init__(self):
- self.__dicts__ = [] # A list of dictionaries
- def __getattr__(self, a):
- for d in self.__dicts__:
- if a in d:
- return d[a]
- raise AttributeError(a)
- # And create an instance.
- constants = Constants()
- # A helpers for DispatchWithEvents - this becomes __setattr__ for the
- # temporary class.
- def _event_setattr_(self, attr, val):
- try:
- # Does the COM object have an attribute of this name?
- self.__class__.__bases__[0].__setattr__(self, attr, val)
- except AttributeError:
- # Otherwise just stash it away in the instance.
- self.__dict__[attr] = val
- # An instance of this "proxy" is created to break the COM circular references
- # that exist (ie, when we connect to the COM events, COM keeps a reference
- # to the object. Thus, the Event connection must be manually broken before
- # our object can die. This solves the problem by manually breaking the connection
- # to the real object as the proxy dies.
- class EventsProxy:
- def __init__(self, ob):
- self.__dict__['_obj_'] = ob
- def __del__(self):
- try:
- # If there is a COM error on disconnection we should
- # just ignore it - object probably already shut down...
- self._obj_.close()
- except pythoncom.com_error:
- pass
- def __getattr__(self, attr):
- return getattr(self._obj_, attr)
- def __setattr__(self, attr, val):
- setattr(self._obj_, attr, val)
- def DispatchWithEvents(clsid, user_event_class):
- """Create a COM object that can fire events to a user defined class.
- clsid -- The ProgID or CLSID of the object to create.
- user_event_class -- A Python class object that responds to the events.
- This requires makepy support for the COM object being created. If
- this support does not exist it will be automatically generated by
- this function. If the object does not support makepy, a TypeError
- exception will be raised.
- The result is a class instance that both represents the COM object
- and handles events from the COM object.
- It is important to note that the returned instance is not a direct
- instance of the user_event_class, but an instance of a temporary
- class object that derives from three classes:
- * The makepy generated class for the COM object
- * The makepy generated class for the COM events
- * The user_event_class as passed to this function.
- If this is not suitable, see the getevents function for an alternative
- technique of handling events.
- Object Lifetimes: Whenever the object returned from this function is
- cleaned-up by Python, the events will be disconnected from
- the COM object. This is almost always what should happen,
- but see the documentation for getevents() for more details.
- Example:
- >>> class IEEvents:
- ... def OnVisible(self, visible):
- ... print "Visible changed:", visible
- ...
- >>> ie = DispatchWithEvents("InternetExplorer.Application", IEEvents)
- >>> ie.Visible = 1
- Visible changed: 1
- >>>
- """
- # Create/Get the object.
- disp = Dispatch(clsid)
- if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
- try:
- ti = disp._oleobj_.GetTypeInfo()
- disp_clsid = ti.GetTypeAttr()[0]
- tlb, index = ti.GetContainingTypeLib()
- tla = tlb.GetLibAttr()
- gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
- # Get the class from the module.
- disp_class = gencache.GetClassForProgID(str(disp_clsid))
- except pythoncom.com_error:
- raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
- else:
- disp_class = disp.__class__
- # If the clsid was an object, get the clsid
- clsid = disp_class.CLSID
- # Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class.
- # XXX - we are still "classic style" classes in py2x, so we need can't yet
- # use 'type()' everywhere - revisit soon, as py2x will move to new-style too...
- try:
- from types import ClassType as new_type
- except ImportError:
- new_type = type # py3k
- events_class = getevents(clsid)
- if events_class is None:
- raise ValueError("This COM object does not support events.")
- result_class = new_type("COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__" : _event_setattr_})
- instance = result_class(disp._oleobj_) # This only calls the first base class __init__.
- events_class.__init__(instance, instance)
- if hasattr(user_event_class, "__init__"):
- user_event_class.__init__(instance)
- return EventsProxy(instance)
- def WithEvents(disp, user_event_class):
- """Similar to DispatchWithEvents - except that the returned
- object is *not* also usable as the original Dispatch object - that is
- the returned object is not dispatchable.
- The difference is best summarised by example.
- >>> class IEEvents:
- ... def OnVisible(self, visible):
- ... print "Visible changed:", visible
- ...
- >>> ie = Dispatch("InternetExplorer.Application")
- >>> ie_events = WithEvents(ie, IEEvents)
- >>> ie.Visible = 1
- Visible changed: 1
- Compare with the code sample for DispatchWithEvents, where you get a
- single object that is both the interface and the event handler. Note that
- the event handler instance will *not* be able to use 'self.' to refer to
- IE's methods and properties.
- This is mainly useful where using DispatchWithEvents causes
- circular reference problems that the simple proxy doesn't deal with
- """
- disp = Dispatch(disp)
- if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
- try:
- ti = disp._oleobj_.GetTypeInfo()
- disp_clsid = ti.GetTypeAttr()[0]
- tlb, index = ti.GetContainingTypeLib()
- tla = tlb.GetLibAttr()
- gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
- # Get the class from the module.
- disp_class = gencache.GetClassForProgID(str(disp_clsid))
- except pythoncom.com_error:
- raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
- else:
- disp_class = disp.__class__
- # Get the clsid
- clsid = disp_class.CLSID
- # Create a new class that derives from 2 classes - the event sink
- # class and the user class.
- try:
- from types import ClassType as new_type
- except ImportError:
- new_type = type # py3k
- events_class = getevents(clsid)
- if events_class is None:
- raise ValueError("This COM object does not support events.")
- result_class = new_type("COMEventClass", (events_class, user_event_class), {})
- instance = result_class(disp) # This only calls the first base class __init__.
- if hasattr(user_event_class, "__init__"):
- user_event_class.__init__(instance)
- return instance
- def getevents(clsid):
- """Determine the default outgoing interface for a class, given
- either a clsid or progid. It returns a class - you can
- conveniently derive your own handler from this class and implement
- the appropriate methods.
- This method relies on the classes produced by makepy. You must use
- either makepy or the gencache module to ensure that the
- appropriate support classes have been generated for the com server
- that you will be handling events from.
- Beware of COM circular references. When the Events class is connected
- to the COM object, the COM object itself keeps a reference to the Python
- events class. Thus, neither the Events instance or the COM object will
- ever die by themselves. The 'close' method on the events instance
- must be called to break this chain and allow standard Python collection
- rules to manage object lifetimes. Note that DispatchWithEvents() does
- work around this problem by the use of a proxy object, but if you use
- the getevents() function yourself, you must make your own arrangements
- to manage this circular reference issue.
- Beware of creating Python circular references: this will happen if your
- handler has a reference to an object that has a reference back to
- the event source. Call the 'close' method to break the chain.
- Example:
- >>>win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1)
- <module 'win32com.gen_py.....
- >>>
- >>> class InternetExplorerEvents(win32com.client.getevents("InternetExplorer.Application.1")):
- ... def OnVisible(self, Visible):
- ... print "Visibility changed: ", Visible
- ...
- >>>
- >>> ie=win32com.client.Dispatch("InternetExplorer.Application.1")
- >>> events=InternetExplorerEvents(ie)
- >>> ie.Visible=1
- Visibility changed: 1
- >>>
- """
- # find clsid given progid or clsid
- clsid=str(pywintypes.IID(clsid))
- # return default outgoing interface for that class
- klass = gencache.GetClassForCLSID(clsid)
- try:
- return klass.default_source
- except AttributeError:
- # See if we have a coclass for the interfaces.
- try:
- return gencache.GetClassForCLSID(klass.coclass_clsid).default_source
- except AttributeError:
- return None
- # A Record object, as used by the COM struct support
- def Record(name, object):
- """Creates a new record object, given the name of the record,
- and an object from the same type library.
- Example usage would be:
- app = win32com.client.Dispatch("Some.Application")
- point = win32com.client.Record("SomeAppPoint", app)
- point.x = 0
- point.y = 0
- app.MoveTo(point)
- """
- # XXX - to do - probably should allow "object" to already be a module object.
- from . import gencache
- object = gencache.EnsureDispatch(object)
- module = sys.modules[object.__class__.__module__]
- # to allow us to work correctly with "demand generated" code,
- # we must use the typelib CLSID to obtain the module
- # (otherwise we get the sub-module for the object, which
- # does not hold the records)
- # thus, package may be module, or may be module's parent if demand generated.
- package = gencache.GetModuleForTypelib(module.CLSID, module.LCID, module.MajorVersion, module.MinorVersion)
- try:
- struct_guid = package.RecordMap[name]
- except KeyError:
- raise ValueError("The structure '%s' is not defined in module '%s'" % (name, package))
- return pythoncom.GetRecordFromGuids(module.CLSID, module.MajorVersion, module.MinorVersion, module.LCID, struct_guid)
- ############################################
- # The base of all makepy generated classes
- ############################################
- class DispatchBaseClass:
- def __init__(self, oobj=None):
- if oobj is None:
- oobj = pythoncom.new(self.CLSID)
- elif isinstance(oobj, DispatchBaseClass):
- try:
- oobj = oobj._oleobj_.QueryInterface(self.CLSID, pythoncom.IID_IDispatch) # Must be a valid COM instance
- except pythoncom.com_error as details:
- import winerror
- # Some stupid objects fail here, even tho it is _already_ IDispatch!!??
- # Eg, Lotus notes.
- # So just let it use the existing object if E_NOINTERFACE
- if details.hresult != winerror.E_NOINTERFACE:
- raise
- oobj = oobj._oleobj_
- self.__dict__["_oleobj_"] = oobj # so we dont call __setattr__
- def __dir__(self):
- lst = list(self.__dict__.keys()) + dir(self.__class__) \
- + list(self._prop_map_get_.keys()) + list(self._prop_map_put_.keys())
- try: lst += [p.Name for p in self.Properties_]
- except AttributeError:
- pass
- return list(set(lst))
- # Provide a prettier name than the CLSID
- def __repr__(self):
- # Need to get the docstring for the module for this class.
- try:
- mod_doc = sys.modules[self.__class__.__module__].__doc__
- if mod_doc:
- mod_name = "win32com.gen_py." + mod_doc
- else:
- mod_name = sys.modules[self.__class__.__module__].__name__
- except KeyError:
- mod_name = "win32com.gen_py.unknown"
- return "<%s.%s instance at 0x%s>" % (mod_name, self.__class__.__name__, id(self))
- # Delegate comparison to the oleobjs, as they know how to do identity.
- def __eq__(self, other):
- other = getattr(other, "_oleobj_", other)
- return self._oleobj_ == other
- def __ne__(self, other):
- other = getattr(other, "_oleobj_", other)
- return self._oleobj_ != other
- def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
- return self._get_good_object_(
- self._oleobj_.InvokeTypes(dispid, 0, wFlags, retType, argTypes, *args),
- user, resultCLSID)
- def __getattr__(self, attr):
- args=self._prop_map_get_.get(attr)
- if args is None:
- raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
- return self._ApplyTypes_(*args)
- def __setattr__(self, attr, value):
- if attr in self.__dict__: self.__dict__[attr] = value; return
- try:
- args, defArgs=self._prop_map_put_[attr]
- except KeyError:
- raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
- self._oleobj_.Invoke(*(args + (value,) + defArgs))
- def _get_good_single_object_(self, obj, obUserName=None, resultCLSID=None):
- return _get_good_single_object_(obj, obUserName, resultCLSID)
- def _get_good_object_(self, obj, obUserName=None, resultCLSID=None):
- return _get_good_object_(obj, obUserName, resultCLSID)
- # XXX - These should be consolidated with dynamic.py versions.
- def _get_good_single_object_(obj, obUserName=None, resultCLSID=None):
- if _PyIDispatchType==type(obj):
- return Dispatch(obj, obUserName, resultCLSID)
- return obj
- def _get_good_object_(obj, obUserName=None, resultCLSID=None):
- if obj is None:
- return None
- elif isinstance(obj, tuple):
- obUserNameTuple = (obUserName,) * len(obj)
- resultCLSIDTuple = (resultCLSID,) * len(obj)
- return tuple(map(_get_good_object_, obj, obUserNameTuple, resultCLSIDTuple))
- else:
- return _get_good_single_object_(obj, obUserName, resultCLSID)
- class CoClassBaseClass:
- def __init__(self, oobj=None):
- if oobj is None: oobj = pythoncom.new(self.CLSID)
- self.__dict__["_dispobj_"] = self.default_interface(oobj)
- def __repr__(self):
- return "<win32com.gen_py.%s.%s>" % (__doc__, self.__class__.__name__)
- def __getattr__(self, attr):
- d=self.__dict__["_dispobj_"]
- if d is not None: return getattr(d, attr)
- raise AttributeError(attr)
- def __setattr__(self, attr, value):
- if attr in self.__dict__: self.__dict__[attr] = value; return
- try:
- d=self.__dict__["_dispobj_"]
- if d is not None:
- d.__setattr__(attr, value)
- return
- except AttributeError:
- pass
- self.__dict__[attr] = value
- # Special methods don't use __getattr__ etc, so explicitly delegate here.
- # Some wrapped objects might not have them, but that's OK - the attribute
- # error can just bubble up.
- def __call__(self, *args, **kwargs):
- return self.__dict__["_dispobj_"].__call__(*args, **kwargs)
- def __str__(self, *args):
- return self.__dict__["_dispobj_"].__str__(*args)
- def __int__(self, *args):
- return self.__dict__["_dispobj_"].__int__(*args)
- def __iter__(self):
- return self.__dict__["_dispobj_"].__iter__()
- def __len__(self):
- return self.__dict__["_dispobj_"].__len__()
- def __nonzero__(self):
- return self.__dict__["_dispobj_"].__nonzero__()
- # A very simple VARIANT class. Only to be used with poorly-implemented COM
- # objects. If an object accepts an arg which is a simple "VARIANT", but still
- # is very pickly about the actual variant type (eg, isn't happy with a VT_I4,
- # which it would get from a Python integer), you can use this to force a
- # particular VT.
- class VARIANT(object):
- def __init__(self, vt, value):
- self.varianttype = vt
- self._value = value
- # 'value' is a property so when set by pythoncom it gets any magic wrapping
- # which normally happens for result objects
- def _get_value(self):
- return self._value
- def _set_value(self, newval):
- self._value = _get_good_object_(newval)
- def _del_value(self):
- del self._value
- value = property(_get_value, _set_value, _del_value)
- def __repr__(self):
- return "win32com.client.VARIANT(%r, %r)" % (self.varianttype, self._value)
|