TListを継承したクラスを作り、その中に登録したフォームすべてをサブクラス化し、
すべてに共通の処理をつけたいと思っています。
でも、従来のWindowProcプロパティを差し替えてサブクラス化する方法では、
どのフォームのサブクラス化かわからないので、前のWindowProcを呼び出せません。
SetWindowLongを使えばウィンドウハンドルがわかるので、識別はできますけど、
他のコンポーネントからサブクラス化することが、できなくなってしまいます。
仕方がない気がするんですけど、自分の知識だけで判断して終わりにしてしまうのが、危険な気がしたので、
迷惑になるかもしれませんけど、質問することにしました。
SetWindowLongを使うしか、方法はないんでしょうか?
ぜひ、お願いいたします。
サブクラス化するウインドウを登録するときに、そのウインドウの
ハンドルは取得できますよね?
登録時にハンドル(もしくはTFormそのもの)と元のWindowProcのアドレスを
セットでリストにしておき、差し替えたWindowProcが呼ばれたときに引数と
して与えられるウインドウハンドルから元のWindowProcのアドレスを検索す
ればよいのでは?
>他のコンポーネントからサブクラス化
この部分が良くわからなかったのでこれしか書けませんでした。
他のコンポーネントからサブクラス化すると、SetWindowLongで設定した値が変更される可能性がある、ということだと思います。
また、RecreateWindowされると、SetWindowLongの値も元に戻ったと思います。
Kenjiさん、にしのさん、おへんじありがとうございます。
でも、ウィンドウハンドルが引数として得られるのは、SetWindowLongでサブクラス化した時のみみたいですけど…。
(TWndMehod型で得られるものには、Msgとw,lParamしかないようですし)
やっぱり、Delphiの方法では、ハンドルが得られないので、無理でしょうか?
> >他のコンポーネントからサブクラス化
> この部分が良くわからなかったのでこれしか書けませんでした。
WindowProcプロパティを置き換えて、サブクラス化する方法(以下Delphiの方法)では、
ひとつのコンポーネントでサブクラス化した後でも、
ほかのコンポーネントが同じ方法を使ってサブクラス化できますよね?
でもSetWindowLongを使った方法(以下API)では、そういうことができなかったと記憶しています。
(VBをやってたときに身につけた知識ですけど…、Delphiでは違うかも?)
実は、このTListを使ったサブクラス化は、汎用的なクラスにしておきたいんです。
なので、このクラスを使ったら、
これに登録したフォームをさらにサブクラス化(言葉の使い方、おかしいかもしれませんけど^^;)することができなくなるでは、
ちょっと、困るんです。
(そういえば、APIを使ったコンポーネントが一つだけの場合、ほかのコンポーネントがDelphiの方法を使ったサブクラス化ができなくなるのかどうかは試してません)
Delphiの場合でしたね。
ヘルプを見てみると確かにウインドウハンドルが得られない…
MSDNでのWindowProcはハンドルがあったので勘違いしました。
しかしWndProcってTControlのメンバなんですね。てっきり
TWinControlかと思ってました。
サブクラス化したウインドウプロシージャから、メッセージと
ウインドウハンドルやSelfポインタなどをパラメータにして
TListの方の関数を呼び出し、処理してもらうのはどうでしょうか?
サブクラス化とはちょっと違うかもしれませんが。
イベント発生
↓
フォームのウインドウプロシージャが呼び出される
↓
Selfとウインドウメッセージをパラメータとして渡す
↓
TListで一部のメッセージを処理
> しかしWndProcってTControlのメンバなんですね。てっきり
> TWinControlかと思ってました。
よそで読んだんですけど、
TWinControlのWndProcは、
もちろん普通にウィンドウプロシージャですけど、
TGraphicControlのWndProcは、
親コントロールのWndProcからメッセージを取り、
それを処理しているものなんだそうです。
あんまり詳しく読んでいませんけど。
> ウインドウハンドルやSelfポインタなどをパラメータにして
> TListの方の関数を呼び出し、処理してもらうのはどうでしょうか?
なるほど、Selfパラメータですか…。
今ちょっと時間がないので試せませんけど、それは有効な方法のような気がします。
うまくいったらここで報告します。
> >?TListの方の関数を呼び出し、処理してもらうのはどうでしょうか?
> なるほど、Selfパラメータですか…。
これですけど、SelfパラメータはTListクラスになってしまいます。
Delphiの方法のサブクラス化では、メッセージとwParamとlParamと戻り値しか使えません。
なので、ウィンドウハンドルや、Selfパラメータを引数として、他の関数を呼び出すことはできないんですけど…。
Kenjiさんはどういうイメージで考えているんでしょうか?
もしかしたら、わたしのやり方が違うだけで、
Kenjiさんのイメージどおりのやり方なら、うまくいくのかもしれません。
type
PWindowData = ^TWindowData;
TWindowData = record
Form : TCustomForm;
PrevWndProc : TWndMethod;
end;
………………
function TWindowList.Add(Form:TCustomForm): Integer;
var
p: PWindowData;
begin
New(p);
p^.Form := Form;
if Running then begin
// サブクラス化
p^.PrevWndProc := Form.WindowProc;
Form.WindowProc := ChildWndProc;
end;
Result := inherited Add(p);
end;
………………
procedure TWindowList.ChildWndProc(var Message:TMessage);
begin
(*いろいろな処理…*)
//ここで、TWindowDataに格納した、PrevWndProcを呼び出したい
end;
今のところ、こういう風に書いています。
スレッド別なら、SetMessageExtraInfo, GetMessageExtraInfoが使えそうです。
http://www.microsoft.com/JAPAN/developer/library/jpuipf/_win32_setmessageextrainfo.htm
http://www.microsoft.com/JAPAN/developer/library/jpuipf/_win32_getmessageextrainfo.htm
現実的に実装するなら、まず作成したWindowProcを持つクラスを作り、サブクラス化したいフォームごとにそのオブジェクトを作成、割り当てます。
そうすれば、各WindowProcのアドレスが一意になるので、TListなどで、フォームと対に対処できるかと思います。
>procedure TWindowList.ChildWndProc
TListを継承したTWindowListにウインドウ関数があるのですか。
そうなるとSelfパラメータはTWindowListのインスタンスそのものに
なってしまいますね。
フォームの方にウインドウ関数を用意して、それからTWindowListの
関数をSelfとMessageのパラメータで呼び出す、という感じで書いた
のですが、汎用的ではないかもしれません。
いまさらですがAPIでも問題ないように思えます。サブクラス化すると
ウインドウ関数が鎖のように連なって呼び出されるので、複数からサブ
クラス化も実現可能です。ただサブクラス化の解除の順番が、最後から
順に解除しないとメッセージが途切れてしまいます。(多分固まる)
どうも思った以上に難しい話になってしまって…。
難しい難しいと思っている間に、どんどんお返事が遅れてしまいました。ごめんなさい。
思った以上に簡単ではなさそうですね、
にしのさんの言った方法も何とか理解できたので、
何とかできそうですけど…。
ほんとうにいまさらで申し訳ないですけど、
ちょっと面倒でも、サブクラス化したいすべてのフォームに、子フォーム用コンポーネントをおいて、
クラス外の変数でお互いに情報をやり取りする…。
という方法でやってみることにしました。
ご迷惑かけてしまいごめんなさい、
でも、おかげでたぶん、つぎこういうことに出会っても、うまくできそうな気がします。
ありがとうございました。
ツイート | ![]() |