/// <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); } } } }
// The TypeLibrary is used to resolve any typedefs public static String TYPEDESCToString(TypeLibrary typeLib, UCOMITypeInfo typeInfo, TYPEDESC typeDesc, bool comType) { String ret = TYPEDESCToStringInt(typeLib, typeInfo, typeDesc, comType, 0); return(ret); }
/// <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); }
protected override CodeObject GenerateCodeDom() { IntPtr varDescPtr; CORRECT_VARDESC varDesc; CodeMemberField codeDom; codeDom = new CodeMemberField(); _typeInfo.GetVarDesc(_index, out varDescPtr); varDesc = (CORRECT_VARDESC) Marshal.PtrToStructure(varDescPtr, typeof(CORRECT_VARDESC)); codeDom.Name = Name; codeDom.Attributes = MemberAttributes.Public; if (_typeKind == TYPEKIND.TKIND_ENUM) { try { Object value = Marshal.GetObjectForNativeVariant (varDesc.u.lpvarValue); codeDom.InitExpression = new CodePrimitiveExpression(value); } catch { // Ignore, was handled earlier } } TYPEDESC typeDesc = varDesc.elemdescVar.tdesc; codeDom.Type = new CodeTypeReference (TypeLibUtil.TYPEDESCToString(_typeLib, _typeInfo, typeDesc, !TypeLibUtil.COMTYPE)); _typeInfo.ReleaseVarDesc(varDescPtr); return(codeDom); }
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 static String TYPEDESCToStringInt(TypeLibrary typeLib, UCOMITypeInfo typeInfo, TYPEDESC typeDesc, bool comType, int level) { String ret; try { if ((VarEnum)typeDesc.vt == VarEnum.VT_PTR || (VarEnum)(typeDesc.vt & ActiveX.VT_TYPEMASK) == VarEnum.VT_SAFEARRAY) { TYPEDESC pTypeDesc = (TYPEDESC)Marshal.PtrToStructure(typeDesc.lpValue, typeof(TYPEDESC)); ret = TYPEDESCToStringInt(typeLib, typeInfo, pTypeDesc, comType, level + 1); if ((VarEnum)(typeDesc.vt & ActiveX.VT_TYPEMASK) == VarEnum.VT_SAFEARRAY) { // FIXME - what about the non-comType return("SAFEARRAY(" + ret + ")"); } if (comType) { ret += "*"; } else { // void* become IntPtr if (ret.Equals("void")) { ret = "System.IntPtr"; } else { // The first pointer is not a ref, its only // a ref if there are two // FIXME - what if there are more? if (level == 1) { ret = "ref " + ret; } } } return(ret); } if ((VarEnum)(typeDesc.vt & ActiveX.VT_TYPEMASK) == VarEnum.VT_CARRAY) { // typeDesc.lpValue in this case is really the laValue // (since TYPEDESC is a contains a union of pointers) ARRAYDESC pArrayDesc = (ARRAYDESC)Marshal.PtrToStructure(typeDesc.lpValue, typeof(ARRAYDESC)); ret = TYPEDESCToStringInt(typeLib, typeInfo, pArrayDesc.tdescElem, comType, level + 1); // Just show the number of diminsions, don't worry about // showing the size of each since we don't want to // get into marshalling the variable length ARRAYDESC // structure for (int i = 0; i < pArrayDesc.cDims; i++) { ret += "[]"; } return(ret); } if ((VarEnum)typeDesc.vt == VarEnum.VT_USERDEFINED) { UCOMITypeInfo uTypeInfo = null; // FIXME - sometimes this chokes and hangs due to a bad // handle value here, need to do something to prevent this int href = typeDesc.lpValue.ToInt32(); typeInfo.GetRefTypeInfo(href, out uTypeInfo); if (uTypeInfo != null) { String docName; String docString; int helpContext; String helpFile; uTypeInfo.GetDocumentation(-1, out docName, out docString, out helpContext, out helpFile); // Fix up misc references if (docName.Equals("GUID")) { docName = "System.Guid"; } // Present the user names for the types in COM // mode, but for the CLR types, get the real // underlying names if (!comType) { return(typeLib.ResolveTypeDef(docName, comType)); } return(docName); } else { TraceUtil.WriteLineWarning(null, "USER: "******" 0x" + href.ToString("X") + " ***UNKNOWN***"); return("(userDef unknown)"); } } return(GetTypeStr(typeDesc.vt, comType)); } catch (Exception ex) { TraceUtil.WriteLineWarning (null, "ActiveX type conversion error: " + ex); return("(error)"); } }
internal ComVariableInfo(BasicInfo parent, TYPEKIND typeKind, UCOMITypeInfo typeInfo, int index) : base(typeInfo) { IntPtr varDescPtr; CORRECT_VARDESC varDesc; _typeKind = typeKind; _typeLib = parent.TypeLib; _container = parent; _index = index; _typeInfo.GetVarDesc(_index, out varDescPtr); varDesc = (CORRECT_VARDESC) Marshal.PtrToStructure(varDescPtr, typeof(CORRECT_VARDESC)); int actLen; String[] memberNames = new String[100]; _typeInfo.GetNames(varDesc.memid, memberNames, memberNames.Length, out actLen); Name = memberNames[0]; if (TraceUtil.If(this, TraceLevel.Verbose)) { Trace.WriteLine("VariableInfo: " + _name); } if (_typeKind == TYPEKIND.TKIND_ENUM) { _infoType = "Constant"; try { Object value = Marshal.GetObjectForNativeVariant (varDesc.u.lpvarValue); _enumValue = value.ToString(); } catch (Exception ex) { _enumValue = "Unknown variant: 0x" + varDesc.u.lpvarValue.ToInt32().ToString("X"); TraceUtil.WriteLineWarning(this, "Exception reading enum value: " + ex); } } else { _infoType = "Variable"; } TYPEDESC typeDesc = varDesc.elemdescVar.tdesc; _varType = TypeLibUtil.TYPEDESCToString (_typeLib, _typeInfo, typeDesc, TypeLibUtil.COMTYPE); _typeInfo.ReleaseVarDesc(varDescPtr); _presInfo = PresentationMap. GetInfo(PresentationMap.COM_VARIABLE); }
/// <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); } }