/// <summary>
        /// This method should be called when typeinfo is not available for an object. The function
        /// will check if the typeinfo is expected to be missing. This can include error cases where
        /// the same error is guaranteed to happen all the time, on all machines, under all circumstances.
        /// In such cases, we just have to operate without the typeinfo.
        ///
        /// However, if accessing the typeinfo is failing in a transient way, we might want to throw
        /// an exception so that we will eagerly predictably indicate the problem.
        /// </summary>
        private static void CheckIfMissingTypeInfoIsExpected(int hresult, bool throwIfMissingExpectedTypeInfo)
        {
            Debug.Assert(!ComHresults.IsSuccess(hresult));

            // Word.Basic always returns this because of an incorrect implementation of IDispatch.GetTypeInfo
            // Any implementation that returns E_NOINTERFACE is likely to do so in all environments
            if (hresult == ComHresults.E_NOINTERFACE)
            {
                return;
            }

            // This assert is potentially over-restrictive since COM components can behave in quite unexpected ways.
            // However, asserting the common expected cases ensures that we find out about the unexpected scenarios, and
            // can investigate the scenarios to ensure that there is no bug in our own code.
            Debug.Assert(hresult == ComHresults.TYPE_E_LIBNOTREGISTERED);

            if (throwIfMissingExpectedTypeInfo)
            {
                Marshal.ThrowExceptionForHR(hresult);
            }
        }
        /// <summary>
        /// Look for typeinfo using IDispatch.GetTypeInfo
        /// </summary>
        /// <param name="dispatch"></param>
        /// <param name="throwIfMissingExpectedTypeInfo">
        /// Some COM objects just dont expose typeinfo. In these cases, this method will return null.
        /// Some COM objects do intend to expose typeinfo, but may not be able to do so if the type-library is not properly
        /// registered. This will be considered as acceptable or as an error condition depending on throwIfMissingExpectedTypeInfo</param>
        /// <returns></returns>
        internal static ComTypes.ITypeInfo GetITypeInfoFromIDispatch(IDispatch dispatch, bool throwIfMissingExpectedTypeInfo)
        {
            uint typeCount;
            int  hresult = dispatch.TryGetTypeInfoCount(out typeCount);

            Marshal.ThrowExceptionForHR(hresult);
            Debug.Assert(typeCount <= 1);
            if (typeCount == 0)
            {
                return(null);
            }

            IntPtr typeInfoPtr = IntPtr.Zero;

            hresult = dispatch.TryGetTypeInfo(0, 0, out typeInfoPtr);
            if (!ComHresults.IsSuccess(hresult))
            {
                CheckIfMissingTypeInfoIsExpected(hresult, throwIfMissingExpectedTypeInfo);
                return(null);
            }
            if (typeInfoPtr == IntPtr.Zero)   // be defensive against components that return IntPtr.Zero
            {
                if (throwIfMissingExpectedTypeInfo)
                {
                    Marshal.ThrowExceptionForHR(ComHresults.E_FAIL);
                }
                return(null);
            }

            ComTypes.ITypeInfo typeInfo = null;
            try {
                typeInfo = Marshal.GetObjectForIUnknown(typeInfoPtr) as ComTypes.ITypeInfo;
            } finally {
                Marshal.Release(typeInfoPtr);
            }

            return(typeInfo);
        }
        public static void CheckThrowException(int hresult, ref ExcepInfo excepInfo, uint argErr, string message)
        {
            if (ComHresults.IsSuccess(hresult))
            {
                return;
            }

            switch (hresult)
            {
            case ComHresults.DISP_E_BADPARAMCOUNT:
                // The number of elements provided to DISPPARAMS is different from the number of arguments
                // accepted by the method or property.
                throw Error.DispBadParamCount(message);

            case ComHresults.DISP_E_BADVARTYPE:
                //One of the arguments in rgvarg is not a valid variant type.
                break;

            case ComHresults.DISP_E_EXCEPTION:
                // The application needs to raise an exception. In this case, the structure passed in pExcepInfo
                // should be filled in.
                throw excepInfo.GetException();

            case ComHresults.DISP_E_MEMBERNOTFOUND:
                // The requested member does not exist, or the call to Invoke tried to set the value of a
                // read-only property.
                throw Error.DispMemberNotFound(message);

            case ComHresults.DISP_E_NONAMEDARGS:
                // This implementation of IDispatch does not support named arguments.
                throw Error.DispNoNamedArgs(message);

            case ComHresults.DISP_E_OVERFLOW:
                // One of the arguments in rgvarg could not be coerced to the specified type.
                throw Error.DispOverflow(message);

            case ComHresults.DISP_E_PARAMNOTFOUND:
                // One of the parameter DISPIDs does not correspond to a parameter on the method. In this case,
                // puArgErr should be set to the first argument that contains the error.
                break;

            case ComHresults.DISP_E_TYPEMISMATCH:
                // One or more of the arguments could not be coerced. The index within rgvarg of the first
                // parameter with the incorrect type is returned in the puArgErr parameter.
                throw Error.DispTypeMismatch(argErr, message);

            case ComHresults.DISP_E_UNKNOWNINTERFACE:
                // The interface identifier passed in riid is not IID_NULL.
                break;

            case ComHresults.DISP_E_UNKNOWNLCID:
                // The member being invoked interprets string arguments according to the LCID, and the
                // LCID is not recognized.
                break;

            case ComHresults.DISP_E_PARAMNOTOPTIONAL:
                // A required parameter was omitted.
                throw Error.DispParamNotOptional(message);
            }

            Marshal.ThrowExceptionForHR(hresult);
        }