Using JavaScript Frameworks inside C# with ChakraBridge

Share this article

Key Takeaways

  • ChakraBridge enables C# developers to utilize JavaScript frameworks within C# applications by leveraging the Chakra JavaScript engine from Microsoft Edge.
  • The integration does not support HTML or CSS directly, meaning JavaScript frameworks that do not rely on DOM or CSS are suitable for use with ChakraBridge.
  • Developers can debug JavaScript code within C# applications using Visual Studio, although they must navigate through executed code in debug mode via the Script Documents section.
  • ChakraBridge provides additional JavaScript tools like XMLHttpRequest and setTimeout, which are typically available in web browsers, to enhance functionality within C# applications.
  • The project is open for further community contributions and enhancements, potentially expanding to support more JavaScript features and frameworks in the future.
This article is part of a web development series from Microsoft. Thank you for supporting the partners who make SitePoint possible. JavaScript has, without doubt, the most vibrant ecosystem out there. There are gazillions of new frameworks released every month (https://www.javascripting.com/). A new javascript framework is coming As a C# developer – even with a great, active C# community – you may sometimes find yourself a little bit jealous. What if we could bring the JavaScript language and ecosystem also into the C# world? What if a C# developer could use JavaScript inside C#? Fret not! I’m thrilled to announce a new WinRT project I’ve created – ChakraBridge – which will allow you to get invited to the party like any web developers. ChakraBridge Indeed, thanks to Chakra (the JavaScript engine used by Microsoft Edge), it is now possible to host one of the fastest JavaScript engines (and also the one with the highest support of ECMAScript 6) inside any Universal Windows Platform application. ChakraBridge embeds Chakra engine in a WinRT application and provides all required high level tools to use it seamlessly in a C# / UWP application. People developing a HTML/JS/CSS based UWP App (WWA or hosted app in the old world) don’t need to host Chakra separately as it’s already a part of the sandbox.

How to Use It?

This is pretty simple: just head to https://github.com/deltakosh/JsBridge and clone the project to your hard drive. Now you have two options: you can either add the ChakraBridge project (which is a WinRT library) in your solution or you can reference ChakraBridge.winmd from /dist folder.

Initializing Chakra

Once referenced, you can call these lines of code to get Chakra ready to use:
host = new ChakraHost();
The variable named host is your JavaScript context. You may also want to be able to trace the messages sent to the JavaScript console. To do so, please add this code:
Console.OnLog += Console_OnLog;
Once connected, this event handler will be called everytime the JavaScript code executes “console.log()“.

Which JavaScript frameworks can I use?

Before defining what you can do, you have to understand that Chakra is a JavaScript engine which means that you can execute JavaScript code in your app but there is nothing related to HTML or CSS. You can then pick any framework not related to HTML (DOM operations) or CSS. Here are some examples (but there are MANY MANY more): Once you’ve picked the framework that you want to use, you have to inject it into your Chakra context. In my case I wanted to use CDC (CloudDataConnector) because I needed a way to seamlessly connect to various cloud data providers (Amazon, Azure, CouchDB, etc..). You can either download the .js files and embed them in your project or download them every time you launch your application:
await ReadAndExecute("cdc.js");
await ReadAndExecute("azuremobileservices.js");
await ReadAndExecute("cdc-azuremobileservices.js");
await ReadAndExecute("sample.js");
You can replace ReadAndExecute by DownloadAndExecute if you prefer referencing live .js files Now your JavaScript context has compiled and executed the referenced files. Please note that “sample.js” is a custom JavaScript file which contains the client code for my application:

varCDCAzureMobileService = new CloudDataConnector.AzureDataService();

varCDCService = new CloudDataConnector.DataService(new CloudDataConnector.OfflineService(), new CloudDataConnector.ConnectivityService());
CDCAzureMobileService.addSource('https://angularpeoplev2.azure-mobile.net/', 'xxxxxxx', ['people']);

CDCService.addSource(CDCAzureMobileService);

vardataContext = {};

varonUpdateDataContext = function (data) {
	if(data &&data.length) {
		syncPeople(data);
    }
}

varsyncPeople = function (data) {
	sendToHost(JSON.stringify(data), "People[]");
}

CDCService.connect(function (results) {
	if (results === false) {
		console.log("CDCService must first be successfully initialized");
	} else {
		console.log("CDCService is good to go!");
    }
}, dataContext, onUpdateDataContext, 3);
Nothing fancy here, I’m just using CDC to connect to an Azure mobile service in order to get a list of people.

Getting data back from JavaScript world

Next, I’ll get my data back from the JavaScript context. As you may have seen in the “sample.js” file, when the data context is updated, I’m calling a global function called sendToHost. This function is provided by ChakraBridge to allow you to communicate with the C# host. To get it working, you have to define what types can be sent from JavaScript:
CommunicationManager.RegisterType(typeof(People[]));
So now when sendToHost is called from JavaScript context, a specific event will be raised on C# side:

CommunicationManager.OnObjectReceived = (data) =>
{
	varpeopleList = (People[])data;
	peopleCollection = new ObservableCollection<People>(peopleList);

	peopleCollection.CollectionChanged += PeopleCollection_CollectionChanged;

	GridView.ItemsSource = peopleCollection;
	WaitGrid.Visibility = Visibility.Collapsed;
};
Obviously you are responsible for the mapping between your JavaScript object and your C# type (same properties names).

Calling JavaScript functions

On the other hand you may want to call specific functions in your JavaScript context from your C# code. Think, for instance, about committing a transaction or adding a new object. So first let’s create a function for a specific task in our “sample.js” file:

commitFunction = function () {
	CDCService.commit(function () {
		console.log('Commit successful');
	}, function (e) {
		console.log('Error during commit');
	});
}
To call this function from C#, you can use this code:
host.CallFunction("commitFunction");
If your function accepts parameters, you can pass them as well:
host.CallFunction("deleteFunction", people.Id);
The current version of ChakraBridge accepts int, double, bool and string types.

Debugging in the JavaScript context

Thanks to Visual Studio, it is still possible to debug your JavaScript code even if you are now in a C# application. To do so, you first have to enable script debugging in the project properties: Debugging JavaScript Then, you can set a breakpoint in your JavaScript code. But there is a trick to know: You cannot set this breakpoint in the files in your project as they are here just as a source. You have to reach the executed code through the Script Documents part of the Solution Explorer when running in debug mode: Debugging JavaScript

How does it work?

Interop

Let’s now discuss how things work under the hood. Basically, Chakra is based on a Win32 library located at “C:\Windows\System32\Chakra.dll” on every Windows 10 desktop devices. So the idea here is to provide a internal C# class that will embed all entry points to the DLL through DllImport attributes:
internal static class Native
{
        [DllImport("Chakra.dll")]
internal static extern JavaScriptErrorCodeJsCreateRuntime(JavaScriptRuntimeAttributesattributes, 
JavaScriptThreadServiceCallbackthreadService, out JavaScriptRuntimeruntime);

        [DllImport("Chakra.dll")]
internal static extern JavaScriptErrorCodeJsCollectGarbage(JavaScriptRuntimehandle);

        [DllImport("Chakra.dll")]
internal static extern JavaScriptErrorCodeJsDisposeRuntime(JavaScriptRuntimehandle);
The list of available functions is pretty long. ChakraBridge is here to encapsulate these functions and provide a higher level abstraction. Other option to consider here: you can also use Rob Paveza’s great wrapper called js-rtwinrt: https://github.com/robpaveza/jsrt-winrt. It’s higher-level than the pure Chakra engine and it avoids needing P/Invoke.

Providing missing pieces

One important point to understand is that Chakra only provides the JavaScript engine. But you, as the host, have to provide tools used alongside JavaScript. These tools are usually provided by browsers (think about C# without .NET). For instance, XmlHttpRequest object or setTimeout function are not part of JavaScript language. They are tools used BY the JavaScript language in the context of your browser. To allow you to use JavaScript frameworks, ChakraBridge provides some of these tools. This is an ongoing process and more tools will be added to ChakraBridge in the future by me or the community. Let’s now have a look at the implementation of XmlHttpRequest:

using System;
using System.Collections.Generic;
using System.Net.Http;

namespace ChakraBridge {
	public delegate void XHREventHandler();

	public sealed class XMLHttpRequest {
		readonlyDictionary<string, string> headers = new Dictionary<string, string>();
		Uri uri;
		string httpMethod;
		private int_readyState;

		public intreadyState {
			get{
				return _readyState;
			}
			private set {
				_readyState = value;
				try {
					onreadystatechange?.Invoke();
				}
				catch {}
			}
		}

		public string response =>responseText;

		public string responseText {
			get;
			private set;
		}

		public string responseType {
			get;
			private set;
		}

		public bool withCredentials { get; set; }

		public XHREventHandleronreadystatechange { get; set; }

		public void setRequestHeader(string key, string value) {
			headers[key] = value;
		}

		public string getResponseHeader(string key) {
			if(headers.ContainsKey(key)) {
				return headers[key];
		    }
			return null;
		}

		public void open(string method, string url) {
			httpMethod = method;
			uri = new Uri(url);

			readyState = 1;
		}

		public void send(string data) {
			SendAsync(data);
		}

		async void SendAsync(string data) {
			using(varhttpClient = new HttpClient()) {
				foreach(varheader in headers) {
					if(header.Key.StartsWith("Content")) {
						continue;
					}
					httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
		        }

				readyState = 2;

				HttpResponseMessageresponseMessage = null;

				switch (httpMethod) {
					case "DELETE":
					responseMessage = await httpClient.DeleteAsync(uri);
					break;

					case "PATCH":

					case "POST":
					responseMessage = await httpClient.PostAsync(uri, new StringContent(data));
					break;

					case "GET":
					responseMessage = await httpClient.GetAsync(uri);
					break;
		        }

				if(responseMessage != null) {
					using (responseMessage) {
						using (varcontent = responseMessage.Content) {
							responseType = "text";
							responseText = await content.ReadAsStringAsync();
							readyState = 4;
		                }
		            }
		        }
		    }
		}
    }
}
As you can see, the XmlHttpRequest
class uses internally a HttpClient and uses it to mimic the XmlHttpRequest object that you can find in a browser or in node.js. This class is then projected (literally) to the JavaScript context:
Native.JsProjectWinRTNamespace("ChakraBridge");
Actually, the entire namespace is projected as there is no way to project only a single class. So a JavaScript is then executed to move the XmlHttpRequest object to the global object:
RunScript("XMLHttpRequest = ChakraBridge.XMLHttpRequest;");

Handling garbage collections

One of the pitfalls you may face if you decide to extend ChakraBridge is garbage collection. Indeed, the JavaScript garbage collector has no idea of what is happening outside of its own context. So for instance, let’s see how the setTimeout function is developed:

internal static class SetTimeout {
	public static JavaScriptValueSetTimeoutJavaScriptNativeFunction(JavaScriptValuecallee, bool isConstructCall, 
	[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] JavaScriptValue[] arguments, 
	ushortargumentCount, IntPtrcallbackData) {
		// setTimeout signature is (callback, after)
		JavaScriptValuecallbackValue = arguments[1];

		JavaScriptValueafterValue = arguments[2].ConvertToNumber();
		varafter = Math.Max(afterValue.ToDouble(), 1);

		uintrefCount;
		Native.JsAddRef(callbackValue, out refCount);
		Native.JsAddRef(callee, out refCount);

		ExecuteAsync((int)after, callbackValue, callee);

		return JavaScriptValue.True;
	}

	staticasync void ExecuteAsync(intdelay, JavaScriptValuecallbackValue, JavaScriptValuecallee) {
		await Task.Delay(delay);
		callbackValue.CallFunction(callee);
		uintrefCount;
		Native.JsRelease(callbackValue, out refCount);
		Native.JsRelease(callee, out refCount);
	}
}
SetTimeoutJavaScriptNativeFunction is the method that will be projected inside JavaScript context. You can note that every parameter is gathered as a JavaScriptValue and then cast to the expected value. For the callback function (callbackValue), we have to indicate to JavaScript garbage collector that we hold a reference so it could not free this variable even if no one is holding it inside JavaScript context:
Native.JsAddRef(callbackValue, out refCount);
The reference has to be released once the callback is called:
Native.JsRelease(callbackValue, out refCount);
On the other hand, C# garbage collector has no idea of what is happening inside the Chakra black box. So you have to take care of keeping reference to objects or functions that you project into the JavaScript context. In the specific case of setTimeout implementation, you first have to create a static field that point to your C# method just to keep a reference on it.

Why not use a Webview?

This is a valid question that you may ask. Using only Chakra provides some great advantages:
  • Memory footprint: No need to embed HTML and CSS engines as we already have XAML.
  • Performance: We can directly control JavaScript context and, for instance, call JavaScript function without having to go through a complex process like with the webview.
  • Simplicity: The webview needs to navigate to a page to execute JavaScript. There is no straightforward way to just execute JavaScript code.
  • Control: By providing our own tools (like XHR or setTimeout), we have a high level of granularity to control what JavaScript can do.

Going Further

Thanks to Chakra engine, this is the beginning of a great collaboration between C#, XAML and JavaScript. Depending on the community response, I plan to add more features in the ChakraBridge project to be able to handle more JavaScript frameworks (for instance, it could be great to add support for canvas drawing in order to be able to use all awesome charting frameworks available for JavaScript). If you are interested in reading more about Chakra itself you can go to the official Chakra samples repository: https://github.com/Microsoft/Chakra-Samples. You may also find these links interesting:

More hands-on with Web Development

This article is part of the web development series from Microsoft tech evangelists on practical JavaScript learning, open source projects, and interoperability best practices including Microsoft Edge browser and the new EdgeHTML rendering engine. We encourage you to test across browsers and devices including Microsoft Edge – the default browser for Windows 10 – with free tools on dev.modern.IE: More in-depth learning from our engineers and evangelists: Our community open source projects: More free tools and back-end web dev stuff:

Frequently Asked Questions about Using JavaScript Frameworks Inside C# with ChakraBridge

How can I install ChakraBridge in my C# project?

To install ChakraBridge in your C# project, you need to use the NuGet package manager. Open your project in Visual Studio, go to the ‘Tools’ menu, select ‘NuGet Package Manager’, and then ‘Manage NuGet Packages for Solution’. In the search bar, type ‘ChakraBridge’ and install it. Remember to add the reference to your project after installation.

Can I use other JavaScript frameworks with ChakraBridge?

Yes, ChakraBridge allows you to use any JavaScript framework that can run in a browser. This includes popular frameworks like React, Angular, and Vue.js. You just need to include the JavaScript file of the framework in your project and reference it in your code.

How can I debug JavaScript code in ChakraBridge?

Debugging JavaScript code in ChakraBridge can be a bit tricky as it doesn’t provide a built-in debugger. However, you can use console.log statements for debugging. The output of these statements can be captured in C# using the ‘JavaScriptConsole’ class provided by ChakraBridge.

Is it possible to use ChakraBridge in a WinForms application?

Yes, ChakraBridge can be used in a WinForms application. You can create a new instance of the ‘JavaScriptRuntime’ class and execute JavaScript code using the ‘RunScript’ method. The result can be displayed in a WinForms control.

How can I handle JavaScript errors in ChakraBridge?

ChakraBridge provides the ‘JavaScriptError’ class to handle JavaScript errors. You can catch these errors in a try-catch block in your C# code. The ‘JavaScriptError’ class provides information about the error, including the error message and the line number where the error occurred.

Can I use ChakraBridge to run Node.js code?

No, ChakraBridge is designed to run JavaScript code in a browser-like environment. It doesn’t support Node.js specific features like file system access or network requests. However, you can use Edge.js if you need to run Node.js code in a C# application.

How can I call a C# method from JavaScript in ChakraBridge?

ChakraBridge allows you to expose C# methods to JavaScript. You can do this by creating a JavaScript function that calls a C# method using the ‘JavaScriptNativeFunction’ class. This function can then be called from your JavaScript code.

Is ChakraBridge compatible with .NET Core?

No, ChakraBridge is not compatible with .NET Core. It is a .NET Framework library and requires the full .NET Framework to run. If you need to run JavaScript in a .NET Core application, you can use Jint or ClearScript.

Can I use ChakraBridge in a WPF application?

Yes, ChakraBridge can be used in a WPF application. The process is similar to using it in a WinForms application. You can execute JavaScript code and display the result in a WPF control.

How can I update the version of JavaScript used by ChakraBridge?

ChakraBridge uses the Chakra JavaScript engine provided by Microsoft. The version of JavaScript used by ChakraBridge depends on the version of the Chakra engine installed on your system. To update the version of JavaScript, you need to update the Chakra engine.

David CatuheDavid Catuhe
View Author

David Catuhe is a Principal Program Manager at Microsoft focusing on web development. He is author of the babylon.js framework for building 3D games with HTML5 and WebGL. Read his blog on MSDN or follow him on Twitter.

CchakraJavaScriptjs frameworkmdn
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week