codekingpro/portable-devtools
114k
1"""Policies 2 3Note that Dispatchers are now implemented in "dispatcher.py", but4are still documented here.5 6Policies7 8 A policy is an object which manages the interaction between a public 9 Python object, and COM . In simple terms, the policy object is the 10 object which is actually called by COM, and it invokes the requested 11 method, fetches/sets the requested property, etc. See the 12 @win32com.server.policy.CreateInstance@ method for a description of13 how a policy is specified or created.14 15 Exactly how a policy determines which underlying object method/property 16 is obtained is up to the policy. A few policies are provided, but you 17 can build your own. See each policy class for a description of how it 18 implements its policy.19 20 There is a policy that allows the object to specify exactly which 21 methods and properties will be exposed. There is also a policy that 22 will dynamically expose all Python methods and properties - even those 23 added after the object has been instantiated.24 25Dispatchers26 27 A Dispatcher is a level in front of a Policy. A dispatcher is the 28 thing which actually receives the COM calls, and passes them to the 29 policy object (which in turn somehow does something with the wrapped 30 object).31 32 It is important to note that a policy does not need to have a dispatcher.33 A dispatcher has the same interface as a policy, and simply steps in its 34 place, delegating to the real policy. The primary use for a Dispatcher 35 is to support debugging when necessary, but without imposing overheads 36 when not (ie, by not using a dispatcher at all).37 38 There are a few dispatchers provided - "tracing" dispatchers which simply 39 prints calls and args (including a variation which uses 40 win32api.OutputDebugString), and a "debugger" dispatcher, which can 41 invoke the debugger when necessary.42 43Error Handling44 45 It is important to realise that the caller of these interfaces may46 not be Python. Therefore, general Python exceptions and tracebacks aren't 47 much use.48 49 In general, there is an Exception class that should be raised, to allow 50 the framework to extract rich COM type error information.51 52 The general rule is that the **only** exception returned from Python COM 53 Server code should be an Exception instance. Any other Python exception 54 should be considered an implementation bug in the server (if not, it 55 should be handled, and an appropriate Exception instance raised). Any 56 other exception is considered "unexpected", and a dispatcher may take 57 special action (see Dispatchers above)58 59 Occasionally, the implementation will raise the policy.error error. 60 This usually means there is a problem in the implementation that the 61 Python programmer should fix.62 63 For example, if policy is asked to wrap an object which it can not 64 support (because, eg, it does not provide _public_methods_ or _dynamic_) 65 then policy.error will be raised, indicating it is a Python programmers 66 problem, rather than a COM error.67 68"""69__author__ = "Greg Stein and Mark Hammond"70 71import sys72import types73 74import pythoncom75import pywintypes76import win32api77import win32con78import winerror79 80# Import a few important constants to speed lookups.81from pythoncom import (82 DISPATCH_METHOD,83 DISPATCH_PROPERTYGET,84 DISPATCH_PROPERTYPUT,85 DISPATCH_PROPERTYPUTREF,86 DISPID_COLLECT,87 DISPID_CONSTRUCTOR,88 DISPID_DESTRUCTOR,89 DISPID_EVALUATE,90 DISPID_NEWENUM,91 DISPID_PROPERTYPUT,92 DISPID_STARTENUM,93 DISPID_UNKNOWN,94 DISPID_VALUE,95)96 97S_OK = 098 99# Few more globals to speed things.100IDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]101IUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]102 103from .exception import COMException104 105error = __name__ + " error"106 107regSpec = "CLSID\\%s\\PythonCOM"108regPolicy = "CLSID\\%s\\PythonCOMPolicy"109regDispatcher = "CLSID\\%s\\PythonCOMDispatcher"110regAddnPath = "CLSID\\%s\\PythonCOMPath"111 112 113def CreateInstance(clsid, reqIID):114 """Create a new instance of the specified IID115 116 The COM framework **always** calls this function to create a new117 instance for the specified CLSID. This function looks up the118 registry for the name of a policy, creates the policy, and asks the119 policy to create the specified object by calling the _CreateInstance_ method.120 121 Exactly how the policy creates the instance is up to the policy. See the122 specific policy documentation for more details.123 """124 # First see is sys.path should have something on it.125 try:126 addnPaths = win32api.RegQueryValue(127 win32con.HKEY_CLASSES_ROOT, regAddnPath % clsid128 ).split(";")129 for newPath in addnPaths:130 if newPath not in sys.path:131 sys.path.insert(0, newPath)132 except win32api.error:133 pass134 try:135 policy = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT, regPolicy % clsid)136 policy = resolve_func(policy)137 except win32api.error:138 policy = DefaultPolicy139 140 try:141 dispatcher = win32api.RegQueryValue(142 win32con.HKEY_CLASSES_ROOT, regDispatcher % clsid143 )144 if dispatcher:145 dispatcher = resolve_func(dispatcher)146 except win32api.error:147 dispatcher = None148 149 if dispatcher:150 retObj = dispatcher(policy, None)151 else:152 retObj = policy(None)153 return retObj._CreateInstance_(clsid, reqIID)154 155 156class BasicWrapPolicy:157 """The base class of policies.158 159 Normally not used directly (use a child class, instead)160 161 This policy assumes we are wrapping another object162 as the COM server. This supports the delegation of the core COM entry points163 to either the wrapped object, or to a child class.164 165 This policy supports the following special attributes on the wrapped object166 167 _query_interface_ -- A handler which can respond to the COM 'QueryInterface' call.168 _com_interfaces_ -- An optional list of IIDs which the interface will assume are169 valid for the object.170 _invoke_ -- A handler which can respond to the COM 'Invoke' call. If this attribute171 is not provided, then the default policy implementation is used. If this attribute172 does exist, it is responsible for providing all required functionality - ie, the173 policy _invoke_ method is not invoked at all (and nor are you able to call it!)174 _getidsofnames_ -- A handler which can respond to the COM 'GetIDsOfNames' call. If this attribute175 is not provided, then the default policy implementation is used. If this attribute176 does exist, it is responsible for providing all required functionality - ie, the177 policy _getidsofnames_ method is not invoked at all (and nor are you able to call it!)178 179 IDispatchEx functionality:180 181 _invokeex_ -- Very similar to _invoke_, except slightly different arguments are used.182 And the result is just the _real_ result (rather than the (hresult, argErr, realResult)183 tuple that _invoke_ uses.184 This is the new, prefered handler (the default _invoke_ handler simply called _invokeex_)185 _getdispid_ -- Very similar to _getidsofnames_, except slightly different arguments are used,186 and only 1 property at a time can be fetched (which is all we support in getidsofnames anyway!)187 This is the new, prefered handler (the default _invoke_ handler simply called _invokeex_)188 _getnextdispid_- uses self._name_to_dispid_ to enumerate the DISPIDs189 """190 191 def __init__(self, object):192 """Initialise the policy object193 194 Params:195 196 object -- The object to wrap. May be None *iff* @BasicWrapPolicy._CreateInstance_@ will be197 called immediately after this to setup a brand new object198 """199 if object is not None:200 self._wrap_(object)201 202 def _CreateInstance_(self, clsid, reqIID):203 """Creates a new instance of a **wrapped** object204 205 This method looks up a "@win32com.server.policy.regSpec@" % clsid entry206 in the registry (using @DefaultPolicy@)207 """208 try:209 classSpec = win32api.RegQueryValue(210 win32con.HKEY_CLASSES_ROOT, regSpec % clsid211 )212 except win32api.error:213 raise error(214 "The object is not correctly registered - %s key can not be read"215 % (regSpec % clsid)216 )217 myob = call_func(classSpec)218 self._wrap_(myob)219 try:220 return pythoncom.WrapObject(self, reqIID)221 except pythoncom.com_error as xxx_todo_changeme:222 (hr, desc, exc, arg) = xxx_todo_changeme.args223 from win32com.util import IIDToInterfaceName224 225 desc = (226 "The object '%r' was created, but does not support the "227 "interface '%s'(%s): %s"228 % (myob, IIDToInterfaceName(reqIID), reqIID, desc)229 )230 raise pythoncom.com_error(hr, desc, exc, arg)231 232 def _wrap_(self, object):233 """Wraps up the specified object.234 235 This function keeps a reference to the passed236 object, and may interogate it to determine how to respond to COM requests, etc.237 """238 # We "clobber" certain of our own methods with ones239 # provided by the wrapped object, iff they exist.240 self._name_to_dispid_ = {}241 ob = self._obj_ = object242 if hasattr(ob, "_query_interface_"):243 self._query_interface_ = ob._query_interface_244 245 if hasattr(ob, "_invoke_"):246 self._invoke_ = ob._invoke_247 248 if hasattr(ob, "_invokeex_"):249 self._invokeex_ = ob._invokeex_250 251 if hasattr(ob, "_getidsofnames_"):252 self._getidsofnames_ = ob._getidsofnames_253 254 if hasattr(ob, "_getdispid_"):255 self._getdispid_ = ob._getdispid_256 257 # Allow for override of certain special attributes.258 if hasattr(ob, "_com_interfaces_"):259 self._com_interfaces_ = []260 # Allow interfaces to be specified by name.261 for i in ob._com_interfaces_:262 if type(i) != pywintypes.IIDType:263 # Prolly a string!264 if i[0] != "{":265 i = pythoncom.InterfaceNames[i]266 else:267 i = pythoncom.MakeIID(i)268 self._com_interfaces_.append(i)269 else:270 self._com_interfaces_ = []271 272 # "QueryInterface" handling.273 def _QueryInterface_(self, iid):274 """The main COM entry-point for QueryInterface.275 276 This checks the _com_interfaces_ attribute and if the interface is not specified277 there, it calls the derived helper _query_interface_278 """279 if iid in self._com_interfaces_:280 return 1281 return self._query_interface_(iid)282 283 def _query_interface_(self, iid):284 """Called if the object does not provide the requested interface in _com_interfaces_,285 and does not provide a _query_interface_ handler.286 287 Returns a result to the COM framework indicating the interface is not supported.288 """289 return 0290 291 # "Invoke" handling.292 def _Invoke_(self, dispid, lcid, wFlags, args):293 """The main COM entry-point for Invoke.294 295 This calls the _invoke_ helper.296 """297 # Translate a possible string dispid to real dispid.298 if type(dispid) == type(""):299 try:300 dispid = self._name_to_dispid_[dispid.lower()]301 except KeyError:302 raise COMException(303 scode=winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found"304 )305 return self._invoke_(dispid, lcid, wFlags, args)306 307 def _invoke_(self, dispid, lcid, wFlags, args):308 # Delegates to the _invokeex_ implementation. This allows309 # a custom policy to define _invokeex_, and automatically get _invoke_ too.310 return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)311 312 # "GetIDsOfNames" handling.313 def _GetIDsOfNames_(self, names, lcid):314 """The main COM entry-point for GetIDsOfNames.315 316 This checks the validity of the arguments, and calls the _getidsofnames_ helper.317 """318 if len(names) > 1:319 raise COMException(320 scode=winerror.DISP_E_INVALID,321 desc="Cannot support member argument names",322 )323 return self._getidsofnames_(names, lcid)324 325 def _getidsofnames_(self, names, lcid):326 ### note: lcid is being ignored...327 return (self._getdispid_(names[0], 0),)328 329 # IDispatchEx support for policies. Most of the IDispathEx functionality330 # by default will raise E_NOTIMPL. Thus it is not necessary for derived331 # policies to explicitely implement all this functionality just to not implement it!332 333 def _GetDispID_(self, name, fdex):334 return self._getdispid_(name, fdex)335 336 def _getdispid_(self, name, fdex):337 try:338 ### TODO - look at the fdex flags!!!339 return self._name_to_dispid_[name.lower()]340 except KeyError:341 raise COMException(scode=winerror.DISP_E_UNKNOWNNAME)342 343 # "InvokeEx" handling.344 def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):345 """The main COM entry-point for InvokeEx.346 347 This calls the _invokeex_ helper.348 """349 # Translate a possible string dispid to real dispid.350 if type(dispid) == type(""):351 try:352 dispid = self._name_to_dispid_[dispid.lower()]353 except KeyError:354 raise COMException(355 scode=winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found"356 )357 return self._invokeex_(dispid, lcid, wFlags, args, kwargs, serviceProvider)358 359 def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):360 """A stub for _invokeex_ - should never be called.361 362 Simply raises an exception.363 """364 # Base classes should override this method (and not call the base)365 raise error("This class does not provide _invokeex_ semantics")366 367 def _DeleteMemberByName_(self, name, fdex):368 return self._deletememberbyname_(name, fdex)369 370 def _deletememberbyname_(self, name, fdex):371 raise COMException(scode=winerror.E_NOTIMPL)372 373 def _DeleteMemberByDispID_(self, id):374 return self._deletememberbydispid(id)375 376 def _deletememberbydispid_(self, id):377 raise COMException(scode=winerror.E_NOTIMPL)378 379 def _GetMemberProperties_(self, id, fdex):380 return self._getmemberproperties_(id, fdex)381 382 def _getmemberproperties_(self, id, fdex):383 raise COMException(scode=winerror.E_NOTIMPL)384 385 def _GetMemberName_(self, dispid):386 return self._getmembername_(dispid)387 388 def _getmembername_(self, dispid):389 raise COMException(scode=winerror.E_NOTIMPL)390 391 def _GetNextDispID_(self, fdex, dispid):392 return self._getnextdispid_(fdex, dispid)393 394 def _getnextdispid_(self, fdex, dispid):395 ids = list(self._name_to_dispid_.values())396 ids.sort()397 if DISPID_STARTENUM in ids:398 ids.remove(DISPID_STARTENUM)399 if dispid == DISPID_STARTENUM:400 return ids[0]401 else:402 try:403 return ids[ids.index(dispid) + 1]404 except ValueError: # dispid not in list?405 raise COMException(scode=winerror.E_UNEXPECTED)406 except IndexError: # No more items407 raise COMException(scode=winerror.S_FALSE)408 409 def _GetNameSpaceParent_(self):410 return self._getnamespaceparent()411 412 def _getnamespaceparent_(self):413 raise COMException(scode=winerror.E_NOTIMPL)414 415 416class MappedWrapPolicy(BasicWrapPolicy):417 """Wraps an object using maps to do its magic418 419 This policy wraps up a Python object, using a number of maps420 which translate from a Dispatch ID and flags, into an object to call/getattr, etc.421 422 It is the responsibility of derived classes to determine exactly how the423 maps are filled (ie, the derived classes determine the map filling policy.424 425 This policy supports the following special attributes on the wrapped object426 427 _dispid_to_func_/_dispid_to_get_/_dispid_to_put_ -- These are dictionaries428 (keyed by integer dispid, values are string attribute names) which the COM429 implementation uses when it is processing COM requests. Note that the implementation430 uses this dictionary for its own purposes - not a copy - which means the contents of431 these dictionaries will change as the object is used.432 433 """434 435 def _wrap_(self, object):436 BasicWrapPolicy._wrap_(self, object)437 ob = self._obj_438 if hasattr(ob, "_dispid_to_func_"):439 self._dispid_to_func_ = ob._dispid_to_func_440 else:441 self._dispid_to_func_ = {}442 if hasattr(ob, "_dispid_to_get_"):443 self._dispid_to_get_ = ob._dispid_to_get_444 else:445 self._dispid_to_get_ = {}446 if hasattr(ob, "_dispid_to_put_"):447 self._dispid_to_put_ = ob._dispid_to_put_448 else:449 self._dispid_to_put_ = {}450 451 def _getmembername_(self, dispid):452 if dispid in self._dispid_to_func_:453 return self._dispid_to_func_[dispid]454 elif dispid in self._dispid_to_get_:455 return self._dispid_to_get_[dispid]456 elif dispid in self._dispid_to_put_:457 return self._dispid_to_put_[dispid]458 else:459 raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)460 461 462class DesignatedWrapPolicy(MappedWrapPolicy):463 """A policy which uses a mapping to link functions and dispid464 465 A MappedWrappedPolicy which allows the wrapped object to specify, via certain466 special named attributes, exactly which methods and properties are exposed.467 468 All a wrapped object need do is provide the special attributes, and the policy469 will handle everything else.470 471 Attributes:472 473 _public_methods_ -- Required, unless a typelib GUID is given -- A list474 of strings, which must be the names of methods the object475 provides. These methods will be exposed and callable476 from other COM hosts.477 _public_attrs_ A list of strings, which must be the names of attributes on the object.478 These attributes will be exposed and readable and possibly writeable from other COM hosts.479 _readonly_attrs_ -- A list of strings, which must also appear in _public_attrs. These480 attributes will be readable, but not writable, by other COM hosts.481 _value_ -- A method that will be called if the COM host requests the "default" method482 (ie, calls Invoke with dispid==DISPID_VALUE)483 _NewEnum -- A method that will be called if the COM host requests an enumerator on the484 object (ie, calls Invoke with dispid==DISPID_NEWENUM.)485 It is the responsibility of the method to ensure the returned486 object conforms to the required Enum interface.487 488 _typelib_guid_ -- The GUID of the typelibrary with interface definitions we use.489 _typelib_version_ -- A tuple of (major, minor) with a default of 1,1490 _typelib_lcid_ -- The LCID of the typelib, default = LOCALE_USER_DEFAULT491 492 _Evaluate -- Dunno what this means, except the host has called Invoke with dispid==DISPID_EVALUATE!493 See the COM documentation for details.494 """495 496 def _wrap_(self, ob):497 # If we have nominated universal interfaces to support, load them now498 tlb_guid = getattr(ob, "_typelib_guid_", None)499 if tlb_guid is not None:500 tlb_major, tlb_minor = getattr(ob, "_typelib_version_", (1, 0))501 tlb_lcid = getattr(ob, "_typelib_lcid_", 0)502 from win32com import universal503 504 # XXX - what if the user wants to implement interfaces from multiple505 # typelibs?506 # Filter out all 'normal' IIDs (ie, IID objects and strings starting with {507 interfaces = [508 i509 for i in getattr(ob, "_com_interfaces_", [])510 if type(i) != pywintypes.IIDType and not i.startswith("{")511 ]512 universal_data = universal.RegisterInterfaces(513 tlb_guid, tlb_lcid, tlb_major, tlb_minor, interfaces514 )515 else:516 universal_data = []517 MappedWrapPolicy._wrap_(self, ob)518 if not hasattr(ob, "_public_methods_") and not hasattr(ob, "_typelib_guid_"):519 raise error(520 "Object does not support DesignatedWrapPolicy, as it does not have either _public_methods_ or _typelib_guid_ attributes."521 )522 523 # Copy existing _dispid_to_func_ entries to _name_to_dispid_524 for dispid, name in self._dispid_to_func_.items():525 self._name_to_dispid_[name.lower()] = dispid526 for dispid, name in self._dispid_to_get_.items():527 self._name_to_dispid_[name.lower()] = dispid528 for dispid, name in self._dispid_to_put_.items():529 self._name_to_dispid_[name.lower()] = dispid530 531 # Patch up the universal stuff.532 for dispid, invkind, name in universal_data:533 self._name_to_dispid_[name.lower()] = dispid534 if invkind == DISPATCH_METHOD:535 self._dispid_to_func_[dispid] = name536 elif invkind in (DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF):537 self._dispid_to_put_[dispid] = name538 elif invkind == DISPATCH_PROPERTYGET:539 self._dispid_to_get_[dispid] = name540 else:541 raise ValueError("unexpected invkind: %d (%s)" % (invkind, name))542 543 # look for reserved methods544 if hasattr(ob, "_value_"):545 self._dispid_to_get_[DISPID_VALUE] = "_value_"546 self._dispid_to_put_[DISPID_PROPERTYPUT] = "_value_"547 if hasattr(ob, "_NewEnum"):548 self._name_to_dispid_["_newenum"] = DISPID_NEWENUM549 self._dispid_to_func_[DISPID_NEWENUM] = "_NewEnum"550 if hasattr(ob, "_Evaluate"):551 self._name_to_dispid_["_evaluate"] = DISPID_EVALUATE552 self._dispid_to_func_[DISPID_EVALUATE] = "_Evaluate"553 554 next_dispid = self._allocnextdispid(999)555 # note: funcs have precedence over attrs (install attrs first)556 if hasattr(ob, "_public_attrs_"):557 if hasattr(ob, "_readonly_attrs_"):558 readonly = ob._readonly_attrs_559 else:560 readonly = []561 for name in ob._public_attrs_:562 dispid = self._name_to_dispid_.get(name.lower())563 if dispid is None:564 dispid = next_dispid565 self._name_to_dispid_[name.lower()] = dispid566 next_dispid = self._allocnextdispid(next_dispid)567 self._dispid_to_get_[dispid] = name568 if name not in readonly:569 self._dispid_to_put_[dispid] = name570 for name in getattr(ob, "_public_methods_", []):571 dispid = self._name_to_dispid_.get(name.lower())572 if dispid is None:573 dispid = next_dispid574 self._name_to_dispid_[name.lower()] = dispid575 next_dispid = self._allocnextdispid(next_dispid)576 self._dispid_to_func_[dispid] = name577 self._typeinfos_ = None # load these on demand.578 579 def _build_typeinfos_(self):580 # Can only ever be one for now.581 tlb_guid = getattr(self._obj_, "_typelib_guid_", None)582 if tlb_guid is None:583 return []584 tlb_major, tlb_minor = getattr(self._obj_, "_typelib_version_", (1, 0))585 tlb = pythoncom.LoadRegTypeLib(tlb_guid, tlb_major, tlb_minor)586 typecomp = tlb.GetTypeComp()587 # Not 100% sure what semantics we should use for the default interface.588 # Look for the first name in _com_interfaces_ that exists in the typelib.589 for iname in self._obj_._com_interfaces_:590 try:591 type_info, type_comp = typecomp.BindType(iname)592 if type_info is not None:593 return [type_info]594 except pythoncom.com_error:595 pass596 return []597 598 def _GetTypeInfoCount_(self):599 if self._typeinfos_ is None:600 self._typeinfos_ = self._build_typeinfos_()601 return len(self._typeinfos_)602 603 def _GetTypeInfo_(self, index, lcid):604 if self._typeinfos_ is None:605 self._typeinfos_ = self._build_typeinfos_()606 if index < 0 or index >= len(self._typeinfos_):607 raise COMException(scode=winerror.DISP_E_BADINDEX)608 return 0, self._typeinfos_[index]609 610 def _allocnextdispid(self, last_dispid):611 while 1:612 last_dispid = last_dispid + 1613 if (614 last_dispid not in self._dispid_to_func_615 and last_dispid not in self._dispid_to_get_616 and last_dispid not in self._dispid_to_put_617 ):618 return last_dispid619 620 def _invokeex_(self, dispid, lcid, wFlags, args, kwArgs, serviceProvider):621 ### note: lcid is being ignored...622 623 if wFlags & DISPATCH_METHOD:624 try:625 funcname = self._dispid_to_func_[dispid]626 except KeyError:627 if not wFlags & DISPATCH_PROPERTYGET:628 raise COMException(629 scode=winerror.DISP_E_MEMBERNOTFOUND630 ) # not found631 else:632 try:633 func = getattr(self._obj_, funcname)634 except AttributeError:635 # May have a dispid, but that doesnt mean we have the function!636 raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)637 # Should check callable here638 try:639 return func(*args)640 except TypeError as v:641 # Particularly nasty is "wrong number of args" type error642 # This helps you see what 'func' and 'args' actually is643 if str(v).find("arguments") >= 0:644 print(645 "** TypeError %s calling function %r(%r)" % (v, func, args)646 )647 raise648 649 if wFlags & DISPATCH_PROPERTYGET:650 try:651 name = self._dispid_to_get_[dispid]652 except KeyError:653 raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found654 retob = getattr(self._obj_, name)655 if type(retob) == types.MethodType: # a method as a property - call it.656 retob = retob(*args)657 return retob658 659 if wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF): ### correct?660 try:661 name = self._dispid_to_put_[dispid]662 except KeyError:663 raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # read-only664 # If we have a method of that name (ie, a property get function), and665 # we have an equiv. property set function, use that instead.666 if (667 type(getattr(self._obj_, name, None)) == types.MethodType668 and type(getattr(self._obj_, "Set" + name, None)) == types.MethodType669 ):670 fn = getattr(self._obj_, "Set" + name)671 fn(*args)672 else:673 # just set the attribute674 setattr(self._obj_, name, args[0])675 return676 677 raise COMException(scode=winerror.E_INVALIDARG, desc="invalid wFlags")678 679 680class EventHandlerPolicy(DesignatedWrapPolicy):681 """The default policy used by event handlers in the win32com.client package.682 683 In addition to the base policy, this provides argument conversion semantics for684 params685 * dispatch params are converted to dispatch objects.686 * Unicode objects are converted to strings (1.5.2 and earlier)687 688 NOTE: Later, we may allow the object to override this process??689 """690 691 def _transform_args_(self, args, kwArgs, dispid, lcid, wFlags, serviceProvider):692 ret = []693 for arg in args:694 arg_type = type(arg)695 if arg_type == IDispatchType:696 import win32com.client697 698 arg = win32com.client.Dispatch(arg)699 elif arg_type == IUnknownType:700 try:701 import win32com.client702 703 arg = win32com.client.Dispatch(704 arg.QueryInterface(pythoncom.IID_IDispatch)705 )706 except pythoncom.error:707 pass # Keep it as IUnknown708 ret.append(arg)709 return tuple(ret), kwArgs710 711 def _invokeex_(self, dispid, lcid, wFlags, args, kwArgs, serviceProvider):712 # transform the args.713 args, kwArgs = self._transform_args_(714 args, kwArgs, dispid, lcid, wFlags, serviceProvider715 )716 return DesignatedWrapPolicy._invokeex_(717 self, dispid, lcid, wFlags, args, kwArgs, serviceProvider718 )719 720 721class DynamicPolicy(BasicWrapPolicy):722 """A policy which dynamically (ie, at run-time) determines public interfaces.723 724 A dynamic policy is used to dynamically dispatch methods and properties to the725 wrapped object. The list of objects and properties does not need to be known in726 advance, and methods or properties added to the wrapped object after construction727 are also handled.728 729 The wrapped object must provide the following attributes:730 731 _dynamic_ -- A method that will be called whenever an invoke on the object732 is called. The method is called with the name of the underlying method/property733 (ie, the mapping of dispid to/from name has been resolved.) This name property734 may also be '_value_' to indicate the default, and '_NewEnum' to indicate a new735 enumerator is requested.736 737 """738 739 def _wrap_(self, object):740 BasicWrapPolicy._wrap_(self, object)741 if not hasattr(self._obj_, "_dynamic_"):742 raise error("Object does not support Dynamic COM Policy")743 self._next_dynamic_ = self._min_dynamic_ = 1000744 self._dyn_dispid_to_name_ = {745 DISPID_VALUE: "_value_",746 DISPID_NEWENUM: "_NewEnum",747 }748 749 def _getdispid_(self, name, fdex):750 # TODO - Look at fdex flags.751 lname = name.lower()752 try:753 return self._name_to_dispid_[lname]754 except KeyError:755 dispid = self._next_dynamic_ = self._next_dynamic_ + 1756 self._name_to_dispid_[lname] = dispid757 self._dyn_dispid_to_name_[dispid] = name # Keep case in this map...758 return dispid759 760 def _invoke_(self, dispid, lcid, wFlags, args):761 return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)762 763 def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):764 ### note: lcid is being ignored...765 ### note: kwargs is being ignored...766 ### note: serviceProvider is being ignored...767 ### there might be assigned DISPID values to properties, too...768 try:769 name = self._dyn_dispid_to_name_[dispid]770 except KeyError:771 raise COMException(772 scode=winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found"773 )774 return self._obj_._dynamic_(name, lcid, wFlags, args)775 776 777DefaultPolicy = DesignatedWrapPolicy778 779 780def resolve_func(spec):781 """Resolve a function by name782 783 Given a function specified by 'module.function', return a callable object784 (ie, the function itself)785 """786 try:787 idx = spec.rindex(".")788 mname = spec[:idx]789 fname = spec[idx + 1 :]790 # Dont attempt to optimize by looking in sys.modules,791 # as another thread may also be performing the import - this792 # way we take advantage of the built-in import lock.793 module = _import_module(mname)794 return getattr(module, fname)795 except ValueError: # No "." in name - assume in this module796 return globals()[spec]797 798 799def call_func(spec, *args):800 """Call a function specified by name.801 802 Call a function specified by 'module.function' and return the result.803 """804 805 return resolve_func(spec)(*args)806 807 808def _import_module(mname):809 """Import a module just like the 'import' statement.810 811 Having this function is much nicer for importing arbitrary modules than812 using the 'exec' keyword. It is more efficient and obvious to the reader.813 """814 __import__(mname)815 # Eeek - result of _import_ is "win32com" - not "win32com.a.b.c"816 # Get the full module from sys.modules817 return sys.modules[mname]818 819 820#######821#822# Temporary hacks until all old code moves.823#824# These have been moved to a new source file, but some code may825# still reference them here. These will end up being removed.826try:827 from .dispatcher import DispatcherTrace, DispatcherWin32trace828except ImportError: # Quite likely a frozen executable that doesnt need dispatchers829 pass830 