Как вставить Javascript в элемент управления WebBrowser?


81

Я пробовал это:

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 и scriptEl.InnerText выдают ошибки:

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)

Есть ли простой способ вставить скрипт в дом?

Ответы:


101

По какой-то причине решение Ричарда не сработало на моей стороне (insertAdjacentText не удалось с исключением). Однако, похоже, это работает:

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

Этот ответ объясняет, как добавить IHTMLScriptElementинтерфейс в ваш проект.


1
Круто, я думаю, что использование IHTMLScriptElement в любом случае делает намерение кода более очевидным. Мне действительно интересно, почему у вас есть исключение, но иногда c'est la vie с COM-взаимодействием.
ZeroBugBounce

А как насчет добавления ссылки на локальный файл js? Пожалуйста , см stackoverflow.com/questions/4029602/...
IAmAN00B

Пожалуйста, помогите мне с этим. Как ты это сделал. Я хочу внедрить JS на свою страницу в реальном времени через приложение C ++. Как мне это сделать.
Johnydep

применимо ли это также для ввода файла 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");

(протестировано в приложении .NET 4 / Windows Forms)

Изменить: исправлена ​​проблема с регистром в наборе функций.


12
Мне понравилось это решение, потому что оно не полагается на (хотя и полезную!) Сборку IHTMLSCriptElement.
Мика Смит

6
@jsight Это должен быть принятый ответ. Это то же самое, что и текущий ответ, но проще и без IHTMLSCriptElementзависимости.
BlueRaja - Дэнни Пфлугофт

32

Вот самый простой способ, который я нашел после работы над этим:

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

Глобальная функция JavaScript eval(str)анализирует и выполняет все, что написано в str. Проверить w3schools реф здесь .


22

Кроме того, в .NET 4 это еще проще, если вы используете ключевое слово dynamic:

dynamic document = this.browser.Document;
dynamic head = document.GetElementsByTagName("head")[0];
dynamic scriptEl = document.CreateElement("script");
scriptEl.text = ...;
head.AppendChild(scriptEl);

2
Зачем кому-то для этого нужна динамика? Если вы пытаетесь сохранить некоторую типизацию, у нас есть вывод типа, начиная с C # 3.0, поэтому var будет приемлемым. Нет необходимости запускать вызов DLR.
alimbada

23
В этом суть ключевого слова dynamic: COM-взаимодействие. На самом деле здесь нет вывода типа, у вас есть документация. Поскольку IHTMLElement2 не может быть назначен, например, из IHtmlElement, а во время выполнения у вас просто есть прокси-объект COM. Вам просто нужно знать, какие интерфейсы и во что преобразовывать. Ключевое слово dynamic поможет вам избавиться от лишней ерунды. Вы знаете, что метод существует, зачем использовать его в каком-то интерфейсе? Он не совсем «вызывает DLR», он просто генерирует код, который знает, как вызывать метод для COM-объектов (в данном случае).
justin.m.chase

1
@ justin.m.chase ты спас мне жизнь.
Хизар Икбал

17

Если все, что вам действительно нужно, это запустить javascript, это будет проще всего (VB .Net):

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

Я предполагаю, что это не будет "вводить" его, но оно запустит вашу функцию, если вам это нужно. (На всякий случай, если вы слишком усложнили проблему.) И если вы можете понять, как внедрить в javascript, поместите это в тело функции «foo» и позвольте javascript сделать инъекцию за вас.


10

Управляемая оболочка для HTML-документа не полностью реализует нужные вам функции, поэтому вам нужно погрузиться в API MSHTML, чтобы выполнить то, что вы хотите:

1) Добавьте ссылку на MSHTML, который, вероятно, будет называться «Библиотека объектов Microsoft HTML» в разделе COM. ссылками .

2) Добавьте 'using mshtml;' в ваши пространства имен.

3) Получите ссылку на свой элемент сценария IHTMLElement:

IHTMLElement iScriptEl = (IHTMLElement)scriptEl.DomElement;

4) Вызовите метод insertAdjacentText со значением первого параметра «afterBegin». Все возможные значения перечислены здесь :

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

5) Теперь вы сможете увидеть код в свойстве scriptEl.InnerText.

Hth, Ричард


1
Хорошо ... это вместе с советами Корчева отлично работает. Хотел бы я установить два принятых решения для этого. :)
jsight

8

Как продолжение принятого ответа , это минимальное определение IHTMLScriptElementинтерфейса, которое не требует включения дополнительных библиотек типов:

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

Таким образом, полный код внутри производного класса элемента управления WebBrowser будет выглядеть так:

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

8

Я считаю, что самый простой метод внедрения Javascript в HTML-документ WebBrowser Control из C # - это вызвать метод execScript с кодом, который будет введен в качестве аргумента.

В этом примере код javascript вводится и выполняется в глобальной области:

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

Если вы хотите отложить выполнение, введите функции и вызовите их после:

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

Это действительно для элементов управления Windows Forms и WPF WebBrowser.

Это решение не является кроссбраузерным, поскольку execScript определен только в IE и Chrome. Но вопрос касается элементов управления Microsoft WebBrowser, и поддерживается только IE.

Чтобы использовать допустимый кроссбраузерный метод для внедрения кода javascript, создайте объект Function с новым ключевым словом. В этом примере создается анонимная функция с внедренным кодом и выполняется ее (javascript реализует замыкания, и функция имеет доступ к глобальному пространству без загрязнения локальной переменной).

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

Конечно, вы можете отложить выполнение:

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

Надеюсь, поможет


7

это решение с использованием 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);

2
Поскольку это ресурс сообщества, его ответ по-прежнему будет полезен другим (например, мне). +1 для эквивалента mshtml, избавил меня от вопроса.
Kyeotic

1
Для тех, кто найдет это, удалите «класс» из последней строки, если ((HTMLHeadElement)head).appendChild((IHTMLDOMNode)scriptObject);вы прочитаете, вы получите ошибки, иначе.
Kyeotic

1
Могут ли админы продвигать этот ответ? Все вышесказанное в действительности неверно. Этот ответ пришел поздно, но на самом деле это самый правильный ответ.
justin.m.chase

2

Я использовал это: D

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

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

Затем вы можете выполнить и получить результат с помощью:

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

Я надеюсь быть полезным


2

Вот пример VB.Net, если вы пытаетесь получить значение переменной из страницы, загруженной в элемент управления WebBrowser.

Шаг 1) Добавьте ссылку на COM в свой проект в библиотеку объектов Microsoft HTML.

Шаг 2) Затем добавьте этот код VB.Net в форму Form1, чтобы импортировать библиотеку mshtml:
Imports mshtml

Шаг 3) Добавьте этот код VB.Net над строкой «Public Class Form1»:
<System.Runtime.InteropServices.ComVisibleAttribute (True)>

Шаг 4) Добавьте элемент управления WebBrowser в свой проект

Шаг 5) Добавьте этот код VB.Net в свою функцию Form1_Load:
WebBrowser1.ObjectForScripting = Me

Шаг 6) Добавьте эту подпрограмму VB.Net, которая будет вставлять функцию «CallbackGetVar» в Javascript веб-страницы:

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

Шаг 7) Добавьте следующую подпрограмму VB.Net, которую затем будет искать Javascript при вызове:

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

Шаг 8) Наконец, чтобы вызвать обратный вызов Javascript, добавьте этот код VB.Net при нажатии кнопки или где угодно:

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

Шаг 9) Если вас удивит, что это работает, вы можете прочитать о функции Javascript "eval", использованной на шаге 6, которая и делает это возможным. Он берет строку и определяет, существует ли переменная с этим именем, и, если да, возвращает значение этой переменной.


1

Вы всегда можете использовать свойство «DocumentStream» или «DocumentText». Для работы с HTML-документами я рекомендую HTML Agility Pack .


Хороший совет ... Я уверен, что lib когда-нибудь пригодится.
jsight

1

я использую это:

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

0

Что вы хотите сделать, это использовать Page.RegisterStartupScript (ключ, скрипт):

Подробнее см. Здесь: http://msdn.microsoft.com/en-us/library/aa478975.aspx

В основном вы создаете строку javascript, передаете ее этому методу и присваиваете ей уникальный идентификатор (на случай, если вы попытаетесь зарегистрировать ее дважды на странице).

РЕДАКТИРОВАТЬ: это то, что вы называете триггером счастливым. Не стесняйтесь опускать его. :)


4
ASP.Net не имеет особого отношения к созданию сценариев для элемента управления WebBrowser в приложении winforms.
jsight

Я, вероятно,
недооценил

0

Если вам нужно ввести весь файл, вы можете использовать это:

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

Не забудьте импортировать System.IO, чтобы использовать StreamReader. Надеюсь, это поможет.


Я знаю, что на этот вопрос ответили за 3 дня до года, но сможете ли вы использовать это, чтобы поместить файл в input type="file"раздел?
Крис Хоббс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.