Calling FORTRAN function or subroutine in DLL from C# code

Calling FORTRAN function or subroutine in DLL from C# code

Intel Fortran does not support generate managed code. To call a FORTRAN function or subroutine from C# code you need build Fortran code into a DLL (Dynamic Link Library), and then use Platform Invoke, a service that enables managed code to call the unmanaged function or subroutines inside the DLL. Platform Invoke service locates and calls unmanaged code as an exported function. It also marshals the call's arguments, such as input and output parameters, integers, strings, arrays, and structures, as needed. To use the service, add below line into your C# code:

 

using System.Runtime.InteropServices;

 

It is recommended to wrap the Fortran function or subroutine in a managed class. Within the class, you define a static method for each Fortran function or subroutine you want to call. Use the DllImportAttribute to identify the DLL and function. Mark the method with the static and extern modifiers. The definition can include additional information, such as the calling convention used in passing method arguments.

 

Example:

 

Fortran Function and Subroutine:

 

INTERFACE

    SUBROUTINE FSUB (A,B)

           !DEC$ ATTRIBUTES DLLEXPORT :: FSUB

        INTEGER :: A,B

END SUBROUTINE

 

    FUNCTION FOO(A)

           !DEC$ ATTRIBUTES DLLEXPORT :: FOO

            INTEGER :: A

            INTEGER :: FOO

    END FUNCTION

END INTERFACE

 

C# Wrapper Class:

 

    publicclassFortranDllWrap

    {

        // CallingConvention.Cdecl must be used since the stack is

        // cleaned up by the caller in Intel Fortran by default

        //

        [DllImport("FDll.dll", CallingConvention = CallingConvention.Cdecl)]

        publicstaticexternvoid FSUB(refint A, refint B);

        [DllImport("FDll.dll", CallingConvention = CallingConvention.Cdecl)]

        publicstaticexternint FOO(refint A);

    }   

 

For more complete information about compiler optimizations, see our Optimization Notice.

16 comments

Top
Duan, Xiaoping (Intel)'s picture

Unable to find an entry point named 'FSUB' in DLL 'FortranDLL01.dll'

Can you check the exported symbols in the dll by command "dumpbin/exports FortranDLL01.dll"?

 

Bradley P.'s picture

I'm a newbie.  I tried to follow this article very explicitly, but I'm getting the following:

  • Unable to find an entry point named 'FSUB' in DLL 'FortranDLL01.dll'

Any ideas?

Thanks!

Brad.

anonymous's picture

Hello,

I have the following problem: I have a complex fortran program which consists of a lot of subroutines (one of these can we call the main routine, so this is the method which calls all the other ones...) . 2 questions:
1. is it possible to make the dll of the main subroutine for integrating the whole programm into this dll? (if not, how can I solve this problem? thougth about writing another main routine which includes all of the subroutines, but I have no experience in fortran, so please give me a hint/link)
2. is it possible to catch the output of the programm through the dll? the fortran programm (written for DOS) is able to if I start the programm with "program.exe > output.dat".

Thanks for your help

Yours sincerly Benny

slippery's picture

I made another attempt at this, without the buffering and explicit marshaling of data.

It worked.

Obviously, my problem with the current code lies in the marshaling. In the "real" app I need the ability to pass a Fortran DLL an array of variable length, along the lines of

INTEGER, INTENT(INOUT) :: COL
REAL (KIND = F), DIMENSION(ROW, *), INTENT(OUT) :: ARRAY

Then, based on COL, I need to access the array in C# like mp_array[c][r] where the extents are known.

Thank you for your help.

slippery's picture

Here is the Fortran code as is:

SUBROUTINE DLL1(MOEBIUS, N)

! Expose subroutine Dll1 to users of this DLL
!
!DEC$ ATTRIBUTES DLLEXPORT :: Dll1

! Variables
INTEGER, DIMENSION(N, N) :: MOEBIUS !, INTENT(OUT)
INTEGER :: N !, INTENT(IN)
! Body of Dll1
! Several lines of code including console output of the MOEBIUS array
END SUBROUTINE DLL1

Here is the relevant C# code up to the point at which the stack is wiped:

using System;
using System.Collections.Generic;
using System.IO;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
private Bitmap DrawingArea; // Area to draw on.

private System.Random rnd;
private Pen myPen;

public Form1()
{
InitializeComponent();
rnd = new System.Random();
myPen = new Pen(Color.Blue);
}

private void button1_Click(object sender, EventArgs e)
{
const int N = 5;
int N_copy = N;

// array ByRef
int[] array2 = new int[N * N];
Console.WriteLine("nnInteger array passed ByRef before call:");
for (int i = 0; i < array2.Length; i++)
{
array2[i] = (i % 3) - 1;
Console.Write(" " + array2[i]);
}
IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(N)
* array2.Length);
Marshal.Copy(array2, 0, buffer, array2.Length);

FortranDLLs.DLL1(ref buffer, ref N_copy);
// if (size > 0)
// {
int[] arrayRes = new int[N * N];
Marshal.Copy(buffer, arrayRes, 0, N * N);
// Many more lines of code including Load, Closed and Paint overrides for the Form
}
}

public class FortranDLLs
{
// Declares a managed prototype for an array of integers by reference.
// The array size can change, but the array is not copied back
// automatically because the marshaler does not know the resulting size.
// The copy must be performed manually.
[DllImport("..\..\..\..\Dll1\Release\Dll1.dll")]
public static extern void DLL1(ref IntPtr array, ref int N);
}

The last call to Marshal.Copy will not work with the buffer having been set to 1, N_copy being set to 0, and this now null. The Fortran is compiled Release + Win32. The C# is compiled Debug + x86. Here is a copy of the console output for the code up to the call to Marshal.Copy.

Integer array passed ByRef before call:
-1 0 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 -1MOEBIUS(3,3) 1
MOEBIUS(4,3) -1
MOEBIUS(4,4) -1
MOEBIUS(3,4) 0
MOEBIUS(2,4) -1
MOEBIUS(2,3) 1
MOEBIUS(2,2) -1
MOEBIUS(3,2) 0
MOEBIUS(4,2) 0
MOEBIUS(5,2) 1
MOEBIUS(5,3) -1
MOEBIUS(5,4) 0
MOEBIUS(5,5) -1
MOEBIUS(4,5) 1
MOEBIUS(3,5) 1
MOEBIUS(2,5) 0
MOEBIUS(1,5) -1
MOEBIUS(1,4) 0
MOEBIUS(1,3) -1
MOEBIUS(1,2) 0
MOEBIUS(1,1) 1
MOEBIUS(2,1) 1
MOEBIUS(3,1) -1
MOEBIUS(4,1) 0
MOEBIUS(5,1) 0

What have I missed?

Duan, Xiaoping (Intel)'s picture

I couldn't reproduce the errors:

For image format error I tried "64bit C# + 64bit Fortran DLL" and "32bit C# using CorFlags + 32bit Fortran DLL". Neither produced the error.

In boths cases the values assigned to the array in Fortran DLL can be returned to C# code. The size of N wasn't changed after the call in C#.

Would you please provide more detail on your Fortran and C# code?

slippery's picture

I recompiled the dll and the C# exe. The C# exe is compiled with x86 on a 64-bit machine. I took the debug *.exe and ran it with the CorFlags conversion tool. I get the same error.

The Fortran dll SUBROUTINE has two arguments, an integer array and an integer size. The dll is being called and is running properly as evidenced by console output from the dll. On return from the call, the IntPtr buffer is now set to 1, the int size is now 0 (input was 5), and the this pointer is now null.

anonymous's picture

I took my debug *.exe, which was compiled x86, and run it with CorFlags conversion tool. I continue to get the same errors.

I call the Fortran dll and the subroutine is executed. I have verified this with some console output. Immediately after the call, the IntPtr buffer used in the call is reset to 1, the N value used for dimensioning is reset to 0, and the this pointer is now null.

anonymous's picture

So, even if I compile the app "x86" on my 64-bit machine, I still need to run

CorFlags.exe app.exe /32BIT+

Why is this necessary? By the way, I have not done this yet.

Duan, Xiaoping (Intel)'s picture

Sorry, the URL in my last post should be:

http://blogs.msdn.com/b/arvindsh/archive/2009/06/21/tip-of-the-day-an-attempt-was-made-to-load-a-program-with-an-incorrect-format-net-p-invoke-issue.aspx

Pages

Add a Comment

Have a technical question? Visit our forums. Have site or software product issues? Contact support.