1

尝试从 C# 控制台应用程序调用存储在 Fortran DLL 中的子例程时,不断收到以下错误:“name.exe 中发生类型为‘System.EntryPointNotFoundException’的未处理异常附加信息:无法找到名为‘Fortran_Subroutine’的入口点' 在 DLL 'Fortran.dll' 中” 几乎所有与此错误消息相关的帖子都与 C#/C++ 相关。Fortran 项目具有将 DLL 复制到 C# 项目 (CSharp_proj\bin\debug) 的构建后事件。

Fortran 代码对 !DEC$ 使用了两次调用,它们看起来好吗?:

C
  MODULE MF_DLL
C
  CONTAINS
C
  SUBROUTINE MFNWT_INIT()
C      
  !DEC$ ATTRIBUTES DLLEXPORT :: MFNWT_INIT
  !DEC$ ATTRIBUTES DECORATE, ALIAS: "MFNWT_INIT"
C
  USE GLOBAL
  ...(do stuff)

  RETURN
  END SUBROUTINE MFNWT_INIT

调用 fortran 的 C# 代码,DLLImport 调用看起来好吗?:

using System.Runtime.InteropServices;

public static class CustomMODSIM
{
    public static Model myModel = new Model();
    private static SortedList myStreamNodes;

    public static void Main(string[] CmdArgs)
    {
        string FileName = CmdArgs[0];
        myModel.Init += OnInitialize;
        ...(do more stuff)...

        //Function call that will invoke "OnInitialize" below
        myProgram.RunSolver(myModel);
    }

    private static void OnInitialize()
    {
        //call Fortran function
        MFNWT_INIT();

        //Initialize list of stream nodes
        myStreamNodes = new SortedList();
        Node m_Node = myModel.FindNode("NonStorage1");
        ...(do more stuff)
    }

    //Fortran DLL interface
    [DllImport("MF_DLL.dll", CallingConvention=CallingConvention.Cdecl)]
    public static extern void MFNWT_INIT();
}
4

3 回答 3

2

您的第二个编译器指令(第二个 !DEC$ 行)不正确 - 它缺少:: MFNWT_INIT指定哪个 Fortran 事物具有指定属性(DECORATE 和 ALIAS)的部分。我希望编译器会发出有关语法问题的警告。

对于它的价值(假设您使用的是 ifort >= 11 左右的版本而不是它的祖先之一):鉴于您想使用 C 调用约定,您最好完全摆脱第二个指令,而只是使用 SUBROUTINE 语句的后缀BIND(C,NAME="MFNWT_INIT")

于 2013-04-27T21:44:35.297 回答
1

很可能 DLL 正在导出带有修饰名称的函数。找出该名称是什么并在 C# 端使用它。

[DllImport("MF_DLL.dll", CallingConvention=CallingConvention.Cdecl,
    EntryPoint="DecoratedNameGoesHere")]
public static extern void MFNWT_INIT();

要查找导出的名称,请使用 dumpbin 或 Dependency Walker 之类的工具。

您确定您的 DLL 使用 cdecl 调用约定吗?

于 2013-04-27T21:16:25.173 回答
0

在 IVF 帮助中,查看构建应用程序/使用混合语言编程/调整混合语言/属性属性中的调用约定和调用约定。那是它在第 11 版上的位置。它可能已在您使用的版本中移动。帮助中令人困惑的一点是导出的符号是大写还是小写。它与为旧的 MS Fortran 77 编译器(大约 1986 年)编写的版本略有不同。如果您不确定导出的符号,请使用取决于找出它们是什么。

1)如果你没有使用别名,那么它在 Fortran 端应该是这样的

MODULE MF_DLL
CONTAINS
SUBROUTINE MFNWT_INIT()
!DEC$ ATTRIBUTES STDCALL, DLLEXPORT :: MFNWT_INIT

如果使用 STDCALL,将会有两个导出符号:MF_DLL_mp_MFNWT_INIT 和 _MF_DLL_mp_MFNWT_INIT@0。如果未指定 STDCALL,则默认为 C。在这种情况下,您只会使用 MF_DLL_mp_MFNWT_INIT。@ 符号后面的数字是例程在返回给调用者之前需要删除的堆栈上的字节数。你不会在 C decl 中得到这个,因为它是调用者的责任。

2) 在 C# 端使用 stdcall

[DllImport("MF_DLL.dll", CallingConvention=CallingConvention.StdCall,
EntryPoint="_MF_DLL_mp_MFNWT_INIT@0")]
public static extern void MFNWT_INIT();

3) 在 C# 端使用 Cdecl

[DllImport("MF_DLL.dll", CallingConvention=CallingConvention.Cdecl,
EntryPoint="MF_DLL_mp_MFNWT_INIT")]
public static extern void MFNWT_INIT();

不同之处在于,在 C 中,它不需要知道参数的数量,而在 stdcall 中则需要。这会影响参数的堆叠/取消堆叠。如果出错,它将进行调用,运行 Fortran 例程,然后在退出时崩溃。在您的情况下,这并不重要,因为没有参数,但很高兴把它做好。

4) 如果使用别名,名称会改变,但调用约定不会改变。在您的情况下,您应该指定

!                                             ,-- This is the name in DLL export
!DEC$ ATTRIBUTES DECORATE, ALIAS: "MFNWT_INIT"::MFNWT_INIT

使用 C decl,您将获得 MFNWT_INIT。使用 STDCALL,您将获得 MFNWT_INIT 和 _MFNWT_INIT@0 在 C# 端,使用 C Decl 时不需要入口点。仅在使用 STDCALL 时才需要。

5) 如果在 Fortran 和 C# 中都使用该例程,那么最好坚持使用 stdcall。

于 2013-04-28T19:39:37.150 回答