尝试从C#控制台应用程序调用存储在Fortran DLL中的子例程时出现以下错误:“name.exe中出现未处理的类型'System.EntryPointNotFoundException'异常附加信息:无法找到入口点在DLL'Fortran.dll'中命名为'Fortran_Subroutine'“几乎所有与此错误消息相关的帖子都与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();
}
答案 0 :(得分:2)
您的第二个编译器指令(第二个!DEC $行)不正确 - 它缺少指定哪个Fortran具有指定属性的:: MFNWT_INIT
部分(DECORATE和ALIAS)。我希望编译器发出有关语法问题的警告。
它的价值(假设您使用的是ifort> = 11左右的版本,而不是其祖先之一):如果你想使用C调用约定,你最好摆脱第二个指令完全,只在SUBROUTINE语句中使用后缀BIND(C,NAME="MFNWT_INIT")
。
答案 1 :(得分:1)
很可能DLL正在使用装饰名称导出函数。找出该名称是什么,并在C#端使用它。
[DllImport("MF_DLL.dll", CallingConvention=CallingConvention.Cdecl,
EntryPoint="DecoratedNameGoesHere")]
public static extern void MFNWT_INIT();
要查找导出的名称,请使用dumpbin或Dependency Walker等工具。
你确定你的DLL使用cdecl调用约定吗?
答案 2 :(得分:0)
在IVF帮助中,查看使用混合语言构建应用程序/编程/调整混合语言/属性属性和调用约定中的调用约定。这就是版本11的版本。它可能已经移动到您正在使用的版本中。帮助中令人困惑的位是导出的符号是大写还是小写。它与为旧的MS Fortran 77编译器(大约1986年)编写的略有不同。如果您不确定导出的符号,请使用depends查看它们是什么。
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语言中得到这个,因为它是调用者的责任。
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。