Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make the second instance of my program pass control back to the first instance?

Tags:

delphi

I have create an application with Delphi XE3. My application have a trayicon (I use TCoolTrayIcon for this) so when the user minimize it there is not a icon on taskbar but only on trayicon.

To avoid more that one istance of my application I use this code:

procedure CreateMutexes(const MutexName: String);
const
  SECURITY_DESCRIPTOR_REVISION = 1;
var
  SecurityDesc: TSecurityDescriptor;
  SecurityAttr: TSecurityAttributes;
  MutexHandle: THandle;
begin
  InitializeSecurityDescriptor(@SecurityDesc, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(@SecurityDesc, True, nil, False);
  SecurityAttr.nLength := SizeOf(SecurityAttr);
  SecurityAttr.lpSecurityDescriptor := @SecurityDesc;
  SecurityAttr.bInheritHandle := False;
  MutexHandle := CreateMutex(@SecurityAttr, False, PChar(MutexName));

  if MutexHandle <> 0 then
    begin
      if GetLastError = ERROR_ALREADY_EXISTS then
        begin
          MessageBox(0, 'You cannot start more than one instance of ContLab.'
                      + #13#10 + 'Use the instance has already started.',
                       'ContLab', mb_IconHand);

          CloseHandle(MutexHandle);
          Halt;
        end
    end;

  CreateMutex(@SecurityAttr, False, PChar('Global\' + MutexName));
end;

In this way when the user start application 2 times he get an error message and the second instance is terminate.

Now I'd like not show the error message but open the main form of first instance of application and terminate the second instance.

Is it possible?

like image 801
Martin Avatar asked Mar 25 '14 14:03

Martin


1 Answers

You need to send a message to the other application to request that it shows itself.

First of all you need to find the other application's main window. There are many ways to do that. For instance you can use FindWindow. Or you can enumerate the top-level windows with EnumWindows. Typically you'd then check for matching window text and/or class name.

Once you've found the main window of the other instance, you need to give it the ability to set itself to be the foreground window. You need to call AllowSetForegroundWindow.

var
  pid: DWORD;
....
GetWindowThreadProcessId(hwndOtherInstance, pid);
AllowSetForegroundWindow(pid);

Then send the window a user-defined message. For instance:

const
  WM_RESTOREWINDOW = WM_APP;
....
SendMessage(hwndOtherInstance, WM_RESTOREWINDOW, 0, 0);

Finally, your other instance's main form needs to listen for this message.

type
  TMainForm = class(TForm)
  ....
  protected
    procedure WMRestoreWindow(var Message: TMessage); message WM_RESTOREWINDOW;
  ....
  end;

When it encounters the message it must do this:

procedure TMainForm.WMRestoreWindow(var Message: TMessage);
begin
  inherited;
  Visible := True;
  Application.Restore;
  Application.BringToFront;
end;

I'm a little sceptical of your mutex handling code. I don't understand the need for security attributes since you are creating it in the local namespace. But then I see a second call to CreateMutex that ignores the return value, but creates an object in the global namespace.

like image 156
David Heffernan Avatar answered Nov 04 '22 05:11

David Heffernan