💾 Archived View for blitter.com › apl-books › APLX50 › APLX-manual › www.microapl.com › apl_help › c… captured on 2023-01-29 at 14:38:00.
⬅️ Previous capture (2022-07-17)
-=-=-=-=-=-=-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Interfacing to .Net</TITLE> <META NAME="DESCRIPTION" CONTENT="APL language help page: Interfacing to .Net"> <META NAME="KEYWORDS" CONTENT="Ruby,apl,aplx,apl help"> <!-- %%COMMON_HEAD%% --> <META http-equiv="Content-Type" content="text/html; charset=utf-8"> <LINK rel="stylesheet" type="text/css" href="http://www.microapl.com/styles_apl_help.css"> <!-- %%END%%--> </HEAD> <body> <table> <tr> <td width="800" valign="top" colspan="2"> <center>Topic: <A HREF="ch.htm">APLX Help</A> : <A HREF="ch_070.htm">Interfacing to other languages</A> : <A HREF="ch_070_030.htm">Interfacing to .Net</A> </center> <center> [<A HREF="ch_070_040.htm">Next</A> | <A HREF="ch_070_020.htm">Previous</A> | <A HREF="ch.htm">Contents</A> | <A HREF="help_index.htm">Index</A> | <A HREF="http://www.microapl.co.uk/apl/index.html">APL Home</A> ]</center> <br></td> </tr> <tr> <td width="120"> <a href="http://www.microapl.co.uk/apl/index.html"><img height="68" border="0" width="119" src="MicroAPL_logo.gif" alt="www.microapl.co.uk"></a> </td> <td align="left" valign="bottom"> <h1>Interfacing to .Net</h1> </td> </tr> <tr> <td width="800" valign="top" colspan="2"> <hr> <H2>Specifying a call to the .Net environment</H2> <p>You can interface to .Net by supplying <tt>'net'</tt> or <tt>.net</tt> as the environment string (left argument) for <A HREF="ch_020_070_505.htm"><code>⎕NEW</code></A>, <A HREF="ch_020_070_345.htm"><code>⎕GETCLASS</code></A>, or <A HREF="ch_020_070_095.htm"><code>⎕CALL</code></A>. These system functions will allow you to create an instance of a .Net class, or to call a .Net static (shared) method. </p> <H2>Specifying .Net class names</H2> <P>.Net classes are defined in <i>Assemblies</i> (each assembly usually corresponds to a single .Net library DLL, for example <tt>mscorlib.dll</tt> which contains the basic .Net utility classes), and are organized into <i>Namespaces</i> (such as <tt>System</tt>, <tt>System.Text</tt>, <tt>System.IO</tt> and so on). For example, Microsoft provide a class called <tt>Font</tt> for representing fonts. This is defined in the <tt>System.Drawing</tt> namespace, so its fully-qualified name is <tt>System.Drawing.Font</tt>. The code for this class is held in the System.Drawing assembly, in the shared library <tt>system.drawing.dll</tt>.</P> <p>When you use <code>⎕NEW</code> (or <code>⎕GETCLASS</code> or <code>⎕CALL</code>) to refer to a .Net class, you can specify the class name in one of the following ways:</P> <UL> <li><p>As an undecorated class name, without the namespace qualifier, for example: <tt>'Font'</tt>. In this case, the assembly must be one of those loaded by default or already loaded by a previous call, and the namespace must be included in the namespace search list (see below).</p></li> <li><p>As a fully (or partially) qualified namespace and class name, for example <tt>'System.Drawing.Font</tt>'. In this case, the assembly must be one of those loaded by default, or already loaded by a previous call, but the namespace does not have to be included in the namespace search list.</p></li> <li><p>As an undecorated or fully-qualified class name, and a simple file name specifying the DLL in which the assembly can be found, for example: <tt>'System.Data.SqlClient,system.data.dll'</tt>. A comma is used as the delimiter between the class and file name. In this case, the DLL must exist in the standard location known as the <i>Global Assembly Cache</i> (GAC).</p></li> <li><p>As an undecorated or fully-qualified class name and a full path name specifying the DLL, for example: <tt>'MyBase.MyFirstClass,c:\devt\myclasses.dll'</tt> Again a comma is used as a delimiter between the class and file name.</p></li> </UL> <H2>Setting the search paths for namespaces and .Net assemblies</H2> <p>When you use <code>⎕NEW</code> (or <code>⎕GETCLASS</code> or <code>⎕CALL</code>) to refer to a .Net class, the system searches through the namespaces and assemblies which are in its search path to resolve the class name. If the class is not found, you will get an error:</p> <pre> SW←'.net' ⎕NEW 'StringWriter' Class StringWriter not found in current search list DOMAIN ERROR SW←'.net' ⎕NEW 'StringWriter' ^</pre> <p>The above example has failed because <tt>StringWriter</tt> is a class defined in the <tt>System.IO</tt> namespace, which is not included in the default search list. You can set the current search path for .Net namespaces and DLLs by using the system function <A HREF="ch_020_070_685.htm"><code>⎕SETUP</code></A> and the <tt>'using'</tt> keyword. For example, we can tell the .Net subsystem to include <tt>System.IO</tt> in the search list:</P> <pre> '.net' ⎕SETUP 'using' 'System' 'System.IO' SW←'.net' ⎕NEW 'StringWriter' SW [.net:StringWriter] </pre> <P>The namespaces (and optionally DLLs in which they are located) should be supplied as character vectors after the keyword:</p> <pre> '.net' ⎕SETUP 'using' 'System' 'System.Text' 'QMath.Geom,c:\dev\qmath.dll' </pre> <p>Each element comprises either just a namespace (such 'System.Text'), or a namespace followed by the name of the DLL in which it is located. This can be a full path name, or just the name of the DLL (in which case the DLL should be in the Global Assembly Cache).</p> <p>For convenience, the path is set by default to include the most important .Net libraries, as follows:</p> <pre>System System,system.dll System.Text System.Collections System.Windows.Forms,system.windows.forms.dll System.Drawing,system.drawing.dll </pre> <p>You can read the current search list by supplying the 'using' keyword to <code>⎕SETUP</code> with no arguments.</p> <H2>Conversion of .Net data types to APL data</H2> <p>When your read a .Net property, or call a method which returns a result, APLX by default applies the following data conversion rules:</p> <ul> <li><p>Any .Net numeric types (Int32, Int64, single- or double-precision floats, decimal, etc) are converted to APL integers or floats. 64-bit integers are converted to APL floats on 32-bit platforms, to APL integers on 64-bit platforms.</p></li> <li><p>.Net Booleans are converted to APL binary values 0 or 1</p></li> <li><p>.Net Strings and Chars are converted to APL character arrays, translated from Unicode to APLX internal representation. (Any characters which do not appear in the APLX character set are converted to question marks.)</p></li> <li><p>.Net Enumeration types are converted to APL integers <i>(see below)</i></p> </li> <li><p>Simple .Net arrays are converted to APL arrays, with individual elements converted as above. </p></li> </ul> <p>Anything else is left as an Object in the .Net environment, and a reference to the object is returned to APL.</p> <p>There are some special cases to consider. The data might not be convertible at all, or it might lose precision in the conversion. For example, a .Net <tt>Decimal</tt> might have a higher precision than an APL double-precision floating point can represent. To handle cases like this, APLX provides the <A HREF="ch_020_080_140.htm"><code>⎕REF</code></A> system method. This forces the data to remain as a .Net object. You can then call <tt>ToString</tt>, or other .Net methods appropriate to the <tt>Decimal</tt> data type, to manipulate the data without losing precision.</p> <p>An example which cannot be represented at all is where a .Net <tt>Double</tt> contains a NaN (Not A Number). APL does not handle NaNs, so it cannot be converted to an APL floating-point value. Instead, NaNs are left as Objects. If you try to use the data in an APL expression, you will get a DOMAIN ERROR, but you can see that it is a NaN and use <tt>ToString</tt> and other operations on it.</p> <H2>Enumeration Types</H2> <p>Enumeration types are special classes in .Net, which contain sets of alternative values which a particular type of variable can hold (somewhat similar to <tt>enum</tt> types in the C language). When a .Net nethod or property returns an Enumeration type, APLX converts it to the equivalent integer. Equally, calls which expect an Enumeration type can be passed the equivalent integer from APL (the type is converted automatically). This means that Enumerations which are sets of bits can be combined in APL using the + primitive. However, for readability of code, and to avoid having to check the specific value of an Enumeration, you can use <code>⎕GETCLASS</code> to fetch the Enumeration type, and use that directly.</p> <p>For example, most .Net dialogs (such as OpenFileDialog, which puts up a dialog inviting the user to select an existing file) return a Enumeration type called <tt>DialogResult</tt>, indicating which button (OK, Cancel, etc) was clicked to end the dialog. We can access this class to see what the various enumeration values are:</p> <pre> DR←'.net' ⎕GETCLASS 'DialogResult' DR.OK 1 DR.OK.ToString OK DR.Cancel 2 DR.Cancel.ToString Cancel ⎕BOX DR.⎕NL 2 Abort Cancel Ignore No None OK Retry value__ Yes </pre> <p>This means that, if you call the <tt>ShowDialog</tt> method of the <tt>OpenFileDialog</tt>, you can test which button ended the dialog either by checking the numeric value returned, or by seeing if it is equal to the named enumeration value:</p> <pre> ∇R←FileDialog;DR;DLG [1] ⍝ Put up file dialog, return file selected or empty vector if none [2] DR←'.net' ⎕GETCLASS 'DialogResult' [3] DLG←'.net' ⎕NEW 'OpenFileDialog' [4] :If DLG.ShowDialog=DR.OK [5] R←DLG.FileName [6] :Else [7] R←'' [8] :End ∇ </pre> <H2>Parameters passed by reference</H2> <p>To handle the (relatively uncomon) cases where a .Net method takes an argument 'by reference', and modifies one or more of the arguments in-situ, you can use <code>⎕SETUP</code> and the <tt>'byref'</tt> keyword. See the documentation for <A HREF="ch_020_070_685.htm"><code>⎕SETUP</code></A>.</p> <H2>Using the .Net interface from multiple APL tasks</H2> <p>There are no special restrictions on using the .Net interface from multiple APL tasks. Each task will have an independent copy of the interface, which will be deleted on <tt>)CLEAR</tt>, <tt>)LOAD</tt>, or <tt>)OFF</tt>.</p> <H2>Inheriting from .Net classes</H2> <p>Your own classes (written in APL) cannot inherit directly from a .Net class. However, you can achieve much the same result by using <A HREF="ch_020_010_162.htm">mixins</a>.</p> <p>In this example, the constructor of a user-defined class called <tt>APLDate</tt> 'mixes-in' the .Net class <tt>DateTime</tt></p> <pre> ⎕cr 'APLDate' APLDate { ∇APLDate b ⍝ Constructor for APLDate class ⍝ Argument is vector of Year Month Day Hour Sec ⍝ (or just Year Month day) ⍝ If no argument supplied, use current ⎕ts value :If 0=⍴b '.net' ⎕mixin(⊂'DateTime'),6↑⎕ts :Else '.net' ⎕mixin(⊂'DateTime'),b :End ∇ ∇r←ts ⍝ Return .Net DateTime in ⎕TS format r←Year,Month,Day,Hour,Minute,Second,Millisecond ∇ ∇r←ts_gmt;gmt ⍝ Return the date/time adjusted to GMT, in ⎕ts form gmt←ToUniversalTime r←gmt.Year,gmt.Month,gmt.Day,gmt.Hour,gmt.Minute,gmt.Second,gmt.Millisecond ∇ } dt←⎕new APLDate </pre> <p>As well as the methods written in APL, all the public properties and methods of the .Net <tt>DateTime</tt> class become available in the APL class <tt>APLDate</tt>:</p> <pre> dt←⎕new APLDate dt.⎕nl 2 Date Day DayOfWeek DayOfYear Hour Kind MaxValue Millisecond MinValue Minute Month Now Second Ticks TimeOfDay Today UtcNow Year dt.ToLongDateString ⍝ Method 'mixed-in' from .Net DateTime 19 March 2009 dt.ts ⍝ Method written in APL 2009 3 19 16 0 5 0 dt.ts_gmt 2009 3 19 21 0 5 0 </pre> <H2>Exceptions</H2> <p>If the .Net environment raises an error (or <i>exception</i>), APLX will normally try to print the error message or exception text on the session window, and then raise an APL error (typically DOMAIN ERROR). You can get further information by using <A HREF="ch_020_070_405.htm"><code>⎕LE</code> Last Exception</A> to read back the .Net exception object: For example:</p> <pre> '.net' ⎕NEW 'DateTime' 'Bastille Day' Constructor on type 'System.DateTime' not found. DOMAIN ERROR '.net' ⎕NEW 'DateTime' 'Bastille Day' ^ exception←'.net' ⎕LE 1 exception [.net:MissingMethodException] exception.⎕NL 2 Data HelpLink InnerException Message Source StackTrace TargetSite exception.Message Constructor on type 'System.DateTime' not found. exception.Source mscorlib exception.ToString System.MissingMethodException: Constructor on type 'System.DateTime' not found. at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder bin der, Object[] args, CultureInfo culture, Object[] activationAttributes) at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binde r binder, Object[] args, CultureInfo culture, Object[] activationAttribute s) at NetBridge.NetBridge.CreateNamedInstance(String class_name, Object[] arg_li st) at APLXObj_New(Void* arch_if, Void** objref, wsobj* args, _ExportedProcs* pro cs, SByte* errmsg) </pre> <H2>User-interface programming in .Net</H2> <p>User-interface programming in APLX can be done by using the built-in System classes (formerly accessed through <code>⎕WI</code>). This has the advantages that it is relatively easy to do, and that APLX applications using the System classes will work on other platforms (e.g. the Macintosh). But for more flexibility, you can alternatively use Microsoft's .Net classes directly.</p> <p>User-interface programming using the .Net framework (<tt>System.Windows.Forms</tt>) is a special case of using .Net objects from within APLX. See the Microsoft documentation for full information on this topic.</p> <p>From the APL programmer's point of view, there are a few specific points to note:</p> <ul> <li><p>The typical scenario is that you create a .Net <tt>Form</tt> object, set various properties of the <tt>Form</tt> (such as the <tt>Text</tt> property, which is the window title), and then create further items using control classes such as <tt>Button</tt> and <tt>TextBox</tt>. You need to use the <tt>Add</tt> method of the form's <tt>Controls</tt> property to link up the controls and the form. <i>(Note: If you have a copy of Microsoft Visual Studio or Visual Studio Express, you can use the form designer to design a form interactively, and then copy the dimensions and other properties created in C# or Visual Basic into your APLX code, adjusting the syntax as necessary).</i></p></li> <li><p>To handle .Net events, you need to assign the name of an APL callback function (or some other expression) to one of the control's event properties. For example, a <tt>Button</tt> has a <tt>Click</tt> event, which fires when the button is clicked. As with System classes, the event is held in a queue and de-queued using the system function <A HREF="ch_020_070_860.htm"><code>⎕WE</code></A>, which causes your event handler to be run. </p> </li> <li><p>When one of your callback functions is running, there are three system functions which you can use to find out more about the event. These are: </p> <p><code><A HREF="ch_020_070_292.htm">⎕EVA</A> </code>Returns a reference to the event argument object (class <tt>EventArgs</tt>)<BR> <code><A HREF="ch_020_070_295.htm">⎕EVN</A> </code>Returns the name of the event (such as <tt>'Click'</tt>)<BR> <code><A HREF="ch_020_070_297.htm">⎕EVT</A> </code>Returns a reference to the target object, for example the <tt>Button</tt> object<BR> </p> </li> </ul> <H3>Example 1</H3> <p>The following complete example (available in the workspace 10 HELPDOTNET) shows a simple application which displays a window with an edit box, a text box to output results, and a button. When the button is clicked, it simply evaluates the APL expression typed into the edit box and displays the result in the text box. Note in particular the callbacks set up on lines 32 and 33. The first of these (the <tt>Closed</tt> event) is triggered when the window is closed; this terminates the function by clearing the <tt>)SI</tt>, and this in turn causes all the object references to be deleted because they are held in localized variables. The second call back (the <tt>Click</tt> event of the button) causes the <tt>EVALUATE</tt> function to be run.</p> <pre> ∇DOT_Forms;X;F;EditResult;EditIn;ButtonDo [1] ⍝ Simple example of using Windows Forms from APLX v4 [2] ⍝ Create main form [3] F←'.net' ⎕NEW 'Form' [4] F.Text←'Expression Evaluator' [5] ⍝ [6] ⍝ Create edit box for input line [7] EditIn←'.net' ⎕NEW 'TextBox' [8] EditIn.Left←12 ⋄ EditIn.Top←21 [9] EditIn.Width←265 ⋄ EditIn.Height←20 [10] EditIn.Font←'.net' ⎕NEW 'Font' 'APLX Upright' 10 [11] F.Controls.Add EditIn [12] ⍝ [13] ⍝ Create multi-line box for result [14] EditResult←'.net' ⎕NEW 'TextBox' [15] EditResult.Multiline←1 [16] EditResult.Left←12 ⋄ EditResult.Top←50 [17] EditResult.Width←265 ⋄ EditResult.Height←180 [18] EditResult.Font←'.net' ⎕NEW 'Font' 'APLX Upright' 10 [19] F.Controls.Add EditResult [20] ⍝ [21] ⍝ Create button [22] ButtonDo←'.net' ⎕NEW 'Button' [23] ButtonDo.Left←94 ⋄ ButtonDo.Top←240 [24] ButtonDo.Width←100 ⋄ ButtonDo.Height←22 [25] ButtonDo.Text←'Evaluate' [26] F.Controls.Add ButtonDo [27] ⍝ [28] ⍝ Make the button accept Enter as equivalent to clicking [29] F.AcceptButton←ButtonDo [30] ⍝ [31] ⍝ Add callbacks [32] F.Closed←'"Cleaning up.." ⋄ →' [33] ButtonDo.Click←'EditResult EVALUATE EditIn.Text' [34] ⍝ [35] ⍝ Show the window [36] F.Show [37] ⍝ [38] ⍝ Process events [39] X←⎕WE ¯1 ∇ ∇A EVALUATE B;RESULT;⎕IO [1] ⍝ Evaluate expression B and put it into the Text property of object A [2] ⍝ If an error occurs, change the colour of the text [3] ⎕IO←1 [4] RESULT←⎕EC B [5] :Select ↑RESULT [6] :Case 0 ⍝ Error [7] A.ForeColor←A.ForeColor.Red [8] A.Text←MAKEVEC 3⊃RESULT [9] :Case 1 ⍝ Expression with a result which would display [10] A.ForeColor←A.ForeColor.DarkOliveGreen [11] A.Text←MAKEVEC⍕3⊃RESULT [12] :Else [13] ⍝ 2 Expression with a result which would not display [14] ⍝ 3 Expression with no explicit result [15] ⍝ 4 Branch to a line [16] ⍝ 5 Naked branch [17] A.Text←'' [18] :End ∇ ∇R←MAKEVEC B [1] ⍝ Given a character array, ensure it's a text vector [2] ⍝ If matrix or higher rank, make into CRLF-delimited vector [3] :If 2>⍴⍴B [4] R←B [5] :Else [6] R←⎕R ⎕BOX B [7] :EndIf [8] R←⎕SS R ⎕R(⎕R,⎕L) ∇ </pre> <H3>Example 2</H3> <p>This function shows a simple example of using the <tt>System.IO</tt> namespace to create a text file and write some text to it. On line [1], it sets up the search path for .Net classes. Lines [2] and [3] fetch the <tt>FileMode</tt> and <tt>FileAccess</tt> enumeration types, which are then used in line [5]. </p> <pre> ∇FileWrite;sw;MyFile;FileMode;FileAccess;DateTime [1] '.net' ⎕SETUP 'using' 'System' 'System.IO' [2] FileMode←'.net' ⎕GETCLASS 'FileMode' [3] FileAccess←'.net' ⎕GETCLASS 'FileAccess' [4] DateTime←'.net' ⎕GETCLASS 'DateTime' [5] MyFile←'.net' ⎕NEW 'FileStream' 'c:\temp\testdotnet.txt' (FileMode.OpenOrCreate) (FileAccess.ReadWrite) [6] sw←'.net' ⎕NEW 'StreamWriter' MyFile [7] sw.Write 'Written from APLX',⎕R,⎕L [8] sw.Write 'Created by APLX Version 4 at ',DateTime.Now.ToString [9] sw.Close [10] MyFile.Close ∇ </pre> <hr> </td> </tr> <tr> <td width="800" valign="top" colspan="2"> <center>Topic: <A HREF="ch.htm">APLX Help</A> : <A HREF="ch_070.htm">Interfacing to other languages</A> : <A HREF="ch_070_030.htm">Interfacing to .Net</A> </center> <center> [<A HREF="ch_070_040.htm">Next</A> | <A HREF="ch_070_020.htm">Previous</A> | <A HREF="ch.htm">Contents</A> | <A HREF="help_index.htm">Index</A> | <A HREF="http://www.microapl.co.uk/apl/index.html">APL Home</A> ]</center> <br></td> </tr> </table> <!-- %%COMMON_BODY_TAIL%% --> <p class="copyright">Copyright © 1996-2010 MicroAPL Ltd</p> <!-- %%END%% --> </body> </html>