テクニカル ノート 43: RFX ルーチン
注意
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。 結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。 最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。
ここでは、レコード フィールド エクスチェンジ (RFX: Record Field Exchange) アーキテクチャについて説明します。 また、RFX_ プロシージャを作成する方法についても説明します。
RFX の概要
レコードセット フィールド関数はすべて C++ コードで記述されています。 特殊なマクロやリソースは使用されていません。 RFX 機構の中核は仮想関数であるため、派生したすべてのレコードセット クラスでオーバーライドします。 これらの関数は次の形式で記述します。
void CMySet::DoFieldExchange(CFieldExchange* pFX)
{
//{{AFX_FIELD_MAP(CMySet)
<recordset exchange field type call>
<recordset exchange function call>
//}}AFX_FIELD_MAP
}
上のコードで使われている AFX 形式のコメントは、ClassWizard が編集するコード部分を囲んでいます。 ClassWizard と互換性のないコードは、この形式のコメントの外側に記述する必要があります。
上の例の <recordset_exchange_field_type_call> は、次のような形式を持ちます。
pFX->SetFieldType(CFieldExchange::outputColumn);
<recordset_exchange_function_call> は、次のような形式を持ちます。
RFX_Custom(pFX, "Col2", m_Col2);
RFX_ 関数の大部分には上のように 3 個の引数がありますが、 一部の関数 (RFX_Text や RFX_Binary など) には、さらにオプションの引数があります。
1 つの DoDataExchange 関数に複数の RFX_ を記述できます。
MFC (Microsoft Foundation Class) で提供されるすべての RFX ルーチンについては、afxdb.h を参照してください。
RFX 関数では、CMySet クラスのフィールド データを保存するためのメモリ領域 (通常はデータ メンバー) を登録できます。
注意
レコードセット フィールド関数は、CRecordset クラスだけで使用することを前提にデザインされています。 通常は、他の MFC クラスでは使用できません。
データの初期値は C++ 標準コンストラクターによって (通常はコメント ブロック //{{AFX_FIELD_INIT(CMylSet)、//}}AFX_FIELD_INIT の中で) 設定されます。
それぞれの RFX_ 関数は、フィールドが変更されていることを示す状態値を返す操作や、フィールドの編集を行う準備としてフィールド値をアーカイブする操作など、さまざまな操作をサポートする必要があります。
DoFieldExchange を呼び出す各関数 (SetFieldNull、IsFieldDirty など) では、DoFieldExchange 呼び出しに関する初期化を独自に行います。
動作原理
レコード フィールド エクスチェンジを使用するだけの場合は、この後の内容を理解する必要はありません。 しかし、処理の背後にある動作原理を理解することによって、独自のエクスチェンジ処理を作成できるようになります。
DoFieldExchange メンバー関数は Serialize メンバー関数と同じように、外部フォームのデータ (ODBC クエリで取得される列データ) とクラスのメンバーとの間でデータを取得したり設定したりします。 パラメーター pFX はデータ交換のコンテキストを指定します。これは CObject::Serialize の CArchive パラメーターに相当します。 pFX (CFieldExchange オブジェクト) には操作インジケーターがあります。このインジケーターは CArchive の方向フラグを一般化したものです。 RFX 関数では、次の操作を実行できます。
BindParam : ODBC に渡すパラメーター データの場所を示します。
BindFieldToColumn : ODBC が outputColumn データの検索や書き込みを行う場所を示します。
Fixup : CString/CByteArray の長さを指定し、NULL ステータス ビットを設定します。
MarkForAddNew : AddNew を呼び出した後で値が変更されていれば、変更ありとマークされます。
MarkForUpdate : Edit を呼び出した後で値が変更されていれば、変更ありとマークされます。
Name : 変更ありとマークされているフィールドに名前を追加します。
名前と値— 追加「< 列名 > =?」フィールド ダーティとしてマーク
値— 追加「?」で区切り記号に続く、のような '、' または ' '
SetFieldDirty : フィールドが変更されていることを表す (DIRTY) ステータス ビットをオンにします。
SetFieldNull : フィールドが NULL 値であることを表すステータス ビットをオンにします。
IsFieldDirty : DIRTY ステータス ビットの値を返します。
IsFieldNull : NULL ステータス ビットの値を返します。
IsFieldNullable : フィールドが NULL 値を保持できるときは TRUE を返します。
StoreField : フィールド値をアーカイブに保存します。
LoadField : アーカイブからフィールド値を読み出します。
GetFieldInfoValue : フィールドに関する情報を返します。
GetFieldInfoOrdinal : フィールドに関する情報を返します。
ユーザー拡張機能
既定の RFX 機構はいくつかの方法で拡張できます。 次のような拡張方法があります。
新しいデータ型を追加する。 次に例を示します
CBookmark
新しいデータ交換プロシージャ (RFX_???) を追加する方法。
void AFXAPI RFX_Bigint(CFieldExchange* pFX, const char *szName, BIGINT& value);
メンバー関数 DoFieldExchange を使用して、状況に応じて RFX 呼び出しやその他の C++ ステートメントを追加する方法。
while (posExtraFields != NULL) { RFX_Text(pFX, m_listName.GetNext(posExtraFields), m_listValue.GetNext(posExtraValues)); }
注意
ClassWizard は、上の例のようなコードを扱えないので、このようなコードは特殊コメント ブロックの外側に記述します。
カスタム RFX の作成
新しい RFX 関数を独自に記述するときは、既存の RFX 関数をコピーし、コピーした関数に必要な変更を加えると確実です。 適切な RFX 関数を選択してコピーすると、簡単に新しい関数を作成できます。 特殊な機能を備えた RFX 関数もあるため、コピーする関数は慎重に選択してください。
RFX_Long および RFX_Int:
最も簡単な RFX 関数です。 データ値はそのまま利用されます。また、データ サイズは固定です。RFX_Single および RFX_Double:
RFX_Long、RFX_Int と同様に簡単な関数で、既定の動作を幅広く利用できます。 ただし、これらの関数は、明示的に参照されているときにだけ浮動小数点ランタイム ライブラリが読み込まれるように、dbrfx.cpp ではなく dbflt.cpp に記述されています。RFX_Text および RFX_Binary:
文字列とバイナリのデータを保存する静的バッファーを事前に確保します。これらのバッファーは &value ではなく ODBC SQLBindCol を使用して登録する必要があります。 このため、これらの関数には特殊な処理を行うコードが多く含まれます。RFX_Date:
ODBC は日付と時刻を独自の形式のデータ構造 TIMESTAMP_STRUCT で返します。 この関数は、日時データの受け渡し用に TIMESTAMP_STRUCT を "プロキシ" として動的に割り当てます。 C++ CTime オブジェクトと TIMESTAMP_STRUCT プロキシとの間で行う日時データの交換は、さまざまな処理において必要です。 この関数の中身はやや複雑ですが、プロキシを使用したデータ交換例として参考になるはずです。RFX_LongBinary:
この関数は、列を連結せずにデータの受け渡しを行う唯一のクラス ライブラリ RFX 関数です。 この関数は BindFieldToColumn 操作を無視します。その代わり、Fixup 操作中に、取得される SQL_LONGVARCHAR データや SQL_LONGVARBINARY データに対してメモリを割り当てます。次に、SQLGetData を呼び出して、割り当てたメモリにデータを取得します。 データ ソースにデータ値を戻す (NameValue 操作と Value 操作など) ときは、その準備として ODBC の DATA_AT_EXEC 機能を利用します。 SQL_LONGVARBINARY や SQL_LONGVARCHAR の扱い方については、「テクニカル ノート 45: MFC/データベースの Long Varchar/Varbinary 型のサポート」を参照してください。
独自の RFX_ 関数を作成するとき、多くの場合、CFieldExchange::Default を使用して特定の操作を実装できます。 必要な操作を実現できるかどうか、Default の実装を調べてみてください。 RFX_ 関数に記述しようとしている操作を CFieldExchange::Default で実行できる場合は、CFieldExchange::Default を呼び出して、その操作を任せることができます。CFieldExchange::Default を呼び出す例については、dbrfx.cpp を参照してください。
RFX 関数では、まず IsFieldType を呼び出すことが重要です。IsFieldType が FALSE を返した場合は、すぐに RFX 関数を終了します。 このようにすると、パラメーター操作を outputColumns に対して適用してしまうなどの危険 (outputColumn に対して BindParam を呼び出すなど) を回避できます。 さらに、IsFieldType は、outputColumns (m_nFields) とパラメーター (m_nParams) の数を自動的に管理します。