Search code examples
delphidelphi-7

Is possible to use messages in a class procedure?


I want use messagesin my program and i've a question: Can I use messages in a class procedure or Can I use messages in a procedure without class?

Here is my code:

const

  WM_CUSTOM_TCP_CLIENT = WM_USER + 10;

type
    TFeedbackEvent = class
      public
        class procedure feedback(var msg: TMessage); message WM_CUSTOM_TCP_CLIENT;
      end;

The Delphi returns the following message:

[Error] unit.pas(33): Invalid message parameter list

Thank you very much.


Solution

  • There is a very nice article on the topic: Handling Messages in Delphi 6. This is a must read.

    Handling or processing a message means that your application responds in some manner to a Windows message. In a standard Windows application, message handling is performed in each window procedure.

    By internalizing the window procedure, however, Delphi makes it much easier to handle individual messages; instead of having one procedure that handles all messages, each message has its own procedure. Three requirements must be met for a procedure to be a message-handling procedure:

    • The procedure must be a method of an object.

    • The procedure must take one var parameter of a TMessage or other message-specific record type.

    • The procedure must use the message directive followed by the constant value of the message you want to process.

    As you can read in the article, the procedure must be a method of an object, not a class. So you cannot just use message handlers in a class procedure.


    A possible workaround to handle messages in a class instance (also in object instance or window-less applications), is to manually create window handle via AllocateHWND, and process messages yourself via a WndProc procedure.

    There is a good example on this in delphi.about.com: Sending messages to non-windowed applications (Page 2):

    The following sample is a version of the above example, modified to work with class method. (If using class method is not really required, use original example from the link above instead):

    First, you need to declare a window handle field and a WndProc procedure:

    TFeedbackEvent = class
    private
      FHandle: HWND;
    protected
      class procedure ClassWndProc(var msg: TMessage); 
    end;
    
    procedure WndProc(var msg: TMessage); 
    

    Then, process the messages manually:

    procedure WndProc(var msg: TMessage); 
    begin
        TFeedbackEvent.ClassWndProc(msg);
    end;
    
    procedure TFeedbackEvent.ClassWndProc(var msg: TMessage);
    begin
      if msg.Msg = WM_CUSTOM_TCP_CLIENT then
        // TODO: Handle your message
      else
        // Let default handler process other messages
        msg.Result := DefWindowProc(FHandle, msg.Msg, msg.wParam, msg.lParam);
    end;
    

    Finally, at the end of the file, declare initialization and finalization section to create/destroy the handle:

    initialization
        FHandle := AllocateHWND(WndProc);
    
    finalization
        DeallocateHWnd(FHandle);
    

    Of course, this is not the recommended way to do this (especially watch for problems with initialization/finalization), it was just an example to show that it is possible.

    Unless you have some very strange requirement to use class method, its better to use regular class method and object constructor and destructor instead initialization and finalization sections (as shown in Sending messages to non-windowed applications (Page 2)).