4.4 Step 4: Implementing the MyEngineDotNetAccess

The fourth step is to implement the MyEngineDotNetAccess class (Figure 10).

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.

using System;
using System.Text;

namespace Oatc.OpenMI.Examples.ModelComponents.SimpleRiver.Wrapper
{

	public class SimpleRiverEngineDotNetAccess
	{
		IntPtr _FortranDllHandle;

		public void Initialize(string filePath)
		{
            _FortranDllHandle = Kernel32Wrapper.LoadLibrary(@"Oatc.OpenMI.Examples.ModelComponents.SimpleRiver.Engine.dll");

            string curDir = System.IO.Directory.GetCurrentDirectory();

			if(!(SimpleRiverEngineDllAccess.Initialize(filePath, ((uint) filePath.Length))))
			{
				CreateAndThrowException();
			}
		}

        public void PerformTimeStep()
        {
            if (!(SimpleRiverEngineDllAccess.PerformTimeStep()))
            {
                CreateAndThrowException();
            }
        }

		public void Finish()
		{
			if(!(SimpleRiverEngineDllAccess.Finish()))
			{
				CreateAndThrowException();
			}
			while(Kernel32Wrapper.FreeLibrary(_FortranDllHandle));
		}
        
		private void CreateAndThrowException()
		{
			int numberOfMessages = 0;
			numberOfMessages = SimpleRiverEngineDllAccess.GetNumberOfMessages();
			string message = "Error Message from SimpleRiver Fortran Core ";

			for (int i = 0; i < numberOfMessages; i++)
			{
				int n = i;
				StringBuilder messageFromCore = new StringBuilder("                                                        ");
				SimpleRiverEngineDllAccess.GetMessage(ref n, messageFromCore, (uint) messageFromCore.Length);
				message +="; ";
				message += messageFromCore.ToString().Trim();
			}
			throw new Exception(message);
		}
	}
}
  • No labels