| December 21, 2008 8:00 AM PST | |
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:
public class FortranDllWrap
{
// 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)]
public static extern void FSUB(ref int A, ref int B);
[DllImport("FDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int FOO(ref int A);
}
This article applies to: Intel® Visual Fortran Compiler for Windows* Knowledge Base
For more complete information about compiler optimizations, see our Optimization Notice.
Comments (14) 
| July 19, 2009 5:08 AM PDT
Xiaoping Duan (Intel)
|
Hi Mark, C# code: //Example of passing array [DllImport("FDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern void ARR_SUB([In,Out] float[,] fArr, ref int row, ref int col); ... float[,] fArr = new float[3, 4]; int row = 3; int col = 4; ... // Call the Fortran subroutine FortranDllWrap.ARR_SUB(fArr, ref row, ref col); Fortran code: subroutine Arr_Sub (var,row,col) !DEC$ ATTRIBUTES DLLEXPORT :: Arr_Sub integer :: row,col real var(col,row) integer :: i, j write(*,*) "Array var received from C# as array is" write(*,*) var ! do something to change the value of var end subroutine Please note that in above example Fortran subroutine uses an implicit interface allowing the array argument to be passed without an Intel Fortran descriptor so the array parameter declaration in C# is a simple array. In some case Intel Fortran subroutine may require an array descriptor. In such case you should correctly interpret the array descriptor in C# so that Intel Fortran subroutine can obtain the information it needs. More information about Intel Fortran descriptor and how to handle it between Intel Fortran and non-Fortran code can be found under topic "Handling Arrays and Fortran Array Descriptors" of Intel Fortran Compiler documentation. |
| January 17, 2010 12:29 AM PST
alireza | could you please provide the example for calling this DLL code in fortran90 |
| January 6, 2011 7:49 AM PST
slippery
|
With the current Fortran compiler, I am having trouble with this example. I code a Fortran SUBROUTINE with the following: 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) I code a class in C# as follows: public class FortranDLLs { // Declares a managed prototype for a matrix of integers by value. [DllImport("<path>/Dll1.dll")] public static extern void DLL1([In, Out] int[,] pMatrix, ref int N); } Inside C#, I attempt to make the following call: int N = 5; int[,] myMoebius = new int[ 5, 5 ]; FortranDLLs.DLL1(myMoebius, ref N); When I hit the call to DLL1 in code, I get the following: System.BadImageFormatException was unhandled Message="An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)" Source="WindowsFormsApplication2" StackTrace: at FortranDLLs.Dll1(Int32[,] pMatrix, Int32& N) ... InnerException: Is there a compile option or something else I am missing on the Fortran or the C# side? |
| January 6, 2011 12:35 PM PST
slippery |
Please ignore the last. It came about when a 64-bit C# exe attempted to link with a 32-bit Fortran dll. The current problem comes with attempting to extract data from the 32-bit dll. I am running into memory violations and the stack being wiped out after calls to the dll. Is there a specific need for marshaling the data in order to get data OUT of the dll? |
| January 6, 2011 5:02 PM PST
Xiaoping Duan (Intel)
|
The error looks not like a coding error or compiler error. Would you please refer to http://blogs.msdn.com/b/arvindsh/archive/2009/06/21/tip-of-t.....issue.aspx and check if your Fortran and C# code were built for 32-bit? |
| January 6, 2011 5:23 PM PST
Xiaoping Duan (Intel)
|
Sorry, the URL in my last post should be: http://blogs.msdn.com/b/arvindsh/archive/2009/06/21/tip-of-t.....issue.aspx |
| January 9, 2011 2:55 PM PST
slippery |
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. |
| January 9, 2011 4:14 PM PST
slippery |
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. |
| January 9, 2011 4:18 PM PST
slippery
|
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. |
| January 9, 2011 6:11 PM PST
Xiaoping Duan (Intel)
|
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? |
| January 10, 2011 6:42 AM PST
slippery
|
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? |
| January 10, 2011 11:47 AM PST
slippery
|
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. |
| June 7, 2011 8:12 AM PDT
Benny |
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 |
Trackbacks (0)
Leave a comment 
Xiaoping Duan (Intel)
|


Mark Thyer
Following your example, I tried to get this working, but could not do. The array returned rubbish in Fortran.
However, If I removed the ref attribute from the C# methods, it appeared to work ok, which I do not understand.
Please advise