Current issue

Vol.26 No.4

Vol.26 No.4

Volumes

© 1984-2024
British APL Association
All rights reserved.

Archive articles posted online on request: ask the archivist.

archive/23/1

Volume 23, No.1

Building C# COM DLLs for APL

Ajay Askoolum
ajayaskoolum@ntlworld.com

In this article, I present a visual guide to building and deploying a C# COM (Component Object Model) Interrop DLL (Dynamic Link Library) for APL using Visual C# 2005 Express Edition. This is not a tutorial on either C# or on Visual C# 2005 Express Edition, but simply an illustration of the process for building COM DLLs in C#.

For a limited period and subject to conditions, Visual C# 2005 Express Edition is available as a free-download, together with installation instructions [1].

The continuing evolution of the .Net platform has spawned a lot of documentation on the internet which appears to be misleading, at least from an APL point of view; a contributing factor in this confusion is that the documentation applies to several versions of the .Net framework and Visual Studio.net. This article redresses the situation by providing a template for building COM DLLs for APL. The actual code in the DLL is of a trivial nature and intended solely to illustrate how to build it.

A COM DLL is a server to a client; in the present context, the client is APL. The COM server exposes methods, properties, and events that the client can use irrespective of the native language of the DLL. In order to deploy such a DLL, the target computer must have the .Net Framework 2.0 installed; this is available as a free download [2]. Visual Studio.Net 2005 uses the same framework and although the interface presented by the Express and full versions are different, it is possible to use Visual Studio 2005 instead of the Express edition. This article is based on the Express edition.

File|New Project

Start the Express edition and click File|New Project. Figure 1 shows the dialogue that appears.

Selecting the type of project
Figure 1. Selecting the type of project

Naming the class
Figure 2. Naming the class

Properties
Figure 3. Properties

Select the Class Library template and specify the name as QuoteQuad, as shown above. This ensures that QuoteQuad is the name of the DLL. By default, the class library is not configured for COM usage.

Modifying the DLL properties

In order to modify the properties of the DLL to enable it for COM, click View|Properties Window and then select Class1.cs; the objective is to change the class name. Figure 2 shows the class name after class1.cs has been renamed Demo.cs.

Clients will instantiate the DLL by using the Library.Classname convention; in this context, the reference is QuoteQuad.Demo.

Setting COM properties

Next, click Project|Quote Quad Properties; this step is shown in Figure 3.

Next, Figure 4 shows the screen that becomes visible.

Specifying COM properties
Figure 4. Specifying COM properties

Click Assembly Information in order to display the dialogue shown in Figure 5.

COM visibility
Figure 5. COM visibility

Tick the Make assembly COM-Visible the option.

Next, click Build in the left pane and specify the output path and tick Register for COM interop as shown in Figure 6.

COM Registration
Figure 6. COM Registration

Save the project

Click File|Save All in order to save the project. The dialogue shown in Figure 7 appears:

Location of DLL
Figure 7. Location of DLL

For Location specify the fully qualified name of the location and then click Save to save all the files for the project. Note the option to Create directory for solution.

Compiling the DLL

The DLL is ready for compilation. Click Build|Build Solution or press F6. However, the DLL does not have any methods, properties, or events to expose; therefore, there is little point in creating the DLL at this stage.

Contents of the assembly

The first file, Assembly.cs, is ready and contains the following information:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("QuoteQuad")]
[assembly: AssemblyDescription("C# COM Interrop DLL: Step-by-step guide")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Ajay Askoolum")]
[assembly: AssemblyProduct("QuoteQuad")]
[assembly: AssemblyCopyright("Copyright © 2006")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("49a5c035-e8c5-46b0-8317-499b7bc5e511")]
// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Note that the code in this file is auto-generated from the configurations set using the graphical user interface dialogues.

Adding methods, properties, and events

As mentioned, this does not provide a tutorial on C# or on C# Express; the objective is to provide a template for building C# COM DLLs. The functionality of the DLL is manually coded in the Demo.cs file. Its content is as follows:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace QuoteQuad
{
  public delegate void EventDel();
  [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
  public interface UserEvents
  {
    [DispId(5)]
    void MyEvent();
  }
  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
  public interface _Demo
  {
    [DispId(1)]
    int MyMethod(int numarg);
    [DispId(2)]
    string MMethod();
    [DispId(3)]
    int MyProperty { get; set;}
    [DispId(4)]
    void eventm();
  }
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("QuoteQuad.Demo")]
  [ComSourceInterfaces(typeof(UserEvents))]
  
  public class Demo : _Demo
  {
    public event EventDel MyEvent;
    public Demo()
    {
    }
    private int myVar;
    public int MyProperty
    {
      get { return myVar; }
      set
      {
        MyEvent();
        myVar = value;
      }
    }
    public int MyMethod(int numarg)
    {
      return numarg + 10;
    }
    public string MMethod()
    {
      return "Ajay Askoolum";
    }
    public void eventm()
    {
      MyEvent();
    }
  }
}

The critical parts of the code are:

  • The DispId attributes must be unique integers.
  • Events are declared in the public interface UserEvents block and methods and properties are declared in the public interface _Demo block.

At this point, a C# language manual is necessary in order to be able to build reasonable functionality in the DLL.

Building the DLL

In order to make the assembly, click Build|Build Solution or press F6. This process creates several files, including the DLL at the following location

C:\OurFiles\Ajay\C#\QQ\QuoteQuad\QuoteQuad\obj\Release

The location will vary depending on the specification for Location; see Figure 7.

The only file that is required for COM operation is:

C:\OurFiles\Ajay\C#\QQ\QuoteQuad\QuoteQuad\obj\Release\QuoteQuad.dll

On the development computer, no further action is necessary before using the DLL as the build process has already registered the DLL silently, and can be seen from APL+Win:

      '#' ⎕wi 'XInfo' 'QUOTE'
QuoteQuad.Demo ActiveObject QuoteQuad.Demo

Deploying the DLL

QUOTEQUAD.DLL properties
Figure 8. QUOTEQUAD.DLL properties

The DLL’s properties can be examined by collating it within the filing system and clicking the right mouse button: see Figure 8.

On the target computer, execute the following steps:

  1. Ensure that there are no existing sessions of a client that will use the DLL.
  2. Ensure that it has the .Net Framework 2.0 installed already.
  3. Copy the DLL to a target computer at any suitable location.

In principle, it is inadvisable to copy the DLL to a system folder such as System32; a good location is the folder housing the application that will deploy the DLL. For the purpose to hand, the DLL is copied to C:\MY APL APPLICATION.

In order to make the DLL available, click Start|Run to display the dialogue shown in Figure 9.

In the Open box, type:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe 
   /codebase "C:\MY APL APPLICATION\QuoteQuad.dll"

Registering the DLL
Figure 9. Registering the DLL

Click OK.

The code in the box shows the fully qualified name of REGASM.EXE followed by the switch /codebase and then the fully qualified name of the DLL. The file locations vary on your computer.

Testing the DLL

At this point, the DLL is available on the target computer and is readily deployed by APL.

Using APL+Win, an instance of the DLL is created as shown in Figure 10.

APL+Win
Figure 10. APL+Win

Using APLX, an instance of the DLL is created as shown in Figure 11.

APLX
Figure 11. APLX

Using Dyalog, an instance of the DLL is created as shown in Figure 12.

Dyalog
Figure 12. Dyalog

APL+Win and APLX are consistent in the enumeration of the events, methods, and properties of the DLL. However, it is unclear why Dyalog is unable to see the event.

Demonstration

I shall use APL+Win to demonstrate the usage of the DLL. First the properties:

      ⎕wi '?MyProperty'
xMyProperty property:
  Value@Long ← ⎕WI 'xMyProperty'
  ⎕WI 'xMyProperty' Value@Long

The property MyProperty is a read/write property that is numeric.

      ⎕wi 'xMyProperty'           ⍝ Read
0
      ⎕wi 'xMyProperty' 100.75    ⍝ Write
      ⎕wi 'xMyProperty'           ⍝ Read
101

There appears to be an inconsistency! On closer examination of the code, it is apparent that the DLL coerces the value of the property to integer; hence, the result is 101 instead of 100.75.

public int MyProperty
{
  get { return myVar; }
  set
  {
    MyEvent();
    myVar = value;
  }
}

This property will also raise the event MyEvent upon the value of the property being changed. However, the event was raised in the previous assignment but I had not specified an event handler. An event handler is specified in the usual manner, thus:

      ⎕wi 'onXMyEvent' '0.01×+\10/1' ⍝ Specify event handler
      ⎕wi 'xMyProperty' 190254 ⍝ Write & expect even handler to run
0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1
      ⎕wi 'xMyProperty' ⍝ Verify
190254

The event handler did fire and ran the APL handler; any APL+Win function may be called by the handler.

The method eventm also raises the event MyEvent.

      ⎕wi 'Xeventm' ⍝ Call method & expect MyEvent to fire
0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1

The method MMethod simply returns a static result:

      ⎕wi 'XMMethod' ⍝ This method returns a string
Ajay Askoolum

Finally, the method MyMethod takes a single argument and returns it incremented by 10.

      ⎕wi '?MyMethod'
XMyMethod method:
  Result@Long ← ⎕WI 'XMyMethod' numarg@Long
  ⎕wi 'XMyMethod' 190254
190264

Although the methods, properties, and events in this demonstration C# COM DLL are simple, it is clear that it is now possible to write COM servers in the Microsoft flagship language, C#, for deployment with APL. Moreover, the template developed in this article for building and deploying such DLLs is minimalist, as would become clear from a study of the subject. For instance, I have completely circumvented the issues relating to strong names, the Global Assembly Cache and the debate related to managed code.

The next step

The source code for the DLL is included in qq.zip, as is quotequad.dll. The next step is for you to replicate the whole demonstration for yourself, using the source code, and then to write more purposeful methods and properties. In order to accomplish this, you will need a good reference on C# and an understanding of the conventions relating to COM objects. Also, use the quotequad.dll to test the deployment technique discussed above.

References

  1. http://msdn.microsoft.com/vstudio/express/visualcsharp/download/default.aspx
  2. http://www.microsoft.com Search for dot net framework 2.0 redistributable
  3. C. Nagel, B. Evjen, J. Glynn, M. Skinner, K. Watson & A. Jones, Professional C# 2005, WROX, ISBN-13 978-0-7645-7534-1
  4. Ajay Askoolum, System Building with APL+Win, John Wiley, ISBN 0-470-03020-8

Valid HTML 4.01 Strict

script began 19:44:01
caching off
debug mode off
cache time 3600 sec
indmtime not found in cache
cached index is fresh
recompiling index.xml
index compiled in 0.1772 secs
read index
read issues/index.xml
identified 26 volumes, 101 issues
array (
  'id' => '10011590',
)
regenerated static HTML
article source is 'HTML'
source file encoding is 'UTF-8'
URL: mailto:ajayaskoolum@ntlworld.com => mailto:AjayAskoolum@ntlworld.com
URL: #ref1 => art10011590#ref1
URL: #ref2 => art10011590#ref2
URL: askdll/image1.png => trad/v231/askdll/image1.png
URL: askdll/image2.png => trad/v231/askdll/image2.png
URL: askdll/image3.png => trad/v231/askdll/image3.png
URL: askdll/image4.png => trad/v231/askdll/image4.png
URL: askdll/image5.png => trad/v231/askdll/image5.png
URL: askdll/image6.png => trad/v231/askdll/image6.png
URL: askdll/image7.png => trad/v231/askdll/image7.png
URL: askdll/image8.png => trad/v231/askdll/image8.png
URL: askdll/image9.png => trad/v231/askdll/image9.png
URL: askdll/image10.png => trad/v231/askdll/image10.png
URL: askdll/image11.png => trad/v231/askdll/image11.png
URL: askdll/image12.png => trad/v231/askdll/image12.png
URL: askdll/qq.zip => trad/v231/askdll/qq.zip
URL: http://msdn.microsoft.com/vstudio/express/visualcsharp/download/default.aspx => http://msdn.microsoft.com/vstudio/express/visualcsharp/download/default.aspx
URL: http://www.microsoft.com => http://www.microsoft.com
URL: http://validator.w3.org/check?uri=referer => http://validator.w3.org/check?uri=referer
URL: http://www.w3.org/icons/valid-html401 => http://www.w3.org/Icons/valid-html401
completed in 0.2122 secs