Tallan's Technology Blog

Tallan's Top Technologists Share Their Thoughts on Today's Technology Challenges

Sending Key Events to a WPF Web Browser (And Injecting Javascript)

Jordan Piscitelli

Recently I had the need to send KeyDown events to the WPF WebBrowser and after many failed attempts I finally figured out a good (or at least a working) solution. It should be noted that this solution does not trigger browser key events but rather triggers javascript key events, such as those bound by addEventListener. The technique utilizes javascript injection, so it could also be used to call any existing javascript method.

Note that for this technique, you will have to reference Microsoft.mshtml. This was not at all clear from the WPF Web Browser documentation and I thought it was worth mentioning.

Without further ado, the way I ended up sending key events to the WebBrowser is by injecting a javascript file that defines the function sendkey. In the C Sharp code I then use the Web Browser’s execScript method to execute my new function with the proper key. I could have sent the entire javascript code in the execScript call but as javascript syntax is fairly similar to C Sharp, I found myself escaping too many characters and the resulting code unreadable. So the best route was to create a text file containing the javascript and inject that into the control.

So I created the file JavascriptSource.txt and set its Copy to Output Directory to Copy Always to ensure it would travel with the binary. The source for the file is below:

function sendkey( key) {
var keyboardEvent = document.createEvent(KeyboardEvent);
var initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? initKeyboardEvent : initKeyEvent;
keyboardEvent[initMethod](keydown, true, true, window, key, 0, , false, en-US);
Object.defineProperty(keyboardEvent, 'which', {
get : function() {
return this.key;
}
});
document.dispatchEvent(keyboardEvent);
}

This script is fairly simple, it creates a javascript KeyboardEvent using document.createEvent, assigns it some very weird and specific values and dispatches the event. It also defines the property “which” which simply returns the keycode. This is just something the code I was trying to fire checked instead of the actual keycode property, thus I had to pretend like my event had added this value.

When the sendkey javascript function is called, it will only fire javascript event handlers, it will not fire browser related events (such as scrolling down the page) due to browser related security.

Before we can fire this method we must inject it into the WebBrowser’s DOM. To do this I wrote two methods, scriptToInject which returns the string representing the javascript script we want to inject and injectJavascript which actually injects the script.

So first let’s write scriptToInject. This should read our JavascriptSource.txt file and return it’s contents. Since we don’t want JavascriptSource to be a file the user has to select and we already copy it with the executable, we simply combine the executables location and the name of our file. Lastly we read the file and return the text.

public string scriptToInject()
{
string path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @JavascriptSource.txt);
return System.IO.File.ReadAllText(path);
}

Next we want to write the function that actually injects the javascript. This function takes in an HTMLDocument, creates the new script element, sets its text to the script to inject, finds the document body, gets its first child and inserts the script after it.

public void injectJavascript(HTMLDocument doc)
{
mshtml.IHTMLElement se = doc.createElement(script) as mshtml.IHTMLElement; //create the script
mshtml.IHTMLScriptElement element = (mshtml.IHTMLScriptElement)se; //cast it as a script element
element.text = scriptToInject(); //set it's text to the script to inject source
IHTMLElement body = doc.body as mshtml.IHTMLElement; //get the body element
IHTMLElement2 childhead = (IHTMLElement2) body.children[0]; //get it's first child
childhead.insertAdjacentElement(afterEnd, (IHTMLElement) element); //add the script element after the first child
}

To inject into a web browser you can call that method like so:

HTMLDocument test = (HTMLDocument)BrowserToInject.Document;
injectJavascript(test);

Now we can actually call our sendkey javascript method and trigger a simulated keyboard event!

public void sendKeyEvent(int key)
{
HTMLDocument test = (HTMLDocument)aWebBrowser.Document;
test.parentWindow.execScript(sendkey( +key.ToString() + ););
}

That’s it! You’re now sending keyboard events to javascript code!

An example javascript method that would be triggered by this sendkey function:

document.addEventListener(keydown, function (event) {
alert(Key down event:  + event.which);
});

No comments

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

\\\