ShowCursor

ゲームを作るときなど、マウスカーソルが邪魔になるとき使うのが ShowCursor 関数です。

使うときは、

Call ShowCursor(0)
で一発非表示、
Call ShowCursor(1)
で一発表示!
・・・などと思っていませんか?

そう思っていた人はこんな経験があるはずです(なければないでいいですが)。

Call ShowCursor(0)
でマウスカーソルを消した後、運悪くエラーなどにより
Call ShowCursor(1)
を使わずに終了することになると、マウスカーソルは消えたままです。
Call ShowCursor(1)
で一発表示が可能ならばもう一度実行すれば戻るはずですが、実際のところ、戻りません
あるいは、もっと単純に、
よく考えずに
Call ShowCursor(0)

Call ShowCursor(1)
を何度も使ってマウスカーソルが現れなくなった、あるいは消えなくなった。

さて、これを見て鋭い人なら気付いたかもしれませんが、問題なのは ShowCursor 関数を呼び出した回数なのです。
それでは、呼び出した回数をわざわざカウントしてやる必要があるのでしょうか。
しかし、そんなことをしていては先ほどの前者の例のときに対応できません。

そもそも、なぜ呼び出した回数が問題になるのでしょうか。
実は ShowCursor 関数自体が呼び出されるたびにカウント(表示カウントという。まんまですね)を取っているのです。
具体的には、

Call ShowCursor(0)
で表示カウントを1減らし、
Call ShowCursor(1)
で表示カウントを1増やします。
そして表示カウントが0未満になるとカーソルが消え、0以上になるとカーソルが現れるのです。
なぜこのような仕組みになっているのか私に聞かれても分かりません。こんなややこしい仕組みを作った Microsoft を恨んでください。

つまり、この表示カウントを取得することができれば、何回 ShowCursor を使えばいいのか分かるわけです。
もちろん自前でカウントする必要なんてありません。
表示カウントは ShowCursor を使ったときの戻り値で得られます。
ですから、表示カウントを1増やしつつ増えたあとのカウントを変数 c に格納したい場合は、次のようになります。

c = ShowCursor(1)
また、表示カウントを変化させずに変数 c に格納するには、プラスマイナスで打ち消せばよいのですから、こうなります。
c = ShowCursor(1)
c = ShowCursor(0)

さて、表示カウントの具体的な数値が分かっても、それだけでは意味がありません。
目的は、マウスカーソルの表示・非表示を切り替えることです。
となれば、実際必要になるのはカウントが0以上か0未満かということだけになります。
それさえ分かれば、カウントが0未満、または0以上になるまで ShowCursor を呼び続ければよいのです。
実際にコードを書くと、次のようになります。

'マウスを出すぞー
Do Until ShowCursor(1) >= 0
Loop
'マウスを消すぞー
Do Until ShowCursor(0) < 0
Loop
あるいは、
'マウスを出すぞー
Do
c = ShowCursor(1)
Loop While c < 0
'マウスを消すぞー
Do
c = ShowCursor(0)
Loop While c >= 0
不等号の向きに注意してください。
下手をすると無限ループに陥り、表示カウントが-10000以下という恐ろしい事態にもなりかねません(再起動すれば直りますが)。

ここまでくればもうよさそうな気もしますが、これではまだ不充分です。
終了する時にマウスカーソルを表示するのは当然ですが、その後に、最初に書いたことを信じている人の作ったプログラムが起動されないとも限らないからです。
そのプログラムが正しく動くように、終了時には表示カウントを初期値0に戻す必要があるのです。
マウスが非表示の状態から表示したのなら問題ありませんが、複数回マウスを表示するコードを実行しているときは、表示カウントが0より大きくなっている可能性があります。
もう書く必要はないと思いますが、実際のコードを書いておきます。

c = ShowCursor(1)
Do While c < 0
c = ShowCursor(1)
Loop
Do While c > 0
c = ShowCursor(0)
Loop

32bit版では…?(04/02/08追加)

現れないと書きましたが、実は32bit版のWindowsでは、つまり今主に使われているバージョンのものでは、ShowCursor の表示カウントはプロセスごとに別のものが使われるので、一つのプログラムでカーソルが現れなくなっても、そのプログラムさえ終了してしまえばカーソルは元に戻ります
最初にこれを書いたときから一応このことは知っていましたが、このことを書いてしまうとちゃんと表示カウンタを戻さずに終了してしまう人がいるかと思い、最初は書かないでおきました。
また、16bit版ではWindows全体で同じ表示カウントを使っているので、ここに書いたことが実際に起こります(事実そういうことが起こったからこそこのページを作ったわけですから)。

VB6用サンプル(4.66kilobyte)