/// <summary>
        /// Analyze the given element (i.e. composite type of an argument) recursively
        /// </summary>
        private void AnalyzeElement(ITypeInfo typeInfo, ELEMDESC elementDesc)
        {
            TYPEDESC typeDesc = elementDesc.tdesc;

            // If the current type is a pointer or an array, determine the child type and analyze that.
            while (((VarEnum)typeDesc.vt == VarEnum.VT_PTR) || ((VarEnum)typeDesc.vt == VarEnum.VT_SAFEARRAY))
            {
                var childTypeDesc = (TYPEDESC)Marshal.PtrToStructure(typeDesc.lpValue, typeof(TYPEDESC));
                typeDesc = childTypeDesc;
            }

            // We're only interested in user defined types for recursive analysis
            if ((VarEnum)typeDesc.vt == VarEnum.VT_USERDEFINED)
            {
                IntPtr         hrefType      = typeDesc.lpValue;
                IFixedTypeInfo childTypeInfo = null;

                try
                {
                    IFixedTypeInfo fixedTypeInfo = (IFixedTypeInfo)typeInfo;
                    fixedTypeInfo.GetRefTypeInfo(hrefType, out childTypeInfo);

                    AnalyzeTypeInfo((ITypeInfo)childTypeInfo);
                }
                finally
                {
                    if (childTypeInfo != null)
                    {
                        _marshalReleaseComObject(childTypeInfo);
                    }
                }
            }
        }
        protected void AddParams(FUNCDESC funcDesc,
                                 String[] names,
                                 int paramCount)
        {
            IntPtr elemPtr = funcDesc.lprgelemdescParam;

            for (int i = 0; i < paramCount; i++)
            {
                ELEMDESC elemDesc =
                    (ELEMDESC)Marshal.PtrToStructure(elemPtr,
                                                     typeof(ELEMDESC));
                ComParamInfo pi = new ComParamInfo
                                      (names[i + 1],
                                      TypeLibUtil.TYPEDESCToString
                                          (_typeLib,
                                          _typeInfo,
                                          elemDesc.tdesc,
                                          TypeLibUtil.COMTYPE),
                                      elemDesc.desc.paramdesc.wParamFlags);
                _parameters.Add(pi);
                // Point to the next one
                elemPtr = new IntPtr(elemPtr.ToInt64() +
                                     Marshal.SizeOf(elemDesc));
            }
        }
示例#3
0
        public void GetFuncDesc(int index, out IntPtr ppFuncDesc)
        {
            // Fail BEFORE allocating the handle to avoid leaks. If the real COM object fails in this method
            // and doesn't return the handle or clean it up itself there's not much we can do to avoid the leak.
            _faultInjector.FailurePointThrow(MockTypeLibrariesFailurePoints.ITypeInfo_GetFuncDesc);

            ppFuncDesc = _memoryHelper.AllocateHandle(Marshal.SizeOf <FUNCDESC>());

            _memoryHelper.EnterSubAllocationScope(ppFuncDesc);
            FUNCDESC funcDesc = new FUNCDESC();

            funcDesc.lprgelemdescParam = _memoryHelper.AllocateHandle(_definedFunctions[index].parameters.Length * Marshal.SizeOf <ELEMDESC>());
            funcDesc.cParams           = (short)_definedFunctions[index].parameters.Length;

            for (int i = 0; i < _definedFunctions[index].parameters.Length; i++)
            {
                ELEMDESC elemDesc = new ELEMDESC();
                elemDesc.tdesc = _definedFunctions[index].parameters[i].CreateTypeDesc(
                    new IntPtr(index * s_HREF_FUNCSPARAM_OFFSET_PERFUNC + i + s_HREF_FUNCSPARAM_OFFSET), _memoryHelper);

                Marshal.StructureToPtr(
                    elemDesc,
                    new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + i * Marshal.SizeOf <ELEMDESC>()),
                    false);
            }

            funcDesc.elemdescFunc.tdesc = _definedFunctions[index].returnType.CreateTypeDesc(
                new IntPtr(index + s_HREF_FUNCSRET_OFFSET), _memoryHelper);
            _memoryHelper.ExitSubAllocationScope();

            Marshal.StructureToPtr(funcDesc, ppFuncDesc, false);
        }
示例#4
0
        internal ComMethodDesc(ITypeInfo typeInfo, FUNCDESC funcDesc)
            : this(funcDesc.memid)
        {
            _hasTypeInfo = true;
            InvokeKind   = funcDesc.invkind;

            int cNames;

            string[] rgNames = new string[1 + funcDesc.cParams];
            typeInfo.GetNames(_memid, rgNames, rgNames.Length, out cNames);
            if (IsPropertyPut && rgNames[rgNames.Length - 1] == null)
            {
                rgNames[rgNames.Length - 1] = "value";
                cNames++;
            }
            Debug.Assert(cNames == rgNames.Length);
            _name = rgNames[0];

            _parameters = new ComParamDesc[funcDesc.cParams];

            int offset = 0;

            for (int i = 0; i < funcDesc.cParams; i++)
            {
                ELEMDESC elemDesc = (ELEMDESC)Marshal.PtrToStructure(
                    new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + offset),
                    typeof(ELEMDESC));

                _parameters[i] = new ComParamDesc(ref elemDesc, rgNames[1 + i]);

                offset += Marshal.SizeOf(typeof(ELEMDESC));
            }
        }
        protected void AddDomParams(FUNCDESC funcDesc,
                                    CodeMemberMethod meth,
                                    int limit)
        {
            IntPtr elemPtr = funcDesc.lprgelemdescParam;

            for (int i = 0; i < limit; i++)
            {
                ELEMDESC elemDesc =
                    (ELEMDESC)Marshal.PtrToStructure(elemPtr,
                                                     typeof(ELEMDESC));
                ComParamInfo parameter = (ComParamInfo)
                                         _parameters[i];
                String paramType = TypeLibUtil.TYPEDESCToString
                                       (_typeLib,
                                       _typeInfo,
                                       elemDesc.tdesc,
                                       !TypeLibUtil.COMTYPE);
                String paramInOut = null;
                if ((parameter._paramFlags & PARAMFLAG.PARAMFLAG_FIN) != 0)
                {
                    paramInOut = "In";
                }
                else if
                ((parameter._paramFlags & PARAMFLAG.PARAMFLAG_FOUT) != 0)
                {
                    paramInOut = "Out";
                    // Ref becomes out for an output parameter
                    if (paramType.StartsWith("ref "))
                    {
                        paramType = "out " + paramType.Substring(4);
                    }
                    else
                    {
                        paramType = "out " + paramType;
                    }
                }
                CodeParameterDeclarationExpression paramExpr =
                    new CodeParameterDeclarationExpression
                        (paramType, parameter._name);
                if (paramInOut != null)
                {
                    paramExpr.CustomAttributes.Add
                        (new CodeAttributeDeclaration
                            ("System.Runtime.InteropServices." + paramInOut));
                }
                meth.Parameters.Add(paramExpr);
                // Point to the next one
                elemPtr = new IntPtr(elemPtr.ToInt64() +
                                     Marshal.SizeOf(elemDesc));
            }
        }
示例#6
0
        /// <summary>
        /// Creates a representation for the parameter of a COM method
        /// </summary>
        internal ComParamDesc(ref ELEMDESC elemDesc, string name)
        {
            // Ensure _defaultValue is set to DBNull.Value regardless of whether or not the
            // default value is extracted from the parameter description.  Failure to do so
            // yields a runtime exception in the ToString() function.
            DefaultValue = DBNull.Value;

            if (!string.IsNullOrEmpty(name))
            {
                // This is a parameter, not a return value
                IsOut      = (elemDesc.desc.paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FOUT) != 0;
                IsOptional = (elemDesc.desc.paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FOPT) != 0;
                // TODO: The PARAMDESCEX struct has a memory issue that needs to be resolved.  For now, we ignore it.
                //_defaultValue = PARAMDESCEX.GetDefaultValue(ref elemDesc.desc.paramdesc);
            }

            _name = name;
            _vt   = (VarEnum)elemDesc.tdesc.vt;
            TYPEDESC typeDesc = elemDesc.tdesc;

            while (true)
            {
                if (_vt == VarEnum.VT_PTR)
                {
                    ByReference = true;
                }
                else if (_vt == VarEnum.VT_ARRAY)
                {
                    IsArray = true;
                }
                else
                {
                    break;
                }

                TYPEDESC childTypeDesc = (TYPEDESC)Marshal.PtrToStructure(typeDesc.lpValue, typeof(TYPEDESC));
                _vt      = (VarEnum)childTypeDesc.vt;
                typeDesc = childTypeDesc;
            }

            VarEnum vtWithoutByref = _vt;

            if ((_vt & VarEnum.VT_BYREF) != 0)
            {
                vtWithoutByref = (_vt & ~VarEnum.VT_BYREF);
                ByReference    = true;
            }

            ParameterType = VarEnumSelector.GetTypeForVarEnum(vtWithoutByref);
        }
示例#7
0
        public ParameterInfo(string paramName, ELEMDESC elemDesc, ITypeInfo info, IVbaTypeRepository repo)
        {
            Contract.Requires <ArgumentNullException>(!string.IsNullOrEmpty(paramName));
            Contract.Requires <ArgumentNullException>(info != null);
            Contract.Requires <ArgumentNullException>(repo != null);

            ParamType = repo.GetVbaType(elemDesc.tdesc, info);
            ParamName = paramName;

            var flags = elemDesc.desc.paramdesc.wParamFlags;

            IsOptional      = flags.HasFlag(PARAMFLAG.PARAMFLAG_FOPT);
            IsOut           = flags.HasFlag(PARAMFLAG.PARAMFLAG_FOUT);
            HasDefaultValue = flags.HasFlag(PARAMFLAG.PARAMFLAG_FHASDEFAULT);

            if (HasDefaultValue)
            {
                DefaultValue = ComHelper.GetDefaultValue(elemDesc.desc.paramdesc);
            }
        }
示例#8
0
        /// <summary>
        /// For a given type, analyze all the functions implemented by it. That means all the argument and return types.
        /// </summary>
        /// <param name="typeInfo"></param>
        /// <param name="typeAttributes"></param>
        private void ScanDefinedFunctions(ITypeInfo typeInfo, TYPEATTR typeAttributes)
        {
            for (int definedFuncIndex = 0; definedFuncIndex < typeAttributes.cFuncs; definedFuncIndex++)
            {
                IntPtr funcDescHandleToRelease = IntPtr.Zero;

                try
                {
                    FUNCDESC funcDesc;
                    ComReference.GetFuncDescForDescIndex(typeInfo, definedFuncIndex, out funcDesc, out funcDescHandleToRelease);

                    int offset = 0;

                    // Analyze the argument types
                    for (int paramIndex = 0; paramIndex < funcDesc.cParams; paramIndex++)
                    {
                        ELEMDESC elemDesc = (ELEMDESC)Marshal.PtrToStructure(
                            new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + offset), typeof(ELEMDESC));

                        AnalyzeElement(typeInfo, elemDesc);

                        offset += Marshal.SizeOf <ELEMDESC>();
                    }

                    // Analyze the return value type
                    AnalyzeElement(typeInfo, funcDesc.elemdescFunc);
                }
                finally
                {
                    if (funcDescHandleToRelease != IntPtr.Zero)
                    {
                        typeInfo.ReleaseFuncDesc(funcDescHandleToRelease);
                    }
                }
            }
        }
        protected override CodeObject GenerateCodeDom()
        {
            CodeTypeMember codeDom;
            IntPtr         funcDescPtr;
            FUNCDESC       funcDesc;

            // Have to get the FUNCDESC from the _typeInfo because
            // it cannot be saved.  The reason for this is that a TYPEDESC
            // may have a pointer to another TYPEDESC which is allocated
            // in the unmanaged memory and that pointer won't be any good
            // outside of the scope where the FUNCDESC is held.
            _typeInfo.GetFuncDesc(_index, out funcDescPtr);
            funcDesc =
                (FUNCDESC)Marshal.PtrToStructure(funcDescPtr,
                                                 typeof(FUNCDESC));
            if (funcDesc.invkind == INVOKEKIND.INVOKE_FUNC)
            {
                CodeMemberMethod meth;
                meth    = new CodeMemberMethod();
                codeDom = meth;
                TYPEDESC retType = funcDesc.elemdescFunc.tdesc;
                if (_parameters.Count > 0)
                {
                    int limit = _parameters.Count;
                    // If the last parameter is a retval and the
                    // function returns an HRESULT, then make
                    // the function return the last parameter
                    if ((VarEnum)funcDesc.elemdescFunc.tdesc.vt
                        == VarEnum.VT_HRESULT)
                    {
                        ComParamInfo lastParam = (ComParamInfo)
                                                 _parameters[_parameters.Count - 1];
                        if ((lastParam._paramFlags &
                             PARAMFLAG.PARAMFLAG_FRETVAL) != 0)
                        {
                            IntPtr   elemPtr  = funcDesc.lprgelemdescParam;
                            ELEMDESC elemDesc = new ELEMDESC();
                            // Point to the last one
                            elemPtr = new IntPtr(elemPtr.ToInt64() +
                                                 ((_parameters.Count - 1) *
                                                  Marshal.SizeOf(elemDesc)));
                            elemDesc = (ELEMDESC)
                                       Marshal.PtrToStructure(elemPtr,
                                                              typeof(ELEMDESC));
                            // Make the return type the last parameter's
                            retType = elemDesc.tdesc;
                            limit--;
                        }
                    }
                    // Only add up to the limit
                    // (may omit the last paremeter)
                    AddDomParams(funcDesc, meth, limit);
                }
                // HRESULT becomes void because its handled by the exception
                // mechanism, we just leave the return type null
                if ((VarEnum)retType.vt != VarEnum.VT_HRESULT)
                {
                    String typeName = TypeLibUtil.TYPEDESCToString
                                          (_typeLib,
                                          _typeInfo,
                                          retType,
                                          !TypeLibUtil.COMTYPE);
                    // Get rid of the ref since this is now a return type
                    if (typeName.StartsWith("ref "))
                    {
                        typeName = typeName.Substring(4);
                    }
                    if (TraceUtil.If(this, TraceLevel.Info))
                    {
                        Trace.WriteLine(this, "CG -  " + Name
                                        + " return: " + typeName);
                    }
                    meth.ReturnType = new CodeTypeReference(typeName);
                }
            }
            else
            {
                CodeMemberProperty prop;
                prop      = new CodeMemberProperty();
                codeDom   = prop;
                prop.Type = new CodeTypeReference
                                (TypeLibUtil.TYPEDESCToString
                                    (_typeLib,
                                    _typeInfo,
                                    funcDesc.elemdescFunc.tdesc,
                                    !TypeLibUtil.COMTYPE));
            }
            codeDom.Name       = Name;
            codeDom.Attributes = MemberAttributes.Public;
            _typeInfo.ReleaseFuncDesc(funcDescPtr);
            return(codeDom);
        }
示例#10
0
        public unsafe ParameterDesc(ELEMDESC elemdesc, string name, Func<uint, TypeDesc> typeFactory, VARFLAGS flags, bool hasValue, object value)
        {
            Name = EscapeParameterName(name);
            SpecialFlag = flags.HasFlag(VARFLAGS.VARFLAG_FREPLACEABLE);
            Hidden = flags.HasFlag(VARFLAGS.VARFLAG_FHIDDEN);
            In = elemdesc.__union1.paramdesc.wParamFlags.HasFlag(PARAMFLAGS.PARAMFLAG_FIN);
            Out = elemdesc.__union1.paramdesc.wParamFlags.HasFlag(PARAMFLAGS.PARAMFLAG_FOUT);
            Optional = elemdesc.__union1.paramdesc.wParamFlags.HasFlag(PARAMFLAGS.PARAMFLAG_FOPT);
            RetVal = elemdesc.__union1.paramdesc.wParamFlags.HasFlag(PARAMFLAGS.PARAMFLAG_FRETVAL);
            HasValue = hasValue;
            Value = value;

            this.typeFactory = typeFactory;

            TYPEDESC typedesc = elemdesc.tdesc;
            while ((typedesc.vt & VARENUM.VT_TYPEMASK) == VARENUM.VT_PTR)
            {
                if ((typedesc.vt & ~VARENUM.VT_TYPEMASK) != VARENUM.VT_EMPTY)
                    throw new Exception("Variant type " + typedesc.vt + " is not supported");

                IndirectionLevel++;
                typedesc = *typedesc.__union1.lptdesc;
            }

            List<int> arraySizes = new List<int>();
            if ((typedesc.vt & VARENUM.VT_TYPEMASK) == VARENUM.VT_CARRAY)
            {
                SAFEARRAYBOUND* boundsPtr = &typedesc.__union1.lpadesc->rgbounds;
                for (int i = 0; i < typedesc.__union1.lpadesc->cDims; i++)
                    arraySizes.Add((int)boundsPtr[i].cElements - boundsPtr[i].lBound);
                typedesc = typedesc.__union1.lpadesc->tdescElem;
            }
            ArraySizes = arraySizes.AsReadOnly();

            if ((typedesc.vt & ~VARENUM.VT_TYPEMASK) != VARENUM.VT_EMPTY)
                throw new Exception("Variant type " + typedesc.vt + " is not supported");

            if (elemdesc.__union1.paramdesc.wParamFlags.HasFlag(PARAMFLAGS.PARAMFLAG_FHASDEFAULT))
            {
                if (hasValue)
                    throw new Exception("Explicit value cannot be set if default value is present");
                if (elemdesc.__union1.paramdesc.pparamdescex == null)
                    throw new Exception("Value address is null");
                Value = Marshal.GetObjectForNativeVariant(new IntPtr(&elemdesc.__union1.paramdesc.pparamdescex->varDefaultValue));
                HasValue = true;
            }

            switch (typedesc.vt & VARENUM.VT_TYPEMASK)
            {
                case VARENUM.VT_VARIANT:
                    type = new TypeDesc("VARIANT", TypeKind.Struct);
                    break;
                case VARENUM.VT_SAFEARRAY:
                    type = new TypeDesc("SAFEARRAY", TypeKind.Struct);
                    break;
                case VARENUM.VT_USERDEFINED:
                    typeReference = typedesc.__union1.hreftype;
                    break;
                case VARENUM.VT_UNKNOWN:
                    type = new TypeDesc("IUnknown", TypeKind.Interface);
                    IndirectionLevel++;
                    break;
                case VARENUM.VT_DISPATCH:
                    type = new TypeDesc("IDispatch", TypeKind.Interface);
                    IndirectionLevel++;
                    break;
                case VARENUM.VT_ERROR:
                case VARENUM.VT_HRESULT:
                    type = new TypeDesc("HRESULT");
                    break;
                case VARENUM.VT_LPSTR:
                    type = new TypeDesc("sbyte");
                    IndirectionLevel++;
                    break;
                case VARENUM.VT_BSTR:
                case VARENUM.VT_LPWSTR:
                    type = new TypeDesc("char");
                    IndirectionLevel++;
                    break;
                case VARENUM.VT_NULL:
                    type = new TypeDesc("void");
                    break;
                case VARENUM.VT_BOOL:
                    type = new TypeDesc("bool");
                    break;
                case VARENUM.VT_I1:
                    type = new TypeDesc("sbyte");
                    break;
                case VARENUM.VT_I2:
                    type = new TypeDesc("short");
                    break;
                case VARENUM.VT_I4:
                case VARENUM.VT_INT:
                    type = new TypeDesc("int");
                    break;
                case VARENUM.VT_I8:
                case VARENUM.VT_FILETIME:
                case VARENUM.VT_CY:
                    type = new TypeDesc("long");
                    break;
                case VARENUM.VT_UI1:
                    type = new TypeDesc("byte");
                    break;
                case VARENUM.VT_UI2:
                    type = new TypeDesc("ushort");
                    break;
                case VARENUM.VT_UI4:
                case VARENUM.VT_UINT:
                    type = new TypeDesc("uint");
                    break;
                case VARENUM.VT_UI8:
                    type = new TypeDesc("ulong");
                    break;
                case VARENUM.VT_R4:
                    type = new TypeDesc("float");
                    break;
                case VARENUM.VT_R8:
                case VARENUM.VT_DATE:
                    type = new TypeDesc("double");
                    break;
                case VARENUM.VT_DECIMAL:
                    type = new TypeDesc("decimal");
                    break;
                case VARENUM.VT_VOID:
                    type = new TypeDesc("void");
                    break;
                case VARENUM.VT_INT_PTR:
                case VARENUM.VT_UINT_PTR:
                    type = new TypeDesc("void");
                    IndirectionLevel++;
                    break;
                default:
                    throw new Exception("Variant type " + typedesc.vt + " is not supported");
            }
        }
示例#11
0
 /// <summary>
 /// Creates a representation for the return value of a COM method
 /// TODO: Return values should be represented by a different type
 /// </summary>
 internal ComParamDesc(ref ELEMDESC elemDesc)
     : this(ref elemDesc, string.Empty)
 {
 }
示例#12
0
 private void Init(ELEMDESC elemDesc)
 {
     m_lptdesc = elemDesc.tdesc.desc.lptdesc;
     m_vt = elemDesc.tdesc.vt;
     m_lpVarValue = elemDesc.desc.paramdesc.lpVarValue;
     m_flags = (int)elemDesc.desc.paramdesc.wParamFlags;
 }
示例#13
0
 public ElemDesc(FuncDesc funcdesc, ELEMDESC elemdesc)
 {
     m_parent = funcdesc;
     Init(elemdesc);
 }
示例#14
0
 public ElemDesc(VarDesc varDesc, ELEMDESC elemdesc)
 {
     m_parent = varDesc;
     Init(elemdesc);
 }
示例#15
0
        /// <summary>
        /// Gets parameter information for the given member.
        /// </summary>
        /// <remarks>
        /// Uses the COM ITypeInfo interface to get the information. Does not throw. If it returns false then the out params
        /// will not hold useful information.
        /// </remarks>
        /// <param name="obj"></param>
        /// <param name="memberName"></param>
        /// <param name="callType"></param>
        /// <param name="isBeingCalledWithZeroArgs"></param>
        /// <param name="paramTypes"></param>
        /// <param name="paramFlags"></param>
        /// <param name="endsWithParamArray"></param>
        /// <returns>true if the name refers to a valid member or if no type information is available</returns>
        ///
        internal static bool GetMemberInfo(object obj, ref string memberName, int callType, bool isBeingCalledWithZeroArgs,
                                           out Type[] paramTypes, out PARAMFLAG[] paramFlags, out bool endsWithParamArray)
        {
            // There are some early returns in this method, so we initialize the out params right away. To the caller, if these values
            // come back null it means that the information could not be determined.
            paramTypes         = null;
            paramFlags         = null;
            endsWithParamArray = false;

            int defaultLCID = GetUserDefaultLCID();

            try {
                // Throws in this outer try cause true to be returned. They mean "type info could not be obtained, so the member is not
                // known to be invalid."

                UCOMIDispatch iDisp        = GetIDispatch(obj);
                int           memberDispId = GetMemberDispID(obj, iDisp, ref memberName);
                if (memberDispId == -1)
                {
                    // Name not found by GetIDsOfNames.
                    return(false);
                }

                // If no args are being passed, then we can skip all the machinations below about type info.
                if (isBeingCalledWithZeroArgs)
                {
                    return(true);
                }

                UCOMITypeInfo iTypeInfo = GetITypeInfo(obj, iDisp);
                IntPtr        pFuncDesc;

                // Check to see if the func index for this member name is already cached from a previous call.
                FuncIndexHolder fih = (FuncIndexHolder)Marshal.GetComObjectData(obj, "NETLinkFuncIndex" + memberName);
                if (fih == null)
                {
                    // This will be populated below.
                    fih = new FuncIndexHolder();
                    Marshal.SetComObjectData(obj, "NETLinkFuncIndex" + memberName, fih);
                }
                int funcIndex = -1;
                if (callType == Install.CALLTYPE_FIELD_OR_SIMPLE_PROP_GET)
                {
                    if (fih.funcIndexForCALLTYPE_FIELD_OR_SIMPLE_PROP_GET != -1)
                    {
                        funcIndex = fih.funcIndexForCALLTYPE_FIELD_OR_SIMPLE_PROP_GET;
                    }
                }
                else if (callType == Install.CALLTYPE_FIELD_OR_SIMPLE_PROP_SET || callType == Install.CALLTYPE_PARAM_PROP_SET)
                {
                    if (fih.funcIndexForCALLTYPE_FIELD_OR_ANY_PROP_SET != -1)
                    {
                        funcIndex = fih.funcIndexForCALLTYPE_FIELD_OR_ANY_PROP_SET;
                    }
                }
                else if (callType == Install.CALLTYPE_METHOD || callType == Install.CALLTYPE_PARAM_PROP_GET)
                {
                    // COM objects are treated by M code as if they all have indexers, so calls like obj[1] will
                    // come in as CALLTYPE_PARAM_PROP_GET with a property name of Item. We can just treat these like
                    // method calls.
                    if (fih.funcIndexForCALLTYPE_METHOD != -1)
                    {
                        funcIndex = fih.funcIndexForCALLTYPE_METHOD;
                    }
                }

                // Did not have the func index for this call type cached.
                if (funcIndex == -1)
                {
                    IntPtr pTypeAttr;
                    iTypeInfo.GetTypeAttr(out pTypeAttr);
                    TYPEATTR typeAttr = (TYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(TYPEATTR));
                    int      numFuncs = typeAttr.cFuncs;
                    iTypeInfo.ReleaseTypeAttr(pTypeAttr);
                    bool foundDispId = false;
                    for (int thisIndex = 0; thisIndex < numFuncs; thisIndex++)
                    {
                        // Be aware that GetFuncDesc() can (I think) throw a cryptic "Element not found" exception,
                        // such as for a hidden property like (Excel) _Application.ActiveDialog.
                        iTypeInfo.GetFuncDesc(thisIndex, out pFuncDesc);
                        FUNCDESC   funcDesc     = (FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(FUNCDESC));
                        int        thisMemberId = funcDesc.memid;
                        INVOKEKIND invokeKind   = funcDesc.invkind;
                        iTypeInfo.ReleaseFuncDesc(pFuncDesc);
                        if (thisMemberId == memberDispId)
                        {
                            foundDispId = true;
                            // Verify that it is a member of the correct call type.
                            if (callType == Install.CALLTYPE_FIELD_OR_SIMPLE_PROP_GET && invokeKind == INVOKEKIND.INVOKE_PROPERTYGET)
                            {
                                fih.funcIndexForCALLTYPE_FIELD_OR_SIMPLE_PROP_GET = thisIndex;
                                funcIndex = thisIndex;
                                break;
                            }
                            else if ((callType == Install.CALLTYPE_FIELD_OR_SIMPLE_PROP_SET || callType == Install.CALLTYPE_PARAM_PROP_SET) &&
                                     (invokeKind == INVOKEKIND.INVOKE_PROPERTYPUT || invokeKind == INVOKEKIND.INVOKE_PROPERTYPUTREF))
                            {
                                fih.funcIndexForCALLTYPE_FIELD_OR_ANY_PROP_SET = thisIndex;
                                funcIndex = thisIndex;
                                break;
                            }
                            else if ((callType == Install.CALLTYPE_METHOD && (invokeKind == INVOKEKIND.INVOKE_FUNC || invokeKind == INVOKEKIND.INVOKE_PROPERTYGET)) ||
                                     (callType == Install.CALLTYPE_PARAM_PROP_GET && invokeKind == INVOKEKIND.INVOKE_PROPERTYGET))
                            {
                                // Parameterized prop gets, not sets, look like CALLTYPE_METHOD. Also, as discussed in an earlier comment,
                                // indexer notation calls (obj[1]) are supported for all COM objects and come in as CALLTYPE_PARAM_PROP_GET.
                                fih.funcIndexForCALLTYPE_METHOD = thisIndex;
                                funcIndex = thisIndex;
                                break;
                            }
                        }
                    }
                    if (funcIndex == -1)
                    {
                        // We didn't find the member in our search. This can happen in two ways. First, the member might
                        // exist but not be the right call type. For this case we want to return false. Second, we didn't
                        // even find the dispid. This can happen in unusual cases I don't understan An example of this
                        // is the IWebBrowser2 interface obtained by CreateCOMObject["InternetExplorer.Application"].
                        // For this case we want to return true, as if there was no type info at all available, so the
                        // call can still proceed.
                        return(!foundDispId);
                    }
                }

                // If we get to here, we have a valid funcIndex and memberDispId, and the member is of the correct variety
                // that corresponds to how it was called (e.g., it's a method and it was called as such). All that
                // remains is to get the parameter types.

                iTypeInfo.GetFuncDesc(funcIndex, out pFuncDesc);
                try {
                    FUNCDESC funcDesc = (FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(FUNCDESC));
                    Debug.Assert(funcDesc.memid == memberDispId);
                    int paramCount = funcDesc.cParams;
                    // Functions that end with a VB-style ParamArray (a variable-length argument sequence) have -1 for the
                    // cParamsOpt member. It is convenient to treat them specially, hence the 'endsWithParamArray' variable
                    // (which is an out param for this method). The special treatment involves leaving the ParamArray arg off
                    // the list of paramFlags and paramTypes. We just pretend there is one less arg to the function but
                    // record that it ends with a ParamArray. We always know the type of this arg anyway: ByRef Variant[].
                    // To the caller, though, the individual args are sent as a sequence, not packed into an array.
                    endsWithParamArray = funcDesc.cParamsOpt == -1;
                    if (endsWithParamArray)
                    {
                        paramCount--;
                    }
                    paramTypes = new Type[paramCount];
                    paramFlags = new PARAMFLAG[paramCount];
                    IntPtr pElemDescArray = funcDesc.lprgelemdescParam;
                    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++)
                    {
                        ELEMDESC elemDesc = (ELEMDESC)Marshal.PtrToStructure((IntPtr)(pElemDescArray.ToInt64() +
                                                                                      paramIndex * Marshal.SizeOf(typeof(ELEMDESC))), typeof(ELEMDESC));
                        TYPEDESC typeDesc = elemDesc.tdesc;
                        paramFlags[paramIndex] = elemDesc.desc.paramdesc.wParamFlags;
                        // I think I should never see a retval param here. They have been automatically converted to return types.
                        Debug.Assert((paramFlags[paramIndex] & PARAMFLAG.PARAMFLAG_FRETVAL) == 0);
                        VarEnum variantType = (VarEnum)typeDesc.vt;
                        bool    isArray     = variantType == VarEnum.VT_SAFEARRAY;
                        bool    isPtr       = variantType == VarEnum.VT_PTR;
                        bool    isOut       = (paramFlags[paramIndex] & PARAMFLAG.PARAMFLAG_FOUT) != 0;
                        // VB array params will have isPtr and !isArray, because they are always ByRef (thus ptrs).
                        // In general (always?), out params will be VT_PTR.
                        if (isArray || isPtr)
                        {
                            IntPtr   pElementTypeDesc = typeDesc.lpValue;
                            TYPEDESC elementTypeDesc  = (TYPEDESC)Marshal.PtrToStructure(pElementTypeDesc, typeof(TYPEDESC));
                            variantType = (VarEnum)elementTypeDesc.vt;
                            // If the arg was a ptr to an array (e.g., as in VB objects), do it again to get the element type.
                            if (variantType == VarEnum.VT_SAFEARRAY)
                            {
                                isArray          = true;
                                pElementTypeDesc = elementTypeDesc.lpValue;
                                elementTypeDesc  = (TYPEDESC)Marshal.PtrToStructure(pElementTypeDesc, typeof(TYPEDESC));
                                variantType      = (VarEnum)elementTypeDesc.vt;
                            }
                        }
                        paramTypes[paramIndex] = managedTypeForVariantType(variantType, isArray, isPtr, isOut);
                    }
                } finally {
                    iTypeInfo.ReleaseFuncDesc(pFuncDesc);
                }
                return(true);
            } catch (Exception) {
                return(true);
            }
        }