マウスカーソルを変更してみる ACT-1

今回はマウスカーソルの変更をしてみます。特に、時間のかかる計算などのために一時的にカーソルをビジー表示(デフォルトでは砂時計カーソル)にする場合の手順について説明することにします。

マウスカーソルの変更

一時的にカーソルをビジー状態に変更するための手順は以下のようになります。

  1. LoadCursor 関数でシステム定義のカーソルの中から砂時計カーソルをロードする。
  2. SetCursor 関数でマウスカーソルを変更する。

今回はあくまで一時的にマウスカーソルの表示を変更するだけです。アプリケーションの新しいマウスカーソルを設定するということはできません。これは、後で述べる理由により、マウスカーソル変更中に waitawaitstop を実行することができないためです。アプリケーション固有のマウスカーソルの設定や、カーソル変更中にスクリプトで waitawaitstop を実行したい場合の方法は、次回に説明します。

マウスカーソルのロード

マウスカーソルは、アイコンの場合と同じく、Windowsユーザーオブジェクトの1つである『カーソルオブジェクト』として扱われることになります。したがって、マウスカーソルを操作するためには、カーソルオブジェクトを作成して、そのハンドルを取得しなければならないことになります。

今回使用するマウスカーソルは、システム定義のカーソルです。システム定義カーソルオブジェクトを作成して、そのハンドルを取得するためには、 LoadCursor 関数を呼び出します。

HCURSOR LoadCursorA(
    HINSTANCE hInstance,   // インスタンスハンドル
    PCSTR     pCursorName  // リソース名/リソースID
);

hInstance パラメータに 0 (NULL) を、 pCursorName パラメータには決められた値を指定することでシステム定義カーソルをロードできます。砂時計カーソルをロードするには pCursorName パラメータに 32514 (IDC_WAIT) を指定します。

LoadCursor 関数は戻り値としてロードされたマウスカーソルのハンドルを返します。


ところで、この LoadCursor 関数によって作成されたカーソルオブジェクトは、前回作成されたアイコンオブジェクトなどとは少し性質が異なります。 LoadCursor 関数によって作成されたカーソルオブジェクトは『共有カーソル(shared cursor)』と呼ばれるもので、以前に作成したのと同じカーソルリソースからカーソルオブジェクトを作成しようとしたときに、新しくオブジェクトを作成するのではなく、以前に作成されたオブジェクトを共有するようにしているのです。したがって、 LoadCursor 関数からは以前のカーソルと同じハンドルが返されます。このような動作を行なうことにより、新しいオブジェクトのための余分なメモリ領域と、作成のためのオーバーヘッドを少なくしているのです。

共有カーソルは、1つのプロセスだけでなく、すべてのプロセスで共有されるようになっています。そのため、オブジェクトが必要なくなったとしても、明示的にオブジェクトを削除する必要がなくなるのです。前回作成されたアイコンは『共有アイコン』ではなかったため破棄していましたが、今回 LoadCursor 関数で取得されたカーソルは破棄する必要がありません。

マウスカーソルの変更

マウスカーソルのハンドルを取得できたら、次に、マウスカーソルを変更します。 マウスカーソルを変更するためには SetCursor 関数を呼び出します。

HCURSOR SetCursor(
    HCURSOR hCoursor    // カーソルハンドル
);

hCoursor パラメータには、先ほど取得したカーソルのハンドルを指定します。

マウスカーソルを元に戻すには

マウスカーソルを元に戻すには、先ほどの手順において、標準矢印カーソルをロードして指定することで行なうことができます。 LoadCursor 関数の pCursorName パラメータに 32512 (IDC_ARROW) を指定することで標準矢印カーソルのハンドルが取得できるので、これを SetCursor 関数で指定すれば元に戻ります。

一時的な変更しかできない理由

今回説明した方法では waitawaitstop のいずれかの命令を実行することでマウスカーソルが元に戻ってしまいます。なぜこれらの命令を実行すると、カーソルが元に戻ってしまうのでしょう?

これは、 waitawaitstop を実行することによって、ウィンドウメッセージを処理する機会が与えられることによります。マウスカーソルの移動やシステム命令実行などを行なうとき、システムはウィンドウに WM_SETCURSOR メッセージを送ることになっています。このメッセージが送られると、メッセージを処理するデフォルトウィンドウプロシージャは、ウィンドウ作成時にHSP内部で登録されたウィンドウクラスで指定されているマウスカーソルに自動的に変更してしまうのです。これは、 waitawaitstop 命令によって WM_SETCURSOR メッセージが処理され、マウスカーソルが元に戻ってしまうことを意味します。

これによってマウスカーソルが元に戻らないようにするには、デフォルトウィンドウプロシージャに stop 命令によって WM_SETCURSOR メッセージを処理させないようにする方法と、ウィンドウクラスに設定されているマウスカーソルそのものを変更してしまう方法とが考えられます。前者はスクリプト上で行なうことはできませんが、後者(ウィンドウクラスのカーソル変更)は実現できます。これは次回に説明します。

サンプルスクリプト

さて、実際にスクリプトを書いてみます。

サンプルスクリプト main.as

    #include "llmod.as"

    #module ;############ カーソル変更モジュール ####################

    ;===============================================================
    ; カーソルをシステム定義カーソルに変更
    ; SetSystemCursor  n1
    ;     n1 : カーソルの種類(32512:標準  32514:砂時計)
    ;===============================================================
    #deffunc SetSystemCursor int
    mref type, 0            ;カーソルの種類

    ; システム定義カーソルのハンドル取得
    pm = 0, type
    dllproc "LoadCursorA", pm, 2, D_USER@
    hCursor = dllret@       ; カーソルハンドル

    ; カーソル変更
    dllproc "SetCursor", hCursor, 1, D_USER@
    return

    #global ;############# モジュール終わり ########################


    button "処理", *lb_work
    stop

*lb_work
    mes "処理中..."

    ; 砂時計カーソルに変更
    SetSystemCursor  32514

    ; 時間のかかる処理
    repeat 5000000 : a++ : loop

    mes "終了"

    ; 元のカーソルに戻す
    SetSystemCursor  32512

    stop