¿Cómo inyectar Javascript en el control WebBrowser?

81

He probado esto:

string newScript = textBox1.Text;
HtmlElement head = browserCtrl.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = browserCtrl.Document.CreateElement("script");
lblStatus.Text = scriptEl.GetType().ToString();
scriptEl.SetAttribute("type", "text/javascript");
head.AppendChild(scriptEl);
scriptEl.InnerHtml = "function sayHello() { alert('hello') }";

scriptEl.InnerHtml y scriptEl.InnerText dan errores:

System.NotSupportedException: Property is not supported on this type of HtmlElement.
   at System.Windows.Forms.HtmlElement.set_InnerHtml(String value)
   at SForceApp.Form1.button1_Click(Object sender, EventArgs e) in d:\jsight\installs\SForceApp\SForceApp\Form1.cs:line 31
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

¿Existe una manera fácil de inyectar un script en el dom?

jsight
fuente

Respuestas:

101

Por alguna razón, la solución de Richard no funcionó en mi extremo (insertAdherentText falló con una excepción). Sin embargo, esto parece funcionar:

HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
element.text = "function sayHello() { alert('hello') }";
head.AppendChild(scriptEl);
webBrowser1.Document.InvokeScript("sayHello");

Esta respuesta explica cómo IHTMLScriptElementincorporar la interfaz a su proyecto.

Atanas Korchev
fuente
1
Genial, creo que el uso de IHTMLScriptElement hace que la intención del código sea más obvia en cualquier caso. Me pregunto por qué tiene una excepción, pero c'est la vie con la interoperabilidad COM a veces.
ZeroBugBounce
¿Qué hay de agregar una referencia a un archivo js local? Consulte stackoverflow.com/questions/4029602/…
IAmAN00B
Por favor, ayúdame con esto. Como hiciste esto. Quiero inyectar JS en mi página en tiempo real a través de mi aplicación C ++. ¿Cómo debo hacerlo?
Johnydep
¿Es esto aplicable para inyectar archivos jquery también?
gumuruh
51
HtmlDocument doc = browser.Document;
HtmlElement head = doc.GetElementsByTagName("head")[0];
HtmlElement s = doc.CreateElement("script");
s.SetAttribute("text","function sayHello() { alert('hello'); }");
head.AppendChild(s);
browser.Document.InvokeScript("sayHello");

(probado en la aplicación .NET 4 / Windows Forms)

Editar: problema de caso fijo en el conjunto de funciones.

Typpo
fuente
12
Aprecié esta solución porque no se basa en el ensamblado IHTMLSCriptElement (¡aunque útil!).
Micah Smith
6
@jsight Esta debería ser la respuesta aceptada. Es lo mismo que la respuesta actual, pero más simple y sin IHTMLSCriptElementdependencia.
BlueRaja - Danny Pflughoeft
32

Esta es la forma más fácil que encontré después de trabajar en esto:

string javascript = "alert('Hello');";
// or any combination of your JavaScript commands
// (including function calls, variables... etc)

// WebBrowser webBrowser1 is what you are using for your web browser
webBrowser1.Document.InvokeScript("eval", new object[] { javascript });

Lo que hace la función JavaScript global eval(str)es analizar y ejecutar lo que esté escrito en str. Consulte la referencia de w3schools aquí .

Ilya Rosikhin
fuente
22

Además, en .NET 4 esto es aún más fácil si usa la palabra clave dinámica:

dynamic document = this.browser.Document;
dynamic head = document.GetElementsByTagName("head")[0];
dynamic scriptEl = document.CreateElement("script");
scriptEl.text = ...;
head.AppendChild(scriptEl);
Justin M. Chase
fuente
2
¿Por qué alguien necesitaría dinámica para eso? Si está tratando de guardar algo de escritura, tenemos inferencia de tipos desde C # 3.0, por lo que var sería aceptable. No es necesario comenzar a invocar el DLR.
alimbada
23
Este es básicamente el punto de la palabra clave dinámica: interoperabilidad COM. En realidad, no tiene Inferencia de tipos aquí, tiene documentación. Porque un IHTMLElement2 no se puede asignar desde IHtmlElement, por ejemplo, y en tiempo de ejecución solo tiene un objeto proxy COM. Solo tienes que saber qué interfaces convertir en qué. La palabra clave dinámica le ayuda a reducir gran parte de ese dinero. ¿Sabes que existe el método por qué convertirlo en alguna interfaz? No 'invoca exactamente el DLR', solo genera código que sabe cómo invocar el método en objetos COM (en este caso).
Justin M. Chase
1
@ justin.m.chase me salvaste la vida.
Khizar Iqbal
17

Si todo lo que realmente desea es ejecutar javascript, esto sería más fácil (VB .Net):

MyWebBrowser.Navigate("javascript:function foo(){alert('hello');}foo();")

Supongo que esto no lo "inyectaría" pero ejecutará su función, si eso es lo que busca. (En caso de que haya complicado demasiado el problema). Y si puede averiguar cómo inyectar en javascript, colóquelo en el cuerpo de la función "foo" y deje que javascript haga la inyección por usted.

Eyal
fuente
10

El contenedor administrado para el documento HTML no implementa completamente la funcionalidad que necesita, por lo que debe sumergirse en la API de MSHTML para lograr lo que desea:

1) Agregue una referencia a MSHTML, que probablemente se llamará "Biblioteca de objetos HTML de Microsoft" en las referencias COM .

2) Agregue 'usando mshtml;' a sus espacios de nombres.

3) Obtenga una referencia al IHTMLElement de su elemento de script:

IHTMLElement iScriptEl = (IHTMLElement)scriptEl.DomElement;

4) Llame al método insertAdherentText, con el primer valor de parámetro "afterBegin". Todos los valores posibles se enumeran aquí :

iScriptEl.insertAdjacentText("afterBegin", "function sayHello() { alert('hello') }");

5) Ahora podrá ver el código en la propiedad scriptEl.InnerText.

Hth, Richard

ZeroBugBounce
fuente
1
Bien ... esto junto con los consejos proporcionados por korchev funciona perfectamente. Ojalá pudiera establecer dos soluciones aceptadas en este caso. :)
jsight
8

Como seguimiento de la respuesta aceptada , esta es una definición mínima de la IHTMLScriptElementinterfaz que no requiere incluir bibliotecas de tipos adicionales:

[ComImport, ComVisible(true), Guid(@"3050f28b-98b5-11cf-bb82-00aa00bdce0b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
[TypeLibType(TypeLibTypeFlags.FDispatchable)]
public interface IHTMLScriptElement
{
    [DispId(1006)]
    string text { set; [return: MarshalAs(UnmanagedType.BStr)] get; }
}

Entonces, un código completo dentro de una clase derivada de control WebBrowser se vería así:

protected override void OnDocumentCompleted(
    WebBrowserDocumentCompletedEventArgs e)
{
    base.OnDocumentCompleted(e);

    // Disable text selection.
    var doc = Document;
    if (doc != null)
    {
        var heads = doc.GetElementsByTagName(@"head");
        if (heads.Count > 0)
        {
            var scriptEl = doc.CreateElement(@"script");
            if (scriptEl != null)
            {
                var element = (IHTMLScriptElement)scriptEl.DomElement;
                element.text =
                    @"function disableSelection()
                    { 
                        document.body.onselectstart=function(){ return false; }; 
                        document.body.ondragstart=function() { return false; };
                    }";
                heads[0].AppendChild(scriptEl);
                doc.InvokeScript(@"disableSelection");
            }
        }
    }
}
Uwe Keim
fuente
8

Creo que el método más simple para inyectar Javascript en un documento HTML de control de WebBrowser desde c # es invocar el método "execScript" con el código que se inyectará como argumento.

En este ejemplo, el código javascript se inyecta y ejecuta a nivel global:

var jsCode="alert('hello world from injected code');";
WebBrowser.Document.InvokeScript("execScript", new Object[] { jsCode, "JavaScript" });

Si desea retrasar la ejecución, inyecte funciones y llámelas después:

var jsCode="function greet(msg){alert(msg);};";
WebBrowser.Document.InvokeScript("execScript", new Object[] { jsCode, "JavaScript" });
...............
WebBrowser.Document.InvokeScript("greet",new object[] {"hello world"});

Esto es válido para los controles de Windows Forms y WPF WebBrowser.

Esta solución no es para varios navegadores porque "execScript" se define solo en IE y Chrome. Pero la pregunta es sobre los controles de Microsoft WebBrowser y IE es el único compatible.

Para que un método de navegador cruzado válido inyecte código javascript, cree un objeto Función con la nueva palabra clave. Este ejemplo crea una función anónima con código inyectado y la ejecuta (javascript implementa cierres y la función tiene acceso al espacio global sin contaminación de variables locales).

var jsCode="alert('hello world');";
(new Function(code))();

Por supuesto, puede retrasar la ejecución:

var jsCode="alert('hello world');";
var inserted=new Function(code);
.................
inserted();

Espero eso ayude

Santiago
fuente
7

esta es una solución usando mshtml

IHTMLDocument2 doc = new HTMLDocumentClass();
doc.write(new object[] { File.ReadAllText(filePath) });
doc.close();

IHTMLElement head = (IHTMLElement)((IHTMLElementCollection)doc.all.tags("head")).item(null, 0);
IHTMLScriptElement scriptObject = (IHTMLScriptElement)doc.createElement("script");
scriptObject.type = @"text/javascript";
scriptObject.text = @"function btn1_OnClick(str){
    alert('you clicked' + str);
}";
((HTMLHeadElementClass)head).appendChild((IHTMLDOMNode)scriptObject);
Camilo Sánchez
fuente
2
Como se trata de un recurso comunitario, su respuesta sigue siendo útil para otros (como yo). +1 para mshtml equivalente, me salvó una pregunta.
Kyeotic
1
Para cualquiera que encuentre esto, elimine 'class' de la última línea, debería leer ((HTMLHeadElement)head).appendChild((IHTMLDOMNode)scriptObject);que obtendrá errores de lo contrario.
Kyeotic
1
¿Hay alguna forma de que los administradores puedan promover esta respuesta? Los que están por encima de esto están todos equivocados en realidad. Este llegó tarde, pero realmente es la respuesta más correcta.
justin.m.chase
2

Usé esto: D

HtmlElement script = this.WebNavegador.Document.CreateElement("SCRIPT");
script.SetAttribute("TEXT", "function GetNameFromBrowser() {" + 
"return 'My name is David';" + 
"}");

this.WebNavegador.Document.Body.AppendChild(script);

Luego puede ejecutar y obtener el resultado con:

string myNameIs = (string)this.WebNavegador.Document.InvokeScript("GetNameFromBrowser");

Espero ser de ayuda

Oscar David Diaz Fortaleché
fuente
2

Aquí hay un ejemplo de VB.Net si está intentando recuperar el valor de una variable desde una página cargada en un control WebBrowser.

Paso 1) Agregue una referencia COM en su proyecto a la biblioteca de objetos HTML de Microsoft

Paso 2) A continuación, agregue este código VB.Net a su Form1 para importar la biblioteca mshtml:
Imports mshtml

Paso 3) Agregue este código VB.Net encima de su línea "Public Class Form1":
<System.Runtime.InteropServices.ComVisibleAttribute (True)>

Paso 4) Agregue un control WebBrowser a su proyecto

Paso 5) Agregue este código VB.Net a su función Form1_Load:
WebBrowser1.ObjectForScripting = Me

Paso 6) Agregue este sub VB.Net que inyectará una función "CallbackGetVar" en el Javascript de la página web:

Public Sub InjectCallbackGetVar(ByRef wb As WebBrowser)
    Dim head As HtmlElement
    Dim script As HtmlElement
    Dim domElement As IHTMLScriptElement

    head = wb.Document.GetElementsByTagName("head")(0)
    script = wb.Document.CreateElement("script")
    domElement = script.DomElement
    domElement.type = "text/javascript"
    domElement.text = "function CallbackGetVar(myVar) { window.external.Callback_GetVar(eval(myVar)); }"
    head.AppendChild(script)
End Sub

Paso 7) Agregue el siguiente sub VB.Net que luego buscará Javascript cuando se invoque:

Public Sub Callback_GetVar(ByVal vVar As String)
    Debug.Print(vVar)
End Sub

Paso 8) Finalmente, para invocar la devolución de llamada de Javascript, agregue este código VB.Net cuando se presiona un botón, o donde desee:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    WebBrowser1.Document.InvokeScript("CallbackGetVar", New Object() {"NameOfVarToRetrieve"})
End Sub

Paso 9) Si le sorprende que esto funcione, es posible que desee leer sobre la función "eval" de Javascript, utilizada en el Paso 6, que es lo que lo hace posible. Tomará una cadena y determinará si existe una variable con ese nombre y, de ser así, devolverá el valor de esa variable.

sh0ber
fuente
1

Siempre puede utilizar una propiedad "DocumentStream" o "DocumentText". Para trabajar con documentos HTML, recomiendo un HTML Agility Pack .

TcKs
fuente
Buen consejo ... Estoy seguro de que lib será útil en algún momento.
jsight
1

yo uso esto:

webBrowser.Document.InvokeScript("execScript", new object[] { "alert(123)", "JavaScript" })
ZRT
fuente
0

Lo que quieres hacer es usar Page.RegisterStartupScript (clave, script):

Consulte aquí para obtener más detalles: http://msdn.microsoft.com/en-us/library/aa478975.aspx

Lo que básicamente hace es construir su cadena de JavaScript, pasarla a ese método y darle una identificación única (en caso de que intente registrarla dos veces en una página).

EDITAR: Esto es lo que llamas gatillo feliz. Siéntete libre de bajarlo. :)

mate
fuente
4
ASP.Net no tiene mucho que ver con la secuencia de comandos del control WebBrowser en una aplicación winforms.
jsight
Probablemente hubiera leído menos de la misma manera y hubiera dado la misma respuesta incorrecta
Grank
0

Si necesita inyectar un archivo completo, puede usar esto:

With Browser.Document
   Dim Head As HtmlElement = .GetElementsByTagName("head")(0)
   Dim Script As HtmlElement = .CreateElement("script")
   Dim Streamer As New StreamReader(<Here goes path to file as String>)
   Using Streamer
       Script.SetAttribute("text", Streamer.ReadToEnd())
   End Using
   Head.AppendChild(Script)
   .InvokeScript(<Here goes a method name as String and without parentheses>)
End With

Recuerde importar System.IOpara usar el StreamReader. Espero que esto ayude.

pablo
fuente
Sé que esto fue respondido hace 3 días menos que hace un año, pero ¿podría usar esto para poner un archivo en una input type="file"sección?
Chris Hobbs