Bloquear diálogos en el control .NET WebBrowser

Tengo un control .NET 2.0 WebBrowser usado para navegar algunas páginas sin interacción del usuario (no preguntes … larga historia). Debido a la naturaleza sin usuario de esta aplicación, he establecido la propiedad ScriptErrorsSuppressed del control WebBrowser en verdadero, que la documentación incluida con los estados de VS 2005 “ocultará […] todos sus cuadros de diálogo que se originan desde el control ActiveX subyacente”. no solo errores de guiones “. El artículo de MSDN no menciona esto, sin embargo. He logrado cancelar el evento NewWindow, que evita las ventanas emergentes, así que eso se solucionó.

¿Alguien tiene experiencia en el uso de uno de estos y bloquea con éxito todos los diálogos, errores de secuencia de comandos, etc.?

EDITAR

Esta no es una instancia independiente de IE, sino una instancia de un control WebBrowser que vive en una aplicación de Windows Form. Alguien tiene alguna experiencia con este control, o el subyacente, AxSHDocVW ?

EDITAR de nuevo

Lo siento, me olvidé de mencionar esto … Estoy tratando de bloquear una alerta de JavaScript () , con solo un botón Aceptar. Tal vez puedo convertir un objeto IHTMLDocument2 y acceder a los scripts de esa manera, he usado MSHTML un poco, ¿alguien sabe?

Esto definitivamente es un hacky, pero si trabajas con el control WebBrowser, te encontrarás haciendo muchas cosas.

Esta es la forma más fácil que conozco para hacer esto. Necesita inyectar JavaScript para anular la función de alerta … algo así como inyectar esta función de JavaScript:

window.alert = function () { } 

Hay muchas formas de hacerlo , pero es muy posible hacerlo. Una posibilidad es enganchar una implementación de la interfaz DWebBrowserEvents2 . Una vez hecho esto, puede conectarse a NavigateComplete, DownloadComplete o DocumentComplete (o, como lo hacemos, a alguna variación del mismo) y luego invocar un método InjectJavaScript que haya implementado que realice esta anulación de la ventana. método.

Como dije, hacky, pero funciona 🙂

Puedo entrar en más detalles si es necesario.

Y para una forma fácil de inyectar esa línea mágica de javascript, lea cómo inyectar javascript en el control del webbrowser .

O simplemente usa este código completo:

 private void InjectAlertBlocker() { HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0]; HtmlElement scriptEl = webBrowser1.Document.CreateElement("script"); string alertBlocker = "window.alert = function () { }"; scriptEl.SetAttribute("text", alertBlocker); head.AppendChild(scriptEl); } 

Bloqueador de alerta a prueba de balas:

 Browser.Navigated += new WebBrowserNavigatedEventHandler( (object sender, WebBrowserNavigatedEventArgs args) => { Action blockAlerts = (HtmlDocument d) => { HtmlElement h = d.GetElementsByTagName("head")[0]; HtmlElement s = d.CreateElement("script"); IHTMLScriptElement e = (IHTMLScriptElement)s.DomElement; e.text = "window.alert=function(){};"; h.AppendChild(s); }; WebBrowser b = sender as WebBrowser; blockAlerts(b.Document); for (int i = 0; i < b.Document.Window.Frames.Count; i++) try { blockAlerts(b.Document.Window.Frames[i].Document); } catch (Exception) { }; } ); 

En este ejemplo se supone que tiene una referencia de Microsoft.mshtml agregada, " usando mshtml; " en sus espacios de nombres y el navegador es su instancia de WebBrowser .

¿Por qué es a prueba de balas? Primero, maneja los guiones dentro de los marcos . Entonces, no se cuelga cuando existe un "marco asesino" especial en el documento. Un "marco asesino" es un marco que genera una excepción al intentar usarlo como objeto HtmlWindow. Cualquier "foreach" utilizado en Document.Window.Frames provocaría una excepción, por lo que se debería utilizar un ciclo "for" más seguro con el bloque try / catch.

Tal vez no sea la pieza de código más legible, pero funciona con páginas mal formadas de la vida real.

Puede que tenga que personalizar algunas cosas, echar un vistazo a IDocHostUIHandler , y luego verifique algunas de las otras interfaces relacionadas. Puede tener una buena cantidad de control, incluso hasta el punto de personalizar la visualización del diálogo / ui (no recuerdo qué interfaz hace esto). Estoy bastante seguro de que puedes hacer lo que quieras, pero se requiere pasar el tiempo en el interior de MSHTML y poder implementar las diversas interfaces COM .

Algunas otras ideas: http://msdn.microsoft.com/en-us/library/aa770041.aspx

 IHostDialogHelper IDocHostShowUI 

Estas pueden ser las cosas que estás buscando implementar.

 webBrowser1.ScriptErrorsSuppressed = true; 

Solo agrégalo a tu función de nivel de entrada. Después de mucha investigación es cuando me encontré con este método, y tocar madera hasta ahora funcionó. ¡¡Aclamaciones!!

Acabo de publicar un artículo sobre Code Project que puede ser útil.

Por favor vea – http://www.codeproject.com/KB/shell/WebBrowserControlDialogs.aspx

Espero que esto ayude.

window.showModelessDialog y window.showModalDialog se pueden bloquear implementando la interfaz INewWindowManager; además, el siguiente código muestra cómo bloquear los diálogos de alerta implementando IDocHostShowUI

 public class MyBrowser : WebBrowser { [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")] public MyBrowser() { } protected override WebBrowserSiteBase CreateWebBrowserSiteBase() { var manager = new NewWindowManagerWebBrowserSite(this); return manager; } protected class NewWindowManagerWebBrowserSite : WebBrowserSite, IServiceProvider, IDocHostShowUI { private readonly NewWindowManager _manager; public NewWindowManagerWebBrowserSite(WebBrowser host) : base(host) { _manager = new NewWindowManager(); } public int ShowMessage(IntPtr hwnd, string lpstrText, string lpstrCaption, int dwType, string lpstrHelpFile, int dwHelpContext, out int lpResult) { lpResult = 0; return Constants.S_OK; // S_OK Host displayed its UI. MSHTML does not display its message box. } // Only files of types .chm and .htm are supported as help files. public int ShowHelp(IntPtr hwnd, string pszHelpFile, uint uCommand, uint dwData, POINT ptMouse, object pDispatchObjectHit) { return Constants.S_OK; // S_OK Host displayed its UI. MSHTML does not display its message box. } #region Implementation of IServiceProvider public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject) { if ((guidService == Constants.IID_INewWindowManager && riid == Constants.IID_INewWindowManager)) { ppvObject = Marshal.GetComInterfaceForObject(_manager, typeof(INewWindowManager)); if (ppvObject != IntPtr.Zero) { return Constants.S_OK; } } ppvObject = IntPtr.Zero; return Constants.E_NOINTERFACE; } #endregion } } [ComVisible(true)] [Guid("01AFBFE2-CA97-4F72-A0BF-E157038E4118")] public class NewWindowManager : INewWindowManager { public int EvaluateNewWindow(string pszUrl, string pszName, string pszUrlContext, string pszFeatures, bool fReplace, uint dwFlags, uint dwUserActionTime) { // use E_FAIL to be the same as CoInternetSetFeatureEnabled with FEATURE_WEBOC_POPUPMANAGEMENT //int hr = MyBrowser.Constants.E_FAIL; int hr = MyBrowser.Constants.S_FALSE; //Block //int hr = MyBrowser.Constants.S_OK; //Allow all return hr; } } 

El InjectAlertBlocker es código absolutamente correcto

 private void InjectAlertBlocker() { HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0]; HtmlElement scriptEl = webBrowser1.Document.CreateElement("script"); IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement; string alertBlocker = "window.alert = function () { }"; element.text = alertBlocker; head.AppendChild(scriptEl); } 

Las referencias que se deben agregar son

  1. Agregue una referencia a MSHTML , que probablemente se denominará ” Biblioteca de objetos HTML de Microsoft ” bajo referencias COM .

  2. Agregar using mshtml; a tus espacios de nombres

  3. Obtenga una referencia al elemento IHTMLElement su elemento de IHTMLElement :

Luego puede usar el evento Navigated de webbrowser como:

 private void InjectAlertBlocker() { HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0]; HtmlElement scriptEl = webBrowser1.Document.CreateElement("script"); IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement; string alertBlocker = "window.alert = function () { }"; element.text = alertBlocker; head.AppendChild(scriptEl); } private void webDest_Navigated(object sender, WebBrowserNavigatedEventArgs e) { InjectAlertBlocker(); } 

¿Estás tratando de implementar un robot web? Tengo poca experiencia en el uso del control de IE alojado, pero completé algunos proyectos de Win32 que intentaron usar el control de IE. La desactivación de las ventanas emergentes debe hacerse a través de los controladores de eventos del control como ya lo hiciste, pero me di cuenta de que también debes cambiar ‘Deshabilitar la depuración de scripts xxxx’ en las opciones de IE (o puedes modificar el registro en tus códigos) como cjheath ya lo señaló. Sin embargo, también encontré que era necesario realizar más pasos para verificar la URL de navegación en busca de contenido descargable para evitar esos diálogos de abrir / guardar. Pero no sé cómo lidiar con los archivos de transmisión, ya que no puedo omitirlos mirando solo las URL y al final recurrí a la biblioteca de Indy, lo que me ahorró todos los problemas al tratar con IE. Finalmente, recuerdo que Microsoft mencionó algo en línea que IE no está diseñado para usarse como control OLE. De acuerdo con mi propia experiencia, ¡cada vez que el control navega hacia una nueva página, introdujo memory leaks para los progtwigs!

Tuve mayores problemas con esto: cargar una página web que está destinada a imprimir y muestra un cuadro de diálogo de Impresión molesto. InjectBlocker era la única forma que funcionaba, pero bastante poco fiable. Bajo ciertas condiciones (considero que es debido a que el control WebBrowser usa el motor de IE y esto depende de la versión de IE instalada) el diálogo de impresión aún aparece. Este es un problema importante, la solución funciona en Win7 con IE9 instalado, pero WinXP con IE8 muestra el diálogo, pase lo que pase.

Creo que la solución está en modificar el código fuente y eliminar el javascript de impresión, antes de que el control represente la página. Sin embargo, lo intenté con la propiedad DocumentText del control webbrowser y no funciona. La propiedad no es de solo lectura, pero no tiene ningún efecto cuando modifico la fuente.

La solución que encontré para mi problema es la secuencia de comandos Exec:

 string alertBlocker = "window.print = function emptyMethod() { }; window.alert = function emptyMethod() { }; window.open = function emptyMethod() { };"; this.Document.InvokeScript("execScript", new Object[] { alertBlocker, "JavaScript" }); 

Me las arreglé para inyectar el código anterior creando una clase extendida de WebBroswer y reemplazando el método OnNavigated .

Esto pareció funcionar bastante bien:

 class WebBrowserEx : WebBrowser { public WebBrowserEx () { } protected override void OnNavigated( WebBrowserNavigatedEventArgs e ) { HtmlElement he = this.Document.GetElementsByTagName( "head" )[0]; HtmlElement se = this.Document.CreateElement( "script" ); mshtml.IHTMLScriptElement element = (mshtml.IHTMLScriptElement)se.DomElement; string alertBlocker = "window.alert = function () { }"; element.text = alertBlocker; he.AppendChild( se ); base.OnNavigated( e ); } } 

Simplemente desde las propiedades de control del navegador: scriptErrorSupressed = true

La forma más fácil de hacerlo es: en el: Control de Webbrowser tiene el procedimiento (estándar) BeforeScriptExecute

(El parámetro para BeforeScriptExecute es pdispwindow )

Agrega esto :

 pdispwindow.execscript("window.alert = function () { }") 

De esta forma, antes de que se ejecute la secuencia de comandos en la ventana de la página, la alerta será suprimida por el código inyectado.