Search code examples
delphiwinapiloggingdelphi-7wndproc

How to check when the control is fully initialized?


How can I check if the control is fully initialized ?
Consider the following code (I know it's very bad practice to do this, please take it as an example)

type
  TForm1 = class(TForm)
    Memo1: TMemo;
  private
    procedure WndProc(var Message: TMessage); override;
  public
    { Public declarations }
  end;

procedure TForm1.WndProc(var Message: TMessage);
begin
{  
   I'd like to log the messages to the memo as soon 
   as it's possible so I need to find out how to
   check if the memo box is ready to use; the following
   code stuck the application, so that the form is not
   even displayed. How would you fix this code except
   "avoid using of component access in window proc" ?
}

  if Assigned(Memo1) then
    if Memo1.HandleAllocated then
      Memo1.Lines.Add('Message: ' + IntToStr(Message.Msg));

  inherited WndProc(Message);
end;

P.S. I know OutputDebugString :-)
Thanks!


Solution

  • Your question rather confused me. When you said:

    log messages to the memo

    What you mean is that you want to log messages to the form by writing text to the memo.

    That approach is fraught with danger since when you write to the memo, the form gets sent messages which results in you writing to the memo and a stack overflow is the inevitable consequence.

    I managed to make your idea sort of work by putting in re-entrancy protection. I also introduced a transient non-visual string list to capture any messages that are delivered before the control is ready to display them. Once you introduce this then you no longer need to worry about finding the precise earliest moment at which it is safe to add to the memo.

    unit LoggingHack;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
    
    type
      TLoggingForm = class(TForm)
        Memo1: TMemo;
      private
        FLog: TStringList;
        FLogging: Boolean;
      protected
        procedure WndProc(var Message: TMessage); override;
      public
        destructor Destroy; override;
      end;
    
    var
      LoggingForm: TLoggingForm;
    
    implementation
    
    {$R *.dfm}
    
    { TLoggingForm }
    
    destructor TLoggingForm.Destroy;
    begin
      FreeAndNil(FLog);
      inherited;
    end;
    
    procedure TLoggingForm.WndProc(var Message: TMessage);
    var
      Msg: string;
    begin
      if not FLogging then begin
        FLogging := True;
        Try
          Msg := IntToStr(Message.Msg);
          if Assigned(Memo1) and Memo1.HandleAllocated then begin
            if Assigned(FLog) then begin
              Memo1.Lines.Assign(FLog);
              FreeAndNil(FLog);
            end;
            Memo1.Lines.Add(Msg);
          end else if not (csDestroying in ComponentState) then begin
            if not Assigned(FLog) then begin
              FLog := TStringList.Create;
            end;
            FLog.Add(Msg);
          end;
        Finally
          FLogging := False;
        End;
      end;
      inherited;
    end;
    
    end.
    
    end;
    

    The moral of the story is that you should use a more appropriate logging framework that does not interact with what you are trying to log.