Theme:
v
AplServer DLL
Downloads
Download APL Server C# Project

Download TestAplServer APL Workspace

Minimum Requirements:

The APLServer DLL is a free product.

Description

The APL Server DLL is a short and simple C# DLL which allows you to fully use APL+Win from a C# project.

There are no restrictions: with this DLL you can:

  1. load any APL workspace from C#
  2. run any APL function (whatever its arguments and results are) in the APL workspace
  3. access (get or set) any global variable in the APL workspace
  4. access (get or set) any APL System variable in the APL workspace
  5. execute any APL expression from C#
  6. get notified of events occurring in APL

Installing the APL Server DLL

Assuming you have Visual Studio or Visual C# Express installed on your computer, download the APL Server C# Project (see above frame) and unzip the LescasseConsulting.AplServer.zip file into your Visual Studio Projects directory:

Note: under Vista or Windows 7, your Visual Studio Projects directory should be something like:

C:\Users\Eric Lescasse\Documents\Visual Studio 2008\Projects

(replace Eric Lescasse by your User Name)

How to use the APL Server DLL

First, be sure to have run the following DOS commands to register APL+Win as an ActiveX Server:

regsvr32 path\aplwco.dll
aplw.exe MyApp.ini /regserver

where MyApp.ini is your APL+Win application .INI file

Examples

Here are 3 complete examples showing how to use the APL Server DLL:

Example 1: Calling an APL function from C#

  1. Create an TestAplServer workspace and add the following APL function to this workspace:

        ∇ R←HelloFromApl
    [1]   ⍝∇ R←HelloFromApl
    [2]   
    [3]   R←'Hello from APL version ',⍕⎕sysver
        ∇
    
  2. Save the APL workspace
  3. Now create a new Windows Forms C# Project called TestAplServer
  4. right click on Add References in Solution Explorer
  5. in the Add Reference dialog select the Browse tab
  6. navigate to the AplServer\bin\Release (or APlServer\bin\Debug directory and select both of the DLLs: LescasseConsulting.AplServer.dll and Interop.APLW.dll
  7. click OK to add these DLLs as references to your project
  8. drag a Button object from the Toolbox to your Form1 form
  9. your form should look like this:
    /images/AplServerDll1.jpg
  10. double click on the button to create its button1_Click event handler

    Define it as follows:

    private void button1_Click(object sender, EventArgs e)
    {
        object aplresult = apl.CallFn("HelloFromApl"); 
        MessageBox.Show((string)aplresult);
    }
    

    This simply calls and runs the HelloFromApl APL function in the TestAplServer APL workspace and then displays a Message Box with the message delivered by the HelloFromApl function

    Note that the result of an APL function (when it returns a result) always is an object in C#

    So we need to cast the result to its real type (here, a string) before we can use it, hence the (string) cast used in the MessageBox.Show argument

    Note that we could have written the above event handler as follows:

    private void button1_Click(object sender, EventArgs e)
    {
        string aplresult = (string)apl.CallFn("HelloFromApl"); 
        MessageBox.Show(aplresult);
    }
    

    which is equivalent

  11. be sure to save the APL workspace
  12. complete your C# Form1.cs code so that it reads:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using LescasseConsulting.AplServer;
    
    namespace TestAplServer
    {
        public partial class Form1 : Form
        {
            AplServer apl;
    
            public Form1()
            {
                InitializeComponent();
                apl = new AplServer("J:\\aplwin\\ELE\\TestAplServer");
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                string aplMessage = (string)apl.CallFn("HelloFromApl");
                MessageBox.Show(aplMessage);
            }
        }
    }
    

    First note that we have added a using LescasseConsulting.AplServer; clause.

    Then, at the top of the class we are declaring a field called apl of type AplServer

    Then, we create an instance of the AplServer object and load the TestAplServer.w3 workspace with: apl = new AplServer("J:\\aplwin\\ELE\\TestAplServer");

  13. it's time to test our C#+APL Client Server application: press F5 to start it
  14. when the form gets displayed, click the button

    You should see a Message Box similar to the following one being displayed:

    /images/AplServerDll2.jpg

Example 2: Passing nested array arguments from C# to APL and returning nested array result from APL

  1. Drag a TextBox object from the ToolBox to the Form1 object
  2. Drag a Label object from the ToolBox to the Form1 object and change its Text property to: Name:
  3. Drag a DateTimePicker object from the ToolBox to the Form1 object
  4. Drag a Label object from the ToolBox to the Form1 object and change its Text property to: Birth Date:

    Your form should now look like this:

    /images/AplServerDll3.jpg
  5. Write the following 2 functions in the APL workspace:
        ∇ R←HelloAplUser rarg;name;year;month;day;age
    [1]   ⍝∇ R←HelloAplUser rarg -- Given a name and birth date in YYYY MM DD format returns name and age
    [2]   
    [3]   (name year month day)←rarg
    [4]   
    [5]   age←ComputeAge year month day
    [6]   
    [7]   R←name age
        ∇
    
        ∇ R←ComputeAge date;year;month;day;nowyear;nowmonth;nowday
    [1]   ⍝∇ R←ComputeAge date -- Given a birthdate in YYYY MM DD format, returns person's age
    [2]   ⍝∇ date ←→ 3-element integer vector (date in YYYY MM DD format)
    [3]   ⍝∇ R ←→ integer scalar (age)
    [4]   ⍝∇ Eric Lescasse 3jan10
    [5]   
    [6]   (year month day)←date
    [7]   (nowyear nowmonth nowday)←3↑⎕ts
    [8]   R←nowyear-year+1
    [9]   R←R+month<nowmonth
    [10]  R←R+(month=nowmonth)×day≤nowday
        ∇
    

    The HelloAplUser function is typical of how you should write APL functions you call from C# when they have arguments and results: such a function should have 3 parts:

    - an instruction that retrieves the (often nested vector) argument and separates it into individual variables
    - some APL instructions to process the argument data
    - an instruction which builds the (often nested vector) result variable

  6. modify the C# Form1.cs code as follows:
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using LescasseConsulting.AplServer;
    
    namespace TestAplServer
    {
        public partial class Form1 : Form
        {
            AplServer apl;
    
            public Form1()
            {
                InitializeComponent();
                apl = new AplServer("J:\\aplwin\\ELE\\TestAplServer");
                dateTimePicker1.Format = DateTimePickerFormat.Short;
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                //string aplMessage = (string)apl.CallFn("HelloFromApl");
                //MessageBox.Show(aplMessage);
    
                object[] rarg = new object[4];
                rarg[0] = textBox1.Text;
                rarg[1] = dateTimePicker1.Value.Year;
                rarg[2] = dateTimePicker1.Value.Month;
                rarg[3] = dateTimePicker1.Value.Day;
    
                object[] aplResult = (object[])apl.CallFn("HelloAplUser", rarg);
    
                string userName = (string)aplResult[0];
                int userAge = (int)aplResult[1];
    
                string message = "Hello " + userName + ", you are " + userAge + " old!";
                MessageBox.Show(message);
            }
        }
    }
    

    The previous call to HelloFromApl has been commented and replaced by the lines which follow.

    Here again the call to the HelloAplUser APL function should be made in 3 parts:

    - preparing the right argument we pass to the HelloAplUser APL function (here rarg)
    - calling the APL function
    - exploiting the result returned by the APL function

    Since we know the APL function result is a nested vector of heterogeneous data (a string and an integer) we cast the result to an object[].

    We can then retrieve the various parts of the result and cast them into what they really are, i.e. a string and an int

  7. Press F5 to run the C# Client application
  8. Enter your name and birth date:
    /images/AplServerDll4.jpg
  9. Click the button. You should see something like:
    /images/AplServerDll5.jpg

Example 3: Get event notifications from APL

This example demonstrates how you can have the Server (APL) notifying the Client (C#) during a long calculation.

  1. Create an LongCalc function in the TestAplServer workspace, reading:
        ∇ LongCalc;I;Z
    [1]   ⍝∇ LongCalc -- Simulates a long calculation
    [2]   ⍝∇ Uses the Notify system object method to notify the client about the progress
    [3]   ⍝∇ Eric Lescasse 2jan10
    [4]   
    [5]   :for I :in ⍳10
    [6]       Z←⎕dl.2
    [7]       Z←'#'⎕wi'Notify' 'onLongCalc'(I×10)
    [8]   :endfor
        ∇
    

    This function simulates a lengthy calculation (only 2 seconds here though), but notifies C# of the progress every 200 ms.

    This is done by invoking the APL System Object Notify method: when you use this method you are free to pass a Code and some Data as its 2 arguments. Here we are passing a string (onLongCalc) and an integer value between 0 and 100 ((I×10) to inform C# about the calculation progress.

  2. in the C# application, drag a ProgressBar object from the Toolbox to your Form1 form

    Your form should now look like this:

    /images/AplServerDll6.jpg
  3. change the Form1.cs C# code to:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using LescasseConsulting.AplServer;
    
    namespace TestAplServer
    {
        public partial class Form1 : Form
        {
            AplServer apl;
    
            public Form1()
            {
                InitializeComponent();
                apl = new AplServer("J:\\aplwin\\ELE\\TestAplServer");
                dateTimePicker1.Format = DateTimePickerFormat.Short;
                Control.CheckForIllegalCrossThreadCalls = false;
                apl.NotifyClient += new AplServer.NotifyEvent(apl_NotifyClient);
            }
    
            void apl_NotifyClient(object Code, object Data)
            {
                string code = (string)Code;
                if (code == "onLongCalc")
                    progressBar1.Value = (int)Data;
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                //-----------------------------------Example 1
                //string aplMessage = (string)apl.CallFn("HelloFromApl");
                //MessageBox.Show(aplMessage);
    
                //-----------------------------------Example 2
                //object[] rarg = new object[4];
                //rarg[0] = textBox1.Text;
                //rarg[1] = dateTimePicker1.Value.Year;
                //rarg[2] = dateTimePicker1.Value.Month;
                //rarg[3] = dateTimePicker1.Value.Day;
    
                //object[] aplResult = (object[])apl.CallFn("HelloAplUser", rarg);
    
                //string userName = (string)aplResult[0];
                //int userAge = (int)aplResult[1];
    
                //string message = "Hello " + userName + ", you are " + userAge + " old!";
                //MessageBox.Show(message);
    
                //-----------------------------------Example 3
                apl.CallFn("LongCalc");
            }
        }
    }
    

    The button1_Click handler now simply calls and runs the LongCalc APL function in the TestAplServer APL workspace

    In the Form1() constructor we are first setting the CheckForIllegalCrossThreadCalls property to false to allow C# to update the User Interface while the lengthy APL calculation is running. Using this property is not recommended, but we are doing this here to make the code simpler: in real life, you would rather use a BackgroundWorker object instead.

    We then subscribe to the NotifyClient event (defined in AplServer.dll: see source code below) so that our C# Client application receives notifications sent by the APL Server application.

    In the apl_NotifyClient event handler we retrieve the Code passed by APL and if it is equal to onLongCalc, we simply change the ProgressBar1 value property to make the ProgressBar progress.

  4. Press F5 to run the C# application, then click the button

    You should see the progress bar showing progress as the APL LongCalc function is being run:

    /images/AplServerDll7.jpg

Source Code

Here is the complete C# Source Code for the APL Server DLL:

using System;
using System.Collections.Generic;
using System.Text;

namespace LescasseConsulting.AplServer
{
    /// 
    /// Apl ActiveX Server Class
    /// This class allows to call and use APL+Win from a C# application
    /// 
    public class AplServer : APLW.WSEngineClass
    {
        public delegate void NotifyEvent(object Code, object Data);
        public event NotifyEvent NotifyClient;
        
        public AplServer()
        {
        }

        public AplServer(string ws)
        {
            SysCall1("LOAD", ws);
            this.Notify += new APLW._WSEngineEvents_NotifyEventHandler(AplServer_NotifyClient);
        }

        void AplServer_NotifyClient(ref object Code, ref object Data, out object Result)
        {
            Result = null;
            NotifyClient(Code, Data);
        }

        public object CallFn(string func)
        {
            return Call0(func);
        }

        public object CallFn(string func, object rarg)
        {
            return Call1(func, rarg);
        }

        public object CallFn(string func, object rarg, object larg)
        {
            return Call(func, rarg, larg);
        }

        public object CallSysFn(string func)
        {
            return SysCall0(func);
        }

        public object CallSysFn(string func, object rarg)
        {
            return SysCall1(func, rarg);
        }

        public object CallSysFn(string func, object rarg, object larg)
        {
            return SysCall(func, rarg, larg);
        }

        public void CloseAPL()
        {
            Close();
        }

        public object Execute(string str)
        {
            return Exec(str);
        }

        public object GetSysVar(string var)
        {
            return get_SysVariable(var);
        }

        public void SetSysVar(string var, object pValue)
        {
            set_SysVariable(var, pValue);
        }

        public object GetVariable(string var)
        {
            return get_Variable(var);
        }

        public void SetVariable(string var, object pValue)
        {
            set_Variable(var, pValue);
        }

        public void RunSysCommand(string str)
        {
            SysCommand(str);
        }
    }
}