/// <summary>Attempts to convert the script object to a string representation.</summary> /// <returns>A string representation of the script object, if the object can be converted; otherwise, a string representation of the object's default property or method.</returns> // Token: 0x060061E7 RID: 25063 RVA: 0x001B74C8 File Offset: 0x001B56C8 public override string ToString() { Guid empty = Guid.Empty; object obj = null; NativeMethods.DISPPARAMS dp = new NativeMethods.DISPPARAMS(); int dispid; HRESULT hresult; if (this.TryGetDispIdForMember("toString", true, out dispid)) { hresult = this.InvokeOnScriptObject(dispid, 1, dp, null, out obj); } else { dispid = 0; hresult = this.InvokeOnScriptObject(dispid, 2, dp, null, out obj); if (hresult.Failed) { hresult = this.InvokeOnScriptObject(dispid, 1, dp, null, out obj); } } if (hresult.Succeeded && obj != null) { return(obj.ToString()); } return(base.ToString()); }
private HRESULT InvokeOnScriptObject(int dispid, int flags, NativeMethods.DISPPARAMS dp, NativeMethods.EXCEPINFO exInfo, out object result) { // If we use reflection to call script code, we need to Assert for the UnmanagedCode permission. // But it will be a security issue when the WPF app makes a framework object available to the // hosted script via ObjectForScripting or as parameter of InvokeScript, and calls the framework // API that demands the UnmanagedCode permission. We do not want the demand to succeed. However, // the stack walk will ignore the native frames and keeps going until it reaches the Assert. // // As an example, if a call to a script object causes an immediate callback before the initial // call returns, reentrancy occurs via COM's blocking message loop on outgoing calls: // // [managed ComVisible object] // [CLR COM interop] // [COM runtime] // ole32.dll!CCliModalLoop::BlockFn() // ole32.dll!ModalLoop() // [COM runtime] // PresentationFramework!DynamicScriptObject::InvokeScript(...) // // That is why we switch to invoking the script via IDispatch with SUCS on the methods. if (_scriptObjectEx != null) { // This case takes care of IE hosting where the use of IDispatchEx is recommended by IE people // since the service provider object we can pass here is used by the browser to enforce cross- // zone scripting mitigations. return(_scriptObjectEx.InvokeEx(dispid, Thread.CurrentThread.CurrentCulture.LCID, flags, dp, out result, exInfo, BrowserInteropHelper.HostHtmlDocumentServiceProvider)); } else { Guid guid = Guid.Empty; return(_scriptObject.Invoke(dispid, ref guid, Thread.CurrentThread.CurrentCulture.LCID, flags, dp, out result, exInfo, null)); } }
private HRESULT InvokeOnScriptObject(int dispid, int flags, NativeMethods.DISPPARAMS dp, NativeMethods.EXCEPINFO exInfo, out object result) { if (this._scriptObjectEx != null) { return(this._scriptObjectEx.InvokeEx(dispid, Thread.CurrentThread.CurrentCulture.LCID, flags, dp, out result, exInfo, BrowserInteropHelper.HostHtmlDocumentServiceProvider)); } Guid empty = Guid.Empty; return(this._scriptObject.Invoke(dispid, ref empty, Thread.CurrentThread.CurrentCulture.LCID, flags, dp, out result, exInfo, null)); }
/// <summary> /// Provides a string representation of the wrapped script object. /// </summary> /// <returns>String representation of the wrapped script object, using the toString method or default member on the script object.</returns> public override string ToString() { // Note we shouldn't throw in this method (rule CA1065), so we try with best attempt. HRESULT hr; Guid guid = Guid.Empty; object result = null; var dp = new NativeMethods.DISPPARAMS(); // Try to find a toString method. int dispid; if (TryGetDispIdForMember("toString", true /* DISPID caching */, out dispid)) { hr = InvokeOnScriptObject(dispid, NativeMethods.DISPATCH_METHOD, dp, null /* EXCEPINFO */, out result); } else { // If no toString method is found, we try the default member first as a property, then as a method. dispid = NativeMethods.DISPID_VALUE; hr = InvokeOnScriptObject(dispid, NativeMethods.DISPATCH_PROPERTYGET, dp, null /* EXCEPINFO */, out result); if (hr.Failed) { hr = InvokeOnScriptObject(dispid, NativeMethods.DISPATCH_METHOD, dp, null /* EXCEPINFO */, out result); } } if (hr.Succeeded && result != null) { return(result.ToString()); } return(base.ToString()); }
internal unsafe bool TryFindMemberAndInvokeNonWrapped(string memberName, int flags, bool cacheDispId, object[] args, out object result) { result = null; // In true DLR style we'd return false here, deferring further attempts for resolution to the // call site. For better debuggability though, and as we're talking to a specialized object // model, we rather throw instead. This Try-method allows internal lookups without directly // throwing on non-fatal "member not found" situations, as we might want to have more complex // fallback logic (as in ToString). int dispid; if (!TryGetDispIdForMember(memberName, cacheDispId, out dispid)) { return false; } NativeMethods.DISPPARAMS dp = new NativeMethods.DISPPARAMS(); // Required additional DISPPARAMS arguments for PROPERTYPUT cases. See IDispatch::Invoke // documentation for more info. int propertyPutDispId = NativeMethods.DISPID_PROPERTYPUT; if (flags == NativeMethods.DISPATCH_PROPERTYPUT || flags == NativeMethods.DISPATCH_PROPERTYPUTREF) { dp.cNamedArgs = 1; // Stack allocated variables are fixed (unmoveable), so no need to use a fixed // statement. The local variable should not get repurposed by the compiler in an // unsafe block as we're handing out a pointer to it. For comparison, see the DLR // code, CorRuntimeHelpers.cs, where the ConvertInt32ByrefToPtr function relies // on the same behavior as they take an IntPtr to a stack-allocated variable that // gets used by a DISPPARAMS in a similar way. They have a separate method likely // because expression trees can't deal with unsafe code, and they need to fix as // they use a by-ref argument which is considered movable (see C# spec, 18.3): // // public static unsafe IntPtr ConvertInt32ByrefToPtr(ref Int32 value) { // fixed (Int32 *x = &value) { // AssertByrefPointsToStack(new IntPtr(x)); // return new IntPtr(x); // } // } dp.rgdispidNamedArgs = new IntPtr(&propertyPutDispId); } try { if (args != null) { // Callers of this method might want to implement fallbacks that require the original // arguments in the original order, maybe to feed it in to this method again. If we // wouldn't clone the arguments array into a local copy, we'd be reversing again. args = (object[])args.Clone(); // Reverse the argument order so that parameters read naturally after IDispatch. // This code was initially ported from [....], see [....] bug 187662. Array.Reverse(args); // Unwrap arguments that were already promoted to DynamicScriptObject. This can happen // if the output of a script accessing method is fed in to the input of another one. for (int i = 0; i < args.Length; i++) { var wrappedArg = args[i] as DynamicScriptObject; if (wrappedArg != null) { args[i] = wrappedArg._scriptObject; } if (args[i] != null) { // Arrays of COM visible objects (in our definition of the word, see further) are // not considered COM visible by themselves, so we take care of this case as well. // Jagged arrays are not supported somehow, causing a SafeArrayTypeMismatchException // in the call to GetNativeVariantForObject on ArrayToVARIANTVector called below. // Multi-dimensional arrays turn out to work fine, so we don't opt out from those. Type argType = args[i].GetType(); if (argType.IsArray) { argType = argType.GetElementType(); } // Caveat: IsTypeVisibleFromCom evaluates false for COM object wrappers provided // by the CLR. Therefore we also check for the IsCOMObject property. It also seems // COM interop special-cases DateTime as it's not revealed to be visible by any // of the first two checks below. if (!Marshal.IsTypeVisibleFromCom(argType) && !argType.IsCOMObject && argType != typeof(DateTime)) { throw new ArgumentException(SR.Get(SRID.NeedToBeComVisible)); } } } dp.rgvarg = UnsafeNativeMethods.ArrayToVARIANTHelper.ArrayToVARIANTVector(args); dp.cArgs = (uint)args.Length; } NativeMethods.EXCEPINFO exInfo = new NativeMethods.EXCEPINFO(); HRESULT hr = InvokeOnScriptObject(dispid, flags, dp, exInfo, out result); if (hr.Failed) { if (hr == HRESULT.DISP_E_MEMBERNOTFOUND) { return false; } // See KB article 247784, INFO: '80020101' Returned From Some ActiveX Scripting Methods. // Internet Explorer returns this error when it has already reported a script error to the user // through a dialog or by putting a message in the status bar (Page contains script error). We // want consistency between browsers, so route this through the DISP_E_EXCEPTION case. if (hr == HRESULT.SCRIPT_E_REPORTED) { exInfo.scode = hr.Code; hr = HRESULT.DISP_E_EXCEPTION; } // We prefix exception messagages with "[memberName]" so that the target of the invocation can // be found easily. This is useful beyond just seeing the call site in the debugger as dynamic // calls lead to complicated call stacks with the DLR sliced in between the source and target. // Also, for good or for bad, dynamic has the potential to encourage endless "dotting into", so // it's preferrable to have our runtime resolution failure eloquating the target of the dynamic // call. Unfortunately stock CLR exception types often don't offer a convenient spot to put // this info in, so we resort to the Message property. Anyway, those exceptions are primarily // meant to provide debugging convenience and should not be reported to the end-user in a well- // tested application. Essentially all of this is to be conceived as "deferred compilation". string member = "[" + (memberName ?? "(default)") + "]"; Exception comException = hr.GetException(); if (hr == HRESULT.DISP_E_EXCEPTION) { // We wrap script execution failures in TargetInvocationException to keep the API surface // free of COMExceptions that reflect a mere implementation detail. int errorCode = exInfo.scode != 0 ? exInfo.scode : exInfo.wCode; hr = HRESULT.Make(true /* severity */, Facility.Dispatch, errorCode); string message = member + " " + (exInfo.bstrDescription ?? string.Empty); throw new TargetInvocationException(message, comException) { HelpLink = exInfo.bstrHelpFile, Source = exInfo.bstrSource }; } else if (hr == HRESULT.DISP_E_BADPARAMCOUNT || hr == HRESULT.DISP_E_PARAMNOTOPTIONAL) { throw new TargetParameterCountException(member, comException); } else if (hr == HRESULT.DISP_E_OVERFLOW || hr == HRESULT.DISP_E_TYPEMISMATCH) { throw new ArgumentException(member, new InvalidCastException(comException.Message, hr.Code)); } else { // Something really bad has happened; keeping the exception as-is. throw comException; } } } finally { if (dp.rgvarg != IntPtr.Zero) { UnsafeNativeMethods.ArrayToVARIANTHelper.FreeVARIANTVector(dp.rgvarg, args.Length); } } return true; }
/// <summary> /// Provides a string representation of the wrapped script object. /// </summary> /// <returns>String representation of the wrapped script object, using the toString method or default member on the script object.</returns> public override string ToString() { // Note we shouldn't throw in this method (rule CA1065), so we try with best attempt. HRESULT hr; Guid guid = Guid.Empty; object result = null; var dp = new NativeMethods.DISPPARAMS(); // Try to find a toString method. int dispid; if (TryGetDispIdForMember("toString", true /* DISPID caching */, out dispid)) { hr = InvokeOnScriptObject(dispid, NativeMethods.DISPATCH_METHOD, dp, null /* EXCEPINFO */, out result); } else { // If no toString method is found, we try the default member first as a property, then as a method. dispid = NativeMethods.DISPID_VALUE; hr = InvokeOnScriptObject(dispid, NativeMethods.DISPATCH_PROPERTYGET, dp, null /* EXCEPINFO */, out result); if (hr.Failed) { hr = InvokeOnScriptObject(dispid, NativeMethods.DISPATCH_METHOD, dp, null /* EXCEPINFO */, out result); } } if (hr.Succeeded && result != null) { return result.ToString(); } return base.ToString(); }
internal unsafe bool TryFindMemberAndInvokeNonWrapped(string memberName, int flags, bool cacheDispId, object[] args, out object result) { result = null; // In true DLR style we'd return false here, deferring further attempts for resolution to the // call site. For better debuggability though, and as we're talking to a specialized object // model, we rather throw instead. This Try-method allows internal lookups without directly // throwing on non-fatal "member not found" situations, as we might want to have more complex // fallback logic (as in ToString). int dispid; if (!TryGetDispIdForMember(memberName, cacheDispId, out dispid)) { return(false); } NativeMethods.DISPPARAMS dp = new NativeMethods.DISPPARAMS(); // Required additional DISPPARAMS arguments for PROPERTYPUT cases. See IDispatch::Invoke // documentation for more info. int propertyPutDispId = NativeMethods.DISPID_PROPERTYPUT; if (flags == NativeMethods.DISPATCH_PROPERTYPUT || flags == NativeMethods.DISPATCH_PROPERTYPUTREF) { dp.cNamedArgs = 1; // Stack allocated variables are fixed (unmoveable), so no need to use a fixed // statement. The local variable should not get repurposed by the compiler in an // unsafe block as we're handing out a pointer to it. For comparison, see the DLR // code, CorRuntimeHelpers.cs, where the ConvertInt32ByrefToPtr function relies // on the same behavior as they take an IntPtr to a stack-allocated variable that // gets used by a DISPPARAMS in a similar way. They have a separate method likely // because expression trees can't deal with unsafe code, and they need to fix as // they use a by-ref argument which is considered movable (see C# spec, 18.3): // // public static unsafe IntPtr ConvertInt32ByrefToPtr(ref Int32 value) { // fixed (Int32 *x = &value) { // AssertByrefPointsToStack(new IntPtr(x)); // return new IntPtr(x); // } // } dp.rgdispidNamedArgs = new IntPtr(&propertyPutDispId); } try { if (args != null) { // Callers of this method might want to implement fallbacks that require the original // arguments in the original order, maybe to feed it in to this method again. If we // wouldn't clone the arguments array into a local copy, we'd be reversing again. args = (object[])args.Clone(); // Reverse the argument order so that parameters read naturally after IDispatch. // This code was initially ported from WinForms, see WinForms bug 187662. Array.Reverse(args); // Unwrap arguments that were already promoted to DynamicScriptObject. This can happen // if the output of a script accessing method is fed in to the input of another one. for (int i = 0; i < args.Length; i++) { var wrappedArg = args[i] as DynamicScriptObject; if (wrappedArg != null) { args[i] = wrappedArg._scriptObject; } if (args[i] != null) { // Arrays of COM visible objects (in our definition of the word, see further) are // not considered COM visible by themselves, so we take care of this case as well. // Jagged arrays are not supported somehow, causing a SafeArrayTypeMismatchException // in the call to GetNativeVariantForObject on ArrayToVARIANTVector called below. // Multi-dimensional arrays turn out to work fine, so we don't opt out from those. Type argType = args[i].GetType(); if (argType.IsArray) { argType = argType.GetElementType(); } // Caveat: IsTypeVisibleFromCom evaluates false for COM object wrappers provided // by the CLR. Therefore we also check for the IsCOMObject property. It also seems // COM interop special-cases DateTime as it's not revealed to be visible by any // of the first two checks below. if ( #if NETFX !Marshal.IsTypeVisibleFromCom(argType) #else !MarshalLocal.IsTypeVisibleFromCom(argType) #endif && !argType.IsCOMObject && argType != typeof(DateTime)) { throw new ArgumentException(SR.Get(SRID.NeedToBeComVisible)); } } } dp.rgvarg = UnsafeNativeMethods.ArrayToVARIANTHelper.ArrayToVARIANTVector(args); dp.cArgs = (uint)args.Length; } NativeMethods.EXCEPINFO exInfo = new NativeMethods.EXCEPINFO(); HRESULT hr = InvokeOnScriptObject(dispid, flags, dp, exInfo, out result); if (hr.Failed) { if (hr == HRESULT.DISP_E_MEMBERNOTFOUND) { return(false); } // See KB article 247784, INFO: '80020101' Returned From Some ActiveX Scripting Methods. // Internet Explorer returns this error when it has already reported a script error to the user // through a dialog or by putting a message in the status bar (Page contains script error). We // want consistency between browsers, so route this through the DISP_E_EXCEPTION case. if (hr == HRESULT.SCRIPT_E_REPORTED) { exInfo.scode = hr.Code; hr = HRESULT.DISP_E_EXCEPTION; } // We prefix exception messagages with "[memberName]" so that the target of the invocation can // be found easily. This is useful beyond just seeing the call site in the debugger as dynamic // calls lead to complicated call stacks with the DLR sliced in between the source and target. // Also, for good or for bad, dynamic has the potential to encourage endless "dotting into", so // it's preferrable to have our runtime resolution failure eloquating the target of the dynamic // call. Unfortunately stock CLR exception types often don't offer a convenient spot to put // this info in, so we resort to the Message property. Anyway, those exceptions are primarily // meant to provide debugging convenience and should not be reported to the end-user in a well- // tested application. Essentially all of this is to be conceived as "deferred compilation". string member = "[" + (memberName ?? "(default)") + "]"; Exception comException = hr.GetException(); if (hr == HRESULT.DISP_E_EXCEPTION) { // We wrap script execution failures in TargetInvocationException to keep the API surface // free of COMExceptions that reflect a mere implementation detail. int errorCode = exInfo.scode != 0 ? exInfo.scode : exInfo.wCode; hr = HRESULT.Make(true /* severity */, Facility.Dispatch, errorCode); string message = member + " " + (exInfo.bstrDescription ?? string.Empty); throw new TargetInvocationException(message, comException) { HelpLink = exInfo.bstrHelpFile, Source = exInfo.bstrSource }; } else if (hr == HRESULT.DISP_E_BADPARAMCOUNT || hr == HRESULT.DISP_E_PARAMNOTOPTIONAL) { throw new TargetParameterCountException(member, comException); } else if (hr == HRESULT.DISP_E_OVERFLOW || hr == HRESULT.DISP_E_TYPEMISMATCH) { throw new ArgumentException(member, new InvalidCastException(comException.Message, hr.Code)); } else { // Something really bad has happened; keeping the exception as-is. throw comException; } } } finally { if (dp.rgvarg != IntPtr.Zero) { UnsafeNativeMethods.ArrayToVARIANTHelper.FreeVARIANTVector(dp.rgvarg, args.Length); } } return(true); }
public object InvokeScript(string scriptName, params object[] args) { VerifyAccess(); if (string.IsNullOrEmpty(scriptName)) { throw new ArgumentNullException("scriptName"); } UnsafeNativeMethods.IDispatchEx scriptObjectEx = null; UnsafeNativeMethods.IHTMLDocument2 htmlDocument = NativeHTMLDocument; if (htmlDocument != null) { scriptObjectEx = htmlDocument.GetScript() as UnsafeNativeMethods.IDispatchEx; } // Protect against the cross domain scripting attacks. // We rely on the site locking feature in gerneral. But in IE 6 server side redirect is not blocked. // (In IE 7 it is blocked by turning on the DOCHOSTUIFLAG.ENABLE_REDIRECT_NOTIFICATION flag so that // the additional BeforeNavigate2 event is fired for server side redirect.) // If it is our internal navigation to blank for navigating to null or load stream, // or before any navigation has happened, Source will be null. Uri currentSource = Source; if (currentSource != null) { SecurityHelper.DemandWebPermission(currentSource); } // Unfortunately, IWebBrowser2::CurrentURL does not always give us the full truth. It is not updated // when the browser navigates to an error page. Then, supposedly, it points to the "intended // destination". (But on canceled navigation the previous URL may stay.) We don't want to allow // running script from the built-in error pages as they are privileged. // See http://support.microsoft.com/kb/272095 and Dev10.724665. Contrary to what the KB article // suggests, document.location.href is not good either. But document.URL seems to be. if (htmlDocument != null) { string innerURL = htmlDocument.GetUrl(); // Again, in the special case we've deliberately navigated to about:blank we don't want to // demand. But NavigatingToAboutBlank may not be true anymore, so it's not used as a condition. // In that case the two URL properties should match. If not, we'll demand to be safe. if (string.CompareOrdinal(innerURL, AxIWebBrowser2.LocationURL) != 0) { SecurityHelper.DemandWebPermission(new Uri(innerURL, UriKind.Absolute)); } } object retVal = null; if (scriptObjectEx != null) { NativeMethods.DISPPARAMS dp = new NativeMethods.DISPPARAMS(); dp.rgvarg = IntPtr.Zero; try { // If we use reflection to call script code, we need to Assert for the UnmanagedCode permission. // But it will be a security issue when the WPF app makes a framework object available to the // hosted script via ObjectForScripting or as paramter of InvokeScript, and calls the framework // API that demands the UnmanagedCode permission. We do not want the demand to succeed. However, // The stack walk will igore the native frames and keeps going until it reaches the Assert. // That is why we switch to invoking the script via IDispatch with SUCS on the methods. Guid guid = Guid.Empty; string[] names = new string[] { scriptName }; int[] dispids = new int[] { NativeMethods.DISPID_UNKNOWN }; HRESULT hr = scriptObjectEx.GetIDsOfNames(ref guid, names, 1, Thread.CurrentThread.CurrentCulture.LCID, dispids); hr.ThrowIfFailed(); if (args != null) { // Reverse the arg order so that parms read naturally after IDispatch. ([....] bug 187662) Array.Reverse(args); } dp.rgvarg = (args == null) ? IntPtr.Zero : UnsafeNativeMethods.ArrayToVARIANTHelper.ArrayToVARIANTVector(args); dp.cArgs = (uint)((args == null) ? 0 : args.Length); dp.rgdispidNamedArgs = IntPtr.Zero; dp.cNamedArgs = 0; // It's important to use IDispatchEx::InvokeEx here rather than the non-Ex versions for security reasons. // This version allows us to pass the IServiceProvider for security context. // See Dev10 bugs 710329 and 710325 for context. hr = scriptObjectEx.InvokeEx( dispids[0], Thread.CurrentThread.CurrentCulture.LCID, NativeMethods.DISPATCH_METHOD, dp, out retVal, new NativeMethods.EXCEPINFO(), (UnsafeNativeMethods.IServiceProvider)htmlDocument); hr.ThrowIfFailed(); } finally { if (dp.rgvarg != IntPtr.Zero) { UnsafeNativeMethods.ArrayToVARIANTHelper.FreeVARIANTVector(dp.rgvarg, args.Length); } } } else { throw new InvalidOperationException(SR.Get(SRID.CannotInvokeScript)); } return retVal; }
/// <summary> /// Executes an Active Scripting function defined in the HTML document currently loaded in the WebBrowser control. /// </summary> /// <param name="scriptName">The name of the script method to invoke.</param> /// <param name="args"></param> /// <returns>The object returned by the Active Scripting call.</returns> public object InvokeScript(string scriptName, params object[] args) { VerifyAccess(); if (string.IsNullOrEmpty(scriptName)) { throw new ArgumentNullException("scriptName"); } UnsafeNativeMethods.IDispatchEx scriptObjectEx = null; UnsafeNativeMethods.IHTMLDocument2 htmlDocument = NativeHTMLDocument; if (htmlDocument != null) { scriptObjectEx = htmlDocument.GetScript() as UnsafeNativeMethods.IDispatchEx; } // Protect against the cross domain scripting attacks. // We rely on the site locking feature in gerneral. But in IE 6 server side redirect is not blocked. // (In IE 7 it is blocked by turning on the DOCHOSTUIFLAG.ENABLE_REDIRECT_NOTIFICATION flag so that // the additional BeforeNavigate2 event is fired for server side redirect.) object retVal = null; if (scriptObjectEx != null) { NativeMethods.DISPPARAMS dp = new NativeMethods.DISPPARAMS(); dp.rgvarg = IntPtr.Zero; try { // If we use reflection to call script code, we need to Assert for the UnmanagedCode permission. // But it will be a security issue when the WPF app makes a framework object available to the // hosted script via ObjectForScripting or as paramter of InvokeScript, and calls the framework // API that demands the UnmanagedCode permission. We do not want the demand to succeed. However, // The stack walk will igore the native frames and keeps going until it reaches the Assert. // That is why we switch to invoking the script via IDispatch with SUCS on the methods. Guid guid = Guid.Empty; string[] names = new string[] { scriptName }; int[] dispids = new int[] { NativeMethods.DISPID_UNKNOWN }; HRESULT hr = scriptObjectEx.GetIDsOfNames(ref guid, names, 1, Thread.CurrentThread.CurrentCulture.LCID, dispids); hr.ThrowIfFailed(); if (args != null) { // Reverse the arg order so that parms read naturally after IDispatch. (WinForms bug 187662) Array.Reverse(args); } dp.rgvarg = (args == null) ? IntPtr.Zero : UnsafeNativeMethods.ArrayToVARIANTHelper.ArrayToVARIANTVector(args); dp.cArgs = (uint)((args == null) ? 0 : args.Length); dp.rgdispidNamedArgs = IntPtr.Zero; dp.cNamedArgs = 0; // It's important to use IDispatchEx::InvokeEx here rather than the non-Ex versions for security reasons. // This version allows us to pass the IServiceProvider for security context. // // Calling window.open from within WPF WebBrowser control results in Access Denied script error // Providing a service provider to InvokeEx only makes sense when nesting occurs (for e.g., when WPF calls // a script which calls back into WPF which in turn calls the script again) and there is a need to maintain // the service provider chain. When the execution is from a root occurance, then there is no valid service // provider that will have all of the information from the stack. // // Until recently, IE was ignoring bad service providers -so our passing (IServiceProvider)htmlDocument to InvokeEx // worked. IE has recently taken a security fix to ensure that it doesn't fall back to the last IOleCommandTarget // in the chain it found - so now we simply pass null to indicate that this is the root call site. hr = scriptObjectEx.InvokeEx( dispids[0], Thread.CurrentThread.CurrentCulture.LCID, NativeMethods.DISPATCH_METHOD, dp, out retVal, new NativeMethods.EXCEPINFO(), null); hr.ThrowIfFailed(); } finally { if (dp.rgvarg != IntPtr.Zero) { UnsafeNativeMethods.ArrayToVARIANTHelper.FreeVARIANTVector(dp.rgvarg, args.Length); } } } else { throw new InvalidOperationException(SR.Get(SRID.CannotInvokeScript)); } return(retVal); }
public object InvokeScript(string scriptName, params object[] args) { base.VerifyAccess(); if (string.IsNullOrEmpty(scriptName)) { throw new ArgumentNullException("scriptName"); } UnsafeNativeMethods.IDispatchEx dispatchEx = null; UnsafeNativeMethods.IHTMLDocument2 nativeHTMLDocument = this.NativeHTMLDocument; if (nativeHTMLDocument != null) { dispatchEx = (nativeHTMLDocument.GetScript() as UnsafeNativeMethods.IDispatchEx); } Uri source = this.Source; if (source != null) { SecurityHelper.DemandWebPermission(source); } if (nativeHTMLDocument != null) { string url = nativeHTMLDocument.GetUrl(); if (string.CompareOrdinal(url, this.AxIWebBrowser2.LocationURL) != 0) { SecurityHelper.DemandWebPermission(new Uri(url, UriKind.Absolute)); } } object result = null; if (dispatchEx != null) { NativeMethods.DISPPARAMS dispparams = new NativeMethods.DISPPARAMS(); dispparams.rgvarg = IntPtr.Zero; try { Guid empty = Guid.Empty; string[] rgszNames = new string[] { scriptName }; int[] array = new int[] { -1 }; dispatchEx.GetIDsOfNames(ref empty, rgszNames, 1, Thread.CurrentThread.CurrentCulture.LCID, array).ThrowIfFailed(); if (args != null) { Array.Reverse(args); } dispparams.rgvarg = ((args == null) ? IntPtr.Zero : UnsafeNativeMethods.ArrayToVARIANTHelper.ArrayToVARIANTVector(args)); dispparams.cArgs = (uint)((args == null) ? 0 : args.Length); dispparams.rgdispidNamedArgs = IntPtr.Zero; dispparams.cNamedArgs = 0U; dispatchEx.InvokeEx(array[0], Thread.CurrentThread.CurrentCulture.LCID, 1, dispparams, out result, new NativeMethods.EXCEPINFO(), null).ThrowIfFailed(); return(result); } finally { if (dispparams.rgvarg != IntPtr.Zero) { UnsafeNativeMethods.ArrayToVARIANTHelper.FreeVARIANTVector(dispparams.rgvarg, args.Length); } } } throw new InvalidOperationException(SR.Get("CannotInvokeScript")); }
internal unsafe bool TryFindMemberAndInvokeNonWrapped(string memberName, int flags, bool cacheDispId, object[] args, out object result) { result = null; int dispid; if (!this.TryGetDispIdForMember(memberName, cacheDispId, out dispid)) { return(false); } NativeMethods.DISPPARAMS dispparams = new NativeMethods.DISPPARAMS(); int num = -3; if (flags == 4 || flags == 8) { dispparams.cNamedArgs = 1U; dispparams.rgdispidNamedArgs = new IntPtr((void *)(&num)); } try { if (args != null) { args = (object[])args.Clone(); Array.Reverse(args); for (int i = 0; i < args.Length; i++) { DynamicScriptObject dynamicScriptObject = args[i] as DynamicScriptObject; if (dynamicScriptObject != null) { args[i] = dynamicScriptObject._scriptObject; } if (args[i] != null) { Type type = args[i].GetType(); if (type.IsArray) { type = type.GetElementType(); } if (!Marshal.IsTypeVisibleFromCom(type) && !type.IsCOMObject && type != typeof(DateTime)) { throw new ArgumentException(SR.Get("NeedToBeComVisible")); } } } dispparams.rgvarg = UnsafeNativeMethods.ArrayToVARIANTHelper.ArrayToVARIANTVector(args); dispparams.cArgs = (uint)args.Length; } NativeMethods.EXCEPINFO excepinfo = new NativeMethods.EXCEPINFO(); HRESULT hrLeft = this.InvokeOnScriptObject(dispid, flags, dispparams, excepinfo, out result); if (hrLeft.Failed) { if (hrLeft == HRESULT.DISP_E_MEMBERNOTFOUND) { return(false); } if (hrLeft == HRESULT.SCRIPT_E_REPORTED) { excepinfo.scode = hrLeft.Code; hrLeft = HRESULT.DISP_E_EXCEPTION; } string text = "[" + (memberName ?? "(default)") + "]"; Exception exception = hrLeft.GetException(); if (hrLeft == HRESULT.DISP_E_EXCEPTION) { int code = (excepinfo.scode != 0) ? excepinfo.scode : ((int)excepinfo.wCode); hrLeft = HRESULT.Make(true, Facility.Dispatch, code); string message = text + " " + (excepinfo.bstrDescription ?? string.Empty); throw new TargetInvocationException(message, exception) { HelpLink = excepinfo.bstrHelpFile, Source = excepinfo.bstrSource }; } if (hrLeft == HRESULT.DISP_E_BADPARAMCOUNT || hrLeft == HRESULT.DISP_E_PARAMNOTOPTIONAL) { throw new TargetParameterCountException(text, exception); } if (hrLeft == HRESULT.DISP_E_OVERFLOW || hrLeft == HRESULT.DISP_E_TYPEMISMATCH) { throw new ArgumentException(text, new InvalidCastException(exception.Message, hrLeft.Code)); } throw exception; } } finally { if (dispparams.rgvarg != IntPtr.Zero) { UnsafeNativeMethods.ArrayToVARIANTHelper.FreeVARIANTVector(dispparams.rgvarg, args.Length); } } return(true); }