Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

dynamic.py 27KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. """Support for dynamic COM client support.
  2. Introduction
  3. Dynamic COM client support is the ability to use a COM server without
  4. prior knowledge of the server. This can be used to talk to almost all
  5. COM servers, including much of MS Office.
  6. In general, you should not use this module directly - see below.
  7. Example
  8. >>> import win32com.client
  9. >>> xl = win32com.client.Dispatch("Excel.Application")
  10. # The line above invokes the functionality of this class.
  11. # xl is now an object we can use to talk to Excel.
  12. >>> xl.Visible = 1 # The Excel window becomes visible.
  13. """
  14. import traceback
  15. import types
  16. import pythoncom # Needed as code we eval() references it.
  17. import win32com.client
  18. import winerror
  19. from pywintypes import IIDType
  20. from . import build
  21. debugging = 0 # General debugging
  22. debugging_attr = 0 # Debugging dynamic attribute lookups.
  23. LCID = 0x0
  24. # These errors generally mean the property or method exists,
  25. # but can't be used in this context - eg, property instead of a method, etc.
  26. # Used to determine if we have a real error or not.
  27. ERRORS_BAD_CONTEXT = [
  28. winerror.DISP_E_MEMBERNOTFOUND,
  29. winerror.DISP_E_BADPARAMCOUNT,
  30. winerror.DISP_E_PARAMNOTOPTIONAL,
  31. winerror.DISP_E_TYPEMISMATCH,
  32. winerror.E_INVALIDARG,
  33. ]
  34. ALL_INVOKE_TYPES = [
  35. pythoncom.INVOKE_PROPERTYGET,
  36. pythoncom.INVOKE_PROPERTYPUT,
  37. pythoncom.INVOKE_PROPERTYPUTREF,
  38. pythoncom.INVOKE_FUNC,
  39. ]
  40. def debug_print(*args):
  41. if debugging:
  42. for arg in args:
  43. print(arg, end=" ")
  44. print()
  45. def debug_attr_print(*args):
  46. if debugging_attr:
  47. for arg in args:
  48. print(arg, end=" ")
  49. print()
  50. def MakeMethod(func, inst, cls):
  51. return types.MethodType(func, inst)
  52. # get the type objects for IDispatch and IUnknown
  53. PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
  54. PyIUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]
  55. _GoodDispatchTypes = (str, IIDType)
  56. _defaultDispatchItem = build.DispatchItem
  57. def _GetGoodDispatch(IDispatch, clsctx=pythoncom.CLSCTX_SERVER):
  58. # quick return for most common case
  59. if isinstance(IDispatch, PyIDispatchType):
  60. return IDispatch
  61. if isinstance(IDispatch, _GoodDispatchTypes):
  62. try:
  63. IDispatch = pythoncom.connect(IDispatch)
  64. except pythoncom.ole_error:
  65. IDispatch = pythoncom.CoCreateInstance(
  66. IDispatch, None, clsctx, pythoncom.IID_IDispatch
  67. )
  68. else:
  69. # may already be a wrapped class.
  70. IDispatch = getattr(IDispatch, "_oleobj_", IDispatch)
  71. return IDispatch
  72. def _GetGoodDispatchAndUserName(IDispatch, userName, clsctx):
  73. # Get a dispatch object, and a 'user name' (ie, the name as
  74. # displayed to the user in repr() etc.
  75. if userName is None:
  76. if isinstance(IDispatch, str):
  77. userName = IDispatch
  78. ## ??? else userName remains None ???
  79. else:
  80. userName = str(userName)
  81. return (_GetGoodDispatch(IDispatch, clsctx), userName)
  82. def _GetDescInvokeType(entry, invoke_type):
  83. # determine the wFlags argument passed as input to IDispatch::Invoke
  84. # Only ever called by __getattr__ and __setattr__ from dynamic objects!
  85. # * `entry` is a MapEntry with whatever typeinfo we have about the property we are getting/setting.
  86. # * `invoke_type` is either INVOKE_PROPERTYGET | INVOKE_PROPERTYSET and really just
  87. # means "called by __getattr__" or "called by __setattr__"
  88. if not entry or not entry.desc:
  89. return invoke_type
  90. if entry.desc.desckind == pythoncom.DESCKIND_VARDESC:
  91. return invoke_type
  92. # So it's a FUNCDESC - just use what it specifies.
  93. return entry.desc.invkind
  94. def Dispatch(
  95. IDispatch,
  96. userName=None,
  97. createClass=None,
  98. typeinfo=None,
  99. UnicodeToString=None,
  100. clsctx=pythoncom.CLSCTX_SERVER,
  101. ):
  102. assert UnicodeToString is None, "this is deprecated and will go away"
  103. IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch, userName, clsctx)
  104. if createClass is None:
  105. createClass = CDispatch
  106. lazydata = None
  107. try:
  108. if typeinfo is None:
  109. typeinfo = IDispatch.GetTypeInfo()
  110. if typeinfo is not None:
  111. try:
  112. # try for a typecomp
  113. typecomp = typeinfo.GetTypeComp()
  114. lazydata = typeinfo, typecomp
  115. except pythoncom.com_error:
  116. pass
  117. except pythoncom.com_error:
  118. typeinfo = None
  119. olerepr = MakeOleRepr(IDispatch, typeinfo, lazydata)
  120. return createClass(IDispatch, olerepr, userName, lazydata=lazydata)
  121. def MakeOleRepr(IDispatch, typeinfo, typecomp):
  122. olerepr = None
  123. if typeinfo is not None:
  124. try:
  125. attr = typeinfo.GetTypeAttr()
  126. # If the type info is a special DUAL interface, magically turn it into
  127. # a DISPATCH typeinfo.
  128. if (
  129. attr[5] == pythoncom.TKIND_INTERFACE
  130. and attr[11] & pythoncom.TYPEFLAG_FDUAL
  131. ):
  132. # Get corresponding Disp interface;
  133. # -1 is a special value which does this for us.
  134. href = typeinfo.GetRefTypeOfImplType(-1)
  135. typeinfo = typeinfo.GetRefTypeInfo(href)
  136. attr = typeinfo.GetTypeAttr()
  137. if typecomp is None:
  138. olerepr = build.DispatchItem(typeinfo, attr, None, 0)
  139. else:
  140. olerepr = build.LazyDispatchItem(attr, None)
  141. except pythoncom.ole_error:
  142. pass
  143. if olerepr is None:
  144. olerepr = build.DispatchItem()
  145. return olerepr
  146. def DumbDispatch(
  147. IDispatch,
  148. userName=None,
  149. createClass=None,
  150. UnicodeToString=None,
  151. clsctx=pythoncom.CLSCTX_SERVER,
  152. ):
  153. "Dispatch with no type info"
  154. assert UnicodeToString is None, "this is deprecated and will go away"
  155. IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch, userName, clsctx)
  156. if createClass is None:
  157. createClass = CDispatch
  158. return createClass(IDispatch, build.DispatchItem(), userName)
  159. class CDispatch:
  160. def __init__(
  161. self, IDispatch, olerepr, userName=None, UnicodeToString=None, lazydata=None
  162. ):
  163. assert UnicodeToString is None, "this is deprecated and will go away"
  164. if userName is None:
  165. userName = "<unknown>"
  166. self.__dict__["_oleobj_"] = IDispatch
  167. self.__dict__["_username_"] = userName
  168. self.__dict__["_olerepr_"] = olerepr
  169. self.__dict__["_mapCachedItems_"] = {}
  170. self.__dict__["_builtMethods_"] = {}
  171. self.__dict__["_enum_"] = None
  172. self.__dict__["_unicode_to_string_"] = None
  173. self.__dict__["_lazydata_"] = lazydata
  174. def __call__(self, *args):
  175. "Provide 'default dispatch' COM functionality - allow instance to be called"
  176. if self._olerepr_.defaultDispatchName:
  177. invkind, dispid = self._find_dispatch_type_(
  178. self._olerepr_.defaultDispatchName
  179. )
  180. else:
  181. invkind, dispid = (
  182. pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET,
  183. pythoncom.DISPID_VALUE,
  184. )
  185. if invkind is not None:
  186. allArgs = (dispid, LCID, invkind, 1) + args
  187. return self._get_good_object_(
  188. self._oleobj_.Invoke(*allArgs), self._olerepr_.defaultDispatchName, None
  189. )
  190. raise TypeError("This dispatch object does not define a default method")
  191. def __bool__(self):
  192. return True # ie "if object:" should always be "true" - without this, __len__ is tried.
  193. # _Possibly_ want to defer to __len__ if available, but Im not sure this is
  194. # desirable???
  195. def __repr__(self):
  196. return "<COMObject %s>" % (self._username_)
  197. def __str__(self):
  198. # __str__ is used when the user does "print object", so we gracefully
  199. # fall back to the __repr__ if the object has no default method.
  200. try:
  201. return str(self.__call__())
  202. except pythoncom.com_error as details:
  203. if details.hresult not in ERRORS_BAD_CONTEXT:
  204. raise
  205. return self.__repr__()
  206. def __dir__(self):
  207. lst = list(self.__dict__.keys()) + dir(self.__class__) + self._dir_ole_()
  208. try:
  209. lst += [p.Name for p in self.Properties_]
  210. except AttributeError:
  211. pass
  212. return list(set(lst))
  213. def _dir_ole_(self):
  214. items_dict = {}
  215. for iTI in range(0, self._oleobj_.GetTypeInfoCount()):
  216. typeInfo = self._oleobj_.GetTypeInfo(iTI)
  217. self._UpdateWithITypeInfo_(items_dict, typeInfo)
  218. return list(items_dict.keys())
  219. def _UpdateWithITypeInfo_(self, items_dict, typeInfo):
  220. typeInfos = [typeInfo]
  221. # suppress IDispatch and IUnknown methods
  222. inspectedIIDs = {pythoncom.IID_IDispatch: None}
  223. while len(typeInfos) > 0:
  224. typeInfo = typeInfos.pop()
  225. typeAttr = typeInfo.GetTypeAttr()
  226. if typeAttr.iid not in inspectedIIDs:
  227. inspectedIIDs[typeAttr.iid] = None
  228. for iFun in range(0, typeAttr.cFuncs):
  229. funDesc = typeInfo.GetFuncDesc(iFun)
  230. funName = typeInfo.GetNames(funDesc.memid)[0]
  231. if funName not in items_dict:
  232. items_dict[funName] = None
  233. # Inspect the type info of all implemented types
  234. # E.g. IShellDispatch5 implements IShellDispatch4 which implements IShellDispatch3 ...
  235. for iImplType in range(0, typeAttr.cImplTypes):
  236. iRefType = typeInfo.GetRefTypeOfImplType(iImplType)
  237. refTypeInfo = typeInfo.GetRefTypeInfo(iRefType)
  238. typeInfos.append(refTypeInfo)
  239. # Delegate comparison to the oleobjs, as they know how to do identity.
  240. def __eq__(self, other):
  241. other = getattr(other, "_oleobj_", other)
  242. return self._oleobj_ == other
  243. def __ne__(self, other):
  244. other = getattr(other, "_oleobj_", other)
  245. return self._oleobj_ != other
  246. def __int__(self):
  247. return int(self.__call__())
  248. def __len__(self):
  249. invkind, dispid = self._find_dispatch_type_("Count")
  250. if invkind:
  251. return self._oleobj_.Invoke(dispid, LCID, invkind, 1)
  252. raise TypeError("This dispatch object does not define a Count method")
  253. def _NewEnum(self):
  254. try:
  255. invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
  256. enum = self._oleobj_.InvokeTypes(
  257. pythoncom.DISPID_NEWENUM, LCID, invkind, (13, 10), ()
  258. )
  259. except pythoncom.com_error:
  260. return None # no enumerator for this object.
  261. from . import util
  262. return util.WrapEnum(enum, None)
  263. def __getitem__(self, index): # syver modified
  264. # Improved __getitem__ courtesy Syver Enstad
  265. # Must check _NewEnum before Item, to ensure b/w compat.
  266. if isinstance(index, int):
  267. if self.__dict__["_enum_"] is None:
  268. self.__dict__["_enum_"] = self._NewEnum()
  269. if self.__dict__["_enum_"] is not None:
  270. return self._get_good_object_(self._enum_.__getitem__(index))
  271. # See if we have an "Item" method/property we can use (goes hand in hand with Count() above!)
  272. invkind, dispid = self._find_dispatch_type_("Item")
  273. if invkind is not None:
  274. return self._get_good_object_(
  275. self._oleobj_.Invoke(dispid, LCID, invkind, 1, index)
  276. )
  277. raise TypeError("This object does not support enumeration")
  278. def __setitem__(self, index, *args):
  279. # XXX - todo - We should support calling Item() here too!
  280. # print "__setitem__ with", index, args
  281. if self._olerepr_.defaultDispatchName:
  282. invkind, dispid = self._find_dispatch_type_(
  283. self._olerepr_.defaultDispatchName
  284. )
  285. else:
  286. invkind, dispid = (
  287. pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF,
  288. pythoncom.DISPID_VALUE,
  289. )
  290. if invkind is not None:
  291. allArgs = (dispid, LCID, invkind, 0, index) + args
  292. return self._get_good_object_(
  293. self._oleobj_.Invoke(*allArgs), self._olerepr_.defaultDispatchName, None
  294. )
  295. raise TypeError("This dispatch object does not define a default method")
  296. def _find_dispatch_type_(self, methodName):
  297. if methodName in self._olerepr_.mapFuncs:
  298. item = self._olerepr_.mapFuncs[methodName]
  299. return item.desc[4], item.dispid
  300. if methodName in self._olerepr_.propMapGet:
  301. item = self._olerepr_.propMapGet[methodName]
  302. return item.desc[4], item.dispid
  303. try:
  304. dispid = self._oleobj_.GetIDsOfNames(0, methodName)
  305. except: ### what error?
  306. return None, None
  307. return pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, dispid
  308. def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
  309. result = self._oleobj_.InvokeTypes(
  310. *(dispid, LCID, wFlags, retType, argTypes) + args
  311. )
  312. return self._get_good_object_(result, user, resultCLSID)
  313. def _wrap_dispatch_(
  314. self, ob, userName=None, returnCLSID=None, UnicodeToString=None
  315. ):
  316. # Given a dispatch object, wrap it in a class
  317. assert UnicodeToString is None, "this is deprecated and will go away"
  318. return Dispatch(ob, userName)
  319. def _get_good_single_object_(self, ob, userName=None, ReturnCLSID=None):
  320. if isinstance(ob, PyIDispatchType):
  321. # make a new instance of (probably this) class.
  322. return self._wrap_dispatch_(ob, userName, ReturnCLSID)
  323. if isinstance(ob, PyIUnknownType):
  324. try:
  325. ob = ob.QueryInterface(pythoncom.IID_IDispatch)
  326. except pythoncom.com_error:
  327. # It is an IUnknown, but not an IDispatch, so just let it through.
  328. return ob
  329. return self._wrap_dispatch_(ob, userName, ReturnCLSID)
  330. return ob
  331. def _get_good_object_(self, ob, userName=None, ReturnCLSID=None):
  332. """Given an object (usually the retval from a method), make it a good object to return.
  333. Basically checks if it is a COM object, and wraps it up.
  334. Also handles the fact that a retval may be a tuple of retvals"""
  335. if ob is None: # Quick exit!
  336. return None
  337. elif isinstance(ob, tuple):
  338. return tuple(
  339. map(
  340. lambda o, s=self, oun=userName, rc=ReturnCLSID: s._get_good_single_object_(
  341. o, oun, rc
  342. ),
  343. ob,
  344. )
  345. )
  346. else:
  347. return self._get_good_single_object_(ob)
  348. def _make_method_(self, name):
  349. "Make a method object - Assumes in olerepr funcmap"
  350. methodName = build.MakePublicAttributeName(name) # translate keywords etc.
  351. methodCodeList = self._olerepr_.MakeFuncMethod(
  352. self._olerepr_.mapFuncs[name], methodName, 0
  353. )
  354. methodCode = "\n".join(methodCodeList)
  355. try:
  356. # print "Method code for %s is:\n" % self._username_, methodCode
  357. # self._print_details_()
  358. codeObject = compile(methodCode, "<COMObject %s>" % self._username_, "exec")
  359. # Exec the code object
  360. tempNameSpace = {}
  361. # "Dispatch" in the exec'd code is win32com.client.Dispatch, not ours.
  362. globNameSpace = globals().copy()
  363. globNameSpace["Dispatch"] = win32com.client.Dispatch
  364. exec(
  365. codeObject, globNameSpace, tempNameSpace
  366. ) # self.__dict__, self.__dict__
  367. name = methodName
  368. # Save the function in map.
  369. fn = self._builtMethods_[name] = tempNameSpace[name]
  370. newMeth = MakeMethod(fn, self, self.__class__)
  371. return newMeth
  372. except:
  373. debug_print("Error building OLE definition for code ", methodCode)
  374. traceback.print_exc()
  375. return None
  376. def _Release_(self):
  377. """Cleanup object - like a close - to force cleanup when you dont
  378. want to rely on Python's reference counting."""
  379. for childCont in self._mapCachedItems_.values():
  380. childCont._Release_()
  381. self._mapCachedItems_ = {}
  382. if self._oleobj_:
  383. self._oleobj_.Release()
  384. self.__dict__["_oleobj_"] = None
  385. if self._olerepr_:
  386. self.__dict__["_olerepr_"] = None
  387. self._enum_ = None
  388. def _proc_(self, name, *args):
  389. """Call the named method as a procedure, rather than function.
  390. Mainly used by Word.Basic, which whinges about such things."""
  391. try:
  392. item = self._olerepr_.mapFuncs[name]
  393. dispId = item.dispid
  394. return self._get_good_object_(
  395. self._oleobj_.Invoke(*(dispId, LCID, item.desc[4], 0) + (args))
  396. )
  397. except KeyError:
  398. raise AttributeError(name)
  399. def _print_details_(self):
  400. "Debug routine - dumps what it knows about an object."
  401. print("AxDispatch container", self._username_)
  402. try:
  403. print("Methods:")
  404. for method in self._olerepr_.mapFuncs.keys():
  405. print("\t", method)
  406. print("Props:")
  407. for prop, entry in self._olerepr_.propMap.items():
  408. print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
  409. print("Get Props:")
  410. for prop, entry in self._olerepr_.propMapGet.items():
  411. print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
  412. print("Put Props:")
  413. for prop, entry in self._olerepr_.propMapPut.items():
  414. print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
  415. except:
  416. traceback.print_exc()
  417. def __LazyMap__(self, attr):
  418. try:
  419. if self._LazyAddAttr_(attr):
  420. debug_attr_print(
  421. "%s.__LazyMap__(%s) added something" % (self._username_, attr)
  422. )
  423. return 1
  424. except AttributeError:
  425. return 0
  426. # Using the typecomp, lazily create a new attribute definition.
  427. def _LazyAddAttr_(self, attr):
  428. if self._lazydata_ is None:
  429. return 0
  430. res = 0
  431. typeinfo, typecomp = self._lazydata_
  432. olerepr = self._olerepr_
  433. # We need to explicitly check each invoke type individually - simply
  434. # specifying '0' will bind to "any member", which may not be the one
  435. # we are actually after (ie, we may be after prop_get, but returned
  436. # the info for the prop_put.)
  437. for i in ALL_INVOKE_TYPES:
  438. try:
  439. x, t = typecomp.Bind(attr, i)
  440. # Support 'Get' and 'Set' properties - see
  441. # bug 1587023
  442. if x == 0 and attr[:3] in ("Set", "Get"):
  443. x, t = typecomp.Bind(attr[3:], i)
  444. if x == pythoncom.DESCKIND_FUNCDESC: # it's a FUNCDESC
  445. r = olerepr._AddFunc_(typeinfo, t, 0)
  446. elif x == pythoncom.DESCKIND_VARDESC: # it's a VARDESC
  447. r = olerepr._AddVar_(typeinfo, t, 0)
  448. else: # not found or TYPEDESC/IMPLICITAPP
  449. r = None
  450. if not r is None:
  451. key, map = r[0], r[1]
  452. item = map[key]
  453. if map == olerepr.propMapPut:
  454. olerepr._propMapPutCheck_(key, item)
  455. elif map == olerepr.propMapGet:
  456. olerepr._propMapGetCheck_(key, item)
  457. res = 1
  458. except:
  459. pass
  460. return res
  461. def _FlagAsMethod(self, *methodNames):
  462. """Flag these attribute names as being methods.
  463. Some objects do not correctly differentiate methods and
  464. properties, leading to problems when calling these methods.
  465. Specifically, trying to say: ob.SomeFunc()
  466. may yield an exception "None object is not callable"
  467. In this case, an attempt to fetch the *property* has worked
  468. and returned None, rather than indicating it is really a method.
  469. Calling: ob._FlagAsMethod("SomeFunc")
  470. should then allow this to work.
  471. """
  472. for name in methodNames:
  473. details = build.MapEntry(self.__AttrToID__(name), (name,))
  474. self._olerepr_.mapFuncs[name] = details
  475. def __AttrToID__(self, attr):
  476. debug_attr_print(
  477. "Calling GetIDsOfNames for property %s in Dispatch container %s"
  478. % (attr, self._username_)
  479. )
  480. return self._oleobj_.GetIDsOfNames(0, attr)
  481. def __getattr__(self, attr):
  482. if attr == "__iter__":
  483. # We can't handle this as a normal method, as if the attribute
  484. # exists, then it must return an iterable object.
  485. try:
  486. invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
  487. enum = self._oleobj_.InvokeTypes(
  488. pythoncom.DISPID_NEWENUM, LCID, invkind, (13, 10), ()
  489. )
  490. except pythoncom.com_error:
  491. raise AttributeError("This object can not function as an iterator")
  492. # We must return a callable object.
  493. class Factory:
  494. def __init__(self, ob):
  495. self.ob = ob
  496. def __call__(self):
  497. import win32com.client.util
  498. return win32com.client.util.Iterator(self.ob)
  499. return Factory(enum)
  500. if attr.startswith("_") and attr.endswith("_"): # Fast-track.
  501. raise AttributeError(attr)
  502. # If a known method, create new instance and return.
  503. try:
  504. return MakeMethod(self._builtMethods_[attr], self, self.__class__)
  505. except KeyError:
  506. pass
  507. # XXX - Note that we current are case sensitive in the method.
  508. # debug_attr_print("GetAttr called for %s on DispatchContainer %s" % (attr,self._username_))
  509. # First check if it is in the method map. Note that an actual method
  510. # must not yet exist, (otherwise we would not be here). This
  511. # means we create the actual method object - which also means
  512. # this code will never be asked for that method name again.
  513. if attr in self._olerepr_.mapFuncs:
  514. return self._make_method_(attr)
  515. # Delegate to property maps/cached items
  516. retEntry = None
  517. if self._olerepr_ and self._oleobj_:
  518. # first check general property map, then specific "put" map.
  519. retEntry = self._olerepr_.propMap.get(attr)
  520. if retEntry is None:
  521. retEntry = self._olerepr_.propMapGet.get(attr)
  522. # Not found so far - See what COM says.
  523. if retEntry is None:
  524. try:
  525. if self.__LazyMap__(attr):
  526. if attr in self._olerepr_.mapFuncs:
  527. return self._make_method_(attr)
  528. retEntry = self._olerepr_.propMap.get(attr)
  529. if retEntry is None:
  530. retEntry = self._olerepr_.propMapGet.get(attr)
  531. if retEntry is None:
  532. retEntry = build.MapEntry(self.__AttrToID__(attr), (attr,))
  533. except pythoncom.ole_error:
  534. pass # No prop by that name - retEntry remains None.
  535. if retEntry is not None: # see if in my cache
  536. try:
  537. ret = self._mapCachedItems_[retEntry.dispid]
  538. debug_attr_print("Cached items has attribute!", ret)
  539. return ret
  540. except (KeyError, AttributeError):
  541. debug_attr_print("Attribute %s not in cache" % attr)
  542. # If we are still here, and have a retEntry, get the OLE item
  543. if retEntry is not None:
  544. invoke_type = _GetDescInvokeType(retEntry, pythoncom.INVOKE_PROPERTYGET)
  545. debug_attr_print(
  546. "Getting property Id 0x%x from OLE object" % retEntry.dispid
  547. )
  548. try:
  549. ret = self._oleobj_.Invoke(retEntry.dispid, 0, invoke_type, 1)
  550. except pythoncom.com_error as details:
  551. if details.hresult in ERRORS_BAD_CONTEXT:
  552. # May be a method.
  553. self._olerepr_.mapFuncs[attr] = retEntry
  554. return self._make_method_(attr)
  555. raise
  556. debug_attr_print("OLE returned ", ret)
  557. return self._get_good_object_(ret)
  558. # no where else to look.
  559. raise AttributeError("%s.%s" % (self._username_, attr))
  560. def __setattr__(self, attr, value):
  561. if (
  562. attr in self.__dict__
  563. ): # Fast-track - if already in our dict, just make the assignment.
  564. # XXX - should maybe check method map - if someone assigns to a method,
  565. # it could mean something special (not sure what, tho!)
  566. self.__dict__[attr] = value
  567. return
  568. # Allow property assignment.
  569. debug_attr_print(
  570. "SetAttr called for %s.%s=%s on DispatchContainer"
  571. % (self._username_, attr, repr(value))
  572. )
  573. if self._olerepr_:
  574. # Check the "general" property map.
  575. if attr in self._olerepr_.propMap:
  576. entry = self._olerepr_.propMap[attr]
  577. invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
  578. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  579. return
  580. # Check the specific "put" map.
  581. if attr in self._olerepr_.propMapPut:
  582. entry = self._olerepr_.propMapPut[attr]
  583. invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
  584. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  585. return
  586. # Try the OLE Object
  587. if self._oleobj_:
  588. if self.__LazyMap__(attr):
  589. # Check the "general" property map.
  590. if attr in self._olerepr_.propMap:
  591. entry = self._olerepr_.propMap[attr]
  592. invoke_type = _GetDescInvokeType(
  593. entry, pythoncom.INVOKE_PROPERTYPUT
  594. )
  595. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  596. return
  597. # Check the specific "put" map.
  598. if attr in self._olerepr_.propMapPut:
  599. entry = self._olerepr_.propMapPut[attr]
  600. invoke_type = _GetDescInvokeType(
  601. entry, pythoncom.INVOKE_PROPERTYPUT
  602. )
  603. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  604. return
  605. try:
  606. entry = build.MapEntry(self.__AttrToID__(attr), (attr,))
  607. except pythoncom.com_error:
  608. # No attribute of that name
  609. entry = None
  610. if entry is not None:
  611. try:
  612. invoke_type = _GetDescInvokeType(
  613. entry, pythoncom.INVOKE_PROPERTYPUT
  614. )
  615. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  616. self._olerepr_.propMap[attr] = entry
  617. debug_attr_print(
  618. "__setattr__ property %s (id=0x%x) in Dispatch container %s"
  619. % (attr, entry.dispid, self._username_)
  620. )
  621. return
  622. except pythoncom.com_error:
  623. pass
  624. raise AttributeError(
  625. "Property '%s.%s' can not be set." % (self._username_, attr)
  626. )