それでもWindows98SEでもちゃんと動作した。
MSDNでは「Win98サポートせず」となっておりWebでも資料は少ない事に納得がいった。
ようするにテストされていない。使われていない関数なのだ。
スレッドを複数組んだ場合には使っても良いだろうが
timeSetEventとtimegettimeの組み合わせが良いという結論に至った。
今回はWindowのフリーズ回避にtimeSetEventのコールバックによるスレッド生成を利用した。
この関数をアプリ内で使う場合
CreateWaitableTimerでタイマーをオブジェクトを生成
↓
SetWaitableTimerでタイマーをセット、待機
↓
タイマーAPCと呼ばれるコールバックで実行
↓
CancelWaitableTimerで繰り返しているなら停止
↓
CloseHandleでタイマーオブジェクトの削除
の流れとなる。
例えば
var
WTimer1 :THandle;
WTimer1t:TLargeInteger;
Wtcnt1:LongInt;
onwmtimeout:Pointer;
とすると
アプリケーションのイニシャライズで
WTimer1:=CreateWaitableTimer(nil,false,nil);
終了処理で
CancelWaitableTimer(WTimer1);
CloseHandle(WTimer1);
とすればいいのがわかる。
呼び出しは一回だけの場合は
WTimer1t:=-50000000;
SetWaitableTimer(WTimer1, WTimer1t,0, onwmtimeout, Pointer(l_param), TRUE);
というところだろうか。
WTimer1tはタイマーの実行される時間なのだが
負の場合は呼出し後の時間(100ns単位)で
正の場合は1601/01/01 00:00:00 からののべ時間
これは分かりにくいかもしれないけれど
FileTimeToSystemTimeやその逆のSystemTimeToFileTimeを用いることで現時間などから取得は可能だ。
タイマーAPCは
Procedure WMTimeCallBack1 (
param:LongWord;TimerLow,TimerHigh:LongWord)stdcall;
の形式で
onwmtimeout:=@WMTimeCallBack1
等として入れ替えればよい。
この関数はFileTime値も得られるのだが
これを他のタイマー関数の感覚で呼び出してもコールバックは呼ばれない。
待っていないといけないのだ。
またWindows98で動くもののFileTime値が得られない。
まとめると
- 呼び出し元はSleepEXやWaitForSingleObjectEx等で待機しなくてはいけない。
- Windows98SEでも動作する。
- Windows98SEのコールバックではFileTimeの値が0になる。
ということのようだ。
基本的にこのタイマーはマルチスレッドを前提としているのだが
一番痛いのはSetWaitableTimerを呼び出した後別スレッドでコールバックが実行されるのはいいとして
呼び出しスレッドが待機状態で無いといけないという点にある。
命令が待ち必須ならばスレッド生成とセットの関数にすればよいのだがその辺がだめなのだ。
それはまた一般的なクライアントの場合
UIスレッドから直接呼び出すとフリーズしてしまう事を意味する。
定期的に動くものならまだしも柔軟なタイミングで動作するものの場合は
かなりコーディングが煩雑になるわけで
できるだけスレッドを減らしたいならばこのましくない。
とするとむしろコンソールアプリケーションでタイミングを取りたい場合に使うならば
コーディングのロスが少ないのではないかと思われる。
現時点では本格的にマルチスレッドには取り組んでいないので動作時は
SleepEx(10000,true);
をSetWaitableTimerの直後においた。
このままだと呼び出した後Windowがフリーズしてしまう。
そこでtimeSetEventのコールバックでSetWaitableTimerを呼び出した。
そうすれば割と簡単に別スレッドで待機状態になりタイマーが使える。
SetWaitableTimerの最終パラメータをTRUEにするとサスペンドやハイパネーションから復帰するそうだが
うまくいかなかった。
これは現在テストしているPCのせいかもしれない。
SetWaitableTimerの最高精度の100nsはまぁ出ないだろうが
1ms以下で刻めるので電子工作みたいなジャンルの模型制御には使えるとは思うが
汎用アプリケーションではtimeSetEventを使えば充分だと思われる。
timeSetEventのコールバックで命令を出す発想自体が非マルチスレッドプログラミングの発想であり
矛盾がある。
Web上でも実用的な話ではなくこの程度の「動作実験サンプル」程度のものしかないのも
使いにくさゆえだろう。
このテストのためにWin98機を組み立てたようなものなのだが得られたものは少ない。
それでもMSDNの内容が明示的に間違っているという事を示す例としてはこの記事にはそれなりの意義はあるかもしれない。
但し休止、ないしはサスペンドからの復帰はタイマーアプリケーション用などに確保したいインフラではあるので
いずれ解決したい。
時間が来たらコールバック関数を呼ぶという使い方をしたければ、スレッドを1つ立てて、そこでWaitForSingleObjectを呼び、コールバック関数を呼ぶという処理を自分で書くといいです。ただし、Windows 98非対応でよければRegisterWaitForSingleObjectが同じことを実装しています。