Visual Basic 5.0 SP3 傳遞字串給 Visual Fortran 5.0c


1.Visual Fortran 5.0c 製作 DLL 檔的基本做法

如圖,選擇一個新專案,Win32 Dynamic-Link Library (若圖看不清楚,請在圖上點一下)

開啟編譯模式為 DLL 的新專案

接下來產生一個新的 Fortran 檔,若有現成函數,此部份可簡略

開啟 Fortran 90 格式新檔案

完成後編譯可得到 DLL 檔

程式碼部分

測試由 Visual Basic 傳遞檔名字串給 Fortran DLL 檔,並在 DLL 內寫出資訊至輸出檔

近日又測試了一些,將分三種範例介紹,要選哪一種就看您的選擇啦!

說明:

  1. 若 Visual BASIC 部分在整合環境下非執行檔,則 DLLWrite.dll 需放於VB.exe 所在路徑或公用路徑,否則需設路徑,且若 Fortran 部分 open 未指定路徑,會 open 至 vb.exe 所在路徑 (C:\Program Files\DevStudio\VB)
  2. Fortran 與 Visual Basic 字串在記憶體中排列方式不同,因此在收 VB 字串部分一直有問題,只好用這種變通的方式收參數
  3. FreeFileUnit 函數請參見 Fortran 首頁取得未使用的檔案代號
  4. 目前介紹三個方法,應該還會有其他方法可供使用才對

法一:
這個方法中,是利用 Visual Basic 以 As Any 宣告時,將不會檢查傳遞引數的方式硬將字串傳給 Fortran ,並讓 Fortran 以字元陣列接收參數後,再轉回一般 Fortran 字串!是我之前用的爛方法,保留下來是可以讓網友稍微能窺視其中記憶體轉移的觀念,而這個方法還可以再改善,可利用宣告 myFileName 與 tFileName 共用記憶體的方式,及 UBound 函數省略迴圈轉置字串的動作
------- In Visual BASIC 5.0 Form1.frm --------
Private Declare Function myWrite Lib "E:\User\TLCheng\Life\FOR\DLLWrite\DLLWrite.dll" (FileName As Any, ByVal LenChar As Long) As Long

Private Sub Form_Load()

Me.Show
tFileName$ = CStr(App.Path + "\Hello.out")
Print tFileName, myWrite(ByVal tFileName$, Len(tFileName$))

End Sub
---- In Visual Fortran 5.0 DLLWrite.f90 ------
integer*4 Function myWrite(myFileName,LenChar)

!DEC$ ATTRIBUTES STDCALL, DLLEXPORT, alias:'myWrite' :: myWrite

integer*4 FreeFileUnit,FileUnit
character*1 myFileName(*)
integer*4 LenChar,i
character*1024 tFileName

do i=1,LenChar,1
 tFileName(i:i)=myFileName(i)
end do

FileUnit=FreeFileUnit()

open(FileUnit, File=tFileName(1:LenChar))
write(FileUnit,'(1x,a)') 'Hello DLL!'
close(FileUnit)

myWrite=FileUnit

end

法二:
這個方法基本觀念是由美商威能資訊台灣分公司廖建華先生所提供的範例修改,經我仔細查證,原始資料列於 MSDN KnowlegageBase 中的 Q161825、Q123841、Q121457、Q106553、Q118643 等資料,是利用 Visual Basic 以使用者自訂型態宣告傳遞引數時,將不會檢查傳遞引數的方式,直接將該塊記憶體傳給 Fortran ,並讓 Fortran 以固定長度接收字串參數!只有這個方法是將字串長度固定死不能變動,因為若在 Visual Basic 中的 Type 宣告 PassString as String ,則記憶體中只有 4 Bytes 在型態裡,透過遠程指標指到 VB 的字串,若傳遞給 Fortran ,則 Fortran 只能收到長整數而已
------- In Visual BASIC 5.0 Form1.frm --------
Private Declare Function myWrite Lib "E:\User\TLCheng\Life\FOR\DLLWrite\DLLWrite.dll" (FileName As typFortranString) As Long

Type typFortranString
 PassString as String*1024
End Type

Private Sub Form_Load()

Me.Show
Dim tFileName as typFortranString

tFileName.PassString = CStr(App.Path + "\Hello.out")
Print tFileName, myWrite(tFileName)

End Sub
---- In Visual Fortran 5.0 DLLWrite.f90 ------
integer*4 Function myWrite(myFileName)

!DEC$ ATTRIBUTES STDCALL, DLLEXPORT, alias:'myWrite' :: myWrite

integer*4 FreeFileUnit,FileUnit
character*1024 myFileName

FileUnit=FreeFileUnit()

open(FileUnit, File=myFileName)
write(FileUnit,'(1x,a)') 'Hello DLL!'
close(FileUnit)

myWrite=FileUnit

end

法三:
這個方法中,是目前我能想出最好的方法了,這是參考 Visual Basic 及 Visual Fortran 的線上說明,並檢視記憶體內容才試出的方法,若你有用過 Windows API ,你會發現很像,在 Visual Basic 先以傳值方式傳遞字串後,再傳字串長度,讓 Fortran 以傳址呼叫接收字串參數!因此,若有需利用字串引數修改字串內容時,本方法亦可使用,同 Visual Basic 使用 Windows API 一般
------- In Visual BASIC 5.0 Form1.frm --------
Private Declare Function myWrite Lib "E:\User\TLCheng\Life\FOR\DLLWrite\DLLWrite.dll" (ByVal FileName As String, ByVal StringLen As Long) As Long

Private Sub Form_Load()

Me.Show
tFileName$ = CStr(App.Path + "\Hello.out")
Print tFileName$, myWriteTest(tFileName$, Len(tFileName$))

End Sub
---- In Visual Fortran 5.0 DLLWrite.f90 ------
integer*4 Function myWrite(myFileName)

!DEC$ ATTRIBUTES DLLEXPORT, alias:'myWrite' :: myWrite

integer*4 FreeFileUnit,FileUnit
character*(*) myFileName

FileUnit=FreeFileUnit()

open(FileUnit, File=myFileName)
write(FileUnit,'(1x,a)') 'Hello DLL!'
close(FileUnit)

myWrite=FileUnit

end

福傳語言