2013-04-27 32 views
0

保持試圖調用從C#控制檯應用程序存儲在一個Fortran DLL子程序時收到以下錯誤:「類型‘System.EntryPointNotFoundException’未處理的異常發生在名稱。 exe文件的附加信息:無法找到DLL「Fortran.dll」」幾乎所有與此相關的錯誤信息的帖子涉及到C#/ C++命名爲切入點「Fortran_Subroutine」。該項目的Fortran有一個生成後事件複製DLL轉移到C#項目(CSharp_proj \ BIN \調試)。C#/ Fortran語言:無法找到切入點

的Fortran代碼使用了兩個調用!DEC $,做他們看起來OK?:

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 

的C#代碼調用FORTRAN,莫非dllimport的通話外觀OK?:

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

*「無法在DLL'Fortran.dll'中找到名爲'Fortran_Subroutine'的入口點。」*很難相信它是這樣說的。當然它說「無法找到名爲'MFNWT_INIT'的入口點...」? – 2013-04-27 21:12:56

+0

我一直在嘗試如此多種不同的組合,我只是在示例錯誤消息中使用了通用名稱。正如你指出的,我添加到帖子中的特定代碼的錯誤消息是「無法在DLL MF_DLL.dll中找到名爲'MFNWT_INIT'的入口點'」 – user2256085 2013-04-27 21:43:47

+0

如果您可以發佈「dumpbin」的結果/導出MF_DLL.dll'。 – user7116 2013-04-27 21:50:48

回答

1

您的第二個編譯器指令(第二個!DEC $行)不正確 - 它缺少指定哪個Fortran事物具有指定屬性(DECORATE和ALIAS)的:: MFNWT_INIT部分。我希望編譯器發出關於語法問題的警告。

爲什麼它的價值(假設你使用的版本ifort> = 11左右,而不是它的祖先之一):鑑於你想使用C調用約定,你最好放棄第二個指令完全,並且只需在SUBROUTINE語句中使用後綴BIND(C,NAME="MFNWT_INIT")

+0

我認爲它默認爲符號末尾的下劃線,沒有任何BIND修飾符或對編譯器選項的更改 – user7116 2013-04-27 21:51:27

+0

我保存了第二行!DEC $行代碼並修改了Fortran子例程以查看例如:「SUBROUTINE MFNWT_INIT()BIND(C,NAME =」MFNWT_INIT「)」That do it! – user2256085 2013-04-27 22:12:35

+0

我還應該添加其他可能會發現這篇文章有幫助,我將C#代碼行更改爲:[DllImport(「MF_DLL.dll」,CallingConvention = CallingConvention。 ** StdCall **)] – user2256085 2013-04-27 22:14:39

1

最可能的DLL是用裝飾名稱導出函數。找出這個名字,並在C#端使用它。

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

要找到導出的名稱,請使用像dumpbin或Dependency Walker這樣的工具。

你肯定你的DLL使用cdecl調用約定?

+0

這將極有可能被'MFNWT_INIT_'或' _MFNWT_INIT'使用大多數默認的編譯器選項 – user7116 2013-04-27 21:50:18

+0

@six是的,問題的關鍵是顯示提問者如何診斷問題 – 2013-04-27 21:52:09

+0

我只是指出許多Fortran編譯器使用的奇數尾部__,我會刪除這個if它錯過了你的觀點 – user7116 2013-04-27 21:53:22

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 decl中得到這個,因爲這是調用者的責任。使用STDCALL

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

3)在使用CDECL

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

的差值C#側

2)在C#的一面是,在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會更好。