Windows は,構造化例外処理 (SEH) と呼ばれる強固な例外および終了処理機構を提供しています。構造化例外処理は,オペレーティング・システムとコンパイラの両方のサポートが要求されます。Visual Fortran は SEH をサポートするための拡張を含んでいません。しかし,この強力なツールの利点を得ることができます。アプリケーションに少しの C コードを導入することで,自分の例外処理要求を満たすために SEH を使用することができます。
これを行うには,C コンパイラが必要になります。個々では,Microsoft Visual C++ がサポートする形式を使用します。他のコンパイラは SEH をサポートしていても,異なった形式を使用しています。
構造化例外処理は,例外および終了処理の両方を含んでいます。両方の機構を例を示しながら説明します。
以下のトピックについて説明します。
Fortran Console,Fortran QuickWin,および Fortran Standard Graphics アプリケーション用カスタム・ハンドラ
Fortran Console および Fortran QuickWin (および Fortran Standard Graphics) アプリケーションは,Fortran 基本例外およびエラー処理機能の十分な利点を持っています。しかしながら,基本機能を補間したり置き換えたいかもしれません。これを実現する方法を示す例が Visual Fortran サンプルに 2 つ提供されています。
サンプル・プログラム CSLEXCP2
サンプル・プログラム CSLEXCP4
サンプル・プログラム CSLEXCP2
以下の説明では,...\SAMPLES\EXCEPTIONHANDLING フォルダに提供されているサンプル・プログラム CSLEXCP2 を使用しています。
アプリケーションの実行中に何らかの予期しないイベントが発生するアプリケーションのファイルのオープンで何をすべきかを決定する前述の問題があると仮定します。サンプル・アプリケーション CSLEXCP2 は,問題の解決のための 1 つの方法を示しています。
CSLEXCP2.F90 を見ると,主プログラム CSLEXCP2 と 3 つのルーチン,MY_CODE,MY_EXCEPTION_FILTER_CODE,および MY_TERMINATION_HANDLER_CODE があります。ルーチン MY_CODE は,アプリケーションの Fortran コードの主部分です。それは,(OPEN 文で) データ・ファイルを開き,2 つの整数値の入力をユーザーに求め,第 1 整数値を第 2 整数値で割った結果を (WRITE 文で) データ・ファイルに書き込んでいます。
ユーザーが,変数 I に値 99 (そして J に 0 以外) を 入力すると, EXIT サブルーチンの呼び出しの結果として Fortran 実行時システムによって,アプリケーションは正常に終了し,データ・ファイルが (CLOSE 文で) 閉じられますが,削除されません。もし,ユーザーが変数 J に 0 を入力すると,整数のゼロによる除算の例外が発生します。これをカスタム例外ハンドラで保護します。
CSLEXCP2 の主プログラムは,以下のようになっています。
PROGRAM CSLEXCP2 INTERFACE SUBROUTINE MY_CODE_WRAPPER() !DEC$ IF DEFINED(_M_X86_) !DEC$ATTRIBUTES C, ALIAS:'_MY_CODE_WRAPPER' :: MY_CODE_WRAPPER !DEC$ ELSE !DEC$ATTRIBUTES C, ALIAS:'MY_CODE_WRAPPER' :: MY_CODE_WRAPPER !DEC$ ENDIF END SUBROUTINE END INTERFACE CALL MY_CODE_WRAPPER() END
CSLEXCP2 が行うすべては,MY_CODE_WRAPPER という名前のルーチンを呼び出すことです。MY_CODE_WRAPPER がカスタム・ハンドラを実装する C ルーチンです。これが行う内容を見るには,ファイル wrapper.c を参照します。MY_CODE_WRAPPER は,Fortran アプリケーションの主部分 MY_CODE の呼び出しに Try/Except コンストラクトをラップします。それはまた,Try/Except コンストラクトに Try/Finally コンストラクトをラップします。MY_CODE の実行中に例外が発生すると,このハンドラがイベントを処理したいのかどうかを見るために,オペレーティング・システムは例外フィルタ式を評価します。例外フィルタ式は,Fortran ルーチン MY_EXCEPTION_FILTER_CODE を呼び出します。
MY_EXCEPTION_FILTER_CODE が EXCEPTION_EXECUTE_HANDLER を返すと,オペレーティング・システムは例外ハンドラ・ブロック (この例では空) の実行を継続します。アプリケーションに対する汎用終了ハンドラ MY_TERMINATION_HANDLER_CODE を呼び出す __finally ブロックの実行を継続します。ルーチン MY_EXCEPTION_FILTER_CODE は,データ・ファイルを削除するかどうかをユーザーに問い合わせます。これが,例外状況を処理するために必要な基本コードのすべてです。ルーチン MY_TERMINATION_HANDLER_CODE は,メッセージを出力し,アプリケーションを終了するために EXIT サブルーチンを呼び出します。
MY_EXCEPTION_FILTER_CODE が EXCEPTION_CONTINUE_SEARCH を返すと,オペレーティング・システムが例外ハンドラの一覧の検索を継続し,基本 Fortran 実行時例外フィルタを見つけ出すことに注意してください。__finally ブロックのコードは,決して実行されません。これは,基本実行時ハンドラが例外の基本処理の一部としてプロセスを終了するためです。以下のハンドラの問い合わせに応答すると,これを経験することができます。
Enter A to exercise the termination handler code path, ...or B to pass the exception to the default handler.
この例で注意すべき点が 2,3 あります。 第 1 に,これは Fortran Console アプリケーションで,インフラが C プログラムに基本的に似ていることを思い出してください。アプリケーションのエントリ・ポイントは,C 実行時システムの mainCRTStartup です。ルーチン mainCRTStartup は,C 実行時システムを初期化し,Fortran 実行時システム main() ルーチンを呼び出します。Fortran 実行時システム main() ルーチンは,Fortran 実行時システムを初期化し,Fortran コードのエントリ・ポイント _MAIN__ を呼び出します。
Fortran コンパイラに機械語コードのリストを要求して,サンプルをビルドすると,Fortran コードを実際に実行する前に初期化を提供する Fortran コンパイラが生成するプロローグ・コードを見ることができます。そのリストの抜粋を示します。
.CODE PUBLIC _MAIN__ 0000 _MAIN__ PROC 55 0000 push ebp EC8B 0001 mov ebp, esp 24EC83 0003 sub esp, 36 53 0006 push ebx 000000E8 0007 call _for_check_flawed_pentium@0 01 04EC83 000C sub esp, 4 0004058D 000F lea eax, dword ptr .literal$+4 0002 50 0015 push eax 000000E8 0016 call _for_set_fpe_@4 01 04C483 001B add esp, 4 04EC83 001E sub esp, 4 00001D8D 0021 lea ebx, dword ptr .literal$ 0002 53 0027 push ebx 000000E8 0028 call _for_set_reentrancy@4 01 04C483 002D add esp, 4 000000E8 0030 call _MY_CODE_WRAPPER
Fortran 実行時ルーチン _for_check_flawed_pentium@0,_for_set_fpe_@4,および _for_set_reentrancy@4 の呼び出しが,_MY_CODE_WRAPPER の呼び出しよりも前で行われていることに注意してください。このコードは,/fpe:0 コンパイル・オプションでコンパイルされているので,ia32 システムでの非基本浮動小数点動作を設定するための _for_set_fpe_@4 を見ることができます。基本 /fpe:3 コンパイラ・オプションでビルドされた時には,この呼び出しはありません。サンプルは,浮動小数点計算を行っていません (/fpe:0 オプションはこの点を示すために選択したに過ぎません)。同様に,Pentium の浮動小数点欠陥を検証する実行時オプションを選択しなければ,_for_check_flawed_pentium@0 の呼び出しも生成されません。
このプロローグ・コードは,Fortran Console,Fortran QuickWin,および Fortran Standard Graphics アプリケーションの基本構造の結果として実行されます。Fortran Windows または Fortran DLL アプリケーションでは実行されません。これは,MAIN__ の呼び出しを行う Fortran 実行時システム main() がないためです。
例外処理の大部分の作業は,フィルタ式の実行過程で行われます。Fortran 基本ハンドラでも同じです。例外を基本実行時システム・ハンドラに渡すようにした場合,制御を再び得ることはできません。これは,基本ハンドラが多くの場合,例外フィルタ内からプロセスを終了するためです。例外を巻き戻すことができないため,自分の終了ハンドラ (__finally コンストラクト) は決して実行されません。
基本実行時例外ハンドラは,デバッガーで実行されている時に例外が発生した場合にアプリケーションが取る役割を演じます。通常,デバッグ中の例外の発生は,プロセスを終了させないことを除いて,通常の動作を基本ハンドラに行わせます。その代わり,EXCEPTION_CONTINUE_SEARCH を返すので,オペレーティング・システムはデバッガーに例外を処理する第 2 の機会を与え,デバッガーは処理できない例外が発生した場所を指すカーソル位置でプログラムを停止します。終了ハンドラの実行を見るよりも例外が発生した場所を見ることに興味がある場合,例外フィルタから EXCEPTION_CONTINUE_SEARCH を返すことができ,基本実行時システム・ハンドラが通常のようにデバッガーでアプリケーションを停止するようにさせることができます。
この (CSLEXCP2) 例では,整数のゼロにより除算が発生した場所を示すためのトレースバック出力を生成しません。これは,基本ハンドラでは通常行われますが,そのハンドラを上書きしています。次の例は,TRACEBACKQQ ルーチンを使って,カスタム・ハンドラでトレースバック出力が生成できることを示しています。
サンプル・プログラム CSLEXCP4
以下の説明では,...\SAMPLES\EXCEPTIONHANDLING フォルダに提供されているサンプル・プログラム CSLEXCP4 を使用しています。
幾つかの例外を基本ハンドラが処理できるようにしたいと仮定します。たとえば,/fpe:0 での浮動小数点アンダーフローです。この場合,ゼロに修正した結果を提供する基本ハンドラを持つことになります。簡単なプログラム状態コードに例外を変更したいと仮定します。サンプル CSLEXCP4 は,これを実行する 2,3 の技術を示しています。
サンプル CSLEXCP4 の前提は,浮動小数点例外を発生する Fortran サブルーチンを呼び出しているということです。浮動小数点のゼロによる除算の例外を検知し,アプリケーションを終了するための有用な動作を取ります。アプリケーションを /fpe:0 でコンパイルし,浮動小数点アンダーフロー例外の処理を基本 Fortran 実行時システム・ハンドラに継続させます。
CSLEXCP4.F90 を見ると,保護するサブルーチンは DIVIDE と呼ばれています。実際のアプリケーションでは,保護されたサブルーチンはより複雑なコーディングにすることができますが,この例では,サブルーチンは単純に割り算を行う 2 つの数字を入力し,結果を印刷します。主 Fortran プログラムは,直接には DIVIDE を呼びません。これは,DIVIDE_WRAPPER と呼ばれる C ラッパ関数から呼び出されます。
ファイル WRAPPER.C 中の DIVIDE_WRAPPER 関数は,Try/Except コンストラクトに DIVIDE への呼び出しをラップします。このサンプルでは,DIVIDE が例外なく戻った場合,すべてが正しく行われ,DIVIDE_WRAPPER が rtn_sts 変数を適宜設定すると仮定しています。例外が発生しない場合,例外フィルタ式は決して評価されることなく,例外ブロックは決して実行されません。実行は,rtn_sts を返す例外ハンドラ・ブロックの直後にある文で継続されます。
DIVIDE の実行中に例外が発生した場合,例外フィルタ式はオペレーティング・システムで評価されます。フィルタ式は次の 2 つの動作を行います。
フィルタ式は,例外ハンドラ・コード・ブロックで使用するために,ローカル変数 ecode でイベントに関連した例外コードを捕らえます。例外ハンドラ・コード・ブロックが実行されると,それは rtn_sts に返すための状態値を決定するために例外コードを検証します。
フィルタ式は,Win32 ルーチン GetExceptionInformation から返される値をそれに渡す CHECK_EXCEPTION_INFO と呼ばれる Fortran 関数を呼び出します。
関数 CHECK_EXCEPTION_INFO が EXCEPTION_EXECUTE_HANDLER を返すと,オペレーティング・システムは例外ハンドラ・コード・ブロックを実行します。ハンドラ・ブロックは,例外コードを検証し,ゼロによる除算が発生したことを示す値または他の例外が発生したことを示す値を rtn_sts に設定します。そして,rtn_sts の値を返す戻り文で継続が実行されます。
関数 CHECK_EXCEPTION_INFO が EXCEPTION_CONTINUE_SEARCH を返すと,オペレーティング・システムは例外を処理したい例外フィルタの 1 つを探すために例外フィルタの一覧を検索します。この例では,次の例外フィルタが基本 Fortran 実行時システム例外フィルタです。
CHECK_EXCEPTION_INFO に渡される値は,オペレーティング・システムが提供する例外記録とコンテキスト記録へのポインタを含むデータ構造のアドレスです。CHECK_EXCEPTION_INFO は,何が発生し,どのように処理するかを決定するためにこの情報を使用します。これが浮動小数点のゼロによる除算であった場合,CHECK_EXCEPTION_INFO はアプリケーション固有のメッセージに従ってスタック・トレースを生成するために TRACEBACKQQ を呼び出します。それは,EXCEPTION_EXECUTE_HANDLER への関数の返し値を設定し,呼び出し側に戻ります。
これが浮動小数点のアンダーフロー例外であった場合,CHECK_EXCEPTION_INFO は TRACEBACKQQ を呼び出します。これにより,アプリケーション出力で何が起こったのかのみを知ることができます。そして,それは EXCEPTION_CONTINUE_SEARCH への関数の返し値を設定します。オペレーティング・システムがこの返し値を見たとき,それは浮動小数点アンダーフローの結果をゼロで修正し,例外の発生点で主プログラムを実行するように,Fortran 実行時基本例外フィルタ式を評価します。
主プログラムは,実行の各パスを例示する 3 つの値の組を持つ 2 つの配列を初期化します。DO ループは,3 つの繰り返しを実行します。
第 1 ループは,例外が発生しない時に何が起こるかを示しています。
第 2 ループは,基本ハンドラでゼロに設定される浮動小数点アンダーフローを示しています。
第 3 ループは,処理される浮動小数点のゼロによる除算を示しています。プログラムは,簡単な STOP 文とメッセージでアプリケーションをシャットダウンする動作を取ります。
Fortran DLL アプリケーション用カスタム・ハンドラ
Fortran DLL アプリケーション用カスタム・ハンドラを作成するには 2 つの方法があります。
Fortran DLL にエラーと例外を含ませる方法
Fortran DLL で浮動小数点トラップを可能にする方法
Fortran DLL にエラーと例外を含ませる方法
Fortran DLL を作成し,それを Visual Basic GUI や他の言語で書かれた主プログラムから呼び出して使用したい場合,DLL でのエラーや例外が主アプリケーションをクラッシュさせないように注意しなければなりません。Fortran DLL を作成する際に注意しなければならない 2, 3 の基本原則を示します。
呼び出し側に状態を返し,呼び出し側にすべきことを決定させるようにライブラリー・ルーチンを作成します。
呼び出し側に期待する状態を返すためには,ライブラリー・コードで防衛的になる必要があります。そのため,これらの他の原則を考慮します。
意味のある場所で,ライブラリー・コードが呼び出し側から渡される入力引数を検証するようにします。つまり,ライブラリー・ルーチンがそれらで作業するために,それらが正しいものであることを確認します。たとえば,ルーチンが数値アルゴリズムを実装し,正しく動作でき,十分に定義された反応を行える正しい入力領域を持っていると仮定します。アルゴリズムを実行する前に入力引数を検証することができ,(予期しない浮動小数点例外のような) 他の結果を発生する予期しない反応を避けることができます。例外 IEEE 数字を検知するために ISNAN および FP_CLASS のような Fortran 組込手続を使用します。DLL コードは,問題を示し,呼び出し側が適切な動作が取れる (アプリケーションをシャットダウンする,別の入力を試みる,など) ように,呼び出し側に状態を返す必要があります。
ライブラリー・コードでは,I/O ルーチンの呼び出し,および動的メモリーの割り当て/割り当て解除での成功または失敗を常に検証するようにし,Fortran では,I/O 文は要求した I/O が成功したかどうかを決定するために使用できる ERR,END,EOR,および IOSTAT 引数を持っています。動的メモリーのための ALLOCATE および DEALLOCATE 文は,動的メモリーの割り当て/割り当て解除の状態を得て,プログラムの終了を回避することができる省略可能な STAT 指定子を持っています。
エラーが発生した時の動作を指定しない場合,Fortran 実行時システムは選択肢を持っていませんが,処理できない重大 (Severe) エラーとしてエラーを処理し,プログラムを終了させます。OPEN 文でファイルが見つからないというエラーを得るための IOSTAT と ERR の使用例については,「IOSTAT 指定子と Fortran 終了コードの使用」を参照してください。同様のことを自分のコードで行うことができますが,Visual Basic や他の Fortran 以外の主プログラムに状態を単に戻し,それに何をすべきかを決定させるようにします。
予期しないプログラム例外が発生しないように自分の DLL を書くようにしますが,予期しない例外が発生した場合に予期しない例外を処理する方法を考案して置くようにします。例外処理の効果的な別の方法は,例外が発生した時に制御を得るために Win32 構造化例外処理を使用することです。すべての DLL ルーチン呼び出しを C Try/Except コンストラクタでラップし,どのように応答するかを決定する自分が定義したルーチンを呼び出す except() フィルタ式を持つようにします。
Fortran DLL で浮動小数点トラップを可能にする方法
DLL で発生する浮動小数点トラップ条件をどのように処理するかを考える前に,それらのトラップが発生するようにそれらのトラップをアンマスクする問題を考慮しなければなりません。/fpe:3 でコンパイルし,例外を検証するために浮動小数点ステイタス・ワードをポーリングする場合,トラップのアンマスクの問題を考える必要はありません。その場合,トラップをアンマスクしません。
しかしながら,/fpe:0 でコンパイルし,浮動小数点例外をトラップする場合,浮動小数点コントロール・ワードのトラップをアンマスクする作業が必要になります。これは,多くの他の言語が基本設定としてトラップをマスクするためです。
Fortran Console または Fortran QuickWin (または Standard Graphics) アプリケーションが自動的にトラップをアンマスクすることを思い出してください。これは,Fortran 実行時システムが主プログラムに提供し,実際のアプリケーション・コードが実行される前にプロローグ・コードを実行する MAIN__ を呼び出すためです。他の言語が呼び出す Fortran DLL ではこれをもっていません。異なった言語は,異なった初期化環境を確立します。自分自身で希望する初期化環境を提供しなければなりません。以下と Fortran Windows アプリケーションの説明のサンプルは,これをどのように行い,いつ行うかのアイデアを与えます。
サンプル・プログラム VBVF1
以下の説明は,...\SAMPLES\EXCEPTIONHANDLING フォルダの Visual Fortran サンプル・プログラム VBVF1 を使用しています。VBVF1 は,構造化例外処理と Visual Basci GUI が呼び出す Fortran DLL で発生する例外とエラーから Visual Basic GUI を保護するためのFortran I/O エラー処理技術の組み込み方法を示しています。エラー処理技術は,ごく一般的なもので,他の状況でも同様に使用することができます。たとえば,Fortran DLL を呼び出す C 主プログラムもまたここで説明するアイデアを使用することができます。
サンプル・アプリケーション VBVF1 は,Visual Basic コードが取り扱うことができるユーザー定義状態条件に予期しないイベントをオンにすることで,Fortran DLL で発生する予期しないエラーや例外の効果を含むようにしています。
GUI は 6 つのボタンを持っており,各ボタンは Fortran DLL で特定のエラー条件を生成します。7 番目のボタンは,GUI で表示されるエラー状態をクリアします。GUI には 3 つのテキストボックスがあります。1 つのボックスは,最後に発生したエラーに関連したメッセージを表示します。他のボックスは最後のエラーのエラー番号を表示し,3 番目のボックスはエラーが発生した Visual Basic ソース・ファイルを表示します。
ボタンの 2 つ,「Test Integer Divide By Zero Response」と「Test Floating Point Overflow Response」は,Fortran DLL で発生したエラーを取り扱うための Visual Basic ハンドラの使用を示すためにコーディングされています。Visual Basic エラー処理はある程度はこれら 2 つの特殊な場合に対して動作しますが,より一般的な場合を取り扱うための有用な解ではありません。Visual Basic エラー処理に詳しくない場合,このサンプルは何をすべきかのアイデアを与えてくれる開始点です。よく詳しい説明は,Visual Basic のマニュアルを参照してください。
残りの 4 つのボタンの各々は,Fortran DLL のルーチン Fortran_Test を呼び出しています。ソース・ファイル gen_errs.f90 を参照してください。ルーチン Fortran_Test は,選択したエラーを生成します。しかしながら,Visual Basic コードは直接には Fortran_Test を呼び出しません。Visual Basic の Fortran_Test の呼び出しすべては,ラッパ・ルーチン Fortran_Test_Wrapper を通して行われます。Fortran_Test_Wrapper (ソース・ファイル wrappers.c) は,DLL への呼び出しで C Try/Except コンストラクトをラップし,DLL で予期せずに引き起こされる例外とエラーを含むための制御点として動作します。Fortran_Test_Wrapper は,常にルーチンを呼び出す Visual Basic にユーザー定義のアプリケーション固有の状態を返します。そして,Visual Basic コードは,ユーザーに適切なメッセージを表示するために状態値に対して反応します。
この機構の実装は,以下の 2 つに依存します。
Fortran I/O 文の I/O 状態の要求と検証を常に行うプログラミング
ラッパ・ルーチンでの Try/Except コンストラクトの使用
ラッパ・ルーチンの __try ブロックでは,呼び出しが Fortran_Test に対して行われます。これは,Fortran 定義状態が返される場合を除きます。I/O エラーを生成するテスト・ケースに対して,Fortran_Test のコードは I/O 状態を捕え,それを Fortran_Test_Wrapper に返します。Fortran_Test_Wrapper は,Fortran 定義状態をユーザー定義状態コードに変換するためにテーブルを検索する他の DLL ルーチン TRANSLATE_TO_MY_APP_CODES を呼び出します。実行は,__except に続く返し文で継続されます。Visual Basic コードは,ユーザー定義状態値を受け取り,適当な表示を行います。
Fortran_Test の実行中に例外が発生すると,制御はオペレーティング・システムに移ります。オペレーティング・システムは,例外ハンドラの一覧を検証し,Fortran_Test_Wrapper に関連する例外フィルタを検索します。それは,例外をここで処理するかどうかを判断するために __except 式を評価します。__except 式は,例外についての情報を提供するためにそれをオペレーティング・システムに渡す他の DLL ルーチン CHECK_EXCEPTION_INFO を呼び出します。CHECK_EXCEPTION_INFO は,例外条件が何かを決定し,イベントに固有の適切なメッセージでトレースバックを生成し,EXCEPTION_EXECUTE_HANDLER を返します。
EXCEPTION_EXECUTE_HANDLER を戻すことは,オペレーティング・システムに Fortran_Test_Wrapper の __except コンストラクトに関連するハンドラ・ブロックで実行を再開することを伝えます。ハンドラ・ブロックのコードは,TRANSLATE_TO_MY_APP_CODES を呼び出します。これは,再びテーブルを参照しますが,Windows 定義状態コードをユーザー定義状態コードに変換するために第 2 テーブルを参照します。実行は,__except ブロックに続く返し文で継続されます。Visual Basic は,ユーザー定義状態値を受け取り,適当な表示を行います。
このサンプルでは,Fortran コードは /fpe:0 でコンパイルされ,内部的に浮動小数点例外を生成します。それは,これらの例外によるトラップを期待しています。基本設定では,Visual Basic がすべての浮動小数点とラップをマスクするので,アプリケーションはトラップをアンマスクするための動作を取らなければなりません。このサンプルでは,2 つのルーチン Fortran_FP_Trap_Enable と Fortran_FP_Trap_Disable が DLL に提供されています。これらは,Visual Basic コードから呼び出されます。手続き cmdFltOvf_Click (ソース・ファイル frmVBVF1.frm) では,例外を生成する DLL ルーチン Fortran_FltOvf を呼び出す前に,トラップをアンマスクするための DLL ルーチン Fortran_FP_Trap_Enable を呼び出しています。
Fortran_FP_Trap_Enabl は,浮動小数点コントロール・ワードでトラップをアンマスクするために GETCONTROLFPQQ と SETCONTROLFPQQ を使用しています。ラベル My_VB_FltOvf_Handler の付いたこのルーチンのための Visual Basic ハンドラ・コードでは,DLL ルーチン Fortran_FP_Trap_Disable は Visual Basic 基本例外設定に戻すために呼び出されています。浮動小数点とラップをアンマスクまたはマスクするタイミングと方法は,アプリケーションのデザインの問題です。サンプルは,ユーザーが使用することができる 1 つの方法を例示しているに過ぎません。
Fortran Windows アプリケーション用カスタム・ハンドラ
Fortran Windows アプリケーションは,Fortran 基本例外処理機能をフックしません。Fortran Windows アプリケーションは,完全にカスタマイズされた領域と考えることができ,Fortran 実行時システムは処理の外に留まろうとするので,自分のコードで自分の思うとおりのことができます。
以下の説明では,...\SAMPLES\EXCEPTIONHANDLING フォルダのサンプル・プログラム WINEXCP1 を参照しています。WINEXCP1 は,Fortran メッセージ処理ルーチンを保護するために,構造化例外処理と標準 I/O エラー処理技術を組み込む方法を示すために GENERIC サンプルを拡張したものです。
このアプリケーションをビルドし実行すると,主ウィンドウが表示され,「Run Error Simulator」というラベルのついたメニューが表示されます。「Run Error Simulator」メニューで,6 つの異なったエラーを生成するテスト・ケースを選ぶことができます。テスト・ケースの 1 つを選択すると,ソース・ファイル generic.f90 の関数 MainWndProc で解釈される固有のメッセージが送られます。各メッセージに対して,MainWndProc は,選択した条件を生成するためにメッセージ処理ルーチン ERROR_SIMULATOR を効果的に呼び出します。MainWndProc は,直接 ERROR_SIMULATOR を呼び出すわけではありません。代わりに,これはラッパ・ルーチン ERROR_SIMULATOR_WRAPPER (ソース・ファイル wrapper.c) を呼び出します。このルーチンは,ERROR_SIMULATOR への呼び出しで Try/Except コンストラクトをラップします。MainWndProc は,ERROR_SIMULATOR_WRAPPER から返される状態値を受け取ることを期待していることも注意してください。ERROR_SIMULATOR_WRAPPER がゼロを返すと,すべてがうまく動作します。非ゼロの状態が返されると,MainWndProc は「Error Simulator」がエラーを持つことを示すメッセージボックスを表示します。
注意すべき重要な点は,「Error Simulator」のエラーが含まれ,何らかの予期しないエラーが発生した時にプログラムを中止するのではなく継続することができる状態コードに変わることです。この動作を行うには,コードは以下の 2 つのことを行わなければなりません。
「Error Simulator」は,常に Fortran I/O 文の完了状態を問い合わせ,検証しなければなりません。I/O 状態が FOR$IOS_SUCCESS ではない場合,「Error Simulator」は適切な動作を行い,非ゼロ状態を MainWndProc に返します。
例外を捕え,処理できなければなりません。ERROR_SIMULATOR_WRAPPER の __except コンストラクトは,これが起こることを許可しています。ERROR_SIMULATOR で例外が発生すると,オペレーティング・システムは,例外をどのように処理するかを調べるために __except フィルタ式を評価します。フィルタ式は,generic1.f90 の CHECK_EXCEPTION_INFO を呼び出しています。例外情報を提供するオペレーティング・システムへのポインタは,CHECK_EXCEPTION_INFO に渡されます。
CHECK_EXCEPTION_INFO が EXCEPTION_EXECUTE_HANDLER を返すと,__except ハンドラ・ブロックのコードが実行されます。ハンドラ・ブロックは,返し状態を非ゼロ値に設定します。実行は,ハンドラ・ブロックに続く返し文で継続され,MainWndProc に戻ります。CHECK_EXCEPTION_INFO は,エラー処理動作が状況に対して適切であれば何でも受け付けます。このサンプル・プログラムでは,浮動小数点例外に対して浮動小数点例外状態をクリアし,すべてのケースでは,発生した例外に固有のメッセージを持つトレースバック出力を生成します。
CHECK_EXCEPTION_INFO が EXCEPTION_CONTINUE_SEARCH を返すと,オペレーティング・システムはハンドラいチンの他のハンドラを検索します。この場合,C 実行時ルーチン _XcptFilter を呼び出す C 実行時システム基本 __except フィルタを見つけます。_XcptFilter は,プロセスを終了させるために C 実行時 _exit ルーチンを呼び出す EXCEPTION_EXECUTE_HANDLER を返します。
最後に,Fortran Console や Fortran QuickWin アプリケーションのように Fortran 実行時システムが Fortran Windows アプリケーションに対して C 主ルーチンを提供しないことを思い出してください。これらの場合,main は,付加的な初期化作業を行うコンパイラが生成した 2, 3 の Fortran 実行時システム呼び出しを実行する MAIN__ を呼び出します。このアプリケーションは,/fpe:0 オプションでコンパイルされているので,浮動小数点トラップを例示することができます。アプリケーションはこれらのトラップをアンマスクしなければなりません。これは,Fortran 実行時システムが自動的にそれを行うために呼び出されないからです。このサンプルでは,トラップは GETCONTROLFPQQ と SETCONTROLFPQQ を使って ERROR_SIMULATOR の始めでアンマスク亜sれています。基本設定では,トラップはマスクされています。
ある状況では,アプリケーションの全実行時間中でトラップをアンマスクする初期化ルーチンを個別に行いたい場合があります。他の場合では,プログラムの実行中に選択した点でトラップのマスクまたはアンマスクを行いたい場合があります。選択的にトラップをマスクおよびアンマスクする場合,トラップをマスクしたとしても,浮動小数点例外ステイタス・ビットを自分のコードで設定しなければならないことに注意してください。浮動小数点ステイタス・ビットは,スティッキー (sticky) です。そのため,トラップを再度アンマスクする前に,浮動小数点ステイタス・ビットをクリアしなければなりません。これにより,自分のコードで間違ったトラップを生成することを防ぐことができます。