2002/09/21

手抜き、よくない

TaskTrapの配布コーナーの方で述べてあるWindows Messengerの手抜きっぷりについてでも触れておきます。

要は、TaskTrapの最前面機能とWindows Messengerの最前面機能が干渉するわけですが、詳しく説明すると、こういう感じで干渉します。

  1. メッセンジャーウィンドウをメッセンジャーで最前面表示にする
  2. TaskTrapでそれを解除する
  3. すると、メッセンジャーウィンドウから最前面表示のところを見ると、解除したのにもかかわらずチェックが入っている
  4. 一応、メッセンジャーから最前面表示のチェックをはずすと、ちゃんとはずれ、すでに最前面ではないが、メッセンジャーの内部的にも最前面ではない状態になる

最後がよく分かんない表現ですが、砕いて言えば、最前面表示かどうかのチェックをメッセンジャー内部ではウィンドウの状態を調べるのではなく内部変数で管理しているので、外部から最前面表示の状態を変更すると、それが狂う、ということです。まっとうなアプリなら、内部変数をチェックするとかいう手抜き実装はせずに、if(GetWindowLong(hwnd, GWL_EXSTYLE)&WS_EX_TOPMOST)というやり方で判定するので外部から最前面表示状態を変更してもきちんとウィンドウの状態に即した反応をするはずです。

まぁ、確かに手抜き実装でやった方が楽ですけど、GetWindowLongしたほうが確実な動作が期待できるし、バグの発生も少なくなるような気がするんですがねぇ…。SetWindowLongは同一プロセスからじゃないと有効じゃない(→外部にDLLをおいてウィンドウをフックしなきゃならない)から手抜きしてもいいだろう、とでも思っているんだろうか。

なお、最前面表示機能をアプリとTaskTrapの間で「併用」するとやばいかも、というだけで、片方だけ使うことに関しては全く問題ありません。そして、常にウィンドウの状態に即した表示、反応を見せるのは、TaskTrapであることを付け加えておきます。

Service Pack 1 と tasktrap v0.1

皆さんご存じのように、Windows XP SP1とInternet Explorer 6 SP1がリリースになりました。ということで、AirH"でフルパッケージを気合い入れて落とそうとしたんですが、3時間で断念して、次の日に大学で落としてきました。WinXP SP1にIE6 SP1も入っているので、その辺は便利。気が向いたらWindows XPな友人達に入れさせようかと思っている今日この頃です。

132MBぐらいなので、大学でなら2時間もあれば落ちるだろう、と思ってたんですが、甘かった。うちの大学の図書館は情報コンセント(LANポートのジャックがあるとこ)のある席が固まって配置されているわけですが、私がWindows XP SP1を落としはじめると、速度が20KByte/sしか出ません。まぁ、大学の共用回線だから仕方ないとはいえ、これだったらADSLの友人に頼んだ方が早い気もするけども隣町なので却下。

というわけで、見事に待ちぼうけを食うハメになり、待ち時間にTaskTrapをカリカリやってたら、気がつけばリリース版が出来あがりそうになるぐらい待ってたんですが、すわりっぱなしも疲れるのでふらふらと図書館内を徘徊して戻ってくると、隣の席のヤツが2ch見てるんです。図書館に自分のノートマシン持ち込んで2chか、おめでてーな。とおもったら、しまいにゃストリーミングで動画とかみてるんです。XP SP1やるからその回線空けろ、とか思いつつひたすら落ちていくSP1の転送速度を眺めながら(最終的には14KByte/sまで落ちた)さらにTaskTrapいじってたら、ウィンドウをたくさん持つアプリの挙動がやっと見えてきてTaskTrap完成、と。

とりあえず、ウィンドウハンドルを渡すとタスクバーに載っているか否かを判別する関数。

bool IsTaskbarWindow(HWND hwnd){
    // 本質的にここは無くてもよいが、ウィンドウタイトルがなければ false にしておくか。
    TCHAR str[3];
    if(!GetWindowText(hwnd, str, 2)){ return false; } 

    if(GetWindowLong(hwnd, GWL_EXSTYLE)&WS_EX_APPWINDOW){ return true; }
    if(GetWindowLong(hwnd, GWL_EXSTYLE)&WS_EX_TOOLWINDOW){ return false; }
    if(GetWindow(hwnd, GW_OWNER)){ return false; }
    return true;
}

GetNextWindow(GetDesktopWindow())で得たウィンドウハンドルを始点にして、do - while((hwnd=GetNextWindow(hwnd))!=NULL)してループ内で判定するわけですが、デスクトップ直下から始めてGetNextWindowで順次得られるウィンドウハンドルはてっきり全部アプリのトップウィンドウだと思って悩んでたんですが、Spy++をずーっと眺めてたら他のウィンドウをオーナーとして持つウィンドウが多々あったのでそれをはじくとすべて問題解消の模様。

あと、隠すときに最小化しておくと、メモリ使用量が減ったり付随のツールウィンドウも一気に隠せたりととってもお得なんですが、復活させるときに隠す前のウィンドウの状態(ShowWindowで何が指定されていたか)を得ることが出来なくて立ち往生してたんですが、SDKヘルプを眺めてたらGetWindowPlacementで得られることに気がついたのでこれでこの件も一件落着、と。よって、現状ではShowWindowでSW_SHOWMINIMIZEDしてからSW_HIDEするようになりました。

というわけで、TaskTrapをよろしくお願いします。tray.icoを差し替えて自分の好きなアイコンをタスクトレイに表示しておくだけでもタスクトレイにたくさんアイコンを増やしたい人にはたまらない機能のような気がしますが、残念ながらTaskTrapは二重起動できませんのでご了承下さい。あ、ミューテックスを使っているので別ユーザは起動できますよ。

……はっはっは、readme.txtを書いてないことに気がついたぞー。

うされもん @acidlemonについて

|'-')/ acidlemonです。鎌倉市在住で、鎌倉で働く普通のITエンジニアです。

30年弱住んだ北海道を離れ、鎌倉でまったりぽわぽわしています。

外部サイト情報

  • twitter
  • github
  • facebook
  • instagram
  • work on kayac