2008年08月07日

GetMessageのエラー処理は必要か?

GetMessageはBOOL型であるにもかかわらず「エラー」という値ももっているとされる。
だったら最初からLongIntなりHresult型にしとけよというつっこみはさておき
GetMessageがエラーを出すのはlpMsg(TMsg)構造体が無効の時とwindowハンドルhwndが無効の時ということはわかっているようだが
どういうときにエラーが出るのか、どうしたらいいのかちゃんと考えているサイトは無い。

とりあえずTMsgが有効でhwndにnull(0)を入れた場合はエラーは出ない。
またWM_DESTROYでハンドルの変数に0を入れてやればエラーは出ないので
エラー処理は不要であるという結論に至った。
返り値の型についてはdelphiの場合素直に宣言文を書き直してしまうのも手ではある。

間違ったコードの典型は以下のようなもの

ret:=LongInt(getmessage(App_Message,0,0,0));
if (ret=-1) then ErrMessage('GetMessage errer');


確認してみたところWindowを一つも作らない状態でgetmessageをかけても
エラーにはならなかった。
つまりエラーをあてにするとgetmessageで無限ブロックになるわけだ。

GetmessageにしろPeekmessageにしろ複数Window対象のときは
ループから出る条件として
一般的なサンプルのメッセージポンプではだめなわけだが
WM_DESTROYでカウンタかなんかで細工すればよいだろう。


又もう一つ気になったこととしてハンドルは基本的にWindowを破棄したとき
どういう値になっているのか気になった。


destroyWindow(App_Window);

とdestroyWindowを明示的にかけ
アプリケーション終了直前にApp_Windowの値を見たところ「0」になっていなかった。
そこでWM_DESTROYでApp_Windowに0を代入してやることにした。

その2点を考慮し

var
App_Windows:LongWord;

としてやると


1://WM_CREATE:
begin
App_Windows:=App_Windows+1;
end;

2://WM_DESTROY:
begin//2
App_Window:=0;
App_Windows:=App_Windows-1;
if (App_Windows=0) then App_Loop:=false;
end;


と明示的にhwnd変数を0にしてやることで無効hwndハンドルが発生しないようにしつつ。
App_WindowsをカウントしてWindowが0になったらループフラグをオフにするようになった。

とはいえ現状複数Windowを生成するような体裁ではないので無駄ではある。

ちなみに対応するPeekmessageでのループのサンプルとしては

Procedure App_loop1;
begin
while App_Loop do begin//2
if PeekMessage(App_Message,0,0,0,1)=True then //PM_REMOVE
begin//3
DispatchMessage(App_Message);
end else begin
TProc(Idle_proc);//waitmessge等
SleepEx(fps_time,true);
end;//2
SleepEx(0,true);
end;//1
end;//0


等としている。
SleepExを入れているのはWaitableタイマーやQueueUserAPCにメッセージスレッドが対応するため
SleepEx(0,true)は無くてもいいかもしれない。
実際はDispatchMessageではなくて外で条件分岐させている。
Idle_procにWaitmessgeを割り当てればgetmessageと互換になる。

このコードには「App_Windows」の影響は出ない。WindowProcだけで完結しているからだ。
つまりGetmessageに入れ替えたとしても無効hwndが行くことは無い。

したがって

while App_Loop do begin//
getmessage(msg,App_Window,0,0);
TranslateMessage (App_Message) ;
DispatchMessage (App_Message) ;
end;

としても
postquitmessage関数を使って


while App_Loop do begin//
while (getmessage(App_Message,App_Window,0,0));
do begin//
TranslateMessage (App_Message) ;
DispatchMessage (App_Message) ;
end;
end;

としても

エラー処理は不要であるといえるがそもそもApp_loopで終了判定する前者のコードであれば
より問題は発生しにくい。

現状のようにWindowハンドルが明示的に得られる場合は
別スレッドからでもPostmessageでコマンドを送ることができるのは
以前のタイマー実験で立証済みなので待機型メッセージループの場合
別スレッドから定期的にWM_USERあたりにメッセージをポストしつつフラグを立てる形にすれば
定間隔動作などもメインスレッドで無難にこなせるだろうが
それは単一スレッド型ガベージコレクションを組み込んだ場合のVC++とかVB.NETの問題であって
自前でメモリ管理する場合はあまり効率の良い方法ではないので自分は使わない。


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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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