In this guide, we will be building a full C# class library, usable in any .NET application, that will act as a wrapper class for the J.
Contents
The following are the step by step guide on how to build the library starting with the base concepts. All the C# files are built with Visual Studio .NET 2005 Professional edition. I believe that the same project described here can also be created using Express edition of Visual Studio ... albeit with some conversion.
Before continuing, I would like to point out this excellent .NET Interop Guide which discusses direct use of the J System into your .NET application. It is a well documented guide which provides ready to use code snippets with brilliant explanation on the why and how of each examples written by, the J Community respected, OlegKobchenko.
So on with the show ... to follow this guide, you'll need at least J version 601a and Visual Studio .NET 2005
WHTTD
What were trying to do is build a generic C# class library that will allow us to execute J scripts from any .NET application.
With that statement in mind, we will be creating the class library in two parts.
J base code - The minimum J script that will be embedded and pre-loaded by the C# class. We need this to properly boot-up the class library.
C# class - This is where the bulk of the coding are located. Handles the interop between .NET and COM J system. Provides features like loading and executing external J scripts.
J base code
As specified in the previous chapter, we need to create a script file with the minimum amount of J script that will be needed by any application. The idea here is that the actual all the code needed by an application should come with the external script that is going to be loaded at run-time.
To create the J base code project, follow these steps
First, create a directory in your computer where to store both your J and C# scripts. In my PC, I put all my projects in D:\Projects. So I put my project in D:\Projectc\J.NET\jscripts
- So fire-up J and load the Project Manager by pressing CTRL+B.
In the Project Manager, create a new project by clicking on the menu File->New to display the New Project Dialog Box. Navigate to the project directory, which in my case is D:\Projectc\J.NET\jscripts and enter jscript then click on Save button.
Now in the Project Manager window, click on the Library tab and add the following libraries to the project. Note though that these are the minimum from my experience ... you're free to remove or add library scripts as you saw fit.
We need to set the J Build Options before proceding so click on the Project->Build Options... to open the J Build Options dialog box and fill it up as with the attached image. The setting that you make here will ensure that the source libraries will be included and loaded in the 'z' locale.
Next is settings the target file for your J base code. So go to your directory and create a bin directory like so: D:\Projects\J.NET\bin. This is where the compiled j scripts will be stored and referenced by the C# Class library.
So now, go back to the the Project Manager dialog box, click on the Add button to bring up the Add Project Script dialog box and make sure that the Target Option is selected before clicking on the OK button.
In the Target File dialog box, navigate to the D:\Projects\J.NET\bin directory and save the file jscripts.ijs there like in this image:
After step 8, your project library should look something like this.
You can now actually build the J base code by clicking on the Build button on the project manager. But the current code is not enough. You see, besides being able to load and execute scripts ... we also want to be able to DEBUG the code at runtime. So we need to add a couple of debugging help scripts to square us away. So you'll need to add these two scripts into your library then
To be able to debug, we need to be able to display the J Session window at runtime and this is the script to do it. You need to add this to your J project. You can just copy and paste it and your project should look something like this:
This code loads the user profile of the current user. Please NOTE that this code presupposes that a complete J installation have been done in the computer where the J Class Library will be running and j scripts debugged. After adding this to your project, I named mine
This is where we build the C# Class Library that will make use of the J base code. In general, it does the following: I'll be breaking up the steps into smaller pieces to make the tutorial more manageable ... so here we go.
Here are the steps required to create the project and add the reference to the J COM object. Create the project by selecting New->Project and fill up the dialog box as with this image: We now add the J.EXE as a COM Interop reference to the project. You can do this by right clicking on the
Here, we will show you how to add the J base code as a resource into the class library. This will mean that the scripts will be stored INSIDE the DLL since were sure that the base script will NEVER change. Right+Click on the properties window and click on the Open context menu to open the Properties Window in Visual Studio. Click on the Resource tab and follow the instruction on the screen so that Visual Studio can initialize the resources for our class library. After initializing the resource file, you need to click on the Add Existing file drop down list to add the J base script located in Verify that the file Since we have the J base code as an embedded resource, we still need a piece of code to be embedded for to complete the resource. It's the script loader that's going to be called in later on by the class library and it looks like this: So to add it to the Class Library, you'll need to do the following: In the properties window, click on new resource and select the Add New String item. Fill up the grid with following information: Later on, we will be using these defined Resources in the class library.
This is the class that will be instantiated by the .NET application. It provides all the functionality that you need to execute your J Scripts from your .NET application. To use this class, just rename the file As with all classes, the JSoftware.Session class is composed of Fields, Properties and Methods which as a whole make this class library work.
~Session() - Use C# destructor syntax for finalization code. This destructor will run only if the Dispose method does not get called. It gives your base class the opportunity to finalize. Do not provide destructors in types derived from this class. Dispose() - a public function that implements IDisposable. This is automatically called by .NET Framework during garbage collection Dispose(bool disposing) - the private function that actually does the work for the public function and executes in two distinct scenarios. If disposing equals true, the method has been called directly or indirectly by a user's code. Managed and unmanaged resources can be disposed. If disposing equals false, the method has been called by the runtime from inside the finalizer and you should not reference other objects. Only unmanaged resources can be disposed. Eval(string command) - executes the J code in the command paramater initialize() - the actual function that initializes the session Load(string fileName) - Loads an external file that contains valid J scripts into the current session Session() - The constructor that creates an instance of J with the debug mode set to FALSE Session(bool debug) - The constructor that creates an instance of J where the caller can set the debugging state Variable(string name,bool value) - sets name into true (1) or false (0) Variable(string name, double value) - sets name into a double value Variable(string name, object value) - sets name into an object value. This is a object Variable(string name) - this is the method you use to get values from the current J session. The return value is an object and you have to Variable(string name, string value) - sets a name into a string value Variable(string name, datetime value) - sets a name into a string value from a datetime type in the format of MM/dd/yyyy HH:mm:ss Variable(string name, int value) - sets a name into an integer value
For those who have not worked on an automated payroll system before, one of the most resource intensive operation in terms of resources and time is the import of the Time Device file. The Time Device file is a text file which is the record of all the swipes for Time-In and Time-Out of an employee. It usually have the Device id, card no, year, month, day, hours, minutes of every swipe of an employee. In a normal factory, an employee will swipe at least 4 times a day; 1 for time-in, 2 for lunch break and 1 for time-out. So you can imagine the size and number of records of this file if you leave the time device to capture for a month in a city size factory. This is one of the strength of J. You see, if you write the parser using Visual Basic or C# or any other procedural programming language ... most of the time spent will be looping through the records trying to find the swipes of a certain employee for a certain range of date. You can download the whole Visual Studio 2005 Project here -->> J_NET.zip Here are 2 screenshots of the example application where you can see the J Session running on the background. Of course in your production environment ... J won't show and your users won't even find out that you're using it. To make your test as simple as possible, just unzip the file to D:\Projects\J.NET and run the binary file in D:\Projects\J.NET\timedeviceviewer\bin\Debug\timedeviceviewer.exe NOTE: There is a known bug in file D:\Projects\J.NET\timedeviceviewer.ijs if your J system doesnt support the old x. and y. notations ... To get it to run, you can manually edit the file do one of the following: Put this code on the top of the file: 9!:49[1 Search and replace all y. with y and x. with x Run the code in http://www.jsoftware.com/jwiki/Scripts/Fixargs against the file Enable support for it in Edit->Configure dialog box like so
There are still a lot of things needed to be done to use J with .NET. What we have done here is the first step. I suggest you look into the following topics. and much-much more! Contributed by AlexRufon Guides Runtime J Session Window
NB. note: pjijx form name permits tests on "jijx" if
NB. there is any ijx session, and on "pjijx" if there
NB. is a pijx session
NB. $Id: pijx.ijs,v 1.1.1.1 2004/02/09 06:24:48 cburke Exp $
coclass 'pijx'
NB. =========================================================
JIJX=: 0 : 0
pc pjijx nomax nosize;
xywh 0 0 250 150;cc e editijx ws_hscroll ws_vscroll es_nohidesel rightmove bottommove;
pas 0 0;
rem form end;
)
NB. =========================================================
showJ=: 3 : 0
if. 1 e. 'jijx' E. wd 'qpx' do. return. end.
create''
)
NB. =========================================================
create=: 3 : 0
wd JIJX
wd 'pn *',pname''
wd 'setfont e "Lucida Console" 11 oem'
wd 'pshow'
)
NB. =========================================================
closeJ=: 3 : 0
if. 0 = 2 wdquery (pname'');'Do you want to close?' do.
2!:55 ''
end.
)
NB. =========================================================
pname=: 3 : 0
'Session Server'
)
NB. =========================================================
pjijx_close=: pjijx_cancel=: closeJ
NB. =========================================================
showJ_z_=: showJ_pijx_
Profile Loader
NB. =========================================================
NB.*loadprofile v Code to allow developers to properly debug
NB.
NB. NOTE: This codes assumes that the current session has a
NB. complete J system installed.
NB. $Id: pijx.ijs,v 1.1.1.1 2004/02/09 08:20:16 cburke Exp $
loadprofile=: 3 : 0
ifp=. 1 e. 'pjijx' E. wd 'qpx'
if. ifp do.
wd 'psel pjijx'
fx=. wd 'qformx'
wd 'pclose'
end.
0!:0 < 1!:45''
wd 'psel ',qsmact_jijs_''
if. ifp do.
wd 'pmovex ',fx
end.
wd 'pn *',pname_pijx_''
9!:3 [ 5 NB. linear representation
load 'debug coutil'
)
C# Class Library
Create the Project and Add the J Reference
Reference node and clicking on the Add Reference contect menu item. The Add Reference dialog box will be displayed and click on the COM tab and look for JEXESERVER entry.
Add the J base code as Resource
D:\Projects\J.NET\bin\jscript.ijs.
18!:4 <'z'
0!:100 baseScript
18!:4 <'base'
erase <'loadScript'
JSoftware.Session
using System;
using System.Collections.Generic;
using System.Text;
using JEXEServerLib;
using JSoftware.Properties;
using System.ComponentModel;
namespace JSoftware
{
/// <summary>
/// Created by: Alexander M. Rufon
/// Year: 2006
/// EMail: amrufon@gmail.com
/// </summary>
/// <remarks>A class to use J in .NET Framework</remarks>
public class Session : IDisposable
{
/// <summary>
/// Holds the instance of the J Object
/// </summary>
private JEXEServerClass jObject;
/// <summary>
/// Holds the debugging flag value
/// </summary>
private bool debug = false;
/// <summary>
/// Holds the flag for the IDisposed state
/// </summary>
private bool disposed = false;
/// <summary>
/// Create an J session with the debugging state specified
/// </summary>
/// <param name="debug">Set to true if you want debugging turned on</param>
public Session(bool debug)
{
this.debug = debug;
this.initialize();
}
/// <summary>
/// Creates an instance of J with default settings
/// </summary>
public Session() { this.initialize(); }
/// <summary>
/// Use C# destructor syntax for finalization code.
/// This destructor will run only if the Dispose method
/// does not get called.
/// It gives your base class the opportunity to finalize.
/// Do not provide destructors in types derived from this class.
/// </summary>
~Session()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
/// <summary>
/// Read-Only property which returns true if object was created with debugging code turned on.
/// </summary>
public bool debugging { get { return this.debug; } }
/// <summary>
/// Actual method that initializes the session
/// </summary>
private void initialize()
{
string jScript = "";
// Create a new copy of the J Object and make sure were in the Z locale
jObject = new JEXEServerClass();
jObject.Quit();
jObject.Log(1);
jObject.Show(1);
this.Eval("18!:4 <'z'");
// Now get the base J Script
jScript = UnicodeEncoding.ASCII.GetString(Resources.jscript);
// If were debugging, show the J Session Window
if (this.debug)
{
// Display the session and load the profile
jScript += "\nshowJ ''";
jScript += "\nloadprofile ''";
}
// load this script to the current session
this.Variable("baseScript", jScript);
// We execute the script using the code stored in the resource file
jScript = Resources.ScriptLoader;
// Load the new script to the current session
this.Variable("loadScript", jScript);
// Now we execute the values in the loadScript variable
this.Eval("0!:100 loadScript");
if (this.debug)
{
// Just display a fun message
string script = "NB. Have fun! - bathala <amrufon@gmail.com>";
this.Eval(script);
}
}
/// <summary>
/// Returns the contents of an J variable
/// </summary>
/// <param name="name">Variable name to retrieve</param>
public object Variable(string name)
{
object retVal;
// Retrieve the data from the J Session
jObject.GetB(name, out retVal);
// Return the object
return retVal;
}
/// <summary>
/// Sets an J variable to a value
/// </summary>
/// <param name="name">Variable name to set</param>
/// <param name="value">The value to set the J variable to</param>
public void Variable(string name, object value) { jObject.SetB(name, ref value); }
/// <summary>
/// Sets a J variable to a string value
/// </summary>
/// <param name="name">Variable name to set</param>
/// <param name="value">String value to fill the variable name</param>
public void Variable(string name, string value)
{
object objTemp = value;
this.Variable(name, objTemp);
}
/// <summary>
/// Set a variable to an int value
/// </summary>
public void Variable(string name, int value)
{
object objTemp = value;
this.Variable(name, objTemp);
}
/// <summary>
/// Set a variable to a DateTime value
/// </summary>
public void Variable(string name, DateTime value)
{
string objTemp = value.ToString("MM/dd/yyyy HH:mm:ss");
this.Variable(name, objTemp);
}
/// <summary>
/// Set a variable to a float value
/// </summary>
public void Variable(string name, double value)
{
object objTemp = value;
this.Variable(name, objTemp);
}
/// <summary>
/// Set a variable to a bool value
/// </summary>
public void Variable(string name, bool value)
{
object objTemp;
if (value)
{
objTemp = 1;
}
else
{
objTemp = 0;
}
this.Variable(name, objTemp);
}
/// <summary>
/// Evaluates J scripts
/// </summary>
/// <param name="command">J script to evaluate</param>
public void Eval(string command)
{
try
{
int result;
// Execute the command
result = jObject.Do(command);
if (result > 0)
{
// Throw the correct error message
object errorMessage;
jObject.ErrorTextB(result, out errorMessage);
Exception eoe = new Exception(Convert.ToString(errorMessage));
throw eoe;
}
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// Loads an external script into the current J session
/// </summary>
/// <param name="fileName">Complete path and filename to the script to be loaded</param>
public void Load(string fileName)
{
string script;
// Assign the filename to a J variable
this.Variable("script2load",fileName);
// Check if were in debug mode first.
if (this.debug)
{
// Were debugging so we show what were loading and stop on error
script = "0!:001 < script2load";
}
else
{
// Not debugging, dont need to show script
script = "0!:0 < script2load";
}
// Now evaluate the script.
this.Eval(script);
}
/// <summary>
/// Implement IDisposable.
/// Do not make this method virutal.
/// A derived class should not be able to overrride this method.
/// </summary>
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing the second time.
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose(bool disposing) executes in two distinct scenarios.
/// If disposing equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.
/// If disposing equals false, the method has been called by the
/// runtime from inside the finalizer and you should not reference
/// other objects. Only unmanaged resources can be disposed.
/// </summary>
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
// Component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed
// We basically call it quits and set the JObject to nothing
if (jObject != null) {
jObject.Quit();
jObject = null;
}
// Force garbage collection
GC.Collect();
}
disposed = true;
}
}
}
Fields
Properties
Methods
catch all function where the datatype of value dictates what format it will be assigned to name Real World Example
|spelling error
| 10000 100 100 base y.
| ^
| dateencode=: 3 :0
|[-343] D:\Projects\J.NET\timedeviceviewer.ijs
What's next?
