0

I need to handle WM_SetFocus or WM_KillFocus on delphi application , i wrote a message handler like this :

Procedure Focus(var Msg: TWMSetFocus); message WM_SetFocus;

but it doesn't work & don,t fire the message handler when WM_SetFocus arrived , after that i wrote a Application Message Handler but against it doesn,t work !

I think this messages send to control directly , is it true ?

Any one can help me to do this ?

Mojtaba Tajik
  • 1,725
  • 16
  • 34

3 Answers3

3

This answer assumes you want the messages to be received by controls on a form.

These messages are non-queued and are sent directly to the control. That explains why your two attempts to receive them have failed.

The only way to receive them is through the window procedure of the control. You have the following options.

  1. Subclass the control and handle the message. This is perhaps most easily done with and interposer class.
  2. Use the WindowProc property of the control to replace the window procedure without deriving a new class.

You might find that TForm.SetFocusedControl could help. It is called in response to a control receiving WM_SetFocus messages, as well as being called in some other situations (see the VCL code for details).

Option 1: Interposer

unit uWindowProc;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TEdit = class(StdCtrls.TEdit)
  protected
    procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  end;

  TMyForm = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
  end;

var
  MyForm: TMyForm;

implementation

{$R *.dfm}

{ TEdit }

procedure TEdit.WMSetFocus(var Message: TWMSetFocus);
begin
  inherited;
  Beep;
end;

end.

Option 2: WindowProc

unit uWindowProc;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TMyForm = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    procedure FormCreate(Sender: TObject);
  private
    FOriginalWindowProc: TWndMethod;
    procedure NewWindowProc(var Message: TMessage);
  end;

var
  MyForm: TMyForm;

implementation

{$R *.dfm}

procedure TMyForm.FormCreate(Sender: TObject);
begin
  FOriginalWindowProc := Edit1.WindowProc;
  Edit1.WindowProc := NewWindowProc;
end;

procedure TMyForm.NewWindowProc(var Message: TMessage);
begin
  if Message.Msg=WM_SETFOCUS then
    Beep;
  FOriginalWindowProc(Message);
end;

end.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • But he dóes "subclass" the control (whatever type of control that might be): he adds an own message handler to the class. Message handlers are automatically invoked for every TObject descendant, but from TControl up, messages reach the handler only if WndProc lets them through. So if his handler never fires, I suppose his or anothers WndProc blocks the message. That's why I thought of "control" being the MainForm. – NGLN May 28 '11 at 07:15
  • 1
    @NGLN I'd bet that message handler in OP's question is associated with the form rather than the control. – David Heffernan May 28 '11 at 07:16
  • My point exactly: he tries this on his Form. I even think that's by design. – NGLN May 28 '11 at 07:24
  • @NGLN The form won't be receiving input focus. Input focus will rest with one of the controls on the form. Both of my code samples above Beep when the edit controls receive input focus . – David Heffernan May 28 '11 at 07:26
  • Of course not, but I didn't point out in my answer enough whether it's _good_ design. ;) – NGLN May 28 '11 at 07:39
0

@Mojtaba - whether or not a message is sent directly to the control depends on what type of control it is.

As Andrei K suggested, a message is sent directly only to controls descended from TWinControl - there are other controls that are descended from TControl but they are not TWinControls. For example, TLabel is not descended from TWinControl, but TPanel is.

For a control that's not a TWinControl, the message is sent to the Delphi application's default message handler (generally the application's 'main form' message handler) which handles the message internally, depending on the message content.

See:

TWinControl: http://docwiki.embarcadero.com/VCL/en/Controls.TWinControl

TLabel: http://docwiki.embarcadero.com/VCL/en/StdCtrls.TLabel

TPanel: http://docwiki.embarcadero.com/VCL/XE/en/ExtCtrls.TPanel

Vector
  • 10,879
  • 12
  • 61
  • 101
  • Correction: A message is sent directly to a control if it is non-queued (i.e. was sent with `SendMessage`). A message is delivered via the message queue (and this visible to Application.OnMessage) if it is a queued message (i.e. was posted with `PostMessage`). – David Heffernan May 28 '11 at 06:27
  • correction 2: non windowed controls don't receive input focus and so are never even associated with these messages – David Heffernan May 28 '11 at 06:39
  • @David - 1) "A message is sent directly to a control..." - I assume you are talking about TWinControls. 2) "non windowed controls don't receive input focus": I was speaking in general but I defer to your superior knowledge, that in the case of non-windowed controls focus messages aren't relevant (which makes perfect sense). I gather the OP was obviously talking about a windowed control since the question was about WM_SETFOCUS, WM_KILLFOCUS, and setFocus, but maybe that's not where their code was executing. – Vector May 28 '11 at 07:17
  • Actually I was talking about windows in Win32, i.e. things that have HWNDs. So analagous to TWinControl. It's only windows (i.e. HWNDs) that receive windows messages in any case. The distinction between queued and non-queued messages is very important but is sadly not universally understood as well as it needs to be. – David Heffernan May 28 '11 at 07:21
  • @David - "windows in Win32, i.e. things that have HWNDs": Understood. "the distinction between queued and non-queued messages": I remember reading about this many moons ago, but Delphi thankfully shields us from all of that when you work in a pure Delphi defined framework (like I have done during most of my decidely corporate/business programming oriented career) Tnx – Vector May 28 '11 at 08:03
0

Or you can use this wich works on freepascal too

var
  Form1: TForm1;
  OldProc : Pointer;
  counter : Integer = 0;
implementation

{$R *.dfm}

function WndProc1(hw:HWND;Msg:Cardinal;wparam:WPARAM;lparam:LPARAM):LResult;stdcall;
begin
  if Msg = WM_SETFOCUS then
  begin
    Inc(counter);
    Form1.Caption:=IntToStr(counter);
  end;
  Result := CallWindowProc(oldProc,hw,Msg,wparam,lparam);
end;

procedure TForm1.FormShow(Sender: TObject);
var
newproc:Pointer;
begin
  DWORD(OldProc) := GetWindowLong(Edit1.Handle,GWL_WNDPROC);
  newproc := @WndProc1;
  SetWindowLong(Edit1.Handle,GWL_WNDPROC,Integer(newproc));
end;
opc0de
  • 11,557
  • 14
  • 94
  • 187