HSPプラグインをABで作る
HSPプラグインをABで作る方法を説明するページです。
アプリケーションタイプをDLLにしたプロジェクトを作成しておいてください。
また、バイナリエディタを(どのソフトでもよいので)用意しておいてください。
| (1)普通にコードを記述する |
| (2)引数の順番などを考える |
| (3)戻り値を考える |
| (4)プロシージャ名を考える |
| (5)コンパイル |
| (6)プロシージャ名を考える Part 2 |
| (7)定義ファイルを作成する |
(1)普通にコードを記述する
普通とはいっても、DLLのときと同じで、
外部から呼び出したい関数には「Sub」や「Function」の後に「Export」を付ける必要があります。
例えば、
Function Export Procedure(Buf As Long) As Long
のようにします。
また、引数や戻り値にString型を使用することはできませんので、BytePtr型で代用します。
但し、HSPでは必ず引数が4つになりますので、引数を4つも必要としなくても、ダミーの引数を作っておいてください。
HSPから呼び出すときにLong型の引数を省略すると、そこには0が入ります。
というより、内部の処理ではその引数は無視しておけばOKです(^^)
また、戻り値はLong型で固定になり、文字列を返したい場合はどこかの引数をBytePtr型にしておいてください。
(2)引数の順番などを考える
引数はどのような順番にすればよいのか。HSPヘルプから引用(一部修正)します。
| タイプ0 : 新規命令 p1,p2,p3 (p1=int, p2=int, p3=int) タイプ1 : 新規命令 p1,p2,p3 (p1=変数バッファへのポインタ, p2=int, p3=int) タイプ2 : 新規命令 p2,p3 (p1=BMSCR構造体へのポインタ, p2=int, p3=int) タイプ3 : 新規命令 p1,p2,p3 (p1=変数情報PVAL構造体へのポインタ, p2=int, p3=int) タイプ4 : 新規命令 p1,p2,p3 (p1=int, p2=str, p3=int) タイプ5 : 新規命令 p1,p2,p3 (p1=変数バッファへのポインタ, p2=str, p3=int) タイプ6 : 新規命令 p2,p3 (p1=BMSCR構造体へのポインタ, p2=str, p3=int) タイプ7 : 新規命令 p1,p2,p3 (p1=変数情報PVAL構造体へのポインタ, p2=str, p3=int) (p4のパラメータ) 各タイプ+$00 : p4=int 各タイプ+$10 : p4=システム変数refstrへのポインタ 各タイプ+$20 : p4=ファイルハンドルdpminfoへのポインタ 各タイプ+$30 : p4=パレットデータrgbptrへのポインタ (その他) 各タイプ+$80 : ver2.5専用のPVAL構造体を使用する 各タイプ+$100 : HSP終了時に自動的に呼び出される関数(クリーンアップ関数)の指定 |
上の表は、HSPで使える引数の組み合わせです。
つまり、上のリストのうちどれかに入るように、引数の順番を変えたりする必要があるわけです。
この中で、BMSCR構造体&PVAL構造体については難しいので解説は省きます(^^;;
但し、BMSCR構造体はHSPのプログラムから引数の指定はせずに自動的に渡されるため、
上の表で「タイプ2」「タイプ6」には引数にp1がないのです。
また、dpminfoやrgbptr、refstrについてもここでは解説しません(^^;;;;
※refstrについても解説します。
「p1=int」というのは、p1(1個目の引数)がint型だということです。
このint型は、ABではLong型に相当します。
同様に、strはString型ですが、DLLですのでBytePtr型となります。
「変数バッファへのポインタ」はVoidPtr型でよいと思いますが、構造体が関わる場合は構造体をデータ型にしてください。
例えば、次の宣言で始まる関数があったとします。
Function Export Func1(a As Long, b As Long) As Long
この場合、上の表の中で一番近いのは「タイプ0」です。
ここで、p3とp4が足りませんので、2つのダミー引数を付加します。
ダミー引数はLong型にしておきます(これは上の表ではint型となります)。
従ってHSP用の関数に書き直すと、
Function Export Func1(a As Long, b As Long, dummy1 As Long, dummy2 As Long) As Long
となります。
Function Export Func2(a As BytePtr, b As Long) As Long
この関数では第1引数が文字列ですが、上の表で「p1=str」というのがありません。
逆に、「タイプ4」には「p2=str」がありますので、引数の順番を入れ替えてやります。
後はダミー引数を2個つけて、
Function Export Func2(b As Long, a As BytePtr, dummy1 As Long, dummy2 As Long) As Long
となります。
※補足
引数の順番を変えたくない場合があるかもしれません。そんなときは「タイプ6」を考えれば大丈夫です。
先程「タイプ2」「タイプ6」の第1引数は自動で渡されると書きました。
そこで、BMSCR構造体をダミー引数にして、
Function Export Func2(dummy1 As VoidPtr, a As BytePtr, b As Long, dummy2 As Long) As Long
とすれば、呼び出す方は引数の順番を変えずにすみます。
もちろん、呼び出す側は第2引数から指定することになります。
Function Export Func3(a As BytePtr, b As BytePtr) As Long
これ、どうすればいいか解りますか!?
上の表で2個の引数が「str」であるタイプは見当たりません。
そこで「タイプ5」を活用します。
「タイプ5」は「変数バッファへのポインタ」とあります。
そこで、少々荒技ですが、第1引数を必ず文字列の入った変数で受け取るようにすると、
Function Export Func3(a As BytePtr, b As BytePtr, dummy1 As Long, dummy2 As Long) As Long
が使えるわけです。
<※補足 2005.05.01>
Function Export Func4(a As BytePtr, b As Long) As Long
bに指定された数値を文字列に変換(例: 500→"500")して、a[ポインタ]にコピーする、というコードだとします。
本当なら関数の戻り値として変換後の文字列が返せたら、と思っていましたが、それができないのです。
こんなとき、システム変数refstrというヤツを使えば、HSP側ではポインタを意識せずに文字列を返してもらえます。
タイプに「$10」を足して指定すると、第4引数がその変数へのポインタになります。
もう a As BytePtr は不要ですから、
Function Export Func4(b As Long) As Long
続いて、ダミー変数を追加しますが、第4引数はポインタですので
Function Export Func4(b As Long, dummy1 As Long, dummy2 As Long, refstr As BytePtr) As Long
となります。
関数内では、
lstrcpy(refstr, Str$(b))
のようにすることで、値を返すことができるのです。
それから、もしDLLにしようとしている関数が、DLLにする目的で作成されているのなら構いませんが、
ABだけで使っていたような関数をDLL用にする場合、文字列型をStringからBytePtrに変えなければなりません。
もしこれがByValで宣言されていて、引数を変数みたいに扱って
引数 = "000"
のように処理の途中経過などを放り込んでいるとしたら。
BytePtr型には直接文字列は代入できません。
そこで下のような方法を使ってみてはいかがでしょうか。上がBefore・下がAfterです。
| Function Func(a As String) As Long (略) End Function |
| Function Export Func(_a As BytePtr) As Long Dim a As String a=_a (略) End Function |
(3)戻り値を考える
HSPでは、プラグインの関数は戻り値をstatという変数に格納します。
但し、このとき正の数を指定すると、エラーでストップしてしまいます。
そこで、戻り値を使うときには必ず負の数で指定します。
ですが、0を返す場面では従来通りそのまま0を返せばいいです。
HSPで1を返したければ、内部の処理では戻り値として-1を返してください。
(4)プロシージャ名を考える
一番やっかいなのがこれです。
HSPプラグイン内の関数は、その名前が
「_関数名@16」
の形をとらなければなりません。
しかし、ABでは関数名に@を含めることができません。
そこで、一度関数名を全て
「_関数名___」
にしてしまってください。
(5)コンパイル
これは普通にすればOKです。
コンパイル後、DLLの拡張子を「.hpi」にしても構いません。
(6)プロシージャ名を考える Part 2
(4)では「_関数名___」の形にしましたので、このままでは動作させることができません。
そこで荒技。バイナリエディタを使用します。
バイナリエディタはどんなものでもいいです。皆さんのお好きなものをお使い下さい。
ここでは「Bz」というソフトを使って解説します。
<1>先程作成したDLLをバイナリエディタで読み込みます。

<2>「___」を検索します。
検索方法はソフトにより異なるので各自で調べてください。

<3>「___」の部分に「@16」と上書きしてください。

<4> <2>〜<3>を繰り返し、すべての「___」を「@16」に置き換えてください。
ソフトに置換機能があればそれを使ってもよいでしょう(あるのだろうか!?)。
※まずないと思いますが、コンパイラが吐き出したコードに「___」が含まれるかもしれませんので、
必ず「関数名を表している」と確認できるところだけを置き換えてください。
(上の画像では処理している部分が _GetAuction___ となっていて関数名だとわかるので置き換えてよい)
<5>上書き保存してください。
これで準備は完了です。
なお、バグ修正などの理由でDLLをコンパイルし直した場合には、この作業をもう一度行う必要があります。
(7)定義ファイルを作成する
ここですよね、最難関…(^^;;
定義ファイルは、拡張子を.asにするのが普通です。
そして、その内容を次のようにします。
| #uselib "プラグイン名" ;これはどんなプラグインでも普通は1個 #func 新規名称 関数名 タイプ ;これを関数の個数分書く |
HSPではコメントの最初に「;」をつけることになっていますので、上でも「;」をつけて書いています。
1行目「#uselib」は、「この関数の命令を使いますよ」という命令です。
2行目以降に関数を「#func」で宣言します。
「新規名称」とはHSPで呼び出すときの命令の名前、「関数名」とはDLL内のプロシージャ名です。但し、
(例)「_Func2@16」ではなく「Func2」 と書いてください。
プロシージャ名がHSPの命令語と被る場合は別ですが、普通は「新規名称」「関数名」は同じでいいでしょう。
なお、ここは""で括る必要はありません。
もちろん、「#func」はDLLに存在する命令についてならいくらでも書くことができます。
次にタイプですが、まずは、(2)に載せたタイプの一覧を再掲します。
| タイプ0 : 新規命令 p1,p2,p3 (p1=int, p2=int, p3=int) タイプ1 : 新規命令 p1,p2,p3 (p1=変数バッファへのポインタ, p2=int, p3=int) タイプ2 : 新規命令 p2,p3 (p1=BMSCR構造体へのポインタ, p2=int, p3=int) タイプ3 : 新規命令 p1,p2,p3 (p1=変数情報PVAL構造体へのポインタ, p2=int, p3=int) タイプ4 : 新規命令 p1,p2,p3 (p1=int, p2=str, p3=int) タイプ5 : 新規命令 p1,p2,p3 (p1=変数バッファへのポインタ, p2=str, p3=int) タイプ6 : 新規命令 p2,p3 (p1=BMSCR構造体へのポインタ, p2=str, p3=int) タイプ7 : 新規命令 p1,p2,p3 (p1=変数情報PVAL構造体へのポインタ, p2=str, p3=int) (p4のパラメータ) 各タイプ+$00 : p4=int 各タイプ+$10 : p4=システム変数refstrへのポインタ 各タイプ+$20 : p4=ファイルハンドルdpminfoへのポインタ 各タイプ+$30 : p4=パレットデータrgbptrへのポインタ (その他) 各タイプ+$80 : ver2.5専用のPVAL構造体を使用する 各タイプ+$100 : HSP終了時に自動的に呼び出される関数(クリーンアップ関数)の指定 |
各関数の引数を見て、適切なタイプを選べばOKです。
ここで「各タイプ+$100」について説明します。
定義ファイルに記述するタイプに$100を足すことで、プログラムが終了するときに呼び出される関数を指定できます。
例えばDLL内のある関数でメモリを確保したら、それを解放する関数を「+$100」で宣言することができます。
なお、数値の前にある「$」は16進数を表し、ABなどほとんどのBASICでは「&H」のことです(しつこいですが…)。
※PureBasicは「$」を使ってたりします(^^;;
例えば、
Function Export Func2(dummy1 As VoidPtr, a As BytePtr, b As Long, dummy2 As Long) As Long
これで始まる関数をHSPで宣言したい場合は、
#func Func2 Func2 6
という感じで定義ファイルに書くことになります。
これをHSPからは
Func2 a,b
のようにすることで、その関数を使うことができます。
以上、かなり速く進んでしまいましたが、何となくでもご理解いただけましたか?
もしよくわからなければ、「AB作品の部屋」にソースコードや定義ファイルもセットの「yauc.hpi」を置いていますので
どうぞご参考になさってください。
また、実際に定義ファイルの呼び出しは、HSPのプログラムから
#include "定義ファイル名"
のように書けばOKです。
また、プラグインと定義ファイルは必ず同じフォルダ内に置いてください。でないとエラーになります。
ご不明な点があれば掲示板で質問してくださっても結構です。
但し、私もHSPは少しかじった程度ですので、あまり難しいご質問に対してはお答えできないことがあります。
それではABでHSP用プラグインを作る企画、これにて終了!