Guides/OLE Server for CSharp
These examples have been created for Visual Studio 2013 and J804 on Windows 10.
Introduction
In C# you can load J and then send it expressions for execution. The results can be obtained as objects.
First of all, C# needs a reference of typelib info of J servers. The usual way of doing this is to add a reference of J server in Visual Studio IDE, but this example shows another way without using Visual Studio IDE.
These 2 files File:Jdllserverlib.cs and File:Jexeserverlib.cs are wrappers using .Net interop, one for JDLLServer, the other for JEXEServer.
Building Executable
Download the above 2 wrappers and also File:Jole csharp test dll.cs and File:Jole csharp test exe.cs to the same folder.
At the command prompt, change directory to the source folder and set up Windows SDK for 64bit
CALL "c:\program files (x86)\microsoft visual studio 12.0\vc\bin\x86_amd64\vcvarsx86_amd64.bat"
See Norman Drinkwater's Guides/Compiling Jqt for how to download and setup Visual Studio 2013.
to build using command line csc.exe, type
csc jole_csharp_test_dll.cs jdllserverlib.cs csc jole_csharp_test_exe.cs jexeserverlib.cs
Executable files jole_csharp_test_dll.exe and jole_csharp_test_exe.exe will be built.
How to use
The JEXEServer is preferred for development and debugging so we shall take JEXEServer as an example for discussion. The few essential steps are explained.
using JEXEServerLib;
the source file jexeserverlib.cs should be in the same folder or included in the project (if you are using Visual Studio IDE)
// Create instance of JEXEServer try { JEXEServerLib.JEXEServer jobject = new JEXEServerLib.JEXEServer(); // QueryInterface for the IJEXEServer interface: JEXEServerLib.IJEXEServer js = (JEXEServerLib.IJEXEServer)jobject;
This will create the j server object. Note that it is the IJEXEServer object (js) that we will actually use.
int rc; // return code, 0 = success
rc will hold return code of COM methods. This is important because it will raise strange errors for methods involving objects if you do not assign the result to a variable.
// the following 3 line have no effect on jdll ole server // rc = js.Quit(); // uncomment to close IDE automatically rc = js.Show(1); // show Term rc = js.Log(1); // log input
Quit will not shutdown j server immediately, but it will terminate J server after C# has finished using it. Quit is not called here, otherwise J server will shutdown before you have a chance to see it.
Up to this point, Term is a bare terminal without menu bar, the standard J profile has not been loaded, so that usual utility verbs such as load or smoutput will not work. Most likely your scripts will not run without a profile. See Guides/OLE Automation for how to load profile.
rc = js.Do("i.3 4"); // simple test
You should see the result as follows inside Term (not applicable for JDLLServer).
0 1 2 3 4 5 6 7 8 9 10 11
rc = js.Do("1+'a'"); // examine error code int err=rc; object errmsg; rc = js.ErrorTextB(err, out errmsg); Console.WriteLine("type : "+errmsg.GetType().ToString()); Console.WriteLine("value: "+errmsg.ToString());
This demonstrate how to use the ErrorTextB method. Note the out keyword, you will not miss it because the program will otherwise failed to compile.
// c# wide char to/from J utf8 rc = js.Do("a=:'"+Convert.ToChar(1056)+Convert.ToChar(1086)+ Convert.ToChar(1089)+Convert.ToChar(1089)+ Convert.ToChar(1080)+Convert.ToChar(1103)+"'"); rc = js.Do("(3!:0 ; # ; ]) a"); object a; rc = js.GetB("a",out a); Console.WriteLine("type : "+a.GetType().ToString()); Console.WriteLine("value: "+a.ToString()); Console.WriteLine("length: "+a.ToString().Length); Console.WriteLine("value: "+Convert.ToInt32(a.ToString()[0])); Console.WriteLine("value: "+Convert.ToInt32(a.ToString()[1])); Console.WriteLine("value: "+Convert.ToInt32(a.ToString()[2])); Console.WriteLine("value: "+Convert.ToInt32(a.ToString()[3])); Console.WriteLine("value: "+Convert.ToInt32(a.ToString()[4])); Console.WriteLine("value: "+Convert.ToInt32(a.ToString()[5]));
J server interface can now handle unicode text. You should see some Cyrillic text in J Term.
In GetB("a",out a), the first refers to a name in J, while the second refers to an object variable in C#.
// boxed array object mat = new object[3, 2] { {1, 2}, {3, 4}, {5, 6} }; rc = js.SetB("b",ref mat); rc = js.Do("b"); object b; rc = js.GetB("b",out b); Console.WriteLine("type : "+b.GetType().ToString()); Console.WriteLine("value: "+b.ToString()); // integer array rc = js.Do("[b=: ($b)$;b"); // unbox on J server rc = js.GetB("b",out b); Console.WriteLine("type : "+b.GetType().ToString()); Console.WriteLine("value: "+b.ToString());
Note the ref keyword in SetB, it is required when passing objects from C# to J. The input to Do does not need ref because it is passed as a string.
C# does not understand J array and J does not support most of data types/structures in C#. Marshaling of data between C# and J only works for some special cases. It should be sufficient for normal use, if not, write your own code to handle it.