E-PROPATHとして配布されているDLLをMS-Excel,Visual Basic,Visual FortranおよびDelphiから 呼び出す(インポートする)方法を説明し,いくつかの利用例を示す. Visual C/C++からの利用はここにまとめた.
現在のE-PROPATHではP-PROPATH,A-PROPATHおよびM-PROPATHの各関数およびサブルーチンが 提供されており,これらはPROPATH関数と同じ関数名か"J+(物質名)_(関数名)"の 形の関数名のどちらも呼び出せるようになっている. 例えばアンモニアPROPATHのPST関数ならば, "PST"あるいは"JNH3_PST"のいずれでも呼出しが可能である.
他言語で作成したプログラムからDLLを呼び出す場合, 静的インポートと動的インポートのいずれかを選択することができる. いずれもプログラムの実行時にリンクされることには変わりがないが, リンクされる時期に違いがある. いわゆるDLLの動的リンクとスタティックライブラリの静的リンクと の違いとは意味が異なる.
静的インポートとして宣言したDLLは, プログラムの起動時にメモリ上にロードされてプログラムにリンクされ,プログラムの 実行中はずっとメモリ上に存在し,プログラムの終了時にメモリ上から開放される. 静的インポートのための手続きは比較的容易であるが,多くのDLLをロードする場合は プログラムの起動に時間がかかる. また,一つでもインポートに失敗した関数があった場合,プログラムの起動は 直ちに中断される.
一方,動的インポートは,プログラムの実行中, 対象となるインポート関数が呼ばれる段階になって初めてメモリ上にロードされてプログラムにリンクされる. インポート関数の呼び出しが終了した時点でDLLはメモリ上から開放される. 多くの関数をインポートする場合,動的インポートの方がより効率的であるが, インポートのための手続きはポインタ操作を必要とし,静的インポートよりも若干複雑である.
MS-ExcelおよびVisual Basicでは明示的にポインタを扱うことができないので, DLLの利用は静的インポートに限定される. Visual FortranおよびDelphiでは,状況に応じて静的インポートと動的インポートを 使い分けることができる.
いずれのインポートにしても,Windowsは以下の順序でDLLを検索し,最初に 見つかったDLLをロードする.
なお,DLLが絶対パス付きで指定された場合はこの限りではない.
MS-Excel
Visual Basic
Visual Fortran
Delphi
E-PROPATHでは,すべてのDLLについてExcelのマクロ言語でインポート関数を 宣言したファイル(インポートファイル(一般的な用語ではない)を提供している. したがって,あらためてインポート関数の宣言を書く必要はなく,インポートファイルを Excelのブックに取り込むだけで直ちにDLLを利用することができる.
ただし,Excelからの利用では,圧力および温度の単位がそれぞれ[bar]および[℃]に 限定される. また,E-PROPATHが提供するインポートファイルに宣言されているのは関数のみであり, M-PROPATHのサブルーチンも複数の関数呼出しに置き換えられたものが宣言されている.
Excelのマクロ言語はVBA(Visual Basic for Application)と呼ばれる Visual Basicのサブセットであり,E--PROPATHが提供するインポートファイルはそのまま Visual Basicでも利用することができる. 以下はアンモニアPROPATHのインポートファイルJNH3.BASをExcelのブックに取り込む手順である.
E-PROPATHが提供するインポートファイルはVisual Basicの標準モジュールである. したがって,インポートファイルをVisual Basicのプロジェクトに追加するだけで, 直ちにDLLを利用することができる.
ただし,"J+(物質名)_(関数名)"の関数名で呼び出すときや
サブルーチンを呼び出すときはユーザー側で関数宣言を書かなければならない.
以下はフォームモジュールに書いた関数宣言の例である.
Private Declare Function JN2_TSP _
Lib "jn2.dll" (ByVal P As Single) As Single
Private Declare Function JH2O_HPT _
Lib "jh2o.dll" (ByVal P As Single, ByVal T as Single) As Single
Private Declare Function JNH3_FC _
Lib "jnh3.dll" (ByVal C As String) As Single
Private Declare Sub SUBXY Lib "jawmx.dll" (ByRef J As Long, _
ByVal T As Single, ByVal P As Single, ByRef X As Single, _
ByRef Y As Single, ByRef VL As Single, ByRef VV As Single, _
ByRef HL As Single, ByRef HV As Single, ByRef SL As Single, _
ByRef SV As Single)
Private Declare Sub KPAMES Lib "jawmx.dll" (ByVal Kpa As Long, _
ByVal Mes As Long)
Private Declare Sub STNKAS Lib "jawmx.dll" (ByVal Kstn As Long, _
ByVal Kas As Long)
Private Declare Sub SUBXY Lib "jawmx.dll" (ByRef J As Long, _
ByVal T As Single, ByVal P As Single, _
ByRef X As Single, ByRef Y As Single, _
ByRef VL As Single, ByRef VV As Single, _
ByRef HL As Single, ByRef HV As Single, _
ByRef SL As Single, ByRef SV As Single)
Private Sub GoBtn_Click()
Dim P As Single, T As Single, X As Single, Y As Single
Dim VL As Single, VV As Single, HL As Single, HV As Single
Dim SL As Single, SV As Single
Dim IT As Long, J As Long
Dim Item As ListItem
Call KPAMES(2, 0)
Call STNKAS(0, 0)
P = 1#
For IT = 270 To 300 Step 5
T = IT
Call SUBXY(J, T, P, X, Y, VL, VV, HL, HV, SL, SV)
Set Item = ResultList.ListItems.Add
Item.Text = IT
Item.SubItems(1) = Format(X, "0.00000")
Item.SubItems(2) = Format(Y, "0.00000")
Next IT
End Sub
PROPATH自体はFortranで書かれているから,Visual Fortranで 敢えてDLLを使う必要はなく,プロジェクトにPROPATHのソースコードを直接追加すればよい. ただし,この場合は複数のPROPATHライブラリを同時に使うことができない. 例えば,窒素と酸素のPROPATHライブラリをプロジェクトに追加した場合, PSTやHPTなどの関数が両方のソースコードで定義されているから, リンク時に関数名の競合が起こる.
E-PROPATHが提供するDLLの関数は,"J+(物質名)_(関数名)"の関数名でも呼び出すことが できるから,Visual Fortran上で複数PROPATHの同時使用が可能になる.
例えば,次の例はJN2.DLL,JO2.DLLおよびJAIR.DLLを静的インポートし,
窒素および酸素の飽和温度と空気の沸点温度および気泡点温度を1barから10barまで
計算するメインプログラムである.
PROGRAM MAIN
REAL TS_N2, TS_O2, TB_AIR, TD_AIR
INTEGER KPA, MES, I
INTERFACE
! Import JN2_KPAMES from JN2.DLL as N2_KPAMES
SUBROUTINE N2_KPAMES(KPA, MES)
!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS : 'JN2_KPAMES' :: N2_KPAMES
INTEGER KPA, MES
END SUBROUTINE
! Import JN2_TSP from JN2.DLL as N2_TSP
REAL FUNCTION N2_TSP(P)
!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS : 'JN2_TSP' :: N2_TSP
REAL P
END FUNCTION
! Import JO2_KPAMES from JO2.DLL as O2_KPAMES
SUBROUTINE O2_KPAMES(KPA, MES)
!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS : 'JO2_KPAMES' :: O2_KPAMES
INTEGER KPA, MES
END SUBROUTINE
! Import JO2_TSP from JO2.DLL as O2_TSP
REAL FUNCTION O2_TSP(P)
!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS : 'JO2_TSP' :: O2_TSP
REAL P
END FUNCTION
! Import JAIR_KPAMES from JAIR.DLL as AIR_KPAMES
SUBROUTINE AIR_KPAMES(KPA, MES)
!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS : 'JAIR_KPAMES' :: AIR_KPAMES
INTEGER KPA, MES
END SUBROUTINE
! Import JAIR_TSPD from JAIR.DLL as AIR_TSPD
REAL FUNCTION AIR_TSPD(P)
!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS : 'JAIR_TSPD' :: AIR_TSPD
REAL P
END FUNCTION
! Import JAIR_TSPDD from JAIR.DLL as AIR_TSPDD
REAL FUNCTION AIR_TSPDD(P)
!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS : 'JAIR_TSPDD' :: AIR_TSPDD
REAL P
END FUNCTION
END INTERFACE
KPA = 2
MES = 1
CALL N2_KPAMES(KPA, MES)
CALL O2_KPAMES(KPA, MES)
CALL AIR_KPAMES(KPA, MES)
WRITE(*, *) ' P[bar] TS_N2[K] TS_O2[K] TB_AIR[K] TD_AIR[K]'
WRITE(*, *) '-----------------------------------------------------'
DO I = 1, 10
P = FLOAT( I )
TS_N2 = N2_TSP( P )
TS_O2 = O2_TSP( P )
TB_AIR = AIR_TSPD( P )
TD_AIR = AIR_TSPDD( P )
WRITE(*, '(I6, 4F12.5)') I, TS_N2, TS_O2, TB_AIR, TD_AIR
END DO
STOP
END
! Output
!
! P[bar] TS_N2[K] TS_O2[K] TB_AIR[K] TD_AIR[K]
! -----------------------------------------------------
! 1 77.23573 90.05882 78.68433 81.88921
! 2 83.61795 97.23663 85.31065 88.36304
! 3 87.89919 102.02858 89.73100 92.64751
! 4 91.22420 105.73900 93.15569 95.94832
! 5 93.98593 108.81424 95.99759 98.67522
! 6 96.37074 111.46553 98.45160 101.02104
! 7 98.48300 113.81096 100.62650 103.09322
! 8 100.38760 115.92383 102.58965 104.95818
! 9 102.12788 117.85304 104.38596 106.66010
! 10 103.73436 119.63301 106.04695 108.22998
SUBROUTINE N2_KPAMES(KPA, MES)
!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS : 'JN2_KPAMES' :: N2_KPAMES
INTEGER KPA, MES
END SUBROUTINE
こののコードをDeveloper Studioでメイクするときは,JN2.DLL,JO2.DLLおよび JAIR.DLLを作成したときに一緒に作成されたインポートライブラリJN2.LIB,JO2.LIBおよび JAIR.OBJをプロジェクトに追加しなければならない. Visual FortranやVisual C/C++では,DLLの関数をインポートする際にDLL名を 指定できないので,このインポートライブラリが必要になる. コマンドラインでメイクするときは,メインプログラムをコンパイルしたのち, 次のようにする.
link MAIN.OBJ JN2.LIB JO2.LIB JAIR.LIB
Visual Fortranではポインタを明示的に使うことができ,
DLLの動的インポートも可能である.
動的インポートするためには,まずWindows APIのLoadLibrary関数に用いて
DLLをロードする.
ロードに成功した場合,この関数はDLLのハンドルを返すので,
GetProcAddress関数を用いてインポートしたい関数のアドレスを
取得する.
動的インポートの場合,関数名ではなくGetProcAddressが返すアドレスによって
インポート関数を呼び出すので,予めインポート関数に適合した関数ポインタ
を用意しておかなければならない.
次の例はJHE4.DLLを動的にインポートし,HPT,SPTおよびVPT関数を
順に呼び出した例である.
PROGRAM DLLLOAD
USE DFWIN
INTERFACE
SUBROUTINE SUB(KPA, MES)
!DEC$ ATTRIBUTES STDCALL:: SUB
INTEGER KPA, MES
END SUBROUTINE
REAL FUNCTION FUNC(P, T)
!DEC$ ATTRIBUTES STDCALL:: FUNC
REAL P, T
END FUNCTION
END INTERFACE
POINTER (P1, SUB), (P2, FUNC)
REAL PRES, TEMP
INTEGER hDLL
LOGICAL STATUS
PRES = 1E5
TEMP = 1E2
hDLL = LoadLibrary( "JHE4.DLL" )
IF (hDLL.EQ.0) THEN
WRITE(*, *) 'Error occurred loading JHE4.DLL'
ELSE
P1 = GetProcAddress( hDLL, "KPAMES" )
CALL SUB( 0, 0 )
P2 = GetProcAddress( hDLL, "HPT" )
WRITE(*, *) 'HPT = ', FUNC( PRES, TEMP ), ' [J/kg]'
P2 = GetProcAddress( hDLL, "SPT" )
WRITE(*, *) 'SPT = ', FUNC( PRES, TEMP ), ' [J/kgK]'
P2 = GetProcAddress( hDLL, "VPT" )
WRITE(*, *) 'VPT = ', FUNC( PRES, TEMP ), ' [m3/kg]'
STATUS = FreeLibrary( hDLL )
END IF
STOP
END
!
! Output
! HPT = 519609.2 [J/kg]
! SPT = 25843.04 [J/kgK]
! VPT = 2.080224 [m3/kg]
まず,INTERFACE部で,二つの整数を引数とするサブルーチンSUBと
二つの単精度実数を引数とし戻り値が単精度実数である関数FUNCを定義する.
いずれも呼び出し規約にSTDCALLを指定する.
また,変数宣言部で,
POINTER (P1, SUB), (P2, FUNC)
DLLをロードするときは,LoadLibrary関数を用いて,
hDLL = LoadLibrary( "JHE4.DLL" )
次にGetProcAddress関数を用いてDLL内のサブルーチンKPAMESのアドレス
を取得し,結果をポインタP1に代入する.
P1 = GetProcAddress( hDLL, "KPAMES" )
最後にFreeLibrary関数によってDLLをアンロードする. この関数の引数はロードしたDLLのハンドルである.
例えば,JNH3.DLL内のPSTを静的インポートする場合,呼出し側の
スコープ内の適切な場所に次のように宣言する.
function PST( T: Single ): Single; stdcall; external 'jnh3.dll';
procedure SUBXY( var J: Integer; T, P: Single;
var X, Y, VL, VV, HL, HV, SL, SV: Single ); stdcall; external 'jawmx.dll';
procedure TF_SUBXY( var J: Integer; T, P: Single;
var X, Y, VL, VV, HL, HV, SL, SV: Single ); stdcall;
external 'jawmx2.dll' name 'SUBXY';
const DLLNAME1 = 'jawmx.dll';
const DLLNAME2 = 'jawmx2.dll';
procedure IK_KPAMES( Kpa, Mes: Integer ); stdcall;
external DLLNAME1 name 'KPAMES';
procedure IK_STNKAS( Stn, Kas: Integer ); stdcall;
external DLLNAME1 name 'STNKAS';
procedure TF_KPAMES( Kpa, Mes: Integer ); stdcall;
external DLLNAME2 name 'KPAMES';
procedure TF_STNKAS( Stn, Kas: Integer ); stdcall;
external DLLNAME2 name 'STNKAS';
procedure IK_SUBXY( var J: Integer; T, P: Single;
var X, Y, VL, VV, HL, HV, SL, SV: Single ); stdcall;
external DLLNAME1 name 'SUBXY';
procedure TF_SUBXY( var J: Integer; T, P: Single;
var X, Y, VL, VV, HL, HV, SL, SV: Single ); stdcall;
external DLLNAME2 name 'SUBXY';
function IK_TSPM( I: Integer; P: Single ): Single; stdcall;
external DLLNAME1 name 'TSPM';
function TF_TSPM( I: Integer; P: Single ): Single; stdcall;
external DLLNAME2 name 'TSPM';
以下はJNH3.DLL内のPSTを動的インポートした例である.
program test_delphi;
uses
Windows, SysUtils, Forms;
type
TPropathFunc = function ( Arg: Single ): Single; stdcall;
var
T, Ps: Single;
LibName, FuncName: String;
LibHandle: DWORD;
PSTFunc: TPropathFunc;
p: Pointer
begin
LibName := 'jnh3.dll';
FuncName := 'PST';
LibHandle := LoadLibrary( PChar( LibName ) );
if LibHandle <> 0 then
begin
p := GetProcAddress( LibHandle, PChar( FuncName ) );
if p <> nil then
begin
PSTFunc := p;
T := 1e2;
Ps := PSTFunc( T );
Writeln( 'Ps = ', Ps );
FreeLibrary( LibHandle );
Readln;
end;
end;
end.
type
TPropathFunc = function ( Arg: Single ): Single; stdcall;
SEO | [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送 | ||