「何か。」のゴースト製作記(14)(2002/03/31)
新着情報 トップメニュー ボードメニュー 掲示板 お手紙はここ!

ホントのところ、この記事を書いてるエディタだって微妙なバージョンアップを続けてるんですよ(笑) 結構安定してると思ったり……まあ、マクロのリファレンス書かんと公開できないんで。弱。
そのマクロも Operation雛≠フエンジンと同じルーチンを使おうか、とか考え中。とはいえ、無効なタグを握り潰しやすい……と言う点では、悪くないんですけどね。


ダウンロードする(MPS0204.exe/1,372KB)



右クリックのポップアップメニューを横取りしようと画策中。現時点では一応、クリックイベントの横取りに成功しています。あとはウィンドウを作るだけ……(^-^;)

MATERIA-B だと、どうもバルーン形式になるみたいなんで。ポップアップで良いじゃんって思うのは俺だけでしょうか。まぁ確かに、ポップアップメニューのカスタマイズって難しい、とは思いますよ。はい。
そもそも、ポップアップメニューってどうやって作成するんだろ(汗)

右クリックの横取りは、SetWindowsLong API でウィンドウプロシージャを置き換える事でできます。置き換えに成功すると元のプロシージャのアドレスを返すので、元々の処理をさせたい場合にこれを使い、横取りしたいメッセージだけ処理させる事ができます。
理屈は最初から明確だったんですが、問題はさくら側のウィンドウンドルの取得でした。当然、その様なドキュメントは在りませんので自分で探す必要が有るわけで……手がかりは DirectSSTP サーバーのハンドル。

結論を先に言えば、DirectSSTP のサーバーのウィンドウハンドル、ちなみにこれはメモリマップトファイルから取得するんですが、このハンドルはさくら側ウインドウハンドルと同一です。

当初、このハンドルは本体(TApplicationクラス)のウィンドウハンドルだと勝手に決めてかかってまして、むろんクリックするのはそこではありませんからメインフォームのハンドルを取ってこようとしたんです。これがドツボの始まりだった……



単純にすれば


hSakura := FindWindow( 'Tmainform', NIL );

で取って来れるんですが、これでは複数の『何か。』が起動している環境ではどのゴーストのウィンドウなのかは判りません。ハンドルを取得しておいて「ウィンドウのタイトル」を要求すれば「それが誰なのか」は判明しますが、違うからって次のウィンドウを……とはなかなか行かない。
FindWindow API の第二引数にウィンドウタイトルを与えれば一発で取得できますが、実際に確認してみると例えばビリオド 480 でのタイトルは、『MATERIA period 480 - ひいな』となっているんです。
よって、少なくとも本体のバージョンを取得しなければならない訳で。

さて、ゴーストの名前や DirectSSTP サーバーのハンドルは、エクスポート関数 load() において既に明らかにする事ができますので、すかさず EXECUTE 1.0 getversion をリクエスト。バージョン番号を取り出す事には成功しました。この番号から前述のウィンドウタイトルを構成し、FindWindow API によるハンドルの取得に成功……

ただし、MATERIA とか period とかいう文字列は今後のバージョンアップで変化する事は想像に難くありません。なので設定ファイルで SakuraWindowCaption=MATERIA period %version - %name と言った文字列を定義し、今後のアップグレードに再コンパイルなしに追従するようにしました。しかしこれ、自動で追従できるパーサーは組めんかな?

ところがどっこい満足したのも一瞬の事で、Delphiのデバッガではうまく行くのに、普通に起動すると本体が Halt してしまいました。……なぜ?

何しろデバッガでは正しく動作するので、不具合位置を特定するのも簡単には行きません。MessageBox API を駆使して何処で落ちるか探したところ……少なくとも DirectSSTP ルーチンからリクエストを SendMessage API で送るまでは進むみたいです。受け側に問題があるのか?
細かい方法は忘れましたが、受け側をデバッガで起動しておいたりして色々と確認を取ってみたところ、どうもリクエストが再入すると駄目 な模様。それが何故デバッガ上ではうまく行くのか説明が出来ないんですが(^-^;)
まぁ自分で自分にリクエストを出すと言う暴挙に出ているのですから、これをバグだと言うわけにも行きますまい。
そう言うわけであれこれ試した挙句に、この方法は放棄しなければならなくなりました。



順当に考えると、ゴーストパスから再帰的に親フォルダを探せば必ずあるはず。比較的容易に MATERIA.EXE を見つけられるのでリソースからバージョンを取得する手も考えたんですが……。実行ファイル名はそりゃ定義ファイルに項目を追加しても良いわけですけど、なんというか、保証がなさすぎてイヤーンな感じ。

とりあえずその方法は保留にして、なんとかさくらのウインドウハンドルに噛り付こうと思い、DirectSSTP のハンドルを親として


hSakura := FindWindowEx( hDirectSSTP, 0, 'Tmainform', NIL );

を実行……しかし失敗。何故何故何故何故ーーっ!
(注:この時点では hDirectSSTP は hSakura の親だと決め付けてた)
で、ダメモトでもって


hSakura := FindWindowEx( 0, hDirectSSTP, 'Tmainform', NIL );

なんてやって見るもののやっぱり玉砕。次に、GetWindow API で GW_CHILD とか GW_NEXT とかを使ってハンドルを取得し、GetWindowClass API でクラス名 'Tmainform' を検索する、と言うのをやってみましたが…… GetWindow 自体が成功しないので話になりません(T-T)
(注:この時点でも hDirectSSTP は hSakura の親だと決め付けてた)

EnumWindows API を使い、該当するウインドウタイトルを持つハンドルを待つ……と言う手も考え付きましたが、その為にはバージョンを取得しなければならないので、結局は FindWindow で十分と言う事になりますから却下。



うー。

むー。

どうなっとるんだッ!?

そもそも FindWindowEx API が成功しないってのがおかしい。本当にコイツはウィンドウなのか!?


Len := GetWindowTextLength( hDirectSSTP )+1;
Buf := GetMemory( Len );
GetWindowText( hDirectSSTP, Buf, Len );

と、まぁこんなコードを書いて実験。結果は……。

MATERIA period 500 - ひいな

お、お、お前かあっ!!



そゆわけで、晴れて SetWindowLong API でのプロシージャ置き換えによって、右クリックをフックする事に成功しました。

取り合えず右メニューを無効にしてみたんですが……ダブルクリックでもメニューがでる設計には驚きましたね。どうも、作者の人は先を読んでいたみたい。凄いや。

つづく。


新着情報 トップメニュー ボードメニュー 掲示板 お手紙はここ!