VB呼叫Fortran的dll檔時出現 呼叫規格錯誤

小弟利用Fortran編寫一個程式,程式碼如下:

real function circle_area(radius)

!DEC$ ATTRIBUTES DLLEXPORT :: CIRCLE_AREA

!DEC$ ATTRIBUTES ALIAS : "Circle_Area" :: CIRCLE_AREA

implicit none

real radius

real, parameter :: PI = 3.14159

circle_area = radius*radius*PI

return

end function

integer function sum(a)

!DEC$ ATTRIBUTES DLLEXPORT :: SUM

implicit none

integer :: a(10)

integer i

sum=0

do i=1,10

sum=sum+a(i)

end do

return

end function

subroutine MakeLower(string)

!DEC$ ATTRIBUTES DLLEXPORT :: MAKELOWER

implicit none

character(len=*) :: string

integer :: len, i, code

len = len_trim(string)

do i=1,len

code = ichar(string(i:i))

if ( code >= ichar('a') .and. code <= ichar('z') ) then

string(i:i) = char(code-32)

end if

end do

return

end subroutine

我利用g95跟dlltool來編譯,編譯指令為

-c $(FileName) -Wuninitialized -Wimplicit-none -Wunused-vars -Wunset-vars -fbounds-check -ftrace=full -O2

-z $(FileTitle).def --export-all-symbols $(FileTitle).o

-shared -mrtd -o $(FileTitle).dll $(FileTitle).o $(FileTitle).def

編譯好之後可以產生 .o .def .dll 三種檔案

由.def檔中可以看到我的進入點為

makelower_ @ 1

sum_ @ 2

circle_area__ @ 3

我利用這三個進入點 配合上下面的Code

Private Declare Function circle_area__ Lib "forlib.dll" (r As Single) As Single

Private Declare Sub makelower_ Lib "forlib.dll" (ByVal s As String, ByVal i As Long)

Private Declare Function sum_ Lib "forlib.dll" (r As Long) As Long

Private Sub Command1_Click()

Dim r As Single

Dim a As Single

r = Val(Text1.Text)

a = circle_area__(r)

Label3 = Str(a)

End Sub

Private Sub Command2_Click()

Dim s As String

s = Text2.Text

Call makelower_(s, Len(s))

Text2.Text = s

End Sub

Private Sub Command3_Click()

Dim a(10) As Long

Dim i As Long

Dim total As Long

For i = 0 To 9

a(i) = Rnd() * 9 + 1

Next i

Label4.Caption = Str(a(0))

For i = 1 To 9

Label4.Caption = Label4.Caption + "+" + Str(a(i))

Next i

total = sum_(a(0))

Label4.Caption = Label4.Caption + "=" + Str(total)

End Sub

已更新項目:

在執行時會出現 執行階段錯誤 49 "DLL 呼叫規格錯誤" 這段文字

偵錯的話 可以看到此VB程式中有三個錯誤,分別為:

a = circle_area__(r)

Call makelower_(s, Len(s))

total = sum_(a(0))

也就是呼叫的部分出錯,目前已經找到正確進入位置了,但是又遇到這個錯誤

上網找了很久,還是找不到相關資訊,不知道這一行應該要如何修改才行呢?

以上,希望有高手能幫忙解惑,謝謝 :)

2 個已更新項目:

A TK UE 2AEO OR UFO 大大,非常感謝您的建議

我也是想說我平常在g95上 ! 後面都是註解,為什麼可以這樣執行

後來我上網找了gfortran,他有 !$GCC 這種開頭,我用了這個方法之後就可以成功掛載上去,但是問題是如果我想要用這種方法的話,我從.o檔開始就得用gfortran的編譯器,這會導致我在編譯.exe檔時用的是g95,編譯.dll檔時反而得用gfortran

我後來在測試說我是否能夠全部改用gfortran,結果我發現gfortran的格式跟g95不一樣(應該說差很多

3 個已更新項目:

我有一個已經可以成功運行於g95的程式,原本輸出格式是

13 0. 30.55555

在我換了gfortran之後就變成了

13 0.0000000000000000 30.555555600000002

這就算了,問題是原本可以執行的程式也因此變成無法跑出正確結果

(我做的是模擬運算,程式本來可以依照我的方程式跟迴圈跑出正確的結果,在換了gfortran之後他會在迴圈跑到一半時卡在迴圈內,雖然程式有在跑,但是無法收斂

我在猜是數值傳遞上出了問題,此外,程式在編譯時出現了警告訊息

4 個已更新項目:

警告的內容大致上是

Warning: Deleted feature: PAUSE statement at (1)

BaseCase.f90:728.23:

IF(BL10 .GT. BLC) PAUSE

1

Warning: Deleted feature: PAUSE statement at (1)

Warning: .drectve `-aligncomm:"___DTOR_LIST__",2 ' unrecognized

5 個已更新項目:

Warning: .drectve `-aligncomm:"___CTOR_LIST__",2' unrecognized

> Terminated with exit code 0.

我的疑問在於,我個人覺得我寫的code算是中規中矩,沒有用一些花式寫法,就算我把pause砍掉,最終還是無法得到正確的答案(應該說,根本跑不動,程式會在一半就卡在迴圈內

因此我想請問,如果我平常在編譯Fortran時,這個code就吃死這個編譯器了? 未來要移植到Visual Fortran上的話是否也會遇到相同的問題?

6 個已更新項目:

(我知道不同的編譯器之間會有不同的特殊語法,但是我目前的程式碼連一般的迴圈都會跑到錯誤我就覺得有點誇張了說O口O

此外,關於dll檔的問題,我後來把 !DEC$ 那些敘述給刪掉了(因為這個也只有註解的功能

接著,我在網路上找到相關寫法,正確解答為

編譯dll檔的語法 應該從

-shared -mrtd -o $(FileTitle).dll $(FileTitle).o $(FileTitle).def

變成

-shared -mrtd -o $(FileTitle).dll $(FileTitle).f90 $(FileTitle).def

7 個已更新項目:

用這個方法所編譯出來的dll檔就可以

如果可以的話,我想請問說,同一個程式碼在不同編譯器下編譯時,如果遇到吃不進去的狀況,應該怎麼辦? 能否提供建議?

還希望您至少能將意見的內容補到回答上,我希望直接給您這20點,真的非常感謝您的提醒 :)

3 個解答

評分
  • 卸貨
    Lv 5
    6 年前
    最佳解答

    我現在沒空幫你研究 Fortran DLL 的東西,但可以給你一點提示。

    !DEC$

    這是 Visual Fortran 專屬的用法,你用在 GCC 上面當然不行。

    你應該搜尋 gfortran 如何導出 C 語言可使用的函式,再搜尋 Visual Basic 如何使用 C 語言 DLL 的方法。

    我弄一個 C 語言在中間有兩個原因:

    1. 你直接找 Fortran -> 很可能查不到什麼東西。

    2. C 語言在程式語言裡面就像英文一樣是世界語言,幾乎所有語言與 C 的轉換相關文章都找得到,也幾乎所有語言都有辦法使用以 C 語言寫成的 Library。

    2015-01-24 22:37:57 補充:

    更正:你直接找 Fortran -> VB 很可能查不到什麼東西。

    2015-01-25 22:43:56 補充:

    我現在沒空幫你研究 Fortran DLL 的東西,但可以給你一點提示。

    !DEC$

    這是 Visual Fortran 專屬的用法,你用在 GCC 上面當然不行。

    你應該搜尋 gfortran 如何導出 C 語言可使用的函式,再搜尋 Visual Basic 如何使用 C 語言 DLL 的方法。

    我弄一個 C 語言在中間有兩個原因:

    1. 你直接找 Fortran -> VB 很可能查不到什麼東西。

    2. C 語言在程式語言裡面就像英文一樣是世界語言,幾乎所有語言與 C 的轉換相關文章都找得到,也幾乎所有語言都有辦法使用以 C 語言寫成的 Library。

    -------- 以上是意見區搬過來的 --------

    首先,13 0.、30.55555 和 13 0.0000000000000000、30.555555600000002 你不覺得是一樣的東西嗎?浮點數的一些小雜訊罷了,你不喜歡的話可以用格式化輸出把它在顯示時忽略掉。

    再來,我印象中 Fortran 裡沒有 PAUSE 這種東西,有一些非標準而是工具專有的東西本來就不被推荐使用。

    還有,你不一定要用 gfortran,如果 g95 有提供 DLL 製做的話也可以使用,不過你就要去查 g95 如何導出 DLL 函式的文章。是的,這很討厭,因為 Fortran 標準並沒有定義關於動態函式庫的東西(其實 C 也沒有),所以有關的做法一定是編譯器相依的。

    因為你的疑問,我後來仔細的看了一下你的程式碼,理論上你的程式碼本身應該沒什麼問題,我覺得問題非常可能就是出在函式的呼叫方式上。

    以 sum 為例,它接收一個長度為十的陣列,然而你有查清楚 Fortran 和 VB 之間對於陣列的處理有什麼差異嗎?你確定你收到的東西都是對的嗎?

    建議你先把函式內的動作全部暫時屏蔽,只把你收到的參數全部列印出來,然後用 VB 呼叫這個函式,檢查一下參數的傳遞是沒有問題的再繼續往下試驗。

    同樣的,MakeLower 接收的是一個字串,這個更慘!字串在不同程式語言裡大概都是字元組成陣列的「變形」,其差異性比陣列還大。你若沒搞清楚這些資料底層格式的差異而對其做應該做的處理,那你能收到正確的參數才有鬼,後面的運算怎麼可能順利進行?

    所以我才會建議你 Fortran DLL 的導出函式要改成 C 語言相容格式,然後再把這個 DLL 當成 C 語言寫的 DLL 讓 VB 來呼叫。

    結論就是,你先測試一下收到的參數是不是都是對的?然後研究一下 VB、C、Fortran 之間基本變數、參數傳遞、以及陣列、字串等等的轉換。

    除了努力研究期間的差異然後設法解決參數傳遞問題以外,還有另外最消極的做法,那就是不要在函式之間(包含參數及返回值)傳遞任何的陣列或字串,只傳遞基本變數如整數、浮點數等,這樣問題是最少的。

    另外補充一點,不管什麼編譯器,其編譯過程大概都是:

    *.f90 -> *.o -> *.exe/*.dll

    並不會因為 g95 或 gfortran 而不同,只是 GCC 可以在內部暫存並完成這些工作,而讓你表面上看起來好像是 *.f90 -> *.dll 而已。

    • Commenter avatar登入以對解答發表意見
  • 6 年前

    下面網址應該對你有幫助

    TS777。CC

    • Commenter avatar登入以對解答發表意見
  • 6 年前

    我有一個已經可以成功運行於g95的程式,原本輸出格式是

    13 0. 30.55555

    在我換了gfortran之後就變成了

    13 0.0000000000000000 30.555555600000002

    這就算了,問題是原本可以執行的程式也因此變成無法跑出正確結果

    (我做的是模擬運算,程式本來可以依照我的方程式跟迴圈跑出正確的結果,在換了gfortran之後他會在迴圈跑到一半時卡在迴圈內,雖然程式有在跑,但是無法收斂

    我在猜是數值傳遞上出了問題,此外,程式在編譯時出現了警告訊息

    • Commenter avatar登入以對解答發表意見
還有問題?馬上發問,尋求解答。