[DELPHI] Component & Windows Messages

Pagina: 1
Acties:
  • 233 views sinds 30-01-2008
  • Reageer

  • Kool
  • Registratie: September 1999
  • Niet online
Hoe kan ik een Windows message bestemd voor een formulier, afvangen in een component wat op dit formulier staat?
Als ik een message-procedure implementeer in het formulier zelf, werkt het. Maar als ik deze implementeer in een component en ik sleep deze op een formulier, ontvangt het component het bericht niet.

  • Belgar
  • Registratie: Januari 2002
  • Laatst online: 17-08 22:31

Belgar

Archmaster ranzige code..

Zover ik weet worden messages alleen afgeleverd op je FORM. Vervolgens moet je form er zelf voor zorgen dat de actieve component deze informatie krijgt.

...Als het maar werkt


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 23:42

Tomatoman

Fulltime prutser

Blijkbaar doe je dan iets niet helemaal goed. Componenten kunnen wel degelijk messages verwerken. Een mogelijke oorzaak is dat je (zelfgeschreven) component geen window handle heeft en dus ook geen message handler. Al weet ik niet of dat bij jouw het probleem is, ik zal je een voorbeeld geven wat je daarmee moet doen. In het voorbeeld moet TFontList onder andere reageren op een WM_FONTCHANGE message van Windows en heeft daarom een window handle nodig.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
type
  TFontList = class(TComponent)
  private
    procedure WndProc(var Message: TMessage);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    ...
  end;

...

constructor TFontList.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FHandle := AllocateHWnd(WndProc); { create invisible window
                        in order to receive messages }
end;

destructor TFontList.Destroy;
begin
  DeallocateHWnd(FHandle);
  inherited;
end;

procedure TFontList.WndProc(var Message: TMessage);
begin
  with Message do
    case Msg of
    WM_FONTCHANGE: DoFontTableChange; // voorbeeldje
    else
    Result := DefWindowProc(FHandle, Msg, wParam, lParam);
    end;
end;

Een goede grap mag vrienden kosten.


Verwijderd

Het lijkt misschien een beetje tricky, maar veel componenten die ik heb gezien doen het zo:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type TMyComp = class(TComponent)
  private
    Wnd: HWnd;
    procedure HandleMessage(var Msg: TMessage);
  public
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
end;

procedure TMyComp.HandleMessage(var Msg: TMessage);
begin
  case Msg.Msg of
     WM_blabla // handle je message
end; 

constructor TMyComp.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Wnd := AllocateHWnd(HandleMessage); // functie uit unit Forms
end;

destructor TMyComp.Destroy;
begin
  DeallocateHWnd(Wnd);
  inherited Destroy;
end;

edit:
Okee ik was niet eerst

In ieder geval hebben alleen componenten die zijn afgeleid van TWinControl een eigen Window Handle. Als je een onzichtbaar component wilt maken of een ander component niet afgeleid van TWinControl, dan zul je zelf een handle moeten maken met AllocateHWnd.

  • Kool
  • Registratie: September 1999
  • Niet online
In principe wil ik eigenlijk alleen de messages voor het Owner Form van het component vangen. Ik vrees namelijk dat je bij bovengenoemde stukken code alle messages van alle forms v.d. applicatie ontvangt??? Misschien kan iemand hier nog wat meer duidelijkheid in scheppen.

In ieder geval bedankt voor de hulp *D

Verwijderd

Op maandag 25 maart 2002 15:06 schreef Kool het volgende:
In principe wil ik eigenlijk alleen de messages voor het Owner Form van het component vangen. Ik vrees namelijk dat je bij bovengenoemde stukken code alle messages van alle forms v.d. applicatie ontvangt???
Hmm, ik denk eigenlijk dat die nieuw gecreeerde HWnd alleen messages ontvangt die expliciet naar die HWnd gestuurd zijn. Dus in het geheel geen messages gestuurd naar het form of andere forms van die applicatie.

Ik heb nog wat code gevonden, die doet meer een 'hook' in de Message handler van het form:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
constructor TMyComp.Create;
begin
  //...
  if not (csDesigning in ComponentState) then
  begin
    // Hook the application
    OldFormProc := Pointer(GetWindowLong(TForm(Owner).Handle, GWL_WNDPROC));
    NewFormProc := MakeObjectInstance(HookFormProc);
    SetWindowLong(TForm(Owner).Handle, GWL_WNDPROC, LongInt(NewFormProc));
  end;
end;

procedure TMyComp.HookFormProc(var Message: TMessage);
begin
  with Message do
  begin
    case Msg of
    WM_SIZE:
      if wParam = SIZE_MINIMIZED then
      begin
        // form is geminimaliseerd
      end;
    end;

    // niet vergeten
    Result := CallWindowProc(OldFormProc, TForm(Owner).Handle, Msg, wParam, lParam);

  end;
end;

Dit werkt natuurlijk alleen als de owner van het component een form is...

O ja vergeet het unhooken niet:
code:
1
2
3
4
5
6
7
8
9
10
11
12
destructor TMyComp.Destroy;
begin
  if not (csDesigning in ComponentState) then
  begin
    if Assigned(OldFormProc) then
    SetWindowLong(TForm(Owner).Handle, GWL_WNDPROC, LongInt(OldFormProc));
    if Assigned(NewFormProc) then
    FreeObjectInstance(NewFormProc);
    NewFormProc := nil;
    OldFormProc := nil;
  end;
  //...

  • Kool
  • Registratie: September 1999
  • Niet online
Ik krijg het niet aan de praat. Je voorbeelden kloppen ook niet 100%, en ik krijg ze zelf helaas ook niet werkend. Zo geeft de procedure TMyComp.HookFormProc een Result ??? En hoe zijn OldFormProc en NewFormProc gedeclareerd, als Pointer? Misschien vind je nog wat code?... :?

Ik zit al te kijken in de VCL welk component messages van het formulier zou onderscheppen maar ik heb er nog niet een gevonden...

Bedankt tot dusver :)

Verwijderd

Gebruik dit:
code:
1
2
3
4
5
6
7
8
9
  TMyComp = class(TComponent)
  private
    FOldFormProc: Pointer;
    FNewFormProc: Pointer;
    procedure HookFormProc(var Message: TMessage);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;
Zo geeft de procedure TMyComp.HookFormProc een Result ???
Result moet je lezen als Message.Result..

Vergeet niet inherited aan te roepen bij Create & Destroy;

[edit]
BTW je zult op een WM_CLOSE moeten reageren.

  • Kool
  • Registratie: September 1999
  • Niet online
Mensen bedankt, het werkt nu... :z
Pagina: 1