撰寫者: Devil(璉璉) Devil.bbs@vlsi1.iie.ncku.edu.tw -------------------------------------------------------------------------- 本範例簡單說明如何利用 VB call Fortran ---- VB 程式碼 ---------------------------------------- Private Declare Function test1 Lib "c:\test1\Release\test1.dll" () As Integer Private Sub Form_Load() Me.Show Print test1() End Sub ------------------------------------------------------- Fortran 部份: 用選單 [File][New] 選擇一 Workspace Project, 再選擇 Dynamic-Link Library 建立, 若已有完成之 *.for or f90 用選單 [Insert][Files into Project] 插入. ---- Fortran 程式碼 ----------------------------------- !ms$attributes dllexport, alias:'test1' :: test1 integer*2 function test1() test1=1 end function ------------------------------------------------------- 將要對外提供的函數以 !ms$attributes dllexport 方式指定. 我個人偏好將 !ms$attributes dllexport 放在檔 頭及所有函式之前, 以便管理. 在 !ms$attributes dllexport, alias:'test1' :: test1 中, 若僅寫成 !ms$attributes dllexport :: test1 則編譯時會將函數 test1 名以 _TEST1@0 方式對外提供, 則 VB 應改宣告成 Private Declare Function test1 Lib "c:\test1\Release\test1.dll" Alias "_TEST1@0" () As Integer 也就是將原函式名改大寫後前面加上 "_", 後面增加 "@" 及所 有引數全部位元組數. 若要以原名 call , 則加入 alias:'test1' , 則小寫的小寫, 方便引用. 若一切無誤, 將 fortran 檔編譯成 DLL, 並確認 DLL所在的路 徑則可供 vb 使用. 若 DLL檔在 PATH 中, Windows, Windows\System 中, 則不需指 定路徑. 以上大概是 VB call Fortran DLL 的範例及注意事項. ----------------------------------------------------------------------------- (續) VB5 Call FPS4 DLL 參數如何傳遞? (二) VB5 call PSF4 參數如何傳遞? VB5: Microsoft Visual BASIC 5.0 PSF4: Microsoft PowerStation Fortran 4.0 在 BASIC中,變數是很複雜的一部份,為了達成簡化程式的複雜性, VB 有特定的預設型態 (Variant),但此型態在其他語言中均不支援 ,因此在使用前應先宣告變數型態及瞭解其所佔位元組後再使用。 比較特別的是字串的部分, VB 中有不定長度的字串,這是任何語言 都沒有的,目前字串部分尚未測試完畢,本次暫時跳過,俟測試完畢 後再行討論。 一般數值傳遞時,可用傳址呼叫及傳值呼叫,將以兩例簡介之。 註:傳址呼叫可更改 VB 中變數的值,傳值呼叫則不行。 傳址呼叫: VB5 Private Declare Sub test2 Lib "c:\devil\test1\Release\test1.dll" (i As Integer) ... Call test2(TempI) Print TempI ... PSF4 !ms$attributes dllexport, alias:'test2' :: test2 subroutine test2(i) integer*2 i i=i*2 end subroutine 傳值呼叫: VB5 Private Declare Function test1 Lib "c:\devil\test1\Release\test1.dll" (ByVal i As Integer) As Integer ... TempI = test1(2) Print TempI ... PSF4 !ms$attributes dllexport, alias:'test1' :: test1 integer*2 function test1(i) !MS$ATTRIBUTES VALUE :: i integer*2 i test1=i*2 end function VB 在動態陣列上比其他語言皆容易使用,本例特別採用動態陣列 做為範本,當然本例亦可運用在靜態陣列上,而陣列在運用上一般 不建議採用傳值呼叫,因此本例僅對傳址呼叫測試。 註: PSF4 之陣列宣告採不定長度宣告。 陣列傳遞: VB5 Private Declare Function test3 Lib "c:\devil\test1\Release\test1.dll" (mytry As Integer) As Integer ... ReDim mytry(1 To 100) As Integer mytry(1) = 1 TempI = test3(mytry(1)) Print TempI, mytry(2) ... PSF4 !ms$attributes dllexport, alias:'test3' :: test3 integer*2 function test3(array) integer*2 array(*) array(2)=array(1)*10 test3=1 end function 附註: ----------------------------------------------------------------------------- PSF4 資料型態 VB5 資料型態 值域 ----------------------------------------------------------------------------- BYTE Byte 0 到 255 INTEGER*1 無 INTEGER*2 Integer -32,768 到 32,767 INTEGER*4 Long -2,147,483,648 到 2,147,483,647 REAL*4 Single +/- 3.402823E38 到 +/- 1.401298E-45 REAL*8 Double 4.94065645841247E-324 到 1.79769313486232E308 CHARACTER*1 無 CHARACTER*(*) String 1 到大約 65,400(固定長度字串) COMPLEX*4 無 COMPLEX*8 無 所有LOGICAL Integer True 或 False 無 Variant 無 Date January 1, 100 到 December 31, 9999 無 Currency -922,337,203,685,477.5808 到 922,337,203,685,477.5807 無 Decimal 無小數點 +/-79228162514264337593543950335 最小的非零值為 +/-0.0000000000000000000000000001 ----------------------------------------------------------------------------- 又 如果未指定 DLL檔的路徑, VB 將按照下列順序尋找檔案: 1.*.exe 檔案所在的目錄 2.目前目錄 3.Windows 系統目錄(通常為 \Windows\System) 4.Windows 目錄(不一定是 \Windows ) 5. Path 環境變數中的目錄 撰寫者: james007.bbs@cis.nctu.edu.tw(雅各) g854502@oz.nthu.edu.tw Devil(璉璉) Devil.bbs@vlsi1.iie.ncku.edu.tw -------------------------------------------------------------------------- 作 者: g854502@oz.nthu.edu.tw 日 期: Thu, 22 Jan 1998 10:33:24 +0800 Devil 你好: 我平常都是透過交大資科上 Fortran 版的,由於今天一直連不上,所以靈機一動就用 E-mail 來跟你討教。首先,我必須說明我並不是質疑你 post 的內容,我只是無法瞭 解自己犯了什麼錯誤,所以現在我將我的測試程式碼列在下面: PowerStation 部份: ======================================================== c234567 subroutine test(a,b,c) !ms$attributes dllexport, alias:'test' :: test real a(10),b(10),c(10) do i=1,10 a(i)=b(i)+c(i) end do end ======================================================== Visual Basic 部份: ======================================================== Private Declare Sub test Lib "Fortran.dll" (ByRef a() As Single, ByRef b() As Single, ByRef c() As Single) ---------------------------------------------------------------------------- Private Sub Command1_Click() Dim x1(10) As Single, y1(10) As Single, z1(10) As Single Picture1.Cls For i = 1 To 10 x1(i) = 10 + (i - 1) y1(i) = 20 + (i - 1) z1(i) = 30 + (i - 1) Next i Call test(x1, y1, z1) For i = 1 To 10 Picture1.Print i, x1(i), y1(i), z1(i) Next i End Sub ========================================================== 這個程式是可以執行的,但是結果就像根本沒有呼叫動態連結檔一樣,也就是說結果 如下列所示: ========================================================== 1 10 20 30 2 11 21 31 3 12 22 32 4 13 23 33 5 14 24 34 . . . . . . . . 10 19 29 39 =========================================================== 感覺結果根本沒傳到動態連結檔,所以根本沒計算。而當我將陣列改為單一變數,也就 是說將 a(10) 改為 a 時(以此類推),結果就是對的。唉!真奇怪。可否請你幫忙看 看呢?謝謝你! 新春愉快 雅各 -------------------------------------------------------------------------- By Devil: 我從新試了一個程式碼,大體上約和你相同,我只用我的習慣去宣告,先行測試 程式結果,如同函數中一般,在副程式中一樣能輕鬆使用,程式碼如下: -------------------------------------------------------------------------- VB5 Private Declare Sub test6 Lib "c:\devil\test1\Release\test1.dll" (mytry1 As Single, mytry2 As Single, mytry3 As Single, SL As Long, SE As Long) ... ReDim mytry1(1 To 10) As Single ReDim mytry2(1 To 10) As Single ReDim mytry3(1 To 10) As Single For i = 1 To 10 mytry1(i) = i mytry2(i) = i * 2 mytry3(i) = i * 3 Next i Call test6(mytry1(1), mytry2(1), mytry3(1), 1, 10) For i = 1 To 10 Print mytry1(i), mytry2(i), mytry3(i) Next i PSF4: !ms$attributes dllexport, alias:'test6' :: test6 subroutine test6(array1,array2,array3,StartB,EndB) real*4 array1(*), array2(*), array3(*) integer*4 StartB, EndB, i do i=StartB,EndB,1 array1(i)=array2(i)+array3(i) end do end subroutine -------------------------------------------------------------------------- 因此我反過來檢討你的程式為何行不通,反覆觀看,可能原因如下: 1. VB 的預設基底為 0,而 PSF4 為 1。 也就是說在 VB 中宣告陣列 a(10) 實際上是 a(0) - a(10)共 11 個。但在 PSF4 中宣告陣列 a(10) 實際上是 a(1) - a(10)共 10 個。因此在陣列傳 遞上大小就有問題。 2.建議在 PSF4 採用不定長度陣列宣告。 在 PSF4 中宣告陣列 a(*) 意指陣列大小來自父程序,若在父程序中宣告陣 列大小為 10 則將沿用父程序的大小為 10 (此處的父程序為 VB5的呼叫) 。 3.在 VB5中傳遞請以陣列第一元素傳遞。 在 VB5中用 a(1) 傳遞可將 a(1) 的記憶體位置傳遞給 PSF4 ,根據陣列存 放連續觀念,可順利取用陣列。反觀若以 a傳遞,將無法掌控所傳遞之資料 。 如上,請參閱。另本件認定具有參考價值,故不以 E-Mail 方式回覆,改以張 貼版上回覆。 註:VB 的 ByRef 為預設,可省略。 以下摘於: mori(鑑古知今) mori.bbs@vlsi1.iie.ncku.edu.tw Devil(璉璉) Devil.bbs@vlsi1.iie.ncku.edu.tw -------------------------------------------------------------------------- 發信人: mori@bar (鑑古知今), 信區: fortran 標 題: 請問VB如何伝"字串"給Fortran呢? 請問VB要如何伝一個字串變數給Fortran呢? 本來想在VB中設計一個界面,在填入檔名後要伝給fortran來開檔、讀值、運算的 可是這個檔名一直都伝不到fortran去(也不知到要如何判斷有沒有伝過去...) 要不就就程式跑到一半就掛了,真是麻煩,請問要如何解決 我的程式如下: VB Code: [Visual Basic 5.0] Option Explicit Dim first, least, inc, s1 As Integer Dim wo As String Private Declare Function aa Lib "c:\tmp\aa.dll" (ByVal wo As String, ByVal first As Integer, ByVal least As Integer, ByVal inc As Integer) As Integer Private Sub Command1_Click() Text1.Text = aa("chara", 1, 5, 7) End Sub Fortran Code: [Visual Fortran] !ms$attributes dllexport, alias:'aa' :: aa integer*2 function aa(wo,first,least,inc) character*5 wo [value] integer*2 first [value] integer*2 least [value] integer*2 inc [value] aa = first+least+inc open (5,file='c:\tmp\t.txt') write(5,*) 'ok' write(5,*) wo ^^^加這個每次都掛...:( return end 在沒要求 fortran做檔案的字串輸時,text1.text伝回的計算結果都是正確的 但一要求輸出字串名便不行了... 再請問一下,被呼叫的函數(Fortran)是不是不能對瑩幕做write(*,*)的動作呢? 如果不能的話,有沒有什麼方法可以確知伝過去的參數有正確地收到呀?? 請高手指教 謝謝!! -------------------------------------------------------------------------- 發信人: Devil@bar (璉璉), 信區: fortran 標 題: Re: 請問VB如何伝"字串"給Fortran呢? : !ms$attributes dllexport, alias:'aa' :: aa ^^^^ 加入 stdcall, 1. 所有語言均不支援 BASIC 中的 "不定長度字串" 所以 BASIC 部分需使用 dim aa as string *5 再用引數傳遞 2. Fortran 可用 Character*(*) wo 代表 wo 長度由呼叫方決定 3. Fortran, BASIC 的字串在記憶體中排列方式不同, 所以要先通知 fortran 用 C (or pascal 忘了) 方式收參數, 所以加 stdcall 4. Fortran 在 DLL時, 不可用 write(*,*) (沒有輸出區), 但可以將 VB 的 form 或 picture 的 hDC 傳入, 在 DLL 中用 Windows API TextOut 函數將文字 show 到或 picture 中 -------------------------------------------------------------------------- 發信人: mori@bar (鑑古知今), 信區: fortran 標 題: Re: 請問VB如何伝"字串"給Fortran呢? : : !ms$attributes dllexport, alias:'aa' :: aa : ^^^^ : 加入 stdcall, 看不太懂這行的意思耶,可否說明一下呢? 因為我要伝的字串是要伝檔名用的,所以沒法先確定字元的長度,在這種情形又要 如何伝不定長度的字串呢? 我有想到一個辨法,就是將字串變數寫到一個檔中,再由Fortran讀取此檔來完成參 數的伝送,雖然可行,但有些麻煩....而且覺得這樣寫好像寫得並不"漂亮".... 您所說的這點是指要Fortran再把值伝回VB後由VB Display結果嗎?或是Fortran可以 直接呼叫API呀? 再請教一下,fortran要如何直接呼叫 Windows API呢? 謝謝!! -------------------------------------------------------------------------- 發信人: Devil@bar (璉璉), 信區: fortran 標 題: Re: 請問VB如何伝"字串"給Fortran呢? : 看不太懂這行的意思耶,可否說明一下呢? !ms$attributes dllexport, stdcall, alias:'aa' :: aa ^^^^^^^ 如果你過去也用 Fortran 就可以了解, Fortran 在傳字串本來就是這樣... ex. [In BASIC] dim sFile as string*12 sFile="test.txt" 實際 sFile="test.txt " so [In Fortran] ... integer i, LocateN character*(*) sFile ... do i=1,LEN(sFile) if (sFile(i:i) .eq. ' ') then LocateN=i-1 end if end do open(Unit=FileUnit, File=sFile(1:LocateN), ...) ... 因為此時 Fortran 是動態函式庫, 不像獨立執行檔有自己的 form(輸出區) 所以須 由 Fortran 直接呼叫 Windows API 在 Fortran 內指定將文字輸出到那個裝置環境 (DC) 中, 而 VB 的 form 或 pictureBox 均有 hDC 的屬性, 所以可以透過引數將 hDC 傳送給 Fortran, 而 TextOut 是不論那個 form 均可, 只要 hDC 對... ps. 裝置環境 (DC) 是指某個視窗畫面, 在 Windows中, 包含印表機都是一個 DC , hDC 是指 DC 的絕對代碼, 你可以想像 open(unit=...,file=...)中的unit 與 file 之間的關係, 每個 DC 有一個只有他自己獨有的編號, hDC ! 所以透 過 hDC可以將文字送到任何地方, 包括桌布, 或是 Help 檔中. 要在 Fortran 中用 Windows API 的話, 以你用的 Visual Fortran 來說, 要先在 宣告區加 USE DFWIN 在 DFWIN.F90 中 TextOut 是這樣宣告的 interface logical(4) function TextOut (dummy0,dummy1,dummy2,dummy3,dummy4) !DEC$ IF DEFINED(_X86_) !DEC$ ATTRIBUTES STDCALL, ALIAS : '_TextOutA@20' :: TextOut !DEC$ ELSE !DEC$ ATTRIBUTES STDCALL, ALIAS : 'TextOutA' :: TextOut !DEC$ ENDIF !DEC$ ATTRIBUTES REFERENCE :: dummy3 integer dummy0 integer dummy1 integer dummy2 character*(*) dummy3 integer dummy4 end function TextOut end interface 若你在灌 Visual Fortran 中有灌 SDK 說明, 則可用 Search 找 TextOut The TextOut function writes a character string at the specified location, using the currently selected font. BOOL TextOut( 成功傳回 True HDC hdc, // handle of device context VB 的 form.hDC int nXStart, // x-coordinate of starting position x 座標 int nYStart, // y-coordinate of starting position y 座標 (相對 hDC) LPCTSTR lpString, // address of string 字串 int cbString // number of characters in string 字元數 ); ex. for Fortran summyB=TextOut(VBhDC,0,0,'Test',len('Test') 又 Windows API 在 VB 中也可使用, 你可在 VB 中測試 ex. for VB summyB=TextOut(Me.hDC,0,0,"Test",Len("Test"))