Moving the blog

After almost 8 months of intermittent work and experiments, It’s finally official: the blog is being moved to a new self-hosted, completely independent location.
From now on the CodeVomit blog will be available at www.codevomit.xyz/bootlog, inside a brand new shining application built by me: Bootlog.

The site is currently in “open beta”, meaning that it is under active development. It may have bugs and be subject to obvious improvements. If you experience issues or have ideas about simple improvements, I guess you can just leave a comment. If you really want to piss me off, you can even open an issue on GitHub.

If you are curious about the reasons that made me switch from WordPress to a different application, you can read A brief history of MiBECoM, an excerpt from one of my failing attempts, and Why Bootlog, the first post I published in the new blog.

Finally, a closing remark. I could have used Jeckyll, you might object. Why not? The real reason is that I wanted to start a new project that could keep me busy and give me a good benchmark to improve my skills. The blog application seemed like the perfect way to combine the need for a new blogging platform with my will to work on a real world system. Not to mention that it feels good to build your own stuff.

Advertisements

How to grant farm-wide read access to a user in Sharepoint 2013

A few weeks ago I was asked to grant a particular user read rights to any resources of a Sharepoint farm; sites, folder, lists, etc…, irrespective of the permission currently set on those resources (i.e. possible unique permissions).

By the way, it appears that I’m also a particularly skilled Sharepoint administrator. It apparently happened over night, without me even noticing.

Unable to find any out-of-the-box solution for my particular situation, I came out with a bunch of scripts that did the trick and I think are worth sharing. Of course, the work I did has been mainly taping together snippets and scripts that I found somewhere else so the credit goes mostly to the Internet, as usual. There is a bit of original work, though.

The basic idea was to give the rights to a new group, GlobalReaders, created for the occasion in each root site, and then add the users to the group as needed. In the future, the users will be easily removed and the rights revoked, if needed.

1. Automating the creation of the GlobalReader group

If the farm has a large number of web applications and site collections, it can be hard to manually create the group. So the first step is automating this task. The following script scans all the sites of the farm and adds the desired group to their root web

function Add-GlobalReadersGroupsToAllSites
{
    $sites = Get-SPSite -Limit ALL
    Write-Host $sites
    foreach($site in $sites)
    {
        Write-Host "Adding GlobalReaders group to " $site.Url
        $site.RootWeb.SiteGroups.Add("GlobalReaders", $site.Owner, $site.Owner,
            "Members of this group has read rights on any resources of the farm (sites, lists, folders, etc...)")
        $site.RootWeb.Update()
    }
}

2. Assigning a group read rights on an SPWeb

The first brick of the procedure is a function that takes the name of a group and an SPWeb URL, and assign the group read rights over the Web itself. The script also takes into account folders and lists whose permission inheritance has been broken (unique permission).


# Assigns Read permission to a list of users for the Web provided
function Grant-ReadPermToGroup
{
    if($args.Length -lt 2)
    {
        Write-Error "Grant-ReadPermToUser requires 2 parameters. Usage: Grant-ReadPermToUser <SubSiteUrl> <GroupName>"
    }

    # Write-Host $args[0] " " $args[1]

    $web = Get-SPWeb -Identity $args[0]
    $site = $web.Site
    $groupList = $web.SiteGroups | Where { $_.Name -eq "GlobalReaders" }
    $readerGroup = $groupList[0]

    Write-Host "Assigning read permission for Web " $web "($($web.Url))"
    Write-Host "Group:"

    Write-Host "    "$readerGroup.Name

    Write-Host "Granting read permission to $readerGroup"
    $assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($readerGroup)
    $role = $site.RootWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)

    $assignment.RoleDefinitionBindings.Add($role);
    Write-Host $assignment
    $web.RoleAssignments.Add($assignment)

    # #########################################################################
    # Now let's deal with lists and folders with unique permission assignments

    # Finds all the lists of the given web that have unique role assignments
    # (broken inheritance)
    $uniqueAssignList = $web | select -ExpandProperty Lists |
        Where { -not $_.Hidden -and $_.EntityTypeName -ne "PublishedFeedList" -and $_.HasUniqueRoleAssignments}

    foreach($l in $uniqueAssignList)
    {
        Write-Host "Grantig read permission for list " $l.Title
        # Assign read permission
        # $role = $site.RootWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)
        $role = $l.ParentWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)
        Write-Host "Role: " $role
        $assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($readerGroup)
        $assignment.RoleDefinitionBindings.Add($role)

        $l.RoleAssignments.Add($assignment)
    }

    # Finds all the non-hidden folder (at any level)
    # of the given web that have unique role assignments
    # (broken inherintance)
    $uniqueFolders = $web |
        select -ExpandProperty Lists |
        Where { -not $_.Hidden -and $_.EntityTypeName -ne "PublishedFeedList"} |
        select -ExpandProperty Folders |
        Where { $_.HasUniqueRoleAssignments -and -not $_.Hidden } 

    foreach($f in $uniqueFolders)
    {
        Write-Host "Grantig read permission for folder " $f.Title
        # Assign read permission
        # $role = $site.RootWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)
        $role = $f.ParentList.ParentWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)
        Write-Host "Role: " $role
        $assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($readerGroup)
        $assignment.RoleDefinitionBindings.Add($role)

        $f.RoleAssignments.Add($assignment)
    }

    Write-Host "     OK! (^__^)"
}

Now we can easily cover the requirement for any SPWeb, provided that we can recursively loop over all of them (and we can, of course).

3. Recursively looping through all the webs

Once you assign the permission for a web, all the subweb are covered, unless they have unique permission. In this case, you have to individually take care of any subwebs that have unique permission (i.e. they do not inherit the permission from the parent because the inheritance has been broken by an administrator at some point in time):

# Grants read permission to a user for a Web and, recursively,
# all its sub-webs
function Grant-ReadPermRecursive
{
    if($args.Length -lt 2)
    {
        Write-Error "Parameters required: Web Url, Group Name"
    }
    # If runDry = True, the function walks the entire web tree, without actually adding new permissions
    $runDry = $false
    if($args.Length -ge 3)
    {
        $runDry = $args[2]
    }
    Write-Host "RunDry = "$runDry
    $web = Get-SPWeb -Identity $args[0]
    Write-Host "Web: " $web
    $groupName = $args[1]
    Write-Host "Username list: " $groupName
    if(!$runDry)
    {
        # Write-Host "I would actually grant permissions"
        Grant-ReadPermToGroup $web.Url $groupName
    }

    $subWebList = $web.Webs
    if($subWebList.Length -gt 0)
    {
        foreach($subWeb in $subWebList)
        {
            if($subWeb.HasUniquePerm)
            {
                Grant-ReadPermRecursive $subWeb.Url $groupName $runDry
            }
        }
    }
}

4. Wrapping it all

Finally, we can wrap the operation with a convenient function that operates on the farm level, looping over all the SPWebApplications and taking into account the root web of each:

$allFarmWebApplications = Get-SPWebApplication
foreach($webApplication in $allFarmWebApplications)
{
    Write-Host "WebApplication: " $webApplication.Url
    foreach($site in $webApplication.Sites)
    {
        Write-Host "   Site: "$site.Url "   RootWeb: "$site.RootWeb
        $rootWeb = $site.RootWeb
        Grant-ReadPermRecursive $rootWeb.Url "GlobalReaders" $false
        Write-Host "-----------------------------"
        Write-Host " "
    }
}

Pentaho Community Meeting 2015

Announcement! Announcement!

pcm-2015-logo

On the 7th of November, 2015, I will be presenting at the Pentaho Community Meeting (PCM) that will be held in London.

I and my former colleague Francesco Corti will be presenting a plugin for Pentaho that allows an external web application to transparently have a user authenticated in Pentaho.

For further details about the meeting check this out.

Some details about the project

The project is an extension to the security and authentication layer of Pentaho and the related Spring Security filter chain. The basic need behind the project is allowing an external application to redirect a user to Pentaho, without him (or her) having to type the username and password, much like in a single sign on fashion, but without the hassle of a fully featured SSO infrastructure.

The final name of the plugin is yet to be decided but it will probably be “Pentaho Transparent Authentication”, although at the moment the Java project’s name is “pentaho-authentication-ext”. The final name is Pentaho Transparent Authentication.

The source code of the project is available here. Please keep in mind that it is still under active development.

Francesco has already written an excellent guide on how to install, use and test the plugin. Take a look!

We expect to release the plugin to the Pentaho Marketplace in the following weeks, right before the presentation, so that it will be immediately available for everyone to try out. Despite what the readme file says, there will be a shiny and fancy installer in the form of a Sparkl application. [EDIT: the installer is in place and the releases 1.0 (for Pentaho 5.4) and 1.1 (for Pentaho 6.0) have been published on Github. We are currently waiting for the review and approval process of the Pentaho Marketplace. In the meanwhile, you can download and unpack the zip file into the system folder of a Pentaho instance. Refer to the readme file and to the aforementioned guide for further instructions]

If you have any curiosities or feedbacks, please do not hesitate to leave a comment.

I began using Eclipse Mars and…

Today I downloaded and started using the latest version of Eclipse, called Mars. I began with a copy of a project I’m currently working on, a Java Web application that uses, among the other things, Java Persistence API (JPA) and Hibernate.

The first things I saw after importing the project into the workspace was this:

jpa1

On any class annotated with @Entity that used a @GeneratedValue on its primary key field. Not a good start…

The reported error was:

No generator named “increment” is defined in the persistence unit.jpa2

…even if the generator is defined right there with the @GenericGenerator annotation.

I searched the Internet for a couple of minutes without success, than the illumination: it’s a validation error. In fact, it does not prevent building the project, nor running it.

This is what you need to do to make the error go away and live easily again: open Windows -> Preferences and filter by “validation”; in the list, search for “JPA”, and uncheck the relative checkboxes. The annoying validation stops being performed.

jpa3

Besides, I’m not even sure that it is an actual error. Eclipse Luna does not report it; Maven, Hibernate, Jetty or Tomcat do not complain about it in any way. Is it a bug of Eclipse Mars? Well, probably I’m not going to research any further on this subject…

WPF WebBrowser control – part 3

Showing the Windows 8 touch keyboard

The WPF WebBrowser control does not natively support touchscreens. To make an application easily usable even on touch devices, you need to provide at least one capability: the tablet keyboard must appear whenever the user touches an editable field and moves the focus there.

In part 2 we saw how you can register to DOM events and handle them in .NET code. Ideally, to make the keyboard appear, you will register a handler for every significant field (input, textarea, etc…) and lauch the keyboard process in the body of the callback.

Making the keyboard appear is quite trivial. In Windows 8/8.1, it is as simple as starting the executable at C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe. The following code will do the trick:


    public static void ShowTouchKeyboard()
    {
        using (var keyboard = new Process())
        {
            keyboard.StartInfo = new ProcessStartInfo(@"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe", "/ManualLaunch");
            keyboard.Start();
        }
    }

Once the field has lost the focus, you may want to hide the keyboard. This is a bit more complicated. First, also for this case, you need to register to the appropriate DOM event on the managed elements (e.g. onblur). Then, you need to rely on the Windows API in order to find the window corresponding to the keyboard process and send it a message to close it. The following code fragment can be added to the WebBrowserAdapter class that we already know from part 2 of this series:


public static readonly int WM_SYSCOMMAND = 274;
public static readonly uint SC_CLOSE = 61536;

[DllImport("user32.dll")]
public static extern IntPtr FindWindow(String sClassName, String sAppName);

[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr KeyboardWnd, int WM_SYSCOMMAND, uint SC_CLOSE, int p);

public static void ShowTouchKeyboard()
{
    using (var keyboard = new Process())
    {
        keyboard.StartInfo = new ProcessStartInfo(@"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe", "/ManualLaunch");
        keyboard.Start();
    }
}

public static void CloseTouchKeyboard()
{
    IntPtr keyboardWnd = FindWindow("IPTip_Main_Window", null);
    if (!keyboardWnd.Equals(IntPtr.Zero))
    {
        PostMessage(keyboardWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
    }
}

My new favourite thing: ASM

[This could also have been titled ANTLR4 project with Maven – Tutorial (episode 4)]

[The full source code is here on github]

Introduction

ASM is:

an all purpose Java bytecode manipulation and analysis framework. It can be used to modify existing classes or dynamically generate classes, directly in binary form. Provided common transformations and analysis algorithms allow to easily assemble custom complex transformations and code analysis tools.

It has several uses but the most remarkable is the ability to easily output Java Bytecode and dump byte array representations of class files.

In order to allow this, ASM has a set of handy APIs and a couple of tools that guides you by examples, rather that teaching you up front a mass of notions. I’ve recently used ASM to build the next step of my ANTLR4 Maven tutorial: an essential compiler that translates parsed expressions into java classes (i.e. .class files). I point you to this branch if you want to take a look at the complete source code.

A lot of cool staff out there uses ASM, in particular Groovy and Clojure, just to mention two main representatives of the JVM languages world, use ASM to compile to class files.

Before starting using ASM a couple of preparatory activities are needed. The first is intalling the Bytecode Outline plugin for Eclipse. This will become your main educational tool. The most useful thing that it can do is generating the source code of a Java class that will output the bytecode of another Java class. To be more clear, if you want to know how to generate the bytecode for a certain class, or method, or block, etc…, you write the source code of the Java class whose bytecode you would like to create, then you inspect it with the Bytecode Outline plugin, and it generates the Java code that you should write in order to builds the corresponding bytecode.

Imagine that I want to output the bytecode that corresponds to the following Java class:


public class SimulatedCompiledExpression
{
	public double compute(){
		return compute_0();
	}
	
	public double compute_0(){
		return compute_01() * compute_02();
	}
	
	public double compute_01(){
		return 2.0D;
	}
	
	public double compute_02(){
		return 3.0;
	}
}

once I have written and compiled the class in Eclipse, I open the Bytecode view (Window -> Show View)

show-view

and it tells me this:

bytecode

If you take the Java class generated on the right panel, compile it and call its dump method, what you get is the bytecode corresponding to the SimulatedCompiledExpression class.

The second preparatory step is specific to my example. Since you want to be able to test the compiled classes on the fly, a custom class loader is useful that could load a class directly from its raw byte array representation. It did something very basic but it’s enough to allow unit tests:

package org.merka.arithmetic.classloader;

import org.apache.commons.lang.StringUtils;

public class ByteArrayClassLoader extends ClassLoader
{
	private byte[] rawClass;
	private String name;
	private Class<?> clazz; 
			
	public ByteArrayClassLoader(byte[] rawClass, String name)
	{
		if(StringUtils.isBlank(name)){
			throw new IllegalArgumentException("name");
		}
		if(rawClass == null){
			throw new IllegalArgumentException("rawClass");
		}
		this.rawClass = rawClass;
		this.name = name;
	}
	
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException
	{
		if(this.name.equals(name)){			
			return defineClass(this.name, this.rawClass, 0, this.rawClass.length);
		}
		return super.findClass(name);
	}
}

Maven dependency for ASM

To have the full power of ASM at your disposal in a Maven project, add the following dependency in the pom.xml file:


<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm-all</artifactId>
    <version>${asm.version}</version>
</dependency>

The latest version (and the one I used in this tutorial) is 5.0.4.

Compilation

The idea for this example is to translate every production of the language in a method that returns the result of the evaluation of the correspondig subtree. I know it’s totally useless but, again, this is just a tutorial to learn how ASM works. I’ve never claimed that the entire arithmetic example was practically usefull in the first place.

Having an expression like “2 * 3”, I would like to create a class that corresponds to the one I’ve just reported previously (look at the SimulatedCompiledExpression above). Every time I did not know how to use the ASM APIs to accomplish my task, I just wrote the Java code correspondig to the ideal result I wanted, checked with Bytecode Outline and then went back to my bytecode generation code.

The actual work of translating expressions into bytecode is done by the NaiveCompilerVisitor. For each significant production, it creates a method that computes and returns the value of its subtree. The visitor is defined as a subclass of ArithmeticBaseVisitor because each visit method returns the name of the method it just created, so that it can be used by the parent level.

Let’s see some code:

	public String visitProgram(ProgramContext ctx)
	{ 
		// builds the prolog of the class
//		FieldVisitor fv;
		MethodVisitor mv;
//		AnnotationVisitor av0;
		
		traceClassVisitor.visit(V1_7, ACC_PUBLIC + ACC_SUPER,
				getQualifiedName(), null, "java/lang/Object",
				null);

		traceClassVisitor.visitSource(className + ".java", null);
		
		// builds the default constructor
		{
			// [here goes the code obtained from the bytecode outline,
			//   slightly modified to fit our needs]
			mv = traceClassVisitor.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
			mv.visitCode();
			Label l0 = new Label();
			mv.visitLabel(l0);
			//mv.visitLineNumber(3, l0);
			mv.visitVarInsn(ALOAD, 0);
			mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
			mv.visitInsn(RETURN);
			Label l1 = new Label();
			mv.visitLabel(l1);
			mv.visitLocalVariable("this", getStackQualifiedName(),
					null, l0, l1, 0);
			mv.visitMaxs(1, 1);
			mv.visitEnd();
		}
		
		// passes itself into the child node
		String innerMethodName = ctx.expression().accept(this);
		
		// creates a top level method named "compute"
		// that internally calls the previous generated innerMethodName
		{	
			// [here goes the code obtained from the bytecode outline,
			//   slightly modified to fit our needs]
			mv = classWriter.visitMethod(ACC_PUBLIC, "compute", "()D", null, null);
			mv.visitCode();
			Label l0 = new Label();
			mv.visitLabel(l0);
			//mv.visitLineNumber(14, l0);
			mv.visitVarInsn(ALOAD, 0);
			mv.visitMethodInsn(INVOKEVIRTUAL, getQualifiedName(), innerMethodName, "()D", false);
			mv.visitInsn(DRETURN);
			Label l1 = new Label();
			mv.visitLabel(l1);
			mv.visitLocalVariable("this", getStackQualifiedName(), null, l0, l1, 0);
			mv.visitMaxs(2, 1);
			mv.visitEnd();
		}
		
		// build the epilog of the class
		traceClassVisitor.visitEnd();
		return "compute";
	}

The code in the top level visit method shown here, writes the bytecode that defines a class, a default constructor and a public method named “compute”. The result of this code alone, translated in Java, would look like this:

public class <TheClassName>;
{
	public double compute(){
		return compute_0(); // "compute_0" is the name returned by ctx.expression().accept(this), see line 35 of the previous snippet
        } 
} 

At line 35 you see the visitor starting the recursion into the subtree. Each subnode, once visited, enriches the class with a new method and returns the name of it, so that it can be employed by the parent level. At the end of the visit, the getRawClass method of the NaiveCompilerVisitor returns the raw byte representation of the class: it can be saved as a .class file (then it becomes a totally legitimate class) or loaded on the fly by the ByteArrayClassLoader.

Let’s see another visit method. From now on you can realize that the code is really similar to that of NaiveInterpreterVisitor:

public String visitAlgebraicSum(AlgebraicSumContext ctx)
	{
		int byteCodeOp = -1;
		String operand = ctx.getChild(1).getText();
		if(operand.equals("+")){
			byteCodeOp = DADD;
		}
		else if(operand.equals("-")){
			byteCodeOp = DSUB;
		}
		else
		{
			throw new ArithmeticException("Something has really gone wrong");
		}
		
		String leftArgumentMethod = ctx.expression(0).accept(this);
		String rightArgumentMethod = ctx.expression(1).accept(this);
		
		// builds a method whose body is
		// 'return <leftArgumentMethod>() + rightArgumentMethod()'
		
		String currentMethodName = getNextMethodName();
		MethodVisitor methodVisitor;
		{
			methodVisitor = classWriter.visitMethod(ACC_PUBLIC, currentMethodName, "()D", null, null);
			methodVisitor.visitCode();
			Label l0 = new Label();
			methodVisitor.visitLabel(l0);

			methodVisitor.visitVarInsn(ALOAD, 0);
			methodVisitor.visitMethodInsn(INVOKEVIRTUAL, getQualifiedName(), leftArgumentMethod, "()D", false);
			methodVisitor.visitVarInsn(ALOAD, 0);
			methodVisitor.visitMethodInsn(INVOKEVIRTUAL, getQualifiedName(), rightArgumentMethod, "()D", false);
			methodVisitor.visitInsn(byteCodeOp);
			methodVisitor.visitInsn(DRETURN);
			Label l1 = new Label();
			methodVisitor.visitLabel(l1);
			methodVisitor.visitLocalVariable("this", getStackQualifiedName(), null, l0, l1, 0);
			methodVisitor.visitMaxs(4, 1);
			methodVisitor.visitEnd();
		}
		
		return currentMethodName;
	}

The idea is the same: first we visit each subtree of the AlgebraicSumContextNode. Each visit creates a method in the output bytecode and returns its name to the parent level. Then we use those names in the generation of the current method (line 31 and 33). As the comment states, the goal here is to have a bytecode method whose body is equivalent to the Java statement:

return <leftArgumentMethod>() (+ | -) <rightArgumentMethod>();

Test

A unit test might help understand how such a visitor can be used by client code:

	@Test
	public void testWriteClass() throws Exception
	{
		String program = "1 + 1 + 1 * 2 * (4+2) * 2 - (1 + 1 - 4 + 1 +1 ) * 2 / 3 / 3 / 3"; // "4 + 1";
		TestArithmeticParser.ArithmeticTestErrorListener errorListener = new TestArithmeticParser.ArithmeticTestErrorListener();
		ProgramContext parseTreeRoot = TestArithmeticParser.parseProgram(program, errorListener);

		NaiveCompilerVisitor visitor = new NaiveCompilerVisitor("org.merka.onthefly",
				"CompiledExpression");

		visitor.visit(parseTreeRoot);
		byte[] rawClass = visitor.getRawClass();
		
		File file = new File("target/org/merka/onthefly/CompiledExpression.class");
		FileUtils.writeByteArrayToFile(file, rawClass);
	}

As usual, first we parse the program (line 6) then we create an instance of the compiler visitor that takes as parameter the name of the package and the simple name of the class to be generated (line 8). We visit the parse tree (line 11), then we get the resulting bytecode as a byte array (line 12). This is the actual content of a class file. We can save it to a file inside the expected folder structure (line 15): now we can use this class as we would do with any other class. In fact, you can also try and open it in Eclipse, and this is what you get:

bytecode2

nice and valid java bytecode.

On the other side, you can generate and load classes on the fly. To do this, I use my custom ByteArrayClassLoader and a bit of reflection, since none of the generated types are known at compile time:

	@Test
	public void testOnTheFly() throws Exception
	{
		String tempPackage = "org.merka.onthefly";
		String program = "2 + 3";
		double result = evaluateClassOnTheFly(program, tempPackage, "CompiledSum");
		assertEquals("result of current program: '" + program + "'", 5, result, 0.00001);
	}

	public double evaluateClassOnTheFly(String program, String packageName, String className) throws Exception
	{
		TestArithmeticParser.ArithmeticTestErrorListener errorListener = new TestArithmeticParser.ArithmeticTestErrorListener();
		ProgramContext parseTreeRoot = TestArithmeticParser.parseProgram(program, errorListener);

		NaiveCompilerVisitor visitor = new NaiveCompilerVisitor(packageName,
				className);

		visitor.visit(parseTreeRoot);
		byte[] rawClass = visitor.getRawClass();
		String name = packageName + "." + className;
		ByteArrayClassLoader classLoader = new ByteArrayClassLoader(rawClass, name);
		Class<?> compiledClass = classLoader.loadClass(name);

		assertNotNull(compiledClass);

		Object instance = compiledClass.newInstance();
		Class<?>[] parameterTypes = new Class<?>[0];
		Method computeMethod = compiledClass.getMethod("compute", parameterTypes);
		Object[] args = new Object[0];
		double result = (double) computeMethod.invoke(instance, args);
		return result;
	}

WPF WebBrowser control – part 2

Registering .NET handlers to DOM elements’ events

With an approach similar to the previous one (see part 1), we can register callbacks that get executed when a DOM event is triggered. Simply put: we can execute .NET code in response, for example, to a click on a HTML input text element. To do this you need to add the MSHTML library to the project references, like I showed in part 1.

As before, let’s assume that we have a simple UserControl that uses a WebBrowser:


<UserControl x:Class="WebBrowserExample.WebBrowserAdapter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <WebBrowser x:Name="WebBrowserControl"></WebBrowser>
    </Grid>
</UserControl>

and we add our UserControl to the main window, just like this:


<Window x:Class="WebBrowserExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:cont="clr-namespace:WebBrowserExample">
    <Grid>
        <cont:WebBrowserAdapter></cont:WebBrowserAdapter>
    </Grid>
</Window>

We can now use types and classes from mshtlm to directly access the DOM of a HTML page. The following snippet reports the complete code of the WebBrowserAdapter class:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using mshtml;

namespace WebBrowserExample
{
    /// <summary>
    /// Interaction logic for WebBrowserAdapter.xaml
    /// </summary>
    public partial class WebBrowserAdapter : UserControl
    {
        public WebBrowserAdapter()
        {
            InitializeComponent();
            this.Loaded += WebBrowserAdapter_Loaded;
        }

        void WebBrowserAdapter_Loaded(object sender, RoutedEventArgs e)
        {
            WebBrowserControl.LoadCompleted += WebBrowserControl_LoadCompleted;
            WebBrowserControl.Navigate("http://www.google.com");
        }

        void WebBrowserControl_LoadCompleted(object sender, NavigationEventArgs e)
        {
            HookHTMLElements();
        }

        private void HookHTMLElements()
        {
            var document = WebBrowserControl.Document as HTMLDocument;
            var inputElements = document.getElementsByTagName("input");
            foreach (var item in inputElements)
            {
                DispHTMLInputElement inputElement = item as DispHTMLInputElement;
                if (inputElement.type == "text" 
                    || inputElement.type == "password" 
                    || inputElement.type == "search")
                {
                    HTMLButtonElementEvents_Event htmlButtonEvent = inputElement as HTMLButtonElementEvents_Event;
                    htmlButtonEvent.onclick += FocusCallback;
                }
            }
        }

        public bool FocusCallback()
        {
            Console.WriteLine("Callback method executed!");
            return true;
        }
    }
}


In the constructor, WebBrowserAdapter registers itself as a listener to its Loaded event. This event is fired when the user control is rendered and ready for interaction. In the WebBrowserAdapter_Loaded it begins to prepare the WebBrowser control: first, it subscribes to the LoadCompleted event of the WebBrowser: the WebBrowser fires this event whenever a navigation successfully ends. Then it makes the WebBrowser navigate to the Google home page, calling the Navigate method.

When the WebBrowserControl_LoadCompleted method gets executed, we can be sure that the WebBrowser holds a valid HTML document and we can safely call its Document property and manipulate the object return. This is what we do in the HookHTMLElements method: here we go retrieve all the elements that have tag name equal to “input”, much like we would do in a Javascript function (in fact, if you take a look at the methods the intellisense proposes you, you will see that they match the Javascript APIs), and we attach .NET callbacks to the DOM events. Now, if you click on the input text box of the Google home page, the FocusCallback method is called and a line is written on the standard output (see pictures below).

callback02