Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
using System;
using System.Run-time.InteropServices;
using System.Text;
namespace MyOrganisation.OpenMI.MyModel
{
   public class MyEngineDLLAccess
   {
      [DLLImport(@'C:\MyEngine\bin\MyEngine.DLL',
         EntryPoint = 'INITIALIZE',
         SetLastError=true,
         ExactSpelling = true,
         CallingConvention=CallingConvention.Cdecl)\]
      public static extern bool Initialize(string filePath, uint length);
      [DLLImport(@'C:\MyEngine\bin\MyEngine.DLL',
         EntryPoint = 'PERFORMTIMESTEP',
         SetLastError=true,
         ExactSpelling = true,
         CallingConvention=CallingConvention.Cdecl)\]
      public static extern bool PerformTimeStep();
      [DLLImport(@'C:\MyEngine\bin\MyEngine.DLL',  
         EntryPoint = 'FINISH',
         SetLastError=true,
         ExactSpelling = true,
         CallingConvention=CallingConvention.Cdecl)\]
      public static extern bool Finish();
   }
}
Step 4: Implementing the MyEngineDotNetAccess

The fourth step is to implement the MyEngineDotNetAccess class (Figure 10). Image Added
Fig 10. MyEngineDotNetAccess class
 
The MyEngineDotNetAccess has two purposes: to change the calling conventions to C# conventions and to change error messages into .NET exceptions.
The listed code below shows the Simple River example code for a MyEngineDotNetAccess class that implements the Initialize method, the PerformTimeStep method and the Finish method. In
each of these methods the corresponding method in the MyEngineDLLAccess class is called and, if this method returns false, the error message from the engine is queried through the GetMessage method (following which an exception is created and thrown).
Note that the MyEngineDLLAccess class is not instantiated. All the methods in this class are static and can be accessed directly by referencing the class. (Note that to make the example simpler, not all DLL import statements are shown.)
The normal convention for Fortran DLLs is that values are returned through the function (effectively, they are passed by reference); for C# results are passed by value. Therefore
there may be a need to state explicitly that C# parameters passed to a Fortran routine are to be passed by reference. In Fortran, arrays normally start from index 1 whereas in C# the
convention is to start from zero. These differences can be handled in the MyEngineDotNetAccess class.
When you have completed the implementation of the methods shown below you can create and implement the corresponding test class and run the unit test.

Code Block

using System.Text;
namespace MyOrganisation.OpenMI.MyModel
{
   public class MyEngineDotNetAccess
   {
      public void Initialize(string filePath)
      {
         if(!(MyModelDLL.Initialize(filePath, ((uint) filePath.Length))))
         {
            CreateAndThrowException();
         }
      }
 
      public void PerformTimeStep()
      {
         if(!(MyModelDLL.PerformTimeStep()))
         {
             CreateAndThrowException();
         }
      }

      public void Finish()
      {
         if(!(MyModel.Finish()))
         {
            CreateAndThrowException();
         }
      }

      private void CreateAndThrowException()
      {
         int numberOfMessages = 0;
         numberOfMessages = MyModelDLL.GetNumberOfMessages();
         string message = 'Error Message from MyModel ';
         for (int i = 0; i < numberOfMessages; i++)
         {
             int n = i;
             StringBuilder messageFromCore = new StringBuilder(' ');
             MyModelDLL.GetMessage(ref n, messageFromCore,(uint) messageFromCore.Length);
             message +='; ';
             message += messageFromCore.ToString().Trim();
         }
         throw new Exception(message);
     }
}