在WebBrowser的Document中调用JavaScript中的C#代码
我有一个C#WinForms应用程序,其中有一个WebBrowser控件。 我想在我的C#表单和embedded式Web浏览器控件中的JavaScript之间进行双向通信。
我知道我可以使用InvokeScript调用JavaScript函数,但是如何从文档中的 JavaScript调用C#代码? 我猜想由于安全原因,这不会很容易,但是不pipe怎样,它有可能吗? 这些JavaScript函数应该是用户函数,就像macros一样,它会告诉WebBrowser到底要用我自己编写的整个C#库的帮助来做什么。 而且由于这是一个网页抓取工具,JavaScript是这些macros的完美语言,因为它非常适合访问HTML文档中的元素。
您需要做的是将Web浏览器控件上的ObjectForScripting
属性设置为包含要从JavaScript调用的C#方法的对象。 然后,您可以使用window.external
从JavaScript访问该对象。 唯一要注意的是对象必须具有[ComVisibleAttribute(true)]
属性。 我已经使用了这几年。
这是一个包含文档和简单示例的页面: http : //msdn.microsoft.com/en-us/library/a0746166.aspx
这里是链接的例子(我还没有试过这个代码):
using System; using System.Windows.Forms; using System.Security.Permissions; [PermissionSet(SecurityAction.Demand, Name="FullTrust")] [System.Runtime.InteropServices.ComVisibleAttribute(true)] public class Form1 : Form { private WebBrowser webBrowser1 = new WebBrowser(); private Button button1 = new Button(); [STAThread] public static void Main() { Application.EnableVisualStyles(); Application.Run(new Form1()); } public Form1() { button1.Text = "call script code from client code"; button1.Dock = DockStyle.Top; button1.Click += new EventHandler(button1_Click); webBrowser1.Dock = DockStyle.Fill; Controls.Add(webBrowser1); Controls.Add(button1); Load += new EventHandler(Form1_Load); } private void Form1_Load(object sender, EventArgs e) { webBrowser1.AllowWebBrowserDrop = false; webBrowser1.IsWebBrowserContextMenuEnabled = false; webBrowser1.WebBrowserShortcutsEnabled = false; webBrowser1.ObjectForScripting = this; // Uncomment the following line when you are finished debugging. //webBrowser1.ScriptErrorsSuppressed = true; webBrowser1.DocumentText = "<html><head><script>" + "function test(message) { alert(message); }" + "</script></head><body><button " + "onclick=\"window.external.Test('called from script code')\">" + "call client code from script code</button>" + "</body></html>"; } public void Test(String message) { MessageBox.Show(message, "client code"); } private void button1_Click(object sender, EventArgs e) { webBrowser1.Document.InvokeScript("test", new String[] { "called from client code" }); } }
您可能正在寻找http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.objectforscripting.aspx
WebBrowser.ObjectForScripting允许您将[ComVisible] .net类的实例公开到托pipe的Web浏览器内运行的JavaScript代码。 它在javascript中暴露为window.external
来自微软的优秀文章: 如何:在DHTML代码和客户端应用程序代码之间实现双向通信
下面是我编写的一些扩展方法,以帮助WebBrowser对象和C#代码之间的双向通信/调用:
using System; using System.Threading; using FluentSharp.Web35; using FluentSharp.WinForms; using FluentSharp.CoreLib; using FluentSharp.CoreLib.API; namespace FluentSharp.Watin { public static class WatiN_IE_ExtensionMethods_Javascript { public static object invokeScript(this WatiN_IE ie, string functionName) { return ie.invokeScript(functionName, null); } public static object invokeScript(this WatiN_IE ie, string functionName, params object[] parameters) { //"[WatiN_IE] invokeScript '{0}' with parameters:{1}".info(functionName ,parameters.size()); return ie.invokeScript(true, functionName, parameters); } public static object invokeScript(this WatiN_IE ie, bool waitForExecutionComplete, string functionName, params object[] parameters) { var sync = new AutoResetEvent(false); object responseValue = null; ie.WebBrowser.invokeOnThread( ()=>{ var document = ie.WebBrowser.Document; if (parameters.isNull()) responseValue = document.InvokeScript(functionName); else responseValue = document.InvokeScript(functionName, parameters); sync.Set(); }); if (waitForExecutionComplete) sync.WaitOne(); return responseValue; } public static object invokeEval(this WatiN_IE ie, string evalScript) { var evalParam = "(function() { " + evalScript + "})();"; //"[WatiN_IE] invokeEval evalParam: {0}".debug(evalParam); return ie.invokeScript("eval", evalParam); } public static WatiN_IE.ToCSharp injectJavascriptFunctions(this WatiN_IE ie) { return ie.injectJavascriptFunctions(false); } public static WatiN_IE.ToCSharp injectJavascriptFunctions(this WatiN_IE ie, bool resetHooks) { if (ie.WebBrowser.isNull()) "in InjectJavascriptFunctions, ie.WebBrowser was null".error(); else { if (ie.WebBrowser.ObjectForScripting.isNull() || resetHooks) { ie.WebBrowser.ObjectForScripting = new WatiN_IE.ToCSharp(); "Injecting Javascript Hooks * Functions for page: {0}".debug(ie.url()); ie.eval("var o2Log = function(message) { window.external.write(message) };"); ie.invokeScript("o2Log","Test from Javascript (via toCSharp(message) )"); ie.eval("$o2 = window.external"); "Injection complete (use o2Log(...) or $o2.write(...) to talk back to O2".info(); return (ie.WebBrowser.ObjectForScripting as WatiN_IE.ToCSharp); } else { if((ie.WebBrowser.ObjectForScripting is WatiN_IE.ToCSharp)) return (ie.WebBrowser.ObjectForScripting as WatiN_IE.ToCSharp); else "in WatiN_IE injectJavascriptFunctions, unexpected type in ie.WebBrowser.ObjectForScripting: {0}".error(ie.WebBrowser.ObjectForScripting.typeName()); } } return null; } public static object downloadAndExecJavascriptFile(this WatiN_IE ie, string url) { "[WatiN_IE] downloadAndExecJavascriptFile: {0}".info(url); var javascriptCode = url.uri().getHtml(); if (javascriptCode.valid()) ie.eval(javascriptCode); return ie; } public static WatiN_IE injectJavascriptFunctions_onNavigate(this WatiN_IE ie) { ie.onNavigate((url)=> ie.injectJavascriptFunctions()); return ie; } public static WatiN_IE setOnAjaxLog(this WatiN_IE ie, Action<string, string,string,string> onAjaxLog) { (ie.WebBrowser.ObjectForScripting as WatiN_IE.ToCSharp).OnAjaxLog = onAjaxLog; return ie; } public static WatiN_IE eval_ASync(this WatiN_IE ie, string script) { O2Thread.mtaThread(()=> ie.eval(script)); return ie; } public static WatiN_IE eval(this WatiN_IE ie, string script) { return ie.eval(script, true); } public static WatiN_IE eval(this WatiN_IE ie, string script, bool waitForExecutionComplete) { var executionThread = O2Thread.staThread(()=> ie.IE.RunScript(script)); if (waitForExecutionComplete) executionThread.Join(); return ie; } public static WatiN_IE alert(this WatiN_IE ie, string alertScript) { return ie.eval("alert({0});".format(alertScript)); } public static object getJsObject(this WatiN_IE ie) { var toCSharpProxy = ie.injectJavascriptFunctions(); if (toCSharpProxy.notNull()) return toCSharpProxy.getJsObject(); return null; } public static T getJsObject<T>(this WatiN_IE ie, string jsCommand) { var jsObject = ie.getJsObject(jsCommand); if (jsObject is T) return (T)jsObject; return default(T); } public static bool doesJsObjectExists(this WatiN_IE ie, string jsCommand) { var toCSharpProxy = ie.injectJavascriptFunctions(); if (toCSharpProxy.notNull()) { var command = "window.external.setJsObject(typeof({0}))".format(jsCommand); ie.invokeEval(command); ie.remapInternalJsObject(); return toCSharpProxy.getJsObject().str()!="undefined"; } return false; } public static object getJsVariable(this WatiN_IE ie, string jsCommand) { return ie.getJsObject(jsCommand); } public static object getJsObject(this WatiN_IE ie, string jsCommand) { var toCSharpProxy = ie.injectJavascriptFunctions(); if (toCSharpProxy.notNull()) { var command = "window.external.setJsObject({0})".format(jsCommand); ie.invokeEval(command); ie.remapInternalJsObject(); return toCSharpProxy.getJsObject(); } return null; } public static WatiN_IE remapInternalJsObject(this WatiN_IE ie) { //"setting JS _jsObject variable to getJsObject()".info(); ie.invokeEval("_jsObject = window.external.getJsObject()"); // creates JS variable to be used from JS return ie; } public static WatiN_IE setJsObject(this WatiN_IE ie, object jsObject) { var toCSharpProxy = ie.injectJavascriptFunctions(); if (toCSharpProxy.notNull()) { toCSharpProxy.setJsObject(jsObject); ie.remapInternalJsObject(); } return ie; } public static object waitForJsObject(this WatiN_IE watinIe) { return watinIe.waitForJsObject(500, 20); } public static object waitForJsObject(this WatiN_IE watinIe, int sleepMiliseconds, int maxSleepTimes) { "[WatiN_IE][waitForJsObject] trying to find jsObject for {0} x {1} ms".info(maxSleepTimes, sleepMiliseconds); watinIe.setJsObject(null); for(var i = 0; i < maxSleepTimes ; i++) { var jsObject = watinIe.getJsObject(); if(jsObject.notNull()) { "[watinIe][waitForJsObject] got value: {0} (n tries)".info(jsObject, i); return jsObject; } watinIe.sleep(500, false); } "[WatiN_IE][waitForJsObject] didn't find jsObject after {0} sleeps of {1} ms".error(maxSleepTimes, sleepMiliseconds); return null; } public static object waitForJsVariable(this WatiN_IE watinIe, string jsCommand) { return watinIe.waitForJsVariable(jsCommand, 500, WatiN_IE_ExtensionMethods.WAITFORJSVARIABLE_MAXSLEEPTIMES); } public static object waitForJsVariable(this WatiN_IE watinIe, string jsCommand, int sleepMiliseconds, int maxSleepTimes) { "[WatiN_IE][waitForJsVariable] trying to find jsObject called '{0}' for {1} x {2} ms".info(jsCommand, maxSleepTimes, sleepMiliseconds); watinIe.setJsObject(null); for(var i = 0; i < maxSleepTimes ; i++) { if (watinIe.doesJsObjectExists(jsCommand)) { var jsObject = watinIe.getJsObject(jsCommand); "[watinIe][waitForJsVariable] got value: {0} ({1} tries)".info(jsObject, i); return jsObject; } watinIe.sleep(500, false); } "[WatiN_IE][waitForJsVariable] didn't find jsObject called '{0}' after {1} sleeps of {2} ms".error(jsCommand, maxSleepTimes, sleepMiliseconds); return null; } public static WatiN_IE deleteJsVariable(this WatiN_IE watinIe, string jsVariable) { var evalString = "try { delete " + jsVariable + " } catch(exception) { }"; watinIe.eval(evalString); return watinIe; } } }