3

One day ago I had started to rewrite one of my old components and I decided to improve its readability. My component is a typical TWinControl that has overridden WndProc to handle a lot of messages of my own. There are so many code for each message and it became a problem for me to read code.
So, looking for a solution to improve code inside WndProc, I have organized these large pieces of code in procedures that called each time when appropriate message has delivered in WndProc. That's how it looks now:

procedure TMyControl.WndProc(var Message: TMessage);
begin
  case Message.Msg of        
    WM_WINDOWPOSCHANGED: 
      WMWINDOWPOSCHANGED(Message);
    WM_DESTROY: 
      WMDESTROY(Message);
    WM_STYLECHANGED: 
      WMSTYLECHANGED(Message);
    //  lots of the same procedures for Windows messages
    //  ...
    MM_FOLDER_CHANGED: 
      MMFOLDERCHANGED(Message);
    MM_DIRECTORY_CHANGED: 
      MMDIRECTORYCHANGED(Message);
    //  lots of the same procedures for my own messages
    //  ...
  else
    Inherited WndProc(Message);
  end;
end;

Unfortunately Inherited word in these procedures doesn't work anymore!

Important note: in some of WM_XXX messages I didn't call Inherited to perform my own handling of such message, so code shown below will break down my efforts to implement some features.

procedure TMyControl.WndProc(var Message: TMessage);
begin
  Inherited WndProc(Message);
  case Message.Msg of        
    WM_WINDOWPOSCHANGED: 
      WMWINDOWPOSCHANGED(Message);
    //  further messages
    //  ...
  end;
end;

I also want to avoid inserting Inherited after each message-ID as shown below, because it looks awful and I think there is exists more elegant way to override WndProc.

procedure TMyControl.WndProc(var Message: TMessage);
begin      
  case Message.Msg of        
    WM_WINDOWPOSCHANGED: 
    begin
      Inherited WndProc(Message);
      WMWINDOWPOSCHANGED(Message);
    end;
    //  further messages
    //  ...
  end;
end;

So my question is:
how to properly override WndProc to have an ability to use code grouped in procedures and to be able to call for original window procedure only for some messages?

Josef Švejk
  • 1,047
  • 2
  • 12
  • 23
  • Either use nested functions, or the `message` directive. – David Heffernan Apr 26 '18 at 11:09
  • `Unfortunately Inherited word in these procedures doesn't work anymore` Have you remembered the `override` WndProc? – Vasek Apr 26 '18 at 11:13
  • That's not it @Vasek. Asker wants to call inherited WndProc. Which is fine from the overridden WndProc, but not from some other method. – David Heffernan Apr 26 '18 at 11:15
  • Calling `DefaultHandler` should suffice, I'd say. – Victoria Apr 26 '18 at 11:24
  • http://docwiki.embarcadero.com/CodeExamples/Tokyo/en/Messages_(Delphi) – David Heffernan Apr 26 '18 at 11:46
  • @Victoria, thank you for comment. I didn't know about `DefaultHandler` before. Documentation on Embarcadero site says "DefaultHandler is called by System.TObject.Dispatch when it cannot find a method for a particular message". Probably, better way is to handle message is to call Dispatch. I will try it. – Josef Švejk Apr 26 '18 at 12:10
  • Maybe just rely on the message result (set it to 1, handled) and call the inherited WndProc in any case? – nil Apr 26 '18 at 12:14
  • @Vasek, yes, `WndProc` has been overridden. But this is not an answer for my question. – Josef Švejk Apr 26 '18 at 12:14
  • @David, thank you for link - I will research it immediatly. – Josef Švejk Apr 26 '18 at 12:14
  • @nil, thank you for comment, but you have described situation which I would like to avoid. I want to call window procedure from procedure that handles message, but I cannot call `inherited WndProc` in such manner. – Josef Švejk Apr 26 '18 at 12:19
  • Well that was not what I was proposing. I meant in your handler procedures to just alter the `Result` of the `var Message` parameter you provide. Then, in your `WndProc` implementation at the end call `inherited WndProc(Message)`. This however relies on descendants to ignore handled messages, is not guaranteed. – nil Apr 26 '18 at 12:45
  • @Dima, `Dispatch` does the only thing. It searches for the message handler methods and call them if found. If not, it calls `DefaultHandler`. Depends on what you want. – Victoria Apr 26 '18 at 16:20
  • @nil, it was my mistake, I've never use such a trick before, so there was a problem for me to understand what you have advised. But thank you for this info! – Josef Švejk Apr 27 '18 at 13:01
  • 1
    @Victoria, answer is already here but your recommendation to use `DefaultHandler` gave me additional ability to get more info about message handling in Delphi. Thank you! – Josef Švejk Apr 27 '18 at 13:04

2 Answers2

5

As RM's answer stated, your message handling methods can call inherited WndProc(Message) instead of just inherited, and that will work fine.

However, by introducing methods with the same names as the messages they are processing, you are exposing knowledge of the specific messages you are processing. So you may find it easier to just use Message Methods instead of overriding WndProc, eg:

type
  TMyControl = class(...)
  private
    procedure WMWindowPosChanged(var Message: TMessage); message WM_WINDOWPOSCHANGED;
    procedure WMDestroy(var Message: TMessage); message WM_DESTROY;
    procedure WMStyleChanged(var Message: TMessage); message WM_STYLECHANGED;
    // and so on ...
  end;

Your message methods can then call inherited (or not) as needed, eg:

procedure TMyControl.WMWindowPosChanged(var Message: TMessage);
begin
  inherited;
  //...
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Well it is real trick (for me) to handle messages in this way. Yesterday [David](https://stackoverflow.com/users/505088/david-heffernan) gave me a link where the same technique is used. Unfortunately he didn't provide its comment as answer, so I will accept your answer. Thank you! – Josef Švejk Apr 27 '18 at 12:56
4

Calling inherited WndProc from WMWINDOWPOSCHANGED will call the inherited one. So you can do it like this:

procedure WMWINDOWPOSCHANGED(var Message: TMessage)
begin
  // call inherited WndProc if you need to
  inherited WndProc(Message);
  .. do you own processing
end;
RM.
  • 1,984
  • 19
  • 29
  • Yes, your solution did the job. But sorry, RM, I accepted [Remy](https://stackoverflow.com/users/65863/remy-lebeau) answer because of its elegance in realisation. Thank you for you useful thoughts! – Josef Švejk Apr 27 '18 at 12:56