Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How can i modify TWincontrol for a single app ?

53 views
Skip to first unread message

Andreas Koch

unread,
Jul 12, 1999, 3:00:00 AM7/12/99
to
Is it possible to modify TWincontrol (and all its children)
in a single program , for example to make all controlls blink
when the mouse is down , or draw a flower-pattern as background,etc...?

Andreas

Nick Ryan

unread,
Jul 12, 1999, 3:00:00 AM7/12/99
to
Andreas Koch <ak...@rbg.informatik.tu-darmstadt.de> wrote in message
news:37899E...@rbg.informatik.tu-darmstadt.de...

> Is it possible to modify TWincontrol (and all its children)
> in a single program , for example to make all controlls blink
> when the mouse is down , or draw a flower-pattern as background,etc...?

You could try creating a custom package which redefined / extended the
TWinControl class - I believe (although I haven't tested this) that this
would override the existing definition of TWinControl at run-time. Of course
whether you could get such global changes as blinking controls and
background drawing to work is another matter altogether...

Nick

Thaddy de Koning

unread,
Jul 12, 1999, 3:00:00 AM7/12/99
to
Copy the source from Twincontrol(dunno the unit by heart) and that of the
descendants (ALL of them!) into your own sourcecode directory. Make the
changes, do a build all(and I mean all:this only works if you recompile the
descendants you use as well)
The trick is that your own project directory has precedence over the
vcl\source directory.
Again make shure you copy the units with descendanyts you use as well,
because otherwise you would get an error message like:
'TwhoknowesWhatYouUsed was compiled with a different version of
Twincontrol'

Andreas Koch wrote:

> Is it possible to modify TWincontrol (and all its children)
> in a single program , for example to make all controlls blink
> when the mouse is down , or draw a flower-pattern as background,etc...?
>

> Andreas

tdk.vcf

Peter Below (TeamB)

unread,
Jul 12, 1999, 3:00:00 AM7/12/99
to
> Is it possible to modify TWincontrol (and all its children)
> in a single program , for example to make all controlls blink
> when the mouse is down , or draw a flower-pattern as background,etc...?
>
Andreas,

it would be if all the controls deriving from TWincontrol would delegate
their painting to TWinControl but that is not the case. So if you manage
to hook into the WM_ERASEBKGND handler of TWincontrol you will gain
little since most controls either handle the message themselves and do
not call the handler they inherited from TWinControl or do even their
background painting in response to WM_PAINT.

Nevertheless, i could not resist in testing whether it is in fact
practical to replace a message handler for a whole class on the fly and
it does actually work. The following code finds the address of
TWincontrols handler for the WM_ERASEBKGND message in TWinControls
dynamic message table and replaces it with a method of the form.

Children, don't do this at home unless you understand what is going on
here. It offers unlimited opportunities to crash your app if you get it
wrong!

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
Grids, StdCtrls, ExtCtrls, Buttons, ComCtrls;

type
TForm1 = class(TForm)
RichEdit: TRichEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
pOldHandler : TMethod;
Procedure EraseHandler( var msg: TWMEraseBkgnd );
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
type
TIndices= Array [1..1024] of SmallInt;
TProcs = Array [1..1024] of Pointer;
var
pDMT : PWord;
i, count: Word;
pIndices : ^TIndices;
pProcs : ^TProcs;
oldProtect: DWORD;
begin
richedit.clear;
// Get pointer to TWinControls VMT
pDMT := Pointer(TWinControl);
// Find pointer to DMT in VMT, first Word is the count of dynamic
// methods
pDMT := Pointer(PDword( Integer(pDMT) + vmtDynamicTable )^);
richedit.lines.add( format('DMT: %p'#13#10'Method count: %d', [pDMT,
pDMT^]));
count := pDMT^;
// Build pointes to array of method indices and addresses
pIndices := Pointer( Integer( pDMT ) + 2 );
pProcs := Pointer( Integer( pDMT ) + 2 + count * sizeof( smallint ));
// find handler for WM_ERASEBKGND
for i:= 1 to count do begin
richedit.lines.add( format( 'i: %d, method index %d', [i,
pIndices^[i] ]));
if pIndices^[i] = WM_ERASEBKGND then begin
richedit.lines.add( Format('WM_ERASEBKGND found, handler at address
%p', [pProcs^[i]] ));
// Store old handler and replace it with out method.
// Since the DMT is in a code segment we have to play with
// the page attributes to get write access.
pOldHandler.Code := pProcs^[i];
VirtualProtect( @pProcs^[i], Sizeof( pointer ),
PAGE_EXECUTE_READWRITE, @oldProtect );
pProcs^[i] := @TForm1.EraseHandler;
VirtualProtect( @pProcs^[i], Sizeof( pointer ), oldProtect,
@oldProtect );
break;
end;
end;
// create a plain TWincontrol to show the effect of the handler
with twincontrol.create(self) do begin
setbounds( richedit.left + richedit.width+ 10,
richedit.top, richedit.width, richedit.height );
parent := self;
color := clBlack; // will be ignored
end;
// make sure this method is only called once!
button1.onclick := nil;

// redraw the form
Invalidate;
end;

procedure TForm1.EraseHandler(var msg: TWMEraseBkgnd);
var
canvas: TCanvas;
begin
canvas:= TCanvas.Create;
try
canvas.Handle := msg.dc;
canvas.brush.color := clYellow;
canvas.brush.style := bsSolid;
canvas.FillRect( clientrect );
msg.result := 1;
finally
canvas.free
end;
// WARNING!! Self in this method will NOT be the form reference but
// the reference to the TWinControl that received the message! So
// if one wanted to chain to the replaced handler one would have
// to do it with
// form1.pOldHandler.Data := self;
// TWndMethod(form1.pOldhandler)( TMessage( msg ));
// Any other reference to form fields or methods would have to be made
// the same way, failing to do so will compile but crash at run-time!
end;

end.

So, since this does not do the trick, what is left if you want to change
the behaviour of the complete application without building a new set of
VCL controls to use? Probably only a thread-specific WH_CALLWNDPROC hook,
which would not be easy to do either (hooks are an advanced Windows
programming topic and also excellent at crashing not only the app but
also Windows when you do them wrong) and have considerable overhead,
since they trap *all* messages going through the window functions of
*all* TWincontrols in your applications.

Peter Below (TeamB) 10011...@compuserve.com)
No e-mail responses, please, unless explicitly requested!


Bill Zissimopoulos

unread,
Jul 13, 1999, 3:00:00 AM7/13/99
to
Andreas Koch <ak...@rbg.informatik.tu-darmstadt.de> wrote:
> Is it possible to modify TWincontrol (and all its children)
> in a single program , for example to make all controlls blink
> when the mouse is down , or draw a flower-pattern as
> background,etc...?
>

VCL controls by default do not include the WS_EX_NOPARENTNOTIFY extended
style. Therefore you will receive WM_PARENTNOTIFY messages for a
window's children. You can use this message in your parent control (e.g.
TForm) to subclass your children (using GetWindowLong/SetWindowLong with
GWL_WNDPROC).

The world will end if you don't use this technique carefully!

Bill


0 new messages