Invoke C# Code from JavaScript in a Document in a Webbrowser

Invoke C# code from JavaScript in a Document in a WebBrowser

What you need to do is set the ObjectForScripting property on the web browser control to an object containing the C# methods you want to call from JavaScript. Then you can access that object from JavaScript using window.external. The only thing to watch out for is that the object has to have the [ComVisibleAttribute(true)] attribute. I've used this successfully for several years.

Here's a page with documenation and a simple example: http://msdn.microsoft.com/en-us/library/a0746166.aspx

Here's the example from the link (I haven't tried this code):

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

How to inject Javascript in WebBrowser control?

For some reason Richard's solution didn't work on my end (insertAdjacentText failed with an exception). This however seems to work:

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 explains how to get the IHTMLScriptElement interface into your project.

Invoke a script in WebBrowser, and wait for it to finish running (synchronized)

using async/await you can wait until the script is executed without blocking the UI.

public async void AMethod()
{
string script =
@"<script>
function Script_A() {
Script_B();
window.external.Completed(); //call C#: CallbackObject's Completed method
}
function Script_B(){
alert('in script');
}
</script>";

TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

webBrowser1.ObjectForScripting = new CallbackObject(tcs);
//Ensure DocumentText is loaded before invoking "InvokeScript",
//by extension method "SetDocumentTextAsync" (below)
await webBrowser1.SetDocumentTextAsync(script);
webBrowser1.Document.InvokeScript("Script_A");

await tcs.Task;

MessageBox.Show("Script executed");
}

[ComVisible(true)]
public class CallbackObject
{
TaskCompletionSource<bool> _tcs = null;

public CallbackObject(TaskCompletionSource<bool> tcs)
{
_tcs = tcs;
}
public void Completed()
{
_tcs.TrySetResult(true);
}
}

public static class BrowserExtensions
{
public static Task SetDocumentTextAsync(this WebBrowser wb, string html)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
WebBrowserDocumentCompletedEventHandler completedEvent = null;
completedEvent = (sender, e) =>
{
wb.DocumentCompleted -= completedEvent;
tcs.SetResult(null);
};
wb.DocumentCompleted += completedEvent;

wb.ScriptErrorsSuppressed = true;
wb.DocumentText = html;

return tcs.Task;
}
}

How to inject Javascript in WebBrowser control

Does this work?

    private void WebBrowser_LoadCompleted
(object sender,
System.Windows.Navigation.NavigationEventArgs e)
{
var webBrowser = sender as WebBrowser;

var document
= webBrowser.Document as mshtml.HTMLDocument;
var ahref
= document.getElementsByTagName("A").Cast<mshtml.IHTMLElement>().First();
ahref.setAttribute(
"onmouseenter",
"javascript:alert('Hi');", 1);
}

You need is Microsoft.mshtml (the .net API and not MS office one) reference.

Also please see this code for WPF webbrowser control that uses ObjectForScripting property of WebBrowser which can help you in injecting javascript...

http://blogs.msdn.com/b/wpf/archive/2011/05/27/how-does-wpf-webbrowser-control-handle-window-external-notify.aspx

Let me know if this helps.

Invoke Javascript Test Click in Web Browser control

[EDITED]
What I said below was incorrect. Your syntax is correct, webBrowser2.Document.InvokeScript("TestClick") should do the same job as webBrowser2.InvokeScript("TestClick"). I'll try your code and get back here.


I think the correct syntax should be this:

webBrowser2.Document.InvokeScript("TestClick()");

Note () after TestClick.

Or you could rather do:

webBrowser2.InvokeScript("TestClick");

[EDITED]
There's something wrong with the HTML originally loaded into the WB, maybe you should include it with your question. The following is just a copy of your code (besides this.webBrowser2.DocumentText) and it does work, TestClick gets invoked.

    private void Form1_Load(object sender, EventArgs e)
{
this.webBrowser2.DocumentText = "<body><div id=flls><button onclick='alert(true)'>go</button></div></body>";
this.webBrowser2.DocumentCompleted += webBrowser2_DocumentCompleted;
}

void webBrowser2_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
HtmlElement head = webBrowser2.Document.GetElementsByTagName("head")[0];
HtmlElement testScript = webBrowser2.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)testScript.DomElement;
element.text = "function simulate(g,c){var e=extend(defaultOptions,arguments[2]||{});var b,f=null;for(var d in eventMatchers){if(eventMatchers[d].test(c)){f=d;break}}if(!f){throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported')}if(document.createEvent){b=document.createEvent(f);if(f=='HTMLEvents'){b.initEvent(c,e.bubbles,e.cancelable)}else{b.initMouseEvent(c,e.bubbles,e.cancelable,document.defaultView,e.button,e.pointerX,e.pointerY,e.pointerX,e.pointerY,e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.button,g)}g.dispatchEvent(b)}else{e.clientX=e.pointerX;e.clientY=e.pointerY;var a=document.createEventObject();b=extend(a,e);g.fireEvent('on'+c,b)}return g}function extend(a,c){for(var b in c){a[b]=c[b]}return a}var eventMatchers={HTMLEvents:/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,MouseEvents:/^(?:click|dblclick|mouse(?:down|up|over|move|out))$/};var defaultOptions={pointerX:0,pointerY:0,button:0,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false,bubbles:true,cancelable:true};function TestClick(){simulate(document.getElementById('flls').firstChild,'click')};";
head.AppendChild(testScript);
webBrowser2.Document.InvokeScript("TestClick");
}

Calling a Javascript function in the C# webBrowser control

Can you specify what failed?

My sample below consists of a form with a WebBrowser and a Button.

The object called y in the end has the sentence "i did it!". So with me it works.

public partial class Form1 : Form
{

public Form1()
{
InitializeComponent();

webBrowser1.DocumentText = @"<html><head>
<script type='text/javascript'>
function doIt() {
alert('hello again');
return 'i did it!';
}
</script>
</head><body>hello!</body></html>";

}

private void button1_Click(object sender, EventArgs e)
{
object y = webBrowser1.Document.InvokeScript("doIt");
}
}

Calling javascript object method using WebBrowser.Document.InvokeScript

The example in the documentation does NOT include the parenthesis.

private void InvokeScript()
{
if (webBrowser1.Document != null)
{
HtmlDocument doc = webBrowser1.Document;
String str = doc.InvokeScript("test").ToString() ;
Object jscriptObj = doc.InvokeScript("testJScriptObject");
Object domOb = doc.InvokeScript("testElement");
}
}

Try

Document.InvokeMethod("obj.method");

Note that you can pass arguments if you use HtmlDocument.InvokeScript Method (String, Object[]).

Edit

Looks like you aren't the only one with this issue: HtmlDocument.InvokeScript - Calling a method of an object . You can make a "Proxy function" like the poster of that link suggests. Basically you have a function that invokes your object's function. It's not an ideal solution, but it'll definitely work. I'll continue looking to see if this is possible.

Another post on same issue: Using WebBrowser.Document.InvokeScript() to mess around with foreign JavaScript . Interesting solution proposed by C. Groß on CodeProject:

private string sendJS(string JScript) {
object[] args = {JScript};
return webBrowser1.Document.InvokeScript("eval",args).ToString();
}

You could make that an extension method on HtmlDocument and call that to run your function, only using this new function you WOULD include parenthesis, arguments, the whole nine yards in the string you pass in (since it is just passed along to an eval).

Looks like HtmlDocument does not have support for calling methods on existing objects. Only global functions. :(



Related Topics



Leave a reply



Submit