예제 #1
0
        // Used to resolve a given type reference (params, fields, etc.)
        public IConvBase GetTypeRef(ConvType convType, TypeInfo type)
        {
            IConvBase ret = null;

            if (!ResolveInternal(type, convType, out ret))
            {
                switch (convType)
                {
                case ConvType.Enum:
                    ret = new ConvEnumLocal(this, type);
                    break;

                case ConvType.Interface:
                    ret = new ConvInterfaceLocal(this, type);
                    break;

                case ConvType.Struct:
                    ret = new ConvStructLocal(this, type);
                    break;

                case ConvType.Union:
                    ret = new ConvUnionLocal(this, type);
                    break;

                case ConvType.CoClass:
                    ret = new ConvCoClassLocal(this, type);
                    break;

                case ConvType.Module:
                    ret = new ConvModuleLocal(this, type);
                    break;

                default:
                    Debug.Assert(false);
                    break;
                }
            }

            Debug.Assert(ret.ConvType == convType);

            return(ret);
        }
예제 #2
0
        private void _Convert()
        {
            VarEnum vt = (VarEnum)m_typeDesc.vt;

            // Strip out VT_PTR
            while (vt == VarEnum.VT_PTR)
            {
                m_typeDesc = m_typeDesc.lptdesc;
                vt         = (VarEnum)m_typeDesc.vt;
                m_nativeIndirections++;
            }

            // Strip out VT_BYREF
            if ((vt & VarEnum.VT_BYREF) != 0)
            {
                vt &= ~VarEnum.VT_BYREF;
                m_nativeIndirections++;
            }

            //
            // Find the corresponding type and save it in result and store the custom attribute in m_attribute
            //
            Type result = null;

            m_attribute = null;
            switch (vt)
            {
            case VarEnum.VT_HRESULT:
                result      = typeof(int);
                m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.Error);
                SetUnmanagedType(UnmanagedType.Error);
                break;

            case VarEnum.VT_VOID:
                result = typeof(void);
                break;

            case VarEnum.VT_UINT:
                result = typeof(uint);
                break;

            case VarEnum.VT_INT:
                result = typeof(int);
                break;

            case VarEnum.VT_UI1:
                result = typeof(byte);
                break;

            case VarEnum.VT_UI2:
                result = typeof(ushort);
                break;

            case VarEnum.VT_UI4:
                result = typeof(uint);
                break;

            case VarEnum.VT_UI8:
                result = typeof(ulong);
                break;

            case VarEnum.VT_I1:
                result = typeof(sbyte);
                break;

            case VarEnum.VT_I2:
                result = typeof(short);
                break;

            case VarEnum.VT_I4:
                result = typeof(int);
                break;

            case VarEnum.VT_I8:
                result = typeof(long);
                break;

            case VarEnum.VT_R4:
                result = typeof(float);
                break;

            case VarEnum.VT_R8:
                result = typeof(double);
                break;

            case VarEnum.VT_ERROR:
                result      = typeof(int);
                m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.Error);
                SetUnmanagedType(UnmanagedType.Error);

                break;

            case VarEnum.VT_BSTR:
                result      = typeof(string);
                m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.BStr);
                SetUnmanagedType(UnmanagedType.BStr);

                // BSTR => string is special as BSTR are actually OLECHAR*, so we should add one indirection
                m_nativeIndirections++;
                break;

            case VarEnum.VT_DISPATCH:
                if (m_convertingNewEnumMember)
                {
                    // When we are creating a new enum member, convert IDispatch to IEnumVariant
                    TryUseCustomMarshaler(WellKnownGuids.IID_IEnumVARIANT, out result);
                }
                else
                {
                    result      = typeof(object);
                    m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.IDispatch);
                    SetUnmanagedType(UnmanagedType.IDispatch);
                }

                // VT_DISPATCH => IDispatch *
                m_nativeIndirections++;

                break;

            case VarEnum.VT_UNKNOWN:
                if (m_convertingNewEnumMember)
                {
                    // When we are creating a new enum member, convert IUnknown to IEnumVariant
                    TryUseCustomMarshaler(WellKnownGuids.IID_IEnumVARIANT, out result);
                }
                else
                {
                    result      = typeof(object);
                    m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.IUnknown);
                    SetUnmanagedType(UnmanagedType.IUnknown);
                }

                // VT_UNKNOWN => IUnknown *
                m_nativeIndirections++;

                break;

            case VarEnum.VT_LPSTR:
                result      = typeof(string);
                m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.LPStr);
                SetUnmanagedType(UnmanagedType.LPStr);
                m_nativeIndirections++;
                break;

            case VarEnum.VT_LPWSTR:
                result      = typeof(string);
                m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.LPWStr);
                SetUnmanagedType(UnmanagedType.LPWStr);
                m_nativeIndirections++;
                break;

            case VarEnum.VT_PTR:
                Debug.Assert(false, "Should not get here");
                break;

            case VarEnum.VT_SAFEARRAY:
            {
                TypeDesc arrayDesc       = m_typeDesc.lpadesc.tdescElem;
                VarEnum  arrayVt         = (VarEnum)arrayDesc.vt;
                Type     userDefinedType = null;

                TypeConverter elemTypeConverter = new TypeConverter(m_info, m_typeInfo, arrayDesc, ConversionType.Element);
                Type          elemType          = elemTypeConverter.ConvertedType;

                // Determine the right VT for MarshalAs attribute
                bool pointerArray = false;
                if (arrayVt == VarEnum.VT_PTR)
                {
                    arrayDesc    = arrayDesc.lptdesc;
                    arrayVt      = (VarEnum)arrayDesc.vt;
                    pointerArray = true;

                    // We don't support marshalling pointers in array except UserType* & void*
                    if (arrayVt != VarEnum.VT_USERDEFINED && arrayVt != VarEnum.VT_VOID)
                    {
                        arrayVt          = VarEnum.VT_INT;
                        m_conversionLoss = true;
                    }
                }

                //
                // Emit UserDefinedSubType if necessary
                //
                if (arrayVt == VarEnum.VT_USERDEFINED)
                {
                    if (elemType.IsEnum)
                    {
                        if (pointerArray)
                        {
                            arrayVt          = VarEnum.VT_INT;
                            m_conversionLoss = true;
                        }
                        else
                        {
                            // For enums, using VT_RECORD is better than VT_I4
                            // Within the runtime, if you specify VT_I4 for enums in SafeArray, we treat it the same way as VT_RECORD
                            // Reflection API also accepts VT_RECORD instead of VT_I4
                            arrayVt = VarEnum.VT_RECORD;
                        }
                    }
                    else if (elemType.IsValueType)
                    {
                        if (pointerArray)
                        {
                            arrayVt          = VarEnum.VT_INT;
                            m_conversionLoss = true;
                        }
                        else
                        {
                            arrayVt = VarEnum.VT_RECORD;
                        }
                    }
                    else if (elemType.IsInterface)
                    {
                        if (pointerArray)
                        {
                            // decide VT_UNKNOWN / VT_DISPATCH
                            if (InterfaceSupportsDispatch(elemType))
                            {
                                arrayVt = VarEnum.VT_DISPATCH;
                            }
                            else
                            {
                                arrayVt = VarEnum.VT_UNKNOWN;
                            }
                        }
                        else
                        {
                            arrayVt          = VarEnum.VT_INT;
                            m_conversionLoss = true;
                        }
                    }
                    else if (elemType == typeof(object) && !elemTypeConverter.UseDefaultMarshal &&
                             (elemTypeConverter.UnmanagedType == UnmanagedType.IUnknown))
                    {
                        // Special case for object that doesn't have default interface and will be marshalled as IUnknown
                        arrayVt = VarEnum.VT_UNKNOWN;
                    }

                    userDefinedType = elemType;
                }

                m_conversionLoss |= elemTypeConverter.IsConversionLoss;

                // Transform to System.Array if /sysarray is set and not vararg
                if (((m_info.Settings.m_flags & TypeLibImporterFlags.SafeArrayAsSystemArray) != 0) &&
                    m_conversionType != ConversionType.VarArgParameter)
                {
                    result = typeof(System.Array);
                }
                else
                {
                    result = elemType.MakeArrayType();

                    // Don't need SafeArrayUserDefinedSubType for non System.Array case
                    userDefinedType = null;
                }

                // TlbImp doesn't have this check for vt == VT_RECORD/VT_UNKNOWN/VT_DISPATCH therefore
                // it will emit SafeArrayUserDefinedSubType even it is not necessary/not valid
                // TlbImp2 will take this into account
                if ((userDefinedType != null) && (arrayVt == VarEnum.VT_RECORD || arrayVt == VarEnum.VT_UNKNOWN || arrayVt == VarEnum.VT_DISPATCH))
                {
                    // The name of the type would be full name in TlbImp2
                    m_attribute = CustomAttributeHelper.GetBuilderForMarshalAsSafeArrayAndUserDefinedSubType(arrayVt, userDefinedType);
                }
                else
                {
                    // Use I4 for enums when SafeArrayUserDefinedSubType is not specified
                    if (elemType.IsEnum && arrayVt == VarEnum.VT_RECORD)
                    {
                        arrayVt = VarEnum.VT_I4;
                    }
                    m_attribute = CustomAttributeHelper.GetBuilderForMarshalAsSafeArray(arrayVt);
                }

                SetUnmanagedType(UnmanagedType.SafeArray);

                // SafeArray <=> array is special because SafeArray is similar to Element*
                m_nativeIndirections++;

                break;
            }

            case VarEnum.VT_RECORD:
            case VarEnum.VT_USERDEFINED:
            {
                // Handle structs, interfaces, enums, and unions

                // Check if any ResolveTo Rule applied.
                TypeInfo refType = m_typeInfo.GetRefTypeInfo(m_typeDesc.hreftype);
                TypeAttr refAttr = refType.GetTypeAttr();
                Type     resolveToType;
                if (RuleEngineResolveRedirection(m_info.Settings.m_ruleSet, refType,
                                                 out resolveToType))
                {
                    result = resolveToType;
                    break;
                }

                // Support for aliasing
                TypeInfo realType;
                TypeAttr realAttr;
                ConvCommon.ResolveAlias(m_typeInfo, m_typeDesc, out realType, out realAttr);

                // Alias for a built-in type?
                if (realAttr.typekind == TypeLibTypes.Interop.TYPEKIND.TKIND_ALIAS)
                {
                    // Recurse to convert the built-in type
                    TypeConverter builtinType = new TypeConverter(m_info, realType, realAttr.tdescAlias, m_conversionType);
                    result      = builtinType.ConvertedType;
                    m_attribute = builtinType.m_attribute;
                }
                else
                {
                    // Otherwise, we must have a non-aliased type, and it is a user defined type
                    // We should use the TypeInfo that this TypeDesc refers to
                    realType = m_typeDesc.GetUserDefinedTypeInfo(m_typeInfo);
                    TypeLibTypes.Interop.TYPEKIND typeKind = realAttr.typekind;

                    using (realAttr = realType.GetTypeAttr())
                    {
                        TypeLib typeLib = realType.GetContainingTypeLib();

                        // Convert StdOle2.Guid to System.Guid
                        if (_IsStdOleGuid(realType))
                        {
                            result      = typeof(Guid);
                            m_attribute = null;
                            ResetUnmanagedType();
                        }
                        else if (realAttr.Guid == WellKnownGuids.IID_IUnknown)
                        {
                            // Occasional goto makes sense
                            // If VT_USERDEFINE *, and the VT_USERDEFINE is actually a VT_UNKNOWN => IUnknown *, we need to decrease the m_nativeIndirections
                            // to compensate for the m_nativeIndirections++ in VT_UNKNOWN
                            m_nativeIndirections--;
                            goto case VarEnum.VT_UNKNOWN;
                        }
                        else if (realAttr.Guid == WellKnownGuids.IID_IDispatch)
                        {
                            // Occasional goto makes sense
                            // See the IID_IUnknown case for why we need to --
                            m_nativeIndirections--;
                            goto case VarEnum.VT_DISPATCH;
                        }
                        else
                        {
                            // Need to use CustomMarshaler?
                            Type customMarshalerResultType;
                            if (TryUseCustomMarshaler(realAttr.Guid, out customMarshalerResultType))
                            {
                                result = customMarshalerResultType;
                            }
                            else
                            {
                                IConvBase ret = m_info.GetTypeRef(ConvCommon.TypeKindToConvType(typeKind), realType);

                                if (m_conversionType == ConversionType.Field)
                                {
                                    // Too bad. Reflection API requires that the field type must be created before creating
                                    // the struct/union type

                                    // Only process indirection = 0 case because > 1 case will be converted to IntPtr
                                    // Otherwise it will leads to a infinite recursion, if you consider the following scenario:
                                    // struct A
                                    // {
                                    //      struct B
                                    //      {
                                    //          struct A *a;
                                    //      } b;
                                    // }
                                    if (ret is ConvUnionLocal && m_nativeIndirections == 0)
                                    {
                                        ConvUnionLocal convUnion = ret as ConvUnionLocal;
                                        convUnion.Create();
                                    }
                                    else if (ret is ConvStructLocal && m_nativeIndirections == 0)
                                    {
                                        ConvStructLocal convStruct = ret as ConvStructLocal;
                                        convStruct.Create();
                                    }
                                    else if (ret is ConvEnumLocal && m_nativeIndirections == 0)
                                    {
                                        ConvEnumLocal convEnum = ret as ConvEnumLocal;
                                        convEnum.Create();
                                    }
                                }

                                result = ret.ManagedType;

                                // Don't reply on result.IsInterface as we have some weird scenarios like refering to a exported type lib
                                // which has interfaces that are class interfaces and have the same name as a class.
                                // For example, manage class M has a class interface _M, and their managed name are both M
                                if (ret.ConvType == ConvType.Interface || ret.ConvType == ConvType.EventInterface || ret.ConvType == ConvType.ClassInterface)
                                {
                                    m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.Interface);
                                    SetUnmanagedType(UnmanagedType.Interface);
                                }

                                if (ret.ConvType == ConvType.CoClass)
                                {
                                    // We need to convert CoClass to default interface (could be converted to class interface if it is exclusive) in signatures
                                    Debug.Assert(ret is IConvCoClass);
                                    IConvCoClass convCoClass = ret as IConvCoClass;
                                    if (convCoClass.DefaultInterface != null)
                                    {
                                        // Use the default interface
                                        result      = convCoClass.DefaultInterface.ManagedType;
                                        m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.Interface);
                                        SetUnmanagedType(UnmanagedType.Interface);
                                    }
                                    else
                                    {
                                        // The coclass has no default interface (source interface excluded)
                                        // Marshal it as IUnknown
                                        result      = typeof(object);
                                        m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.IUnknown);
                                        SetUnmanagedType(UnmanagedType.IUnknown);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            break;

            case VarEnum.VT_VARIANT:
                result      = typeof(object);
                m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.Struct);
                SetUnmanagedType(UnmanagedType.Struct);

                // object is special that it will be marshaled to VARIANT
                // because we'll think object as having one indirection, now we are one indirection less,
                // so we need add 1 to m_indirections
                m_nativeIndirections++;
                break;

            case VarEnum.VT_CY:
                result      = typeof(System.Decimal);
                m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.Currency);
                SetUnmanagedType(UnmanagedType.Currency);

                break;

            case VarEnum.VT_DATE:
                result = typeof(System.DateTime);
                break;

            case VarEnum.VT_DECIMAL:
                result = typeof(System.Decimal);
                break;


            case VarEnum.VT_CARRAY:
            {
                TypeDesc      elemTypeDesc      = m_typeDesc.lptdesc;
                TypeConverter elemTypeConverter = new TypeConverter(m_info, m_typeInfo, elemTypeDesc, ConversionType.Element);
                Type          elemType          = elemTypeConverter.ConvertedType;
                result = elemType.MakeArrayType();

                m_conversionLoss |= elemTypeConverter.IsConversionLoss;

                uint             elements = 1;
                SAFEARRAYBOUND[] bounds   = m_typeDesc.lpadesc.Bounds;
                foreach (SAFEARRAYBOUND bound in bounds)
                {
                    elements *= bound.cElements;
                }

                // SizeConst can only hold Int32.MaxValue
                if (elements <= Int32.MaxValue)
                {
                    UnmanagedType arrayType;
                    if (m_conversionType == ConversionType.Field)
                    {
                        arrayType = UnmanagedType.ByValArray;
                    }
                    else
                    {
                        arrayType = UnmanagedType.LPArray;
                    }

                    if (elemTypeConverter.UseDefaultMarshal)
                    {
                        m_attribute = CustomAttributeHelper.GetBuilderForMarshalAsConstArray(arrayType, (int)elements);
                    }
                    else
                    {
                        if (elemTypeConverter.UnmanagedType == UnmanagedType.BStr ||
                            elemTypeConverter.UnmanagedType == UnmanagedType.LPStr ||
                            elemTypeConverter.UnmanagedType == UnmanagedType.LPWStr ||
                            elemTypeConverter.UnmanagedType == UnmanagedType.VariantBool)
                        {
                            m_attribute = CustomAttributeHelper.GetBuilderForMarshalAsConstArray(arrayType, (int)elements, elemTypeConverter.UnmanagedType);
                        }
                        else
                        {
                            m_attribute = CustomAttributeHelper.GetBuilderForMarshalAsConstArray(arrayType, (int)elements);
                        }
                    }

                    SetUnmanagedType(arrayType);
                }
                else
                {
                    m_nativeIndirections = 0;
                    result      = typeof(IntPtr);
                    m_attribute = null;
                    ResetUnmanagedType();
                    m_conversionLoss = true;
                }
            }
            break;

            case VarEnum.VT_BOOL:
                // For VT_BOOL in fields, use short if v2 switch is not specified.
                if (m_conversionType == ConversionType.Field)
                {
                    if (m_info.Settings.m_isVersion2)
                    {
                        result      = typeof(bool);
                        m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.VariantBool);
                        SetUnmanagedType(UnmanagedType.VariantBool);
                    }
                    else
                    {
                        result = typeof(short);
                    }
                }
                else
                {
                    result = typeof(bool);
                    SetUnmanagedType(UnmanagedType.VariantBool);
                }
                break;

            default:
                m_info.ReportEvent(
                    WarningCode.Wrn_BadVtType,
                    Resource.FormatString("Wrn_BadVtType", (int)vt, m_typeInfo.GetDocumentation()));
                result           = typeof(IntPtr);
                m_conversionLoss = true;
                break;
            }

            //
            // String -> StringBuilder special case
            //
            if (result == typeof(string))
            {
                if (_IsParamOut() && m_nativeIndirections == 1 && (m_conversionType == ConversionType.Parameter || m_conversionType == ConversionType.VarArgParameter))
                {
                    // [out] or [in, out] LPSTR/LPWSTR scenario
                    if (vt != VarEnum.VT_BSTR)
                    {
                        // String is immutable and cannot be [out]/[in, out]. We can convert to StringBuilder
                        result = typeof(StringBuilder);
                    }
                    else // VT_BSTR
                    {
                        // VT_BSTR is also immutable. So conversion loss here
                        m_conversionLoss     = true;
                        result               = typeof(IntPtr);
                        m_attribute          = null;
                        m_nativeIndirections = 0;
                        ResetUnmanagedType();
                    }
                }
            }

            // Special rule for void* => IntPtr
            if (result == typeof(void))
            {
                result = typeof(IntPtr);

                switch (m_conversionType)
                {
                case ConversionType.Element:
                    m_nativeIndirections = 0;
                    break;

                case ConversionType.Field:
                    m_nativeIndirections = 0;
                    break;

                default:
                    if (m_nativeIndirections > 1)
                    {
                        m_nativeIndirections = 1;
                    }
                    else
                    {
                        m_nativeIndirections = 0;
                    }
                    break;
                }
            }

            //
            // If the type is already a byref type, remove the byref and add extra indirection(s).
            // This is necessary to avoid trying to call MakeByRef on the byref type
            //
            if (result.IsByRef)
            {
                result = result.GetElementType();
                if (result.IsValueType)
                {
                    m_nativeIndirections++;     // Value& = Value *
                }
                else
                {
                    m_nativeIndirections += 2;  // RefType& = RefType**
                }
            }

            //
            // Process indirection
            //
            if (m_nativeIndirections > 0)
            {
                if (result.IsValueType)
                {
                    switch (m_conversionType)
                    {
                    case ConversionType.VarArgParameter:
                    case ConversionType.Parameter:
                        // Decimal/Guid can support extra level of indirection using LpStruct in parameters
                        // LpStruct has no effect in other places and for other types
                        // Only use LpStruct for scenarios like GUID **
                        // This is different from old TlbImp. Old TlbImp will use IntPtr
                        if ((result == typeof(Decimal) || result == typeof(Guid)) && m_nativeIndirections == 2)
                        {
                            m_nativeIndirections--;
                            m_attribute = CustomAttributeHelper.GetBuilderForMarshalAs(UnmanagedType.LPStruct);
                            ResetUnmanagedType();
                            SetUnmanagedType(UnmanagedType.LPStruct);
                        }

                        if (m_nativeIndirections >= 2)
                        {
                            m_conversionLoss = true;
                            result           = typeof(IntPtr);
                            m_attribute      = null;
                            ResetUnmanagedType();
                        }
                        else if (m_nativeIndirections > 0)
                        {
                            result = result.MakeByRefType();
                        }

                        break;

                    case ConversionType.Field:
                        m_conversionLoss = true;
                        result           = typeof(IntPtr);
                        m_attribute      = null;
                        ResetUnmanagedType();
                        break;

                    case ConversionType.ParamRetVal:
                        m_nativeIndirections--;
                        goto case ConversionType.ReturnValue;
                    // Fall through to ConversionType.ReturnValue

                    case ConversionType.ReturnValue:
                        if (m_nativeIndirections >= 1)
                        {
                            m_conversionLoss = true;
                            result           = typeof(IntPtr);
                            m_attribute      = null;
                            ResetUnmanagedType();
                        }
                        break;

                    case ConversionType.Element:
                        m_conversionLoss = true;
                        result           = typeof(IntPtr);
                        m_attribute      = null;
                        ResetUnmanagedType();
                        break;
                    }
                }
                else
                {
                    switch (m_conversionType)
                    {
                    case ConversionType.Field:
                        // ** => IntPtr, ConversionLoss
                        if (m_nativeIndirections > 1)
                        {
                            result           = typeof(IntPtr);
                            m_conversionLoss = true;
                            m_attribute      = null;
                            ResetUnmanagedType();
                        }
                        break;

                    case ConversionType.VarArgParameter:
                    case ConversionType.Parameter:
                        if (m_nativeIndirections > 2)
                        {
                            result           = typeof(IntPtr);
                            m_conversionLoss = true;
                            m_attribute      = null;
                            ResetUnmanagedType();
                        }
                        else if (m_nativeIndirections == 2)
                        {
                            result = result.MakeByRefType();
                        }
                        break;

                    case ConversionType.ParamRetVal:
                        m_nativeIndirections--;
                        goto case ConversionType.ReturnValue;
                    // Fall through to ConversionType.ReturnValue

                    case ConversionType.ReturnValue:
                        if (m_nativeIndirections > 1)
                        {
                            result           = typeof(IntPtr);
                            m_conversionLoss = true;
                            m_attribute      = null;
                            ResetUnmanagedType();
                        }
                        break;

                    case ConversionType.Element:
                        if (m_nativeIndirections > 1)
                        {
                            m_conversionLoss = true;
                            result           = typeof(IntPtr);
                            m_attribute      = null;
                            ResetUnmanagedType();
                        }
                        break;
                    }
                }
            }

            m_convertedType = result;
        }