2008年07月25日

SetTimer関数のあいたたな仕様と対応

今回も前記事同様消化の際のプロセスで得たことだが
結果的に有効なものではなくメモ書き程度だと思う。

メインスレッド集中型アプリケーション用として
タイムアウト処理にはSetTimer関数も使えるかもしれないと思ったのだが
WM_MENUSELECT等を切っているのに
タイマー動作中にメニュー操作をするなどしていると不正終了することがあった。
これは本来TimerProcにするはずのlparamを値として活用したことが原因だったらしい。
どうもメニュー操作とかダイアログが出ている間はPeekmessageを経ずに処理されるようだ。

Peekmessageのループからメニューなどの処理でOS側に処理が渡っていている最中に
メッセージが来た場合
こちらのループを介さず直接WindowProcにメッセージが送られることがしばしば観察されたが
WM_TIMERも同様でdispatchmessagesを介していなくても
OSの処理中にメッセージが来た場合はlparamに値が入っているとTimerProcとみなして
処理する。

従って安定した処理をするためにはlparamに0を入れておくか
一律でTimerProcで処理させるかということになる。
アプリケーションのwindowハンドルを使わず0を指定した場合IDの管理をしなくていいから
ひとつパラメータを利用できれば汎用性を持たせられると踏んでいたのだが
どうもだめのようだ。
他のメッセージと異なりWindowProcにもポストされないというのがミソ

とすると一つの関数だけが対象となるわけだ。
一方アプリケーションのwindowハンドルを使った場合IDをキーにできるわけで
そのIDに基づいたオブジェクトをアクセスして関数なりパラメータを取り出せるわけだが
settimer命令を発行するときにIDの空きを探したりする作業が必要となる。
これをやるなら「次回の発行時間」を時間の配列をソートしてやったほうがまし

使い方としては普通の人がやっているように特定のOnTimer関数を実行するか
任意のTimerProcをパラメータなしでやるかに限るといえそうだ。
後者の方がまだマシかもしれない。

従ってどうしても使う向きには以下のようなコードになる。繰り返しは切捨て
ID指定する場合はコールバック無しにしてID配列をアクセスしてどうにかするようにする。
型指定してTimerProcを呼び出すのも面倒なのでlparamに値があるときは「DispatchMessage」を呼ぶことにした。


if PeekMessage(App_Message,0,0,0,1)=True then begin//3 PM_REMOVE
case App_Message.message of//4


での分岐

$113://WM_TIMER
begin// 5
if App_Message.lparam<>0 then DispatchMessage(App_Message)
else begin //6
killTimer(App_Message.hwnd,App_Message.wparam);

if (App_Message.hwnd = 0) then begin //7
OnTimeout2;
end else begin//7
OnTimeout(App_Message.wparam);
end;//6
end;//5
end;//4


TimerProcはID指定する場合とWindowハンドル0でID無しの場合と両方で使える。
処理やタイマーの後片付けもコールバック側が行う。

コールバックをしない場合はここでタイマーを削除してTimeOut専用にしている。

OnTimeout2はTproc型の変数で一つの関数を指定できる。IDを指定しないので呼び出しは簡単だが
一つの使い方しか出来ないしパラメータは順序型のグローバル配列を使うしかない。
OnTimeoutはIDを利用できるのでプロシージャ自体をそちらに関連付けた配列に入れることができる。

TimerProcの一例


Procedure TimerProc1(p_wnd:HWND;p_msg,p_ID:LongInt;time:DWord);stdcall;
begin
KillTimer(p_wnd,p_ID);

end;

Windowが0のときとApp_Windowのときと分けることができる。
App_Windowの時のID値は任意なのでデータを関連付けできる。
まぁIDと関連付けなくてもJavaScriptのSetTimeOutやActionScriptのsetIntervalのような
直接割り当てのものなら出来そうだ。

呼び出し

SetTimer(0,0,timet,@TimerProc1);


WaitableTimerはひとつのタイマーで複数のタイムアウトの管理とか出来ないので
settimer命令を遅延実行にどうかということを考えていたのだが
自前でのリスト処理をしないでだらだらとパラメータ付のタイムアウト処理をメインスレッドでしたいならば
TimesetEventとQueueUserAPCの組み合わせに限ると思った。

タイマーイベントは一つの時間に一回起きるわけだから
タイマー命令発生時にソートして一番近い時間にトリガーをかけるのが良いとすると
精度的にもSetWaitableTimerかTimeSetEventの方が好ましいように思えた。

今回の話があんまり見られないのは技術バランス的に
SetTimerの処理でコールバックを使う人が皆無に近いせいだと思うが
この使えそうで使いにくいというものほど苛立ちを感じるものは無いと感じた。


ラベル:SetTimer
posted by Xo_ox at 22:35| Comment(0) | Win出直し | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。