/// <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)); } }
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); }
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)); } }
/// <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); }
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); } }
/// <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); }
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"); } }
/// <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) { }
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; }
public ElemDesc(FuncDesc funcdesc, ELEMDESC elemdesc) { m_parent = funcdesc; Init(elemdesc); }
public ElemDesc(VarDesc varDesc, ELEMDESC elemdesc) { m_parent = varDesc; Init(elemdesc); }
/// <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); } }