X
49 Rate this article:
No rating

INTERNAL: Callable IDL example using Micrsoft's C# .net

Anonym
Topic:
Microsoft Visual Studio version 7.0 encourages the use of the C# programming language. C# (C-sharp) is an object oriented programming language with syntax similar to C/C++. C# is NOT a direct extension of C/C++ and does NOT conform to the ANSI C standards.

The following is an example of calling into IDL from C# using the callable IDL interface.Discussion:
One important thing to note about C# is that it discourages the use of pointers. Instead of allocating a block of memory and using a pointer to access it as an array as it is done in plain C, C# encourages creating an instance of an object class to encapsulate the data.

Since IDL is written in C, there is a need to use pointers to efficiently access IDL arrays. In order to be able to use pointers directly in C# the scope must be declared as "unsafe".

The following example shows how to access an IDL array in C# and vice versa using the Callable IDL interface.

File->New->Project

Select Visual C# projects and Windows Application.

Select a name (for example: "callableIDL") and a location.

Select View->Toolbox

Add a button. Change the text on the button to for example "Callable IDL example".

Double-click on the button. This should bring up the code editor. and a stub should appear:


      private void button1_Click(object sender, System.EventArgs e)
      {
      
      }


Add the word "unsafe" to the function declaration since pointers will be used:

      unsafe private void button1_Click(object sender, System.EventArgs e)
      {
      
      }



Add the following code to the button click event:

         /* Initialize IDL */
         int Status = 0;
         Status = Idl32.IDL_Win32Init(0, (void *) (this.Handle), null, null);
         if (Status==0) MessageBox.Show("Failed to initialize IDL");
   
         /* Create an array of 10 double precision floating point numbers */
         double[] d=new double[10];

         /* Initialize array */
         for (int i=0;i<10;i++) d[i] = i;


         /* Use IDL_ImportNamedArray to access a C# array inside IDL */
         fixed (void *data=d)/* IDL needs a pointer to the data */
         {
            long[] dim = new long[8]; /* define dimensions array */
            dim[0] = 10;
            Idl32.IDL_ImportNamedArray("D", 1, dim, 5, data, null, null);
            string command = "D[9]=12"; /* Change the last element of D */
            Status = Idl32.IDL_ExecuteStr(command);
            command = "void=dialog_message('D = '+strcompress(strjoin(D)),/info)";
            Status = Idl32.IDL_ExecuteStr(command);
            if (d[9] == 12) MessageBox.Show("C# Array successfully altered by IDL");
            if (d[9] == 9) MessageBox.Show("IDL Failed to change original array");

            /* Use IDL_GetVarAddr to access an IDL array in C# */
            double *test; /* define a pointer to the array */
            long n=0;
            Idl32.IDL_ExecuteStr("A=dindgen(20)");
            Idl32.IDL_VarGetData(Idl32.IDL_GetVarAddr("A"),
               &n, (char **) &test, 0);

            /* Calculate the total sum of "lindgen(20)", it should be 190 */
            double total=0;
            for (int i=0;i<20;i++) total+=test[i];
            MessageBox.Show(total.ToString());
            if (total == 190) MessageBox.Show("C# successfully calculated the total (190)");
            else MessageBox.Show("C# Failed to calculate the correct total");
               
            /* Change the elements of the array by adding 100 */
            for (int i=0;i<20;i++) test[i]+=100.0;

            /* Show that the IDL array was modified from C# */
            command = "void=dialog_message('A = '+strcompress(strjoin(A)),/info)";
            Idl32.IDL_ExecuteStr(command);
         }   
         Status = Idl32.IDL_Cleanup(0);





Next, go to the beginning of the file where the following lines can be seen:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;




Add the following line (after using System.Data) to allow calling idl32.dll from this project.

using System.Runtime.InteropServices;


The next lines in the file are:

namespace callableIDL
{




After these two lines declare a new class that will encapsulate idl32.dll.
In this example 5 functions are defined from idl32.dll. These are:
IDL_Win32Init, IDL_ExecuteStr, IDL_ImportNamedArray, IDL_GetVarAddr, and IDL_VarGetData.


   /* Declare a class that encapsulates the idl32.dll */
   /* The function definitions are adapted from idl_export.h */
   /* The "unsafe" and "public" keywords are added for C# compatibility */
   public class Idl32
   {
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern int IDL_Win32Init(int iOpts, void *hinstExe,
         void *hwndExe, void *hAccel);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern int IDL_ExecuteStr(
         [MarshalAs(UnmanagedType.LPStr)] string cmd);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      public static extern int IDL_Cleanup(int just_cleanup);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern void* IDL_ImportNamedArray(
         [MarshalAs(UnmanagedType.LPStr)]string name,
         int n_dim, long[] dim, int type, void* data, void* free_cb, void* s);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern char* IDL_GetVarAddr(
         [MarshalAs(UnmanagedType.LPStr)] string name);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern void* IDL_VarGetData(void *var, long *n,
         char **pd, int ensure_simple);
   }



The next step is to change the build options to allow "unsafe" functions.
Select View->Solution Explorer.
Right-click on the project name (callableIDL in this example).
Select Properties from the pulldown menu.
Go to Configuration Properties->Build.
Change "Allow unsafe code blocks" from 'False" to "True".
Click "Apply" and "OK"


At this point the project can be run with Debug->Start or hit F5.

The complete listing of the form code (Form1.cs) is included below.Solution:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;

namespace callableIDL
{
   /* Declare a class that encapsulates the idl32.dll */
   /* The function definitions are adapted from idl_export.h */
   /* The "unsafe" and "public" keywords are added for C# compatibility */
   public class Idl32
   {
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern int IDL_Win32Init(int iOpts, void *hinstExe,
         void *hwndExe, void *hAccel);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern int IDL_ExecuteStr(
         [MarshalAs(UnmanagedType.LPStr)] string cmd);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      public static extern int IDL_Cleanup(int just_cleanup);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern void* IDL_ImportNamedArray(
         [MarshalAs(UnmanagedType.LPStr)]string name,
         int n_dim, long[] dim, int type, void* data, void* free_cb, void* s);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern char* IDL_GetVarAddr(
         [MarshalAs(UnmanagedType.LPStr)] string name);
      [DllImport(@"C:\RSI\IDL60\bin\bin.x86\idl32.dll", CharSet=CharSet.Auto)]
      unsafe public static extern void* IDL_VarGetData(void *var, long *n,
         char **pd, int ensure_simple);
   }
   ///
   /// Summary description for Form1.
   ///

   public class Form1 : System.Windows.Forms.Form
   {
      private System.Windows.Forms.Button button1;
      ///
      /// Required designer variable.
      ///

      private System.ComponentModel.Container components = null;

      public Form1()
      {
         //
         // Required for Windows Form Designer support
         //
         InitializeComponent();
         //
         // TODO: Add any constructor code after InitializeComponent call
         //
      }

      ///
      /// Clean up any resources being used.
      ///

      protected override void Dispose( bool disposing )
      {
         if( disposing )
         {
            if (components != null)
            {
               components.Dispose();
            }
         }
         base.Dispose( disposing );
      }

      #region Windows Form Designer generated code
      ///
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      ///

      private void InitializeComponent()
      {
         this.button1 = new System.Windows.Forms.Button();
         this.SuspendLayout();
         //
         // button1
         //
         this.button1.Location = new System.Drawing.Point(96, 88);
         this.button1.Name = "button1";
         this.button1.TabIndex = 0;
         this.button1.Text = "CallableIDL";
         this.button1.Click += new System.EventHandler(this.button1_Click);
         //
         // Form1
         //
         this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
         this.ClientSize = new System.Drawing.Size(292, 266);
         this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                       this.button1});
         this.Name = "Form1";
         this.Text = "Form1";
         this.ResumeLayout(false);
      }
      #endregion

      ///
      /// The main entry point for the application.
      ///

      [STAThread]
      static void Main()
      {
         Application.Run(new Form1());
      }
      unsafe private void button1_Click(object sender, System.EventArgs e)
      {
         /* Initialize IDL */
         int Status = 0;
         Status = Idl32.IDL_Win32Init(0, (void *) (this.Handle), null, null);
         if (Status==0) MessageBox.Show("Failed to initialize IDL");
   
         /* Create an array of 10 double precision floating point numbers */
         double[] d=new double[10];

         /* Initialize array */
         for (int i=0;i<10;i++) d[i] = i;


         /* Use IDL_ImportNamedArray to access a C# array inside IDL */
         fixed (void *data=d)/* IDL needs a pointer to the data */
         {
            long[] dim = new long[8]; /* define dimensions array */
            dim[0] = 10;
            Idl32.IDL_ImportNamedArray("D", 1, dim, 5, data, null, null);
            string command = "D[9]=12"; /* Change the last element of D */
            Status = Idl32.IDL_ExecuteStr(command);
            command = "void=dialog_message('D = '+strcompress(strjoin(D)),/info)";
            Status = Idl32.IDL_ExecuteStr(command);
            if (d[9] == 12) MessageBox.Show("C# Array successfully altered by IDL");
            if (d[9] == 9) MessageBox.Show("IDL Failed to change original array");

            /* Use IDL_GetVarAddr to access an IDL array in C# */
            double *test; /* define a pointer to the array */
            long n=0;
            Idl32.IDL_ExecuteStr("A=dindgen(20)");
            Idl32.IDL_VarGetData(Idl32.IDL_GetVarAddr("A"),
               &n, (char **) &test, 0);

            /* Calculate the total sum of "lindgen(20)", it should be 190 */
            double total=0;
            for (int i=0;i<20;i++) total+=test[i];
            MessageBox.Show(total.ToString());
            if (total == 190) MessageBox.Show("C# successfully calculated the total (190)");
            else MessageBox.Show("C# Failed to calculate the correct total");
               
            /* Change the elements of the array by adding 100 */
            for (int i=0;i<20;i++) test[i]+=100.0;

            /* Show that the IDL array was modified from C# */
            command = "void=dialog_message('A = '+strcompress(strjoin(A)),/info)";
            Idl32.IDL_ExecuteStr(command);
         }   
         Status = Idl32.IDL_Cleanup(0);

      }
   }
}