static void Main(string[] args)
        {
            // To try to imitate the issue in the Engine as closely as possible, the following is done:
            //  1. A WSC is created (this is like a Control on Page)
            //  2. This WSC gets a Response reference and its Go method is called, both over IDispatch
            //  3. Inside the WSC, it will call Response.CreateObject to get another WSC - this one is wrapped in a FromFileWSCWrapper (so it is like a Booking Component)
            //  4. That WSC gets its Response reference set by VBScript in the first WSC and then "Go" is called on the second WSC, which calls Response.Redirect, which throws a RedirectException
            //  5. The error is caught in the IDispatchAccess layer, which was used when the first component called the second's "Go" method (since that invocation went through IReflect and so
            //     through the FromFileWSCWrapper and called into the second component over IDispatch). The error has all of the message content, so it may be succesfully translated back into
            //     a RedirectException.
            //  6. If we call the first component using IDispatchAccess then the error is caught again there - the error code is correct (it may be mapped back onto a RedirectException) but its
            //     bstrDescription is null and so we can not retrieve the URL to redirect to (using COMSurvivableException.RethrowAsOriginalIfPossible will result in a RedirectException being
            //     raised with a generic message "Exception of type 'System.Runtime.InteropServices.COMException' was thrown" which is not good
            //     - However, if we call the first component's "Go" method using dynamic then we DO get the full RedirectException being throw, which is very good
            // Note: If the second component is not wrapped in a FromFileWSCWrapper then we get the full RedirectException whether we use IDispatch or dynamic
            var callOuterComponentMethodsUsingDynamic = false;

            var wscFile = new FileInfo("TestComponent.wsc");
            var wsc     = Interaction.GetObject("script:" + wscFile.FullName, null);

            try
            {
                var response = new WrappedResponse(new Response());
                IDispatchAccess.SetProperty(wsc, "Response", response);                 // Have to use IDispatchAccess here since dynamic sets the property using PutDispProperty instead of PutRefDispProperty (.net bug, I think)
                if (callOuterComponentMethodsUsingDynamic)
                {
                    ((dynamic)wsc).Go();
                }
                else
                {
                    IDispatchAccess.CallMethod(wsc, "Go");
                }
            }
            catch (RedirectException e)
            {
                Console.WriteLine("Redirect: " + e.Message);
                if (e.Message.StartsWith("Exception of type"))
                {
                    Console.WriteLine("^ ********** This is a disappointment");
                }
                else
                {
                    Console.WriteLine("^ ********** THIS IS WHAT WE WANT!");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("FAIL: " + e.Message + " (" + e.GetType().Name + ")");
                Console.WriteLine("^ ********** This is a disappointment");
            }
            Console.ReadLine();
        }
        private object InvokeMemberInner(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
        {
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            if (args == null)
            {
                throw new ArgumentNullException("args");
            }

            var requireDefaultMember = string.IsNullOrWhiteSpace(name) || (name == DispIdZeroIdentifier);

            if (!requireDefaultMember && invokeAttr.HasFlag(BindingFlags.GetProperty) && invokeAttr.HasFlag(BindingFlags.InvokeMethod))
            {
                // When VBScript tries to access a member that is not clearly a property or a method (eg. "a = x.Name") then it will include binding flags GetProperty AND
                // AND InvokeMethod but we need to try to work out which of the two it is now because the IDispatchAccess code doesn't like that ambiguity.
                var nameComparison = invokeAttr.HasFlag(BindingFlags.IgnoreCase) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
                if (_template.GetProperties().Any(p => p.Name.Equals(name, nameComparison)))
                {
                    invokeAttr = invokeAttr ^ BindingFlags.InvokeMethod;                     // It's a property so remove InvokeMethod by xor'ing
                }
                else
                {
                    invokeAttr = invokeAttr ^ BindingFlags.GetProperty;                     // It's not a property so remove GetProperty by xor'ing
                }
            }

            if (invokeAttr.HasFlag(BindingFlags.GetProperty))
            {
                object value;
                if (requireDefaultMember)
                {
                    value = IDispatchAccess.GetDefaultProperty <object>(_target, args);
                }
                else
                {
                    value = IDispatchAccess.GetProperty(_target, name, args);
                }
                return(CurrencyToFloatWhereApplicable(value));
            }

            if (requireDefaultMember)
            {
                throw new Exception("Currently there is only support for default member (DispId zero) on GetProperty requests");
            }

            if (invokeAttr.HasFlag(BindingFlags.SetProperty) || invokeAttr.HasFlag(BindingFlags.PutDispProperty) || invokeAttr.HasFlag(BindingFlags.PutRefDispProperty))
            {
                var value = args[args.Length - 1];
                if ((value == null) && invokeAttr.HasFlag(BindingFlags.PutRefDispProperty))
                {
                    // When a VBScript statement "Set x = Nothing" becomes an IReflect.InvokeMember call, the invokeAttr will have value PutRefDispProperty (because it's
                    // a property setter call for an *object* reference, rather than PutDispProperty - which is for a non-object type) but the value will have been
                    // interpreted as null (.net null, not VBScript null). This will be problematic if we try to pass it on to the underlying reference through
                    // IDispatch because it will be like us saying "Set x = Empty", which will fail because Empty is not an object. So we need to look out
                    // for this case and transform the .net null back into VBScript Nothing.
                    value = new DispatchWrapper(null);
                }
                var argsWithoutValues = new object[args.Length - 1];
                Array.Copy(args, argsWithoutValues, args.Length - 1);
                IDispatchAccess.SetProperty(_target, name, value, argsWithoutValues);
                return(null);
            }

            if (invokeAttr.HasFlag(BindingFlags.InvokeMethod))
            {
                return(CurrencyToFloatWhereApplicable(IDispatchAccess.CallMethod(_target, name, args)));
            }

            throw new Exception("Don't know what to do with invokeAttr " + invokeAttr);
        }