Integrating Native Applications

Finsemble allows you to assemble different applications into a unified desktop experience. Native applications can be both visually and logically integrated.

  • Visual integration allows your application to benefit from Finsemble's UX.

  • Logical integration allows Java and .NET applications to communicate through Finsemble's API.

Finsemble achieves native integration using a technology we call "Assimilation." We provide this as an executable: AssimilationMain.exe. This executable acts as a bridge between native Windows events (move, minimize, maximize, etc.) and Finsemble, communicating over a WebSocket that runs on the desktop’s loopback (localhost).

It is not necessary to manually install AssimilationMain.exe on user’s desktops; it can be included as a Finsemble asset and run dynamically.

This tutorial introduces the concepts needed to integrate native applications into Finsemble workflows.

Visually integrating native applications

Visually integrated applications are spawned and controlled by Finsemble. Visual integration can be extended to applications installed on a local machine or apps bundled with your Finsemble installer.

  • Visually integrated applications have windows that can snap, dock, and group.
  • Visually integrated applications gain workspace persistence between sessions, reloading in the same position and with the same content.

Note: Note that Finsemble disables the ability of WPF components to maximize using aero snap (i.e., WINDOWS KEY + UP). This is done to ensure that these components do not overlap the Finsemble Toolbar.

Launch native applications

There are two ways to launch native applications. Refer to the components.json object block below for each scenario.

"NativeApp": {
    "window": {
        "id": "NativeApp",
		"name": "NativeApp",
		"windowTitleWhiteList": ".*(Outlook|Today).*",
		"windowTitleBlackList": "Opening.*",
        "windowType": "assimilation",
        "path": "nativeapp.exe",
        "arguments": "arg1 arg2 arg3",
        "options": {"autoShow": true},
        "addToWorkspace": true
    },
    "component": {
        "spawnOnStartup": true
    },
    "foreign": {
        "components": {
            "App Launcher": {
                "launchableByUser": true
            }
        }
    }
}
  • When a Finsemble-aware application is launched by Finsemble, several command line parameters are sent to the application specified under the path object. When updating path, you may use the application file location or simply add the file name if it is on your machine's system path.
  • Set windowType to native if you are running a .NET/WPF/C# application.
  • Set windowType to assimilation if you are running your application from a binary file that you do not have the source code for.
  • Arguments should be separated by spaces. Note that when params.argumentsAsQueryString is true, arguments should be a single string in uri format (i.e,. a=1&b=2).
Integrating the Microsoft Office Suite: Assimilation is scoped to handle single windows. Native applications that spawn separate windows (like the "loading" windows that the Microsoft Office Suite spawn) can cause problems for Assimilation. Two configs, `windowTitleWhiteList` and `windowTitleBlackList`, are included to handle this behavior. Use `windowTitleWhiteList` to specify the name of the window that you actively want to Finsemblize. Exclude extraneous windows that you do not want to integrate, like loading windows, using `windowTitleBlackList`. Both fields take regular expression strings containing match criteria for the window titles.

Native applications launched by an intermediary process

If you wish to integrate a native application into Finsemble that is launched via an intermediary process, such as a batch script or protocol handler registered with your operating system (e.g., myLauncher://myapp), then note that you will need to integrate the Finsemble DLL into the application so that it can register itself with Assimilation by passing in the window handle of its window(s).

When using windowType: "assimilation", Finsemble Assimilation is designed to observe the process and capture the first window handle that it creates (that passes any windowTitleWhiteList or windowTitleBlackList set) and then begin managing that window. However, where an intermediary process is used to launch the application, Assimilation will not be able to observe the creation of window handles. By integrating the Finsemble DLL into the application and using windowType: "native", the application spawned can instead proactively register its window handle(s) with Finsemble Assimilation, bypassing this issue.

Bundle native applications with Finsemble

You can bundle applications with Finsemble so that they can be accessed even though if they are not installed on a user's machine. Use this method to bundle AssimilationMain.EXE with your smart desktop.

  1. Update the appAssets array in ..configs/application/application/manifest-local.json. Note: The first item in the array should be assimilation.
"appAssets": [
	{
		"src":"http://localhost:3375/example.zip", // Your application should be packed into a Zip file, whose location is set at the `src` element and may be hosted at any appropriate URL.
		"alias": "NativeExample", // "alias" will be used to reference your application from your *components.json* file later.
		"version": "1.0", <// This represents the version of the package. You can increment the version to force new updates.
		"target": "example.exe", // "target" should identify the binary file inside the Zip that you want to execute.
		"args":"" // Using "args", you can pass in an argument string as you would write it on the command prompt (e.g., `"-mode=a b c"`).
		// You can add multiple copies of the config to `"appAssets"`, with different `"alias"` and `"args"` settings, to create multiple components (with different launch arguments) from the same `src`.
	}
  1. Then update the alias field in components.json to match the alias of the applications zipped bundle.
		"Native": {
			"window": {
				"id": "Native",
				"windowType": "native",
				"alias": "native",
				"url": "",
				"defaultHeight": 600,
				"autoShow": true,
				"alwaysOnTop": false,
				"resizable": true,
				"showTaskbarIcon": false,
				"contextMenu": true,
				"addToWorkspace": true
			},
			...

Note: These options are defined in the Config Reference.


Logically integrating native applications

A deeper level of integration is possible by having your native apps communicate with each other via Finsemble. This is called logical integration, and it allows users to create custom workflows between two or more interoperating components.

Finsemble-aware native applications are specified with a windowType of native. As before, you can use a full path or an alias for a downlaoded asset.

Native Finsemble clients for Java applications

For the most part, Java applications have parity with the JavaScript API. The Java API calls follow a paradigm appropriate for that language: they are accessed at FSBL.getClients().getClientName().method, e.g., FSBL.getClients().getLauncherClient().getComponentsThatCanReceiveDataTypes.

As examples, we've made a few Java sample projects.

Native Finsemble clients for .NET/C#/WPF applications

Integrating .NET applications is made possible by using our .NET library, finsemble.dll. As examples, we've made a few .NET sample projects.

Note: In finsemble.dll, the clients are on the FSBL object itself, e.g., FSBL.LinkerClient. This is in contrast with the main JavaScript APIs, where the FSBL object already exists as a global object and the clients are accessed as FSBL.Clients.ClientName.

Examples

Drag and Drop

  • Emitters: To show the drag and drop icon in the Window Title Bar, you must create an emitter. Here is the code from a sample application that shares a "symbol" object that contains the text from a TextBox called "DataToSend". (See WPF Window Title Bar for more information.) Example:

In C#:

FSBL.DragAndDropClient.SetEmitters(new List<KeyValuePair<string, DragAndDropClient.emitter>>()
{
	new KeyValuePair<string, DragAndDropClient.emitter>("symbol", () =>
	{
		return new JObject
		{
			["symbol"] = DataToSend.Text,
			["description"] = "Symbol " + DataToSend.Text
		};
	})
});

In Java:

final Map<String, Function> emitters = new HashMap<>();
emitters.put("symbol", this::emitterCallback);
emitters.put("description", "Symbol" + symbolToSend);
fsbl.getClients().getDragAndDropClient().setEmitters(emitters);
  • Receivers: To allow data from Finsemble to be dropped onto your application, you must create receivers. Here is the code from our sample application that receives a "symbol" object and sets that as the text of the "DataToSend" TextBox. Example:

In C#:

FSBL.DragAndDropClient.AddReceivers(new List<KeyValuePair<string, EventHandler<FinsembleEventArgs>>>()
{
	new KeyValuePair<string, EventHandler<FinsembleEventArgs>>("symbol", (s, args) =>
	{
		var data = args.response["data"]?["symbol"]?["symbol"];
		if(data != null)
		{
			Application.Current.Dispatcher.Invoke((Action)delegate //main thread
			{
			DataToSend.Text = data.ToString();
			});
		};
	})
});

In Java:

final Map<String, CallbackListener> receivers = new HashMap<>();
receivers.put("symbol", this::symbolReceiverCallback);
fsbl.getClients().getDragAndDropClient().addReceivers(receivers);

Linker

  • Publish: Publish a piece of data. The data will be published to all channels that the component is linked to. Foreign components that are linked to those channels will receive the data if they have subscribed to this data type. They can then use that data to synchronize their internal state (see "Subscribe"). You can publish to specific channels by specifying a channels array in the parameters.
  • Subscribe: Registers a client for a specific data type that is sent to a channel.
  • Unsubscribe: Remove all listeners for the specified data type. Example:

In C#:

FSBL.LinkerClient.Subscribe("symbol", (sender, event) => {
	var symbol = event.response["data"];
});
FSBL.LinkerClient.Unsubscribe("symbol", (s, e) => { });

In Java:

fsbl.getClients().getLinkerClient().subscribe("symbol", this::handleSymbol);

fsbl.getClients().getLinkerClient().unsubscribe("symbol", (e, r) -> {});
  • LinkToChannel: Add a component to a Linker channel programmatically. Components will begin receiving any new contexts published to this channel but will not receive the currently established context.
  • UnlinkFromChannel: Unlinks a component from a Linker channel programmatically. Example:

In C#:

FSBL.LinkerClient.LinkToChannel("group1", null, (s, e) => { });
FSBL.LinkerClient.UnlinkFromChannel("group1", null, (s, e) => { });

In Java:

fsbl.getClients().getLinkerClient().linkToChannel(channel, wi, (err, res) -> { });
fsbl.getClients().getLinkerClient().unlinkFromChannel(channel, wi, (err, res) -> { });

Router Client

The .NET Router Client is very similar to the JavaScript client with a few exceptions:

  • RouterClient.Subscribe: This does not return a subscribeID.
  • RouterClient.Unsubscribe: This takes the same parameters as subscribe above.

Only the following API Calls are currently supported:

  • Transmit
  • AddListener
  • RemoveListener
  • Subscribe
  • Unsubscribe
  • Query
  • AddResponder
  • RemoveResponder

Window Client

  • getSpawnData - Retrieves data that was set with LauncherClient.spawn.

  • setComponentState - Given a field, this function sets and persists an application component's state.

In C#:

 FSBL.WindowClient.SetComponentState(new JObject
{
    ["field"] = "symbol",
    ["value"] = DataToSend.Text
}, delegate (object s, FinsembleEventArgs e) { });

In Java:

final JSONObject state = new JSONObject() {{
	put("field", "symbol");
	put("value", symbolTextField.getText())
		}};
fsbl.getClients().getWindowClient().setComponentState(state, (err, res) -> {});
  • getComponentState - Given a field, this function retrieves an application component's state. If no params are given you get the full state.

In C#:

FSBL.WindowClient.GetComponentState(
    new JObject { ["field"] = "symbol" },
    delegate (object s, FinsembleEventArgs state)
    {
        try {
            if (state.response != null)
            {
                var symbol = (JValue)state.response;
                if (symbol != null)
                {
                    //Do something with the state retrieved
                }
            }
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
        }
    }
);

In Java:

final JSONObject field = new JSONObject() {{
	put("field", "symbol");
}};
fsbl.getClients().getWindowClient().getComponentState(field, (err, res) -> {
	if (err != null) {
		// do something with the error state
	} else {
		// do something with the symbol response
	}
});

Partial native clients available via RPC

In addition to the native clients listed above, certain Finsemble clients are available via a remote procedure call (RPC) mechanism. They are not yet fully implemented as native clients.

To use these endpoints, use the RPC function. For example:

// Definition:
RPC(string endpoint, List<JToken> arguments, RPCCallBack callback)

The RPC call takes three parameters. The first one is the API endpoint. The second is a list of all the arguments to the API call that are specified in the JavaScript API documentation—except the callback or eventHandler. The third parameter is the callback or eventHandler.

Here is the list of such Finsemble API endpoints currently supported for .NET. Refer to the documentation of each endpoint in our JavaScript API documentation:

Config Client

  • getValue

Launcher Client

  • getActiveDescriptors
  • showWindow
  • spawn

Logger

  • error
  • warn
  • log
  • info
  • debug
  • verbose

Note: All logs will be sent to Finsemble regardless of settings in the Central Logger.


Modifying Assimilation

You can modify Assimilation's behavior, if desired, by modifying the AssimilationMain.EXE asset. This is typically done by creating an INI file. Your modified Assimilation asset should then be hosted on a server and bundled with your smart desktop, as per normal.

An example configuration that turns off hotkeys can be found at the Hotkeys tutorial.


Finsemble's Bloomberg Terminal Connect Integration

One of the native applications many users are interested in integrating into their smart desktop is the Bloomberg Terminal. The Finsemble Ecosystem Team has provided a sample of this integration using Bloomberg Terminal Connect. With Bloomberg Terminal Connect, you can create a one-to-many connection between the Bloomberg Terminal and the applications integrated into your smart desktop.

Learn more about the Bloomberg Terminal Connect Integration by reading its documentation.


check   You can both visually and logically integrate native components into Finsemble. Visual integration allows a user to launch, move, dock, group, and save the state of native components that Finsemble launches. Logical integration allows for native components to use Finsemble APIs like the Linker Client or Drag and Drop Client.
 

Further reading