2
votes

I've copied code from this article: Controlling the number of application instances

However, the message being sent by SendMessage is not being 'caught' by the main form.

This is the code in the DPR file, where we are registering the message, and then broadcasting it if an instance of the application is already running:

var
  Mutex: THandle;

begin
  MyMsg := RegisterWindowMessage('Show_Main_Form');
  Mutex := CreateMutex(nil, True, 'B8C24BD7-4CFB-457E-841E-1978A8ED0B16');

  if (Mutex = 0) or (GetLastError = ERROR_ALREADY_EXISTS) then
  begin
    SendMessage(HWND_BROADCAST, MyMsg, 0, 0);
  end

This is code from the main form:

var
  fmMain: TfmMain;
  MyMsg: Cardinal;

implementation

uses
  uSettings;

{$R *.dfm}

procedure TfmMain.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
  if (Msg.Message = MyMsg) then
  begin
    beep;

    Application.Restore;
    Application.MainForm.Visible := True;
    SetForeGroundWindow(Application.MainForm.Handle);
    Handled := True;
  end;
end;

procedure TfmMain.FormCreate(Sender: TObject);
begin
  Application.OnMessage := AppMessage;
end;

The problem is that the procedure AppMessage does not get called. What is wrong?

1

1 Answers

3
votes

OnMessage is used to intercept queued messages. However, this message is sent rather than queued. You need to override the form's window procedure in order to receive it:

Add this to the protected part of your form's type declaration:

procedure WndProc(var Message: TMessage); override;

Implement it like this:

procedure TfmMain.WndProc(var Message: TMessage);
begin
  inherited;

  if Message.Msg = MyMsg then
  begin
    Beep;

    Application.Restore;
    Application.MainForm.Visible := True;
    SetForeGroundWindow(Application.MainForm.Handle);
  end;
end;

Since this form is presumably the single instance of the application's main form you might replace the body of the message handler with this:

Application.Restore;
Visible := True;
SetForeGroundWindow(Handle);

I would also comment that broadcasting such a message seems a little risky to me. You'll be sending that message to every top-level window in the system. I think that has definite potential to cause problems if you encounter a program that reacts to that message when it should not.

Were it me I would identify the window which you intend to target, and send the message directly to that window. And I would use SendMessageTimeout to be robust to the scenario where the target app is not responding. In that scenario, SendMessage will never return and the sending application will also become hung.