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