【问题标题】:How to inject Javascript in WebBrowser control?如何在 WebBrowser 控件中注入 Javascript?
【发布时间】:2008-09-30 16:05:54
【问题描述】:

我试过了:

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)

有没有一种简单的方法可以将脚本注入到 dom 中?

【问题讨论】:

    标签: c# javascript .net winforms webbrowser-control


    【解决方案1】:

    由于某种原因,Richard 的解决方案对我不起作用(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");
    

    This answer 解释了如何将IHTMLScriptElement 接口添加到您的项目中。

    【讨论】:

    • 酷,我认为 IHTMLScriptElement 的使用使代码意图在任何情况下都更加明显。我确实想知道您为什么会遇到异常,但有时会与 COM 互操作保持一致。
    • 添加对本地js文件的引用怎么样?请看stackoverflow.com/questions/4029602/…
    • 请帮帮我。你是怎么做到的。我想通过我的 C++ 应用程序将 JS 实时注入我的页面。我该怎么做呢。
    • 这也适用于注入jquery文件吗?
    【解决方案2】:
    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 窗体应用程序中测试)

    编辑:修复了函数集中的大小写问题。

    【讨论】:

    • 我很欣赏这个解决方案,因为它不依赖于(尽管有用!)IHTMLSCriptElement 程序集。
    • @jsight 这应该是公认的答案。它与当前答案相同,但更简单且没有 IHTMLSCriptElement 依赖项。
    【解决方案3】:

    这是我在处理此问题后发现的最简单的方法:

    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 ref here

    【讨论】:

      【解决方案4】:

      此外,在 .NET 4 中,如果您使用 dynamic 关键字,这会更容易:

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

      【讨论】:

      • 为什么有人需要动态呢?如果您想节省一些输入,我们确实从 C# 3.0 开始进行类型推断,因此 var 是可以接受的。无需开始调用 DLR。
      • 这基本上正是动态关键字的重点:COM interop。您实际上没有类型推断,您有文档。例如,因为 IHTMLElement2 不能从 IHtmlElement 分配,并且在运行时您只有一个 COM 代理对象。您只需要知道将哪些接口转换为什么。动态关键字可以帮助您减少很多麻烦。您知道该方法存在为什么要将其转换为某个接口?它并不完全“调用 DLR”,它只是生成知道如何在 COM 对象上调用方法的代码(在这种情况下)。
      • @justin.m.chase 你救了我的命。
      【解决方案5】:

      如果您真正想要的是运行 javascript,这将是最简单的(VB .Net):

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

      我想这不会“注入”它,但它会运行你的函数,如果那是你所追求的。 (以防万一你把问题复杂化了。)如果你能弄清楚如何注入 javascript,把它放到函数“foo”的主体中,让 javascript 为你注入。

      【讨论】:

        【解决方案6】:

        HTML 文档的托管包装器并未完全实现您需要的功能,因此您需要使用 MSHTML API 来完成您想要的:

        1) 添加对 MSHTML 的引用,该引用在 COM 引用下可能被称为“Microsoft HTML 对象库”。

        2) 添加“使用 mshtml;”到你的命名空间。

        3) 获取对脚本元素的 IHTMLElement 的引用:

        IHTMLElement iScriptEl = (IHTMLElement)scriptEl.DomElement;
        

        4) 调用insertAdjacentText 方法,第一个参数值为“afterBegin”。列出了所有可能的值here

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

        5) 现在您将能够在 scriptEl.InnerText 属性中看到代码。

        第, 理查德

        【讨论】:

        • 很好......这与 korchev 提供的技巧一起完美地工作。我希望我可以为此设置两个公认的解决方案。 :)
        【解决方案7】:

        我相信从 c# 向 WebBrowser 控件 HTML 文档中注入 Javascript 的最简单方法是将要注入的代码作为参数调用“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 窗体和 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();
        

        希望对你有帮助

        【讨论】:

          【解决方案8】:

          作为accepted answer 的后续,这是IHTMLScriptElement interface 的最小定义,不需要包含额外的类型库:

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

          【讨论】:

            【解决方案9】:

            这是使用 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);
            

            【讨论】:

            • 由于这是一个社区资源,他的回答对其他人(比如我自己)仍然有用。 +1 相当于 mshtml,帮我省了一个问题。
            • 对于找到这个的任何人,从最后一行删除'class',应该阅读((HTMLHeadElement)head).appendChild((IHTMLDOMNode)scriptObject);否则你会得到错误。
            • 有没有这个答案可以被管理员推广?以上这些在现实中都是错误的。这个来晚了,但确实是最正确的答案。
            【解决方案10】:

            我用过这个: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");
            

            希望对你有所帮助

            【讨论】:

              【解决方案11】:

              如果您尝试从加载到 WebBrowser 控件中的页面中检索变量的值,这里是一个 VB.Net 示例。

              步骤 1) 在您的项目中添加一个 COM 引用到 Microsoft HTML 对象库

              第 2 步)接下来,将此 VB.Net 代码添加到您的 Form1 以导入 mshtml 库:
              导入 mshtml

              第 3 步)在“Public Class Form1”行上方添加此 VB.Net 代码:

              第 4 步)将 WebBrowser 控件添加到您的项目中

              第 5 步)将此 VB.Net 代码添加到您的 Form1_Load 函数中:
              WebBrowser1.ObjectForScripting = 我

              第 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 步)如果您对它的工作感到惊讶,您可能需要阅读第 6 步中使用的 Javascript“eval”函数,这使这成为可能。它将接受一个字符串并确定是否存在具有该名称的变量,如果存在,则返回该变量的值。

              【讨论】:

                【解决方案12】:

                您始终可以使用“DocumentStream”或“DocumentText”属性。 对于处理 HTML 文档,我推荐HTML Agility Pack

                【讨论】:

                • 不错的提示...我相信 lib 在某些时候会派上用场。
                【解决方案13】:

                我用这个:

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

                【讨论】:

                  【解决方案14】:

                  你想要做的是使用 Page.RegisterStartupScript(key, script) :

                  更多详情请看这里:http://msdn.microsoft.com/en-us/library/aa478975.aspx

                  你基本上做的是构建你的javascript字符串,将它传递给那个方法并给它一个唯一的id(以防你尝试在一个页面上注册两次。)

                  编辑:这就是你所说的触发快乐。随意放下它。 :)

                  【讨论】:

                  • ASP.Net 与在 winforms 应用程序中编写 WebBrowser 控件的脚本没有太大关系。
                  • 我可能会以同样的方式阅读不足并给出同样的错误答案
                  【解决方案15】:

                  如果你需要注入整个文件,那么你可以使用这个:

                  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" 部分吗?
                  猜你喜欢
                  • 2011-12-21
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-26
                  • 2019-09-30
                  • 1970-01-01
                  • 2015-12-19
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多