Sometimes you wish to use a deployed Synapse component from software not written in a .NET language. If the software is written in C++ or can make use of native DLLs you can still make use of deployed Synapse components either directly from C++ or by creating a C++ wrapper that exports key functionality.
This post shows how this can be done by writing a simple application in C++ that uses the deployed component from the famous police tutorial. For the steps described in this post we will not need Synapse but will use Microsoft Visual Studio 2010 (Make sure it is the C++ version if you use the express edition).
In order to manage our .NET object in the unmanaged world we will make a class called Client that will handle instantiation and destruction of an instance of the .NET object (in our this case Peltarion.Deployed.GodCopBadCop). In this class we can also implement any access methods to provide communication with the managed object.
We will then use this Client class to instantiate an instance of the deployed Synapse component and evaluate a test sample from the police data file.
Fist off start Visual Studio and create a new C++ project. For this exercise we will use a Win32 Console Application. I'm calling this project NativeSyn. (Check empty project in the second page before you click finish.)
I have copied the Synapse deployment directory GodCopBadCop to the project folder for easy access. To use it though we must add a reference to it. In the Solution Explorer select the NativeSyn project and hit Alt+Enter to bring up the properties window.
Turn on Common Language Runtime Support under configuration properties
Then add a reference to the dll containing the deploys Synapse system. For us that will be GodCopBadCop.dll.
(If you just want the project and source files to play with on your own you can find them at the end of the post)
Now let's add a header file called Client.h and start crafting our Client class. We will need a private void *ref to keep track of the managed .NET object. (We can't point to it directly since it's in managed space but we will get to that.) We will also need an alloc and a free method that will be called from the constructor and destructor respectively. Finally we will need a test method to facilitate the testing of Dutch policemen.
For the actual implementation add a source file called Client.cpp.
Include windows.h , Client.h and in order to comunicate with the managed world, vcclr.h. We will also be using namespaces System and System::Runtime::InteropServices.
Next is the implementation of the constructor and destructor. This is particularly easy as all they have to do is call alloc() and free(). We will however add a #pragma unmanaged prior to their implementation.
The rest of the code will be devoted to dealing with managed interop so #pragma managed this time. To run anything .NET related we need a using directive to mscorlib.dll and we will need another to GodCopBadCop.dll.
The type we go through all this trouble for is Peltarion.Deployed.GodCopBadCop in C++ that becomes Peltarion::Deployed::GoodCopBadCop. Since it is quite long to type let's make a typedef.
alloc(), now the interesting things start. Our .NET type is managed and subject to the CLS garbage collector. If we just instantiate an instance of it and there is no managed pointer to it we can be pretty certain the GC will destroy and collect it so will need to notify the GC we want that object to stay alive. This is done through the concept of internal pointers and GCHandles. I will not go in to details here but the process is to use gcnew to create an object instance and then register it with a GCHandle and finally store a pointer to the GCHandle.
Now, every time we need to use the object we need to go through the GCHandle to find it. When we want to tell the GC we are done with the object, and that it can be safely collected, we need get our GCHandle and call free on it.
The implementation of alloc() and free() looks like this:
The test method also need to access the object so we need to use the Target property of the GCHandle to get an internal pointer to it. After that you can use it access all the methods of the .NET object. We will primarily be interested in the Set_CSV and the StepEpoch methods along with the properties exposing the system output (on this system the output function layer is called FunctionLayer4 so we will access the FunctionLayer4_Port0 property).
Using the Client in an application is strait forward. We add a main.cpp source file to our project, include Client.h and we are god to go. (If you want you can turn off Common Language Runtime Support for the main.cpp file and all other files that don't do explicit interop stuff.)
Now, the moment of truth! Ctrl+F5
This was a small example to get you going. You might want to mimic some of the deployed Synapse components API or if this was a wrapper class to cater for some other interop you would probably export methods for creation, destruction and access methods that suite that particular platform.
The Visual Studio solution that was created for this post can be downloaded here and Microsoft has published a video tutorial covering this same issue over at http://msdn.microsoft.com/en-us/visualc/Video/bb892742