Communicate with a Native App from Chrome Extension using C# Blazor to save a file to a specific location

Ansa Baby
4 min readJan 20, 2024

Chrome extensions are small software programmes that customise the browsing experience. Native messaging allows these extensions to communicate with native applications outside the browser sandbox, enabling an extension to exchange messages with a native application installed on the user’s computer. The native messaging serves the extensions without additional access over the web.

Setting Up the Extension

First, create a new Chrome extension project using the Blazor Wasm template and add the necessary files. In my previous post, I already shared how to create a Blazor C# extension. Please follow those steps.

Edit the manifest JSON and the razor pages to communicate with the native application. In this example, I created a native console app in C# to save a file to a specified location with the message sent from the extension.

Manifest.json

Other than the mandatory manifest_version,name,description,version keys, the required keys for this extension are:

1. browser_action: Defines the behaviour when the extension icon is clicked. In this case, it specifies the HTML file

2. popup.html: to be displayed, and the icons for different sizes.

3. Permissions: Specifies the permissions required by the extension. In this example, it requests permission for native messaging (“nativeMessaging”).

{
"manifest_version": 3,
"name": "Extension Explorer",
"description": "My browser extension built with Blazor WebAssembly",
"version": "0.1",
"background": {
"service_worker": "BackgroundWorker.js",
"type": "module"
},
"action": {
"default_popup": "popup.html"

},
"options_ui": {
"page": "options.html",
"open_in_tab": true
},
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
},
"permissions": [ "nativeMessaging", "downloads", "storage" ],
"web_accessible_resources": [
{
"resources": [
"framework/*",
"content/*"
],
"matches": [ "<all_urls>" ]
}
]
}

Adding Functionality to the Popup: PopUp.razor

I added a button and its click function to the code behind it to invoke the JavaScript method to send the native messaging. A TaskCompletionSource bridges the asynchronous gap between JavaScript and C#. This is particularly useful when you need to await the completion of a JavaScript operation in C#.
JSRuntime.InvokeAsync is part of Blazor's JavaScript Interop API, allowing communication between JavaScript and C#

@page "/popup.html"
@inherits BasePage
@inject IJSRuntime JSRuntime
<h1>Extension Explorer</h1>
<button @onclick="sendMessage">Save</button>
@code {
private async Task sendMessage()
{
var tcs = new TaskCompletionSource<object>();
await JSRuntime.InvokeVoidAsync("sendMessage", DotNetObjectReference.Create(tcs));
}
}

JavaScript page

On the button click, the JavaScript function will be called. The chrome.runtime.sendNativeMessage API in a Chrome extension sends messages from the extension to a native application outside the browser. This is part of the Native Messaging API, allowing secure communication between an extension and a native application installed on the user’s machine.

function sendMessage(tcs) {
window.alert('Preparing to send message...');
chrome.runtime.sendNativeMessage('com.example.nativeapp', { message: 'Hello from Chrome extension!' }, function (response) {
window.alert(response);
});
}

Setting Up the Native Application

A native app is a way of talking to our Chrome extension. It’s like sending messages back and forth. Here, the native app is a C# Console application that receives a message from an extension and saves it to a specific path. This can be used as a method to download anything from client-side applications like Blazor WASM to save as a file to a specific folder.

Console Application

I created a C# console application to receive messages from extensions. Standard input and standard output are used for communication between programs, processes, or components.

static void Main(string[] args)
{
string message = ReadAndSaveMessage();
if (!string.IsNullOrEmpty(message))
SendResponse("Saved successfully");
}

static string ReadAndSaveMessage()
{
byte[] lengthBytes = new byte[4];
Console.OpenStandardInput().Read(lengthBytes, 0, 4);
int length = BitConverter.ToInt32(lengthBytes, 0);
byte[] messageBytes = new byte[length];
Console.OpenStandardInput().Read(messageBytes, 0, length);

string message = Encoding.UTF8.GetString(messageBytes);

// Specify the file path
string filePath = @"D:\Test\example.txt";

// Save the message to the file
File.WriteAllText(filePath, message);

return message;
}
static void SendResponse(object responseObject)
{
string responseJson = System.Text.Json.JsonSerializer.Serialize(responseObject);
Console.OpenStandardOutput().Write(BitConverter.GetBytes(responseJson.Length), 0, 4);
Console.OpenStandardOutput().Write(Encoding.UTF8.GetBytes(responseJson), 0, responseJson.Length);
Console.OpenStandardOutput().Flush();
}

Manifest JSON for Native App

Like extensions, native apps also need Manifest Json The manifest.json file for a native messaging host, which is used to establish communication between a Chrome extension and a native application, outlines key details about the native app.

{
"name": "com.example.nativeapp",
"description": "Native Messaging Example",
"path": "C:\\Users\\ansa\\repos\\NativeAppTest\\NativeAppTest\\bin\\Debug\\NativeAppTest.exe",
"type": "stdio",
"allowed_origins": [
"chrome-extension://rcpncgjoefuonojoiaeajicdtclkbvgf/"
]
}

· name: A unique identifier for the native app in lowercase.

· Description: A brief description of the native messaging host.

· Path: The full path to the native application executable.

· type: The communication type; “stdio” for standard input/output.

· allowed_origins: An array containing the extension’s ID to specify which extension can communicate with the native app

Make sure the name should be in lowercase.

Register the Native Messaging Host in the Registry

1. Add a registry entry for the native messaging host.(Click Windows + R and open regedit.)

2. Open the directory → HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts

3. In the Manifest JSON, add a key with Name and Value as the path where the saved JSON is located.

Key: com.example.nativeapp

Path → C:\\Users\\ansa\\NativeApp\\nativeappmanifest.json

Configure the Extension’s Manifest

In the Chrome extension’s manifest.json, specify the native messaging host.

"permissions": ["nativeMessaging"],
"nativeMessaging": {
"com.example.nativeapp": "nativeAppManifest.json"
},

Helpful Articles

Native messaging — Mozilla | MDN

Thank you for reading my blog!!! Before you go:

  • ➡️Be sure to clap and follow the writer ️for more content!!👏
  • ➡️Your feedback matters a lot. Kindly leave one in the comments. 🙂
  • ➡️Subscribe to my YouTube channel
  • ➡️ Follow me on LinkedIn
  • ➡️Let’s Talk:

--

--

Ansa Baby

Data Scientist |Machine Learning Engineer | RPA | Automation