3

I have a question about calling a fortran DLL from C# (using VS 2010).

I cannot get this program to run. When stepping into the Fortran code from C#, at the line where z is calculated (sum of x and y), a message box pops up:

An unhandled exception of type 'System.AccessViolationException' occurred in WinApp_FortranDLLStruct2.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt"

How do I fix this?

The Fortran DLL is called "TestDLL.DLL"

Fortran code:

MODULE TESTING
    TYPE Point      
            REAL*8 :: x(10)
            REAL*8 :: y(10)
            REAL*8 :: z(10)
    ENDTYPE Point
    end module

    !DEC$ ATTRIBUTES DLLEXPORT::CalcPoint
    !DEC$ ATTRIBUTES ALIAS : "CalcPoint" :: CalcPoint

    SUBROUTINE CalcPoint(myPts)          
    use TESTING
    IMPLICIT NONE 
    INTEGER*4 I,J,NPTS
    REAL*8 Sum_Out
    TYPE(Point) :: myPts

    do i = 1,10
        myPts.z(i) = myPts.x(i) + myPts.y(i)
    enddo

    END SUBROUTINE CalcPoint

C# code:

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

namespace WinApp_FortranDLLStruct2 {
    public partial class Form1 : Form {
        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct myPoint {
            public fixed double x[10];
            public fixed double y[10];
            public fixed double z[10];
        }
        public class FortranCall {
            [DllImport("TestDLL.dll")]
            public unsafe static extern void CalcPoint([Out] myPoint t);     
        }

        public Form1()
        {
            InitializeComponent();
        }

        private  unsafe  void  button1_Click(object sender, EventArgs e) {
            int i;
            double d1 = 1.0;

            myPoint T = new myPoint();
            for (i = 0; i < 10; i++) {
                T.x[i] = (i+1)*d1;
                T.y[i] = (i+2)*d1;
            }
            FortranCall.CalcPoint(T);
        }
    }
}
Magus
  • 1,302
  • 9
  • 16
  • It's probably breaking on the `[Out]` attribute. Try using pointers instead. – Dai Feb 13 '14 at 18:57
  • What @Dai said, but wrap the CalcPoint method with a method that has a standard return value, perhaps. – Magus Feb 13 '14 at 19:00
  • Not sure how fixed works in C#, but wouldn't you want to make your myPoint T as the fixed object? I understand that fixed prevents the GC from moving the object in memory (thus keeping the pointer valid), so maybe when your Fortran code tries to read from the memory, your object is no longer there. Just a thought. – Pedro Feb 13 '14 at 20:05
  • On another thought, should't the CalcPoint parameter be ref instead? – Pedro Feb 13 '14 at 20:07
  • Related post: http://stackoverflow.com/q/16738861/380384 – John Alexiou Feb 13 '14 at 21:06
  • Related post: http://stackoverflow.com/questions/10317691/making-fortran-dll-and-calling-it-from-c-sharp/10318273#10318273 – John Alexiou Feb 13 '14 at 21:06
  • Related post: http://stackoverflow.com/q/6231751/380384 – John Alexiou Feb 13 '14 at 21:09
  • I am getting an unbalanced stack error. I am trying without the fixed arrays, using an array of `struct` instead of a struct of arrays. – John Alexiou Feb 13 '14 at 21:38
  • 1
    Out of curiosity, what Fortran compiler are you using? – Dai Feb 13 '14 at 21:56
  • You are new to `C#` and diving into fixed arrays? Brave soul! – John Alexiou Feb 13 '14 at 22:06

2 Answers2

3

Adding C calling conventions solved my stack imbalance problems. Also change the [Out] to ref in order to point at the same memory and not copy values around. Here is my code

FORTRAN

MODULE TESTING
    INTEGER, PARAMETER :: SIZE = 10
    TYPE Point
    SEQUENCE
        REAL*8 :: x(SIZE), y(SIZE) , z(SIZE)
    ENDTYPE Point
end module


SUBROUTINE CalcPoint(myPts)
!DEC$ ATTRIBUTES DLLEXPORT::CalcPoint
!DEC$ ATTRIBUTES ALIAS : 'CalcPoint' :: CalcPoint
use TESTING
IMPLICIT NONE 
! Arguments
TYPE(Point), INTENT(INOUT) :: myPts
! Local variables
INTEGER*4 I

do i = 1,SIZE
    myPts%z(i) = myPts%x(i) + myPts%y(i)
enddo

END SUBROUTINE CalcPoint

C#

[StructLayout(LayoutKind.Sequential)]
public unsafe struct myPoint
{        
    public const int size = 10;
    public fixed double x[size];
    public fixed double y[size];
    public fixed double z[size];

    public void Initialize(double d1)
    {
        fixed (double* x_ptr=x, y_ptr=y, z_ptr=z)
        {
            for (int i=0; i<size; i++)
            {
                x_ptr[i]=(i+1)*d1;
                y_ptr[i]=(i+1)*d1;
                z_ptr[i]=0.0;
            }
        }
    }
}

class Program
{
    [DllImport(@"FortranDll1.dll", CallingConvention=CallingConvention.Cdecl)]
    public unsafe static extern void CalcPoint(ref myPoint t);

    unsafe Program()
    {
        double d1=1.0;
        var T=new myPoint();
        T.Initialize(d1);
        Program.CalcPoint(ref T);
        // T.z = {2,4,6,...}
    }

    static void Main(string[] args)
    {
        new Program();
    }
}

Appendix

To convert a fixed array to managed array and vise versa use the following code

public unsafe struct myPoint
{
    public const int size=10;
    public fixed double x[size];
    ...
    public double[] X
    {
        get
        {
            double[] res=new double[size];
            fixed (double* ptr=x)
            {
                for (int i=0; i<size; i++)
                {
                    res[i]=ptr[i];
                }
            }
            return res;
        }
        set
        {
            if (value.Length>size) throw new IndexOutOfRangeException();
            fixed (double* ptr=x)
            {
                for (int i=0; i<value.Length; i++)
                {
                    ptr[i]=value[i];
                }
            }
        }
    }
}
John Alexiou
  • 28,472
  • 11
  • 77
  • 133
0

Try:

[DllImport("TestDLL.dll")] public unsafe static extern void CalcPoint(ref myPoint t);

If that doesn't work, try creating a really simple FORTRAN method that takes X/Y values and adds them together and returns the sum. This will allow you to diagnose that the overall process is working correctly. Work up from there, adding more complexity in until it breaks. That will tell you if something else is wrong.

Jon Barker
  • 1,788
  • 15
  • 25
  • Is this an answer or a guess? – PVitt Feb 13 '14 at 21:29
  • Thanks for all your responses on this question. Since I am new to C#, i will try all these options out. I did try the "ref" option, but it created same error. Thanks again. Will keep you posted. – user3307346 Feb 13 '14 at 21:39