Jak wstrzyknąć JavaScript w kontrolce WebBrowser?

81

Próbowałem tego:

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 i scriptEl.InnerText wyświetlają błędy:

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)

Czy istnieje łatwy sposób na wprowadzenie skryptu do domeny?

jsight
źródło

Odpowiedzi:

101

Z jakiegoś powodu rozwiązanie Richarda nie zadziałało po mojej stronie (insertAdjacentText zawiódł z wyjątkiem). To jednak wydaje się działać:

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");

W tej odpowiedzi wyjaśniono, jak wprowadzić IHTMLScriptElementinterfejs do projektu.

Atanas Korchev
źródło
1
Fajnie, myślę, że użycie IHTMLScriptElement sprawia, że ​​intencja kodu jest bardziej oczywista w każdym przypadku. Zastanawiam się, dlaczego masz wyjątek, ale czasami c'est la vie z interopem COM.
ZeroBugBounce
A co z dodaniem odwołania do lokalnego pliku js? Zobacz stackoverflow.com/questions/4029602/…
IAmAN00B
Proszę, pomóż mi z tym. Jak to zrobiłeś. Chcę wstawić JS do mojej strony w czasie rzeczywistym za pośrednictwem mojej aplikacji C ++. Jak mam się do tego zabrać.
Johnydep
czy to ma zastosowanie również do wstrzyknięcia pliku jquery?
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");

(testowane w .NET 4 / aplikacji Windows Forms)

Edycja: Naprawiono problem z zestawem funkcji.

typpo
źródło
12
Doceniam to rozwiązanie, ponieważ nie opiera się na (aczkolwiek użytecznym!) Zestawie IHTMLSCriptElement.
Micah Smith
6
@jsight To powinna być zaakceptowana odpowiedź. Jest taka sama jak obecna odpowiedź, ale prostsza i bez IHTMLSCriptElementzależności.
BlueRaja - Danny Pflughoeft
32

Oto najłatwiejszy sposób, jaki znalazłem po pracy nad tym:

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 });

To, co eval(str)robi globalna funkcja JavaScript , to analizuje i wykonuje wszystko, co jest napisane w str. Sprawdź referencje w3schools tutaj .

Ilya Rosikhin
źródło
22

Ponadto w .NET 4 jest to jeszcze łatwiejsze, jeśli używasz dynamicznego słowa kluczowego:

dynamic document = this.browser.Document;
dynamic head = document.GetElementsByTagName("head")[0];
dynamic scriptEl = document.CreateElement("script");
scriptEl.text = ...;
head.AppendChild(scriptEl);
justin.m.chase
źródło
2
Dlaczego ktoś miałby do tego potrzebować dynamiki? Jeśli próbujesz zaoszczędzić trochę pisania, mamy wnioskowanie o typie od C # 3.0, więc var byłby akceptowalny. Nie ma potrzeby wywoływania DLR.
alimbada
23
W zasadzie jest to dokładnie punkt dynamicznego słowa kluczowego: współdziałanie COM. W rzeczywistości nie masz tutaj wnioskowania o typie, masz dokumentację. Ponieważ na przykład IHTMLElement2 nie można przypisać z poziomu IHtmlElement, a w czasie wykonywania masz po prostu obiekt proxy COM. Musisz tylko wiedzieć, na jakie interfejsy rzutować. Dynamiczne słowo kluczowe pomaga ograniczyć dużą część tego okrucieństwa. Wiesz, że metoda istnieje, dlaczego rzutuje się ją na jakiś interfejs? Nie dokładnie `` wywołuje DLR '', po prostu generuje kod, który wie, jak wywołać metodę na obiektach COM (w tym przypadku).
justin.m.chase
1
@ justin.m.chase uratowałeś mi życie.
Khizar Iqbal
17

Jeśli wszystko, czego naprawdę chcesz, to uruchomić javascript, byłoby to najłatwiejsze (VB .Net):

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

Myślę, że to nie „wstrzyknie” tego, ale uruchomi twoją funkcję, jeśli tego szukasz. (Na wypadek gdybyś zbytnio skomplikował problem.) A jeśli potrafisz wymyślić, jak wykonać wstrzyknięcie w javascript, umieść to w treści funkcji „foo” i pozwól, aby javascript wykonał wstrzyknięcie za Ciebie.

Eyal
źródło
10

Zarządzany wrapper dla dokumentu HTML nie implementuje w pełni potrzebnej funkcjonalności, więc musisz zagłębić się w API MSHTML, aby osiągnąć to, co chcesz:

1) Dodaj odniesienie do MSHTML, które prawdopodobnie będzie nazywane „Biblioteką obiektów Microsoft HTML” w COM odniesieniach .

2) Dodaj „używając mshtml;” do twoich przestrzeni nazw.

3) Uzyskaj odniesienie do IHTMLElement elementu skryptu:

IHTMLElement iScriptEl = (IHTMLElement)scriptEl.DomElement;

4) Wywołaj metodę insertAdjacentText z pierwszą wartością parametru „afterBegin”. Wszystkie możliwe wartości są wymienione tutaj :

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

5) Teraz będziesz mógł zobaczyć kod we właściwości scriptEl.InnerText.

Hth, Richard

ZeroBugBounce
źródło
1
Fajnie ... to razem ze wskazówkami dostarczonymi przez Korcheva działa doskonale. Żałuję, że nie mogę ustawić dwóch akceptowanych rozwiązań dla tego. :)
jsight
8

Jako kontynuacja zaakceptowanej odpowiedzi , jest to minimalna definicja IHTMLScriptElementinterfejsu, która nie wymaga dołączania dodatkowych bibliotek typów:

[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; }
}

Tak więc pełny kod wewnątrz klasy pochodnej kontrolki WebBrowser wyglądałby następująco:

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
źródło
8

Uważam, że najprostszą metodą wstrzyknięcia JavaScript do dokumentu HTML kontrolnego WebBrowser z języka c # jest wywołanie metody „execScript” z kodem, który ma zostać wstrzyknięty jako argument.

W tym przykładzie kod javascript jest wstrzykiwany i wykonywany w zasięgu globalnym:

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

Jeśli chcesz opóźnić wykonanie, wstrzyknij funkcje i wywołaj je po:

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

Dotyczy to formantów Windows Forms i WPF WebBrowser.

To rozwiązanie nie działa na wiele przeglądarek, ponieważ „execScript” jest zdefiniowany tylko w przeglądarkach IE i Chrome. Ale pytanie dotyczy formantów Microsoft WebBrowser, a IE jest jedynym obsługiwanym.

Aby uzyskać prawidłową metodę wstrzykiwania kodu JavaScript w różnych przeglądarkach, utwórz obiekt Function z nowym słowem kluczowym. Ten przykład tworzy anonimową funkcję z wstrzykniętym kodem i wykonuje ją (javascript implementuje domknięcia, a funkcja ma dostęp do globalnej przestrzeni bez lokalnego zanieczyszczenia zmiennego).

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

Oczywiście możesz opóźnić wykonanie:

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

Mam nadzieję, że to pomoże

Santiago
źródło
7

jest to rozwiązanie wykorzystujące 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 Sanchez
źródło
2
Ponieważ jest to zasób społeczności, jego odpowiedź jest nadal przydatna dla innych (takich jak ja). +1 dla odpowiednika mshtml, uratował mi pytanie.
Kyeotic
1
Dla każdego, kto to znajdzie, usuń „class” z ostatniej linii, jeśli przeczytasz ((HTMLHeadElement)head).appendChild((IHTMLDOMNode)scriptObject);, w przeciwnym razie wystąpią błędy.
Kyeotic
1
Czy jest tak, że ta odpowiedź może być promowana przez administratorów? Wszystkie powyższe są błędne w rzeczywistości. Ten był spóźniony, ale naprawdę jest najbardziej poprawną odpowiedzią.
justin.m.chase
2

Użyłem tego: D

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

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

Następnie możesz wykonać i uzyskać wynik za pomocą:

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

Mam nadzieję, że będę pomocny

Oscar David Diaz Fortaleché
źródło
2

Oto przykład VB.Net, jeśli próbujesz pobrać wartość zmiennej ze strony załadowanej do kontrolki WebBrowser.

Krok 1) Dodaj odwołanie COM w swoim projekcie do biblioteki obiektów Microsoft HTML

Krok 2) Następnie dodaj ten kod VB.Net do formularza Form1, aby zaimportować bibliotekę mshtml:
Importuje mshtml

Krok 3) Dodaj ten kod VB.Net nad wierszem „Public Class Form1”:
<System.Runtime.InteropServices.ComVisibleAttribute (True)>

Krok 4) Dodaj formant WebBrowser do projektu

Krok 5) Dodaj ten kod VB.Net do funkcji Form1_Load:
WebBrowser1.ObjectForScripting = Me

Krok 6) Dodaj ten podrzędny VB.Net, który wstrzyknie funkcję „CallbackGetVar” do JavaScript strony internetowej:

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

Krok 7) Dodaj następującą podrzędną wartość VB.Net, której JavaScript będzie szukał po wywołaniu:

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

Krok 8) Na koniec, aby wywołać wywołanie zwrotne Javascript, dodaj ten kod VB.Net po naciśnięciu przycisku lub gdziekolwiek chcesz:

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

Krok 9) Jeśli zaskoczy Cię, że to działa, możesz poczytać o funkcji Javascript „eval”, użytej w kroku 6, która to umożliwia. Pobierze łańcuch i określi, czy istnieje zmienna o tej nazwie, a jeśli tak, zwróci wartość tej zmiennej.

sh0ber
źródło
1

Zawsze możesz użyć właściwości „DocumentStream” lub „DocumentText”. Do pracy z dokumentami HTML polecam HTML Agility Pack .

TcKs
źródło
Niezła wskazówka ... Jestem pewien, że lib się kiedyś przyda.
jsight
1

używam tego:

webBrowser.Document.InvokeScript("execScript", new object[] { "alert(123)", "JavaScript" })
ZRT
źródło
0

To, co chcesz zrobić, to użyć Page.RegisterStartupScript (klucz, skrypt):

Więcej informacji można znaleźć tutaj: http://msdn.microsoft.com/en-us/library/aa478975.aspx

To, co w zasadzie robisz, to budowanie ciągu javascript, przekazanie go do tej metody i nadanie mu unikalnego identyfikatora (na wypadek, gdybyś próbował zarejestrować go dwukrotnie na stronie).

EDYCJA: To jest to, co nazywasz wyzwalaczem szczęśliwym. Możesz go opuścić. :)

mattlant
źródło
4
ASP.Net nie ma wiele wspólnego ze skryptowaniem kontrolki WebBrowser w aplikacji winForms.
jsight
Prawdopodobnie nie przeczytałbym tego samego w ten sam sposób i udzieliłbym tej samej błędnej odpowiedzi
Grank,
0

Jeśli chcesz wstrzyknąć cały plik, możesz użyć tego:

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

Pamiętaj, aby zaimportować System.IO, aby użyć rozszerzenia StreamReader. Mam nadzieję, że to pomoże.

pablo
źródło
Wiem, że odpowiedź na to pytanie padła 3 dni przed rokiem, ale czy byłbyś w stanie użyć tego do umieszczenia pliku w input type="file"sekcji?
Chris Hobbs