The following article is about one of the subjects I’m most passionate about that’s integration of disparate systems and cross-platform computing. I think, it’s something I had to publish and it’s one of the core components of my SIF project (Service Integration Framework). This approach can be applied to a variety of scenarios, like exposing performance counters on a *nix system that is hosted in the cloud (good example, a NAT server hosted AWS – Linux server without any UI elements but only allowing connection over HTTP/HTTPS/SSH). This also reminds me of a distributed CyberKiosk (Cyber Cafe) solution I built in Visual FoxPro like 20 years ago, unfortunately back then SOAP was pretty much unknown territory thus management and instrumentation of the networked computers was achievable through sockets only (and yes, I did try DCOM but back then it gave me more headaches than solutions).
Anyways, I’m done with the introduction so what’s this article about? Well, in a nutshell it’s part of my SIF project but it’s also also a PoC (Proof of Concept) of a Qt application hosting the CLR implemented in mono which in turn exposes a WCF service that allows me to instrument the target *nix system through SOAP, sounds cool, right? *nix systems as their Windows counterparts provide a variety of different performance counters and profiling tools, a good example can be the perf stat utility
In order to understand how this solution was architected is important to mention that our Qt application hosts the CLR (more information on embedding mono here), once the AppDomain is created we pass a native functor to the managed world in order to bubble up messages back to the Qt application. C++11 introduced the std::function class template that’s passed to the CLR as a pointer and marshalled by our C# code, but I think it’s worthy to start from the very beginning and then we’ll describe this later.
Our Qt application implements a wrapper class (monowrapper) to interact with mono
The mainwindow class has an instance of the monowrapper class
Please note that I’m using a few features available in modern C++ like smart-pointers and the new std::function (which is the equivalent of Action<T> in .NET). Once our application has loaded the CLR we can see it in the “Loaded Modules” in Qt Creator (shown highlighted “mscorlib”)
In the C#/CLR side of things (mono) we can see how we get the void* from Qt as an IntPtr, we then pin it with GCHandle.Alloc (this is to prevent the GC to move it around) and we pass it across to service implementation too, therefore we can bubble up or fire notifications back to the native world (Qt application). Below the implementation of InitializeHost and ShowMessage method, please note how we need to add the null-operator to the string we’re returning through the callback otherwise our strings in the Qt application might have some “funny” characters at the end.
As you can see, our native callback will be the interface between managed and native world. The ShowMessage method will output the notifications to the “Application Output” window in Qt Creator (System console)
but also to the ListBox (QListWidget) in the main window
The client application or the one driving the operations is a Windows Form application (depicted below)
One of the methods in our MonoDaemon (that’s the Qt application acting as WCF host, effectively) is StartRemoteProcess that can easily call a bash script or in this case we’re starting “gnome-calculator”. The image below shows my Ubuntu desktop along with “gnome-system-monitor” (highlighting qtMonoHost and mono is also visible there) and the application we started remotely (gnome-calculator)
The requests/responses are HTTP/SOAP so unlike my situation a few years back I had to do it with sockets. The WireShark image below shows the SOAP envelope returned to the client after having called the “GetRunningProcesses” method.
And that’s pretty much it, folks. Source code can be found here.