0001 # This module exists to create the "best" dispatch object for a given 0002 # object. If "makepy" support for a given object is detected, it is 0003 # used, otherwise a dynamic dispatch object. 0004 0005 # Note that if the unknown dispatch object then returns a known 0006 # dispatch object, the known class will be used. This contrasts 0007 # with dynamic.Dispatch behaviour, where dynamic objects are always used. 0008 import __builtin__ 0009 # For some bizarre reason, __builtins__ fails with attribute error on __dict__ here? 0010 NeedUnicodeConversions = not hasattr(__builtin__, "unicode") 0011 0012 import dynamic, gencache, pythoncom 0013 import sys 0014 import pywintypes 0015 from types import TupleType 0016 from pywintypes import UnicodeType 0017 _PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch] 0018 0019 0020 def __WrapDispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, \ 0021 UnicodeToString = NeedUnicodeConversions, clsctx = pythoncom.CLSCTX_SERVER, 0022 WrapperClass = None): 0023 """ 0024 Helper function to return a makepy generated class for a CLSID if it exists, 0025 otherwise cope by using CDispatch. 0026 """ 0027 if resultCLSID is None: 0028 try: 0029 typeinfo = dispatch.GetTypeInfo() 0030 if typeinfo is not None: # Some objects return NULL, some raise exceptions... 0031 resultCLSID = str(typeinfo.GetTypeAttr()[0]) 0032 except pythoncom.com_error: 0033 pass 0034 if resultCLSID is not None: 0035 import gencache 0036 # Attempt to load generated module support 0037 # This may load the module, and make it available 0038 klass = gencache.GetClassForCLSID(resultCLSID) 0039 if klass is not None: 0040 return klass(dispatch) 0041 0042 # Return a "dynamic" object - best we can do! 0043 if WrapperClass is None: WrapperClass = CDispatch 0044 return dynamic.Dispatch(dispatch, userName, WrapperClass, typeinfo, UnicodeToString=UnicodeToString,clsctx=clsctx) 0045 0046 0047 def GetObject(Pathname = None, Class = None, clsctx = None): 0048 """ 0049 Mimic VB's GetObject() function. 0050 0051 ob = GetObject(Class = "ProgID") or GetObject(Class = clsid) will 0052 connect to an already running instance of the COM object. 0053 0054 ob = GetObject(r"c:\blah\blah\foo.xls") (aka the COM moniker syntax) 0055 will return a ready to use Python wrapping of the required COM object. 0056 0057 Note: You must specifiy one or the other of these arguments. I know 0058 this isn't pretty, but it is what VB does. Blech. If you don't 0059 I'll throw ValueError at you. :) 0060 0061 This will most likely throw pythoncom.com_error if anything fails. 0062 """ 0063 if clsctx is None: 0064 clsctx = pythoncom.CLSCTX_ALL 0065 0066 if (Pathname is None and Class is None) or \ 0067 (Pathname is not None and Class is not None): 0068 raise ValueError, "You must specify a value for Pathname or Class, but not both." 0069 0070 if Class is not None: 0071 return GetActiveObject(Class, clsctx) 0072 else: 0073 return Moniker(Pathname, clsctx) 0074 0075 def GetActiveObject(Class, clsctx = pythoncom.CLSCTX_ALL): 0076 """ 0077 Python friendly version of GetObject's ProgID/CLSID functionality. 0078 """ 0079 resultCLSID = pywintypes.IID(Class) 0080 dispatch = pythoncom.GetActiveObject(resultCLSID) 0081 dispatch = dispatch.QueryInterface(pythoncom.IID_IDispatch) 0082 return __WrapDispatch(dispatch, Class, resultCLSID = resultCLSID, clsctx = clsctx) 0083 0084 def Moniker(Pathname, clsctx = pythoncom.CLSCTX_ALL): 0085 """ 0086 Python friendly version of GetObject's moniker functionality. 0087 """ 0088 moniker, i, bindCtx = pythoncom.MkParseDisplayName(Pathname) 0089 dispatch = moniker.BindToObject(bindCtx, None, pythoncom.IID_IDispatch) 0090 return __WrapDispatch(dispatch, Pathname, clsctx = clsctx) 0091 0092 def Dispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=NeedUnicodeConversions, clsctx = pythoncom.CLSCTX_SERVER): 0093 """Creates a Dispatch based COM object. 0094 """ 0095 dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx) 0096 return __WrapDispatch(dispatch, userName, resultCLSID, typeinfo, UnicodeToString, clsctx) 0097 0098 def DispatchEx(clsid, machine=None, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=NeedUnicodeConversions, clsctx = None): 0099 """Creates a Dispatch based COM object on a specific machine. 0100 """ 0101 # If InProc is registered, DCOM will use it regardless of the machine name 0102 # (and regardless of the DCOM config for the object.) So unless the user 0103 # specifies otherwise, we exclude inproc apps when a remote machine is used. 0104 if clsctx is None: 0105 clsctx = pythoncom.CLSCTX_SERVER 0106 if machine is not None: clsctx = clsctx & ~pythoncom.CLSCTX_INPROC 0107 if machine is None: 0108 serverInfo = None 0109 else: 0110 serverInfo = (machine,) 0111 if userName is None: userName = clsid 0112 dispatch = pythoncom.CoCreateInstanceEx(clsid, None, clsctx, serverInfo, (pythoncom.IID_IDispatch,))[0] 0113 return Dispatch(dispatch, userName, resultCLSID, typeinfo, UnicodeToString=UnicodeToString, clsctx=clsctx) 0114 0115 class CDispatch(dynamic.CDispatch): 0116 """ 0117 The dynamic class used as a last resort. 0118 The purpose of this overriding of dynamic.CDispatch is to perpetuate the policy 0119 of using the makepy generated wrapper Python class instead of dynamic.CDispatch 0120 if/when possible. 0121 """ 0122 def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString = NeedUnicodeConversions): 0123 return Dispatch(ob, userName, returnCLSID,None,UnicodeToString) 0124 0125 def CastTo(ob, target): 0126 """'Cast' a COM object to another interface""" 0127 # todo - should support target being an IID 0128 if hasattr(target, "index"): # string like 0129 # for now, we assume makepy for this to work. 0130 if not ob.__class__.__dict__.has_key("CLSID"): 0131 # Eeek - no makepy support - try and build it. 0132 ob = gencache.EnsureDispatch(ob) 0133 if not ob.__class__.__dict__.has_key("CLSID"): 0134 raise ValueError, "Must be a makepy-able object for this to work" 0135 clsid = ob.CLSID 0136 # Lots of hoops to support "demand-build" - ie, generating 0137 # code for an interface first time it is used. We assume the 0138 # interface name exists in the same library as the object. 0139 # This is generally the case - only referenced typelibs may be 0140 # a problem, and we can handle that later. Maybe <wink> 0141 # So get the generated module for the library itself, then 0142 # find the interface CLSID there. 0143 mod = gencache.GetModuleForCLSID(clsid) 0144 # Get the 'root' module. 0145 mod = gencache.GetModuleForTypelib(mod.CLSID, mod.LCID, 0146 mod.MajorVersion, mod.MinorVersion) 0147 # Find the CLSID of the target 0148 target_clsid = mod.NamesToIIDMap.get(target) 0149 if target_clsid is None: 0150 raise ValueError, "The interface name '%s' does not appear in the " \ 0151 "same library as object '%r'" % (target, ob) 0152 mod = gencache.GetModuleForCLSID(target_clsid) 0153 target_class = getattr(mod, target) 0154 # resolve coclass to interface 0155 target_class = getattr(target_class, "default_interface", target_class) 0156 return target_class(ob) # auto QI magic happens 0157 raise ValueError, "This object can not be cast" 0158 0159 class Constants: 0160 """A container for generated COM constants. 0161 """ 0162 def __init__(self): 0163 self.__dicts__ = [] # A list of dictionaries 0164 def __getattr__(self, a): 0165 for d in self.__dicts__: 0166 if d.has_key(a): 0167 return d[a] 0168 raise AttributeError, a 0169 0170 # And create an instance. 0171 constants = Constants() 0172 0173 # A helpers for DispatchWithEvents - this becomes __setattr__ for the 0174 # temporary class. 0175 def _event_setattr_(self, attr, val): 0176 try: 0177 # Does the COM object have an attribute of this name? 0178 self.__class__.__bases__[0].__setattr__(self, attr, val) 0179 except AttributeError: 0180 # Otherwise just stash it away in the instance. 0181 self.__dict__[attr] = val 0182 0183 # An instance of this "proxy" is created to break the COM circular references 0184 # that exist (ie, when we connect to the COM events, COM keeps a reference 0185 # to the object. Thus, the Event connection must be manually broken before 0186 # our object can die. This solves the problem by manually breaking the connection 0187 # to the real object as the proxy dies. 0188 class EventsProxy: 0189 def __init__(self, ob): 0190 self.__dict__['_obj_'] = ob 0191 def __del__(self): 0192 try: 0193 # If there is a COM error on disconnection we should 0194 # just ignore it - object probably already shut down... 0195 self._obj_.close() 0196 except pythoncom.com_error: 0197 pass 0198 def __getattr__(self, attr): 0199 return getattr(self._obj_, attr) 0200 def __setattr__(self, attr, val): 0201 setattr(self._obj_, attr, val) 0202 0203 def DispatchWithEvents(clsid, user_event_class): 0204 """Create a COM object that can fire events to a user defined class. 0205 clsid -- The ProgID or CLSID of the object to create. 0206 user_event_class -- A Python class object that responds to the events. 0207 0208 This requires makepy support for the COM object being created. If 0209 this support does not exist it will be automatically generated by 0210 this function. If the object does not support makepy, a TypeError 0211 exception will be raised. 0212 0213 The result is a class instance that both represents the COM object 0214 and handles events from the COM object. 0215 0216 It is important to note that the returned instance is not a direct 0217 instance of the user_event_class, but an instance of a temporary 0218 class object that derives from three classes: 0219 * The makepy generated class for the COM object 0220 * The makepy generated class for the COM events 0221 * The user_event_class as passed to this function. 0222 0223 If this is not suitable, see the getevents function for an alternative 0224 technique of handling events. 0225 0226 Object Lifetimes: Whenever the object returned from this function is 0227 cleaned-up by Python, the events will be disconnected from 0228 the COM object. This is almost always what should happen, 0229 but see the documentation for getevents() for more details. 0230 0231 Example: 0232 0233 >>> class IEEvents: 0234 ... def OnVisible(self, visible): 0235 ... print "Visible changed:", visible 0236 ... 0237 >>> ie = DispatchWithEvents("InternetExplorer.Application", IEEvents) 0238 >>> ie.Visible = 1 0239 Visible changed: 1 0240 >>> 0241 """ 0242 # Create/Get the object. 0243 disp = Dispatch(clsid) 0244 if not disp.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it. 0245 try: 0246 ti = disp._oleobj_.GetTypeInfo() 0247 disp_clsid = ti.GetTypeAttr()[0] 0248 tlb, index = ti.GetContainingTypeLib() 0249 tla = tlb.GetLibAttr() 0250 gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0) 0251 # Get the class from the module. 0252 disp_class = gencache.GetClassForProgID(str(disp_clsid)) 0253 except pythoncom.com_error: 0254 raise TypeError, "This COM object can not automate the makepy process - please run makepy manually for this object" 0255 else: 0256 disp_class = disp.__class__ 0257 # If the clsid was an object, get the clsid 0258 clsid = disp_class.CLSID 0259 # Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class. 0260 import new 0261 events_class = getevents(clsid) 0262 if events_class is None: 0263 raise ValueError, "This COM object does not support events." 0264 result_class = new.classobj("COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__" : _event_setattr_}) 0265 instance = result_class(disp._oleobj_) # This only calls the first base class __init__. 0266 events_class.__init__(instance, instance) 0267 if hasattr(user_event_class, "__init__"): 0268 user_event_class.__init__(instance) 0269 return EventsProxy(instance) 0270 0271 def WithEvents(disp, user_event_class): 0272 """Similar to DispatchWithEvents - except that the returned 0273 object is *not* also usable as the original Dispatch object - that is 0274 the returned object is not dispatchable. 0275 0276 The difference is best summarised by example. 0277 0278 >>> class IEEvents: 0279 ... def OnVisible(self, visible): 0280 ... print "Visible changed:", visible 0281 ... 0282 >>> ie = Dispatch("InternetExplorer.Application") 0283 >>> ie_events = WithEvents(ie, IEEvents) 0284 >>> ie.Visible = 1 0285 Visible changed: 1 0286 0287 Compare with the code sample for DispatchWithEvents, where you get a 0288 single object that is both the interface and the event handler. Note that 0289 the event handler instance will *not* be able to use 'self.' to refer to 0290 IE's methods and properties. 0291 0292 This is mainly useful where using DispatchWithEvents causes 0293 circular reference problems that the simple proxy doesn't deal with 0294 """ 0295 disp = Dispatch(disp) 0296 if not disp.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it. 0297 try: 0298 ti = disp._oleobj_.GetTypeInfo() 0299 disp_clsid = ti.GetTypeAttr()[0] 0300 tlb, index = ti.GetContainingTypeLib() 0301 tla = tlb.GetLibAttr() 0302 gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0) 0303 # Get the class from the module. 0304 disp_class = gencache.GetClassForProgID(str(disp_clsid)) 0305 except pythoncom.com_error: 0306 raise TypeError, "This COM object can not automate the makepy process - please run makepy manually for this object" 0307 else: 0308 disp_class = disp.__class__ 0309 # Get the clsid 0310 clsid = disp_class.CLSID 0311 # Create a new class that derives from 2 classes - the event sink 0312 # class and the user class. 0313 import new 0314 events_class = getevents(clsid) 0315 if events_class is None: 0316 raise ValueError, "This COM object does not support events." 0317 result_class = new.classobj("COMEventClass", (events_class, user_event_class), {}) 0318 instance = result_class(disp) # This only calls the first base class __init__. 0319 if hasattr(user_event_class, "__init__"): 0320 user_event_class.__init__(instance) 0321 return instance 0322 0323 def getevents(clsid): 0324 """Determine the default outgoing interface for a class, given 0325 either a clsid or progid. It returns a class - you can 0326 conveniently derive your own handler from this class and implement 0327 the appropriate methods. 0328 0329 This method relies on the classes produced by makepy. You must use 0330 either makepy or the gencache module to ensure that the 0331 appropriate support classes have been generated for the com server 0332 that you will be handling events from. 0333 0334 Beware of COM circular references. When the Events class is connected 0335 to the COM object, the COM object itself keeps a reference to the Python 0336 events class. Thus, neither the Events instance or the COM object will 0337 ever die by themselves. The 'close' method on the events instance 0338 must be called to break this chain and allow standard Python collection 0339 rules to manage object lifetimes. Note that DispatchWithEvents() does 0340 work around this problem by the use of a proxy object, but if you use 0341 the getevents() function yourself, you must make your own arrangements 0342 to manage this circular reference issue. 0343 0344 Beware of creating Python circular references: this will happen if your 0345 handler has a reference to an object that has a reference back to 0346 the event source. Call the 'close' method to break the chain. 0347 0348 Example: 0349 0350 >>>win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1) 0351 <module 'win32com.gen_py..... 0352 >>> 0353 >>> class InternetExplorerEvents(win32com.client.getevents("InternetExplorer.Application.1")): 0354 ... def OnVisible(self, Visible): 0355 ... print "Visibility changed: ", Visible 0356 ... 0357 >>> 0358 >>> ie=win32com.client.Dispatch("InternetExplorer.Application.1") 0359 >>> events=InternetExplorerEvents(ie) 0360 >>> ie.Visible=1 0361 Visibility changed: 1 0362 >>> 0363 """ 0364 0365 # find clsid given progid or clsid 0366 clsid=str(pywintypes.IID(clsid)) 0367 # return default outgoing interface for that class 0368 klass = gencache.GetClassForCLSID(clsid) 0369 try: 0370 return klass.default_source 0371 except AttributeError: 0372 # See if we have a coclass for the interfaces. 0373 try: 0374 return gencache.GetClassForCLSID(klass.coclass_clsid).default_source 0375 except AttributeError: 0376 return None 0377 0378 # A Record object, as used by the COM struct support 0379 def Record(name, object): 0380 """Creates a new record object, given the name of the record, 0381 and an object from the same type library. 0382 0383 Example usage would be: 0384 app = win32com.client.Dispatch("Some.Application") 0385 point = win32com.client.Record("SomeAppPoint", app) 0386 point.x = 0 0387 point.y = 0 0388 app.MoveTo(point) 0389 """ 0390 # XXX - to do - probably should allow "object" to already be a module object. 0391 import gencache 0392 object = gencache.EnsureDispatch(object) 0393 module = sys.modules[object.__class__.__module__] 0394 # to allow us to work correctly with "demand generated" code, 0395 # we must use the typelib CLSID to obtain the module 0396 # (otherwise we get the sub-module for the object, which 0397 # does not hold the records) 0398 # thus, package may be module, or may be module's parent if demand generated. 0399 package = gencache.GetModuleForTypelib(module.CLSID, module.LCID, module.MajorVersion, module.MinorVersion) 0400 try: 0401 struct_guid = package.RecordMap[name] 0402 except KeyError: 0403 raise ValueError, "The structure '%s' is not defined in module '%s'" % (name, package) 0404 0405 return pythoncom.GetRecordFromGuids(module.CLSID, module.MajorVersion, module.MinorVersion, module.LCID, struct_guid) 0406 0407 0408 ############################################ 0409 # The base of all makepy generated classes 0410 ############################################ 0411 class DispatchBaseClass: 0412 def __init__(self, oobj=None): 0413 if oobj is None: 0414 oobj = pythoncom.new(self.CLSID) 0415 elif type(self) == type(oobj): # An instance 0416 try: 0417 oobj = oobj._oleobj_.QueryInterface(self.CLSID, pythoncom.IID_IDispatch) # Must be a valid COM instance 0418 except pythoncom.com_error, details: 0419 import winerror 0420 # Some stupid objects fail here, even tho it is _already_ IDispatch!!?? 0421 # Eg, Lotus notes. 0422 # So just let it use the existing object if E_NOINTERFACE 0423 if details[0] != winerror.E_NOINTERFACE: 0424 raise 0425 oobj = oobj._oleobj_ 0426 self.__dict__["_oleobj_"] = oobj # so we dont call __setattr__ 0427 # Provide a prettier name than the CLSID 0428 def __repr__(self): 0429 # Need to get the docstring for the module for this class. 0430 try: 0431 mod_doc = sys.modules[self.__class__.__module__].__doc__ 0432 if mod_doc: 0433 mod_name = "win32com.gen_py." + mod_doc 0434 else: 0435 mod_name = sys.modules[self.__class__.__module__].__name__ 0436 except KeyError: 0437 mod_name = "win32com.gen_py.unknown" 0438 return "<%s.%s instance at 0x%s>" % (mod_name, self.__class__.__name__, id(self)) 0439 # Delegate comparison to the oleobjs, as they know how to do identity. 0440 def __cmp__(self, other): 0441 other = getattr(other, "_oleobj_", other) 0442 return cmp(self._oleobj_, other) 0443 0444 def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, 0445 resultCLSID, *args): 0446 return self._get_good_object_( 0447 self._oleobj_.InvokeTypes( 0448 dispid, 0, wFlags, retType, argTypes, *args), 0449 user, resultCLSID) 0450 0451 def __getattr__(self, attr): 0452 args=self._prop_map_get_.get(attr) 0453 if args is None: 0454 raise AttributeError, "'%s' object has no attribute '%s'" % (repr(self), attr) 0455 return self._ApplyTypes_(*args) 0456 0457 def __setattr__(self, attr, value): 0458 if self.__dict__.has_key(attr): self.__dict__[attr] = value; return 0459 try: 0460 args, defArgs=self._prop_map_put_[attr] 0461 except KeyError: 0462 raise AttributeError, "'%s' object has no attribute '%s'" % (repr(self), attr) 0463 self._oleobj_.Invoke(*(args + (value,) + defArgs)) 0464 def _get_good_single_object_(self, obj, obUserName=None, resultCLSID=None): 0465 return _get_good_single_object_(obj, obUserName, resultCLSID) 0466 def _get_good_object_(self, obj, obUserName=None, resultCLSID=None): 0467 return _get_good_object_(obj, obUserName, resultCLSID) 0468 0469 # XXX - These should be consolidated with dynamic.py versions. 0470 def _get_good_single_object_(obj, obUserName=None, resultCLSID=None): 0471 if _PyIDispatchType==type(obj): 0472 return Dispatch(obj, obUserName, resultCLSID, UnicodeToString=NeedUnicodeConversions) 0473 elif NeedUnicodeConversions and UnicodeType==type(obj): 0474 return str(obj) 0475 return obj 0476 0477 def _get_good_object_(obj, obUserName=None, resultCLSID=None): 0478 if obj is None: 0479 return None 0480 elif type(obj)==TupleType: 0481 obUserNameTuple = (obUserName,) * len(obj) 0482 resultCLSIDTuple = (resultCLSID,) * len(obj) 0483 return tuple(map(_get_good_object_, obj, obUserNameTuple, resultCLSIDTuple)) 0484 else: 0485 return _get_good_single_object_(obj, obUserName, resultCLSID) 0486 0487 class CoClassBaseClass: 0488 def __init__(self, oobj=None): 0489 if oobj is None: oobj = pythoncom.new(self.CLSID) 0490 self.__dict__["_dispobj_"] = self.default_interface(oobj) 0491 def __repr__(self): 0492 return "<win32com.gen_py.%s.%s>" % (__doc__, self.__class__.__name__) 0493 0494 def __getattr__(self, attr): 0495 d=self.__dict__["_dispobj_"] 0496 if d is not None: return getattr(d, attr) 0497 raise AttributeError, attr 0498 def __setattr__(self, attr, value): 0499 if self.__dict__.has_key(attr): self.__dict__[attr] = value; return 0500 try: 0501 d=self.__dict__["_dispobj_"] 0502 if d is not None: 0503 d.__setattr__(attr, value) 0504 return 0505 except AttributeError: 0506 pass 0507 self.__dict__[attr] = value 0508
Generated by PyXR 0.9.4