X作成講座 on FB[01] Glue ファイルの修正


 HyperXcmd Glue.INCL (以下 "Glueファイル" )はFBに付属しているファイルです。 HyperCard の持っているグルールーチン(コールバック)をFBから呼び出せるようにするためのインクルードファイルで、これがないと XCMD を作れません。


 最初 FutureBASIC II 日本語版に付いていた Glueファイルは作成日が95年11月24日のものでした。これはバグだらけでほとんど使いものにならないシロモノです。その後FBII が 2.3 にアップデートされた時に Glueファイルもアップデートされ、現在のFBII 日本語版には作成日96年8月7日のものが付属していると思います。(ファイル中に何カ所も '・・・ 7/8/96 ・・・ というコメントが入っていて、修正した跡が見られます)

 ところがこの新版の Glueファイルもバグだらけだったのです。オリジナルに手を加えるのはどうも気持ちが悪いのですが、このまま使うとものによっては爆弾の憂き目に会いますので、ここはひとつ思い切ってインクルードファイルの書き換えをしてしまいましょう。


             −・−・−


 今回変更を加えるのは日本語版FBII の中の "(FB) Examples" フォルダの、"Extending Appls" の中の "HyperCard" フォルダにある "XCMD Templates" フォルダです。ここにある "HyperXcmd Glue.INCL" ファイルの作成日が96年8月7日(月)であることを確認して下さい。もし作成日が違っていたら ModE のサイトから最新版を落として来て下さい。

 ファイルを確認したら、この "XCMD Templates" フォルダを丸ごとコピーします。名前は "XCMD テンプレート" とでも変えてオリジナルと区別できるようにしておいて下さい。以下の説明ではこの新しく作ったフォルダの中のファイルを書き換えます。

 同じ HyperCard フォルダに "XCMD projects" フォルダがありますが、この中のファイルは触らないで下さい。サンプル XCMD は古い Glueファイルを使ってコンパイルするように出来ているので、ここの Glueファイルを変更してしまうとサンプルがコンパイル出来なくなってしまいます。また同梱されている英語版のファイルもそのままにしておいて下さい。

 ここではグルールーチンを規定している HyperXcmd Glue.INCL を修正し、それに伴って変更が必要になる HyperXcmd.MAIN も一部修正します。


             −・−・−


 まず HyperXcmd Glue.INCL を修正しましょう。

 以下私の分かる範囲でチェックして、修正が必要な部分について変更法方を書いてあります。また各ルーチンの簡単な説明も添えてありますので、グルールーチンの使い方の参考にして下さい。変更した部分は全て実際にコンパイルして HyperCard 上でテストしてあります。


 凡例:
 ○ = 動作確認済み
 ● = ソース書き換えが必要
 − = 使用不可
 Θ = 未確認

 theStr$..... FB文字列。
 cStrPtr&.... C文字列ポインタ。 例:[cStrHandl&]
 cStrHandl&.. C文字列ハンドル。 例:[xCmdPtr& + _params + 0 * 4]


 注意1:ハンドルについて
  関数の返値としてハンドルを返すものがあります。
  このハンドルはHCに渡すか、使用後に破棄する必要があります。

 注意2:真偽値について
  関数の返値として真偽値を返すものがあります。
  偽は常に0ですが、真は「0以外の数字」と思っていた方がいいでしょう。
  従ってIF文では IF bool% <> _false THEN のように使うのが確実です。


             −・−・−


' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
' hypertalk utilities
' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

●cStrHandl& = FN EvalExpr (xCmdPtr&, theStr$)
 FB文字列をHCに「評価」させる。
 例えば "5 > 3" という文字列を与えると "true" という
 文字列のハンドルが返ってくる。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN EvalExpr (xCmdPtr&, @strPtr&)

●FN SendCardMessage (xCmdPtr&, theStr$)
 FB文字列をメッセージとしてカードに送る。
 例えば "go next" という文字列を渡すと次のカードに移動する。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN SendCardMessage (xCmdPtr&, @strPtr&)

●FN SendHCMessage (xCmdPtr&, theStr$)
 FB文字列をメッセージとしてハイパーカード本体に直接送る。
 カード等を経由しないぶん速い。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN SendHCMessage (xCmdPtr&, @strPtr&)


' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
' memory utilities
' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

●cStrHandl& = FN GetGlobal (xCmdPtr&, glblName$)
 HCのグローバル変数の名前を渡すと、その変数の値を文字列として収めた
 ハンドルが返ってくる。一般にスクリプト中で使われる it はローカル変数で
 あるので、使用中の it の値を得るには別の方法が必要。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN GetGlobal (xCmdPtr&, @glblNamePtr&)

●FN SetGlobal (xCmdPtr&, glblName$, glblHndl&)
 HCのグローバル変数を指定してそこに値(文字列)を書き込む。
 変数 it については GetGlobal と同じ注意が必要です。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN SetGlobal (xCmdPtr&, @glblNamePtr&, glblHndl&)
 :また、最後の行も以下のように書き換えて下さい。
 :DEF DISPOSEH(glbHndl&) → DEF DISPOSEH(glblHndl&)

○FN ZeroBytes (xCmdPtr&, dstPtr&, count&)
 与えたアドレスから count& バイトだけ0で埋める。


' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
' string utilities
' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

●FN ScanToReturn (xCmdPtr&, cStrPtr&)
 C文字列ポインタを渡すとリターンか最後までポインタを進める。
 関数実行後は cStrPtr& は0か13を指している。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN ScanToReturn (xCmdPtr&, @scanPtr&)

●FN ScanToZero (xCmdPtr&, cStrPtr&)
 C文字列ポインタを渡すと文字列の最後までポインタを進める。
 関数実行後は cStrPtr& は0を指している。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN ScanToReturn (xCmdPtr&, @scanPtr&)

●bool% = FN StringEqual (xCmdPtr&, theStr1$, theStr2$)
 二つのFB文字列を比較して真偽値を返す。
 大文字小文字は区別しません。日本語の文字列では正しく動作しません。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN StringEqual (xCmdPtr&, @str1Ptr&, @str2Ptr&)
 :また、最後の行も以下のように書き換えて下さい。
 :END FN = [xCmdPtr& + _outArgs]

○length& = FN StringLength (xCmdPtr&, cStrPtr&)
 C文字列ポインタを渡すとその文字長を返す。

●pos& = FN StringMatch (xCmdPtr&, theStr$, cStrPtr&)
 C文字列(第3パラメタ)の中にFB文字列(第2パラメタ)が含まれるかを
 調べる。含まれていればその文字列のポインタを、含まれていなければ0を返す。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN StringMatch (xCmdPtr&, @matchStrPtr&, strPtr&)


' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
' string conversions
' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

●FN BooltoStr (xCmdPtr&, bool%, theStr$)
 真偽値を文字列に変換する。
 bool% が0なら "false"、0以外なら "true" がFB文字列に入れられます。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN BoolToStr (xCmdPtr&, bool&, @strPtr&)

−FN ExtToStr (xCmdPtr&, extNum, @theStr$)
 浮動小数点を文字列に変換する。
 ...はずだけど、FBのXでは浮動小数点を扱えません。

○FN LongToStr (xCmdPtr&, longInt&, theStr$)
 符号なしの longInt(倍精度整数)をFB文字列に変換。
 扱う範囲は 0 から 4,294,967,298 まで。

○FN NumToHex (xCmdPtr&, longInt&, numDig%, theStr$)
 符号なしの longInt(倍精度整数)を16進文字列に変換。
 第2パラメータは元になる数。第3パラメータは16進文字列の桁数を指定。

○FN NumToStr (xCmdPtr&, longInt&, theStr$)
 符号付きの longInt(倍精度整数)を文字列に変換。
 扱う範囲は -2,147,483,647 から 2,147,483,647 まで。
 マイナスの返ってくる可能性のある関数を呼んだ時は、LongToStr ではなく
 NumToStr を使うようにしましょう。

○num& = FN StrToNum (xCmdPtr&, theStr$)
 文字列を符号付きの longInt(倍精度整数)に変換して返す。

○FN ReturnToPas (xCmdPtr&, cStrPtr&, theStr$)
 C文字列の先頭からリターン(もしくは文字列の終わり)までを
 FB文字列に入れて返す。

○bool% = FN StrToBool (xCmdPtr&, theStr$)
 真偽の文字列("true" または "false")を真偽値に変換。
 "true"(大文字小文字は問わない)以外の文字列は全て偽(0)が返ってきます。

−Var# = FN StrToExt (xCmdPtr&, theStr$, extNum)
 文字列を浮動小数点に変換する。
 ...はずだけど、FBのXでは浮動小数点を扱えません。

●num& = FN StrToLong (xCmdPtr&, theStr$)
 文字列を符号なし longInt(倍精度整数)に変換。
 マイナスを含む文字列を渡してもマイナスを無視して変換します。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN StrToLong& (xCmdPtr&, @strPtr&)

○cStrHandl& = FN PasToZero (xCmdPtr&, theStr$)
 FB文字列をC文字列に変換してそのハンドルを返す。

○FN ZeroToPas (xCmdPtr&, cStrPtr&, theStr$)
 C文字列をFB文字列に変換する。


' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
' field utilities
' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

○cStrHandl& = FN GetFieldByID (xCmdPtr&, cdFlag%, fieldID%)
 フィールドの文字列をコピーして、そのハンドルを返す。
 第2パラメタはカードフィールドなら1、BGフィールドなら0を渡す。
 第3パラメタはフィールドIDを指定。

●cStrHandl& = FN GetFieldByName (xCmdPtr&, cdFlag%, fieldName$)
 フィールドの文字列をコピーして、そのハンドルを返す。
 第3パラメタはフィールド名を入れたFB文字列。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN GetFieldByName(xCmdPtr&, cdFlag%, @fieldNamePtr&)

○cStrHandl& = FN GetFieldByNum (xCmdPtr&, cdFlag%, fieldNum%)
 フィールドの文字列をコピーして、そのハンドルを返す。
 第3パラメタはフィールド番号。

○FN SetFieldByID (xCmdPtr&, cdFlag%, fieldID%, cStrHandl&)
 フィールドにC文字列ハンドルの内容をコピーする。
 第2パラメタはカードフィールドなら1、BGフィールドなら0を渡す。
 第3パラメタはフィールドID。
 第4パラメタはフィールドに入れるC文字列のハンドル。

●FN SetFieldByName (xCmdPtr&, cdFlag%, fieldName$, cStrHandl&)
 フィールドにC文字列ハンドルの内容をコピーする。
 第3パラメタはフィールド名を入れたFB文字列。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN SetFieldByName (xCmdPtr&, cdFlag%, @fieldNamePtr&, dataHndl&)

○FN SetFieldByNum (xCmdPtr&, cdFlag%, fieldNum%, cStrHandl&)
 フィールドにC文字列ハンドルの内容をコピーする。
 第3パラメタはフィールドナンバー。


' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
' script routines
' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

○FN FormatScript (xCmdPtr&, scrptHndl&, insertPt&, quickFormat&)
 C文字列をスクリプトとみなして段落を付ける。
 insertPt& はカーソルの位置。quickFormat& は 0 で良い。

○FN ZeroTermHandle (xCmdPtr&, hndl&)
 ハンドルを1バイト拡張して最後に0を書き込む。
 C文字列としての終端処理。

ΘFN PrintTEHandle (xCmdPtr&, handleTE&, header&)
 テキストエディットの文字列をプリントする。
 header& はヘッダ文字列(C文字列)へのポインタ。

ΘFN SendHCEvent (xCmdPtr&, hcEvent&)
 ハイパーカードにイベントを送るらしい。

ΘProcPtr& = FN HCWordBreakProc (xCmdPtr&)
 テキストエディットのワードラップルーチンアドレスを返すらしい。


' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
' sound routines
' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

ΘFN BeginXSound (xCmdPtr&, wndPtr&)
 Xがサウンドマネージャを使うことを宣言するらしい。
 THINK C でもコンパイル出来ませんでした。使い方よくわからん。

ΘFN EndXSound (xCmdPtr&)
 サウンドの使用が終了したことを宣言するらしい。

○FN RunHandler (xCmdPtr&, cStrHandl&)
 C文字列ハンドルの文字列をスクリプトとしてハイパーカードに実行させる。
 改行を含んだ(=複数行からなる)長いスクリプトを実行させることも出来る。
 ループや条件分岐なども含めて良い。


' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
' general utility routines
' ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

●FN GetXResInfo (xCmdPtr&, refNum&, xID%, rType&, rName$)
 現在呼び出されているX自身の情報を得る。
 refNum& にはXの入っているファイルのリファレンスナンバー、
 xID% にはXのリソースID、
 rType& にはXのタイプ( _"XCMD" または _"XFCN")、
 rName$ にはリソース名が返ってきます。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN GetXResInfo (xCmdPtr&, @resFilePtr& , @resID&, @rType&, @resNamePtr&)

●bool% = FN GetFilePath (xCmdPtr&, fileName$, numTypes%, typeList&, askUser&, fileType&, fullName$)
 ファイル名 fileName$が、ホームにあるファイル検索パスにあるかどうかを返す。
 askUser& に1を入れておくと見つからなかった場合にファイルダイアログを
 出してユーザーに確認を求める。0なら単に _false を返す。
 fileType& にはファイルタイプ( _"APPL"、_"STAK" など)を指定するが、
 ファイルが見つからずにユーザーがダイアログからファイルを指定した場合は
 関数側からの返値を収める変数として働く。
 numTypes% にはファイルのタイプ数、typeList& にはタイプリストのポインタ、
 fullName$ にはファイルのフルパスが返ってきます。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN GetFilePath (xCmdPtr&, @fileNamePtr&, @numTypes&, @typeList&, askUser&, @fileType&, @fullNamePtr&)

ΘdocPtr& = FrontDocWindow( xCmdPtr& )
 一番上のドキュメントレイヤーウィンドウ(カードウィンドウ)のポインタを
 返す。フローティングパレットレイヤーのものは除外される。

●FN PointToStr (xCmdPtr&, thePoint, theStr$)
 座標を渡すとそれを "50,100" などのFB文字列に変換する。
 :関数定義の2行目を以下のように書き換えて下さい。
 :xCmdPtr&.inArgs0& = ptPtr&

○FN RectToStr (xCmdPtr&, theRect, theStr$)
 レクトを渡すとそれを "50,100,200,300" などのFB文字列に変換する。

○FN StrToPoint (xCmdPtr&, theStr$, thePoint)
 座標を表すFB文字列("50,100" など)をポイント型に変換。

●FN StrToRect (xCmdPtr&, theStr$, theRect)
 レクトを表すFB文字列("0,0,50,100" など)をレクト型に変換。
 構造体(レコード) theRect にレクトが入ります。
 予め DIM theRect.8 のように宣言して領域を確保しておきましょう。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN StrToRect (xCmdPtr&, @strPtr&, @rctPtr&)

●myTE& = FN GetFieldTE (xCmdPtr&, cardFldFlag&, fieldID&, fieldNum&, fieldName$)
 フィールドのTEハンドルのコピーを得る。
 cardFldFlag& はカードフィールドなら1、BGフィールドなら0を指定。
 残りの fieldID&, fieldNum&, fieldNamePtr& はそれぞれフィールドID、
 フィールドナンバー、フィールド名を与えるが、前から順に優先順位があり、
 それぞれが0だった場合にその次のパラメータが有効になる。
 返値のTEハンドルは不要になったら破棄する必要があります。
 :関数定義の一行目を以下のように書き換えて下さい。
 :FN GetFieldTE (xCmdPtr&, cardFldFlag&, fieldID&, fieldNum&, @fieldNamePtr&)

●FN SetFieldTE (xCmdPtr&, cardFldFlag&, fieldID&, fieldNum&, fieldName$, fieldTE&)
 フィールドにTEの内容をセットする。
 cardFldFlag& から fieldName$ の各パラメータは GetFieldTE と同じ。
 fieldTE& はセットするデータの入ったテキストエディットハンドル。
 このTEの内容がフィールドにコピーされます。不要になったら破棄すること。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN SetFieldTE (xCmdPtr&, cardFldFlag&, fieldID&, fieldNum&, @fieldNamePtr&, fieldTE&)

ΘFN GetObjectName (xCmdPtr&, object&, @objName$)
 スクリプトエディタに関するユーティリティ。

ΘFN GetObjectScript (xCmdPtr&, object&, scriptHndl&)
 スクリプトエディタに関するユーティリティ。

ΘFN SetObjectScript (xCmdPtr&, object&, scriptHndl&)
 スクリプトエディタに関するユーティリティ。

●refNum& = FN StackNameToNum (xCmdPtr&, stackName$)
 スタックネームを与えるとそのスタックのリファレンスナンバーを返す。
 ホームの検索パスに見つからなかった場合はファイルダイアログが出ます。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN StackNameToNum (xCmdPtr&, @stackNamePtr&)

○FN Notify (xCmdPtr&)
 ハイパーカードが他のアプリの背面にある場合に、前面に出されるまで
 Xの動作を止めてアプリケーションメニューのミニアイコンを点滅させる。
 ハイパーカードが最前面にある時は無視されます。

●ans% = FN ShowHCAlert (xCmdPtr&, dlgID%, theStr$)
 theStr$ 文字列と共にダイアログを出す。
 dlgID% が1の場合は OK のみ、2なら OK Cancel、
 3なら Cancel Delete 、4なら Yes Cancel No のボタンが付きます。
 それぞれ一番左がデフォルトで、選んだボタン番号が返されます。
 :関数定義の一行目を以下のように書き換えて下さい。
 :LOCAL FN ShowHCAlert (xCmdPtr&, dlgID%, @promptStrPtr&)


 editing with windoids と 残りの script routines は、まず使うことは無いと思うので割愛します。external windows はXでウィンドウを扱うときに必須なのですが、ちょっと検証作業がタイヘンだったのでこれも割愛します(^^;)
 下の2つはオマケです(^^;)

○FN HideHCPalettes (xCmdPtr&)
 ミニウィンドウ(ツール/パターンパレット、ビット表示、スクロール
 ウィンドウ、メッセージボックス)を全て隠す。Xを終了すると自動的に
 元に戻される。

○FN ShowHCPalettes (xCmdPtr&)
 HideHCPalettes で隠したウィンドウを元に戻す。



 HyperXcmd Glue.INCL ファイルの修正は以上です。

 次に、ここまで説明してきたグルールーチンを使う側の HyperXcmd.MAIN も修正します。
 33行から以下のようなプログラムになっているので

 LONG IF prmCount% <> 0
   FOR j = 1 TO prmCount%                        'cycle thru parameters
     FN ZeroToPas (xCmdPtr&, j-1, @str255$(j-1)) 'converting all C-strings to
   NEXT                                          'Pascal strings
 END IF
 この真ん中の行を下のように書き換えて下さい。

FN ZeroToPas (xCmdPtr&, [[xCmdPtr& + _params + (j - 1) * 4]], str255$(j-1))

 個人的には str255$() というのはなんだかおかしな変数名なので、paramStr$() とでも変えた方がいいような気がします。もし変更するなら str255$ を使っている部分を全て変更する必要がありますが。


 HyperXcmd.MAIN ファイルの修正は以上です。


             −・−・−


 以上でファイルの修正が終わりました。ここまではバグフィックスなので、この作業は最初に1回行えばOKです。以後 XCMD を作る時は、ひとつの XCMD ごとにこのフォルダをコピーして使います。


Next



FutureBASIC Lab.

UDI's HomePage
inserted by FC2 system