PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Microsoft.Dynamic/IDispatchComObject.cs

https://bitbucket.org/stefanrusek/xronos
C# | 611 lines | 409 code | 96 blank | 106 comment | 109 complexity | 66f7e79d941a0bee2c6d846e8572a9a0 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System; using Microsoft;
  16. #if !SILVERLIGHT // ComObject
  17. using System.Collections;
  18. using System.Collections.Generic;
  19. using System.Diagnostics;
  20. using System.Globalization;
  21. #if CODEPLEX_40
  22. using System.Linq.Expressions;
  23. #else
  24. using Microsoft.Linq.Expressions;
  25. #endif
  26. using System.Reflection;
  27. using System.Runtime.InteropServices;
  28. using ComTypes = System.Runtime.InteropServices.ComTypes;
  29. #if CODEPLEX_40
  30. namespace System.Dynamic {
  31. #else
  32. namespace Microsoft.Scripting {
  33. #endif
  34. /// <summary>
  35. /// An object that implements IDispatch
  36. ///
  37. /// This currently has the following issues:
  38. /// 1. If we prefer ComObjectWithTypeInfo over IDispatchComObject, then we will often not
  39. /// IDispatchComObject since implementations of IDispatch often rely on a registered type library.
  40. /// If we prefer IDispatchComObject over ComObjectWithTypeInfo, users get a non-ideal experience.
  41. /// 2. IDispatch cannot distinguish between properties and methods with 0 arguments (and non-0
  42. /// default arguments?). So obj.foo() is ambiguous as it could mean invoking method foo,
  43. /// or it could mean invoking the function pointer returned by property foo.
  44. /// We are attempting to find whether we need to call a method or a property by examining
  45. /// the ITypeInfo associated with the IDispatch. ITypeInfo tell's use what parameters the method
  46. /// expects, is it a method or a property, what is the default property of the object, how to
  47. /// create an enumerator for collections etc.
  48. /// 3. IronPython processes the signature and converts ref arguments into return values.
  49. /// However, since the signature of a DispMethod is not available beforehand, this conversion
  50. /// is not possible. There could be other signature conversions that may be affected. How does
  51. /// VB6 deal with ref arguments and IDispatch?
  52. ///
  53. /// We also support events for IDispatch objects:
  54. /// Background:
  55. /// COM objects support events through a mechanism known as Connect Points.
  56. /// Connection Points are separate objects created off the actual COM
  57. /// object (this is to prevent circular references between event sink
  58. /// and event source). When clients want to sink events generated by
  59. /// COM object they would implement callback interfaces (aka source
  60. /// interfaces) and hand it over (advise) to the Connection Point.
  61. ///
  62. /// Implementation details:
  63. /// When IDispatchComObject.TryGetMember request is received we first check
  64. /// whether the requested member is a property or a method. If this check
  65. /// fails we will try to determine whether an event is requested. To do
  66. /// so we will do the following set of steps:
  67. /// 1. Verify the COM object implements IConnectionPointContainer
  68. /// 2. Attempt to find COM object's coclass's description
  69. /// a. Query the object for IProvideClassInfo interface. Go to 3, if found
  70. /// b. From object's IDispatch retrieve primary interface description
  71. /// c. Scan coclasses declared in object's type library.
  72. /// d. Find coclass implementing this particular primary interface
  73. /// 3. Scan coclass for all its source interfaces.
  74. /// 4. Check whether to any of the methods on the source interfaces matches
  75. /// the request name
  76. ///
  77. /// Once we determine that TryGetMember requests an event we will return
  78. /// an instance of BoundDispEvent class. This class has InPlaceAdd and
  79. /// InPlaceSubtract operators defined. Calling InPlaceAdd operator will:
  80. /// 1. An instance of ComEventSinksContainer class is created (unless
  81. /// RCW already had one). This instance is hanged off the RCW in attempt
  82. /// to bind the lifetime of event sinks to the lifetime of the RCW itself,
  83. /// meaning event sink will be collected once the RCW is collected (this
  84. /// is the same way event sinks lifetime is controlled by PIAs).
  85. /// Notice: ComEventSinksContainer contains a Finalizer which will go and
  86. /// unadvise all event sinks.
  87. /// Notice: ComEventSinksContainer is a list of ComEventSink objects.
  88. /// 2. Unless we have already created a ComEventSink for the required
  89. /// source interface, we will create and advise a new ComEventSink. Each
  90. /// ComEventSink implements a single source interface that COM object
  91. /// supports.
  92. /// 3. ComEventSink contains a map between method DISPIDs to the
  93. /// multicast delegate that will be invoked when the event is raised.
  94. /// 4. ComEventSink implements IReflect interface which is exposed as
  95. /// custom IDispatch to COM consumers. This allows us to intercept calls
  96. /// to IDispatch.Invoke and apply custom logic - in particular we will
  97. /// just find and invoke the multicast delegate corresponding to the invoked
  98. /// dispid.
  99. /// </summary>
  100. internal sealed class IDispatchComObject : ComObject, IDynamicMetaObjectProvider {
  101. private readonly IDispatch _dispatchObject;
  102. private ComTypeDesc _comTypeDesc;
  103. private static readonly Dictionary<Guid, ComTypeDesc> _CacheComTypeDesc = new Dictionary<Guid, ComTypeDesc>();
  104. internal IDispatchComObject(IDispatch rcw)
  105. : base(rcw) {
  106. _dispatchObject = rcw;
  107. }
  108. public override string ToString() {
  109. EnsureScanDefinedMethods();
  110. string typeName = this._comTypeDesc.TypeName;
  111. if (String.IsNullOrEmpty(typeName))
  112. typeName = "IDispatch";
  113. return String.Format(CultureInfo.CurrentCulture, "{0} ({1})", RuntimeCallableWrapper.ToString(), typeName);
  114. }
  115. public ComTypeDesc ComTypeDesc {
  116. get {
  117. EnsureScanDefinedMethods();
  118. return _comTypeDesc;
  119. }
  120. }
  121. public IDispatch DispatchObject {
  122. get {
  123. return _dispatchObject;
  124. }
  125. }
  126. private static int GetIDsOfNames(IDispatch dispatch, string name, out int dispId) {
  127. int[] dispIds = new int[1];
  128. Guid emtpyRiid = Guid.Empty;
  129. int hresult = dispatch.TryGetIDsOfNames(
  130. ref emtpyRiid,
  131. new string[] { name },
  132. 1,
  133. 0,
  134. dispIds);
  135. dispId = dispIds[0];
  136. return hresult;
  137. }
  138. static int Invoke(IDispatch dispatch, int memberDispId, out object result) {
  139. Guid emtpyRiid = Guid.Empty;
  140. ComTypes.DISPPARAMS dispParams = new ComTypes.DISPPARAMS();
  141. ComTypes.EXCEPINFO excepInfo = new ComTypes.EXCEPINFO();
  142. uint argErr;
  143. int hresult = dispatch.TryInvoke(
  144. memberDispId,
  145. ref emtpyRiid,
  146. 0,
  147. ComTypes.INVOKEKIND.INVOKE_PROPERTYGET,
  148. ref dispParams,
  149. out result,
  150. out excepInfo,
  151. out argErr);
  152. return hresult;
  153. }
  154. internal bool TryGetGetItem(out ComMethodDesc value) {
  155. ComMethodDesc methodDesc = _comTypeDesc.GetItem;
  156. if (methodDesc != null) {
  157. value = methodDesc;
  158. return true;
  159. }
  160. return SlowTryGetGetItem(out value);
  161. }
  162. private bool SlowTryGetGetItem(out ComMethodDesc value) {
  163. EnsureScanDefinedMethods();
  164. ComMethodDesc methodDesc = _comTypeDesc.GetItem;
  165. // Without type information, we really don't know whether or not we have a property getter.
  166. if (methodDesc == null) {
  167. string name = "[PROPERTYGET, DISPID(0)]";
  168. _comTypeDesc.EnsureGetItem(new ComMethodDesc(name, ComDispIds.DISPID_VALUE, ComTypes.INVOKEKIND.INVOKE_PROPERTYGET));
  169. methodDesc = _comTypeDesc.GetItem;
  170. }
  171. value = methodDesc;
  172. return true;
  173. }
  174. internal bool TryGetSetItem(out ComMethodDesc value) {
  175. ComMethodDesc methodDesc = _comTypeDesc.SetItem;
  176. if (methodDesc != null) {
  177. value = methodDesc;
  178. return true;
  179. }
  180. return SlowTryGetSetItem(out value);
  181. }
  182. private bool SlowTryGetSetItem(out ComMethodDesc value) {
  183. EnsureScanDefinedMethods();
  184. ComMethodDesc methodDesc = _comTypeDesc.SetItem;
  185. // Without type information, we really don't know whether or not we have a property setter.
  186. if (methodDesc == null) {
  187. string name = "[PROPERTYPUT, DISPID(0)]";
  188. _comTypeDesc.EnsureSetItem(new ComMethodDesc(name, ComDispIds.DISPID_VALUE, ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT));
  189. methodDesc = _comTypeDesc.SetItem;
  190. }
  191. value = methodDesc;
  192. return true;
  193. }
  194. internal bool TryGetMemberMethod(string name, out ComMethodDesc method) {
  195. EnsureScanDefinedMethods();
  196. return _comTypeDesc.TryGetFunc(name, out method);
  197. }
  198. internal bool TryGetMemberEvent(string name, out ComEventDesc @event) {
  199. EnsureScanDefinedEvents();
  200. return _comTypeDesc.TryGetEvent(name, out @event);
  201. }
  202. internal bool TryGetMemberMethodExplicit(string name, out ComMethodDesc method) {
  203. EnsureScanDefinedMethods();
  204. int dispId;
  205. int hresult = GetIDsOfNames(_dispatchObject, name, out dispId);
  206. if (hresult == ComHresults.S_OK) {
  207. ComMethodDesc cmd = new ComMethodDesc(name, dispId, ComTypes.INVOKEKIND.INVOKE_FUNC);
  208. _comTypeDesc.AddFunc(name, cmd);
  209. method = cmd;
  210. return true;
  211. } else if (hresult == ComHresults.DISP_E_UNKNOWNNAME) {
  212. method = null;
  213. return false;
  214. } else {
  215. throw Error.CouldNotGetDispId(name, string.Format(CultureInfo.InvariantCulture, "0x{1:X})", hresult));
  216. }
  217. }
  218. internal bool TryGetPropertySetterExplicit(string name, out ComMethodDesc method, Type limitType, bool holdsNull) {
  219. EnsureScanDefinedMethods();
  220. int dispId;
  221. int hresult = GetIDsOfNames(_dispatchObject, name, out dispId);
  222. if (hresult == ComHresults.S_OK) {
  223. // we do not know whether we have put or putref here
  224. // and we will not guess and pretend we found both.
  225. ComMethodDesc put = new ComMethodDesc(name, dispId, ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT);
  226. _comTypeDesc.AddPut(name, put);
  227. ComMethodDesc putref = new ComMethodDesc(name, dispId, ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF);
  228. _comTypeDesc.AddPutRef(name, putref);
  229. if (ComBinderHelpers.PreferPut(limitType, holdsNull)) {
  230. method = put;
  231. } else {
  232. method = putref;
  233. }
  234. return true;
  235. } else if (hresult == ComHresults.DISP_E_UNKNOWNNAME) {
  236. method = null;
  237. return false;
  238. } else {
  239. throw Error.CouldNotGetDispId(name, string.Format(CultureInfo.InvariantCulture, "0x{1:X})", hresult));
  240. }
  241. }
  242. internal override IList<string> GetMemberNames(bool dataOnly) {
  243. EnsureScanDefinedMethods();
  244. EnsureScanDefinedEvents();
  245. return ComTypeDesc.GetMemberNames(dataOnly);
  246. }
  247. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
  248. internal override IList<KeyValuePair<string, object>> GetMembers(IEnumerable<string> names) {
  249. if (names == null) {
  250. names = GetMemberNames(true);
  251. }
  252. Type comType = RuntimeCallableWrapper.GetType();
  253. var members = new List<KeyValuePair<string, object>>();
  254. foreach (string name in names) {
  255. if (name == null) {
  256. continue;
  257. }
  258. ComMethodDesc method;
  259. if (ComTypeDesc.TryGetFunc(name, out method) && method.IsDataMember) {
  260. try {
  261. object value = comType.InvokeMember(
  262. method.Name,
  263. BindingFlags.GetProperty,
  264. null,
  265. RuntimeCallableWrapper,
  266. new object[0],
  267. CultureInfo.InvariantCulture
  268. );
  269. members.Add(new KeyValuePair<string, object>(method.Name, value));
  270. //evaluation failed for some reason. pass exception out
  271. } catch (Exception ex) {
  272. members.Add(new KeyValuePair<string, object>(method.Name, ex));
  273. }
  274. }
  275. }
  276. return members.ToArray();
  277. }
  278. DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) {
  279. EnsureScanDefinedMethods();
  280. return new IDispatchMetaObject(parameter, this);
  281. }
  282. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
  283. private static void GetFuncDescForDescIndex(ComTypes.ITypeInfo typeInfo, int funcIndex, out ComTypes.FUNCDESC funcDesc, out IntPtr funcDescHandle) {
  284. IntPtr pFuncDesc = IntPtr.Zero;
  285. typeInfo.GetFuncDesc(funcIndex, out pFuncDesc);
  286. // GetFuncDesc should never return null, this is just to be safe
  287. if (pFuncDesc == IntPtr.Zero) {
  288. throw Error.CannotRetrieveTypeInformation();
  289. }
  290. funcDesc = (ComTypes.FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(ComTypes.FUNCDESC));
  291. funcDescHandle = pFuncDesc;
  292. }
  293. private void EnsureScanDefinedEvents() {
  294. // _comTypeDesc.Events is null if we have not yet attempted
  295. // to scan the object for events.
  296. if (_comTypeDesc != null && _comTypeDesc.Events != null) {
  297. return;
  298. }
  299. // check type info in the type descriptions cache
  300. ComTypes.ITypeInfo typeInfo = ComRuntimeHelpers.GetITypeInfoFromIDispatch(_dispatchObject, true);
  301. if (typeInfo == null) {
  302. _comTypeDesc = ComTypeDesc.CreateEmptyTypeDesc();
  303. return;
  304. }
  305. ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo);
  306. if (_comTypeDesc == null) {
  307. lock (_CacheComTypeDesc) {
  308. if (_CacheComTypeDesc.TryGetValue(typeAttr.guid, out _comTypeDesc) == true &&
  309. _comTypeDesc.Events != null) {
  310. return;
  311. }
  312. }
  313. }
  314. ComTypeDesc typeDesc = ComTypeDesc.FromITypeInfo(typeInfo, typeAttr);
  315. ComTypes.ITypeInfo classTypeInfo = null;
  316. Dictionary<string, ComEventDesc> events = null;
  317. var cpc = RuntimeCallableWrapper as ComTypes.IConnectionPointContainer;
  318. if (cpc == null) {
  319. // No ICPC - this object does not support events
  320. events = ComTypeDesc.EmptyEvents;
  321. } else if ((classTypeInfo = GetCoClassTypeInfo(this.RuntimeCallableWrapper, typeInfo)) == null) {
  322. // no class info found - this object may support events
  323. // but we could not discover those
  324. events = ComTypeDesc.EmptyEvents;
  325. } else {
  326. events = new Dictionary<string, ComEventDesc>();
  327. ComTypes.TYPEATTR classTypeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(classTypeInfo);
  328. for (int i = 0; i < classTypeAttr.cImplTypes; i++) {
  329. int hRefType;
  330. classTypeInfo.GetRefTypeOfImplType(i, out hRefType);
  331. ComTypes.ITypeInfo interfaceTypeInfo;
  332. classTypeInfo.GetRefTypeInfo(hRefType, out interfaceTypeInfo);
  333. ComTypes.IMPLTYPEFLAGS flags;
  334. classTypeInfo.GetImplTypeFlags(i, out flags);
  335. if ((flags & ComTypes.IMPLTYPEFLAGS.IMPLTYPEFLAG_FSOURCE) != 0) {
  336. ScanSourceInterface(interfaceTypeInfo, ref events);
  337. }
  338. }
  339. if (events.Count == 0) {
  340. events = ComTypeDesc.EmptyEvents;
  341. }
  342. }
  343. lock (_CacheComTypeDesc) {
  344. ComTypeDesc cachedTypeDesc;
  345. if (_CacheComTypeDesc.TryGetValue(typeAttr.guid, out cachedTypeDesc)) {
  346. _comTypeDesc = cachedTypeDesc;
  347. } else {
  348. _comTypeDesc = typeDesc;
  349. _CacheComTypeDesc.Add(typeAttr.guid, _comTypeDesc);
  350. }
  351. _comTypeDesc.Events = events;
  352. }
  353. }
  354. private static void ScanSourceInterface(ComTypes.ITypeInfo sourceTypeInfo, ref Dictionary<string, ComEventDesc> events) {
  355. ComTypes.TYPEATTR sourceTypeAttribute = ComRuntimeHelpers.GetTypeAttrForTypeInfo(sourceTypeInfo);
  356. for (int index = 0; index < sourceTypeAttribute.cFuncs; index++) {
  357. IntPtr funcDescHandleToRelease = IntPtr.Zero;
  358. try {
  359. ComTypes.FUNCDESC funcDesc;
  360. GetFuncDescForDescIndex(sourceTypeInfo, index, out funcDesc, out funcDescHandleToRelease);
  361. // we are not interested in hidden or restricted functions for now.
  362. if ((funcDesc.wFuncFlags & (int)ComTypes.FUNCFLAGS.FUNCFLAG_FHIDDEN) != 0) {
  363. continue;
  364. }
  365. if ((funcDesc.wFuncFlags & (int)ComTypes.FUNCFLAGS.FUNCFLAG_FRESTRICTED) != 0) {
  366. continue;
  367. }
  368. string name = ComRuntimeHelpers.GetNameOfMethod(sourceTypeInfo, funcDesc.memid);
  369. name = name.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
  370. // Sometimes coclass has multiple source interfaces. Usually this is caused by
  371. // adding new events and putting them on new interfaces while keeping the
  372. // old interfaces around. This may cause name collisioning which we are
  373. // resolving by keeping only the first event with the same name.
  374. if (events.ContainsKey(name) == false) {
  375. ComEventDesc eventDesc = new ComEventDesc();
  376. eventDesc.dispid = funcDesc.memid;
  377. eventDesc.sourceIID = sourceTypeAttribute.guid;
  378. events.Add(name, eventDesc);
  379. }
  380. } finally {
  381. if (funcDescHandleToRelease != IntPtr.Zero) {
  382. sourceTypeInfo.ReleaseFuncDesc(funcDescHandleToRelease);
  383. }
  384. }
  385. }
  386. }
  387. private static ComTypes.ITypeInfo GetCoClassTypeInfo(object rcw, ComTypes.ITypeInfo typeInfo) {
  388. Debug.Assert(typeInfo != null);
  389. IProvideClassInfo provideClassInfo = rcw as IProvideClassInfo;
  390. if (provideClassInfo != null) {
  391. IntPtr typeInfoPtr = IntPtr.Zero;
  392. try {
  393. provideClassInfo.GetClassInfo(out typeInfoPtr);
  394. if (typeInfoPtr != IntPtr.Zero) {
  395. return Marshal.GetObjectForIUnknown(typeInfoPtr) as ComTypes.ITypeInfo;
  396. }
  397. } finally {
  398. if (typeInfoPtr != IntPtr.Zero) {
  399. Marshal.Release(typeInfoPtr);
  400. }
  401. }
  402. }
  403. // retrieving class information through IPCI has failed -
  404. // we can try scanning the typelib to find the coclass
  405. ComTypes.ITypeLib typeLib;
  406. int typeInfoIndex;
  407. typeInfo.GetContainingTypeLib(out typeLib, out typeInfoIndex);
  408. string typeName = ComRuntimeHelpers.GetNameOfType(typeInfo);
  409. ComTypeLibDesc typeLibDesc = ComTypeLibDesc.GetFromTypeLib(typeLib);
  410. ComTypeClassDesc coclassDesc = typeLibDesc.GetCoClassForInterface(typeName);
  411. if (coclassDesc == null) {
  412. return null;
  413. }
  414. ComTypes.ITypeInfo typeInfoCoClass;
  415. Guid coclassGuid = coclassDesc.Guid;
  416. typeLib.GetTypeInfoOfGuid(ref coclassGuid, out typeInfoCoClass);
  417. return typeInfoCoClass;
  418. }
  419. private void EnsureScanDefinedMethods() {
  420. if (_comTypeDesc != null && _comTypeDesc.Funcs != null) {
  421. return;
  422. }
  423. ComTypes.ITypeInfo typeInfo = ComRuntimeHelpers.GetITypeInfoFromIDispatch(_dispatchObject, true);
  424. if (typeInfo == null) {
  425. _comTypeDesc = ComTypeDesc.CreateEmptyTypeDesc();
  426. return;
  427. }
  428. ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo);
  429. if (_comTypeDesc == null) {
  430. lock (_CacheComTypeDesc) {
  431. if (_CacheComTypeDesc.TryGetValue(typeAttr.guid, out _comTypeDesc) == true &&
  432. _comTypeDesc.Funcs != null) {
  433. return;
  434. }
  435. }
  436. }
  437. ComTypeDesc typeDesc = ComTypeDesc.FromITypeInfo(typeInfo, typeAttr);
  438. ComMethodDesc getItem = null;
  439. ComMethodDesc setItem = null;
  440. Hashtable funcs = new Hashtable(typeAttr.cFuncs);
  441. Hashtable puts = new Hashtable();
  442. Hashtable putrefs = new Hashtable();
  443. for (int definedFuncIndex = 0; definedFuncIndex < typeAttr.cFuncs; definedFuncIndex++) {
  444. IntPtr funcDescHandleToRelease = IntPtr.Zero;
  445. try {
  446. ComTypes.FUNCDESC funcDesc;
  447. GetFuncDescForDescIndex(typeInfo, definedFuncIndex, out funcDesc, out funcDescHandleToRelease);
  448. if ((funcDesc.wFuncFlags & (int)ComTypes.FUNCFLAGS.FUNCFLAG_FRESTRICTED) != 0) {
  449. // This function is not meant for the script user to use.
  450. continue;
  451. }
  452. ComMethodDesc method = new ComMethodDesc(typeInfo, funcDesc);
  453. string name = method.Name.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
  454. if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT) != 0) {
  455. puts.Add(name, method);
  456. // for the special dispId == 0, we need to store
  457. // the method descriptor for the Do(SetItem) binder.
  458. if (method.DispId == ComDispIds.DISPID_VALUE && setItem == null) {
  459. setItem = method;
  460. }
  461. continue;
  462. }
  463. if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF) != 0) {
  464. putrefs.Add(name, method);
  465. // for the special dispId == 0, we need to store
  466. // the method descriptor for the Do(SetItem) binder.
  467. if (method.DispId == ComDispIds.DISPID_VALUE && setItem == null) {
  468. setItem = method;
  469. }
  470. continue;
  471. }
  472. if (funcDesc.memid == ComDispIds.DISPID_NEWENUM) {
  473. funcs.Add("GETENUMERATOR", method);
  474. continue;
  475. }
  476. funcs.Add(name, method);
  477. // for the special dispId == 0, we need to store the method descriptor
  478. // for the Do(GetItem) binder.
  479. if (funcDesc.memid == ComDispIds.DISPID_VALUE) {
  480. getItem = method;
  481. }
  482. } finally {
  483. if (funcDescHandleToRelease != IntPtr.Zero) {
  484. typeInfo.ReleaseFuncDesc(funcDescHandleToRelease);
  485. }
  486. }
  487. }
  488. lock (_CacheComTypeDesc) {
  489. ComTypeDesc cachedTypeDesc;
  490. if (_CacheComTypeDesc.TryGetValue(typeAttr.guid, out cachedTypeDesc)) {
  491. _comTypeDesc = cachedTypeDesc;
  492. } else {
  493. _comTypeDesc = typeDesc;
  494. _CacheComTypeDesc.Add(typeAttr.guid, _comTypeDesc);
  495. }
  496. _comTypeDesc.Funcs = funcs;
  497. _comTypeDesc.Puts = puts;
  498. _comTypeDesc.PutRefs = putrefs;
  499. _comTypeDesc.EnsureGetItem(getItem);
  500. _comTypeDesc.EnsureSetItem(setItem);
  501. }
  502. }
  503. internal bool TryGetPropertySetter(string name, out ComMethodDesc method, Type limitType, bool holdsNull) {
  504. EnsureScanDefinedMethods();
  505. if (ComBinderHelpers.PreferPut(limitType, holdsNull)) {
  506. return _comTypeDesc.TryGetPut(name, out method) ||
  507. _comTypeDesc.TryGetPutRef(name, out method);
  508. } else {
  509. return _comTypeDesc.TryGetPutRef(name, out method) ||
  510. _comTypeDesc.TryGetPut(name, out method);
  511. }
  512. }
  513. }
  514. }
  515. #endif