从C#调用Fortran DLL

时间:2014-02-13 18:49:41

标签: c# fortran

我有一个关于从C#调用fortran DLL的问题(使用VS 2010)。

我无法运行此程序。当从C#进入Fortran代码时,在计算z的行(xy的总和)处,会弹出一个消息框:

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"

我该如何解决这个问题?

Fortran DLL被称为" TestDLL.DLL"

Fortran代码:

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#代码:

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);
        }
    }
}

2 个答案:

答案 0 :(得分:3)

添加C调用约定解决了我的堆栈不平衡问题。同时将[Out]更改为ref以指向相同的内存,而不是复制值。这是我的代码

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();
    }
}

附录

要将固定数组转换为托管数组,反之亦然,请使用以下代码

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];
                }
            }
        }
    }
}

答案 1 :(得分:0)

尝试:

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

如果这不起作用,请尝试创建一个非常简单的FORTRAN方法,该方法接受X / Y值并将它们加在一起并返回总和。这将允许您诊断整个过程是否正常工作。从那里开始工作,增加了更多的复杂性,直到它中断。这会告诉你是否有其他错误。