Win32API
今章ではHSPの可能性を大きく広げるウィンドウズAPIについて説明します。
この辺りの処理はHSP2でやるよりも後発のHSP3以降で組む方が随分と楽になりますが、
何らかの理由により、HSP2をあえて使い続ける人のために紹介したいと思います。
…と、その前にAPIとは何か、から簡単に説明したいと思います。
APIとはApplicationProgram(ming)Interface(アプリケーションプログラムインタフェイス)の頭文字で
ソフト開発をする上で使用できる命令や関数群のこと、または手続きを定めた規約の集合のことです。
具体的に、文字列・画像・ファイル等の共通機能をプログラマが全てコーディングせずとも済む様
汎用(柔軟)性のある抽象的な外枠(基本)部分だけを提供しており、
同一プラットフォームにおいて別マシンでも原則は同じ動作をさせることができます。
HSPには多くの命令が実装されていますが、元々は、Windowsから提供されているAPIを、
HSP内でステートメント(命令名)と数値か文字列パラメータ指定するだけの形に変えたり、
幾つかのAPIを組み合わせて一連の処理にしたものなのです。
つまり、HSPではAPI関数を間接的に呼び出すラッパー命令という位置付けになります。
HSPの命令として存在する処理を間接的にではなく直接利用してもメリットはあまりありませんが、
存在しない機能を利用できるというのは、HSPの可能性が大きく広がルことを意味します。
尚、APIはHSP拡張プラグイン同様にDLLとして提供されているので、
「直接」使うといっても、呼び出すために結局はHSPの命令を介する必要があり、
HSP2.55以前に拡張プラグインLoadlib.dllとして存在し、
HSP2.6以降では標準命令となったtom氏のロードライブラリ(ll系)命令群を使用します。
基本的な流れとして以下の様になっています。
1. DLLをロード
2. 使用するAPI関数を指定
3. API関数の実行
4. 必要ならば戻り値を受け取る
5. DLLをクローズ
ll系命令には、この流れを行うのに2通りの方法がありますが、
自分は一方のやり方でしか行わないので、
面倒そうではあるが、より低レベルな制御もできそうな方法は使ったことがありません。
ココでは「その使ったことのないやり方」に比べると簡単な方法を紹介します。
APIを利用する上で初めに行うべきDLLの読み込み処理から。
「ll_libload 受け取る変数, DLL名」という書式で、
メモリにロードし、またp2で指定したDLLを操作するために必要なハンドルをp1に代入します。
p2には「user32.dll」や「winmm.dll」等を指定しますが、
もし、名前が間違っている等でロードに失敗した場合はp1の変数に0が入ります。
取得に失敗した場合は、以降の処理が行えませんので、終了させるなりするようにしましょう。
ll_libload h, "hspbc.dll" // こんなDLLは元々ない(と思う)が…
if h = 0 {
dialog "DLLの読み込みに失敗しました。", 1
end
}
mes "["hspbc.dll]の関数をロードします..."
stop
もし、ハンドルが取得できたら、API関数を使用するたびに必要となりますし、
また、不要になったときの解放時にもこのハンドルを指定する必要がありますので
そのまま保持し続けるか、別の変数に退避するようにしてください。
完全に不要となるのは、アプリケーション終了時か大半であり、
アプリケーションの終了時には自動で解放されるために使う頻度は多くありませんが、
読み込んだDLLを解放する命令はll_libfree命令で、
書式は「ll_libfree 解放するハンドル」となっています。
もし、ll_libload命令により同一DLLを複数回ロードしたら、
前回読み込んだものとは別に再度確保する仕組みとなっているので、
複数回読み込むロジックとなっている場合は、その回数だけll_libfree命令で解放するようにしてください。
尚、ロードしていない時にll_libfree命令で解放しようとしても何も起こりません。
if ole = 0 {
ll_libload ole, "ole32.dll" // ロードしていない時だけロードする
}
onexit *exit
// :
// API関数を使用していく
// :
stop
*exit
ll_libfree ole // ロードしていれば解放する
続いて、読み込んだDLLのハンドルから、使用するAPI関数のポインタを取得するわけですが、
この辺りから、API関数を紹介しているサイトを併用してみなければ、
どんな関数があり、どんな型の引数が幾つ必要なのかがわかりません。
サーチエンジン等を利用してAPI関数名を逆引きで検索してください。
尚、関数のポインタを取得するll_getproc命令の書式は
「ll_getproc ポインタ受取先変数, API関数名, DLLハンドル」です。
ココではパソコンを起動してからの経過時間を取得したいと思います。
「api パソコン 経過時間 取得」といったワードで検索すると、
DLLはKernel32.dllであり、GetTickCountが取得に必要なAPI関数であることがわかりますので
ロードDLLにKernel32.dllを、ll_getproc命令のAPI関数名文字列として渡すのです。
ll_libload kernel, "kernel32.dll"
ll_getproc ptr, "GetTickCount", kernel
mes ptr
ll_libfree kernel
stop
HSPウィンドウに謎な数値が表示されましたか?
もし0が表示されたなら取得失敗です。
HSPのステートメントと違い、API関数名の大文字小文字は正確に指定しなければなりませんよ。
0以外の数値が無事に表示されたなら、それがGetTickCountのポインタとなります。
ようやくAPI関数の実行に移るわけですが、関数を実行する命令は2種類用意されています。
1つはパラメータなし用、もう1つはパラメータあり用。
総本山とも言うべきMSDNライブラリの書式には「DWORD GetTickCount(VOID);」とあります。
ページ下を読んでいくと書いていますが、見方として「戻り値 関数名(パラメータ)」となり、
以前に紹介したデータ型を見てみると、戻り値は4バイト、引数なしであることがわかります。
つまり、今回使用するのはパラメータのない方の命令を実行しましょう。
ll_callfnv命令がそれに当たり、書式は「ll_callfnv API関数ポインタ」となっていますので、
先程、ll_getproc命令で取得したポインタをパラメータに渡してあげるとOKです。
ll_libload kernel, "kernel32.dll"
ll_getproc ptr, "GetTickCount", kernel
ll_callfnv ptr
ll_libfree kernel
stop
API関数を実行しましたが、結果値はstatにも入っていません。
パラメータのないAPI関数の設定処理だけだとコレで完了なのですが、
取得処理の場合は、結果を問い合わせする必要があるのです。
それを行うのがll_ret命令となり、書式は「ll_ret 取得変数」です。
指定した変数が文字列型の場合、正常に取得できませんので数値型の変数を指定するように注意願います。
ll_libload kernel, "kernel32.dll"
ll_getproc ptr, "GetTickCount", kernel
ll_callfnv ptr
ll_ret result
hour = result / 1000 / 60 / 60
minute = result / 1000 / 60 \ 60
second = result / 1000 \ 60
ll_libfree kernel
mes "起動してから" + hour + "時間" + minute + "分" + second + "秒経っています"
stop
API関数を実行し、結果を取得するのに毎回問い合わせをするのは面倒です。
statのように戻り値を確認する時は問い合わせしなくてもこの変数を見ればわかる
というようなことをしたいのであれば、ll_retset命令を使いましょう。
書式は「ll_retset 設定変数」で、以後はll_ret命令で問い合わせをしなくても
指定した変数に自動的に結果が格納される様になります。
尚、ll_retset命令で設定するのはll_callfnv等でAPI関数を実行する前にしなければなりません。
ll_libload kernel, "kernel32.dll"
ll_getproc ptr, "GetTickCount", kernel
ll_retset _stat
ll_callfnv ptr
hour = _stat / 1000 / 60 / 60
minute = _stat / 1000 / 60 \ 60
second = _stat / 1000 \ 60
ll_libfree kernel
mes "起動してから" + hour + "時間" + minute + "分" + second + "秒経っています"
stop
以上が1通りの流れになりますが、
まだAPI関数に引数ありのタイプを紹介していないのでそちらも紹介しておきます。
数値や文字列パラメータをAPI関数に渡して処理する場合に使用する命令はll_callfuncで、
書式は「ll_callfunc パラメータ配列, パラメータ数, API関数ポインタ」となっています。
p1で指定する配列変数から、p2の配列変数要素0から幾つ渡すかを指定する形なので、
モジュール命令みたいに8個までしか指定できない…とか、
パラメータ数に上限は設けられているわけではなく、あらゆるAPI関数に対応できるようになっています。
標準のmouse命令でマウスカーソル位置を変更できますが、
API関数のSetCursorPosで変更する場合を例にするとしてその書式を検索してみると、
DLLはuser32.dllであり、「BOOL SetCursorPos(INT, INT);」と書かれています。
戻り値BOOLは変更に成功したか否かの真偽値、
パラメータINTの2つはディスプレイ左上を基点としたX座標、Y座標とありますので
ll_callfunc命令の出番であることは間違いなさそうですね。
パラメータは1つにまとめて渡すと書きましたように、「prm = x, y」のように前準備しておきましょう。
そしてこの変数prmとパラメータ数2をll_callfunc命令のp1とp2に渡すのです。
p3で指定するポインタはll_callfnv命令で説明したものと同じなのでわかりますよね。
ll_libload user, "user32.dll"
ll_getproc SetCursorPos, "SetCursorPos", user
zahyou = dispx, dispy
ll_callfunc zahyou, 2, SetCursorPos
ll_ret result
if result : mes "カーソルをディスプレイ右下端に移動しました"
stop
API関数に渡すパラメータに数値ではなく文字列データを渡したい場合もあるはずですが、
API関数に文字列だけであればまだしも、数値と文字列を同時に渡したい場合に
配列に異なるデータ型を混在させることはできませんよね。このような場合はどうするのでしょう?
そもそも、HSP命令のように文字列をそのままパラメータとして渡す関数はないのですが、
これは文字列データは渡せないということではなく、文字列データの形を変えて渡すということです。
今までに何度か出てきている「ポインタ」というものが解決してくれます。
ポインタは以前に説明した通り、データそのものではなく、
データの格納先を示すアドレスをデータとして保持するものですので
データが数値だろうが文字列だろうが関係ありません。
文字列や構造体と呼ばれるデータの塊をAPI関数のパラメータに指定する場合、
この文字列や構造体のポインタを渡すようになっているのです。
HSP2.6から変数のポインタを取得するll_getptr命令が追加されましたのでコレを使いましょう。
ll_getptr命令の書式は「ll_getptr 取得元変数」で、
無事に取得成功できるとll_ret命令等でポインタを知ることができます。
それでは、文字列をパラメータとして、ダイアログを表示するAPI関数MessageBoxAを使ってみましょう。
書式は「INT MessageBoxA(HWND, PCTSTR, PCTSTR, INT);」でDLLはuser32.dllとなってます。
1つ目のハンドルパラメータにはオーナーとなるウィンドウハンドルを指定できますが、
0指定でオーナーウィンドウの持たなくなるだけでエラーにはなりません。
2つ目の変数ポインタはダイアログの内容メッセージを格納した変数のポインタを指定します。
ポインタを指定しない場合、空文字を送ったのと同じ状態を意味します。
3つ目の変数ポインタはダイアログのタイトルメッセージを格納した変数のポインタを指定します。
これも0指定で空文字を送るのと同じ状態を意味します。
4つ目のタイプ値には、配置ボタンの種類や、表示アイコンの設定等を指定できますが
ココではll_getptr命令の使い方に留まり、MessageBoxAの指定できる値については説明しません。
ll_libload user, "user32.dll"
ll_getproc MessageBoxA, "MessageBoxA", user
param = 0, 0, 0, 0
msg1 = "内容のテキスト"
msg2 = "タイトルテキスト"
ll_getptr msg1 : ll_ret param.1
ll_getptr msg2 : ll_ret param.2
param.3 = 4
ll_callfunc param, 4, MessageBoxA
ll_ret result
mes "結果=" + result
stop