View Issue Details

IDProjectCategoryView StatusLast Update
0008051LazarusIDEpublic2017-03-12 17:31
ReporterFlorian Assigned ToJuha Manninen  
PrioritynormalSeveritytweakReproducibilityalways
Status closedResolutionfixed 
PlatformWin32OSWindows x64 
Product Version0.9.20 
Summary0008051: Double clicking a file in the file explorer opens another lazarus instance even if lazarus is already open
DescriptionI think this behaviour should be at least configurable.
TagsNo tags attached.
Fixed in Revisionr49907, r49908, r49909, r49917, r49918
LazTarget-
WidgetsetWin32/Win64
Attached Files

Relationships

related to 0007992 resolvedJuha Manninen Integrate the "UniqueInstance" feature into TApplication 
has duplicate 0021647 resolvedJuha Manninen Feature Request: Have One Instance Of Project Open At One Time 

Activities

Zeljan Rikalo

2012-02-04 12:12

developer   ~0056342

Related to unique instance.

Ondrej Pokorny

2015-09-12 23:27

developer   ~0085867

Last edited: 2015-09-13 07:09

View 2 revisions

See the attached patch "ide-one-instance-1.patch". I tested Windows 10, Linux Kubuntu and OSX 10. OSX 10 seems to handle the feature by itself so nothing really changed there (maybe we should hide the option on OSX? - I am not an OSX user so somebody should give more information).

Some information about how it works:

1.) If you open IDE with no file parameters, a new instance is always created.

2.) The new options handle only the case when there are files in parameters. The options should be self-explanatory. "start new Lazarus instance" is default and corresponds with current behavior.

3.) Because it is not exactly a "single instance" I could not reuse the UniqueInstance component.

4.) I also had to write a new IRC because simpleirc didn't do what I expected - it allows multiple servers with the same ID on Windows but not on Linux (throws exception). And it is not "thread-safe" on Linux (you cannot use multiple clients from different processes talking to one server). So I wrote a simple file blocking approach that should work everywhere.

5.) It can handle multiple files opening from explorer at once! It gathers them from all starting processes. This works without problems on Windows, however on Linux with some issues. It seems that the file locks are not working correctly when they are executed simultaneusly (more locks are possible to open to one blocking file). This issue has to be handled on FPC-level. If I won't forget, I'll open a bug report about it.

6.) I also updated startlazarus. This is due to these facts:
  a) Handle the files before the splash screen appears.
  b) Prevent the leak report dialog opening when you compiled lazarus with debug info. (If you start directly "lazarus.exe myfile.pas", the file is handed over to running instance and Lazarus closes itself, showing an empty leak report. That is annoying. Therefore associate the files always with startlazarus.

Btw. empty leak reports can be hidden with the new heaptrc.GlobalSkipIfNoLeaks variable (FPC 3.1.1 trunk). Maybe it should be applied in lazarus.lpr. See 0028578 .


It's again a lot of code. I hope I haven't forgotten/overseen anything in it and have described it fully.

+ Please wait and do not commit the patch now. I have an idea how to improve it.

Ondrej Pokorny

2015-09-12 23:27

developer  

ide-multiple-instances.png (38,303 bytes)   
ide-multiple-instances.png (38,303 bytes)   

Ondrej Pokorny

2015-09-12 23:27

developer  

ide-one-instance-1.patch (61,044 bytes)   
Index: ide/environmentopts.pp
===================================================================
--- ide/environmentopts.pp	(revision 49818)
+++ ide/environmentopts.pp	(working copy)
@@ -176,6 +176,16 @@
       'Never'
     );
 
+type
+  TIDEStartWithFilesOption = (isoStartNewInstance, isoOpenFilesInRunningInstance, isoPrompt, isoProjectPromptOtherInRunning);
+const
+  IDEStartWithFilesOptionNames: array[TIDEStartWithFilesOption] of string = (
+    'StartNewInstance',           // isoStartNewInstance
+    'OpenFilesInRunningInstance', // isoOpenFilesInRunningInstance
+    'Prompt',                     // isoPrompt
+    'ProjectPromptOtherInRunning' // isoProjectPromptOtherInRunning
+    );
+
   { Messages window }
 type
   TMsgWndFileNameStyle = (
@@ -475,6 +485,7 @@
     FRecentPackageFiles: TStringList;
     FMaxRecentPackageFiles: integer;
     FOpenLastProjectAtStart: boolean;
+    FOpenParamFilesInInstance: TIDEStartWithFilesOption;
     // Prevent repopulating Recent project files menu with example projects if it was already cleared up.
     FAlreadyPopulatedRecentFiles : Boolean;
 
@@ -728,6 +739,8 @@
     property LastOpenPackages: TLastOpenPackagesList read FLastOpenPackages;
     property OpenLastProjectAtStart: boolean read FOpenLastProjectAtStart
                                              write FOpenLastProjectAtStart;
+    property OpenParamFilesInInstance: TIDEStartWithFilesOption read FOpenParamFilesInInstance
+                                                                write FOpenParamFilesInInstance;
     property FileDialogFilter: string read FFileDialogFilter write FFileDialogFilter;
 
     // backup
@@ -802,6 +815,7 @@
 function CharCaseFileActionNameToType(const Action: string): TCharCaseFileAction;
 function UnitRenameReferencesActionNameToType(const Action: string): TUnitRenameReferencesAction;
 function StrToMsgWndFilenameStyle(const s: string): TMsgWndFileNameStyle;
+function StrToIDEStartWithFilesOption(const s: string): TIDEStartWithFilesOption;
 
 function SimpleDirectoryCheck(const OldDir, NewDir,
   NotFoundErrMsg: string; out StopChecking: boolean): boolean;
@@ -871,6 +885,13 @@
   Result:=mwfsShort;
 end;
 
+function StrToIDEStartWithFilesOption(const s: string): TIDEStartWithFilesOption;
+begin
+  for Result in TIDEStartWithFilesOption do
+    if CompareText(s,IDEStartWithFilesOptionNames[Result])=0 then exit;
+  Result:=isoStartNewInstance;
+end;
+
 function SimpleDirectoryCheck(const OldDir, NewDir,
   NotFoundErrMsg: string; out StopChecking: boolean): boolean;
 var
@@ -1308,6 +1329,7 @@
   FRecentPackageFiles:=TStringList.Create;
   FMaxRecentPackageFiles:=DefaultMaxRecentPackageFiles;
   FOpenLastProjectAtStart:=true;
+  FOpenParamFilesInInstance:=isoStartNewInstance;
 
   // backup
   with FBackupInfoProjectFiles do begin
@@ -1744,6 +1766,7 @@
       Path+'UnitRenameReferencesAction/Value',UnitRenameReferencesActionNames[urraAsk]));
     FAskForFilenameOnNewFile:=FXMLCfg.GetValue(Path+'AskForFilenameOnNewFile/Value',false);
     FLowercaseDefaultFilename:=FXMLCfg.GetValue(Path+'LowercaseDefaultFilename/Value',true);
+    FOpenParamFilesInInstance:=StrToIDEStartWithFilesOption(FXMLCfg.GetValue(Path+'OpenParamFilesInInstance/Value',''));
 
     // fpdoc
     FPDocPaths := FXMLCfg.GetValue(Path+'LazDoc/Paths','');
@@ -2051,6 +2074,11 @@
                              FAskForFilenameOnNewFile,false);
     FXMLCfg.SetDeleteValue(Path+'LowercaseDefaultFilename/Value',
                              FLowercaseDefaultFilename,true);
+    if FOpenParamFilesInInstance = isoStartNewInstance then
+      FXMLCfg.DeletePath(Path+'OpenParamFilesInInstance')
+    else
+      FXMLCfg.SetValue(Path+'OpenParamFilesInInstance/Value',IDEStartWithFilesOptionNames[FOpenParamFilesInInstance]);
+
     // fpdoc
     FXMLCfg.SetDeleteValue(Path+'LazDoc/Paths',FPDocPaths,'');
 
Index: ide/frames/files_options.lfm
===================================================================
--- ide/frames/files_options.lfm	(revision 49818)
+++ ide/frames/files_options.lfm	(working copy)
@@ -47,12 +47,12 @@
   end
   object ShowCompileDialogCheckBox: TCheckBox
     AnchorSideLeft.Control = Owner
-    AnchorSideTop.Control = OpenLastProjectAtStartCheckBox
+    AnchorSideTop.Control = OpenParamFilesInInstanceComboBox
     AnchorSideTop.Side = asrBottom
     AnchorSideRight.Side = asrBottom
     Left = 2
     Height = 19
-    Top = 69
+    Top = 94
     Width = 180
     BorderSpacing.Top = 2
     Caption = 'ShowCompileDialogCheckBox'
@@ -65,7 +65,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 117
+    Top = 142
     Width = 82
     BorderSpacing.Top = 10
     Caption = 'LazarusDirLabel'
@@ -80,7 +80,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 132
+    Top = 157
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -94,7 +94,7 @@
     AnchorSideRight.Control = LazarusDirButton
     Left = 2
     Height = 23
-    Top = 132
+    Top = 157
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -108,7 +108,7 @@
     AnchorSideRight.Control = CompilerPathButton
     Left = 2
     Height = 23
-    Top = 176
+    Top = 201
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -123,7 +123,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 176
+    Top = 201
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -136,7 +136,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 161
+    Top = 186
     Width = 101
     BorderSpacing.Top = 6
     Caption = 'CompilerPathLabel'
@@ -149,7 +149,7 @@
     AnchorSideRight.Control = FPCSourceDirButton
     Left = 2
     Height = 23
-    Top = 220
+    Top = 245
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -164,7 +164,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 220
+    Top = 245
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -177,7 +177,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 205
+    Top = 230
     Width = 100
     BorderSpacing.Top = 6
     Caption = 'FPCSourceDirLabel'
@@ -189,7 +189,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 249
+    Top = 274
     Width = 81
     BorderSpacing.Top = 6
     Caption = 'MakePathLabel'
@@ -201,7 +201,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 293
+    Top = 318
     Width = 91
     BorderSpacing.Top = 6
     Caption = 'TestBuildDirLabel'
@@ -214,7 +214,7 @@
     AnchorSideRight.Control = MakePathButton
     Left = 2
     Height = 23
-    Top = 264
+    Top = 289
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -229,7 +229,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 264
+    Top = 289
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -243,7 +243,7 @@
     AnchorSideRight.Control = TestBuildDirButton
     Left = 2
     Height = 23
-    Top = 308
+    Top = 333
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -258,7 +258,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 308
+    Top = 333
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -272,7 +272,7 @@
     AnchorSideRight.Side = asrBottom
     Left = 32
     Height = 19
-    Top = 88
+    Top = 113
     Width = 206
     BorderSpacing.Left = 30
     Caption = 'AutoCloseCompileDialogCheckBox'
@@ -284,7 +284,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 337
+    Top = 362
     Width = 153
     Alignment = taRightJustify
     BorderSpacing.Top = 6
@@ -301,7 +301,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 352
+    Top = 377
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -317,7 +317,7 @@
     AnchorSideRight.Control = CompilerTranslationFileButton
     Left = 2
     Height = 23
-    Top = 352
+    Top = 377
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -365,4 +365,27 @@
     BorderSpacing.Around = 2
     TabOrder = 16
   end
+  object OpenParamFilesInInstanceLabel: TLabel
+    AnchorSideLeft.Control = Owner
+    AnchorSideTop.Control = OpenParamFilesInInstanceComboBox
+    AnchorSideTop.Side = asrCenter
+    Left = 2
+    Height = 15
+    Top = 73
+    Width = 168
+    Caption = 'OpenParamFilesInInstanceLabel'
+    ParentColor = False
+  end
+  object OpenParamFilesInInstanceComboBox: TComboBox
+    AnchorSideLeft.Control = OpenParamFilesInInstanceLabel
+    AnchorSideLeft.Side = asrBottom
+    Left = 178
+    Height = 23
+    Top = 69
+    Width = 238
+    BorderSpacing.Left = 8
+    ItemHeight = 15
+    Style = csDropDownList
+    TabOrder = 17
+  end
 end
Index: ide/frames/files_options.pas
===================================================================
--- ide/frames/files_options.pas	(revision 49818)
+++ ide/frames/files_options.pas	(working copy)
@@ -40,6 +40,7 @@
 
   TFilesOptionsFrame = class(TAbstractIDEOptionsEditor)
     AutoCloseCompileDialogCheckBox: TCheckBox;
+    OpenParamFilesInInstanceComboBox: TComboBox;
     CompilerTranslationFileButton:TButton;
     CompilerTranslationFileComboBox:TComboBox;
     CompilerTranslationFileLabel:TLabel;
@@ -49,6 +50,7 @@
     FPCSourceDirButton:TButton;
     FPCSourceDirComboBox:TComboBox;
     FPCSourceDirLabel:TLabel;
+    OpenParamFilesInInstanceLabel: TLabel;
     lblCenter: TLabel;
     LazarusDirButton:TButton;
     LazarusDirComboBox:TComboBox;
@@ -229,6 +231,17 @@
     Add(ProgramDirectory(true));
     EndUpdate;
   end;
+  OpenParamFilesInInstanceLabel.Caption := dlgOpenParamFilesInInstance;
+  with OpenParamFilesInInstanceComboBox.Items do
+  begin
+    BeginUpdate;
+    Add(dlgOpenParamFilesInInstance_StartNewInstance);
+    Add(dlgOpenParamFilesInInstance_OpenFilesInRunningInstance);
+    Add(dlgOpenParamFilesInInstance_Prompt);
+    Add(dlgOpenParamFilesInInstance_ProjectPromptOtherInRunning);
+    EndUpdate;
+  end;
+  Assert(OpenParamFilesInInstanceComboBox.Items.Count = Ord(High(TIDEStartWithFilesOption))+1);
 
   CompilerPathLabel.Caption:=Format(dlgFpcExecutable,[GetDefaultCompilerFilename]);
   FPCSourceDirLabel.Caption:=dlgFpcSrcPath;
@@ -361,6 +374,8 @@
     // open last project at start
     OpenLastProjectAtStartCheckBox.Checked:=OpenLastProjectAtStart;
 
+    OpenParamFilesInInstanceComboBox.ItemIndex := Ord(OpenParamFilesInInstance);
+
     // compile dialog
     fOldShowCompileDialog:=ShowCompileDialog;
     ShowCompileDialogCheckBox.Checked:=ShowCompileDialog;
@@ -391,6 +406,7 @@
     MaxRecentOpenFiles := MaxRecentOpenFilesSpin.Value;
     MaxRecentProjectFiles := MaxRecentProjectFilesSpin.Value;
     OpenLastProjectAtStart:=OpenLastProjectAtStartCheckBox.Checked;
+    OpenParamFilesInInstance := TIDEStartWithFilesOption(OpenParamFilesInInstanceComboBox.ItemIndex);
     ShowCompileDialog := ShowCompileDialogCheckBox.Checked;
     AutoCloseCompileDialog := AutoCloseCompileDialogCheckBox.Checked;
   end;
Index: ide/idecmdline.pas
===================================================================
--- ide/idecmdline.pas	(revision 49818)
+++ ide/idecmdline.pas	(working copy)
@@ -50,6 +50,7 @@
   NoSplashScreenOptLong='--no-splash-screen';
   NoSplashScreenOptShort='--nsc';
   StartedByStartLazarusOpt='--started-by-startlazarus';
+  ForceNewInstanceOpt='--force-new-instance';
   SkipLastProjectOpt='--skip-last-project';
   DebugLogOpt='--debug-log=';
   DebugLogOptEnable='--debug-enable=';
Index: ide/ideinstances.pas
===================================================================
--- ide/ideinstances.pas	(nonexistent)
+++ ide/ideinstances.pas	(working copy)
@@ -0,0 +1,987 @@
+{
+ /***************************************************************************
+                              ideinstances.pas
+                              ----------------
+
+ ***************************************************************************/
+
+ ***************************************************************************
+ *                                                                         *
+ *   This source is free software; you can redistribute it and/or modify   *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This code is distributed in the hope that it will be useful, but      *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   General Public License for more details.                              *
+ *                                                                         *
+ *   A copy of the GNU General Public License is available on the World    *
+ *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
+ *   obtain it by writing to the Free Software Foundation,                 *
+ *   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
+ *                                                                         *
+ ***************************************************************************
+
+  Author: Ondrej Pokorny
+
+  Abstract:
+    This unit handles one/multiple Lazarus IDE instances.
+
+}
+unit IDEInstances;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  {$IFDEF UNIX}
+  baseunix,
+  {$endif}
+  sysutils, Interfaces, Classes, Controls, Forms, Dialogs, ExtCtrls,
+  LCLProc, LCLIntf, LCLType,
+  LazFileUtils, LazUTF8, Laz2_DOM, laz2_XMLRead, laz2_XMLWrite,
+  LazarusIDEStrConsts, IDECmdLine;
+
+type
+  TIDEStartWithFilesAction = (isaStartNewInstance, isaOpenFilesInRunningInstance, isaPrompt, isaModalError);
+  TChooseActionEvent = procedure(
+    const aFiles: TStrings;
+    var Result: TIDEStartWithFilesAction;
+    var outPromptMessage: string;
+    var outOpenNewInstanceMessage: string) of object;//we want to receive translated outPromptMessage and outOpenNewInstanceMessage -> do not read them directly from LazarusIDEStrConsts here
+  TOpenFilesResult = (ofrStartNewInstance, ofrDoNotStart, ofrModalError);
+  TOpenFilesEvent = procedure(const aFiles: TStrings;
+    var outResult: TOpenFilesResult; var outHandleBringToFront: HWND) of object;
+
+type
+  TMessageParam = record
+    Name: string;
+    Value: string;
+  end;
+  TMessageParams = array of TMessageParam;
+
+  TMessageHeader = packed record
+    MsgType: Integer;
+    MsgLen: Integer;
+  end;
+
+  TFileHandle = Classes.THandle;
+
+  TIPCBase = class
+  private
+    FFileName: string;
+    FServerName: string;
+
+    function GetUniqueFileName: string;
+    procedure SetServerName(const aServerName: string); virtual;
+  public
+    constructor Create; virtual;
+  public
+    property ServerName: string read FServerName write SetServerName;
+  end;
+
+  TIPCClient = class(TIPCBase)
+  public
+    procedure SendMessage(const aMsgType: Integer; const aStream: TStream);
+    function ServerRunning: Boolean;
+  end;
+
+  TIPCServer = class(TIPCBase)
+  private
+    FFileHandle: TFileHandle;
+    FLastMsgFileName: string;
+    FLastMsgFileHandle: TFileHandle;
+    FMaxClientFileIndex: Integer;
+    FMsgData: TStream;
+    FMsgType: Integer;
+    FActive: Boolean;
+
+    function CanReadFile(const aFileName: string; out outHandle: TFileHandle): Boolean;
+    function FindFirstFile(out outFileName: string; out outHandle: TFileHandle): Boolean;
+    function CloseFileHandle(var ioFileName: string; var ioHandle: TFileHandle): Boolean;
+    procedure CleanUpTempFiles;
+  public
+    constructor Create; override;
+    Destructor Destroy; override;
+  public
+    function PeekMessage: Boolean; overload;
+    function PeekMessage(aTimeOut: Integer): Boolean; overload;
+    function StartServer: Boolean;//true if unique and started
+    function StopServer: Boolean;//true if stopped
+    property Active: Boolean read FActive;//true if started
+    property MsgType: Integer read FMsgType;
+    property MsgData: TStream read FMsgData;
+  end;
+
+  TUniqueServer = class(TIPCServer)
+  public
+    procedure StartUnique(const aServerPrefix: string);
+  end;
+
+  TMainServer = class(TUniqueServer)
+  private
+    FOpenFilesEvent: TOpenFilesEvent;
+    FChooseActionEvent: TChooseActionEvent;
+    FTimer: TTimer;
+
+    procedure DoChooseActionEvent(const aInParams: TMessageParams);
+    procedure DoOpenFiles(const aInParams: TMessageParams);
+
+    procedure SimpleResponse(const aResponseToServer, aResponseType: string;
+      const aParams: array of TMessageParam);
+
+    procedure DoCheckMessages;
+    procedure CheckMessagesOnTimer(Sender: TObject);
+
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+  end;
+
+  TResponseClient = class(TIPCClient)
+  public
+    function SendFilesToRunningInstance(
+      const aFiles: TStrings; var outOpenNewInstanceMessage: string;
+      var outHandleBringToFront: HWND): TOpenFilesResult;
+  end;
+
+  TIDEInstances = class
+  private
+    FMainServer: TMainServer;//running IDE
+    FStartIDE: Boolean;// = True;
+    FForceNewInstance: Boolean;
+    FAllowOpenLastProject: Boolean;// = True;
+    FFilesToOpen: TStrings;
+
+    class procedure AddFilesFromParams(const aInParams: TMessageParams;
+      const aFiles: TStrings); static;
+    class procedure BuildMessage(const aMessageType: string;
+      const aParams: array of TMessageParam; const aStream: TStream); static;
+    class function MessageParam(const aName, aValue: string): TMessageParam; static;
+    class procedure ParseMessage(const aStream: TStream; out outMessageType: string;
+      out outParams: TMessageParams); static;
+    class function GetMessageParam(const aParams: array of TMessageParam;
+      const aParamName: string): string; static;
+
+    function CheckParamsForForceNewInstanceOpt: Boolean;
+    procedure CollectOtherOpeningFiles(out
+      outFilesWereSentToCollectingServer: Boolean);
+    procedure CollectOtherOpeningFiles_ReceiveFromClient(
+      const aServer: TIPCServer; const aFiles: TStrings);
+    function CollectOtherOpeningFiles_SendToServer(
+      const aClient: TIPCClient; const aFiles: TStrings): Boolean;
+    function SendFilesToRunningInstance(const aFiles: TStrings;
+      var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND
+      ): TOpenFilesResult;
+    procedure InitIDEInstances;
+  public
+    constructor Create;
+    destructor Destroy; override;
+  public
+    procedure PerformCheck;//call PerformCheck after Application.Initialize - it can open dialogs!
+
+    procedure StartListening(const aOpenFilesEvent: TOpenFilesEvent;
+      const aChooseActionEvent: TChooseActionEvent);
+    procedure StopListening;
+    function StartIDE: Boolean;//can the IDE be started?
+    function AllowOpenLastProject: Boolean;//if a secondary IDE is starting, do NOT reopen last project!
+    function FilesToOpen: TStrings;
+  end;
+
+function LazIDEInstances: TIDEInstances;
+
+implementation
+
+const
+  {$IFDEF UNIX}
+  GLOBAL_RIGHTS = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH or S_IWOTH;
+  {$ELSE}
+  GLOBAL_RIGHTS = 0;
+  {$ENDIF}
+  SERVERPREFIX_MAIN = 'LazarusMain';
+  SERVERPREFIX_RESPONSE = 'LazarusResponse';
+  SERVERNAME_COLLECT = 'LazarusCollect';
+  MESSAGETYPE_XML = 2;
+  ELEMENT_ROOT = 'ideinstances';
+  ATTR_VALUE = 'value';
+  ATTR_MESSAGE_TYPE = 'msgtype';
+  MESSAGE_CHOOSEACTION = 'chooseaction';
+  RESPONSE_SHOWPROMPT = 'showprompt';
+  MESSAGE_OPENFILES = 'openfiles';
+  MESSAGE_COLLECTFILES = 'collectfiles';
+  RESPONSE_STARTNEWINSTANCE = 'startnewinstance';
+  PARAM_RESPONSETOSERVER = 'responsetoserver';
+  PARAM_FILE = 'file';
+  PARAM_RESULT = 'result';
+  PARAM_HANDLEBRINGTOFRONT = 'handlebringtofront';
+  PARAM_PROMPTMESSAGE = 'promptmessage';
+  PARAM_OPENNEWINSTANCEMESSAGE = 'opennewinstancemessage';
+  MAX_CHECK_INSTANCES = 10;//check maximum count of continuously started instances
+
+var
+  FLazIDEInstances: TIDEInstances;
+
+function LazIDEInstances: TIDEInstances;
+begin
+  Result := FLazIDEInstances;
+end;
+
+{ TIPCServer }
+
+function TIPCServer.CanReadFile(const aFileName: string; out outHandle: TFileHandle
+  ): Boolean;
+begin
+  outHandle := feInvalidHandle;
+  Result := FileExists(aFileName);
+  if Result then
+  begin
+    outHandle := FileOpen(aFileName, fmOpenRead or fmShareExclusive);
+    Result := outHandle<>feInvalidHandle;
+  end;
+end;
+
+procedure TIPCServer.CleanUpTempFiles;
+var
+  I: Integer;
+begin
+  DeleteFile(FFileName);
+  for I := 1 to FMaxClientFileIndex + 10 do
+    DeleteFile(FFileName+IntToStr(I));
+
+  FMaxClientFileIndex := 0;
+end;
+
+function TIPCServer.CloseFileHandle(var ioFileName: string;
+  var ioHandle: TFileHandle): Boolean;
+begin
+  if ioHandle<>feInvalidHandle then
+  begin
+    try
+      FileClose(ioHandle);
+    finally
+      ioHandle:=feInvalidHandle;
+    end;
+  end;
+  if ioFileName<>'' then
+  begin
+    Result := DeleteFile(ioFileName);
+    ioFileName := '';
+  end
+  else
+    Result := True;
+end;
+
+constructor TIPCServer.Create;
+begin
+  inherited Create;
+
+  FFileHandle := feInvalidHandle;
+  FLastMsgFileHandle := feInvalidHandle;
+  FMsgData := TMemoryStream.Create;
+end;
+
+destructor TIPCServer.Destroy;
+begin
+  StopServer;
+  FMsgData.Free;
+
+  inherited Destroy;
+end;
+
+function TIPCServer.FindFirstFile(out outFileName: string; out
+  outHandle: TFileHandle): Boolean;
+var
+  I: Integer;
+begin
+  I := 0;
+  repeat
+    Inc(I);
+    outFileName := FFileName+'_'+IntToStr(I);
+    Result := CanReadFile(outFileName, outHandle);
+  until Result or (I >= FMaxClientFileIndex+5);
+
+  if Result then
+  begin
+    if (I > FMaxClientFileIndex) then
+     FMaxClientFileIndex := I;
+  end;
+end;
+
+function TIPCServer.PeekMessage(aTimeOut: Integer): Boolean;
+var
+  xStart: QWord;
+begin
+  Result := False;
+  xStart := GetTickCount64;
+  repeat
+    if Self.PeekMessage() then//important do not use PeekMessage only, the compiler will think that it is the Result variable
+      Exit(True)
+    else if aTimeOut > 20 then
+      Sleep(10);
+  until (GetTickCount64-xStart > aTimeOut);
+end;
+
+function TIPCServer.PeekMessage: Boolean;
+var
+  xMsgStream: THandleStream;
+  xHeader: TMessageHeader;
+begin
+  CloseFileHandle(FLastMsgFileName, FLastMsgFileHandle);
+  Result := FindFirstFile(FLastMsgFileName, FLastMsgFileHandle);
+
+  FMsgData.Size := 0;
+  if Result then
+  begin
+    xMsgStream := nil;
+    try
+      xMsgStream := THandleStream.Create(FLastMsgFileHandle);
+
+      if (xMsgStream.Size > SizeOf(xHeader)) then
+      begin
+        xMsgStream.ReadBuffer(xHeader{%H-},SizeOf(xHeader));
+        FMsgType := xHeader.MsgType;
+        if xHeader.MsgLen > 0 then
+          FMsgData.CopyFrom(xMsgStream, xHeader.MsgLen);
+      end;
+    finally
+      xMsgStream.Free;
+      CloseFileHandle(FLastMsgFileName, FLastMsgFileHandle);
+    end;
+  end;
+end;
+
+function TIPCServer.StartServer: Boolean;
+begin
+  FFileHandle := FileCreate(FFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
+  Result := (FFileHandle<>feInvalidHandle);
+  FActive := Result;
+end;
+
+function TIPCServer.StopServer: Boolean;
+begin
+  CloseFileHandle(FLastMsgFileName, FLastMsgFileHandle);
+  Result := (FFileHandle = feInvalidHandle) or CloseFileHandle(FFileName, FFileHandle);
+  CleanUpTempFiles;
+  FActive := False;
+end;
+
+{ TIPCBase }
+
+constructor TIPCBase.Create;
+begin
+  inherited Create;
+end;
+
+function TIPCBase.GetUniqueFileName: string;
+var
+  I: Integer;
+begin
+  I := 0;
+  repeat
+    Inc(I);
+    Result := FFileName+'_'+IntToStr(I);
+  until not FileExists(Result);
+end;
+
+procedure TIPCBase.SetServerName(const aServerName: string);
+begin
+  if FServerName = aServerName then Exit;
+  FServerName := aServerName;
+
+  FFileName := GetTempDir(False) + aServerName;//get temp dir for current user
+end;
+
+{ TIPCClient }
+
+procedure TIPCClient.SendMessage(const aMsgType: Integer; const aStream: TStream
+  );
+var
+  xHeader: TMessageHeader;
+  xStream: TFileStream;
+begin
+  xHeader.MsgType := aMsgType;
+  xHeader.MsgLen := aStream.Size;
+
+  xStream := TFileStream.Create(GetUniqueFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
+  try
+    xStream.WriteBuffer(xHeader, SizeOf(xHeader));
+    xStream.CopyFrom(aStream, 0);
+  finally
+    xStream.Free;
+  end;
+end;
+
+function TIPCClient.ServerRunning: Boolean;
+var
+  xServerFileHandle: TFileHandle;
+begin
+  Result:=FileExists(FFileName);
+  if Result then
+  begin//+ check -> we should not be able to access the file
+    xServerFileHandle := FileCreate(FFileName, fmOpenReadWrite or fmShareExclusive, GLOBAL_RIGHTS);
+    Result := (xServerFileHandle=feInvalidHandle);
+    if not Result then
+      FileClose(xServerFileHandle);
+  end;
+end;
+
+{ TIDEInstances }
+
+class function TIDEInstances.MessageParam(const aName, aValue: string): TMessageParam;
+begin
+  Result.Name := aName;
+  Result.Value := aValue;
+end;
+
+function TIDEInstances.StartIDE: Boolean;
+begin
+  Result := FStartIDE;
+end;
+
+function TIDEInstances.AllowOpenLastProject: Boolean;
+begin
+  Result := FAllowOpenLastProject;
+end;
+
+function TIDEInstances.FilesToOpen: TStrings;
+begin
+  if not Assigned(FFilesToOpen) then
+    FFilesToOpen := TStringList.Create;
+  Result := FFilesToOpen;
+end;
+
+procedure TIDEInstances.StartListening(const aOpenFilesEvent: TOpenFilesEvent;
+  const aChooseActionEvent: TChooseActionEvent);
+begin
+  Assert(Assigned(aOpenFilesEvent) and Assigned(aChooseActionEvent));
+
+  if not Assigned(FMainServer) then
+  begin
+    FMainServer := TMainServer.Create;
+    FMainServer.StartUnique(SERVERPREFIX_MAIN);
+  end;
+  FMainServer.FOpenFilesEvent := aOpenFilesEvent;
+  FMainServer.FChooseActionEvent := aChooseActionEvent;
+end;
+
+procedure TIDEInstances.StopListening;
+begin
+  FreeAndNil(FMainServer);
+end;
+
+class procedure TIDEInstances.AddFilesFromParams(
+  const aInParams: TMessageParams; const aFiles: TStrings);
+var
+  I: Integer;
+begin
+  //do not clear aFiles
+  for I := Low(aInParams) to High(aInParams) do
+    if aInParams[I].Name = PARAM_FILE then
+      aFiles.Add(aInParams[I].Value);
+end;
+
+class function TIDEInstances.GetMessageParam(
+  const aParams: array of TMessageParam; const aParamName: string): string;
+var
+  I: Integer;
+begin
+  for I := 0 to Length(aParams) do
+  if aParams[I].Name = aParamName then
+    Exit(aParams[I].Value);
+
+  Result := '';//not found
+end;
+
+class procedure TIDEInstances.BuildMessage(const aMessageType: string;
+  const aParams: array of TMessageParam; const aStream: TStream);
+var
+  xDOM: TXMLDocument;
+  xRoot: TDOMElement;
+  xParam: TDOMElement;
+  I: Integer;
+begin
+  xDOM := TXMLDocument.Create;
+  try
+    xRoot := xDOM.CreateElement(ELEMENT_ROOT);
+    xRoot.AttribStrings[ATTR_MESSAGE_TYPE] := aMessageType;
+    xDOM.AppendChild(xRoot);
+
+    for I := Low(aParams) to High(aParams) do
+    begin
+      xParam := xDOM.CreateElement(aParams[I].Name);
+      xRoot.AppendChild(xParam);
+      xParam.AttribStrings[ATTR_VALUE] := aParams[I].Value;
+    end;
+
+    WriteXMLFile(xDOM, aStream);
+  finally
+    xDOM.Free;
+  end;
+end;
+
+class procedure TIDEInstances.ParseMessage(const aStream: TStream; out
+  outMessageType: string; out outParams: TMessageParams);
+var
+  xDOM: TXMLDocument;
+  xChildList: TDOMNodeList;
+  I, J: Integer;
+begin
+  outMessageType := '';
+  SetLength(outParams, 0);
+  ReadXMLFile(xDOM, aStream, []);
+  try
+    if (xDOM = nil) or (xDOM.DocumentElement = nil) or (xDOM.DocumentElement.NodeName <> ELEMENT_ROOT) then
+      Exit;
+
+    outMessageType := xDOM.DocumentElement.AttribStrings[ATTR_MESSAGE_TYPE];
+
+    xChildList := xDOM.DocumentElement.ChildNodes;
+    SetLength(outParams, xChildList.Count);
+    J := 0;
+    for I := 0 to xChildList.Count-1 do
+    if xChildList[I] is TDOMElement then
+    begin
+      outParams[J].Name := xChildList[I].NodeName;
+      outParams[J].Value := TDOMElement(xChildList[I]).AttribStrings[ATTR_VALUE];
+      Inc(J);
+    end;
+    SetLength(outParams, J);
+  finally
+    xDOM.Free;
+  end;
+end;
+
+function TIDEInstances.SendFilesToRunningInstance(const aFiles: TStrings;
+  var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND
+  ): TOpenFilesResult;
+var
+  xStartClient: TResponseClient;
+  I: Integer;
+begin
+  Result := ofrStartNewInstance;
+  xStartClient := TResponseClient.Create;
+  try
+    for I := 1 to MAX_CHECK_INSTANCES do//check for multiple instances
+    begin
+      xStartClient.ServerName := SERVERPREFIX_MAIN+IntToStr(I);
+      if xStartClient.ServerRunning then
+      begin
+        //there are open Lazarus instances, do not reopen last project!
+        FAllowOpenLastProject := False;
+        Result := xStartClient.SendFilesToRunningInstance(aFiles, outOpenNewInstanceMessage, outHandleBringToFront);
+        if Result <> ofrModalError then//if the current IDE is modal, try another one
+          Exit;//handle only one running Lazarus IDE
+      end;
+    end;
+  finally
+    xStartClient.Free;
+  end;
+end;
+
+function TIDEInstances.CheckParamsForForceNewInstanceOpt: Boolean;
+var
+  I: Integer;
+begin
+  Result := False;
+  for I := 1 to ParamsAndCfgCount do
+    if ParamIsOption(i, ForceNewInstanceOpt) then//ignore the settings and start new Lazarus IDE instance
+      Result := True;
+end;
+
+procedure TIDEInstances.PerformCheck;
+var
+  xResult: TOpenFilesResult;
+  xOpenNewInstanceMessage: string = '';
+  xHandleBringToFront: HWND = 0;
+begin
+  if not FStartIDE then//InitIDEInstances->CollectOtherOpeningFiles decided not to start the IDE
+    Exit;
+
+  if not FForceNewInstance and (FilesToOpen.Count > 0) then
+    xResult := SendFilesToRunningInstance(FilesToOpen, xOpenNewInstanceMessage, xHandleBringToFront)
+  else
+    xResult := ofrStartNewInstance;
+
+  if xOpenNewInstanceMessage = '' then
+    xOpenNewInstanceMessage := dlgRunningInstanceModalOpenNew;
+  FStartIDE :=
+    (xResult = ofrStartNewInstance) or
+      ((xResult = ofrModalError) and
+       (MessageDlg(lisLazarusIDE, Format(xOpenNewInstanceMessage, [FilesToOpen.Text]), mtWarning, mbYesNo, 0, mbYes) = mrYes));//user decided to open in new ide
+
+  {$IFDEF MSWINDOWS}
+  if not FStartIDE and (xHandleBringToFront <> 0) and (xHandleBringToFront <> 0) then
+  begin
+    try
+      SetForegroundWindow(xHandleBringToFront);//SetForegroundWindow works (on Windows) only if the calling process is the foreground process, therefore it must be here!
+    except
+      //eat all widget exceptions
+    end;
+  end;
+  {$ENDIF}
+end;
+
+function TIDEInstances.CollectOtherOpeningFiles_SendToServer(
+  const aClient: TIPCClient; const aFiles: TStrings): Boolean;
+var
+  xOutParams: TMessageParams;
+  xStream: TMemoryStream;
+  I: Integer;
+begin
+  Result := aClient.ServerRunning;
+
+  SetLength(xOutParams, aFiles.Count);
+  for I := 0 to aFiles.Count-1 do
+    xOutParams[I] := MessageParam(PARAM_FILE, aFiles[I]);
+  xStream := TMemoryStream.Create;
+  try
+    BuildMessage(MESSAGE_COLLECTFILES, xOutParams, xStream);
+    xStream.Position := 0;
+    aClient.SendMessage(MESSAGETYPE_XML, xStream);
+  finally
+    xStream.Free;
+  end;
+end;
+
+constructor TIDEInstances.Create;
+begin
+  inherited Create;
+
+  FStartIDE := True;
+  FAllowOpenLastProject := True;
+end;
+
+destructor TIDEInstances.Destroy;
+begin
+  FreeAndNil(FMainServer);
+  FreeAndNil(FFilesToOpen);
+
+  inherited Destroy;
+end;
+
+procedure TIDEInstances.CollectOtherOpeningFiles_ReceiveFromClient(
+  const aServer: TIPCServer; const aFiles: TStrings);
+var
+  xInParams: TMessageParams;
+  xBreak, xNewBreak: QWord;
+  xMsgType: string;
+const
+  FIRST_TIMEOUT = 100;//timeout to wait for first file, do not set too small!
+  FURTHER_TIMEOUT = 100;//timeout to wait for other files, do not set too small!
+begin
+  xBreak := GetTickCount64 + FIRST_TIMEOUT;
+  repeat
+    if aServer.Active and
+       aServer.PeekMessage and
+       (aServer.MsgType = MESSAGETYPE_XML)
+    then
+    begin
+      aServer.MsgData.Position := 0;
+      ParseMessage(aServer.MsgData, xMsgType, xInParams);
+      if (xMsgType = MESSAGE_COLLECTFILES) then
+        AddFilesFromParams(xInParams, aFiles);
+      xNewBreak := GetTickCount64 + FURTHER_TIMEOUT;//start ticking again from the point of last message, but with a shorter timeout
+      if xNewBreak > xBreak then
+        xBreak := xNewBreak;
+    end;
+  until (GetTickCount64 > xBreak);
+end;
+
+procedure TIDEInstances.CollectOtherOpeningFiles(out
+  outFilesWereSentToCollectingServer: Boolean);
+var
+  xServer: TIPCServer;
+  xClient: TIPCClient;
+begin
+  //if you select more files in explorer and open them, they are not opened in one process but one process is started per file
+  // -> collect them
+
+  outFilesWereSentToCollectingServer := False;
+
+  xServer := nil;
+  xClient := nil;
+  try
+    xServer := TIPCServer.Create;
+    xServer.ServerName := SERVERNAME_COLLECT;
+    if xServer.StartServer then
+    begin
+      //I am server, receive files from the client
+      CollectOtherOpeningFiles_ReceiveFromClient(xServer, FilesToOpen);
+    end else
+    begin
+      //I am client, send my files to the server
+      xClient := TIPCClient.Create;
+      xClient.ServerName := xServer.ServerName;
+      outFilesWereSentToCollectingServer :=
+        CollectOtherOpeningFiles_SendToServer(xClient, FilesToOpen);
+    end;
+  finally
+    xClient.Free;
+    xServer.Free;
+  end;
+end;
+
+procedure TIDEInstances.InitIDEInstances;
+var
+  xFilesWereSentToCollectingServer: Boolean;
+begin
+  FForceNewInstance := CheckParamsForForceNewInstanceOpt;
+
+  //get cmd line filenames
+  FFilesToOpen := ExtractCmdLineFilenames;
+
+  if FilesToOpen.Count > 0 then//if there are file in the cmd, check for multiple starting instances
+  begin
+    CollectOtherOpeningFiles(xFilesWereSentToCollectingServer);
+    if xFilesWereSentToCollectingServer then
+    begin
+      FilesToOpen.Clear;
+      FStartIDE := False;
+    end;
+  end;
+end;
+
+{ TUniqueServer }
+
+procedure TUniqueServer.StartUnique(const aServerPrefix: string);
+var
+  I: Integer;
+begin
+  if Active then
+    StopServer;
+
+  I := 0;
+  while not Active do
+  begin
+    Inc(I);
+    ServerName := aServerPrefix+IntToStr(I);
+    StartServer;
+  end;
+end;
+
+{ TResponseClient }
+
+function TResponseClient.SendFilesToRunningInstance(const aFiles: TStrings;
+  var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND
+  ): TOpenFilesResult;
+var
+  xStartServer: TUniqueServer;
+  I: Integer;
+  xStream: TMemoryStream;
+  xMsgType: string;
+  xOutParams, xInParams: TMessageParams;
+  xChooseAction: TIDEStartWithFilesAction;
+  xPromptMessage: string;
+begin
+  Result := ofrStartNewInstance;
+  xStream := nil;
+  xStartServer := nil;
+  try
+    xStream := TMemoryStream.Create;
+    xStartServer := TUniqueServer.Create;
+
+    xStartServer.StartUnique(SERVERPREFIX_RESPONSE);
+
+    //ask to show prompt
+    xChooseAction := isaStartNewInstance;
+    SetLength(xOutParams, aFiles.Count+1);
+    xOutParams[0] := TIDEInstances.MessageParam(PARAM_RESPONSETOSERVER, xStartServer.ServerName);
+    for I := 0 to aFiles.Count-1 do
+      xOutParams[I+1] := TIDEInstances.MessageParam(PARAM_FILE, aFiles[I]);
+    TIDEInstances.BuildMessage(MESSAGE_CHOOSEACTION, xOutParams, xStream);
+    xStream.Position := 0;
+    Self.SendMessage(MESSAGETYPE_XML, xStream);
+    if xStartServer.PeekMessage(500) and//first timeout is 1/2 second, we should get an answer fast
+       (xStartServer.MsgType = MESSAGETYPE_XML) then
+    begin
+      xStartServer.MsgData.Position := 0;
+      TIDEInstances.ParseMessage(xStartServer.MsgData, xMsgType, xInParams);
+      if (xMsgType = RESPONSE_SHOWPROMPT) then
+      begin
+        xChooseAction := TIDEStartWithFilesAction(StrToIntDef(TIDEInstances.GetMessageParam(xInParams, PARAM_RESULT), 0));
+        outOpenNewInstanceMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_OPENNEWINSTANCEMESSAGE);
+        xPromptMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_PROMPTMESSAGE);
+        if xChooseAction = isaModalError then
+          Exit(ofrModalError);
+      end;
+    end else//no response, the IDE is modal and cannot accept messages
+      Exit(ofrModalError);
+
+    case xChooseAction of
+      isaPrompt:
+      begin
+        if xPromptMessage = '' then
+          xPromptMessage := dlgOpenInRunningInstance;
+        case MessageDlg(lisLazarusIDE, Format(xPromptMessage, [aFiles.Text]), mtConfirmation, mbYesNo, 0, mbYes) of
+          mrYes: begin end;//user hit "yes" -> proceed
+          mrNo: Exit(ofrStartNewInstance);//user hit "no" -> open new instance
+        else//cancel/close -> do nothing, do not open IDE
+          Exit(ofrDoNotStart);
+        end;
+      end;
+      isaStartNewInstance://settings is startnewide -> open new instance
+        Exit(ofrStartNewInstance);
+    end;
+
+    //open files in new instance
+    xStream.Clear;
+    TIDEInstances.BuildMessage(MESSAGE_OPENFILES, xOutParams, xStream);
+    xStream.Position := 0;
+    Self.SendMessage(MESSAGETYPE_XML, xStream);
+    if xStartServer.PeekMessage(3*1000) and//we know that Lazarus is already running so we get an answer, the UnHide process could take some time, let's wait for it max 3 seconds
+       (xStartServer.MsgType = MESSAGETYPE_XML) then
+    begin
+      xStartServer.MsgData.Position := 0;
+      TIDEInstances.ParseMessage(xStartServer.MsgData, xMsgType, xInParams);
+      if (xMsgType = RESPONSE_STARTNEWINSTANCE) then
+      begin
+        Result := TOpenFilesResult(StrToIntDef(TIDEInstances.GetMessageParam(xInParams, PARAM_RESULT), 0));
+        outHandleBringToFront := StrToInt64Def(TIDEInstances.GetMessageParam(xInParams, PARAM_HANDLEBRINGTOFRONT), 0);
+      end;
+    end else//no response, the IDE is modal and cannot accept messages
+      Exit(ofrModalError);
+  finally
+    xStartServer.Free;
+    xStream.Free;
+  end;
+end;
+
+{ TMainServer }
+
+procedure TMainServer.CheckMessagesOnTimer(Sender: TObject);
+begin
+  DoCheckMessages;
+end;
+
+constructor TMainServer.Create;
+begin
+  inherited Create;
+
+  FTimer := TTimer.Create(nil);
+  FTimer.OnTimer := @CheckMessagesOnTimer;
+  FTimer.Interval := 50;
+  FTimer.Enabled := True;
+end;
+
+destructor TMainServer.Destroy;
+begin
+  FTimer.Free;//must free manually before inherited Destroy
+
+  inherited Destroy;
+end;
+
+procedure TMainServer.DoChooseActionEvent(const aInParams: TMessageParams);
+var
+  xResult: TIDEStartWithFilesAction;
+  xParams: TMessageParams;
+  xResponseTo: string;
+  xPromptMessage, xOpenNewInstanceMessage: string;
+  xFiles: TStringList;
+begin
+  xResponseTo := TIDEInstances.GetMessageParam(aInParams, PARAM_RESPONSETOSERVER);
+  xResult := isaStartNewInstance;
+  xPromptMessage := '';
+  xOpenNewInstanceMessage := '';
+  if Assigned(FChooseActionEvent) then
+  begin
+    xFiles := TStringList.Create;
+    try
+      TIDEInstances.AddFilesFromParams(aInParams, xFiles);
+      FChooseActionEvent(xFiles, xResult, xPromptMessage, xOpenNewInstanceMessage);
+    finally
+      xFiles.Free;
+    end;
+  end;
+
+  SetLength(xParams, 3);
+  xParams[0] := TIDEInstances.MessageParam(PARAM_RESULT, IntToStr(Ord(xResult)));
+  xParams[1] := TIDEInstances.MessageParam(PARAM_PROMPTMESSAGE, xPromptMessage);
+  xParams[2] := TIDEInstances.MessageParam(PARAM_OPENNEWINSTANCEMESSAGE, xOpenNewInstanceMessage);
+  SimpleResponse(xResponseTo, RESPONSE_SHOWPROMPT, xParams);
+end;
+
+procedure TMainServer.DoOpenFiles(const aInParams: TMessageParams);
+var
+  xResult: TOpenFilesResult;
+  xHandleBringToFront: HWND;
+  xFiles: TStrings;
+  xParams: TMessageParams;
+  xResponseTo: string;
+begin
+  xResponseTo := TIDEInstances.GetMessageParam(aInParams, PARAM_RESPONSETOSERVER);
+  xResult := ofrStartNewInstance;
+  xHandleBringToFront := 0;
+  if Assigned(FOpenFilesEvent) then
+  begin
+    xFiles := TStringList.Create;
+    try
+      TIDEInstances.AddFilesFromParams(aInParams, xFiles);
+      FOpenFilesEvent(xFiles, xResult, xHandleBringToFront);
+    finally
+      xFiles.Free;
+    end;
+  end;
+
+  SetLength(xParams, 2);
+  xParams[0] := TIDEInstances.MessageParam(PARAM_RESULT, IntToStr(Ord(xResult)));
+  xParams[1] := TIDEInstances.MessageParam(PARAM_HANDLEBRINGTOFRONT, IntToStr(xHandleBringToFront));
+  SimpleResponse(xResponseTo, RESPONSE_STARTNEWINSTANCE, xParams);
+end;
+
+procedure TMainServer.SimpleResponse(const aResponseToServer,
+  aResponseType: string; const aParams: array of TMessageParam);
+var
+  xClient: TIPCClient;
+  xStream: TMemoryStream;
+begin
+  xStream := nil;
+  xClient := nil;
+  try
+    xStream := TMemoryStream.Create;
+    xClient := TIPCClient.Create;
+    xClient.ServerName := aResponseToServer;
+    if xClient.ServerRunning then
+    begin
+      TIDEInstances.BuildMessage(aResponseType, aParams, xStream);
+
+      xStream.Position := 0;
+      xClient.SendMessage(MESSAGETYPE_XML, xStream);
+    end;
+  finally
+    xClient.Free;
+    xStream.Free;
+  end;
+end;
+
+procedure TMainServer.DoCheckMessages;
+var
+  xMessageType: string;
+  xParams: TMessageParams;
+begin
+  if Active then
+  begin
+    if PeekMessage and
+       (MsgType = MESSAGETYPE_XML) then
+    begin
+      MsgData.Position := 0;
+      TIDEInstances.ParseMessage(MsgData, xMessageType, xParams);
+      if xMessageType = MESSAGE_CHOOSEACTION then
+        DoChooseActionEvent(xParams)
+      else
+      if xMessageType = MESSAGE_OPENFILES then
+        DoOpenFiles(xParams);
+    end;
+  end;
+end;
+
+initialization
+  FLazIDEInstances := TIDEInstances.Create;
+  FLazIDEInstances.InitIDEInstances;
+
+finalization
+  FreeAndNil(FLazIDEInstances);
+
+end.
Index: ide/lazarus.pp
===================================================================
--- ide/lazarus.pp	(revision 49818)
+++ ide/lazarus.pp	(working copy)
@@ -52,6 +52,7 @@
   {$IFEND}
   SysUtils,
   Interfaces,
+  IDEInstances,
   Forms, LCLProc,
   IDEOptionsIntf,
   LazConf, IDEGuiCmdLine,
@@ -104,6 +105,9 @@
   {$ENDIF}
 
   Application.Initialize;
+  LazIDEInstances.PerformCheck;
+  if not LazIDEInstances.StartIDE then
+    Exit;
   TMainIDE.ParseCmdLineOptions;
   if not SetupMainIDEInstance then exit;
   if Application.Terminated then exit;
Index: ide/lazarusidestrconsts.pas
===================================================================
--- ide/lazarusidestrconsts.pas	(revision 49818)
+++ ide/lazarusidestrconsts.pas	(working copy)
@@ -142,6 +142,14 @@
   lisMoveFiles2 = 'Move files?';
   lrsPLDDeleteSelected = 'Delete selected';
 
+  dlgOpenParamFilesInInstance = 'If second Lazarus IDE is started with files';
+  dlgOpenParamFilesInInstance_StartNewInstance = 'start new Lazarus instance';
+  dlgOpenParamFilesInInstance_OpenFilesInRunningInstance = 'open them in running instance';
+  dlgOpenParamFilesInInstance_Prompt = 'ask what to do';
+  dlgOpenParamFilesInInstance_ProjectPromptOtherInRunning = 'projects: ask; other: open in running';
+  dlgOpenInRunningInstance = 'Do you want to open the following file(s) in running Lazarus instance?'+sLineBreak+sLineBreak+'%s';
+  dlgRunningInstanceModalOpenNew = 'The running Lazarus instance cannot accept any files.'+sLineBreak+'Do you want to them in a new IDE instance?'+sLineBreak+sLineBreak+'%s';
+
   // *** Rest of the resource strings ***
 
   lisImportPackageListXml = 'Import package list (*.xml)';
Index: ide/lazarusmanager.pas
===================================================================
--- ide/lazarusmanager.pas	(revision 49818)
+++ ide/lazarusmanager.pas	(working copy)
@@ -92,8 +92,8 @@
   BaseUnix,
 {$ENDIF}
   Classes, SysUtils, Process, Forms, Controls, Dialogs, LCLProc,
-  UTF8Process, FileUtil, LazFileUtils, LazUTF8, FileProcs,
-  IDECmdLine, LazConf, Splash, BaseIDEIntf;
+  UTF8Process, FileUtil, FileProcs, LazUTF8, LazFileUtils,
+  IDECmdLine, LazConf, Splash, BaseIDEIntf, IDEInstances;
   
 type
 
@@ -244,6 +244,14 @@
   if FShowSplashOption then
     ShowSplash;
 
+  // we already handled IDEInstances, ignore it in lazarus EXE
+  FCmdLineParams.Add(ForceNewInstanceOpt);
+  // pass the AllowOpenLastProject parameter to lazarus EXE
+  if not LazIDEInstances.AllowOpenLastProject and
+     (FCmdLineParams.IndexOf(SkipLastProjectOpt) = -1)
+  then
+    FCmdLineParams.Add(SkipLastProjectOpt);
+
   // set primary config path
   PCP:=ExtractPrimaryConfigPath(FCmdLineParams);
   if PCP<>'' then
@@ -250,7 +258,7 @@
     SetPrimaryConfigPath(PCP);
 
   // get command line files
-  CmdLineFiles := ExtractCmdLineFilenames;
+  CmdLineFiles := LazIDEInstances.FilesToOpen;
   if CmdLineFiles<>nil then
   begin
     for i := 0 to CmdLineFiles.Count-1 do
Index: ide/main.pp
===================================================================
--- ide/main.pp	(revision 49818)
+++ ide/main.pp	(working copy)
@@ -157,6 +157,7 @@
   CleanDirDlg, CodeContextForm, AboutFrm, CompatibilityRestrictions,
   RestrictionBrowser, ProjectWizardDlg, IDECmdLine, IDEGuiCmdLine, CodeExplOpts,
   EditorMacroListViewer, SourceFileManager, EditorToolbarStatic,
+  IDEInstances,
   // main ide
   MainBar, MainIntf, MainBase;
 
@@ -187,6 +188,11 @@
     procedure HandleRemoteControlTimer(Sender: TObject);
     procedure HandleSelectFrame(Sender: TObject; var AComponentClass: TComponentClass);
     procedure OIChangedTimerTimer(Sender: TObject);
+    procedure NewInstanceChooseAction(const aFiles: TStrings;
+      var Result: TIDEStartWithFilesAction;
+      var outPromptMessage, outOpenNewInstanceMessage: string);
+    procedure NewInstanceOpenFiles(const aFiles: TStrings;
+      var Result: TOpenFilesResult; var outHandleBringToFront: HWND);
   public
     // file menu
     procedure mnuNewUnitClicked(Sender: TObject);
@@ -637,6 +643,7 @@
     OldCompilerFilename, OldLanguage: String;
     OIChangedTimer: TIdleTimer;
 
+    procedure DoDropFilesAsync(Data: PtrInt);
     procedure RenameInheritedMethods(AnUnitInfo: TUnitInfo; List: TStrings);
     function OIHelpProvider: TAbstractIDEHTMLProvider;
     // form editor and designer
@@ -950,6 +957,14 @@
   StartedByStartLazarus: boolean = false;
   ShowSetupDialog: boolean = false;
 
+type
+  TDoDropFilesAsyncParams = class(TComponent)
+  public
+    FileNames: array of string;
+    WindowIndex: Integer;
+    BringToFront: Boolean;
+  end;
+
 function FindDesignComponent(const aName: string): TComponent;
 var
   AnUnitInfo: TUnitInfo;
@@ -1556,6 +1571,7 @@
   DoShowMessagesView(false);           // reopen extra windows
   fUserInputSinceLastIdle:=true; // Idle work gets done initially before user action.
   MainIDEBar.ApplicationIsActivate:=true;
+  LazIDEInstances.StartListening(@NewInstanceOpenFiles, @NewInstanceChooseAction);
   FIDEStarted:=true;
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.StartIDE END');{$ENDIF}
 end;
@@ -1946,6 +1962,7 @@
 procedure TMainIDE.MainIDEFormClose(Sender: TObject;
   var CloseAction: TCloseAction);
 begin
+  LazIDEInstances.StopListening;
   DoCallNotifyHandler(lihtIDEClose);
   SaveEnvironment(true);
   if IDEDockMaster<>nil then
@@ -2182,119 +2199,116 @@
   {$ENDIF}
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject A');{$ENDIF}
   // load command line project or last project or create a new project
-  CmdLineFiles:=ExtractCmdLineFilenames;
-  try
-    ProjectLoaded:=false;
+  CmdLineFiles:=LazIDEInstances.FilesToOpen;
+  ProjectLoaded:=false;
 
-    // try command line project
-    if (CmdLineFiles<>nil) and (CmdLineFiles.Count>0) then begin
-      AProjectFilename:=CmdLineFiles[0];
-      if (CompareFileExt(AProjectFilename,'.lpr',false)=0) then
-        AProjectFilename:=ChangeFileExt(AProjectFilename,'.lpi');
-      // only try to load .lpi files, other files are loaded later
-      if (CompareFileExt(AProjectFilename,'.lpi',false)=0) then begin
-        AProjectFilename:=CleanAndExpandFilename(AProjectFilename);
-        if FileExistsUTF8(AProjectFilename) then begin
-          CmdLineFiles.Delete(0);
-          ProjectLoaded:=(DoOpenProjectFile(AProjectFilename,[])=mrOk);
-        end;
+  // try command line project
+  if (CmdLineFiles<>nil) and (CmdLineFiles.Count>0) then begin
+    AProjectFilename:=CmdLineFiles[0];
+    if (CompareFileExt(AProjectFilename,'.lpr',false)=0) then
+      AProjectFilename:=ChangeFileExt(AProjectFilename,'.lpi');
+    // only try to load .lpi files, other files are loaded later
+    if (CompareFileExt(AProjectFilename,'.lpi',false)=0) then begin
+      AProjectFilename:=CleanAndExpandFilename(AProjectFilename);
+      if FileExistsUTF8(AProjectFilename) then begin
+        CmdLineFiles.Delete(0);
+        ProjectLoaded:=(DoOpenProjectFile(AProjectFilename,[])=mrOk);
       end;
     end;
+  end;
 
-    // try loading last project if lazarus didn't fail last time
-    if (not ProjectLoaded)
-    and (not SkipAutoLoadingLastProject)
-    and (EnvironmentOptions.OpenLastProjectAtStart)
-    and (EnvironmentOptions.LastSavedProjectFile<>'')
-    and (EnvironmentOptions.LastSavedProjectFile<>RestoreProjectClosed)
-    and (FileExistsCached(EnvironmentOptions.LastSavedProjectFile))
-    then begin
-      if (not IDEProtocolOpts.LastProjectLoadingCrashed)
-      or AskIfLoadLastFailingProject then begin
-        // protocol that the IDE is trying to load the last project and did not
-        // yet succeed
-        IDEProtocolOpts.LastProjectLoadingCrashed := True;
-        IDEProtocolOpts.Save;
-        // try loading the project
-        ProjectLoaded:=
-          (DoOpenProjectFile(EnvironmentOptions.LastSavedProjectFile,[])=mrOk);
-        // protocol that the IDE was able to open the project without crashing
-        IDEProtocolOpts.LastProjectLoadingCrashed := false;
-        IDEProtocolOpts.Save;
-        if ProjectLoaded then
+  // try loading last project if lazarus didn't fail last time
+  if (not ProjectLoaded)
+  and (LazIDEInstances.AllowOpenLastProject)
+  and (not SkipAutoLoadingLastProject)
+  and (EnvironmentOptions.OpenLastProjectAtStart)
+  and (EnvironmentOptions.LastSavedProjectFile<>'')
+  and (EnvironmentOptions.LastSavedProjectFile<>RestoreProjectClosed)
+  and (FileExistsCached(EnvironmentOptions.LastSavedProjectFile))
+  then begin
+    if (not IDEProtocolOpts.LastProjectLoadingCrashed)
+    or AskIfLoadLastFailingProject then begin
+      // protocol that the IDE is trying to load the last project and did not
+      // yet succeed
+      IDEProtocolOpts.LastProjectLoadingCrashed := True;
+      IDEProtocolOpts.Save;
+      // try loading the project
+      ProjectLoaded:=
+        (DoOpenProjectFile(EnvironmentOptions.LastSavedProjectFile,[])=mrOk);
+      // protocol that the IDE was able to open the project without crashing
+      IDEProtocolOpts.LastProjectLoadingCrashed := false;
+      IDEProtocolOpts.Save;
+      if ProjectLoaded then
+      begin
+        PkgOpenFlags:=[pofAddToRecent];
+        for I := 0 to EnvironmentOptions.LastOpenPackages.Count-1 do
         begin
-          PkgOpenFlags:=[pofAddToRecent];
-          for I := 0 to EnvironmentOptions.LastOpenPackages.Count-1 do
-          begin
-            AFilename:=EnvironmentOptions.LastOpenPackages[I];
-            if AFilename='' then
-              continue;
-            if i<EnvironmentOptions.LastOpenPackages.Count-1 then
-              Include(PkgOpenFlags,pofMultiOpen)
-            else
-              Exclude(PkgOpenFlags,pofMultiOpen);
-            if PkgBoss.DoOpenPackageFile(AFilename,PkgOpenFlags,true)=mrAbort then begin
-              break;
-            end;
+          AFilename:=EnvironmentOptions.LastOpenPackages[I];
+          if AFilename='' then
+            continue;
+          if i<EnvironmentOptions.LastOpenPackages.Count-1 then
+            Include(PkgOpenFlags,pofMultiOpen)
+          else
+            Exclude(PkgOpenFlags,pofMultiOpen);
+          if PkgBoss.DoOpenPackageFile(AFilename,PkgOpenFlags,true)=mrAbort then begin
+            break;
           end;
-        end else
-        begin
-          DoCloseProject;
         end;
+      end else
+      begin
+        DoCloseProject;
       end;
     end;
-    {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject B');{$ENDIF}
+  end;
+  {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject B');{$ENDIF}
 
-    if (not ProjectLoaded) then
-    begin
-      if EnvironmentOptions.OpenLastProjectAtStart
-      and (EnvironmentOptions.LastSavedProjectFile=RestoreProjectClosed) then begin
-        // IDE was closed without a project => restore that state
-      end else begin
-        // create new project
-        DoNewProject(ProjectDescriptorApplication);
-      end;
+  if (not ProjectLoaded) then
+  begin
+    if EnvironmentOptions.OpenLastProjectAtStart
+    and (EnvironmentOptions.LastSavedProjectFile=RestoreProjectClosed) then begin
+      // IDE was closed without a project => restore that state
+    end else begin
+      // create new project
+      DoNewProject(ProjectDescriptorApplication);
     end;
+  end;
 
-    // load the cmd line files
-    if CmdLineFiles<>nil then begin
-      for i:=0 to CmdLineFiles.Count-1 do
-      Begin
-        AFilename:=CleanAndExpandFilename(CmdLineFiles.Strings[i]);
-        if not FileExistsCached(AFilename) then begin
-          debugln(['WARNING: command line file not found: "',AFilename,'"']);
-          continue;
+  // load the cmd line files
+  if CmdLineFiles<>nil then begin
+    for i:=0 to CmdLineFiles.Count-1 do
+    Begin
+      AFilename:=CleanAndExpandFilename(CmdLineFiles.Strings[i]);
+      if not FileExistsCached(AFilename) then begin
+        debugln(['WARNING: command line file not found: "',AFilename,'"']);
+        continue;
+      end;
+      if Project1=nil then begin
+        // to open a file a project is needed
+        // => create a project
+        DoNewProject(ProjectDescriptorEmptyProject);
+      end;
+      if CompareFileExt(AFilename,'.lpk',false)=0 then begin
+        if PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent,pofMultiOpen],true)=mrAbort
+        then
+          break;
+      end else begin
+        OpenFlags:=[ofAddToRecent,ofRegularFile];
+        if i<CmdLineFiles.Count then
+          Include(OpenFlags,ofMultiOpen);
+        if DoOpenEditorFile(AFilename,-1,-1,OpenFlags)=mrAbort then begin
+          break;
         end;
-        if Project1=nil then begin
-          // to open a file a project is needed
-          // => create a project
-          DoNewProject(ProjectDescriptorEmptyProject);
-        end;
-        if CompareFileExt(AFilename,'.lpk',false)=0 then begin
-          if PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent,pofMultiOpen],true)=mrAbort
-          then
-            break;
-        end else begin
-          OpenFlags:=[ofAddToRecent,ofRegularFile];
-          if i<CmdLineFiles.Count then
-            Include(OpenFlags,ofMultiOpen);
-          if DoOpenEditorFile(AFilename,-1,-1,OpenFlags)=mrAbort then begin
-            break;
-          end;
-        end;
       end;
     end;
+  end;
 
-    if Project1=nil then
-      DoNoProjectWizard(nil);
+  if Project1=nil then
+    DoNoProjectWizard(nil);
 
-    {$IFDEF IDE_DEBUG}
-    debugln('TMainIDE.Create B');
-    {$ENDIF}
-    {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject C');{$ENDIF}
-  finally
-    CmdLineFiles.Free;
-  end;
+  {$IFDEF IDE_DEBUG}
+  debugln('TMainIDE.Create B');
+  {$ENDIF}
+  {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject C');{$ENDIF}
 end;
 
 procedure TMainIDE.SetupRemoteControl;
@@ -5387,6 +5401,21 @@
   SetRecentFilesMenu;
 end;
 
+procedure TMainIDE.DoDropFilesAsync(Data: PtrInt);
+var
+  xParams: TDoDropFilesAsyncParams;
+begin
+  xParams := TDoDropFilesAsyncParams(Data);
+  DoDropFiles(Self, xParams.FileNames, xParams.WindowIndex);
+  if xParams.BringToFront then
+  begin
+    Application.BringToFront;
+    if SourceEditorManager.ActiveSourceWindow <> nil then
+      SourceEditorManager.ActiveSourceWindow.BringToFront;
+  end;
+  xParams.Free;
+end;
+
 function TMainIDE.DoSelectFrame: TComponentClass;
 var
   UnitList: TStringList;
@@ -9117,6 +9146,66 @@
   Result:=false;
 end;
 
+procedure TMainIDE.NewInstanceChooseAction(const aFiles: TStrings;
+  var Result: TIDEStartWithFilesAction; var outPromptMessage,
+  outOpenNewInstanceMessage: string);
+begin
+  outPromptMessage := dlgOpenInRunningInstance;
+  outOpenNewInstanceMessage := dlgRunningInstanceModalOpenNew;
+
+  case EnvironmentOptions.OpenParamFilesInInstance of
+    isoStartNewInstance: Result := isaStartNewInstance;
+    isoOpenFilesInRunningInstance: Result := isaOpenFilesInRunningInstance;
+    isoPrompt: Result := isaPrompt;
+    isoProjectPromptOtherInRunning:
+    begin
+      if (aFiles.Count > 0) and
+         ((CompareFileExt(aFiles[0], '.lpr', False) = 0) or
+          (CompareFileExt(aFiles[0], '.lpi', False) = 0))
+      then
+        Result := isaPrompt
+      else
+        Result := isaOpenFilesInRunningInstance;
+    end;
+  end;
+
+  if (Result <> isaStartNewInstance) and
+       (not IsWindowEnabled(Application.MainForm.Handle) or//check that main is active
+        (Application.ModalLevel > 0))//check that no modal window is opened
+  then
+  begin
+    Result := isaModalError;
+    Exit;
+  end;
+end;
+
+procedure TMainIDE.NewInstanceOpenFiles(const aFiles: TStrings;
+  var Result: TOpenFilesResult; var outHandleBringToFront: HWND);
+var
+  xParams: TDoDropFilesAsyncParams;
+  I: Integer;
+begin
+  if (not IsWindowEnabled(Application.MainForm.Handle) or//check that main is active
+     (Application.ModalLevel > 0))//check that no modal window is opened
+  then
+  begin
+    Result := ofrModalError;
+    Exit;
+  end;
+
+  xParams := TDoDropFilesAsyncParams.Create(Self);//we need direct response, do not wait to get the files opened!
+  SetLength(xParams.FileNames, aFiles.Count);
+  for I := 0 to aFiles.Count-1 do
+    xParams.FileNames[I] := aFiles[I];
+  xParams.WindowIndex := -1;
+  xParams.BringToFront := True;
+  Application.QueueAsyncCall(@DoDropFilesAsync, PtrInt(xParams));
+  Result := ofrDoNotStart;
+  outHandleBringToFront := Application.MainFormHandle;
+  if Application.MainForm.WindowState = wsMinimized then
+    UnhideIDE;
+end;
+
 procedure TMainIDE.ApplyCodeToolChanges;
 begin
   // all changes were handled automatically by events, just clear the logs
Index: ide/startlazarus.lpi
===================================================================
--- ide/startlazarus.lpi	(revision 49818)
+++ ide/startlazarus.lpi	(working copy)
@@ -8,6 +8,7 @@
       </Flags>
       <SessionStorage Value="InIDEConfig"/>
       <MainUnit Value="0"/>
+      <UseXPManifest Value="True"/>
       <Icon Value="0"/>
     </General>
     <BuildModes Count="1">
Index: ide/startlazarus.lpr
===================================================================
--- ide/startlazarus.lpr	(revision 49818)
+++ ide/startlazarus.lpr	(working copy)
@@ -34,6 +34,7 @@
   redirect_stderr,
   Interfaces, SysUtils,
   Forms,
+  IDEInstances,
   LazarusManager;
   
 {$R *.res}
@@ -44,6 +45,9 @@
 begin
   redirect_stderr.DoShowWindow := False;
   Application.Initialize;
+  LazIDEInstances.PerformCheck;
+  if not LazIDEInstances.StartIDE then
+    Exit;
   ALazarusManager := TLazarusManager.Create(nil);
   ALazarusManager.Initialize;
   ALazarusManager.Run;
ide-one-instance-1.patch (61,044 bytes)   

Juha Manninen

2015-09-13 10:52

developer   ~0085871

> 3.) Because it is not exactly a "single instance" I could not reuse the UniqueInstance component.

I think it could be a single instance. UniqueInstance component could be reused and it would simplify things.
This feature can be turned off anyway.

Ondrej Pokorny

2015-09-13 11:06

developer   ~0085872

> I think it could be a single instance.

I don't think it's a good idea. E.g. what to do if I want to be able to have multiple instances of Lazarus open but still automatically open files in running instances? Not possible with UniqueInstance.

What about debugging Lazarus with UniqueInstance? There is no chance because one instance debugs the other.

My solution is OK but needs a small tidy-up and refactoring so that the code is simpler. I am already doing it. Sorry, I really thought yesterday that everything is OK but now I got an idea how to do the things simpler.

Ondrej Pokorny

2015-09-13 14:34

developer   ~0085876

ide-one-instance-2.patch:

Point (5) now works like a charm also on Linux! I changed the code so that it doesn't depend on blocking files. So there should be no functional issues any more.

The code is now simpler and more comprehensible.

I refactored and moved the IPC code to an extra unit advancedipc.pas. It now supports request-response system. I placed it into the ide directory - I know that it doesn't belong there but I cannot decide where to put it. LCL or FPC sources? Please you managers decide and move it appropriately. Also please update the file header to a proper one. I don't know what to use there. Use standard Lazarus license.

IMO it works very neatly now :)

(Btw. about "single instance": the bug report doesn't demand unique Lazarus instance but to open files in running instance. And this is what I implemented.)

Ondrej Pokorny

2015-09-13 14:34

developer  

ide-one-instance-2.patch (68,571 bytes)   
Index: ide/advancedipc.pas
===================================================================
--- ide/advancedipc.pas	(nonexistent)
+++ ide/advancedipc.pas	(working copy)
@@ -0,0 +1,470 @@
+{
+    *** Please use appropriate header ***
+
+    License standard LCL (GPL/LGPL ?)
+
+
+  Author: Ondrej Pokorny
+
+  Abstract:
+    Unit implementing two-way (request/response) IPC between 1 server and more clients
+
+ **********************************************************************}
+unit AdvancedIPC;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  {$IFDEF UNIX}
+  baseunix,
+  {$endif}
+  sysutils, Classes;
+
+const
+  HEADER_VERSION = 1;
+
+type
+  TMessageHeader = packed record
+    HdrVer: Integer;//version of header
+    FileLock: Byte;//0 = unlocked, 1 = locked
+    MsgType: Integer;
+    MsgLen: Integer;
+  end;
+
+  TFileHandle = Classes.THandle;
+
+  TReleaseHandleStream = class(THandleStream)
+  public
+    destructor Destroy; override;
+  end;
+
+  TIPCBase = class
+  protected
+    FFileName: string;
+    FServerName: string;
+
+    function GetUniqueFileName: string;
+    procedure SetServerName(const aServerName: string); virtual;
+
+    function CanReadMessage(const aFileName: string; out outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+    procedure DoPostMessage(const aFileName: string; const aMsgType: Integer; const aStream: TStream);
+  public
+    constructor Create; virtual;
+  public
+    property ServerName: string read FServerName write SetServerName;
+  end;
+
+  TIPCClient = class(TIPCBase)
+  var
+    FLastMsgFileName: string;
+  public
+    procedure PostRequest(const aMsgType: Integer; const aStream: TStream); overload;
+    procedure PostRequest(const aMsgType: Integer; const aStream: TStream; out outMsgID: string); overload;
+    function PeekResponse(const aStream: TStream; var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+    procedure DeleteRequest;
+    function ServerRunning: Boolean;
+  end;
+
+  TIPCServer = class(TIPCBase)
+  private
+    FFileHandle: TFileHandle;
+    FActive: Boolean;
+
+    function FindFirstRequest(out outFileName: string; out outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+    function CloseFileHandle(var ioFileName: string; var ioHandle: TFileHandle): Boolean;
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+  public
+    function PeekRequest(const aStream: TStream; var outMsgType: Integer): Boolean; overload;
+    function PeekRequest(const aStream: TStream; var outMsgType: Integer; const aTimeOut: Integer): Boolean; overload;
+    function PeekRequest(const aStream: TStream; var outMsgID: string; var outMsgType: Integer): Boolean; overload;
+    function PeekRequest(const aStream: TStream; var outMsgID: string; var outMsgType: Integer; const aTimeOut: Integer): Boolean; overload;
+    procedure PostResponse(const aMsgID: string; const aMsgType: Integer; const aStream: TStream);
+
+    function FindHighestPendingRequestId: string;
+    function GetPendingRequestCount: Integer;
+
+    function StartServer(const aDeletePendingRequests: Boolean = True): Boolean;//true if unique and started
+    function StopServer(const aDeletePendingRequests: Boolean = True): Boolean;//true if stopped
+
+    procedure DeletePendingRequests;
+
+    property Active: Boolean read FActive;//true if started
+  end;
+
+
+implementation
+
+const
+  {$IFDEF UNIX}
+  GLOBAL_RIGHTS = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH or S_IWOTH;
+  {$ELSE}
+  GLOBAL_RIGHTS = 0;
+  {$ENDIF}
+  FILE_POSTFIX = '%s_';
+  FILE_WITH_INDEX = FILE_POSTFIX+'%x';
+  FILE_RESPONSE = '%s_r';
+
+{ TIPCBase }
+
+function TIPCBase.CanReadMessage(const aFileName: string; out
+  outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+var
+  xFileHandle: TFileHandle;
+  xHeader: TMessageHeader;
+begin
+  outStream := nil;
+  outMsgType := -1;
+  outMsgLen := 0;
+  Result := FileExists(aFileName);
+  if not Result then
+    Exit;
+
+  xFileHandle := FileOpen(aFileName, fmOpenRead or fmShareExclusive);
+  Result := xFileHandle <> feInvalidHandle;
+  if not Result then
+    Exit;
+
+  outStream := TReleaseHandleStream.Create(xFileHandle);
+
+  Result := (outStream.Size >= SizeOf(xHeader));
+  if not Result then
+  begin
+    FreeAndNil(outStream);
+    Exit;
+  end;
+
+  outStream.ReadBuffer(xHeader{%H-}, SizeOf(xHeader));
+  Result := (xHeader.HdrVer = HEADER_VERSION) and (xHeader.FileLock = 0) and
+    (outStream.Size = Int64(SizeOf(xHeader))+Int64(xHeader.MsgLen));
+  if not Result then
+  begin
+    FreeAndNil(outStream);
+    Exit;
+  end;
+  outMsgType := xHeader.MsgType;
+  outMsgLen := xHeader.MsgLen;
+end;
+
+constructor TIPCBase.Create;
+begin
+  inherited Create;
+end;
+
+function TIPCBase.GetUniqueFileName: string;
+var
+  I: Integer;
+begin
+  Randomize;
+  repeat
+    I := Random(High(Integer));
+    Result := Format(FILE_WITH_INDEX, [FFileName, I]);
+  until not FileExists(Result);
+end;
+
+procedure TIPCBase.DoPostMessage(const aFileName: string;
+  const aMsgType: Integer; const aStream: TStream);
+var
+  xHeader: TMessageHeader;
+  xStream: TFileStream;
+begin
+  xHeader.HdrVer := HEADER_VERSION;
+  xHeader.FileLock := 1;//locking
+  xHeader.MsgType := aMsgType;
+  xHeader.MsgLen := aStream.Size-aStream.Position;
+
+  xStream := TFileStream.Create(aFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
+  try
+    xStream.WriteBuffer(xHeader, SizeOf(xHeader));
+    xStream.CopyFrom(aStream, 0);
+
+    xStream.Position := 0;//unlocking
+    xHeader.FileLock := 0;
+    xStream.WriteBuffer(xHeader, SizeOf(xHeader));
+  finally
+    xStream.Free;
+  end;
+end;
+
+procedure TIPCBase.SetServerName(const aServerName: string);
+begin
+  if FServerName = aServerName then Exit;
+  FServerName := aServerName;
+
+  FFileName := GetTempDir(False) + aServerName;//get temp dir for current user
+end;
+
+{ TIPCClient }
+
+procedure TIPCClient.DeleteRequest;
+begin
+  if DeleteFile(FLastMsgFileName) then
+    FLastMsgFileName := '';
+end;
+
+function TIPCClient.PeekResponse(const aStream: TStream;
+  var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+var
+  xStart: QWord;
+  xStream: TStream;
+  xMsgLen: Integer;
+  xFileResponse: string;
+begin
+  aStream.Size := 0;
+  outMsgType := -1;
+  Result := False;
+  xStart := GetTickCount64;
+  repeat
+    xFileResponse := Format(FILE_RESPONSE, [FLastMsgFileName]);
+    if CanReadMessage(xFileResponse, xStream, outMsgType, xMsgLen) then
+    begin
+      aStream.CopyFrom(xStream, xMsgLen);
+      xStream.Free;
+      aStream.Position := 0;
+      DeleteFile(xFileResponse);
+      Exit(True);
+    end
+    else if aTimeOut > 20 then
+      Sleep(10);
+  until (GetTickCount64-xStart > aTimeOut);
+end;
+
+procedure TIPCClient.PostRequest(const aMsgType: Integer; const aStream: TStream);
+var
+  xMsgID: string;
+begin
+  PostRequest(aMsgType, aStream, xMsgID);
+end;
+
+procedure TIPCClient.PostRequest(const aMsgType: Integer;
+  const aStream: TStream; out outMsgID: string);
+begin
+  outMsgID := GetUniqueFileName;
+  FLastMsgFileName := outMsgID;
+  DeleteFile(Format(FILE_RESPONSE, [FLastMsgFileName]));//delete old response, if there is any
+  DoPostMessage(FLastMsgFileName, aMsgType, aStream);
+end;
+
+function TIPCClient.ServerRunning: Boolean;
+var
+  xServerFileHandle: TFileHandle;
+begin
+  Result:=FileExists(FFileName);
+  if Result then
+  begin//+ check -> we should not be able to access the file
+    xServerFileHandle := FileCreate(FFileName, fmOpenReadWrite or fmShareExclusive, GLOBAL_RIGHTS);
+    Result := (xServerFileHandle=feInvalidHandle);
+    if not Result then
+      FileClose(xServerFileHandle);
+  end;
+end;
+
+{ TReleaseHandleStream }
+
+destructor TReleaseHandleStream.Destroy;
+begin
+  FileClose(Handle);
+
+  inherited Destroy;
+end;
+
+{ TIPCServer }
+
+procedure TIPCServer.DeletePendingRequests;
+var
+  xRec: TRawByteSearchRec;
+  xDir: string;
+begin
+  xDir := ExtractFilePath(FFileName);
+  if FindFirst(Format(FILE_POSTFIX, [FFileName])+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      DeleteFile(xDir+xRec.Name);
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.CloseFileHandle(var ioFileName: string;
+  var ioHandle: TFileHandle): Boolean;
+begin
+  if ioHandle<>feInvalidHandle then
+  begin
+    try
+      FileClose(ioHandle);
+    finally
+      ioHandle:=feInvalidHandle;
+    end;
+  end;
+  if ioFileName<>'' then
+  begin
+    Result := DeleteFile(ioFileName);
+    ioFileName := '';
+  end
+  else
+    Result := True;
+end;
+
+constructor TIPCServer.Create;
+begin
+  inherited Create;
+
+  FFileHandle := feInvalidHandle;
+end;
+
+destructor TIPCServer.Destroy;
+begin
+  if FActive then
+    StopServer;
+
+  inherited Destroy;
+end;
+
+function TIPCServer.FindFirstRequest(out outFileName: string; out
+  outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+var
+  xRec: TRawByteSearchRec;
+  xDir: string;
+begin
+  outFileName := '';
+  outStream := nil;
+  outMsgType := -1;
+  outMsgLen := 0;
+  Result := False;
+  xDir := ExtractFilePath(FFileName);
+  if FindFirst(Format(FILE_POSTFIX, [FFileName])+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      outFileName := xDir+xRec.Name;
+      Result := CanReadMessage(outFileName, outStream, outMsgType, outMsgLen);
+    until Result or (FindNext(xRec) <> 0);
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.FindHighestPendingRequestId: string;
+var
+  xRec: TRawByteSearchRec;
+  xIdStart: Integer;
+  xMessageId,  xHighestId: LongInt;
+  xDir: string;
+begin
+  xHighestId := -1;
+  Result := '';
+  xIdStart := Length(Format(FILE_POSTFIX, [FServerName]))+1;
+  xDir := ExtractFilePath(FFileName);
+  if FindFirst(Format(FILE_POSTFIX, [FFileName])+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      xMessageId := StrToIntDef('$'+Copy(xRec.Name, xIdStart), -1);
+      if xMessageId > xHighestId then
+      begin
+        xHighestId := xMessageId;
+        Result := xDir+xRec.Name;
+      end;
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.GetPendingRequestCount: Integer;
+var
+  xRec: TRawByteSearchRec;
+begin
+  Result := 0;
+  if FindFirst(Format(FILE_POSTFIX, [FFileName])+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      Inc(Result);
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream;
+  var outMsgType: Integer): Boolean;
+var
+  xMsgFileName: string;
+begin
+  Result := PeekRequest(aStream, xMsgFileName{%H-}, outMsgType);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream;
+  var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+var
+  xMsgFileName: string;
+begin
+  Result := PeekRequest(aStream, xMsgFileName{%H-}, outMsgType, aTimeOut);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; var outMsgID: string;
+  var outMsgType: Integer): Boolean;
+var
+  xStream: TStream;
+  xMsgLen: Integer;
+begin
+  aStream.Size := 0;
+  outMsgID := '';
+  outMsgType := -1;
+  Result := FindFirstRequest(outMsgID, xStream, outMsgType, xMsgLen);
+  if Result then
+  begin
+    aStream.CopyFrom(xStream, xMsgLen);
+    aStream.Position := 0;
+    xStream.Free;
+    DeleteFile(outMsgID);
+  end;
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; var outMsgID: string;
+  var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+var
+  xStart: QWord;
+begin
+  Result := False;
+  xStart := GetTickCount64;
+  repeat
+    if PeekRequest(aStream, outMsgID, outMsgType) then
+      Exit(True)
+    else if aTimeOut > 20 then
+      Sleep(10);
+  until (GetTickCount64-xStart > aTimeOut);
+end;
+
+procedure TIPCServer.PostResponse(const aMsgID: string;
+  const aMsgType: Integer; const aStream: TStream);
+begin
+  DoPostMessage(Format(FILE_RESPONSE, [aMsgID]), aMsgType, aStream);
+end;
+
+function TIPCServer.StartServer(const aDeletePendingRequests: Boolean): Boolean;
+begin
+  FFileHandle := FileCreate(FFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
+  Result := (FFileHandle<>feInvalidHandle);
+  FActive := Result;
+  if Result and aDeletePendingRequests then
+    DeletePendingRequests;
+end;
+
+function TIPCServer.StopServer(const aDeletePendingRequests: Boolean): Boolean;
+begin
+  if not FActive then
+    Exit(True);
+
+  Result := CloseFileHandle(FFileName, FFileHandle);
+  if not Result then
+    Exit;
+
+  DeleteFile(FFileName);
+
+  if aDeletePendingRequests then
+    DeletePendingRequests;
+
+  FActive := False;
+end;
+
+end.
+
Index: ide/environmentopts.pp
===================================================================
--- ide/environmentopts.pp	(revision 49818)
+++ ide/environmentopts.pp	(working copy)
@@ -176,6 +176,16 @@
       'Never'
     );
 
+type
+  TIDEStartWithFilesOption = (isoStartNewInstance, isoOpenFilesInRunningInstance, isoPrompt, isoProjectPromptOtherInRunning);
+const
+  IDEStartWithFilesOptionNames: array[TIDEStartWithFilesOption] of string = (
+    'StartNewInstance',           // isoStartNewInstance
+    'OpenFilesInRunningInstance', // isoOpenFilesInRunningInstance
+    'Prompt',                     // isoPrompt
+    'ProjectPromptOtherInRunning' // isoProjectPromptOtherInRunning
+    );
+
   { Messages window }
 type
   TMsgWndFileNameStyle = (
@@ -475,6 +485,7 @@
     FRecentPackageFiles: TStringList;
     FMaxRecentPackageFiles: integer;
     FOpenLastProjectAtStart: boolean;
+    FOpenParamFilesInInstance: TIDEStartWithFilesOption;
     // Prevent repopulating Recent project files menu with example projects if it was already cleared up.
     FAlreadyPopulatedRecentFiles : Boolean;
 
@@ -728,6 +739,8 @@
     property LastOpenPackages: TLastOpenPackagesList read FLastOpenPackages;
     property OpenLastProjectAtStart: boolean read FOpenLastProjectAtStart
                                              write FOpenLastProjectAtStart;
+    property OpenParamFilesInInstance: TIDEStartWithFilesOption read FOpenParamFilesInInstance
+                                                                write FOpenParamFilesInInstance;
     property FileDialogFilter: string read FFileDialogFilter write FFileDialogFilter;
 
     // backup
@@ -802,6 +815,7 @@
 function CharCaseFileActionNameToType(const Action: string): TCharCaseFileAction;
 function UnitRenameReferencesActionNameToType(const Action: string): TUnitRenameReferencesAction;
 function StrToMsgWndFilenameStyle(const s: string): TMsgWndFileNameStyle;
+function StrToIDEStartWithFilesOption(const s: string): TIDEStartWithFilesOption;
 
 function SimpleDirectoryCheck(const OldDir, NewDir,
   NotFoundErrMsg: string; out StopChecking: boolean): boolean;
@@ -871,6 +885,13 @@
   Result:=mwfsShort;
 end;
 
+function StrToIDEStartWithFilesOption(const s: string): TIDEStartWithFilesOption;
+begin
+  for Result in TIDEStartWithFilesOption do
+    if CompareText(s,IDEStartWithFilesOptionNames[Result])=0 then exit;
+  Result:=isoStartNewInstance;
+end;
+
 function SimpleDirectoryCheck(const OldDir, NewDir,
   NotFoundErrMsg: string; out StopChecking: boolean): boolean;
 var
@@ -1308,6 +1329,7 @@
   FRecentPackageFiles:=TStringList.Create;
   FMaxRecentPackageFiles:=DefaultMaxRecentPackageFiles;
   FOpenLastProjectAtStart:=true;
+  FOpenParamFilesInInstance:=isoStartNewInstance;
 
   // backup
   with FBackupInfoProjectFiles do begin
@@ -1744,6 +1766,7 @@
       Path+'UnitRenameReferencesAction/Value',UnitRenameReferencesActionNames[urraAsk]));
     FAskForFilenameOnNewFile:=FXMLCfg.GetValue(Path+'AskForFilenameOnNewFile/Value',false);
     FLowercaseDefaultFilename:=FXMLCfg.GetValue(Path+'LowercaseDefaultFilename/Value',true);
+    FOpenParamFilesInInstance:=StrToIDEStartWithFilesOption(FXMLCfg.GetValue(Path+'OpenParamFilesInInstance/Value',''));
 
     // fpdoc
     FPDocPaths := FXMLCfg.GetValue(Path+'LazDoc/Paths','');
@@ -2051,6 +2074,11 @@
                              FAskForFilenameOnNewFile,false);
     FXMLCfg.SetDeleteValue(Path+'LowercaseDefaultFilename/Value',
                              FLowercaseDefaultFilename,true);
+    if FOpenParamFilesInInstance = isoStartNewInstance then
+      FXMLCfg.DeletePath(Path+'OpenParamFilesInInstance')
+    else
+      FXMLCfg.SetValue(Path+'OpenParamFilesInInstance/Value',IDEStartWithFilesOptionNames[FOpenParamFilesInInstance]);
+
     // fpdoc
     FXMLCfg.SetDeleteValue(Path+'LazDoc/Paths',FPDocPaths,'');
 
Index: ide/frames/files_options.lfm
===================================================================
--- ide/frames/files_options.lfm	(revision 49818)
+++ ide/frames/files_options.lfm	(working copy)
@@ -47,12 +47,12 @@
   end
   object ShowCompileDialogCheckBox: TCheckBox
     AnchorSideLeft.Control = Owner
-    AnchorSideTop.Control = OpenLastProjectAtStartCheckBox
+    AnchorSideTop.Control = OpenParamFilesInInstanceComboBox
     AnchorSideTop.Side = asrBottom
     AnchorSideRight.Side = asrBottom
     Left = 2
     Height = 19
-    Top = 69
+    Top = 94
     Width = 180
     BorderSpacing.Top = 2
     Caption = 'ShowCompileDialogCheckBox'
@@ -65,7 +65,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 117
+    Top = 142
     Width = 82
     BorderSpacing.Top = 10
     Caption = 'LazarusDirLabel'
@@ -80,7 +80,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 132
+    Top = 157
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -94,7 +94,7 @@
     AnchorSideRight.Control = LazarusDirButton
     Left = 2
     Height = 23
-    Top = 132
+    Top = 157
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -108,7 +108,7 @@
     AnchorSideRight.Control = CompilerPathButton
     Left = 2
     Height = 23
-    Top = 176
+    Top = 201
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -123,7 +123,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 176
+    Top = 201
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -136,7 +136,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 161
+    Top = 186
     Width = 101
     BorderSpacing.Top = 6
     Caption = 'CompilerPathLabel'
@@ -149,7 +149,7 @@
     AnchorSideRight.Control = FPCSourceDirButton
     Left = 2
     Height = 23
-    Top = 220
+    Top = 245
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -164,7 +164,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 220
+    Top = 245
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -177,7 +177,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 205
+    Top = 230
     Width = 100
     BorderSpacing.Top = 6
     Caption = 'FPCSourceDirLabel'
@@ -189,7 +189,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 249
+    Top = 274
     Width = 81
     BorderSpacing.Top = 6
     Caption = 'MakePathLabel'
@@ -201,7 +201,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 293
+    Top = 318
     Width = 91
     BorderSpacing.Top = 6
     Caption = 'TestBuildDirLabel'
@@ -214,7 +214,7 @@
     AnchorSideRight.Control = MakePathButton
     Left = 2
     Height = 23
-    Top = 264
+    Top = 289
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -229,7 +229,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 264
+    Top = 289
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -243,7 +243,7 @@
     AnchorSideRight.Control = TestBuildDirButton
     Left = 2
     Height = 23
-    Top = 308
+    Top = 333
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -258,7 +258,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 308
+    Top = 333
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -272,7 +272,7 @@
     AnchorSideRight.Side = asrBottom
     Left = 32
     Height = 19
-    Top = 88
+    Top = 113
     Width = 206
     BorderSpacing.Left = 30
     Caption = 'AutoCloseCompileDialogCheckBox'
@@ -284,7 +284,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 337
+    Top = 362
     Width = 153
     Alignment = taRightJustify
     BorderSpacing.Top = 6
@@ -301,7 +301,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 352
+    Top = 377
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -317,7 +317,7 @@
     AnchorSideRight.Control = CompilerTranslationFileButton
     Left = 2
     Height = 23
-    Top = 352
+    Top = 377
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -365,4 +365,27 @@
     BorderSpacing.Around = 2
     TabOrder = 16
   end
+  object OpenParamFilesInInstanceLabel: TLabel
+    AnchorSideLeft.Control = Owner
+    AnchorSideTop.Control = OpenParamFilesInInstanceComboBox
+    AnchorSideTop.Side = asrCenter
+    Left = 2
+    Height = 15
+    Top = 73
+    Width = 168
+    Caption = 'OpenParamFilesInInstanceLabel'
+    ParentColor = False
+  end
+  object OpenParamFilesInInstanceComboBox: TComboBox
+    AnchorSideLeft.Control = OpenParamFilesInInstanceLabel
+    AnchorSideLeft.Side = asrBottom
+    Left = 178
+    Height = 23
+    Top = 69
+    Width = 238
+    BorderSpacing.Left = 8
+    ItemHeight = 15
+    Style = csDropDownList
+    TabOrder = 17
+  end
 end
Index: ide/frames/files_options.pas
===================================================================
--- ide/frames/files_options.pas	(revision 49818)
+++ ide/frames/files_options.pas	(working copy)
@@ -40,6 +40,7 @@
 
   TFilesOptionsFrame = class(TAbstractIDEOptionsEditor)
     AutoCloseCompileDialogCheckBox: TCheckBox;
+    OpenParamFilesInInstanceComboBox: TComboBox;
     CompilerTranslationFileButton:TButton;
     CompilerTranslationFileComboBox:TComboBox;
     CompilerTranslationFileLabel:TLabel;
@@ -49,6 +50,7 @@
     FPCSourceDirButton:TButton;
     FPCSourceDirComboBox:TComboBox;
     FPCSourceDirLabel:TLabel;
+    OpenParamFilesInInstanceLabel: TLabel;
     lblCenter: TLabel;
     LazarusDirButton:TButton;
     LazarusDirComboBox:TComboBox;
@@ -229,6 +231,17 @@
     Add(ProgramDirectory(true));
     EndUpdate;
   end;
+  OpenParamFilesInInstanceLabel.Caption := dlgOpenParamFilesInInstance;
+  with OpenParamFilesInInstanceComboBox.Items do
+  begin
+    BeginUpdate;
+    Add(dlgOpenParamFilesInInstance_StartNewInstance);
+    Add(dlgOpenParamFilesInInstance_OpenFilesInRunningInstance);
+    Add(dlgOpenParamFilesInInstance_Prompt);
+    Add(dlgOpenParamFilesInInstance_ProjectPromptOtherInRunning);
+    EndUpdate;
+  end;
+  Assert(OpenParamFilesInInstanceComboBox.Items.Count = Ord(High(TIDEStartWithFilesOption))+1);
 
   CompilerPathLabel.Caption:=Format(dlgFpcExecutable,[GetDefaultCompilerFilename]);
   FPCSourceDirLabel.Caption:=dlgFpcSrcPath;
@@ -361,6 +374,8 @@
     // open last project at start
     OpenLastProjectAtStartCheckBox.Checked:=OpenLastProjectAtStart;
 
+    OpenParamFilesInInstanceComboBox.ItemIndex := Ord(OpenParamFilesInInstance);
+
     // compile dialog
     fOldShowCompileDialog:=ShowCompileDialog;
     ShowCompileDialogCheckBox.Checked:=ShowCompileDialog;
@@ -391,6 +406,7 @@
     MaxRecentOpenFiles := MaxRecentOpenFilesSpin.Value;
     MaxRecentProjectFiles := MaxRecentProjectFilesSpin.Value;
     OpenLastProjectAtStart:=OpenLastProjectAtStartCheckBox.Checked;
+    OpenParamFilesInInstance := TIDEStartWithFilesOption(OpenParamFilesInInstanceComboBox.ItemIndex);
     ShowCompileDialog := ShowCompileDialogCheckBox.Checked;
     AutoCloseCompileDialog := AutoCloseCompileDialogCheckBox.Checked;
   end;
Index: ide/idecmdline.pas
===================================================================
--- ide/idecmdline.pas	(revision 49818)
+++ ide/idecmdline.pas	(working copy)
@@ -50,6 +50,7 @@
   NoSplashScreenOptLong='--no-splash-screen';
   NoSplashScreenOptShort='--nsc';
   StartedByStartLazarusOpt='--started-by-startlazarus';
+  ForceNewInstanceOpt='--force-new-instance';
   SkipLastProjectOpt='--skip-last-project';
   DebugLogOpt='--debug-log=';
   DebugLogOptEnable='--debug-enable=';
Index: ide/ideinstances.pas
===================================================================
--- ide/ideinstances.pas	(nonexistent)
+++ ide/ideinstances.pas	(working copy)
@@ -0,0 +1,783 @@
+{
+ /***************************************************************************
+                              ideinstances.pas
+                              ----------------
+
+ ***************************************************************************/
+
+ ***************************************************************************
+ *                                                                         *
+ *   This source is free software; you can redistribute it and/or modify   *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This code is distributed in the hope that it will be useful, but      *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   General Public License for more details.                              *
+ *                                                                         *
+ *   A copy of the GNU General Public License is available on the World    *
+ *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
+ *   obtain it by writing to the Free Software Foundation,                 *
+ *   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
+ *                                                                         *
+ ***************************************************************************
+
+  Author: Ondrej Pokorny
+
+  Abstract:
+    This unit handles one/multiple Lazarus IDE instances.
+
+}
+unit IDEInstances;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  sysutils, Interfaces, Classes, Controls, Forms, Dialogs, ExtCtrls,
+  LCLProc, LCLIntf, LCLType, AdvancedIPC,
+  LazFileUtils, LazUTF8, Laz2_DOM, laz2_XMLRead, laz2_XMLWrite,
+  LazarusIDEStrConsts, IDECmdLine;
+
+type
+  TIDEStartWithFilesAction = (isaStartNewInstance, isaOpenFilesInRunningInstance, isaPrompt, isaModalError);
+  TChooseActionEvent = procedure(
+    const aFiles: TStrings;
+    var Result: TIDEStartWithFilesAction;
+    var outPromptMessage: string;
+    var outOpenNewInstanceMessage: string) of object;//we want to receive translated outPromptMessage and outOpenNewInstanceMessage -> do not read them directly from LazarusIDEStrConsts here
+  TOpenFilesResult = (ofrStartNewInstance, ofrDoNotStart, ofrModalError);
+  TOpenFilesEvent = procedure(const aFiles: TStrings;
+    var outResult: TOpenFilesResult; var outHandleBringToFront: HWND) of object;
+
+  TMessageParam = record
+    Name: string;
+    Value: string;
+  end;
+  TMessageParams = array of TMessageParam;
+
+  TUniqueServer = class(TIPCServer)
+  public
+    procedure StartUnique(const aServerPrefix: string);
+  end;
+
+  TMainServer = class(TUniqueServer)
+  private
+    FOpenFilesEvent: TOpenFilesEvent;
+    FChooseActionEvent: TChooseActionEvent;
+    FTimer: TTimer;
+    FMsgStream: TMemoryStream;
+
+    procedure DoChooseActionEvent(const aMsgID: string; const aInParams: TMessageParams);
+    procedure DoOpenFiles(const aMsgID: string; const aInParams: TMessageParams);
+
+    procedure SimpleResponse(
+      const aResponseToMsgID, aResponseType: string;
+      const aParams: array of TMessageParam);
+
+    procedure DoCheckMessages;
+    procedure CheckMessagesOnTimer(Sender: TObject);
+
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+  end;
+
+  TResponseClient = class(TIPCClient)
+  public
+    function SendFilesToRunningInstance(
+      const aFiles: TStrings; var outOpenNewInstanceMessage: string;
+      var outHandleBringToFront: HWND): TOpenFilesResult;
+  end;
+
+  TIDEInstances = class
+  private
+    FMainServer: TMainServer;//running IDE
+    FStartIDE: Boolean;// = True;
+    FForceNewInstance: Boolean;
+    FAllowOpenLastProject: Boolean;// = True;
+    FFilesToOpen: TStrings;
+
+    class procedure AddFilesToParams(const aFiles: TStrings;
+      var ioParams: TMessageParams); static;
+    class procedure AddFilesFromParams(const aInParams: TMessageParams;
+      const aFiles: TStrings); static;
+    class procedure BuildMessage(const aMessageType: string;
+      const aParams: array of TMessageParam; const aStream: TStream); static;
+    class function MessageParam(const aName, aValue: string): TMessageParam; static;
+    class function ParseMessage(const aStream: TStream; out outMessageType: string;
+      out outParams: TMessageParams): Boolean; static;
+    class function GetMessageParam(const aParams: array of TMessageParam;
+      const aParamName: string): string; static;
+
+    function CheckParamsForForceNewInstanceOpt: Boolean;
+
+    procedure CollectFiles(out
+      outFilesWereSentToCollectingServer: Boolean);
+
+    function SendFilesToRunningInstance(const aFiles: TStrings;
+      var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND): TOpenFilesResult;
+    procedure InitIDEInstances;
+  public
+    constructor Create;
+    destructor Destroy; override;
+  public
+    procedure PerformCheck;//call PerformCheck after Application.Initialize - it can open dialogs!
+
+    procedure StartListening(const aOpenFilesEvent: TOpenFilesEvent;
+      const aChooseActionEvent: TChooseActionEvent);
+    procedure StopListening;
+    function StartIDE: Boolean;//can the IDE be started?
+    function AllowOpenLastProject: Boolean;//if a secondary IDE is starting, do NOT reopen last project!
+    function FilesToOpen: TStrings;
+    procedure DeleteCollectMessageFiles;
+  end;
+
+function LazIDEInstances: TIDEInstances;
+
+implementation
+
+const
+  SERVERPREFIX_MAIN = 'LazarusMain';
+  SERVERNAME_COLLECT = 'LazarusCollect';
+  MESSAGETYPE_XML = 2;
+  ELEMENT_ROOT = 'ideinstances';
+  ATTR_VALUE = 'value';
+  ATTR_MESSAGE_TYPE = 'msgtype';
+  MESSAGE_CHOOSEACTION = 'chooseaction';
+  RESPONSE_SHOWPROMPT = 'showprompt';
+  MESSAGE_OPENFILES = 'openfiles';
+  MESSAGE_COLLECTFILES = 'collectfiles';
+  RESPONSE_STARTNEWINSTANCE = 'startnewinstance';
+  PARAM_FILE = 'file';
+  PARAM_RESULT = 'result';
+  PARAM_HANDLEBRINGTOFRONT = 'handlebringtofront';
+  PARAM_PROMPTMESSAGE = 'promptmessage';
+  PARAM_OPENNEWINSTANCEMESSAGE = 'opennewinstancemessage';
+  MAX_CHECK_INSTANCES = 10;//check maximum count of continuously started instances
+  TIMEOUT_COLLECTFILES = 100;
+  TIMEOUT_SHOWPROMPT = 500;//first timeout is 1/2 second, we should get an answer fast
+  TIMEOUT_STARTNEWINSTANCE = 3000;//we know that Lazarus is already running and responding so we get an answer, the UnHide process could take some time, let's wait for it max 3 seconds
+
+var
+  FLazIDEInstances: TIDEInstances;
+
+function LazIDEInstances: TIDEInstances;
+begin
+  Result := FLazIDEInstances;
+end;
+
+{ TIDEInstances }
+
+class function TIDEInstances.MessageParam(const aName, aValue: string): TMessageParam;
+begin
+  Result.Name := aName;
+  Result.Value := aValue;
+end;
+
+function TIDEInstances.StartIDE: Boolean;
+begin
+  Result := FStartIDE;
+end;
+
+function TIDEInstances.AllowOpenLastProject: Boolean;
+begin
+  Result := FAllowOpenLastProject;
+end;
+
+function TIDEInstances.FilesToOpen: TStrings;
+begin
+  if not Assigned(FFilesToOpen) then
+    FFilesToOpen := TStringList.Create;
+  Result := FFilesToOpen;
+end;
+
+procedure TIDEInstances.StartListening(const aOpenFilesEvent: TOpenFilesEvent;
+  const aChooseActionEvent: TChooseActionEvent);
+begin
+  Assert(Assigned(aOpenFilesEvent) and Assigned(aChooseActionEvent));
+
+  if not Assigned(FMainServer) then
+  begin
+    FMainServer := TMainServer.Create;
+    FMainServer.StartUnique(SERVERPREFIX_MAIN);
+  end;
+  FMainServer.FOpenFilesEvent := aOpenFilesEvent;
+  FMainServer.FChooseActionEvent := aChooseActionEvent;
+
+  DeleteCollectMessageFiles;
+end;
+
+procedure TIDEInstances.StopListening;
+begin
+  FreeAndNil(FMainServer);
+end;
+
+class procedure TIDEInstances.AddFilesFromParams(
+  const aInParams: TMessageParams; const aFiles: TStrings);
+var
+  I: Integer;
+begin
+  //do not clear aFiles
+  for I := Low(aInParams) to High(aInParams) do
+    if aInParams[I].Name = PARAM_FILE then
+      aFiles.Add(aInParams[I].Value);
+end;
+
+class procedure TIDEInstances.AddFilesToParams(const aFiles: TStrings;
+  var ioParams: TMessageParams);
+var
+  xStartIndex: Integer;
+  I: Integer;
+begin
+  xStartIndex := Length(ioParams);
+  SetLength(ioParams, xStartIndex+aFiles.Count);
+  for I := 0 to aFiles.Count-1 do
+    ioParams[xStartIndex+I] := MessageParam(PARAM_FILE, aFiles[I]);
+end;
+
+class function TIDEInstances.GetMessageParam(
+  const aParams: array of TMessageParam; const aParamName: string): string;
+var
+  I: Integer;
+begin
+  for I := 0 to Length(aParams) do
+  if aParams[I].Name = aParamName then
+    Exit(aParams[I].Value);
+
+  Result := '';//not found
+end;
+
+class procedure TIDEInstances.BuildMessage(const aMessageType: string;
+  const aParams: array of TMessageParam; const aStream: TStream);
+var
+  xDOM: TXMLDocument;
+  xRoot: TDOMElement;
+  xParam: TDOMElement;
+  I: Integer;
+begin
+  xDOM := TXMLDocument.Create;
+  try
+    xRoot := xDOM.CreateElement(ELEMENT_ROOT);
+    xRoot.AttribStrings[ATTR_MESSAGE_TYPE] := aMessageType;
+    xDOM.AppendChild(xRoot);
+
+    for I := Low(aParams) to High(aParams) do
+    begin
+      xParam := xDOM.CreateElement(aParams[I].Name);
+      xRoot.AppendChild(xParam);
+      xParam.AttribStrings[ATTR_VALUE] := aParams[I].Value;
+    end;
+
+    WriteXMLFile(xDOM, aStream);
+  finally
+    xDOM.Free;
+  end;
+end;
+
+class function TIDEInstances.ParseMessage(const aStream: TStream; out
+  outMessageType: string; out outParams: TMessageParams): Boolean;
+var
+  xDOM: TXMLDocument;
+  xChildList: TDOMNodeList;
+  I, J: Integer;
+begin
+  Result := False;
+
+  outMessageType := '';
+  SetLength(outParams, 0);
+  try
+    ReadXMLFile(xDOM, aStream, []);
+  except
+    on EXMLReadError do
+      Exit;//eat XML exceptions
+  end;
+  try
+    if (xDOM = nil) or (xDOM.DocumentElement = nil) or (xDOM.DocumentElement.NodeName <> ELEMENT_ROOT) then
+      Exit;
+
+    outMessageType := xDOM.DocumentElement.AttribStrings[ATTR_MESSAGE_TYPE];
+
+    xChildList := xDOM.DocumentElement.ChildNodes;
+    SetLength(outParams, xChildList.Count);
+    J := 0;
+    for I := 0 to xChildList.Count-1 do
+    if xChildList[I] is TDOMElement then
+    begin
+      outParams[J].Name := xChildList[I].NodeName;
+      outParams[J].Value := TDOMElement(xChildList[I]).AttribStrings[ATTR_VALUE];
+      Inc(J);
+    end;
+    SetLength(outParams, J);
+    Result := True;
+  finally
+    xDOM.Free;
+  end;
+end;
+
+function TIDEInstances.SendFilesToRunningInstance(const aFiles: TStrings;
+  var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND
+  ): TOpenFilesResult;
+var
+  xStartClient: TResponseClient;
+  I: Integer;
+begin
+  Result := ofrStartNewInstance;
+  xStartClient := TResponseClient.Create;
+  try
+    for I := 1 to MAX_CHECK_INSTANCES do//check for multiple instances
+    begin
+      xStartClient.ServerName := SERVERPREFIX_MAIN+IntToStr(I);
+      if xStartClient.ServerRunning then
+      begin
+        //there are open Lazarus instances, do not reopen last project!
+        FAllowOpenLastProject := False;
+        Result := xStartClient.SendFilesToRunningInstance(aFiles, outOpenNewInstanceMessage, outHandleBringToFront);
+        if Result <> ofrModalError then//if the current IDE is modal, try another one
+          Exit;//handle only one running Lazarus IDE
+      end;
+    end;
+  finally
+    xStartClient.Free;
+  end;
+end;
+
+function TIDEInstances.CheckParamsForForceNewInstanceOpt: Boolean;
+var
+  I: Integer;
+begin
+  Result := False;
+  for I := 1 to ParamsAndCfgCount do
+    if ParamIsOption(i, ForceNewInstanceOpt) then//ignore the settings and start new Lazarus IDE instance
+      Result := True;
+end;
+
+procedure TIDEInstances.DeleteCollectMessageFiles;
+var
+  xServer: TIPCServer;
+begin
+  xServer := TIPCServer.Create;
+  try
+    xServer.ServerName := SERVERNAME_COLLECT;
+    xServer.DeletePendingRequests;
+  finally
+    xServer.Free;
+  end;
+end;
+
+procedure TIDEInstances.PerformCheck;
+var
+  xResult: TOpenFilesResult;
+  xOpenNewInstanceMessage: string = '';
+  xHandleBringToFront: HWND = 0;
+begin
+  if not FStartIDE then//InitIDEInstances->CollectOtherOpeningFiles decided not to start the IDE
+    Exit;
+
+  if not FForceNewInstance and (FilesToOpen.Count > 0) then
+    xResult := SendFilesToRunningInstance(FilesToOpen, xOpenNewInstanceMessage, xHandleBringToFront)
+  else
+    xResult := ofrStartNewInstance;
+
+  if xOpenNewInstanceMessage = '' then
+    xOpenNewInstanceMessage := dlgRunningInstanceModalOpenNew;
+  FStartIDE :=
+    (xResult = ofrStartNewInstance) or
+      ((xResult = ofrModalError) and
+       (MessageDlg(lisLazarusIDE, Format(xOpenNewInstanceMessage, [FilesToOpen.Text]), mtWarning, mbYesNo, 0, mbYes) = mrYes));//user decided to open in new ide
+
+  {$IFDEF MSWINDOWS}
+  if not FStartIDE and (xHandleBringToFront <> 0) then
+  begin
+    try
+      SetForegroundWindow(xHandleBringToFront);//SetForegroundWindow works (on Windows) only if the calling process is the foreground process, therefore it must be here!
+    except
+      //eat all widget exceptions
+    end;
+  end;
+  {$ENDIF}
+end;
+
+constructor TIDEInstances.Create;
+begin
+  inherited Create;
+
+  FStartIDE := True;
+  FAllowOpenLastProject := True;
+end;
+
+destructor TIDEInstances.Destroy;
+begin
+  FreeAndNil(FMainServer);
+  FreeAndNil(FFilesToOpen);
+
+  inherited Destroy;
+end;
+
+procedure TIDEInstances.CollectFiles(out
+  outFilesWereSentToCollectingServer: Boolean);
+
+var
+  xThisClientMessageId: string;
+
+  procedure _SendToServer;
+  var
+    xClient: TIPCClient;
+    xOutParams: TMessageParams;
+    xStream: TMemoryStream;
+  begin
+    xClient := TIPCClient.Create;
+    try
+      xClient.ServerName := SERVERNAME_COLLECT;
+
+      SetLength(xOutParams, 0);
+      AddFilesToParams(FilesToOpen, xOutParams);
+
+      xStream := TMemoryStream.Create;
+      try
+        BuildMessage(MESSAGE_COLLECTFILES, xOutParams, xStream);
+        xStream.Position := 0;
+        xClient.PostRequest(MESSAGETYPE_XML, xStream, xThisClientMessageId);
+      finally
+        xStream.Free;
+      end;
+    finally
+      xClient.Free;
+    end;
+  end;
+
+  procedure _WaitForFiles;
+  var
+    xLastCount, xNewCount: Integer;
+    xServer: TIPCServer;
+  begin
+    xServer := TIPCServer.Create;
+    try
+      xServer.ServerName := SERVERNAME_COLLECT;
+      //do not start server here
+      xLastCount := -1;
+      xNewCount := xServer.GetPendingRequestCount;
+      while xLastCount <> xNewCount do
+      begin
+        xLastCount := xNewCount;
+        Sleep(TIMEOUT_COLLECTFILES);
+        xNewCount := xServer.GetPendingRequestCount;
+      end;
+    finally
+      xServer.Free;
+    end;
+  end;
+
+  function _ReceiveAsServer: Boolean;
+  var
+    xServer: TIPCServer;
+    xInParams: TMessageParams;
+    xStream: TMemoryStream;
+    xMsgType: Integer;
+    xMessageType: string;
+  begin
+    xStream := TMemoryStream.Create;
+    xServer := TIPCServer.Create;
+    try
+      xServer.ServerName := SERVERNAME_COLLECT;
+      //files have to be handled only by one instance!
+      Result := xServer.FindHighestPendingRequestId = xThisClientMessageId;
+      if Result then
+      begin
+        //we are the highest client, handle the files
+        xServer.StartServer(False);
+      end else
+      begin
+        //we are not the highest client, maybe there are pending files, check that
+        {$IFNDEF MSWINDOWS}
+        //this code is not slowing up IDE start because if there was highest client found (the normal way), we close anyway
+        Randomize;
+        Sleep(50+Random(50));//random sleep in order to prevent double file locks on linux
+        {$ENDIF}
+        if not (xServer.StartServer(False) and (xServer.GetPendingRequestCount > 0)) then
+          Exit;//server is already running or there are no pending message -> close
+        Result := True;//no one handled handled the files, do it by myself
+      end;
+
+      FilesToOpen.Clear;
+      while xServer.PeekRequest(xStream, xMsgType{%H-}) do
+      if xMsgType = MESSAGETYPE_XML then
+      begin
+        if ParseMessage(xStream, xMessageType, xInParams) and
+           (xMessageType = MESSAGE_COLLECTFILES)
+        then
+          AddFilesFromParams(xInParams, FilesToOpen);
+      end;
+    finally
+      xStream.Free;
+      xServer.Free;
+    end;
+  end;
+begin
+  //if you select more files in explorer and open them, they are not opened in one process but one process is started per file
+  // -> collect them
+
+  //first send messages to queue (there is no server, no problem, it will collect the messages when it is created)
+  _SendToServer;
+
+  //now wait until we have everything
+  _WaitForFiles;
+
+  //now send them to one instance
+  outFilesWereSentToCollectingServer := not _ReceiveAsServer;
+end;
+
+procedure TIDEInstances.InitIDEInstances;
+var
+  xFilesWereSentToCollectingServer: Boolean;
+begin
+  FForceNewInstance := CheckParamsForForceNewInstanceOpt;
+
+  //get cmd line filenames
+  FFilesToOpen := ExtractCmdLineFilenames;
+
+  if FilesToOpen.Count > 0 then//if there are file in the cmd, check for multiple starting instances
+  begin
+    CollectFiles(xFilesWereSentToCollectingServer);
+    if xFilesWereSentToCollectingServer then
+    begin
+      FilesToOpen.Clear;
+      FStartIDE := False;
+    end;
+  end;
+end;
+
+{ TUniqueServer }
+
+procedure TUniqueServer.StartUnique(const aServerPrefix: string);
+var
+  I: Integer;
+begin
+  if Active then
+    StopServer;
+
+  I := 0;
+  while not Active do
+  begin
+    Inc(I);
+    ServerName := aServerPrefix+IntToStr(I);
+    StartServer;
+  end;
+end;
+
+{ TResponseClient }
+
+function TResponseClient.SendFilesToRunningInstance(const aFiles: TStrings;
+  var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND
+  ): TOpenFilesResult;
+var
+  xStream: TMemoryStream;
+  xMsgType: Integer;
+  xResponseType: string;
+  xOutParams, xInParams: TMessageParams;
+  xChooseAction: TIDEStartWithFilesAction;
+  xPromptMessage: string;
+begin
+  Result := ofrStartNewInstance;
+  xStream := TMemoryStream.Create;
+  try
+    //ask to show prompt
+    xChooseAction := isaStartNewInstance;
+    SetLength(xOutParams, 0);
+    TIDEInstances.AddFilesToParams(aFiles, xOutParams);
+    TIDEInstances.BuildMessage(MESSAGE_CHOOSEACTION, xOutParams, xStream);
+    xStream.Position := 0;
+    PostRequest(MESSAGETYPE_XML, xStream);
+    xStream.Clear;
+    if PeekResponse(xStream, xMsgType{%H-}, TIMEOUT_SHOWPROMPT) and
+       (xMsgType = MESSAGETYPE_XML) then
+    begin
+      xStream.Position := 0;
+      if TIDEInstances.ParseMessage(xStream, xResponseType, xInParams) and
+         (xResponseType = RESPONSE_SHOWPROMPT) then
+      begin
+        xChooseAction := TIDEStartWithFilesAction(StrToIntDef(TIDEInstances.GetMessageParam(xInParams, PARAM_RESULT), 0));
+        outOpenNewInstanceMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_OPENNEWINSTANCEMESSAGE);
+        xPromptMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_PROMPTMESSAGE);
+        if xChooseAction = isaModalError then
+          Exit(ofrModalError);
+      end;
+    end else//no response, the IDE is modal and cannot accept messages
+    begin
+      DeleteRequest;
+      Exit(ofrModalError);
+    end;
+
+    case xChooseAction of
+      isaPrompt:
+      begin
+        if xPromptMessage = '' then
+          xPromptMessage := dlgOpenInRunningInstance;
+        case MessageDlg(lisLazarusIDE, Format(xPromptMessage, [aFiles.Text]), mtConfirmation, mbYesNo, 0, mbYes) of
+          mrYes: begin end;//user hit "yes" -> proceed
+          mrNo: Exit(ofrStartNewInstance);//user hit "no" -> open new instance
+        else//cancel/close -> do nothing, do not open IDE
+          Exit(ofrDoNotStart);
+        end;
+      end;
+      isaStartNewInstance://settings is startnewide -> open new instance
+        Exit(ofrStartNewInstance);
+    end;
+
+    //open files in new instance
+    xStream.Clear;
+    TIDEInstances.BuildMessage(MESSAGE_OPENFILES, xOutParams, xStream);
+    xStream.Position := 0;
+    Self.PostRequest(MESSAGETYPE_XML, xStream);
+    xStream.Clear;
+    if PeekResponse(xStream, xMsgType, TIMEOUT_STARTNEWINSTANCE) and
+       (xMsgType = MESSAGETYPE_XML) then
+    begin
+      xStream.Position := 0;
+      if TIDEInstances.ParseMessage(xStream, xResponseType, xInParams) and
+         (xResponseType = RESPONSE_STARTNEWINSTANCE) then
+      begin
+        Result := TOpenFilesResult(StrToIntDef(TIDEInstances.GetMessageParam(xInParams, PARAM_RESULT), 0));
+        outHandleBringToFront := StrToInt64Def(TIDEInstances.GetMessageParam(xInParams, PARAM_HANDLEBRINGTOFRONT), 0);
+      end;
+    end else//no response, the IDE is modal and cannot accept messages
+    begin
+      DeleteRequest;
+      Exit(ofrModalError);
+    end;
+  finally
+    xStream.Free;
+  end;
+end;
+
+{ TMainServer }
+
+procedure TMainServer.CheckMessagesOnTimer(Sender: TObject);
+begin
+  DoCheckMessages;
+end;
+
+constructor TMainServer.Create;
+begin
+  inherited Create;
+
+  FMsgStream := TMemoryStream.Create;
+  FTimer := TTimer.Create(nil);
+  FTimer.OnTimer := @CheckMessagesOnTimer;
+  FTimer.Interval := 50;
+  FTimer.Enabled := True;
+end;
+
+destructor TMainServer.Destroy;
+begin
+  FMsgStream.Free;
+  FTimer.Free;//must free manually before inherited Destroy
+
+  inherited Destroy;
+end;
+
+procedure TMainServer.DoChooseActionEvent(const aMsgID: string;
+  const aInParams: TMessageParams);
+var
+  xResult: TIDEStartWithFilesAction;
+  xParams: TMessageParams;
+  xPromptMessage, xOpenNewInstanceMessage: string;
+  xFiles: TStringList;
+begin
+  xResult := isaStartNewInstance;
+  xPromptMessage := '';
+  xOpenNewInstanceMessage := '';
+  if Assigned(FChooseActionEvent) then
+  begin
+    xFiles := TStringList.Create;
+    try
+      TIDEInstances.AddFilesFromParams(aInParams, xFiles);
+      FChooseActionEvent(xFiles, xResult, xPromptMessage, xOpenNewInstanceMessage);
+    finally
+      xFiles.Free;
+    end;
+  end;
+
+  SetLength(xParams, 3);
+  xParams[0] := TIDEInstances.MessageParam(PARAM_RESULT, IntToStr(Ord(xResult)));
+  xParams[1] := TIDEInstances.MessageParam(PARAM_PROMPTMESSAGE, xPromptMessage);
+  xParams[2] := TIDEInstances.MessageParam(PARAM_OPENNEWINSTANCEMESSAGE, xOpenNewInstanceMessage);
+  SimpleResponse(aMsgID, RESPONSE_SHOWPROMPT, xParams);
+end;
+
+procedure TMainServer.DoOpenFiles(const aMsgID: string;
+  const aInParams: TMessageParams);
+var
+  xResult: TOpenFilesResult;
+  xHandleBringToFront: HWND;
+  xFiles: TStrings;
+  xParams: TMessageParams;
+begin
+  xResult := ofrStartNewInstance;
+  xHandleBringToFront := 0;
+  if Assigned(FOpenFilesEvent) then
+  begin
+    xFiles := TStringList.Create;
+    try
+      TIDEInstances.AddFilesFromParams(aInParams, xFiles);
+      FOpenFilesEvent(xFiles, xResult, xHandleBringToFront);
+    finally
+      xFiles.Free;
+    end;
+  end;
+
+  SetLength(xParams, 2);
+  xParams[0] := TIDEInstances.MessageParam(PARAM_RESULT, IntToStr(Ord(xResult)));
+  xParams[1] := TIDEInstances.MessageParam(PARAM_HANDLEBRINGTOFRONT, IntToStr(xHandleBringToFront));
+  SimpleResponse(aMsgID, RESPONSE_STARTNEWINSTANCE, xParams);
+end;
+
+procedure TMainServer.SimpleResponse(const aResponseToMsgID,
+  aResponseType: string; const aParams: array of TMessageParam);
+var
+  xStream: TMemoryStream;
+begin
+  xStream := TMemoryStream.Create;
+  try
+    TIDEInstances.BuildMessage(aResponseType, aParams, xStream);
+    xStream.Position := 0;
+    PostResponse(aResponseToMsgID, MESSAGETYPE_XML, xStream);
+  finally
+    xStream.Free;
+  end;
+end;
+
+procedure TMainServer.DoCheckMessages;
+var
+  xMessageType: string;
+  xParams: TMessageParams;
+  xMsgID: string;
+  xMsgType: Integer;
+begin
+  if Active then
+  begin
+    if PeekRequest(FMsgStream, xMsgID{%H-}, xMsgType{%H-}) and
+       (xMsgType = MESSAGETYPE_XML) then
+    begin
+      if TIDEInstances.ParseMessage(FMsgStream, xMessageType, xParams) and
+         (xMessageType = MESSAGE_CHOOSEACTION)
+      then
+        DoChooseActionEvent(xMsgID, xParams)
+      else
+      if xMessageType = MESSAGE_OPENFILES then
+        DoOpenFiles(xMsgID, xParams);
+    end;
+  end;
+end;
+
+initialization
+  FLazIDEInstances := TIDEInstances.Create;
+  FLazIDEInstances.InitIDEInstances;
+
+finalization
+  FreeAndNil(FLazIDEInstances);
+
+end.
Index: ide/lazarus.pp
===================================================================
--- ide/lazarus.pp	(revision 49818)
+++ ide/lazarus.pp	(working copy)
@@ -52,6 +52,7 @@
   {$IFEND}
   SysUtils,
   Interfaces,
+  IDEInstances,//keep up so that it will be initialized soon
   Forms, LCLProc,
   IDEOptionsIntf,
   LazConf, IDEGuiCmdLine,
@@ -104,6 +105,9 @@
   {$ENDIF}
 
   Application.Initialize;
+  LazIDEInstances.PerformCheck;
+  if not LazIDEInstances.StartIDE then
+    Exit;
   TMainIDE.ParseCmdLineOptions;
   if not SetupMainIDEInstance then exit;
   if Application.Terminated then exit;
Index: ide/lazarusidestrconsts.pas
===================================================================
--- ide/lazarusidestrconsts.pas	(revision 49818)
+++ ide/lazarusidestrconsts.pas	(working copy)
@@ -142,6 +142,14 @@
   lisMoveFiles2 = 'Move files?';
   lrsPLDDeleteSelected = 'Delete selected';
 
+  dlgOpenParamFilesInInstance = 'If second Lazarus IDE is started with files';
+  dlgOpenParamFilesInInstance_StartNewInstance = 'start new Lazarus instance';
+  dlgOpenParamFilesInInstance_OpenFilesInRunningInstance = 'open them in running instance';
+  dlgOpenParamFilesInInstance_Prompt = 'ask what to do';
+  dlgOpenParamFilesInInstance_ProjectPromptOtherInRunning = 'projects: ask; other: open in running';
+  dlgOpenInRunningInstance = 'Do you want to open the following file(s) in running Lazarus instance?'+sLineBreak+sLineBreak+'%s';
+  dlgRunningInstanceModalOpenNew = 'The running Lazarus instance cannot accept any files.'+sLineBreak+'Do you want to open them in a new IDE instance?'+sLineBreak+sLineBreak+'%s';
+
   // *** Rest of the resource strings ***
 
   lisImportPackageListXml = 'Import package list (*.xml)';
Index: ide/lazarusmanager.pas
===================================================================
--- ide/lazarusmanager.pas	(revision 49818)
+++ ide/lazarusmanager.pas	(working copy)
@@ -92,8 +92,8 @@
   BaseUnix,
 {$ENDIF}
   Classes, SysUtils, Process, Forms, Controls, Dialogs, LCLProc,
-  UTF8Process, FileUtil, LazFileUtils, LazUTF8, FileProcs,
-  IDECmdLine, LazConf, Splash, BaseIDEIntf;
+  UTF8Process, FileUtil, FileProcs, LazUTF8, LazFileUtils,
+  IDECmdLine, LazConf, Splash, BaseIDEIntf, IDEInstances;
   
 type
 
@@ -244,6 +244,15 @@
   if FShowSplashOption then
     ShowSplash;
 
+  // we already handled IDEInstances, ignore it in lazarus EXE
+  if (FCmdLineParams.IndexOf(ForceNewInstanceOpt) = -1) then
+    FCmdLineParams.Add(ForceNewInstanceOpt);
+  // pass the AllowOpenLastProject parameter to lazarus EXE
+  if not LazIDEInstances.AllowOpenLastProject and
+     (FCmdLineParams.IndexOf(SkipLastProjectOpt) = -1)
+  then
+    FCmdLineParams.Add(SkipLastProjectOpt);
+
   // set primary config path
   PCP:=ExtractPrimaryConfigPath(FCmdLineParams);
   if PCP<>'' then
@@ -250,7 +259,7 @@
     SetPrimaryConfigPath(PCP);
 
   // get command line files
-  CmdLineFiles := ExtractCmdLineFilenames;
+  CmdLineFiles := LazIDEInstances.FilesToOpen;
   if CmdLineFiles<>nil then
   begin
     for i := 0 to CmdLineFiles.Count-1 do
Index: ide/main.pp
===================================================================
--- ide/main.pp	(revision 49818)
+++ ide/main.pp	(working copy)
@@ -157,6 +157,7 @@
   CleanDirDlg, CodeContextForm, AboutFrm, CompatibilityRestrictions,
   RestrictionBrowser, ProjectWizardDlg, IDECmdLine, IDEGuiCmdLine, CodeExplOpts,
   EditorMacroListViewer, SourceFileManager, EditorToolbarStatic,
+  IDEInstances,
   // main ide
   MainBar, MainIntf, MainBase;
 
@@ -187,6 +188,11 @@
     procedure HandleRemoteControlTimer(Sender: TObject);
     procedure HandleSelectFrame(Sender: TObject; var AComponentClass: TComponentClass);
     procedure OIChangedTimerTimer(Sender: TObject);
+    procedure NewInstanceChooseAction(const aFiles: TStrings;
+      var Result: TIDEStartWithFilesAction;
+      var outPromptMessage, outOpenNewInstanceMessage: string);
+    procedure NewInstanceOpenFiles(const aFiles: TStrings;
+      var Result: TOpenFilesResult; var outHandleBringToFront: HWND);
   public
     // file menu
     procedure mnuNewUnitClicked(Sender: TObject);
@@ -637,6 +643,7 @@
     OldCompilerFilename, OldLanguage: String;
     OIChangedTimer: TIdleTimer;
 
+    procedure DoDropFilesAsync(Data: PtrInt);
     procedure RenameInheritedMethods(AnUnitInfo: TUnitInfo; List: TStrings);
     function OIHelpProvider: TAbstractIDEHTMLProvider;
     // form editor and designer
@@ -950,6 +957,14 @@
   StartedByStartLazarus: boolean = false;
   ShowSetupDialog: boolean = false;
 
+type
+  TDoDropFilesAsyncParams = class(TComponent)
+  public
+    FileNames: array of string;
+    WindowIndex: Integer;
+    BringToFront: Boolean;
+  end;
+
 function FindDesignComponent(const aName: string): TComponent;
 var
   AnUnitInfo: TUnitInfo;
@@ -1556,6 +1571,7 @@
   DoShowMessagesView(false);           // reopen extra windows
   fUserInputSinceLastIdle:=true; // Idle work gets done initially before user action.
   MainIDEBar.ApplicationIsActivate:=true;
+  LazIDEInstances.StartListening(@NewInstanceOpenFiles, @NewInstanceChooseAction);
   FIDEStarted:=true;
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.StartIDE END');{$ENDIF}
 end;
@@ -1946,6 +1962,7 @@
 procedure TMainIDE.MainIDEFormClose(Sender: TObject;
   var CloseAction: TCloseAction);
 begin
+  LazIDEInstances.StopListening;
   DoCallNotifyHandler(lihtIDEClose);
   SaveEnvironment(true);
   if IDEDockMaster<>nil then
@@ -2182,119 +2199,116 @@
   {$ENDIF}
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject A');{$ENDIF}
   // load command line project or last project or create a new project
-  CmdLineFiles:=ExtractCmdLineFilenames;
-  try
-    ProjectLoaded:=false;
+  CmdLineFiles:=LazIDEInstances.FilesToOpen;
+  ProjectLoaded:=false;
 
-    // try command line project
-    if (CmdLineFiles<>nil) and (CmdLineFiles.Count>0) then begin
-      AProjectFilename:=CmdLineFiles[0];
-      if (CompareFileExt(AProjectFilename,'.lpr',false)=0) then
-        AProjectFilename:=ChangeFileExt(AProjectFilename,'.lpi');
-      // only try to load .lpi files, other files are loaded later
-      if (CompareFileExt(AProjectFilename,'.lpi',false)=0) then begin
-        AProjectFilename:=CleanAndExpandFilename(AProjectFilename);
-        if FileExistsUTF8(AProjectFilename) then begin
-          CmdLineFiles.Delete(0);
-          ProjectLoaded:=(DoOpenProjectFile(AProjectFilename,[])=mrOk);
-        end;
+  // try command line project
+  if (CmdLineFiles<>nil) and (CmdLineFiles.Count>0) then begin
+    AProjectFilename:=CmdLineFiles[0];
+    if (CompareFileExt(AProjectFilename,'.lpr',false)=0) then
+      AProjectFilename:=ChangeFileExt(AProjectFilename,'.lpi');
+    // only try to load .lpi files, other files are loaded later
+    if (CompareFileExt(AProjectFilename,'.lpi',false)=0) then begin
+      AProjectFilename:=CleanAndExpandFilename(AProjectFilename);
+      if FileExistsUTF8(AProjectFilename) then begin
+        CmdLineFiles.Delete(0);
+        ProjectLoaded:=(DoOpenProjectFile(AProjectFilename,[])=mrOk);
       end;
     end;
+  end;
 
-    // try loading last project if lazarus didn't fail last time
-    if (not ProjectLoaded)
-    and (not SkipAutoLoadingLastProject)
-    and (EnvironmentOptions.OpenLastProjectAtStart)
-    and (EnvironmentOptions.LastSavedProjectFile<>'')
-    and (EnvironmentOptions.LastSavedProjectFile<>RestoreProjectClosed)
-    and (FileExistsCached(EnvironmentOptions.LastSavedProjectFile))
-    then begin
-      if (not IDEProtocolOpts.LastProjectLoadingCrashed)
-      or AskIfLoadLastFailingProject then begin
-        // protocol that the IDE is trying to load the last project and did not
-        // yet succeed
-        IDEProtocolOpts.LastProjectLoadingCrashed := True;
-        IDEProtocolOpts.Save;
-        // try loading the project
-        ProjectLoaded:=
-          (DoOpenProjectFile(EnvironmentOptions.LastSavedProjectFile,[])=mrOk);
-        // protocol that the IDE was able to open the project without crashing
-        IDEProtocolOpts.LastProjectLoadingCrashed := false;
-        IDEProtocolOpts.Save;
-        if ProjectLoaded then
+  // try loading last project if lazarus didn't fail last time
+  if (not ProjectLoaded)
+  and (LazIDEInstances.AllowOpenLastProject)
+  and (not SkipAutoLoadingLastProject)
+  and (EnvironmentOptions.OpenLastProjectAtStart)
+  and (EnvironmentOptions.LastSavedProjectFile<>'')
+  and (EnvironmentOptions.LastSavedProjectFile<>RestoreProjectClosed)
+  and (FileExistsCached(EnvironmentOptions.LastSavedProjectFile))
+  then begin
+    if (not IDEProtocolOpts.LastProjectLoadingCrashed)
+    or AskIfLoadLastFailingProject then begin
+      // protocol that the IDE is trying to load the last project and did not
+      // yet succeed
+      IDEProtocolOpts.LastProjectLoadingCrashed := True;
+      IDEProtocolOpts.Save;
+      // try loading the project
+      ProjectLoaded:=
+        (DoOpenProjectFile(EnvironmentOptions.LastSavedProjectFile,[])=mrOk);
+      // protocol that the IDE was able to open the project without crashing
+      IDEProtocolOpts.LastProjectLoadingCrashed := false;
+      IDEProtocolOpts.Save;
+      if ProjectLoaded then
+      begin
+        PkgOpenFlags:=[pofAddToRecent];
+        for I := 0 to EnvironmentOptions.LastOpenPackages.Count-1 do
         begin
-          PkgOpenFlags:=[pofAddToRecent];
-          for I := 0 to EnvironmentOptions.LastOpenPackages.Count-1 do
-          begin
-            AFilename:=EnvironmentOptions.LastOpenPackages[I];
-            if AFilename='' then
-              continue;
-            if i<EnvironmentOptions.LastOpenPackages.Count-1 then
-              Include(PkgOpenFlags,pofMultiOpen)
-            else
-              Exclude(PkgOpenFlags,pofMultiOpen);
-            if PkgBoss.DoOpenPackageFile(AFilename,PkgOpenFlags,true)=mrAbort then begin
-              break;
-            end;
+          AFilename:=EnvironmentOptions.LastOpenPackages[I];
+          if AFilename='' then
+            continue;
+          if i<EnvironmentOptions.LastOpenPackages.Count-1 then
+            Include(PkgOpenFlags,pofMultiOpen)
+          else
+            Exclude(PkgOpenFlags,pofMultiOpen);
+          if PkgBoss.DoOpenPackageFile(AFilename,PkgOpenFlags,true)=mrAbort then begin
+            break;
           end;
-        end else
-        begin
-          DoCloseProject;
         end;
+      end else
+      begin
+        DoCloseProject;
       end;
     end;
-    {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject B');{$ENDIF}
+  end;
+  {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject B');{$ENDIF}
 
-    if (not ProjectLoaded) then
-    begin
-      if EnvironmentOptions.OpenLastProjectAtStart
-      and (EnvironmentOptions.LastSavedProjectFile=RestoreProjectClosed) then begin
-        // IDE was closed without a project => restore that state
-      end else begin
-        // create new project
-        DoNewProject(ProjectDescriptorApplication);
-      end;
+  if (not ProjectLoaded) then
+  begin
+    if EnvironmentOptions.OpenLastProjectAtStart
+    and (EnvironmentOptions.LastSavedProjectFile=RestoreProjectClosed) then begin
+      // IDE was closed without a project => restore that state
+    end else begin
+      // create new project
+      DoNewProject(ProjectDescriptorApplication);
     end;
+  end;
 
-    // load the cmd line files
-    if CmdLineFiles<>nil then begin
-      for i:=0 to CmdLineFiles.Count-1 do
-      Begin
-        AFilename:=CleanAndExpandFilename(CmdLineFiles.Strings[i]);
-        if not FileExistsCached(AFilename) then begin
-          debugln(['WARNING: command line file not found: "',AFilename,'"']);
-          continue;
+  // load the cmd line files
+  if CmdLineFiles<>nil then begin
+    for i:=0 to CmdLineFiles.Count-1 do
+    Begin
+      AFilename:=CleanAndExpandFilename(CmdLineFiles.Strings[i]);
+      if not FileExistsCached(AFilename) then begin
+        debugln(['WARNING: command line file not found: "',AFilename,'"']);
+        continue;
+      end;
+      if Project1=nil then begin
+        // to open a file a project is needed
+        // => create a project
+        DoNewProject(ProjectDescriptorEmptyProject);
+      end;
+      if CompareFileExt(AFilename,'.lpk',false)=0 then begin
+        if PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent,pofMultiOpen],true)=mrAbort
+        then
+          break;
+      end else begin
+        OpenFlags:=[ofAddToRecent,ofRegularFile];
+        if i<CmdLineFiles.Count then
+          Include(OpenFlags,ofMultiOpen);
+        if DoOpenEditorFile(AFilename,-1,-1,OpenFlags)=mrAbort then begin
+          break;
         end;
-        if Project1=nil then begin
-          // to open a file a project is needed
-          // => create a project
-          DoNewProject(ProjectDescriptorEmptyProject);
-        end;
-        if CompareFileExt(AFilename,'.lpk',false)=0 then begin
-          if PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent,pofMultiOpen],true)=mrAbort
-          then
-            break;
-        end else begin
-          OpenFlags:=[ofAddToRecent,ofRegularFile];
-          if i<CmdLineFiles.Count then
-            Include(OpenFlags,ofMultiOpen);
-          if DoOpenEditorFile(AFilename,-1,-1,OpenFlags)=mrAbort then begin
-            break;
-          end;
-        end;
       end;
     end;
+  end;
 
-    if Project1=nil then
-      DoNoProjectWizard(nil);
+  if Project1=nil then
+    DoNoProjectWizard(nil);
 
-    {$IFDEF IDE_DEBUG}
-    debugln('TMainIDE.Create B');
-    {$ENDIF}
-    {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject C');{$ENDIF}
-  finally
-    CmdLineFiles.Free;
-  end;
+  {$IFDEF IDE_DEBUG}
+  debugln('TMainIDE.Create B');
+  {$ENDIF}
+  {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject C');{$ENDIF}
 end;
 
 procedure TMainIDE.SetupRemoteControl;
@@ -5387,6 +5401,21 @@
   SetRecentFilesMenu;
 end;
 
+procedure TMainIDE.DoDropFilesAsync(Data: PtrInt);
+var
+  xParams: TDoDropFilesAsyncParams;
+begin
+  xParams := TDoDropFilesAsyncParams(Data);
+  DoDropFiles(Self, xParams.FileNames, xParams.WindowIndex);
+  if xParams.BringToFront then
+  begin
+    Application.BringToFront;
+    if SourceEditorManager.ActiveSourceWindow <> nil then
+      SourceEditorManager.ActiveSourceWindow.BringToFront;
+  end;
+  xParams.Free;
+end;
+
 function TMainIDE.DoSelectFrame: TComponentClass;
 var
   UnitList: TStringList;
@@ -9117,6 +9146,66 @@
   Result:=false;
 end;
 
+procedure TMainIDE.NewInstanceChooseAction(const aFiles: TStrings;
+  var Result: TIDEStartWithFilesAction; var outPromptMessage,
+  outOpenNewInstanceMessage: string);
+begin
+  outPromptMessage := dlgOpenInRunningInstance;
+  outOpenNewInstanceMessage := dlgRunningInstanceModalOpenNew;
+
+  case EnvironmentOptions.OpenParamFilesInInstance of
+    isoStartNewInstance: Result := isaStartNewInstance;
+    isoOpenFilesInRunningInstance: Result := isaOpenFilesInRunningInstance;
+    isoPrompt: Result := isaPrompt;
+    isoProjectPromptOtherInRunning:
+    begin
+      if (aFiles.Count > 0) and
+         ((CompareFileExt(aFiles[0], '.lpr', False) = 0) or
+          (CompareFileExt(aFiles[0], '.lpi', False) = 0))
+      then
+        Result := isaPrompt
+      else
+        Result := isaOpenFilesInRunningInstance;
+    end;
+  end;
+
+  if (Result <> isaStartNewInstance) and
+       (not IsWindowEnabled(Application.MainForm.Handle) or//check that main is active
+        (Application.ModalLevel > 0))//check that no modal window is opened
+  then
+  begin
+    Result := isaModalError;
+    Exit;
+  end;
+end;
+
+procedure TMainIDE.NewInstanceOpenFiles(const aFiles: TStrings;
+  var Result: TOpenFilesResult; var outHandleBringToFront: HWND);
+var
+  xParams: TDoDropFilesAsyncParams;
+  I: Integer;
+begin
+  if (not IsWindowEnabled(Application.MainForm.Handle) or//check that main is active
+     (Application.ModalLevel > 0))//check that no modal window is opened
+  then
+  begin
+    Result := ofrModalError;
+    Exit;
+  end;
+
+  xParams := TDoDropFilesAsyncParams.Create(Self);//we need direct response, do not wait to get the files opened!
+  SetLength(xParams.FileNames, aFiles.Count);
+  for I := 0 to aFiles.Count-1 do
+    xParams.FileNames[I] := aFiles[I];
+  xParams.WindowIndex := -1;
+  xParams.BringToFront := True;
+  Application.QueueAsyncCall(@DoDropFilesAsync, PtrInt(xParams));
+  Result := ofrDoNotStart;
+  outHandleBringToFront := Application.MainFormHandle;
+  if Application.MainForm.WindowState = wsMinimized then
+    UnhideIDE;
+end;
+
 procedure TMainIDE.ApplyCodeToolChanges;
 begin
   // all changes were handled automatically by events, just clear the logs
Index: ide/startlazarus.lpi
===================================================================
--- ide/startlazarus.lpi	(revision 49818)
+++ ide/startlazarus.lpi	(working copy)
@@ -8,6 +8,7 @@
       </Flags>
       <SessionStorage Value="InIDEConfig"/>
       <MainUnit Value="0"/>
+      <UseXPManifest Value="True"/>
       <Icon Value="0"/>
     </General>
     <BuildModes Count="1">
Index: ide/startlazarus.lpr
===================================================================
--- ide/startlazarus.lpr	(revision 49818)
+++ ide/startlazarus.lpr	(working copy)
@@ -34,6 +34,7 @@
   redirect_stderr,
   Interfaces, SysUtils,
   Forms,
+  IDEInstances,
   LazarusManager;
   
 {$R *.res}
@@ -44,6 +45,9 @@
 begin
   redirect_stderr.DoShowWindow := False;
   Application.Initialize;
+  LazIDEInstances.PerformCheck;
+  if not LazIDEInstances.StartIDE then
+    Exit;
   ALazarusManager := TLazarusManager.Create(nil);
   ALazarusManager.Initialize;
   ALazarusManager.Run;
ide-one-instance-2.patch (68,571 bytes)   

Stephano

2015-09-13 20:46

developer   ~0085880

Last edited: 2015-09-13 20:47

View 2 revisions

I second Juha in the use of Unique Instance with the ability to disable it. That way one can opt for multiple instances when needed.

As for opening files in existing instance: what if there are multiple instances? Which one would be used to open the file?

Finally, Unique Instance has a serverID property which can be manipulated if needed to allow controlled multiple instances.

Ondrej Pokorny

2015-09-14 05:13

developer   ~0085881

@ what if there are multiple instances? Which one would be used to open the file?

Always the first one. Again, all IDEs are registered.

Have you tried/checked the patch? It is more advanced than the "Unique Instance" component. Believe me, I don't like unnecessary work and tried to use Unique Instance first but there were issues. Particularly if you open more files from the explorer at once. when Unique Instance is disabled it opens every one file in a new Lazarus IDE. Furthermore, Unique Instance only allows one-way communication. For example you cannot check if the files were actually open in the target IDE (e.g. if you have a system modal dialog open in the IDE, you cannot open files in the IDE!).

What I did is that I developed a better Unique Instance component.

+ If you want to force "Unique Instance", I can easily add a fifth option.

Ondrej Pokorny

2015-09-14 09:23

developer   ~0085883

I am working on the "force unique instance" option.

Juha Manninen

2015-09-14 10:39

developer   ~0085886

Last edited: 2015-09-14 10:51

View 3 revisions

I don't have Lazarus in path now. If I do in Lazarus dir :
 $ cd components/daemon/
 $ ../../lazarus lazdaemonapp.pp
then Lazarus gives an error that file is not found and wants to create it. I don't really think it is a problem because for all "normal" use cases Lazarus is found in path.

> Unique Instance only allows one-way communication. For example you cannot check if the files were actually open in the target IDE ...

I still don't understand this issue. What needs to check if the files were actually opened? What does it do with the information?

Opening more files from the explorer at once sounds like a real issue though.

> What I did is that I developed a better Unique Instance component.

Are you saying this can be used as a component for any app instead of Luiz Americo's component? Does it really require IPC? Luiz Americo should comment on this, my expertise is not enough.
In any case I was planning to add a Unique Instance component to the palette.

Ondrej Pokorny

2015-09-14 11:07

developer   ~0085887

Last edited: 2015-09-14 11:16

View 4 revisions

>>> Unique Instance only allows one-way communication. For example you cannot check if the files were actually open in the target IDE ...

> I still don't understand this issue. What needs to check if the files were actually opened? What does it do with the information?

1.) Check if Lazarus is even able to open the files. It is not possible to open files if you have modal windows opened in Lazarus! This could cause serious problems e.g. if you have opened the "project options" dialog and you open another project from the explorer.

2.) Tell the user if an error occurred (you requested files to open but no files were open e.g. due to some communication problem or due to modal windows in Lazarus). But maybe this is overcomplicated...

3.) Furthermore I use the two way communication to get the environment settings from the running Lazarus instance because I do the check even before EnvironmentSettings are loaded (and startlazarus doesn't load them at all). This speeds up the whole process a lot.

And there is the "collect files" issue at startup... All in all the UniqueInstance component simply cannot do this advanced stuff.

+ I'll check the paths, thanks.

+ I am simplifying it again to have only 3 options. See screenshot.

Ondrej Pokorny

2015-09-14 11:17

developer  

ide-multiple-instances-2.png (35,091 bytes)   
ide-multiple-instances-2.png (35,091 bytes)   

Stephano

2015-09-14 12:23

developer   ~0085894

I checked with Michael Van Canneyt who happens to be the author of the simpleipc unit:
======
- The multiple servers on windows should not happen, this is definitely a bug.

- Multiple clients talking to one server: Not simultaneously, because a FIFO is used. You need a semaphore to protect access to the FIFO, but I never programmed that. It should be simple to add, in fact.

- File locks are not working correctly when they are executed simultaneously on Linux: Linux or unix does not have mandatory file locks as windows has, and furthermore it is always a 2-step process. The linux FileOpen/FileCreate calls try to mimic the windows version, but it will never be 100% foolproof. So, any mechanism relying on windows-type file locks will never work 100% cross-platform.
======

You can thus submit patches (preferable) or bug reports for the 1st 2 points above especially that I doubt that the advancedipc unit will be included with FPC (where such functionality belongs).

Ondrej Pokorny

2015-09-14 12:29

developer   ~0085896

Ad 3.) And the current EnvironmentSettings don't have to be saved in the xml file. So you should really get them from the running instance.

ide-multipleinstances-3.patch

I fixed the "../../lazarus lazdaemonapp.pp" issue. Thanks, it was a valid problem. No it should work as expected.

I simplified the code further and added a new "unique instance" option (what you probably prefer). When this option is set, Lazarus cannot be started twice unless you start lazarus with the "--force-new-instance" parameter. This is good e.g. when you debug the IDE from another IDE and you still use this option as default.

The options are as follows:
a) "always start a new instance" - like the old behavior with one difference that if you open multiple files from explorer they still will be collected and started in one instance and not every file in an extra IDE.
b) "open files in a running instance" - if lazarus is started without files, it will open a new IDE; if it is started with files, the files will be opened in running instance (my favorite option).
c) "do not allow multiple instances" - open files in running instance; if you start lazarus without files the running instance is shown ("UniqueInstance" behavior).

Ondrej Pokorny

2015-09-14 12:31

developer  

ide-multipleinstances-3.patch (66,558 bytes)   
Index: ide/advancedipc.pas
===================================================================
--- ide/advancedipc.pas	(nonexistent)
+++ ide/advancedipc.pas	(working copy)
@@ -0,0 +1,484 @@
+{
+    *** Please use appropriate header ***
+
+    License standard LCL (GPL/LGPL ?)
+
+
+  Author: Ondrej Pokorny
+
+  Abstract:
+    Unit implementing two-way (request/response) IPC between 1 server and more clients
+
+ **********************************************************************}
+unit AdvancedIPC;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  {$IFDEF UNIX}
+  baseunix,
+  {$endif}
+  sysutils, Classes;
+
+const
+  HEADER_VERSION = 1;
+
+type
+  TMessageHeader = packed record
+    HdrVer: Integer;//version of header
+    FileLock: Byte;//0 = unlocked, 1 = locked
+    MsgType: Integer;
+    MsgLen: Integer;
+  end;
+
+  TFileHandle = Classes.THandle;
+
+  TReleaseHandleStream = class(THandleStream)
+  public
+    destructor Destroy; override;
+  end;
+
+  TIPCBase = class
+  protected
+    FFileName: string;
+    FServerName: string;
+
+    function GetUniqueFileName: string;
+    procedure SetServerName(const aServerName: string);
+
+    function CanReadMessage(const aFileName: string; out outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+    procedure DoPostMessage(const aFileName: string; const aMsgType: Integer; const aStream: TStream);
+  public
+    constructor Create; virtual;
+  public
+    class procedure FindRunningServers(const aServerNamePrefix: string; const outServerNames: TStrings);
+    class function ServerIsRunning(const aServerName: string): Boolean;
+    property ServerName: string read FServerName write SetServerName;
+  end;
+
+  TIPCClient = class(TIPCBase)
+  var
+    FLastMsgFileName: string;
+  public
+    procedure PostRequest(const aMsgType: Integer; const aStream: TStream); overload;
+    procedure PostRequest(const aMsgType: Integer; const aStream: TStream; out outMsgID: string); overload;
+    function PeekResponse(const aStream: TStream; var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+    procedure DeleteRequest;
+    function ServerRunning: Boolean;
+  end;
+
+  TIPCServer = class(TIPCBase)
+  private
+    FFileHandle: TFileHandle;
+    FActive: Boolean;
+
+    function FindFirstRequest(out outFileName: string; out outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+  public
+    function PeekRequest(const aStream: TStream; var outMsgType: Integer): Boolean; overload;
+    function PeekRequest(const aStream: TStream; var outMsgType: Integer; const aTimeOut: Integer): Boolean; overload;
+    function PeekRequest(const aStream: TStream; var outMsgID: string; var outMsgType: Integer): Boolean; overload;
+    function PeekRequest(const aStream: TStream; var outMsgID: string; var outMsgType: Integer; const aTimeOut: Integer): Boolean; overload;
+    procedure PostResponse(const aMsgID: string; const aMsgType: Integer; const aStream: TStream);
+
+    function FindHighestPendingRequestId: string;
+    function GetPendingRequestCount: Integer;
+
+    function StartServer(const aDeletePendingRequests: Boolean = True): Boolean;//true if unique and started
+    function StopServer(const aDeletePendingRequests: Boolean = True): Boolean;//true if stopped
+
+    procedure DeletePendingRequests;
+
+    property Active: Boolean read FActive;//true if started
+  end;
+
+  EICPException = class(Exception);
+
+implementation
+
+const
+  {$IFDEF UNIX}
+  GLOBAL_RIGHTS = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH or S_IWOTH;
+  {$ELSE}
+  GLOBAL_RIGHTS = 0;
+  {$ENDIF}
+  FILE_POSTFIX = '%s_';
+  FILE_WITH_INDEX = FILE_POSTFIX+'%x';
+  FILE_RESPONSE = '%s_r';
+
+{ TIPCBase }
+
+function TIPCBase.CanReadMessage(const aFileName: string; out
+  outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+var
+  xFileHandle: TFileHandle;
+  xHeader: TMessageHeader;
+begin
+  outStream := nil;
+  outMsgType := -1;
+  outMsgLen := 0;
+  Result := FileExists(aFileName);
+  if not Result then
+    Exit;
+
+  xFileHandle := FileOpen(aFileName, fmOpenRead or fmShareExclusive);
+  Result := xFileHandle <> feInvalidHandle;
+  if not Result then
+    Exit;
+
+  outStream := TReleaseHandleStream.Create(xFileHandle);
+
+  Result := (outStream.Size >= SizeOf(xHeader));
+  if not Result then
+  begin
+    FreeAndNil(outStream);
+    Exit;
+  end;
+
+  outStream.ReadBuffer(xHeader{%H-}, SizeOf(xHeader));
+  Result := (xHeader.HdrVer = HEADER_VERSION) and (xHeader.FileLock = 0) and
+    (outStream.Size = Int64(SizeOf(xHeader))+Int64(xHeader.MsgLen));
+  if not Result then
+  begin
+    FreeAndNil(outStream);
+    Exit;
+  end;
+  outMsgType := xHeader.MsgType;
+  outMsgLen := xHeader.MsgLen;
+end;
+
+constructor TIPCBase.Create;
+begin
+  inherited Create;
+end;
+
+function TIPCBase.GetUniqueFileName: string;
+var
+  I: Integer;
+begin
+  Randomize;
+  repeat
+    I := Random(High(Integer));
+    Result := Format(FILE_WITH_INDEX, [FFileName, I]);
+  until not FileExists(Result);
+end;
+
+class function TIPCBase.ServerIsRunning(const aServerName: string): Boolean;
+var
+  xServerFileHandle: TFileHandle;
+  xFileName: String;
+begin
+  xFileName := GetTempDir(False)+aServerName;
+  Result := FileExists(xFileName);
+  if Result then
+  begin//+ check -> we should not be able to access the file
+    xServerFileHandle := FileCreate(xFileName, fmOpenReadWrite or fmShareExclusive, GLOBAL_RIGHTS);
+    Result := (xServerFileHandle=feInvalidHandle);
+    if not Result then
+      FileClose(xServerFileHandle);
+  end;
+end;
+
+procedure TIPCBase.DoPostMessage(const aFileName: string;
+  const aMsgType: Integer; const aStream: TStream);
+var
+  xHeader: TMessageHeader;
+  xStream: TFileStream;
+begin
+  xHeader.HdrVer := HEADER_VERSION;
+  xHeader.FileLock := 1;//locking
+  xHeader.MsgType := aMsgType;
+  xHeader.MsgLen := aStream.Size-aStream.Position;
+
+  xStream := TFileStream.Create(aFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
+  try
+    xStream.WriteBuffer(xHeader, SizeOf(xHeader));
+    xStream.CopyFrom(aStream, 0);
+
+    xStream.Position := 0;//unlocking
+    xHeader.FileLock := 0;
+    xStream.WriteBuffer(xHeader, SizeOf(xHeader));
+  finally
+    xStream.Free;
+  end;
+end;
+
+class procedure TIPCBase.FindRunningServers(const aServerNamePrefix: string;
+  const outServerNames: TStrings);
+var
+  xRec: TRawByteSearchRec;
+begin
+  if FindFirst(GetTempDir(False)+aServerNamePrefix+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      if (Pos('_', xRec.Name) = 0) and//file that we found is not pending a message
+         ServerIsRunning(xRec.Name)
+      then
+        outServerNames.Add(xRec.Name);
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+procedure TIPCBase.SetServerName(const aServerName: string);
+var
+  I: Integer;
+begin
+  if FServerName = aServerName then Exit;
+
+  for I := 1 to Length(aServerName) do
+  if not (aServerName[I] in ['a'..'z', 'A'..'Z', '0'..'9']) then
+    raise EICPException.Create('You cannot use the "_" character in server name.');
+  FServerName := aServerName;
+
+  FFileName := GetTempDir(False) + aServerName;//get temp dir for current user
+end;
+
+{ TIPCClient }
+
+procedure TIPCClient.DeleteRequest;
+begin
+  if DeleteFile(FLastMsgFileName) then
+    FLastMsgFileName := '';
+end;
+
+function TIPCClient.PeekResponse(const aStream: TStream;
+  var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+var
+  xStart: QWord;
+  xStream: TStream;
+  xMsgLen: Integer;
+  xFileResponse: string;
+begin
+  aStream.Size := 0;
+  outMsgType := -1;
+  Result := False;
+  xStart := GetTickCount64;
+  repeat
+    xFileResponse := Format(FILE_RESPONSE, [FLastMsgFileName]);
+    if CanReadMessage(xFileResponse, xStream, outMsgType, xMsgLen) then
+    begin
+      aStream.CopyFrom(xStream, xMsgLen);
+      xStream.Free;
+      aStream.Position := 0;
+      DeleteFile(xFileResponse);
+      Exit(True);
+    end
+    else if aTimeOut > 20 then
+      Sleep(10);
+  until (GetTickCount64-xStart > aTimeOut);
+end;
+
+procedure TIPCClient.PostRequest(const aMsgType: Integer; const aStream: TStream);
+var
+  xMsgID: string;
+begin
+  PostRequest(aMsgType, aStream, xMsgID);
+end;
+
+procedure TIPCClient.PostRequest(const aMsgType: Integer;
+  const aStream: TStream; out outMsgID: string);
+begin
+  outMsgID := GetUniqueFileName;
+  FLastMsgFileName := outMsgID;
+  DeleteFile(Format(FILE_RESPONSE, [FLastMsgFileName]));//delete old response, if there is any
+  DoPostMessage(FLastMsgFileName, aMsgType, aStream);
+end;
+
+function TIPCClient.ServerRunning: Boolean;
+begin
+  Result := ServerIsRunning(ServerName);
+end;
+
+{ TReleaseHandleStream }
+
+destructor TReleaseHandleStream.Destroy;
+begin
+  FileClose(Handle);
+
+  inherited Destroy;
+end;
+
+{ TIPCServer }
+
+procedure TIPCServer.DeletePendingRequests;
+var
+  xRec: TRawByteSearchRec;
+  xDir: string;
+begin
+  xDir := ExtractFilePath(FFileName);
+  if FindFirst(Format(FILE_POSTFIX, [FFileName])+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      DeleteFile(xDir+xRec.Name);
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+constructor TIPCServer.Create;
+begin
+  inherited Create;
+
+  FFileHandle := feInvalidHandle;
+end;
+
+destructor TIPCServer.Destroy;
+begin
+  if FActive then
+    StopServer;
+
+  inherited Destroy;
+end;
+
+function TIPCServer.FindFirstRequest(out outFileName: string; out
+  outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+var
+  xRec: TRawByteSearchRec;
+  xDir: string;
+begin
+  outFileName := '';
+  outStream := nil;
+  outMsgType := -1;
+  outMsgLen := 0;
+  Result := False;
+  xDir := ExtractFilePath(FFileName);
+  if FindFirst(Format(FILE_POSTFIX, [FFileName])+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      outFileName := xDir+xRec.Name;
+      if (Copy(outFileName, Length(outFileName)-1, 2) = '_r') then//file is response and not request!
+        Result := False
+      else
+        Result := CanReadMessage(outFileName, outStream, outMsgType, outMsgLen);
+    until Result or (FindNext(xRec) <> 0);
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.FindHighestPendingRequestId: string;
+var
+  xRec: TRawByteSearchRec;
+  xIdStart: Integer;
+  xMessageId,  xHighestId: LongInt;
+  xDir: string;
+begin
+  xHighestId := -1;
+  Result := '';
+  xIdStart := Length(Format(FILE_POSTFIX, [FServerName]))+1;
+  xDir := ExtractFilePath(FFileName);
+  if FindFirst(Format(FILE_POSTFIX, [FFileName])+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      xMessageId := StrToIntDef('$'+Copy(xRec.Name, xIdStart), -1);
+      if xMessageId > xHighestId then
+      begin
+        xHighestId := xMessageId;
+        Result := xDir+xRec.Name;
+      end;
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.GetPendingRequestCount: Integer;
+var
+  xRec: TRawByteSearchRec;
+begin
+  Result := 0;
+  if FindFirst(Format(FILE_POSTFIX, [FFileName])+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      Inc(Result);
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream;
+  var outMsgType: Integer): Boolean;
+var
+  xMsgFileName: string;
+begin
+  Result := PeekRequest(aStream, xMsgFileName{%H-}, outMsgType);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream;
+  var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+var
+  xMsgFileName: string;
+begin
+  Result := PeekRequest(aStream, xMsgFileName{%H-}, outMsgType, aTimeOut);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; var outMsgID: string;
+  var outMsgType: Integer): Boolean;
+var
+  xStream: TStream;
+  xMsgLen: Integer;
+begin
+  aStream.Size := 0;
+  outMsgID := '';
+  outMsgType := -1;
+  Result := FindFirstRequest(outMsgID, xStream, outMsgType, xMsgLen);
+  if Result then
+  begin
+    aStream.CopyFrom(xStream, xMsgLen);
+    aStream.Position := 0;
+    xStream.Free;
+    DeleteFile(outMsgID);
+  end;
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; var outMsgID: string;
+  var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+var
+  xStart: QWord;
+begin
+  Result := False;
+  xStart := GetTickCount64;
+  repeat
+    if PeekRequest(aStream, outMsgID, outMsgType) then
+      Exit(True)
+    else if aTimeOut > 20 then
+      Sleep(10);
+  until (GetTickCount64-xStart > aTimeOut);
+end;
+
+procedure TIPCServer.PostResponse(const aMsgID: string;
+  const aMsgType: Integer; const aStream: TStream);
+begin
+  DoPostMessage(Format(FILE_RESPONSE, [aMsgID]), aMsgType, aStream);
+end;
+
+function TIPCServer.StartServer(const aDeletePendingRequests: Boolean): Boolean;
+begin
+  FFileHandle := FileCreate(FFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
+  Result := (FFileHandle<>feInvalidHandle);
+  FActive := Result;
+  if Result and aDeletePendingRequests then
+    DeletePendingRequests;
+end;
+
+function TIPCServer.StopServer(const aDeletePendingRequests: Boolean): Boolean;
+begin
+  if not FActive then
+    Exit(True);
+
+  if FFileHandle<>feInvalidHandle then
+    FileClose(FFileHandle);
+  DeleteFile(FFileName);
+  FFileName := '';
+
+  if aDeletePendingRequests then
+    DeletePendingRequests;
+
+  FActive := False;
+end;
+
+end.
+
Index: ide/environmentopts.pp
===================================================================
--- ide/environmentopts.pp	(revision 49824)
+++ ide/environmentopts.pp	(working copy)
@@ -176,6 +176,15 @@
       'Never'
     );
 
+type
+  TIDEMultipleInstancesOption = (mioAlwaysStartNew, mioOpenFilesInRunning, mioForceSingleInstance);
+const
+  IDEMultipleInstancesOptionNames: array[TIDEMultipleInstancesOption] of string = (
+    'AlwaysStartNew',      // mioAlwaysStartNew
+    'OpenFilesInRunning',  // mioOpenFilesInRunning
+    'ForceSingleInstance'  // mioForceSingleInstance
+    );
+
   { Messages window }
 type
   TMsgWndFileNameStyle = (
@@ -475,6 +484,7 @@
     FRecentPackageFiles: TStringList;
     FMaxRecentPackageFiles: integer;
     FOpenLastProjectAtStart: boolean;
+    FMultipleInstances: TIDEMultipleInstancesOption;
     // Prevent repopulating Recent project files menu with example projects if it was already cleared up.
     FAlreadyPopulatedRecentFiles : Boolean;
 
@@ -728,6 +738,8 @@
     property LastOpenPackages: TLastOpenPackagesList read FLastOpenPackages;
     property OpenLastProjectAtStart: boolean read FOpenLastProjectAtStart
                                              write FOpenLastProjectAtStart;
+    property MultipleInstances: TIDEMultipleInstancesOption read FMultipleInstances
+                                                                write FMultipleInstances;
     property FileDialogFilter: string read FFileDialogFilter write FFileDialogFilter;
 
     // backup
@@ -802,6 +814,7 @@
 function CharCaseFileActionNameToType(const Action: string): TCharCaseFileAction;
 function UnitRenameReferencesActionNameToType(const Action: string): TUnitRenameReferencesAction;
 function StrToMsgWndFilenameStyle(const s: string): TMsgWndFileNameStyle;
+function StrToIDEMultipleInstancesOption(const s: string): TIDEMultipleInstancesOption;
 
 function SimpleDirectoryCheck(const OldDir, NewDir,
   NotFoundErrMsg: string; out StopChecking: boolean): boolean;
@@ -871,6 +884,13 @@
   Result:=mwfsShort;
 end;
 
+function StrToIDEMultipleInstancesOption(const s: string): TIDEMultipleInstancesOption;
+begin
+  for Result in TIDEMultipleInstancesOption do
+    if CompareText(s,IDEMultipleInstancesOptionNames[Result])=0 then exit;
+  Result:=mioAlwaysStartNew;
+end;
+
 function SimpleDirectoryCheck(const OldDir, NewDir,
   NotFoundErrMsg: string; out StopChecking: boolean): boolean;
 var
@@ -1308,6 +1328,7 @@
   FRecentPackageFiles:=TStringList.Create;
   FMaxRecentPackageFiles:=DefaultMaxRecentPackageFiles;
   FOpenLastProjectAtStart:=true;
+  FMultipleInstances:=mioAlwaysStartNew;
 
   // backup
   with FBackupInfoProjectFiles do begin
@@ -1744,6 +1765,7 @@
       Path+'UnitRenameReferencesAction/Value',UnitRenameReferencesActionNames[urraAsk]));
     FAskForFilenameOnNewFile:=FXMLCfg.GetValue(Path+'AskForFilenameOnNewFile/Value',false);
     FLowercaseDefaultFilename:=FXMLCfg.GetValue(Path+'LowercaseDefaultFilename/Value',true);
+    FMultipleInstances:=StrToIDEMultipleInstancesOption(FXMLCfg.GetValue(Path+'MultipleInstances/Value',''));
 
     // fpdoc
     FPDocPaths := FXMLCfg.GetValue(Path+'LazDoc/Paths','');
@@ -2051,6 +2073,11 @@
                              FAskForFilenameOnNewFile,false);
     FXMLCfg.SetDeleteValue(Path+'LowercaseDefaultFilename/Value',
                              FLowercaseDefaultFilename,true);
+    if FMultipleInstances = mioAlwaysStartNew then
+      FXMLCfg.DeletePath(Path+'MultipleInstances')
+    else
+      FXMLCfg.SetValue(Path+'MultipleInstances/Value',IDEMultipleInstancesOptionNames[FMultipleInstances]);
+
     // fpdoc
     FXMLCfg.SetDeleteValue(Path+'LazDoc/Paths',FPDocPaths,'');
 
Index: ide/frames/files_options.lfm
===================================================================
--- ide/frames/files_options.lfm	(revision 49824)
+++ ide/frames/files_options.lfm	(working copy)
@@ -47,12 +47,12 @@
   end
   object ShowCompileDialogCheckBox: TCheckBox
     AnchorSideLeft.Control = Owner
-    AnchorSideTop.Control = OpenLastProjectAtStartCheckBox
+    AnchorSideTop.Control = MultipleInstancesComboBox
     AnchorSideTop.Side = asrBottom
     AnchorSideRight.Side = asrBottom
     Left = 2
     Height = 19
-    Top = 69
+    Top = 94
     Width = 180
     BorderSpacing.Top = 2
     Caption = 'ShowCompileDialogCheckBox'
@@ -65,7 +65,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 117
+    Top = 142
     Width = 82
     BorderSpacing.Top = 10
     Caption = 'LazarusDirLabel'
@@ -80,7 +80,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 132
+    Top = 157
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -94,7 +94,7 @@
     AnchorSideRight.Control = LazarusDirButton
     Left = 2
     Height = 23
-    Top = 132
+    Top = 157
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -108,7 +108,7 @@
     AnchorSideRight.Control = CompilerPathButton
     Left = 2
     Height = 23
-    Top = 176
+    Top = 201
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -123,7 +123,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 176
+    Top = 201
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -136,7 +136,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 161
+    Top = 186
     Width = 101
     BorderSpacing.Top = 6
     Caption = 'CompilerPathLabel'
@@ -149,7 +149,7 @@
     AnchorSideRight.Control = FPCSourceDirButton
     Left = 2
     Height = 23
-    Top = 220
+    Top = 245
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -164,7 +164,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 220
+    Top = 245
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -177,7 +177,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 205
+    Top = 230
     Width = 100
     BorderSpacing.Top = 6
     Caption = 'FPCSourceDirLabel'
@@ -189,7 +189,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 249
+    Top = 274
     Width = 81
     BorderSpacing.Top = 6
     Caption = 'MakePathLabel'
@@ -201,7 +201,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 293
+    Top = 318
     Width = 91
     BorderSpacing.Top = 6
     Caption = 'TestBuildDirLabel'
@@ -214,7 +214,7 @@
     AnchorSideRight.Control = MakePathButton
     Left = 2
     Height = 23
-    Top = 264
+    Top = 289
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -229,7 +229,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 264
+    Top = 289
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -243,7 +243,7 @@
     AnchorSideRight.Control = TestBuildDirButton
     Left = 2
     Height = 23
-    Top = 308
+    Top = 333
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -258,7 +258,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 308
+    Top = 333
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -272,7 +272,7 @@
     AnchorSideRight.Side = asrBottom
     Left = 32
     Height = 19
-    Top = 88
+    Top = 113
     Width = 206
     BorderSpacing.Left = 30
     Caption = 'AutoCloseCompileDialogCheckBox'
@@ -284,7 +284,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 337
+    Top = 362
     Width = 153
     Alignment = taRightJustify
     BorderSpacing.Top = 6
@@ -301,7 +301,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 352
+    Top = 377
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -317,7 +317,7 @@
     AnchorSideRight.Control = CompilerTranslationFileButton
     Left = 2
     Height = 23
-    Top = 352
+    Top = 377
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -365,4 +365,27 @@
     BorderSpacing.Around = 2
     TabOrder = 16
   end
+  object MultipleInstancesLabel: TLabel
+    AnchorSideLeft.Control = Owner
+    AnchorSideTop.Control = MultipleInstancesComboBox
+    AnchorSideTop.Side = asrCenter
+    Left = 2
+    Height = 15
+    Top = 73
+    Width = 121
+    Caption = 'MultipleInstancesLabel'
+    ParentColor = False
+  end
+  object MultipleInstancesComboBox: TComboBox
+    AnchorSideLeft.Control = MultipleInstancesLabel
+    AnchorSideLeft.Side = asrBottom
+    Left = 131
+    Height = 23
+    Top = 69
+    Width = 238
+    BorderSpacing.Left = 8
+    ItemHeight = 15
+    Style = csDropDownList
+    TabOrder = 17
+  end
 end
Index: ide/frames/files_options.pas
===================================================================
--- ide/frames/files_options.pas	(revision 49824)
+++ ide/frames/files_options.pas	(working copy)
@@ -40,6 +40,7 @@
 
   TFilesOptionsFrame = class(TAbstractIDEOptionsEditor)
     AutoCloseCompileDialogCheckBox: TCheckBox;
+    MultipleInstancesComboBox: TComboBox;
     CompilerTranslationFileButton:TButton;
     CompilerTranslationFileComboBox:TComboBox;
     CompilerTranslationFileLabel:TLabel;
@@ -49,6 +50,7 @@
     FPCSourceDirButton:TButton;
     FPCSourceDirComboBox:TComboBox;
     FPCSourceDirLabel:TLabel;
+    MultipleInstancesLabel: TLabel;
     lblCenter: TLabel;
     LazarusDirButton:TButton;
     LazarusDirComboBox:TComboBox;
@@ -229,6 +231,16 @@
     Add(ProgramDirectory(true));
     EndUpdate;
   end;
+  MultipleInstancesLabel.Caption := dlgMultipleInstances;
+  with MultipleInstancesComboBox.Items do
+  begin
+    BeginUpdate;
+    Add(dlgMultipleInstances_AlwaysStartNew);
+    Add(dlgMultipleInstances_OpenFilesInRunning);
+    Add(dlgMultipleInstances_ForceSingleInstance);
+    EndUpdate;
+  end;
+  Assert(MultipleInstancesComboBox.Items.Count = Ord(High(TIDEMultipleInstancesOption))+1);
 
   CompilerPathLabel.Caption:=Format(dlgFpcExecutable,[GetDefaultCompilerFilename]);
   FPCSourceDirLabel.Caption:=dlgFpcSrcPath;
@@ -361,6 +373,8 @@
     // open last project at start
     OpenLastProjectAtStartCheckBox.Checked:=OpenLastProjectAtStart;
 
+    MultipleInstancesComboBox.ItemIndex := Ord(MultipleInstances);
+
     // compile dialog
     fOldShowCompileDialog:=ShowCompileDialog;
     ShowCompileDialogCheckBox.Checked:=ShowCompileDialog;
@@ -391,6 +405,7 @@
     MaxRecentOpenFiles := MaxRecentOpenFilesSpin.Value;
     MaxRecentProjectFiles := MaxRecentProjectFilesSpin.Value;
     OpenLastProjectAtStart:=OpenLastProjectAtStartCheckBox.Checked;
+    MultipleInstances := TIDEMultipleInstancesOption(MultipleInstancesComboBox.ItemIndex);
     ShowCompileDialog := ShowCompileDialogCheckBox.Checked;
     AutoCloseCompileDialog := AutoCloseCompileDialogCheckBox.Checked;
   end;
Index: ide/idecmdline.pas
===================================================================
--- ide/idecmdline.pas	(revision 49824)
+++ ide/idecmdline.pas	(working copy)
@@ -50,6 +50,7 @@
   NoSplashScreenOptLong='--no-splash-screen';
   NoSplashScreenOptShort='--nsc';
   StartedByStartLazarusOpt='--started-by-startlazarus';
+  ForceNewInstanceOpt='--force-new-instance';
   SkipLastProjectOpt='--skip-last-project';
   DebugLogOpt='--debug-log=';
   DebugLogOptEnable='--debug-enable=';
Index: ide/ideinstances.pas
===================================================================
--- ide/ideinstances.pas	(nonexistent)
+++ ide/ideinstances.pas	(working copy)
@@ -0,0 +1,736 @@
+{
+ /***************************************************************************
+                              ideinstances.pas
+                              ----------------
+
+ ***************************************************************************/
+
+ ***************************************************************************
+ *                                                                         *
+ *   This source is free software; you can redistribute it and/or modify   *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This code is distributed in the hope that it will be useful, but      *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   General Public License for more details.                              *
+ *                                                                         *
+ *   A copy of the GNU General Public License is available on the World    *
+ *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
+ *   obtain it by writing to the Free Software Foundation,                 *
+ *   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
+ *                                                                         *
+ ***************************************************************************
+
+  Author: Ondrej Pokorny
+
+  Abstract:
+    This unit handles one/multiple Lazarus IDE instances.
+
+}
+unit IDEInstances;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  sysutils, Interfaces, Classes, Controls, Forms, Dialogs, ExtCtrls,
+  LCLProc, LCLIntf, LCLType, AdvancedIPC,
+  LazFileUtils, LazUTF8, Laz2_DOM, laz2_XMLRead, laz2_XMLWrite,
+  LazarusIDEStrConsts, IDECmdLine;
+
+type
+  TStartNewInstanceResult = (ofrStartNewInstance, ofrDoNotStart, ofrModalError, ofrForceSingleInstanceModalError, ofrNotResponding);
+  TStartNewInstanceEvent = procedure(const aFiles: TStrings;
+    var outResult: TStartNewInstanceResult) of object;
+
+  TMessageParam = record
+    Name: string;
+    Value: string;
+  end;
+  TMessageParams = array of TMessageParam;
+
+  TUniqueServer = class(TIPCServer)
+  public
+    procedure StartUnique(const aServerPrefix: string);
+  end;
+
+  TMainServer = class(TUniqueServer)
+  private
+    FStartNewInstanceEvent: TStartNewInstanceEvent;
+    FTimer: TTimer;
+    FMsgStream: TMemoryStream;
+
+    procedure DoStartNewInstance(const aMsgID: string; const aInParams: TMessageParams);
+
+    procedure SimpleResponse(
+      const aResponseToMsgID, aResponseType: string;
+      const aParams: array of TMessageParam);
+
+    procedure DoCheckMessages;
+    procedure CheckMessagesOnTimer(Sender: TObject);
+
+    procedure StartListening(const aStartNewInstanceEvent: TStartNewInstanceEvent);
+    procedure StopListening;
+
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+  end;
+
+  TResponseClient = class(TIPCClient)
+  public
+    function SendFilesToRunningInstance(
+      const aFiles: TStrings; var outModalErrorMessage,
+      outModalErrorForceUniqueMessage, outNotRespondingErrorMessage: string;
+      var outHandleBringToFront: HWND): TStartNewInstanceResult;
+  end;
+
+  TIDEInstances = class
+  private
+    FMainServer: TMainServer;//running IDE
+    FStartIDE: Boolean;// = True;
+    FForceNewInstance: Boolean;
+    FAllowOpenLastProject: Boolean;// = True;
+    FFilesToOpen: TStrings;
+
+    class procedure AddFilesToParams(const aFiles: TStrings;
+      var ioParams: TMessageParams); static;
+    class procedure AddFilesFromParams(const aParams: TMessageParams;
+      const aFiles: TStrings); static;
+    class procedure BuildMessage(const aMessageType: string;
+      const aParams: array of TMessageParam; const aStream: TStream); static;
+    class function MessageParam(const aName, aValue: string): TMessageParam; static;
+    class function ParseMessage(const aStream: TStream; out outMessageType: string;
+      out outParams: TMessageParams): Boolean; static;
+    class function GetMessageParam(const aParams: array of TMessageParam;
+      const aParamName: string): string; static;
+
+    function CheckParamsForForceNewInstanceOpt: Boolean;
+
+    procedure CollectFiles(out
+      outFilesWereSentToCollectingServer: Boolean);
+
+    function SendFilesToRunningInstance(const aFiles: TStrings;
+      var outModalErrorMessage, outModalErrorForceUniqueMessage, outNotRespondingErrorMessage: string;
+      var outHandleBringToFront: HWND): TStartNewInstanceResult;
+    procedure InitIDEInstances;
+  public
+    constructor Create;
+    destructor Destroy; override;
+  public
+    procedure PerformCheck;//call PerformCheck after Application.Initialize - it can open dialogs!
+
+    procedure StartServer;
+    procedure StopServer;
+    procedure StartListening(const aStartNewInstanceEvent: TStartNewInstanceEvent);
+    procedure StopListening;
+
+    function StartIDE: Boolean;//can the IDE be started?
+    function AllowOpenLastProject: Boolean;//if a secondary IDE is starting, do NOT reopen last project!
+    function FilesToOpen: TStrings;
+  end;
+
+function LazIDEInstances: TIDEInstances;
+
+implementation
+
+const
+  SERVERPREFIX_MAIN = 'LazarusMain';
+  SERVERNAME_COLLECT = 'LazarusCollect';
+  MESSAGETYPE_XML = 2;
+  ELEMENT_ROOT = 'ideinstances';
+  ATTR_VALUE = 'value';
+  ATTR_MESSAGE_TYPE = 'msgtype';
+  MESSAGE_STARTNEWINSTANCE = 'startnewinstance';//ooooooo
+  RESPONSE_OPENFILES = 'openfilesResponse';
+  TIMEOUT_OPENFILES = 1000;
+  MESSAGE_COLLECTFILES = 'collectfiles';
+  TIMEOUT_COLLECTFILES = 100;
+  PARAM_FILE = 'file';
+  PARAM_RESULT = 'result';
+  PARAM_HANDLEBRINGTOFRONT = 'handlebringtofront';
+  PARAM_MODALERRORMESSAGE = 'modalerrormessage';
+  PARAM_FORCEUNIQUEMODALERRORMESSAGE = 'forceuniquemodalerrormessage';
+  PARAM_NOTRESPONDINGERRORMESSAGE = 'notrespondingerrormessage';
+
+var
+  FLazIDEInstances: TIDEInstances;
+
+function LazIDEInstances: TIDEInstances;
+begin
+  Result := FLazIDEInstances;
+end;
+
+{ TIDEInstances }
+
+class function TIDEInstances.MessageParam(const aName, aValue: string): TMessageParam;
+begin
+  Result.Name := aName;
+  Result.Value := aValue;
+end;
+
+function TIDEInstances.StartIDE: Boolean;
+begin
+  Result := FStartIDE;
+end;
+
+function TIDEInstances.AllowOpenLastProject: Boolean;
+begin
+  Result := FAllowOpenLastProject;
+end;
+
+function TIDEInstances.FilesToOpen: TStrings;
+begin
+  if not Assigned(FFilesToOpen) then
+    FFilesToOpen := TStringList.Create;
+  Result := FFilesToOpen;
+end;
+
+procedure TIDEInstances.StartListening(const aStartNewInstanceEvent: TStartNewInstanceEvent);
+begin
+  Assert(Assigned(FMainServer));
+
+  FMainServer.StartListening(aStartNewInstanceEvent);
+end;
+
+procedure TIDEInstances.StartServer;
+begin
+  Assert(FMainServer = nil);
+
+  FMainServer := TMainServer.Create;
+  FMainServer.StartUnique(SERVERPREFIX_MAIN);
+end;
+
+procedure TIDEInstances.StopListening;
+begin
+  FMainServer.StopListening;
+end;
+
+procedure TIDEInstances.StopServer;
+begin
+  FreeAndNil(FMainServer);
+end;
+
+class procedure TIDEInstances.AddFilesFromParams(const aParams: TMessageParams;
+  const aFiles: TStrings);
+var
+  I: Integer;
+begin
+  //do not clear aFiles
+  for I := Low(aParams) to High(aParams) do
+    if aParams[I].Name = PARAM_FILE then
+      aFiles.Add(aParams[I].Value);
+end;
+
+class procedure TIDEInstances.AddFilesToParams(const aFiles: TStrings;
+  var ioParams: TMessageParams);
+var
+  xStartIndex: Integer;
+  I: Integer;
+begin
+  xStartIndex := Length(ioParams);
+  SetLength(ioParams, xStartIndex+aFiles.Count);
+  for I := 0 to aFiles.Count-1 do
+    ioParams[xStartIndex+I] := MessageParam(PARAM_FILE, aFiles[I]);
+end;
+
+class function TIDEInstances.GetMessageParam(
+  const aParams: array of TMessageParam; const aParamName: string): string;
+var
+  I: Integer;
+begin
+  for I := Low(aParams) to High(aParams) do
+  if aParams[I].Name = aParamName then
+    Exit(aParams[I].Value);
+
+  Result := '';//not found
+end;
+
+class procedure TIDEInstances.BuildMessage(const aMessageType: string;
+  const aParams: array of TMessageParam; const aStream: TStream);
+var
+  xDOM: TXMLDocument;
+  xRoot: TDOMElement;
+  xParam: TDOMElement;
+  I: Integer;
+begin
+  xDOM := TXMLDocument.Create;
+  try
+    xRoot := xDOM.CreateElement(ELEMENT_ROOT);
+    xRoot.AttribStrings[ATTR_MESSAGE_TYPE] := aMessageType;
+    xDOM.AppendChild(xRoot);
+
+    for I := Low(aParams) to High(aParams) do
+    begin
+      xParam := xDOM.CreateElement(aParams[I].Name);
+      xRoot.AppendChild(xParam);
+      xParam.AttribStrings[ATTR_VALUE] := aParams[I].Value;
+    end;
+
+    WriteXMLFile(xDOM, aStream);
+  finally
+    xDOM.Free;
+  end;
+end;
+
+class function TIDEInstances.ParseMessage(const aStream: TStream; out
+  outMessageType: string; out outParams: TMessageParams): Boolean;
+var
+  xDOM: TXMLDocument;
+  xChildList: TDOMNodeList;
+  I, J: Integer;
+begin
+  Result := False;
+
+  outMessageType := '';
+  SetLength(outParams, 0);
+  try
+    ReadXMLFile(xDOM, aStream, []);
+  except
+    on EXMLReadError do
+      Exit;//eat XML exceptions
+  end;
+  try
+    if (xDOM = nil) or (xDOM.DocumentElement = nil) or (xDOM.DocumentElement.NodeName <> ELEMENT_ROOT) then
+      Exit;
+
+    outMessageType := xDOM.DocumentElement.AttribStrings[ATTR_MESSAGE_TYPE];
+
+    xChildList := xDOM.DocumentElement.ChildNodes;
+    SetLength(outParams, xChildList.Count);
+    J := 0;
+    for I := 0 to xChildList.Count-1 do
+    if xChildList[I] is TDOMElement then
+    begin
+      outParams[J].Name := xChildList[I].NodeName;
+      outParams[J].Value := TDOMElement(xChildList[I]).AttribStrings[ATTR_VALUE];
+      Inc(J);
+    end;
+    SetLength(outParams, J);
+    Result := True;
+  finally
+    xDOM.Free;
+  end;
+end;
+
+function TIDEInstances.SendFilesToRunningInstance(const aFiles: TStrings;
+  var outModalErrorMessage, outModalErrorForceUniqueMessage,
+  outNotRespondingErrorMessage: string; var outHandleBringToFront: HWND
+  ): TStartNewInstanceResult;
+var
+  xStartClient: TResponseClient;
+  I: Integer;
+  xServerNames: TStringList;
+begin
+  Result := ofrStartNewInstance;
+  xStartClient := TResponseClient.Create;
+  xServerNames := TStringList.Create;
+  try
+    xStartClient.FindRunningServers(SERVERPREFIX_MAIN, xServerNames);//check for multiple instances
+    xServerNames.Sort;
+    for I := xServerNames.Count-1 downto 0 do//last started is first to choose
+    begin
+      xStartClient.ServerName := xServerNames[I];
+      if xStartClient.ServerRunning then
+      begin
+        //there are open Lazarus instances, do not reopen last project!
+        FAllowOpenLastProject := False;
+        Result := xStartClient.SendFilesToRunningInstance(aFiles, outModalErrorMessage,
+          outModalErrorForceUniqueMessage, outNotRespondingErrorMessage, outHandleBringToFront);
+        if not(Result in [ofrModalError, ofrForceSingleInstanceModalError, ofrNotResponding]) then
+          Exit;//handle only one running Lazarus IDE
+      end;
+    end;
+  finally
+    xStartClient.Free;
+    xServerNames.Free;
+  end;
+end;
+
+function TIDEInstances.CheckParamsForForceNewInstanceOpt: Boolean;
+var
+  I: Integer;
+begin
+  Result := False;
+  for I := 1 to ParamsAndCfgCount do
+    if ParamIsOption(i, ForceNewInstanceOpt) then//ignore the settings and start new Lazarus IDE instance
+      Result := True;
+end;
+
+procedure TIDEInstances.PerformCheck;
+var
+  xResult: TStartNewInstanceResult;
+  xModalErrorMessage: string = '';
+  xModalErrorForceUniqueMessage: string = '';
+  xNotRespondingErrorMessage: string = '';
+  xHandleBringToFront: HWND = 0;
+begin
+  if not FStartIDE then//InitIDEInstances->CollectOtherOpeningFiles decided not to start the IDE
+    Exit;
+
+  if not FForceNewInstance then
+    xResult := SendFilesToRunningInstance(FilesToOpen, xModalErrorMessage, xModalErrorForceUniqueMessage, xNotRespondingErrorMessage, xHandleBringToFront)
+  else
+    xResult := ofrStartNewInstance;
+
+  if xModalErrorMessage = '' then
+    xModalErrorMessage := dlgRunningInstanceModalError;
+  if xModalErrorForceUniqueMessage = '' then
+    xModalErrorForceUniqueMessage := dlgForceUniqueInstanceModalError;
+
+  FStartIDE := (xResult = ofrStartNewInstance);
+  case xResult of
+    ofrModalError:
+      FStartIDE := MessageDlg(lisLazarusIDE, Format(xModalErrorMessage, [FilesToOpen.Text]), mtWarning, mbYesNo, 0, mbYes) = mrYes;
+    ofrNotResponding:
+      MessageDlg(lisLazarusIDE, xNotRespondingErrorMessage, mtError, mbYesNo, 0, mbYes);
+    ofrForceSingleInstanceModalError:
+      MessageDlg(lisLazarusIDE, xModalErrorForceUniqueMessage, mtError, [mbOK], 0);
+  end;
+
+  {$IFDEF MSWINDOWS}
+  if not FStartIDE and (xHandleBringToFront <> 0) then
+  begin
+    try
+      SetForegroundWindow(xHandleBringToFront);//SetForegroundWindow works (on Windows) only if the calling process is the foreground process, therefore it must be here!
+    except
+      //eat all widget exceptions
+    end;
+  end;
+  {$ENDIF}
+end;
+
+constructor TIDEInstances.Create;
+begin
+  inherited Create;
+
+  FStartIDE := True;
+  FAllowOpenLastProject := True;
+end;
+
+destructor TIDEInstances.Destroy;
+begin
+  StopServer;
+  FreeAndNil(FMainServer);
+  FreeAndNil(FFilesToOpen);
+
+  inherited Destroy;
+end;
+
+procedure TIDEInstances.CollectFiles(out
+  outFilesWereSentToCollectingServer: Boolean);
+
+var
+  xThisClientMessageId: string;
+
+  procedure _SendToServer;
+  var
+    xClient: TIPCClient;
+    xOutParams: TMessageParams;
+    xStream: TMemoryStream;
+  begin
+    xClient := TIPCClient.Create;
+    try
+      xClient.ServerName := SERVERNAME_COLLECT;
+
+      SetLength(xOutParams, 0);
+      AddFilesToParams(FilesToOpen, xOutParams);
+
+      xStream := TMemoryStream.Create;
+      try
+        BuildMessage(MESSAGE_COLLECTFILES, xOutParams, xStream);
+        xStream.Position := 0;
+        xClient.PostRequest(MESSAGETYPE_XML, xStream, xThisClientMessageId);
+      finally
+        xStream.Free;
+      end;
+    finally
+      xClient.Free;
+    end;
+  end;
+
+  procedure _WaitForFiles;
+  var
+    xLastCount, xNewCount: Integer;
+    xServer: TIPCServer;
+  begin
+    xServer := TIPCServer.Create;
+    try
+      xServer.ServerName := SERVERNAME_COLLECT;
+      //do not start server here
+      xLastCount := -1;
+      xNewCount := xServer.GetPendingRequestCount;
+      while xLastCount <> xNewCount do
+      begin
+        xLastCount := xNewCount;
+        Sleep(TIMEOUT_COLLECTFILES);
+        xNewCount := xServer.GetPendingRequestCount;
+      end;
+    finally
+      xServer.Free;
+    end;
+  end;
+
+  function _ReceiveAsServer: Boolean;
+  var
+    xServer: TIPCServer;
+    xInParams: TMessageParams;
+    xStream: TMemoryStream;
+    xMsgType: Integer;
+    xMessageType: string;
+  begin
+    xStream := TMemoryStream.Create;
+    xServer := TIPCServer.Create;
+    try
+      xServer.ServerName := SERVERNAME_COLLECT;
+      //files have to be handled only by one instance!
+      Result := xServer.FindHighestPendingRequestId = xThisClientMessageId;
+      if Result then
+      begin
+        //we are the highest client, handle the files
+        xServer.StartServer(False);
+      end else
+      begin
+        //we are not the highest client, maybe there are pending files, check that
+        {$IFNDEF MSWINDOWS}
+        //this code is not slowing up IDE start because if there was highest client found (the normal way), we close anyway
+        Randomize;
+        Sleep(50+Random(50));//random sleep in order to prevent double file locks on linux
+        {$ENDIF}
+        if not (xServer.StartServer(False) and (xServer.GetPendingRequestCount > 0)) then
+          Exit;//server is already running or there are no pending message -> close
+        Result := True;//no one handled handled the files, do it by myself
+      end;
+
+      FilesToOpen.Clear;
+      while xServer.PeekRequest(xStream, xMsgType{%H-}) do
+      if xMsgType = MESSAGETYPE_XML then
+      begin
+        if ParseMessage(xStream, xMessageType, xInParams) and
+           (xMessageType = MESSAGE_COLLECTFILES)
+        then
+          AddFilesFromParams(xInParams, FilesToOpen);
+      end;
+    finally
+      xStream.Free;
+      xServer.Free;
+    end;
+  end;
+begin
+  //if you select more files in explorer and open them, they are not opened in one process but one process is started per file
+  // -> collect them
+
+  //first send messages to queue (there is no server, no problem, it will collect the messages when it is created)
+  _SendToServer;
+
+  //now wait until we have everything
+  _WaitForFiles;
+
+  //now send them to one instance
+  outFilesWereSentToCollectingServer := not _ReceiveAsServer;
+end;
+
+procedure TIDEInstances.InitIDEInstances;
+var
+  xFilesWereSentToCollectingServer: Boolean;
+  I: Integer;
+begin
+  FForceNewInstance := CheckParamsForForceNewInstanceOpt;
+
+  //get cmd line filenames
+  FFilesToOpen := ExtractCmdLineFilenames;
+  for I := 0 to FilesToOpen.Count-1 do
+    FilesToOpen[I] := CleanAndExpandFilename(FilesToOpen[I]);
+
+  if FilesToOpen.Count > 0 then//if there are file in the cmd, check for multiple starting instances
+  begin
+    CollectFiles(xFilesWereSentToCollectingServer);
+    if xFilesWereSentToCollectingServer then
+    begin
+      FilesToOpen.Clear;
+      FStartIDE := False;
+    end;
+  end;
+end;
+
+{ TUniqueServer }
+
+procedure TUniqueServer.StartUnique(const aServerPrefix: string);
+var
+  I: Integer;
+begin
+  if Active then
+    StopServer;
+
+  I := 0;
+  while not Active do
+  begin
+    Inc(I);
+    if I < 10 then
+      ServerName := aServerPrefix+'0'+IntToStr(I)
+    else
+      ServerName := aServerPrefix+IntToStr(I);
+    StartServer;
+  end;
+end;
+
+{ TResponseClient }
+
+function TResponseClient.SendFilesToRunningInstance(const aFiles: TStrings;
+  var outModalErrorMessage, outModalErrorForceUniqueMessage,
+  outNotRespondingErrorMessage: string; var outHandleBringToFront: HWND
+  ): TStartNewInstanceResult;
+var
+  xStream: TMemoryStream;
+  xMsgType: Integer;
+  xResponseType: string;
+  xOutParams, xInParams: TMessageParams;
+begin
+  Result := ofrStartNewInstance;
+  xStream := TMemoryStream.Create;
+  try
+    //ask to show prompt
+    xStream.Clear;
+    SetLength(xOutParams, 0);
+    TIDEInstances.AddFilesToParams(aFiles, xOutParams);
+    TIDEInstances.BuildMessage(MESSAGE_STARTNEWINSTANCE, xOutParams, xStream);
+    xStream.Position := 0;
+    Self.PostRequest(MESSAGETYPE_XML, xStream);
+    xStream.Clear;
+    if PeekResponse(xStream, xMsgType{%H-}, TIMEOUT_OPENFILES) and
+       (xMsgType = MESSAGETYPE_XML) then
+    begin
+      xStream.Position := 0;
+      if TIDEInstances.ParseMessage(xStream, xResponseType, xInParams) and
+         (xResponseType = RESPONSE_OPENFILES) then
+      begin
+        Result := TStartNewInstanceResult(StrToIntDef(TIDEInstances.GetMessageParam(xInParams, PARAM_RESULT), 0));
+        outModalErrorMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_MODALERRORMESSAGE);
+        outModalErrorForceUniqueMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_FORCEUNIQUEMODALERRORMESSAGE);
+        outNotRespondingErrorMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_NOTRESPONDINGERRORMESSAGE);
+        outHandleBringToFront := StrToInt64Def(TIDEInstances.GetMessageParam(xInParams, PARAM_HANDLEBRINGTOFRONT), 0);
+      end;
+    end else//no response, the IDE is modal and cannot accept messages
+    begin
+      DeleteRequest;
+      Result := ofrNotResponding;
+    end;
+  finally
+    xStream.Free;
+  end;
+end;
+
+{ TMainServer }
+
+procedure TMainServer.CheckMessagesOnTimer(Sender: TObject);
+begin
+  DoCheckMessages;
+end;
+
+constructor TMainServer.Create;
+begin
+  inherited Create;
+
+  FMsgStream := TMemoryStream.Create;
+end;
+
+destructor TMainServer.Destroy;
+begin
+  FMsgStream.Free;
+  StopListening;
+
+  inherited Destroy;
+end;
+
+procedure TMainServer.DoStartNewInstance(const aMsgID: string;
+  const aInParams: TMessageParams);
+var
+  xResult: TStartNewInstanceResult;
+  xFiles: TStrings;
+  xParams: TMessageParams;
+begin
+  xResult := ofrStartNewInstance;
+  if Assigned(FStartNewInstanceEvent) then
+  begin
+    xFiles := TStringList.Create;
+    try
+      TIDEInstances.AddFilesFromParams(aInParams, xFiles);
+      FStartNewInstanceEvent(xFiles, xResult);
+    finally
+      xFiles.Free;
+    end;
+  end;
+
+  SetLength(xParams, 5);
+  xParams[0] := TIDEInstances.MessageParam(PARAM_RESULT, IntToStr(Ord(xResult)));
+  xParams[1] := TIDEInstances.MessageParam(PARAM_HANDLEBRINGTOFRONT, IntToStr(Application.MainFormHandle));
+  xParams[2] := TIDEInstances.MessageParam(PARAM_MODALERRORMESSAGE, dlgRunningInstanceModalError);
+  xParams[3] := TIDEInstances.MessageParam(PARAM_FORCEUNIQUEMODALERRORMESSAGE, dlgForceUniqueInstanceModalError);
+  xParams[4] := TIDEInstances.MessageParam(PARAM_NOTRESPONDINGERRORMESSAGE, dlgNotRespondingError);
+  SimpleResponse(aMsgID, RESPONSE_OPENFILES, xParams);
+end;
+
+procedure TMainServer.SimpleResponse(const aResponseToMsgID,
+  aResponseType: string; const aParams: array of TMessageParam);
+var
+  xStream: TMemoryStream;
+begin
+  xStream := TMemoryStream.Create;
+  try
+    TIDEInstances.BuildMessage(aResponseType, aParams, xStream);
+    xStream.Position := 0;
+    PostResponse(aResponseToMsgID, MESSAGETYPE_XML, xStream);
+  finally
+    xStream.Free;
+  end;
+end;
+
+procedure TMainServer.StartListening(const aStartNewInstanceEvent: TStartNewInstanceEvent);
+begin
+  Assert((FTimer = nil) and Assigned(aStartNewInstanceEvent));
+
+  FTimer := TTimer.Create(nil);
+  FTimer.OnTimer := @CheckMessagesOnTimer;
+  FTimer.Interval := 50;
+  FTimer.Enabled := True;
+
+  FStartNewInstanceEvent := aStartNewInstanceEvent;
+end;
+
+procedure TMainServer.StopListening;
+begin
+  FreeAndNil(FTimer);
+
+  FStartNewInstanceEvent := nil;
+end;
+
+procedure TMainServer.DoCheckMessages;
+var
+  xMessageType: string;
+  xParams: TMessageParams;
+  xMsgID: string;
+  xMsgType: Integer;
+begin
+  if Active then
+  begin
+    if PeekRequest(FMsgStream, xMsgID{%H-}, xMsgType{%H-}) and
+       (xMsgType = MESSAGETYPE_XML) and
+       (TIDEInstances.ParseMessage(FMsgStream, xMessageType, xParams)) and
+       (xMessageType = MESSAGE_STARTNEWINSTANCE)
+    then
+      DoStartNewInstance(xMsgID, xParams);
+  end;
+end;
+
+initialization
+  FLazIDEInstances := TIDEInstances.Create;
+  FLazIDEInstances.InitIDEInstances;
+
+finalization
+  FreeAndNil(FLazIDEInstances);
+
+end.
Index: ide/lazarus.pp
===================================================================
--- ide/lazarus.pp	(revision 49824)
+++ ide/lazarus.pp	(working copy)
@@ -52,6 +52,7 @@
   {$IFEND}
   SysUtils,
   Interfaces,
+  IDEInstances,//keep IDEInstances up so that it will be initialized soon
   Forms, LCLProc,
   IDEOptionsIntf,
   LazConf, IDEGuiCmdLine,
@@ -104,6 +105,10 @@
   {$ENDIF}
 
   Application.Initialize;
+  LazIDEInstances.PerformCheck;
+  if not LazIDEInstances.StartIDE then
+    Exit;
+  LazIDEInstances.StartServer;
   TMainIDE.ParseCmdLineOptions;
   if not SetupMainIDEInstance then exit;
   if Application.Terminated then exit;
Index: ide/lazarusidestrconsts.pas
===================================================================
--- ide/lazarusidestrconsts.pas	(revision 49824)
+++ ide/lazarusidestrconsts.pas	(working copy)
@@ -142,6 +142,14 @@
   lisMoveFiles2 = 'Move files?';
   lrsPLDDeleteSelected = 'Delete selected';
 
+  dlgMultipleInstances = 'Multiple Lazarus instances';
+  dlgMultipleInstances_AlwaysStartNew = 'always start a new instance';
+  dlgMultipleInstances_OpenFilesInRunning = 'open files in a running instance';
+  dlgMultipleInstances_ForceSingleInstance = 'do not allow multiple instances';
+  dlgRunningInstanceModalError = 'The running Lazarus instance cannot accept any files.'+sLineBreak+'Do you want to open them in a new IDE instance?'+sLineBreak+sLineBreak+'%s';
+  dlgForceUniqueInstanceModalError = 'The running Lazarus instance cannot accept any files.';
+  dlgNotRespondingError = 'Lazarus instance is running but not responding.';
+
   // *** Rest of the resource strings ***
 
   lisImportPackageListXml = 'Import package list (*.xml)';
Index: ide/lazarusmanager.pas
===================================================================
--- ide/lazarusmanager.pas	(revision 49824)
+++ ide/lazarusmanager.pas	(working copy)
@@ -92,8 +92,8 @@
   BaseUnix,
 {$ENDIF}
   Classes, SysUtils, Process, Forms, Controls, Dialogs, LCLProc,
-  UTF8Process, FileUtil, LazFileUtils, LazUTF8, FileProcs,
-  IDECmdLine, LazConf, Splash, BaseIDEIntf;
+  UTF8Process, FileUtil, FileProcs, LazUTF8, LazFileUtils,
+  IDECmdLine, LazConf, Splash, BaseIDEIntf, IDEInstances;
   
 type
 
@@ -244,6 +244,15 @@
   if FShowSplashOption then
     ShowSplash;
 
+  // we already handled IDEInstances, ignore it in lazarus EXE
+  if (FCmdLineParams.IndexOf(ForceNewInstanceOpt) = -1) then
+    FCmdLineParams.Add(ForceNewInstanceOpt);
+  // pass the AllowOpenLastProject parameter to lazarus EXE
+  if not LazIDEInstances.AllowOpenLastProject and
+     (FCmdLineParams.IndexOf(SkipLastProjectOpt) = -1)
+  then
+    FCmdLineParams.Add(SkipLastProjectOpt);
+
   // set primary config path
   PCP:=ExtractPrimaryConfigPath(FCmdLineParams);
   if PCP<>'' then
@@ -250,7 +259,7 @@
     SetPrimaryConfigPath(PCP);
 
   // get command line files
-  CmdLineFiles := ExtractCmdLineFilenames;
+  CmdLineFiles := LazIDEInstances.FilesToOpen;
   if CmdLineFiles<>nil then
   begin
     for i := 0 to CmdLineFiles.Count-1 do
Index: ide/main.pp
===================================================================
--- ide/main.pp	(revision 49824)
+++ ide/main.pp	(working copy)
@@ -157,6 +157,7 @@
   CleanDirDlg, CodeContextForm, AboutFrm, CompatibilityRestrictions,
   RestrictionBrowser, ProjectWizardDlg, IDECmdLine, IDEGuiCmdLine, CodeExplOpts,
   EditorMacroListViewer, SourceFileManager, EditorToolbarStatic,
+  IDEInstances,
   // main ide
   MainBar, MainIntf, MainBase;
 
@@ -187,6 +188,8 @@
     procedure HandleRemoteControlTimer(Sender: TObject);
     procedure HandleSelectFrame(Sender: TObject; var AComponentClass: TComponentClass);
     procedure OIChangedTimerTimer(Sender: TObject);
+    procedure LazInstancesStartNewInstance(const aFiles: TStrings;
+      var Result: TStartNewInstanceResult);
   public
     // file menu
     procedure mnuNewUnitClicked(Sender: TObject);
@@ -637,6 +640,7 @@
     OldCompilerFilename, OldLanguage: String;
     OIChangedTimer: TIdleTimer;
 
+    procedure DoDropFilesAsync(Data: PtrInt);
     procedure RenameInheritedMethods(AnUnitInfo: TUnitInfo; List: TStrings);
     function OIHelpProvider: TAbstractIDEHTMLProvider;
     // form editor and designer
@@ -950,6 +954,14 @@
   StartedByStartLazarus: boolean = false;
   ShowSetupDialog: boolean = false;
 
+type
+  TDoDropFilesAsyncParams = class(TComponent)
+  public
+    FileNames: array of string;
+    WindowIndex: Integer;
+    BringToFront: Boolean;
+  end;
+
 function FindDesignComponent(const aName: string): TComponent;
 var
   AnUnitInfo: TUnitInfo;
@@ -1556,6 +1568,7 @@
   DoShowMessagesView(false);           // reopen extra windows
   fUserInputSinceLastIdle:=true; // Idle work gets done initially before user action.
   MainIDEBar.ApplicationIsActivate:=true;
+  LazIDEInstances.StartListening(@LazInstancesStartNewInstance);
   FIDEStarted:=true;
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.StartIDE END');{$ENDIF}
 end;
@@ -1946,6 +1959,7 @@
 procedure TMainIDE.MainIDEFormClose(Sender: TObject;
   var CloseAction: TCloseAction);
 begin
+  LazIDEInstances.StopListening;
   DoCallNotifyHandler(lihtIDEClose);
   SaveEnvironment(true);
   if IDEDockMaster<>nil then
@@ -2182,119 +2196,116 @@
   {$ENDIF}
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject A');{$ENDIF}
   // load command line project or last project or create a new project
-  CmdLineFiles:=ExtractCmdLineFilenames;
-  try
-    ProjectLoaded:=false;
+  CmdLineFiles:=LazIDEInstances.FilesToOpen;
+  ProjectLoaded:=false;
 
-    // try command line project
-    if (CmdLineFiles<>nil) and (CmdLineFiles.Count>0) then begin
-      AProjectFilename:=CmdLineFiles[0];
-      if (CompareFileExt(AProjectFilename,'.lpr',false)=0) then
-        AProjectFilename:=ChangeFileExt(AProjectFilename,'.lpi');
-      // only try to load .lpi files, other files are loaded later
-      if (CompareFileExt(AProjectFilename,'.lpi',false)=0) then begin
-        AProjectFilename:=CleanAndExpandFilename(AProjectFilename);
-        if FileExistsUTF8(AProjectFilename) then begin
-          CmdLineFiles.Delete(0);
-          ProjectLoaded:=(DoOpenProjectFile(AProjectFilename,[])=mrOk);
-        end;
+  // try command line project
+  if (CmdLineFiles<>nil) and (CmdLineFiles.Count>0) then begin
+    AProjectFilename:=CmdLineFiles[0];
+    if (CompareFileExt(AProjectFilename,'.lpr',false)=0) then
+      AProjectFilename:=ChangeFileExt(AProjectFilename,'.lpi');
+    // only try to load .lpi files, other files are loaded later
+    if (CompareFileExt(AProjectFilename,'.lpi',false)=0) then begin
+      AProjectFilename:=CleanAndExpandFilename(AProjectFilename);
+      if FileExistsUTF8(AProjectFilename) then begin
+        CmdLineFiles.Delete(0);
+        ProjectLoaded:=(DoOpenProjectFile(AProjectFilename,[])=mrOk);
       end;
     end;
+  end;
 
-    // try loading last project if lazarus didn't fail last time
-    if (not ProjectLoaded)
-    and (not SkipAutoLoadingLastProject)
-    and (EnvironmentOptions.OpenLastProjectAtStart)
-    and (EnvironmentOptions.LastSavedProjectFile<>'')
-    and (EnvironmentOptions.LastSavedProjectFile<>RestoreProjectClosed)
-    and (FileExistsCached(EnvironmentOptions.LastSavedProjectFile))
-    then begin
-      if (not IDEProtocolOpts.LastProjectLoadingCrashed)
-      or AskIfLoadLastFailingProject then begin
-        // protocol that the IDE is trying to load the last project and did not
-        // yet succeed
-        IDEProtocolOpts.LastProjectLoadingCrashed := True;
-        IDEProtocolOpts.Save;
-        // try loading the project
-        ProjectLoaded:=
-          (DoOpenProjectFile(EnvironmentOptions.LastSavedProjectFile,[])=mrOk);
-        // protocol that the IDE was able to open the project without crashing
-        IDEProtocolOpts.LastProjectLoadingCrashed := false;
-        IDEProtocolOpts.Save;
-        if ProjectLoaded then
+  // try loading last project if lazarus didn't fail last time
+  if (not ProjectLoaded)
+  and (LazIDEInstances.AllowOpenLastProject)
+  and (not SkipAutoLoadingLastProject)
+  and (EnvironmentOptions.OpenLastProjectAtStart)
+  and (EnvironmentOptions.LastSavedProjectFile<>'')
+  and (EnvironmentOptions.LastSavedProjectFile<>RestoreProjectClosed)
+  and (FileExistsCached(EnvironmentOptions.LastSavedProjectFile))
+  then begin
+    if (not IDEProtocolOpts.LastProjectLoadingCrashed)
+    or AskIfLoadLastFailingProject then begin
+      // protocol that the IDE is trying to load the last project and did not
+      // yet succeed
+      IDEProtocolOpts.LastProjectLoadingCrashed := True;
+      IDEProtocolOpts.Save;
+      // try loading the project
+      ProjectLoaded:=
+        (DoOpenProjectFile(EnvironmentOptions.LastSavedProjectFile,[])=mrOk);
+      // protocol that the IDE was able to open the project without crashing
+      IDEProtocolOpts.LastProjectLoadingCrashed := false;
+      IDEProtocolOpts.Save;
+      if ProjectLoaded then
+      begin
+        PkgOpenFlags:=[pofAddToRecent];
+        for I := 0 to EnvironmentOptions.LastOpenPackages.Count-1 do
         begin
-          PkgOpenFlags:=[pofAddToRecent];
-          for I := 0 to EnvironmentOptions.LastOpenPackages.Count-1 do
-          begin
-            AFilename:=EnvironmentOptions.LastOpenPackages[I];
-            if AFilename='' then
-              continue;
-            if i<EnvironmentOptions.LastOpenPackages.Count-1 then
-              Include(PkgOpenFlags,pofMultiOpen)
-            else
-              Exclude(PkgOpenFlags,pofMultiOpen);
-            if PkgBoss.DoOpenPackageFile(AFilename,PkgOpenFlags,true)=mrAbort then begin
-              break;
-            end;
+          AFilename:=EnvironmentOptions.LastOpenPackages[I];
+          if AFilename='' then
+            continue;
+          if i<EnvironmentOptions.LastOpenPackages.Count-1 then
+            Include(PkgOpenFlags,pofMultiOpen)
+          else
+            Exclude(PkgOpenFlags,pofMultiOpen);
+          if PkgBoss.DoOpenPackageFile(AFilename,PkgOpenFlags,true)=mrAbort then begin
+            break;
           end;
-        end else
-        begin
-          DoCloseProject;
         end;
+      end else
+      begin
+        DoCloseProject;
       end;
     end;
-    {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject B');{$ENDIF}
+  end;
+  {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject B');{$ENDIF}
 
-    if (not ProjectLoaded) then
-    begin
-      if EnvironmentOptions.OpenLastProjectAtStart
-      and (EnvironmentOptions.LastSavedProjectFile=RestoreProjectClosed) then begin
-        // IDE was closed without a project => restore that state
-      end else begin
-        // create new project
-        DoNewProject(ProjectDescriptorApplication);
-      end;
+  if (not ProjectLoaded) then
+  begin
+    if EnvironmentOptions.OpenLastProjectAtStart
+    and (EnvironmentOptions.LastSavedProjectFile=RestoreProjectClosed) then begin
+      // IDE was closed without a project => restore that state
+    end else begin
+      // create new project
+      DoNewProject(ProjectDescriptorApplication);
     end;
+  end;
 
-    // load the cmd line files
-    if CmdLineFiles<>nil then begin
-      for i:=0 to CmdLineFiles.Count-1 do
-      Begin
-        AFilename:=CleanAndExpandFilename(CmdLineFiles.Strings[i]);
-        if not FileExistsCached(AFilename) then begin
-          debugln(['WARNING: command line file not found: "',AFilename,'"']);
-          continue;
+  // load the cmd line files
+  if CmdLineFiles<>nil then begin
+    for i:=0 to CmdLineFiles.Count-1 do
+    Begin
+      AFilename:=CleanAndExpandFilename(CmdLineFiles.Strings[i]);
+      if not FileExistsCached(AFilename) then begin
+        debugln(['WARNING: command line file not found: "',AFilename,'"']);
+        continue;
+      end;
+      if Project1=nil then begin
+        // to open a file a project is needed
+        // => create a project
+        DoNewProject(ProjectDescriptorEmptyProject);
+      end;
+      if CompareFileExt(AFilename,'.lpk',false)=0 then begin
+        if PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent,pofMultiOpen],true)=mrAbort
+        then
+          break;
+      end else begin
+        OpenFlags:=[ofAddToRecent,ofRegularFile];
+        if i<CmdLineFiles.Count then
+          Include(OpenFlags,ofMultiOpen);
+        if DoOpenEditorFile(AFilename,-1,-1,OpenFlags)=mrAbort then begin
+          break;
         end;
-        if Project1=nil then begin
-          // to open a file a project is needed
-          // => create a project
-          DoNewProject(ProjectDescriptorEmptyProject);
-        end;
-        if CompareFileExt(AFilename,'.lpk',false)=0 then begin
-          if PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent,pofMultiOpen],true)=mrAbort
-          then
-            break;
-        end else begin
-          OpenFlags:=[ofAddToRecent,ofRegularFile];
-          if i<CmdLineFiles.Count then
-            Include(OpenFlags,ofMultiOpen);
-          if DoOpenEditorFile(AFilename,-1,-1,OpenFlags)=mrAbort then begin
-            break;
-          end;
-        end;
       end;
     end;
+  end;
 
-    if Project1=nil then
-      DoNoProjectWizard(nil);
+  if Project1=nil then
+    DoNoProjectWizard(nil);
 
-    {$IFDEF IDE_DEBUG}
-    debugln('TMainIDE.Create B');
-    {$ENDIF}
-    {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject C');{$ENDIF}
-  finally
-    CmdLineFiles.Free;
-  end;
+  {$IFDEF IDE_DEBUG}
+  debugln('TMainIDE.Create B');
+  {$ENDIF}
+  {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject C');{$ENDIF}
 end;
 
 procedure TMainIDE.SetupRemoteControl;
@@ -5387,6 +5398,27 @@
   SetRecentFilesMenu;
 end;
 
+procedure TMainIDE.DoDropFilesAsync(Data: PtrInt);
+var
+  xParams: TDoDropFilesAsyncParams;
+begin
+  xParams := TDoDropFilesAsyncParams(Data);
+  try
+    if Length(xParams.FileNames) > 0 then
+      DoDropFiles(Self, xParams.FileNames, xParams.WindowIndex);
+    if xParams.BringToFront then
+    begin
+      if Application.MainForm.WindowState = wsMinimized then
+        UnhideIDE;
+      Application.BringToFront;
+      if SourceEditorManager.ActiveSourceWindow <> nil then
+        SourceEditorManager.ActiveSourceWindow.BringToFront;
+    end;
+  finally
+    xParams.Free;
+  end;
+end;
+
 function TMainIDE.DoSelectFrame: TComponentClass;
 var
   UnitList: TStringList;
@@ -9117,6 +9149,51 @@
   Result:=false;
 end;
 
+procedure TMainIDE.LazInstancesStartNewInstance(const aFiles: TStrings;
+  var Result: TStartNewInstanceResult);
+var
+  xParams: TDoDropFilesAsyncParams;
+  I: Integer;
+begin
+  if aFiles.Count > 0 then
+  begin
+    //there are files to open
+    if EnvironmentOptions.MultipleInstances = mioAlwaysStartNew then
+    begin//the user wants to open them in new instance!
+      Result := ofrStartNewInstance;
+    end else
+    if (not IsWindowEnabled(Application.MainForm.Handle) or//check that main is active
+       (Application.ModalLevel > 0))//check that no modal window is opened
+    then
+    begin
+      if EnvironmentOptions.MultipleInstances = mioForceSingleInstance then
+        Result := ofrForceSingleInstanceModalError
+      else
+        Result := ofrModalError;
+    end else
+      Result := ofrDoNotStart;
+  end else
+  begin
+    //no files to open
+    if EnvironmentOptions.MultipleInstances = mioForceSingleInstance then
+      Result := ofrDoNotStart
+    else
+      Result := ofrStartNewInstance;
+  end;
+
+  if Result in [ofrStartNewInstance, ofrModalError, ofrForceSingleInstanceModalError]  then
+    Exit;
+
+  //show up the current IDE and open files (if there are any)
+  xParams := TDoDropFilesAsyncParams.Create(Self);//we need direct response, do not wait to get the files opened!
+  SetLength(xParams.FileNames, aFiles.Count);
+  for I := 0 to aFiles.Count-1 do
+    xParams.FileNames[I] := aFiles[I];
+  xParams.WindowIndex := -1;
+  xParams.BringToFront := True;
+  Application.QueueAsyncCall(@DoDropFilesAsync, PtrInt(xParams));
+end;
+
 procedure TMainIDE.ApplyCodeToolChanges;
 begin
   // all changes were handled automatically by events, just clear the logs
Index: ide/startlazarus.lpi
===================================================================
--- ide/startlazarus.lpi	(revision 49824)
+++ ide/startlazarus.lpi	(working copy)
@@ -8,6 +8,7 @@
       </Flags>
       <SessionStorage Value="InIDEConfig"/>
       <MainUnit Value="0"/>
+      <UseXPManifest Value="True"/>
       <Icon Value="0"/>
     </General>
     <BuildModes Count="1">
Index: ide/startlazarus.lpr
===================================================================
--- ide/startlazarus.lpr	(revision 49824)
+++ ide/startlazarus.lpr	(working copy)
@@ -34,6 +34,7 @@
   redirect_stderr,
   Interfaces, SysUtils,
   Forms,
+  IDEInstances,
   LazarusManager;
   
 {$R *.res}
@@ -44,6 +45,9 @@
 begin
   redirect_stderr.DoShowWindow := False;
   Application.Initialize;
+  LazIDEInstances.PerformCheck;
+  if not LazIDEInstances.StartIDE then
+    Exit;
   ALazarusManager := TLazarusManager.Create(nil);
   ALazarusManager.Initialize;
   ALazarusManager.Run;
ide-multipleinstances-3.patch (66,558 bytes)   

Juha Manninen

2015-09-14 13:03

developer   ~0085897

Ok, I believe an IPC system is needed, although first many arguments sounded hypothetical. For example I still don't understand why many Lazarus instances should be opened if a "single instance" option is selected.

Like Stephano, I also feel uncomfortable that the existing IPC code is not reused. Would it be possible to improve and patch it? Then a patched version could be copied to Lazarus sources temporarily of course.
Otherwise we will end up with competing IPC libs and everybody is confused about which one to use.

The IPC solution should actually be discussed in fpc-pascal or fpc-dev mailing list.
The "right" people read it.

Another issue is which code should be used for a Unique Instance component in LCL. It should be discussed in Lazarus list.

Ondrej Pokorny

2015-09-14 13:05

developer   ~0085898

@ Stephano

>> - The multiple servers on windows should not happen, this is definitely a bug.
Yes it happens and it is a bug. simpleipc uses window handles. On Win you can define multiple windows with one name. So you first have to check if the window is registered and then register it. This is not an atomic operation so you need system-wide semaphores for it.

>> - Multiple clients talking to one server. Not simultaneously, because a FIFO is used.
There are no semaphores for Lazarus in Linux(?) Please correct me if I am wrong. So this is not possible with current simpleipc.
The advancedipc doesn't use FIFO (the processing order of the sent messages is random) so therefore it is possible.

>> - File locks are not working correctly when they are executed simultaneously on Linux
Yes, I know. Therefore you need a workaround.

------
All in all I definitely could have modified simpleipc but the changes are be so big that I decided to post it in a new unit. Basically the simpleipc.pas must have been replaced with "advancedipc.pas".

Ondrej Pokorny

2015-09-14 13:07

developer   ~0085899

>> Juha: For example I still don't understand why many Lazarus instances should be opened if a "single instance" option is selected.

There is nothing like it. What do you mean?

Juha Manninen

2015-09-14 13:28

developer   ~0085901

> There is nothing like it. What do you mean?

Maybe it was only in the initial version. You wrote "E.g. what to do if I want to be able to have multiple instances of Lazarus open but still automatically open files in running instances?"
Also you wanted to support debugging Lazarus itself with that option.

Ok, now you support opening a project in a new instance but files in an exising one. It is very good, I admit IPC is needed.
About the Simple IPC versus your Advanced IPC issue, I can later write in FPC list and ask what they think. You can do that, too, if you want.
Now I really must do other things ...

Ondrej Pokorny

2015-09-14 16:11

developer   ~0085904

>> You wrote "E.g. what to do if I want to be able to have multiple instances of Lazarus open but still automatically open files in running instances?"

Yes, this still is there. But it's not a "single instance" option. It is a "multi instance" option that still opens files from the explorer.
E.g. I need to work on multiple projects at once and because Lazarus doesn't support project groups (or I have to debug both projects at once), I have to open more IDE instances. But I still want the files from the explorer be opened in one of the Lazarus windows. I don't really care in which one - but (idea) it is possible to show a dialog to the user to ask in what Lazarus instance the files should be opened.

Sometimes I am bad at explaining things...

>> Ok, now you support opening a project in a new instance but files in an exising one.
I dropped this in the latest patch because I wanted to simplify the options (to have only 3). Now everything opens in the running instance, if "open files in a running instance" is set.

---
I wrote about the IPC issue on the fpc-devel list. Let's talk about it there and wait with this patch until IPC is resolved.

Juha Manninen

2015-09-29 11:52

developer   ~0086153

The mailing list thread :
 https://www.mail-archive.com/fpc-devel@lists.freepascal.org/msg33032.html

Ondrej Pokorny

2015-09-30 19:46

developer  

ide-multipleinstances-4.patch (75,120 bytes)   
Index: ide/advancedipc.pp
===================================================================
--- ide/advancedipc.pp	(nonexistent)
+++ ide/advancedipc.pp	(working copy)
@@ -0,0 +1,686 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2015 by Ondrej Pokorny
+
+    Unit implementing two-way (request/response) IPC between 1 server and more
+    clients, based on files.
+    The order of message processing is not deterministic (if there are more
+    pending messages, the server won't process them in the order they have
+    been sent to the server.
+    SendRequest and PostRequest+PeekResponse sequences from 1 client are
+    blocking and processed in correct order.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit advancedipc;
+
+{$mode objfpc}
+{$H+}
+
+interface
+
+uses
+  {$IFDEF UNIX}
+  baseunix,
+  {$endif}
+  sysutils, Classes;
+
+const
+  HEADER_VERSION = 2;
+
+type
+  TMessageType = LongInt;
+  TMessageHeader = packed record
+    HeaderVersion: Byte;
+    FileLock: Byte;//0 = unlocked, 1 = locked
+    MsgType: TMessageType;
+    MsgLen: Integer;
+    MsgVersion: Integer;
+  end;
+
+  TFileHandle = Classes.THandle;
+
+  TReleaseHandleStream = class(THandleStream)
+  public
+    destructor Destroy; override;
+  end;
+
+  TIPCBase = class(TComponent)
+  private
+    FGlobal: Boolean;
+    FFileName: string;
+    FServerID: string;
+    FMessageVersion: Integer;
+  protected
+    class function ServerIDToFileName(const aServerID: string; const aGlobal: Boolean): string;
+    function GetResponseFileName(const aRequestID: Integer): string;
+    function GetResponseFileName(const aRequestFileName: string): string;
+    function GetPeekedRequestFileName(const aRequestID: Integer): string;
+    function GetPeekedRequestFileName(const aRequestFileName: string): string;
+    function GetRequestPrefix: string;
+    function GetRequestFileName(const aRequestID: Integer): string;
+    function RequestFileNameToID(const aFileName: string): Integer;
+    function RequestExists(const aRequestFileName: string): Boolean;
+
+    function GetUniqueRequest(out outFileName: string): Integer;
+    procedure SetServerID(const aServerID: string); virtual;
+    procedure SetGlobal(const aGlobal: Boolean); virtual;
+
+    function CanReadMessage(const aFileName: string; out outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer): Boolean;
+    procedure DoPostMessage(const aFileName: string; const aMsgType: TMessageType; const aStream: TStream);
+
+    property FileName: string read FFileName;
+  public
+    class procedure FindRunningServers(const aServerIDPrefix: string;
+      const outServerIDs: TStrings; const aGlobal: Boolean = False);
+    class function ServerRunning(const aServerID: string; const aGlobal: Boolean = False): Boolean; overload;
+  public
+    //ServerID: name/ID of the server. Use only ['a'..'z', 'A'..'Z', '_'] characters
+    property ServerID: string read FServerID write SetServerID;
+    //Global: if true, processes from different users can communicate; false, processes only from current users can communicate
+    property Global: Boolean read FGlobal write SetGlobal;
+    //MessageVersion: only messages with the same MessageVersion can be delivered between server/client
+    property MessageVersion: Integer read FMessageVersion write FMessageVersion;
+  end;
+
+  TIPCClient = class(TIPCBase)
+  private
+    FLastMsgFileName: string;
+  public
+    //post request to server, do not wait until request is peeked; returns request ID
+    function PostRequest(const aMsgType: TMessageType; const aStream: TStream): Integer;
+    //send request to server, wait until request is peeked; returns True if request was peeked within the aTimeOut limit
+    function SendRequest(const aMsgType: TMessageType; const aStream: TStream; const aTimeOut: Integer): Boolean;
+    function SendRequest(const aMsgType: TMessageType; const aStream: TStream; const aTimeOut: Integer; out outRequestID: Integer): Boolean;
+    //peek a response from last request from this client
+    function PeekResponse(const aStream: TStream; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
+    //delete last request from this client
+    procedure DeleteRequest;
+    //check if server is running
+    function ServerRunning: Boolean; overload;
+  end;
+
+  TIPCServer = class(TIPCBase)
+  private
+    FFileHandle: TFileHandle;
+    FActive: Boolean;
+
+    function FindFirstRequest(out outFileName: string; out outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer): Integer;
+
+  protected
+    procedure SetServerID(const aServerID: string); override;
+    procedure SetGlobal(const aGlobal: Boolean); override;
+  public
+    constructor Create(aOwner: TComponent); override;
+    destructor Destroy; override;
+  public
+    //peek request and read the message into a stream
+    function PeekRequest(const aStream: TStream; out outMsgType: TMessageType): Boolean; overload;
+    function PeekRequest(const aStream: TStream; out outRequestID: Integer; out outMsgType: TMessageType): Boolean; overload;
+    function PeekRequest(const aStream: TStream; out outRequestID: Integer; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean; overload;
+    //only peek request, you have to read/delete the request manually with ReadRequest/DeleteRequest
+    function PeekRequest(out outMsgType: TMessageType): Boolean; overload;
+    function PeekRequest(out outRequestID: Integer; out outMsgType: TMessageType): Boolean; overload;
+    function PeekRequest(out outRequestID: Integer; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean; overload;
+    //read a peeked request (that hasn't been read yet)
+    function ReadRequest(const aRequestID: Integer; const aStream: TStream): Boolean;
+    //delete a peeked request (that hasn't been read yet)
+    procedure DeleteRequest(const aRequestID: Integer);
+
+    //post response to a request
+    procedure PostResponse(const aRequestID: Integer; const aMsgType: TMessageType; const aStream: TStream);
+
+    //find the highest request ID from all pending requests
+    function FindHighestPendingRequestId: Integer;
+    //get the pending request count
+    function GetPendingRequestCount: Integer;
+
+    //start server: returns true if unique and started
+    function StartServer(const aDeletePendingRequests: Boolean = True): Boolean;
+    //stop server: returns true if stopped
+    function StopServer(const aDeletePendingRequests: Boolean = True): Boolean;
+
+    //delete all pending requests and responses
+    procedure DeletePendingRequests;
+  public
+    //true if server runs (was started)
+    property Active: Boolean read FActive;
+  end;
+
+  EICPException = class(Exception);
+
+resourcestring
+  SErrInvalidServerID = 'Invalid server ID "%s". Please use only alphanumerical characters and underlines.';
+  SErrSetGlobalActive = 'You cannot change the global property when the server is active.';
+  SErrSetServerIDActive = 'You cannot change the server ID when the server is active.';
+
+implementation
+
+const
+  {$IFDEF UNIX}
+  GLOBAL_RIGHTS = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH or S_IWOTH;
+  {$ELSE}
+  GLOBAL_RIGHTS = 0;
+  {$ENDIF}
+
+{ TIPCBase }
+
+function TIPCBase.CanReadMessage(const aFileName: string; out
+  outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer
+  ): Boolean;
+var
+  xFileHandle: TFileHandle;
+  xHeader: TMessageHeader;
+begin
+  outStream := nil;
+  outMsgType := -1;
+  outMsgLen := 0;
+  Result := FileExists(aFileName);
+  if not Result then
+    Exit;
+
+  xFileHandle := FileOpen(aFileName, fmOpenRead or fmShareExclusive);
+  Result := xFileHandle <> feInvalidHandle;
+  if not Result then
+    Exit;
+
+  outStream := TReleaseHandleStream.Create(xFileHandle);
+
+  Result := (outStream.Size >= SizeOf(xHeader));
+  if not Result then
+  begin
+    FreeAndNil(outStream);
+    Exit;
+  end;
+
+  outStream.ReadBuffer(xHeader{%H-}, SizeOf(xHeader));
+  Result :=
+    (xHeader.HeaderVersion = HEADER_VERSION) and (xHeader.FileLock = 0) and
+    (xHeader.MsgVersion = MessageVersion) and
+    (outStream.Size = Int64(SizeOf(xHeader))+Int64(xHeader.MsgLen));
+  if not Result then
+  begin
+    FreeAndNil(outStream);
+    Exit;
+  end;
+  outMsgType := xHeader.MsgType;
+  outMsgLen := xHeader.MsgLen;
+end;
+
+function TIPCBase.GetUniqueRequest(out outFileName: string): Integer;
+begin
+  Randomize;
+  repeat
+    //if Randomize/Random is started from 2 processes at exactly same moment, it returns the same number! -> prevent duplicates by adding GetCurrentThreadId
+    Result := Integer(Int64(Random(High(Integer)))+GetCurrentThreadId);
+    outFileName := GetRequestFileName(Result);
+  until not RequestExists(outFileName);
+end;
+
+function TIPCBase.RequestExists(const aRequestFileName: string): Boolean;
+begin
+  Result :=
+    (FileExists(aRequestFileName) or
+     FileExists(GetResponseFileName(aRequestFileName)) or
+     FileExists(GetPeekedRequestFileName(aRequestFileName)));
+end;
+
+class function TIPCBase.ServerRunning(const aServerID: string;
+  const aGlobal: Boolean): Boolean;
+var
+  xServerFileHandle: TFileHandle;
+  xFileName: String;
+begin
+  xFileName := ServerIDToFileName(aServerID, aGlobal);
+  Result := FileExists(xFileName);
+  if Result then
+  begin//+ check -> we should not be able to access the file
+    xServerFileHandle := FileCreate(xFileName, fmOpenReadWrite or fmShareExclusive, GLOBAL_RIGHTS);
+    Result := (xServerFileHandle=feInvalidHandle);
+    if not Result then
+      FileClose(xServerFileHandle);
+  end;
+end;
+
+class function TIPCBase.ServerIDToFileName(const aServerID: string;
+  const aGlobal: Boolean): string;
+begin
+  Result := GetTempDir(aGlobal)+aServerID;
+end;
+
+procedure TIPCBase.SetGlobal(const aGlobal: Boolean);
+begin
+  if FGlobal = aGlobal then Exit;
+
+  FGlobal := aGlobal;
+  FFileName := ServerIDToFileName(FServerID, FGlobal);
+end;
+
+procedure TIPCBase.DoPostMessage(const aFileName: string;
+  const aMsgType: TMessageType; const aStream: TStream);
+var
+  xHeader: TMessageHeader;
+  xStream: TFileStream;
+begin
+  xHeader.HeaderVersion := HEADER_VERSION;
+  xHeader.FileLock := 1;//locking
+  xHeader.MsgType := aMsgType;
+  if Assigned(aStream) then
+    xHeader.MsgLen := aStream.Size-aStream.Position
+  else
+    xHeader.MsgLen := 0;
+  xHeader.MsgVersion := MessageVersion;
+
+  xStream := TFileStream.Create(aFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
+  try
+    xStream.WriteBuffer(xHeader, SizeOf(xHeader));
+    if Assigned(aStream) and (aStream.Size-aStream.Position > 0) then
+      xStream.CopyFrom(aStream, aStream.Size-aStream.Position);
+
+    xStream.Position := 0;//unlocking
+    xHeader.FileLock := 0;
+    xStream.WriteBuffer(xHeader, SizeOf(xHeader));
+  finally
+    xStream.Free;
+  end;
+end;
+
+function TIPCBase.RequestFileNameToID(const aFileName: string): Integer;
+begin
+  //the function prevents all responses/temp files to be handled
+  //only valid response files are returned
+  if (Length(aFileName) > 9) and (aFileName[Length(aFileName)-8] = '-') then
+    Result := StrToIntDef('$'+Copy(aFileName, Length(aFileName)-7, 8), -1)
+  else
+    Result := -1;
+end;
+
+class procedure TIPCBase.FindRunningServers(const aServerIDPrefix: string;
+  const outServerIDs: TStrings; const aGlobal: Boolean);
+var
+  xRec: TRawByteSearchRec;
+begin
+  if FindFirst(ServerIDToFileName(aServerIDPrefix+'*', aGlobal), faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      if (Pos('-', xRec.Name) = 0) and//file that we found is a pending message
+         ServerRunning(xRec.Name, aGlobal)
+      then
+        outServerIDs.Add(xRec.Name);
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCBase.GetPeekedRequestFileName(const aRequestID: Integer): string;
+begin
+  Result := GetPeekedRequestFileName(GetRequestFileName(aRequestID));
+end;
+
+function TIPCBase.GetPeekedRequestFileName(const aRequestFileName: string
+  ): string;
+begin
+  Result := aRequestFileName+'-t';
+end;
+
+function TIPCBase.GetRequestFileName(const aRequestID: Integer): string;
+begin
+  Result := GetRequestPrefix+IntToHex(aRequestID, 8);
+end;
+
+function TIPCBase.GetRequestPrefix: string;
+begin
+  Result := FFileName+'-';
+end;
+
+function TIPCBase.GetResponseFileName(const aRequestID: Integer): string;
+begin
+  Result := GetResponseFileName(GetRequestFileName(aRequestID));
+end;
+
+function TIPCBase.GetResponseFileName(const aRequestFileName: string): string;
+begin
+  Result := aRequestFileName+'-r';
+end;
+
+procedure TIPCBase.SetServerID(const aServerID: string);
+var
+  I: Integer;
+begin
+  if FServerID = aServerID then Exit;
+
+  for I := 1 to Length(aServerID) do
+  if not (aServerID[I] in ['a'..'z', 'A'..'Z', '0'..'9', '_']) then
+    raise EICPException.CreateFmt(SErrInvalidServerID , [aServerID]);
+
+  FServerID := aServerID;
+
+  FFileName := ServerIDToFileName(FServerID, FGlobal);
+end;
+
+{ TIPCClient }
+
+procedure TIPCClient.DeleteRequest;
+begin
+  if DeleteFile(FLastMsgFileName) then
+    FLastMsgFileName := '';
+end;
+
+function TIPCClient.PeekResponse(const aStream: TStream; out
+  outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
+var
+  xStart: QWord;
+  xStream: TStream;
+  xMsgLen: Integer;
+  xFileResponse: string;
+begin
+  aStream.Size := 0;
+  Result := False;
+  xStart := GetTickCount64;
+  repeat
+    xFileResponse := GetResponseFileName(FLastMsgFileName);
+    if CanReadMessage(xFileResponse, xStream, outMsgType, xMsgLen) then
+    begin
+      if xMsgLen > 0 then
+        aStream.CopyFrom(xStream, xMsgLen);
+      xStream.Free;
+      aStream.Position := 0;
+      DeleteFile(xFileResponse);
+      Exit(True);
+    end
+    else if aTimeOut > 20 then
+      Sleep(10);
+  until (GetTickCount64-xStart > aTimeOut);
+end;
+
+function TIPCClient.PostRequest(const aMsgType: TMessageType;
+  const aStream: TStream): Integer;
+begin
+  Result := GetUniqueRequest(FLastMsgFileName);
+  DeleteFile(GetResponseFileName(FLastMsgFileName));//delete old response, if there is any
+  DoPostMessage(FLastMsgFileName, aMsgType, aStream);
+end;
+
+function TIPCClient.SendRequest(const aMsgType: TMessageType;
+  const aStream: TStream; const aTimeOut: Integer): Boolean;
+var
+  xRequestID: Integer;
+begin
+  Result := SendRequest(aMsgType, aStream, aTimeOut, xRequestID);
+end;
+
+function TIPCClient.SendRequest(const aMsgType: TMessageType;
+  const aStream: TStream; const aTimeOut: Integer; out outRequestID: Integer
+  ): Boolean;
+var
+  xStart: QWord;
+  xRequestFileName: string;
+begin
+  outRequestID := PostRequest(aMsgType, aStream);
+  Result := False;
+
+  xRequestFileName := GetRequestFileName(outRequestID);
+  xStart := GetTickCount64;
+  repeat
+    if not FileExists(xRequestFileName) then
+      Exit(True)
+    else if aTimeOut > 20 then
+      Sleep(10);
+  until (GetTickCount64-xStart > aTimeOut);
+end;
+
+function TIPCClient.ServerRunning: Boolean;
+begin
+  Result := ServerRunning(ServerID, Global);
+end;
+
+{ TReleaseHandleStream }
+
+destructor TReleaseHandleStream.Destroy;
+begin
+  FileClose(Handle);
+
+  inherited Destroy;
+end;
+
+{ TIPCServer }
+
+procedure TIPCServer.DeletePendingRequests;
+var
+  xRec: TRawByteSearchRec;
+  xDir: string;
+begin
+  xDir := ExtractFilePath(FFileName);
+  if FindFirst(GetRequestPrefix+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      DeleteFile(xDir+xRec.Name);
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+procedure TIPCServer.DeleteRequest(const aRequestID: Integer);
+begin
+  DeleteFile(GetPeekedRequestFileName(aRequestID));
+end;
+
+constructor TIPCServer.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+
+  FFileHandle := feInvalidHandle;
+end;
+
+destructor TIPCServer.Destroy;
+begin
+  if Active then
+    StopServer;
+
+  inherited Destroy;
+end;
+
+function TIPCServer.FindFirstRequest(out outFileName: string; out
+  outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer
+  ): Integer;
+var
+  xRec: TRawByteSearchRec;
+begin
+  outFileName := '';
+  outStream := nil;
+  outMsgType := -1;
+  outMsgLen := 0;
+  Result := -1;
+  if FindFirst(GetRequestPrefix+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      Result := RequestFileNameToID(xRec.Name);
+      if Result >= 0 then
+      begin
+        outFileName := GetRequestFileName(Result);
+        if not CanReadMessage(outFileName, outStream, outMsgType, outMsgLen) then
+          Result := -1;
+      end;
+    until (Result >= 0) or (FindNext(xRec) <> 0);
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.FindHighestPendingRequestId: Integer;
+var
+  xRec: TRawByteSearchRec;
+  xRequestID, xHighestId: LongInt;
+begin
+  xHighestId := -1;
+  Result := -1;
+  if FindFirst(GetRequestPrefix+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      xRequestID := RequestFileNameToID(xRec.Name);
+      if xRequestID > xHighestId then
+      begin
+        xHighestId := xRequestID;
+        Result := xRequestID;
+      end;
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.GetPendingRequestCount: Integer;
+var
+  xRec: TRawByteSearchRec;
+begin
+  Result := 0;
+  if FindFirst(GetRequestPrefix+'*', faAnyFile, xRec) = 0 then
+  begin
+    repeat
+      if RequestFileNameToID(xRec.Name) >= 0 then
+        Inc(Result);
+    until FindNext(xRec) <> 0;
+  end;
+  FindClose(xRec);
+end;
+
+function TIPCServer.PeekRequest(out outRequestID: Integer; out
+  outMsgType: TMessageType): Boolean;
+var
+  xStream: TStream;
+  xMsgLen: Integer;
+  xMsgFileName: string;
+begin
+  outMsgType := -1;
+  xMsgFileName := '';
+  outRequestID := FindFirstRequest(xMsgFileName, xStream, outMsgType, xMsgLen);
+  Result := outRequestID >= 0;
+  if Result then
+  begin
+    xStream.Free;
+    RenameFile(xMsgFileName, GetPeekedRequestFileName(xMsgFileName));
+  end;
+end;
+
+function TIPCServer.PeekRequest(out outRequestID: Integer; out
+  outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
+var
+  xStart: QWord;
+begin
+  Result := False;
+  xStart := GetTickCount64;
+  repeat
+    if PeekRequest(outRequestID, outMsgType) then
+      Exit(True)
+    else if aTimeOut > 20 then
+      Sleep(10);
+  until (GetTickCount64-xStart > aTimeOut);
+end;
+
+function TIPCServer.PeekRequest(out outMsgType: TMessageType): Boolean;
+var
+  xRequestID: Integer;
+begin
+  Result := PeekRequest(xRequestID, outMsgType);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; out outRequestID: Integer;
+  out outMsgType: TMessageType): Boolean;
+begin
+  Result := PeekRequest(outRequestID, outMsgType);
+  if Result then
+    Result := ReadRequest(outRequestID, aStream);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; out outRequestID: Integer;
+  out outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
+begin
+  Result := PeekRequest(outRequestID, outMsgType, aTimeOut);
+  if Result then
+    Result := ReadRequest(outRequestID, aStream);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; out
+  outMsgType: TMessageType): Boolean;
+var
+  xRequestID: Integer;
+begin
+  Result := PeekRequest(aStream, xRequestID, outMsgType);
+end;
+
+procedure TIPCServer.PostResponse(const aRequestID: Integer;
+  const aMsgType: TMessageType; const aStream: TStream);
+begin
+  DoPostMessage(GetResponseFileName(aRequestID), aMsgType, aStream);
+end;
+
+function TIPCServer.ReadRequest(const aRequestID: Integer; const aStream: TStream
+  ): Boolean;
+var
+  xStream: TStream;
+  xMsgLen: Integer;
+  xMsgType: TMessageType;
+  xFileRequest: string;
+begin
+  aStream.Size := 0;
+  xFileRequest := GetPeekedRequestFileName(aRequestID);
+  Result := CanReadMessage(xFileRequest, xStream, xMsgType, xMsgLen);
+  if Result then
+  begin
+    if xMsgLen > 0 then
+      aStream.CopyFrom(xStream, xMsgLen);
+    xStream.Free;
+    aStream.Position := 0;
+    DeleteFile(xFileRequest);
+  end;
+end;
+
+procedure TIPCServer.SetGlobal(const aGlobal: Boolean);
+begin
+  if Active then
+    raise EICPException.Create(SErrSetGlobalActive);
+
+  inherited SetGlobal(aGlobal);
+end;
+
+procedure TIPCServer.SetServerID(const aServerID: string);
+begin
+  if Active then
+    raise EICPException.Create(SErrSetServerIDActive);
+
+  inherited SetServerID(aServerID);
+end;
+
+function TIPCServer.StartServer(const aDeletePendingRequests: Boolean): Boolean;
+begin
+  if Active then
+    Exit(True);
+
+  FFileHandle := FileCreate(FFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
+  Result := (FFileHandle<>feInvalidHandle);
+  FActive := Result;
+  if Result and aDeletePendingRequests then
+    DeletePendingRequests;
+end;
+
+function TIPCServer.StopServer(const aDeletePendingRequests: Boolean): Boolean;
+begin
+  if not Active then
+    Exit(True);
+
+  if FFileHandle<>feInvalidHandle then
+    FileClose(FFileHandle);
+  Result := DeleteFile(FFileName);
+
+  if aDeletePendingRequests then
+    DeletePendingRequests;
+
+  FActive := False;
+end;
+
+end.
Index: ide/environmentopts.pp
===================================================================
--- ide/environmentopts.pp	(revision 49901)
+++ ide/environmentopts.pp	(working copy)
@@ -184,6 +184,15 @@
       'Never'
     );
 
+type
+  TIDEMultipleInstancesOption = (mioAlwaysStartNew, mioOpenFilesInRunning, mioForceSingleInstance);
+const
+  IDEMultipleInstancesOptionNames: array[TIDEMultipleInstancesOption] of string = (
+    'AlwaysStartNew',      // mioAlwaysStartNew
+    'OpenFilesInRunning',  // mioOpenFilesInRunning
+    'ForceSingleInstance'  // mioForceSingleInstance
+    );
+
   { Messages window }
 type
   TMsgWndFileNameStyle = (
@@ -485,6 +494,7 @@
     FRecentPackageFiles: TStringList;
     FMaxRecentPackageFiles: integer;
     FOpenLastProjectAtStart: boolean;
+    FMultipleInstances: TIDEMultipleInstancesOption;
     // Prevent repopulating Recent project files menu with example projects if it was already cleared up.
     FAlreadyPopulatedRecentFiles : Boolean;
 
@@ -737,6 +747,8 @@
     property LastOpenPackages: TLastOpenPackagesList read FLastOpenPackages;
     property OpenLastProjectAtStart: boolean read FOpenLastProjectAtStart
                                              write FOpenLastProjectAtStart;
+    property MultipleInstances: TIDEMultipleInstancesOption read FMultipleInstances
+                                                                write FMultipleInstances;
     property FileDialogFilter: string read FFileDialogFilter write FFileDialogFilter;
 
     // backup
@@ -812,6 +824,7 @@
 function CharCaseFileActionNameToType(const Action: string): TCharCaseFileAction;
 function UnitRenameReferencesActionNameToType(const Action: string): TUnitRenameReferencesAction;
 function StrToMsgWndFilenameStyle(const s: string): TMsgWndFileNameStyle;
+function StrToIDEMultipleInstancesOption(const s: string): TIDEMultipleInstancesOption;
 
 function SimpleDirectoryCheck(const OldDir, NewDir,
   NotFoundErrMsg: string; out StopChecking: boolean): boolean;
@@ -881,6 +894,13 @@
   Result:=mwfsShort;
 end;
 
+function StrToIDEMultipleInstancesOption(const s: string): TIDEMultipleInstancesOption;
+begin
+  for Result in TIDEMultipleInstancesOption do
+    if CompareText(s,IDEMultipleInstancesOptionNames[Result])=0 then exit;
+  Result:=mioAlwaysStartNew;
+end;
+
 function SimpleDirectoryCheck(const OldDir, NewDir,
   NotFoundErrMsg: string; out StopChecking: boolean): boolean;
 var
@@ -1324,6 +1344,7 @@
   FRecentPackageFiles:=TStringList.Create;
   FMaxRecentPackageFiles:=DefaultMaxRecentPackageFiles;
   FOpenLastProjectAtStart:=true;
+  FMultipleInstances:=mioAlwaysStartNew;
 
   // backup
   with FBackupInfoProjectFiles do begin
@@ -1761,6 +1782,7 @@
       Path+'UnitRenameReferencesAction/Value',UnitRenameReferencesActionNames[urraAsk]));
     FAskForFilenameOnNewFile:=FXMLCfg.GetValue(Path+'AskForFilenameOnNewFile/Value',false);
     FLowercaseDefaultFilename:=FXMLCfg.GetValue(Path+'LowercaseDefaultFilename/Value',true);
+    FMultipleInstances:=StrToIDEMultipleInstancesOption(FXMLCfg.GetValue(Path+'MultipleInstances/Value',''));
 
     // fpdoc
     FPDocPaths := FXMLCfg.GetValue(Path+'LazDoc/Paths','');
@@ -2070,6 +2092,11 @@
                              FAskForFilenameOnNewFile,false);
     FXMLCfg.SetDeleteValue(Path+'LowercaseDefaultFilename/Value',
                              FLowercaseDefaultFilename,true);
+    if FMultipleInstances = mioAlwaysStartNew then
+      FXMLCfg.DeletePath(Path+'MultipleInstances')
+    else
+      FXMLCfg.SetValue(Path+'MultipleInstances/Value',IDEMultipleInstancesOptionNames[FMultipleInstances]);
+
     // fpdoc
     FXMLCfg.SetDeleteValue(Path+'LazDoc/Paths',FPDocPaths,'');
 
Index: ide/frames/files_options.lfm
===================================================================
--- ide/frames/files_options.lfm	(revision 49901)
+++ ide/frames/files_options.lfm	(working copy)
@@ -47,12 +47,12 @@
   end
   object ShowCompileDialogCheckBox: TCheckBox
     AnchorSideLeft.Control = Owner
-    AnchorSideTop.Control = OpenLastProjectAtStartCheckBox
+    AnchorSideTop.Control = MultipleInstancesComboBox
     AnchorSideTop.Side = asrBottom
     AnchorSideRight.Side = asrBottom
     Left = 2
     Height = 19
-    Top = 69
+    Top = 94
     Width = 180
     BorderSpacing.Top = 2
     Caption = 'ShowCompileDialogCheckBox'
@@ -65,7 +65,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 117
+    Top = 142
     Width = 82
     BorderSpacing.Top = 10
     Caption = 'LazarusDirLabel'
@@ -80,7 +80,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 132
+    Top = 157
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -94,7 +94,7 @@
     AnchorSideRight.Control = LazarusDirButton
     Left = 2
     Height = 23
-    Top = 132
+    Top = 157
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -108,7 +108,7 @@
     AnchorSideRight.Control = CompilerPathButton
     Left = 2
     Height = 23
-    Top = 176
+    Top = 201
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -123,7 +123,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 176
+    Top = 201
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -136,7 +136,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 161
+    Top = 186
     Width = 101
     BorderSpacing.Top = 6
     Caption = 'CompilerPathLabel'
@@ -149,7 +149,7 @@
     AnchorSideRight.Control = FPCSourceDirButton
     Left = 2
     Height = 23
-    Top = 220
+    Top = 245
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -164,7 +164,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 220
+    Top = 245
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -177,7 +177,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 205
+    Top = 230
     Width = 100
     BorderSpacing.Top = 6
     Caption = 'FPCSourceDirLabel'
@@ -189,7 +189,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 249
+    Top = 274
     Width = 81
     BorderSpacing.Top = 6
     Caption = 'MakePathLabel'
@@ -201,7 +201,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 293
+    Top = 318
     Width = 91
     BorderSpacing.Top = 6
     Caption = 'TestBuildDirLabel'
@@ -214,7 +214,7 @@
     AnchorSideRight.Control = MakePathButton
     Left = 2
     Height = 23
-    Top = 264
+    Top = 289
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -229,7 +229,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 264
+    Top = 289
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -243,7 +243,7 @@
     AnchorSideRight.Control = TestBuildDirButton
     Left = 2
     Height = 23
-    Top = 308
+    Top = 333
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -258,7 +258,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 308
+    Top = 333
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -272,7 +272,7 @@
     AnchorSideRight.Side = asrBottom
     Left = 32
     Height = 19
-    Top = 88
+    Top = 113
     Width = 206
     BorderSpacing.Left = 30
     Caption = 'AutoCloseCompileDialogCheckBox'
@@ -284,7 +284,7 @@
     AnchorSideTop.Side = asrBottom
     Left = 2
     Height = 15
-    Top = 337
+    Top = 362
     Width = 153
     Alignment = taRightJustify
     BorderSpacing.Top = 6
@@ -301,7 +301,7 @@
     AnchorSideBottom.Side = asrBottom
     Left = 542
     Height = 23
-    Top = 352
+    Top = 377
     Width = 25
     Anchors = [akTop, akRight, akBottom]
     Caption = '...'
@@ -317,7 +317,7 @@
     AnchorSideRight.Control = CompilerTranslationFileButton
     Left = 2
     Height = 23
-    Top = 352
+    Top = 377
     Width = 540
     Anchors = [akTop, akLeft, akRight]
     ItemHeight = 15
@@ -365,4 +365,27 @@
     BorderSpacing.Around = 2
     TabOrder = 16
   end
+  object MultipleInstancesLabel: TLabel
+    AnchorSideLeft.Control = Owner
+    AnchorSideTop.Control = MultipleInstancesComboBox
+    AnchorSideTop.Side = asrCenter
+    Left = 2
+    Height = 15
+    Top = 73
+    Width = 121
+    Caption = 'MultipleInstancesLabel'
+    ParentColor = False
+  end
+  object MultipleInstancesComboBox: TComboBox
+    AnchorSideLeft.Control = MultipleInstancesLabel
+    AnchorSideLeft.Side = asrBottom
+    Left = 131
+    Height = 23
+    Top = 69
+    Width = 238
+    BorderSpacing.Left = 8
+    ItemHeight = 15
+    Style = csDropDownList
+    TabOrder = 17
+  end
 end
Index: ide/frames/files_options.pas
===================================================================
--- ide/frames/files_options.pas	(revision 49901)
+++ ide/frames/files_options.pas	(working copy)
@@ -40,6 +40,7 @@
 
   TFilesOptionsFrame = class(TAbstractIDEOptionsEditor)
     AutoCloseCompileDialogCheckBox: TCheckBox;
+    MultipleInstancesComboBox: TComboBox;
     CompilerTranslationFileButton:TButton;
     CompilerTranslationFileComboBox:TComboBox;
     CompilerTranslationFileLabel:TLabel;
@@ -49,6 +50,7 @@
     FPCSourceDirButton:TButton;
     FPCSourceDirComboBox:TComboBox;
     FPCSourceDirLabel:TLabel;
+    MultipleInstancesLabel: TLabel;
     lblCenter: TLabel;
     LazarusDirButton:TButton;
     LazarusDirComboBox:TComboBox;
@@ -229,6 +231,16 @@
     Add(ProgramDirectory(true));
     EndUpdate;
   end;
+  MultipleInstancesLabel.Caption := dlgMultipleInstances;
+  with MultipleInstancesComboBox.Items do
+  begin
+    BeginUpdate;
+    Add(dlgMultipleInstances_AlwaysStartNew);
+    Add(dlgMultipleInstances_OpenFilesInRunning);
+    Add(dlgMultipleInstances_ForceSingleInstance);
+    EndUpdate;
+  end;
+  Assert(MultipleInstancesComboBox.Items.Count = Ord(High(TIDEMultipleInstancesOption))+1);
 
   CompilerPathLabel.Caption:=Format(dlgFpcExecutable,[GetDefaultCompilerFilename]);
   FPCSourceDirLabel.Caption:=dlgFpcSrcPath;
@@ -361,6 +373,8 @@
     // open last project at start
     OpenLastProjectAtStartCheckBox.Checked:=OpenLastProjectAtStart;
 
+    MultipleInstancesComboBox.ItemIndex := Ord(MultipleInstances);
+
     // compile dialog
     fOldShowCompileDialog:=ShowCompileDialog;
     ShowCompileDialogCheckBox.Checked:=ShowCompileDialog;
@@ -391,6 +405,7 @@
     MaxRecentOpenFiles := MaxRecentOpenFilesSpin.Value;
     MaxRecentProjectFiles := MaxRecentProjectFilesSpin.Value;
     OpenLastProjectAtStart:=OpenLastProjectAtStartCheckBox.Checked;
+    MultipleInstances := TIDEMultipleInstancesOption(MultipleInstancesComboBox.ItemIndex);
     ShowCompileDialog := ShowCompileDialogCheckBox.Checked;
     AutoCloseCompileDialog := AutoCloseCompileDialogCheckBox.Checked;
   end;
Index: ide/idecmdline.pas
===================================================================
--- ide/idecmdline.pas	(revision 49901)
+++ ide/idecmdline.pas	(working copy)
@@ -50,6 +50,7 @@
   NoSplashScreenOptLong='--no-splash-screen';
   NoSplashScreenOptShort='--nsc';
   StartedByStartLazarusOpt='--started-by-startlazarus';
+  ForceNewInstanceOpt='--force-new-instance';
   SkipLastProjectOpt='--skip-last-project';
   DebugLogOpt='--debug-log=';
   DebugLogOptEnable='--debug-enable=';
Index: ide/ideinstances.pas
===================================================================
--- ide/ideinstances.pas	(nonexistent)
+++ ide/ideinstances.pas	(working copy)
@@ -0,0 +1,737 @@
+{
+ /***************************************************************************
+                              ideinstances.pas
+                              ----------------
+
+ ***************************************************************************/
+
+ ***************************************************************************
+ *                                                                         *
+ *   This source is free software; you can redistribute it and/or modify   *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This code is distributed in the hope that it will be useful, but      *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   General Public License for more details.                              *
+ *                                                                         *
+ *   A copy of the GNU General Public License is available on the World    *
+ *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
+ *   obtain it by writing to the Free Software Foundation,                 *
+ *   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
+ *                                                                         *
+ ***************************************************************************
+
+  Author: Ondrej Pokorny
+
+  Abstract:
+    This unit handles one/multiple Lazarus IDE instances.
+
+}
+unit IDEInstances;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  sysutils, Interfaces, Classes, Controls, Forms, Dialogs, ExtCtrls,
+  LCLProc, LCLIntf, LCLType, AdvancedIPC,
+  LazFileUtils, LazUTF8, Laz2_DOM, laz2_XMLRead, laz2_XMLWrite,
+  LazarusIDEStrConsts, IDECmdLine;
+
+type
+  TStartNewInstanceResult = (ofrStartNewInstance, ofrDoNotStart, ofrModalError, ofrForceSingleInstanceModalError, ofrNotResponding);
+  TStartNewInstanceEvent = procedure(const aFiles: TStrings;
+    var outResult: TStartNewInstanceResult) of object;
+
+  TMessageParam = record
+    Name: string;
+    Value: string;
+  end;
+  TMessageParams = array of TMessageParam;
+
+  TUniqueServer = class(TIPCServer)
+  public
+    procedure StartUnique(const aServerPrefix: string);
+  end;
+
+  TMainServer = class(TUniqueServer)
+  private
+    FStartNewInstanceEvent: TStartNewInstanceEvent;
+    FTimer: TTimer;
+    FMsgStream: TMemoryStream;
+
+    procedure DoStartNewInstance(const aMsgID: Integer; const aInParams: TMessageParams);
+
+    procedure SimpleResponse(const aResponseToMsgID: Integer;
+      const aResponseType: string; const aParams: array of TMessageParam);
+
+    procedure DoCheckMessages;
+    procedure CheckMessagesOnTimer(Sender: TObject);
+
+    procedure StartListening(const aStartNewInstanceEvent: TStartNewInstanceEvent);
+    procedure StopListening;
+
+  public
+    constructor Create(aOwner: TComponent); override;
+    destructor Destroy; override;
+  end;
+
+  TResponseClient = class(TIPCClient)
+  public
+    function AllowStartNewInstance(
+      const aFiles: TStrings; var outModalErrorMessage,
+      outModalErrorForceUniqueMessage, outNotRespondingErrorMessage: string;
+      var outHandleBringToFront: HWND): TStartNewInstanceResult;
+  end;
+
+  TIDEInstances = class(TComponent)
+  private
+    FMainServer: TMainServer;//running IDE
+    FStartIDE: Boolean;// = True;
+    FForceNewInstance: Boolean;
+    FAllowOpenLastProject: Boolean;// = True;
+    FFilesToOpen: TStrings;
+
+    class procedure AddFilesToParams(const aFiles: TStrings;
+      var ioParams: TMessageParams); static;
+    class procedure AddFilesFromParams(const aParams: TMessageParams;
+      const aFiles: TStrings); static;
+    class procedure BuildMessage(const aMessageType: string;
+      const aParams: array of TMessageParam; const aStream: TStream); static;
+    class function MessageParam(const aName, aValue: string): TMessageParam; static;
+    class function ParseMessage(const aStream: TStream; out outMessageType: string;
+      out outParams: TMessageParams): Boolean; static;
+    class function GetMessageParam(const aParams: array of TMessageParam;
+      const aParamName: string): string; static;
+
+    function CheckParamsForForceNewInstanceOpt: Boolean;
+
+    procedure CollectFiles(out
+      outFilesWereSentToCollectingServer: Boolean);
+
+    function AllowStartNewInstance(const aFiles: TStrings;
+      var outModalErrorMessage, outModalErrorForceUniqueMessage, outNotRespondingErrorMessage: string;
+      var outHandleBringToFront: HWND): TStartNewInstanceResult;
+    procedure InitIDEInstances;
+  public
+    constructor Create(aOwner: TComponent); override;
+    destructor Destroy; override;
+  public
+    procedure PerformCheck;//call PerformCheck after Application.Initialize - it can open dialogs!
+
+    procedure StartServer;
+    procedure StopServer;
+    procedure StartListening(const aStartNewInstanceEvent: TStartNewInstanceEvent);
+    procedure StopListening;
+
+    function StartIDE: Boolean;//can the IDE be started?
+    function AllowOpenLastProject: Boolean;//if a secondary IDE is starting, do NOT reopen last project!
+    function FilesToOpen: TStrings;
+  end;
+
+function LazIDEInstances: TIDEInstances;
+
+implementation
+
+const
+  SERVERPREFIX_MAIN = 'LazarusMain';
+  SERVERNAME_COLLECT = 'LazarusCollect';
+  MESSAGETYPE_XML = 2;
+  ELEMENT_ROOT = 'ideinstances';
+  ATTR_VALUE = 'value';
+  ATTR_MESSAGE_TYPE = 'msgtype';
+  MESSAGE_STARTNEWINSTANCE = 'startnewinstance';
+  RESPONSE_OPENFILES = 'openfilesResponse';
+  TIMEOUT_OPENFILES = 1000;
+  MESSAGE_COLLECTFILES = 'collectfiles';
+  TIMEOUT_COLLECTFILES = 100;
+  PARAM_FILE = 'file';
+  PARAM_RESULT = 'result';
+  PARAM_HANDLEBRINGTOFRONT = 'handlebringtofront';
+  PARAM_MODALERRORMESSAGE = 'modalerrormessage';
+  PARAM_FORCEUNIQUEMODALERRORMESSAGE = 'forceuniquemodalerrormessage';
+  PARAM_NOTRESPONDINGERRORMESSAGE = 'notrespondingerrormessage';
+
+var
+  FLazIDEInstances: TIDEInstances;
+
+function LazIDEInstances: TIDEInstances;
+begin
+  Result := FLazIDEInstances;
+end;
+
+{ TIDEInstances }
+
+class function TIDEInstances.MessageParam(const aName, aValue: string): TMessageParam;
+begin
+  Result.Name := aName;
+  Result.Value := aValue;
+end;
+
+function TIDEInstances.StartIDE: Boolean;
+begin
+  Result := FStartIDE;
+end;
+
+function TIDEInstances.AllowOpenLastProject: Boolean;
+begin
+  Result := FAllowOpenLastProject;
+end;
+
+function TIDEInstances.FilesToOpen: TStrings;
+begin
+  if not Assigned(FFilesToOpen) then
+    FFilesToOpen := TStringList.Create;
+  Result := FFilesToOpen;
+end;
+
+procedure TIDEInstances.StartListening(const aStartNewInstanceEvent: TStartNewInstanceEvent);
+begin
+  Assert(Assigned(FMainServer));
+
+  FMainServer.StartListening(aStartNewInstanceEvent);
+end;
+
+procedure TIDEInstances.StartServer;
+begin
+  Assert(FMainServer = nil);
+
+  FMainServer := TMainServer.Create(Self);
+  FMainServer.StartUnique(SERVERPREFIX_MAIN);
+end;
+
+procedure TIDEInstances.StopListening;
+begin
+  FMainServer.StopListening;
+end;
+
+procedure TIDEInstances.StopServer;
+begin
+  FreeAndNil(FMainServer);
+end;
+
+class procedure TIDEInstances.AddFilesFromParams(const aParams: TMessageParams;
+  const aFiles: TStrings);
+var
+  I: Integer;
+begin
+  //do not clear aFiles
+  for I := Low(aParams) to High(aParams) do
+    if aParams[I].Name = PARAM_FILE then
+      aFiles.Add(aParams[I].Value);
+end;
+
+class procedure TIDEInstances.AddFilesToParams(const aFiles: TStrings;
+  var ioParams: TMessageParams);
+var
+  xStartIndex: Integer;
+  I: Integer;
+begin
+  xStartIndex := Length(ioParams);
+  SetLength(ioParams, xStartIndex+aFiles.Count);
+  for I := 0 to aFiles.Count-1 do
+    ioParams[xStartIndex+I] := MessageParam(PARAM_FILE, aFiles[I]);
+end;
+
+class function TIDEInstances.GetMessageParam(
+  const aParams: array of TMessageParam; const aParamName: string): string;
+var
+  I: Integer;
+begin
+  for I := Low(aParams) to High(aParams) do
+  if aParams[I].Name = aParamName then
+    Exit(aParams[I].Value);
+
+  Result := '';//not found
+end;
+
+class procedure TIDEInstances.BuildMessage(const aMessageType: string;
+  const aParams: array of TMessageParam; const aStream: TStream);
+var
+  xDOM: TXMLDocument;
+  xRoot: TDOMElement;
+  xParam: TDOMElement;
+  I: Integer;
+begin
+  xDOM := TXMLDocument.Create;
+  try
+    xRoot := xDOM.CreateElement(ELEMENT_ROOT);
+    xRoot.AttribStrings[ATTR_MESSAGE_TYPE] := aMessageType;
+    xDOM.AppendChild(xRoot);
+
+    for I := Low(aParams) to High(aParams) do
+    begin
+      xParam := xDOM.CreateElement(aParams[I].Name);
+      xRoot.AppendChild(xParam);
+      xParam.AttribStrings[ATTR_VALUE] := aParams[I].Value;
+    end;
+
+    WriteXMLFile(xDOM, aStream);
+  finally
+    xDOM.Free;
+  end;
+end;
+
+class function TIDEInstances.ParseMessage(const aStream: TStream; out
+  outMessageType: string; out outParams: TMessageParams): Boolean;
+var
+  xDOM: TXMLDocument;
+  xChildList: TDOMNodeList;
+  I, J: Integer;
+begin
+  Result := False;
+
+  outMessageType := '';
+  SetLength(outParams, 0);
+  try
+    ReadXMLFile(xDOM, aStream, []);
+  except
+    on EXMLReadError do
+      Exit;//eat XML exceptions
+  end;
+  try
+    if (xDOM = nil) or (xDOM.DocumentElement = nil) or (xDOM.DocumentElement.NodeName <> ELEMENT_ROOT) then
+      Exit;
+
+    outMessageType := xDOM.DocumentElement.AttribStrings[ATTR_MESSAGE_TYPE];
+
+    xChildList := xDOM.DocumentElement.ChildNodes;
+    SetLength(outParams, xChildList.Count);
+    J := 0;
+    for I := 0 to xChildList.Count-1 do
+    if xChildList[I] is TDOMElement then
+    begin
+      outParams[J].Name := xChildList[I].NodeName;
+      outParams[J].Value := TDOMElement(xChildList[I]).AttribStrings[ATTR_VALUE];
+      Inc(J);
+    end;
+    SetLength(outParams, J);
+    Result := True;
+  finally
+    xDOM.Free;
+  end;
+end;
+
+function TIDEInstances.AllowStartNewInstance(const aFiles: TStrings;
+  var outModalErrorMessage, outModalErrorForceUniqueMessage,
+  outNotRespondingErrorMessage: string; var outHandleBringToFront: HWND
+  ): TStartNewInstanceResult;
+var
+  xStartClient: TResponseClient;
+  I: Integer;
+  xServerIDs: TStringList;
+begin
+  Result := ofrStartNewInstance;
+  xStartClient := TResponseClient.Create(nil);
+  xServerIDs := TStringList.Create;
+  try
+    xStartClient.FindRunningServers(SERVERPREFIX_MAIN, xServerIDs);//check for multiple instances
+    xServerIDs.Sort;
+
+    for I := xServerIDs.Count-1 downto 0 do//last started is first to choose
+    begin
+      xStartClient.ServerID := xServerIDs[I];
+      if xStartClient.ServerRunning then
+      begin
+        //there are open Lazarus instances, do not reopen last project!
+        FAllowOpenLastProject := False;
+        Result := xStartClient.AllowStartNewInstance(aFiles, outModalErrorMessage,
+          outModalErrorForceUniqueMessage, outNotRespondingErrorMessage, outHandleBringToFront);
+        if not(Result in [ofrModalError, ofrForceSingleInstanceModalError, ofrNotResponding]) then
+          Exit;//handle only one running Lazarus IDE
+      end;
+    end;
+  finally
+    xStartClient.Free;
+    xServerIDs.Free;
+  end;
+end;
+
+function TIDEInstances.CheckParamsForForceNewInstanceOpt: Boolean;
+var
+  I: Integer;
+begin
+  Result := False;
+  for I := 1 to ParamsAndCfgCount do
+    if ParamIsOption(i, ForceNewInstanceOpt) then//ignore the settings and start new Lazarus IDE instance
+      Result := True;
+end;
+
+procedure TIDEInstances.PerformCheck;
+var
+  xResult: TStartNewInstanceResult;
+  xModalErrorMessage: string = '';
+  xModalErrorForceUniqueMessage: string = '';
+  xNotRespondingErrorMessage: string = '';
+  xHandleBringToFront: HWND = 0;
+begin
+  if not FStartIDE then//InitIDEInstances->CollectOtherOpeningFiles decided not to start the IDE
+    Exit;
+
+  if not FForceNewInstance then
+    xResult := AllowStartNewInstance(FilesToOpen, xModalErrorMessage, xModalErrorForceUniqueMessage, xNotRespondingErrorMessage, xHandleBringToFront)
+  else
+    xResult := ofrStartNewInstance;
+
+  if xModalErrorMessage = '' then
+    xModalErrorMessage := dlgRunningInstanceModalError;
+  if xNotRespondingErrorMessage = '' then
+    xNotRespondingErrorMessage := dlgRunningInstanceNotRespondingError;
+  if xModalErrorForceUniqueMessage = '' then
+    xModalErrorForceUniqueMessage := dlgForceUniqueInstanceModalError;
+
+  FStartIDE := (xResult = ofrStartNewInstance);
+  case xResult of
+    ofrModalError:
+      FStartIDE := MessageDlg(lisLazarusIDE, Format(xModalErrorMessage, [FilesToOpen.Text]), mtWarning, mbYesNo, 0, mbYes) = mrYes;
+    ofrNotResponding:
+      MessageDlg(lisLazarusIDE, xNotRespondingErrorMessage, mtError, [mbOK], 0);
+    ofrForceSingleInstanceModalError:
+      MessageDlg(lisLazarusIDE, xModalErrorForceUniqueMessage, mtError, [mbOK], 0);
+  end;
+
+  {$IFDEF MSWINDOWS}
+  if not FStartIDE and (xHandleBringToFront <> 0) then
+  begin
+    try
+      SetForegroundWindow(xHandleBringToFront);//SetForegroundWindow works (on Windows) only if the calling process is the foreground process, therefore it must be here!
+    except
+      //eat all widget exceptions
+    end;
+  end;
+  {$ENDIF}
+end;
+
+constructor TIDEInstances.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+
+  FStartIDE := True;
+  FAllowOpenLastProject := True;
+end;
+
+destructor TIDEInstances.Destroy;
+begin
+  StopServer;
+  FreeAndNil(FMainServer);
+  FreeAndNil(FFilesToOpen);
+
+  inherited Destroy;
+end;
+
+procedure TIDEInstances.CollectFiles(out
+  outFilesWereSentToCollectingServer: Boolean);
+
+var
+  xThisClientMessageId: Integer;
+
+  procedure _SendToServer;
+  var
+    xClient: TIPCClient;
+    xOutParams: TMessageParams;
+    xStream: TMemoryStream;
+  begin
+    xClient := TIPCClient.Create(nil);
+    try
+      xClient.ServerID := SERVERNAME_COLLECT;
+
+      SetLength(xOutParams, 0);
+      AddFilesToParams(FilesToOpen, xOutParams);
+
+      xStream := TMemoryStream.Create;
+      try
+        BuildMessage(MESSAGE_COLLECTFILES, xOutParams, xStream);
+        xStream.Position := 0;
+        xThisClientMessageId := xClient.PostRequest(MESSAGETYPE_XML, xStream);
+      finally
+        xStream.Free;
+      end;
+    finally
+      xClient.Free;
+    end;
+  end;
+
+  procedure _WaitForFiles;
+  var
+    xLastCount, xNewCount: Integer;
+    xServer: TIPCServer;
+  begin
+    xServer := TIPCServer.Create(nil);
+    try
+      xServer.ServerID := SERVERNAME_COLLECT;
+      //do not start server here
+      xLastCount := -1;
+      xNewCount := xServer.GetPendingRequestCount;
+      while xLastCount <> xNewCount do
+      begin
+        xLastCount := xNewCount;
+        Sleep(TIMEOUT_COLLECTFILES);
+        xNewCount := xServer.GetPendingRequestCount;
+      end;
+    finally
+      xServer.Free;
+    end;
+  end;
+
+  function _ReceiveAsServer: Boolean;
+  var
+    xServer: TIPCServer;
+    xInParams: TMessageParams;
+    xStream: TMemoryStream;
+    xMsgType: Integer;
+    xMessageType: string;
+  begin
+    xStream := TMemoryStream.Create;
+    xServer := TIPCServer.Create(nil);
+    try
+      xServer.ServerID := SERVERNAME_COLLECT;
+      //files have to be handled only by one instance!
+      Result := xServer.FindHighestPendingRequestId = xThisClientMessageId;
+      if Result then
+      begin
+        //we are the highest client, handle the files
+        xServer.StartServer(False);
+      end else
+      begin
+        //we are not the highest client, maybe there are pending files, check that
+        {$IFNDEF MSWINDOWS}
+        //this code is not slowing up IDE start because if there was highest client found (the normal way), we close anyway
+        Randomize;
+        Sleep(Random(($3F+GetCurrentThreadId) and $3F));//random sleep in order to prevent double file locks on unix
+        {$ENDIF}
+        if not (xServer.StartServer(False) and (xServer.GetPendingRequestCount > 0)) then
+          Exit;//server is already running or there are no pending message -> close
+        Result := True;//no one handled handled the files, do it by myself
+      end;
+
+      FilesToOpen.Clear;
+      while xServer.PeekRequest(xStream, xMsgType{%H-}) do
+      if xMsgType = MESSAGETYPE_XML then
+      begin
+        if ParseMessage(xStream, xMessageType, xInParams) and
+           (xMessageType = MESSAGE_COLLECTFILES)
+        then
+          AddFilesFromParams(xInParams, FilesToOpen);
+      end;
+    finally
+      xStream.Free;
+      xServer.Free;
+    end;
+  end;
+begin
+  //if you select more files in explorer and open them, they are not opened in one process but one process is started per file
+  // -> collect them
+
+  //first send messages to queue (there is no server, no problem, it will collect the messages when it is created)
+  _SendToServer;
+
+  //now wait until we have everything
+  _WaitForFiles;
+
+  //now send them to one instance
+  outFilesWereSentToCollectingServer := not _ReceiveAsServer;
+end;
+
+procedure TIDEInstances.InitIDEInstances;
+var
+  xFilesWereSentToCollectingServer: Boolean;
+  I: Integer;
+begin
+  FForceNewInstance := CheckParamsForForceNewInstanceOpt;
+
+  //get cmd line filenames
+  FFilesToOpen := ExtractCmdLineFilenames;
+  for I := 0 to FilesToOpen.Count-1 do
+    FilesToOpen[I] := CleanAndExpandFilename(FilesToOpen[I]);
+
+  if FilesToOpen.Count > 0 then//if there are file in the cmd, check for multiple starting instances
+  begin
+    CollectFiles(xFilesWereSentToCollectingServer);
+    if xFilesWereSentToCollectingServer then
+    begin
+      FilesToOpen.Clear;
+      FStartIDE := False;
+    end;
+  end;
+end;
+
+{ TUniqueServer }
+
+procedure TUniqueServer.StartUnique(const aServerPrefix: string);
+var
+  I: Integer;
+begin
+  if Active then
+    StopServer;
+
+  I := 0;
+  while not Active do
+  begin
+    Inc(I);
+    if I < 10 then
+      ServerID := aServerPrefix+'0'+IntToStr(I)
+    else
+      ServerID := aServerPrefix+IntToStr(I);
+    StartServer;
+  end;
+end;
+
+{ TResponseClient }
+
+function TResponseClient.AllowStartNewInstance(const aFiles: TStrings;
+  var outModalErrorMessage, outModalErrorForceUniqueMessage,
+  outNotRespondingErrorMessage: string; var outHandleBringToFront: HWND
+  ): TStartNewInstanceResult;
+var
+  xStream: TMemoryStream;
+  xMsgType: Integer;
+  xResponseType: string;
+  xOutParams, xInParams: TMessageParams;
+begin
+  Result := ofrStartNewInstance;
+  xStream := TMemoryStream.Create;
+  try
+    //ask to show prompt
+    xStream.Clear;
+    SetLength(xOutParams, 0);
+    TIDEInstances.AddFilesToParams(aFiles, xOutParams);
+    TIDEInstances.BuildMessage(MESSAGE_STARTNEWINSTANCE, xOutParams, xStream);
+    xStream.Position := 0;
+    Self.PostRequest(MESSAGETYPE_XML, xStream);
+    xStream.Clear;
+    if PeekResponse(xStream, xMsgType{%H-}, TIMEOUT_OPENFILES) and
+       (xMsgType = MESSAGETYPE_XML) then
+    begin
+      xStream.Position := 0;
+      if TIDEInstances.ParseMessage(xStream, xResponseType, xInParams) and
+         (xResponseType = RESPONSE_OPENFILES) then
+      begin
+        Result := TStartNewInstanceResult(StrToIntDef(TIDEInstances.GetMessageParam(xInParams, PARAM_RESULT), 0));
+        outModalErrorMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_MODALERRORMESSAGE);
+        outModalErrorForceUniqueMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_FORCEUNIQUEMODALERRORMESSAGE);
+        outNotRespondingErrorMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_NOTRESPONDINGERRORMESSAGE);
+        outHandleBringToFront := StrToInt64Def(TIDEInstances.GetMessageParam(xInParams, PARAM_HANDLEBRINGTOFRONT), 0);
+      end;
+    end else//no response, the IDE is modal and cannot accept messages
+    begin
+      DeleteRequest;
+      Result := ofrNotResponding;
+    end;
+  finally
+    xStream.Free;
+  end;
+end;
+
+{ TMainServer }
+
+procedure TMainServer.CheckMessagesOnTimer(Sender: TObject);
+begin
+  DoCheckMessages;
+end;
+
+constructor TMainServer.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+
+  FMsgStream := TMemoryStream.Create;
+end;
+
+destructor TMainServer.Destroy;
+begin
+  FMsgStream.Free;
+  StopListening;
+
+  inherited Destroy;
+end;
+
+procedure TMainServer.DoStartNewInstance(const aMsgID: Integer;
+  const aInParams: TMessageParams);
+var
+  xResult: TStartNewInstanceResult;
+  xFiles: TStrings;
+  xParams: TMessageParams;
+begin
+  xResult := ofrStartNewInstance;
+  if Assigned(FStartNewInstanceEvent) then
+  begin
+    xFiles := TStringList.Create;
+    try
+      TIDEInstances.AddFilesFromParams(aInParams, xFiles);
+      FStartNewInstanceEvent(xFiles, xResult);
+    finally
+      xFiles.Free;
+    end;
+  end;
+
+  SetLength(xParams, 5);
+  xParams[0] := TIDEInstances.MessageParam(PARAM_RESULT, IntToStr(Ord(xResult)));
+  xParams[1] := TIDEInstances.MessageParam(PARAM_HANDLEBRINGTOFRONT, IntToStr(Application.MainFormHandle));
+  xParams[2] := TIDEInstances.MessageParam(PARAM_MODALERRORMESSAGE, dlgRunningInstanceModalError);
+  xParams[3] := TIDEInstances.MessageParam(PARAM_FORCEUNIQUEMODALERRORMESSAGE, dlgForceUniqueInstanceModalError);
+  xParams[4] := TIDEInstances.MessageParam(PARAM_NOTRESPONDINGERRORMESSAGE, dlgRunningInstanceNotRespondingError);
+  SimpleResponse(aMsgID, RESPONSE_OPENFILES, xParams);
+end;
+
+procedure TMainServer.SimpleResponse(const aResponseToMsgID: Integer; const
+  aResponseType: string; const aParams: array of TMessageParam);
+var
+  xStream: TMemoryStream;
+begin
+  xStream := TMemoryStream.Create;
+  try
+    TIDEInstances.BuildMessage(aResponseType, aParams, xStream);
+    xStream.Position := 0;
+    PostResponse(aResponseToMsgID, MESSAGETYPE_XML, xStream);
+  finally
+    xStream.Free;
+  end;
+end;
+
+procedure TMainServer.StartListening(const aStartNewInstanceEvent: TStartNewInstanceEvent);
+begin
+  Assert((FTimer = nil) and Assigned(aStartNewInstanceEvent));
+
+  FTimer := TTimer.Create(nil);
+  FTimer.OnTimer := @CheckMessagesOnTimer;
+  FTimer.Interval := 50;
+  FTimer.Enabled := True;
+
+  FStartNewInstanceEvent := aStartNewInstanceEvent;
+end;
+
+procedure TMainServer.StopListening;
+begin
+  FreeAndNil(FTimer);
+
+  FStartNewInstanceEvent := nil;
+end;
+
+procedure TMainServer.DoCheckMessages;
+var
+  xMessageType: string;
+  xParams: TMessageParams;
+  xMsgID, xMsgType: Integer;
+begin
+  if Active then
+  begin
+    if PeekRequest(FMsgStream, xMsgID{%H-}, xMsgType{%H-}) and
+       (xMsgType = MESSAGETYPE_XML) and
+       (TIDEInstances.ParseMessage(FMsgStream, xMessageType, xParams)) and
+       (xMessageType = MESSAGE_STARTNEWINSTANCE)
+    then
+      DoStartNewInstance(xMsgID, xParams);
+  end;
+end;
+
+initialization
+  FLazIDEInstances := TIDEInstances.Create(nil);
+  FLazIDEInstances.InitIDEInstances;
+
+finalization
+  FreeAndNil(FLazIDEInstances);
+
+end.
Index: ide/lazarus.pp
===================================================================
--- ide/lazarus.pp	(revision 49901)
+++ ide/lazarus.pp	(working copy)
@@ -52,6 +52,7 @@
   {$IFEND}
   SysUtils,
   Interfaces,
+  IDEInstances,//keep IDEInstances up so that it will be initialized soon
   Forms, LCLProc,
   IDEOptionsIntf,
   LazConf, IDEGuiCmdLine,
@@ -104,6 +105,10 @@
   {$ENDIF}
 
   Application.Initialize;
+  LazIDEInstances.PerformCheck;
+  if not LazIDEInstances.StartIDE then
+    Exit;
+  LazIDEInstances.StartServer;
   TMainIDE.ParseCmdLineOptions;
   if not SetupMainIDEInstance then exit;
   if Application.Terminated then exit;
Index: ide/lazarusidestrconsts.pas
===================================================================
--- ide/lazarusidestrconsts.pas	(revision 49901)
+++ ide/lazarusidestrconsts.pas	(working copy)
@@ -142,6 +142,14 @@
   lisMoveFiles2 = 'Move files?';
   lrsPLDDeleteSelected = 'Delete selected';
 
+  dlgMultipleInstances = 'Multiple Lazarus instances';
+  dlgMultipleInstances_AlwaysStartNew = 'always start a new instance';
+  dlgMultipleInstances_OpenFilesInRunning = 'open files in a running instance';
+  dlgMultipleInstances_ForceSingleInstance = 'do not allow multiple instances';
+  dlgRunningInstanceModalError = 'The running Lazarus instance cannot accept any files.'+sLineBreak+'Do you want to open them in a new IDE instance?'+sLineBreak+sLineBreak+'%s';
+  dlgForceUniqueInstanceModalError = 'The running Lazarus instance cannot accept any files.';
+  dlgRunningInstanceNotRespondingError = 'Lazarus instance is running but not responding.';
+
   // *** Rest of the resource strings ***
 
   lisImportPackageListXml = 'Import package list (*.xml)';
Index: ide/lazarusmanager.pas
===================================================================
--- ide/lazarusmanager.pas	(revision 49901)
+++ ide/lazarusmanager.pas	(working copy)
@@ -92,8 +92,8 @@
   BaseUnix,
 {$ENDIF}
   Classes, SysUtils, Process, Forms, Controls, Dialogs, LCLProc,
-  UTF8Process, FileUtil, LazFileUtils, LazUTF8, FileProcs,
-  IDECmdLine, LazConf, Splash, BaseIDEIntf;
+  UTF8Process, FileUtil, FileProcs, LazUTF8, LazFileUtils,
+  IDECmdLine, LazConf, Splash, BaseIDEIntf, IDEInstances;
   
 type
 
@@ -244,6 +244,15 @@
   if FShowSplashOption then
     ShowSplash;
 
+  // we already handled IDEInstances, ignore it in lazarus EXE
+  if (FCmdLineParams.IndexOf(ForceNewInstanceOpt) = -1) then
+    FCmdLineParams.Add(ForceNewInstanceOpt);
+  // pass the AllowOpenLastProject parameter to lazarus EXE
+  if not LazIDEInstances.AllowOpenLastProject and
+     (FCmdLineParams.IndexOf(SkipLastProjectOpt) = -1)
+  then
+    FCmdLineParams.Add(SkipLastProjectOpt);
+
   // set primary config path
   PCP:=ExtractPrimaryConfigPath(FCmdLineParams);
   if PCP<>'' then
@@ -250,7 +259,7 @@
     SetPrimaryConfigPath(PCP);
 
   // get command line files
-  CmdLineFiles := ExtractCmdLineFilenames;
+  CmdLineFiles := LazIDEInstances.FilesToOpen;
   if CmdLineFiles<>nil then
   begin
     for i := 0 to CmdLineFiles.Count-1 do
Index: ide/main.pp
===================================================================
--- ide/main.pp	(revision 49901)
+++ ide/main.pp	(working copy)
@@ -157,6 +157,7 @@
   CleanDirDlg, CodeContextForm, AboutFrm, CompatibilityRestrictions,
   RestrictionBrowser, ProjectWizardDlg, IDECmdLine, IDEGuiCmdLine, CodeExplOpts,
   EditorMacroListViewer, SourceFileManager, EditorToolbarStatic,
+  IDEInstances,
   // main ide
   MainBar, MainIntf, MainBase;
 
@@ -187,6 +188,8 @@
     procedure HandleRemoteControlTimer(Sender: TObject);
     procedure HandleSelectFrame(Sender: TObject; var AComponentClass: TComponentClass);
     procedure OIChangedTimerTimer(Sender: TObject);
+    procedure LazInstancesStartNewInstance(const aFiles: TStrings;
+      var Result: TStartNewInstanceResult);
   public
     // file menu
     procedure mnuNewUnitClicked(Sender: TObject);
@@ -637,6 +640,7 @@
     OldCompilerFilename, OldLanguage: String;
     OIChangedTimer: TIdleTimer;
 
+    procedure DoDropFilesAsync(Data: PtrInt);
     procedure RenameInheritedMethods(AnUnitInfo: TUnitInfo; List: TStrings);
     function OIHelpProvider: TAbstractIDEHTMLProvider;
     // form editor and designer
@@ -950,6 +954,14 @@
   StartedByStartLazarus: boolean = false;
   ShowSetupDialog: boolean = false;
 
+type
+  TDoDropFilesAsyncParams = class(TComponent)
+  public
+    FileNames: array of string;
+    WindowIndex: Integer;
+    BringToFront: Boolean;
+  end;
+
 function FindDesignComponent(const aName: string): TComponent;
 var
   AnUnitInfo: TUnitInfo;
@@ -1556,6 +1568,7 @@
   DoShowMessagesView(false);           // reopen extra windows
   fUserInputSinceLastIdle:=true; // Idle work gets done initially before user action.
   MainIDEBar.ApplicationIsActivate:=true;
+  LazIDEInstances.StartListening(@LazInstancesStartNewInstance);
   FIDEStarted:=true;
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.StartIDE END');{$ENDIF}
 end;
@@ -1946,6 +1959,7 @@
 procedure TMainIDE.MainIDEFormClose(Sender: TObject;
   var CloseAction: TCloseAction);
 begin
+  LazIDEInstances.StopServer;
   DoCallNotifyHandler(lihtIDEClose);
   SaveEnvironment(true);
   if IDEDockMaster<>nil then
@@ -2182,119 +2196,116 @@
   {$ENDIF}
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject A');{$ENDIF}
   // load command line project or last project or create a new project
-  CmdLineFiles:=ExtractCmdLineFilenames;
-  try
-    ProjectLoaded:=false;
+  CmdLineFiles:=LazIDEInstances.FilesToOpen;
+  ProjectLoaded:=false;
 
-    // try command line project
-    if (CmdLineFiles<>nil) and (CmdLineFiles.Count>0) then begin
-      AProjectFilename:=CmdLineFiles[0];
-      if (CompareFileExt(AProjectFilename,'.lpr',false)=0) then
-        AProjectFilename:=ChangeFileExt(AProjectFilename,'.lpi');
-      // only try to load .lpi files, other files are loaded later
-      if (CompareFileExt(AProjectFilename,'.lpi',false)=0) then begin
-        AProjectFilename:=CleanAndExpandFilename(AProjectFilename);
-        if FileExistsUTF8(AProjectFilename) then begin
-          CmdLineFiles.Delete(0);
-          ProjectLoaded:=(DoOpenProjectFile(AProjectFilename,[])=mrOk);
-        end;
+  // try command line project
+  if (CmdLineFiles<>nil) and (CmdLineFiles.Count>0) then begin
+    AProjectFilename:=CmdLineFiles[0];
+    if (CompareFileExt(AProjectFilename,'.lpr',false)=0) then
+      AProjectFilename:=ChangeFileExt(AProjectFilename,'.lpi');
+    // only try to load .lpi files, other files are loaded later
+    if (CompareFileExt(AProjectFilename,'.lpi',false)=0) then begin
+      AProjectFilename:=CleanAndExpandFilename(AProjectFilename);
+      if FileExistsUTF8(AProjectFilename) then begin
+        CmdLineFiles.Delete(0);
+        ProjectLoaded:=(DoOpenProjectFile(AProjectFilename,[ofAddToRecent])=mrOk);
       end;
     end;
+  end;
 
-    // try loading last project if lazarus didn't fail last time
-    if (not ProjectLoaded)
-    and (not SkipAutoLoadingLastProject)
-    and (EnvironmentOptions.OpenLastProjectAtStart)
-    and (EnvironmentOptions.LastSavedProjectFile<>'')
-    and (EnvironmentOptions.LastSavedProjectFile<>RestoreProjectClosed)
-    and (FileExistsCached(EnvironmentOptions.LastSavedProjectFile))
-    then begin
-      if (not IDEProtocolOpts.LastProjectLoadingCrashed)
-      or AskIfLoadLastFailingProject then begin
-        // protocol that the IDE is trying to load the last project and did not
-        // yet succeed
-        IDEProtocolOpts.LastProjectLoadingCrashed := True;
-        IDEProtocolOpts.Save;
-        // try loading the project
-        ProjectLoaded:=
-          (DoOpenProjectFile(EnvironmentOptions.LastSavedProjectFile,[])=mrOk);
-        // protocol that the IDE was able to open the project without crashing
-        IDEProtocolOpts.LastProjectLoadingCrashed := false;
-        IDEProtocolOpts.Save;
-        if ProjectLoaded then
+  // try loading last project if lazarus didn't fail last time
+  if (not ProjectLoaded)
+  and (LazIDEInstances.AllowOpenLastProject)
+  and (not SkipAutoLoadingLastProject)
+  and (EnvironmentOptions.OpenLastProjectAtStart)
+  and (EnvironmentOptions.LastSavedProjectFile<>'')
+  and (EnvironmentOptions.LastSavedProjectFile<>RestoreProjectClosed)
+  and (FileExistsCached(EnvironmentOptions.LastSavedProjectFile))
+  then begin
+    if (not IDEProtocolOpts.LastProjectLoadingCrashed)
+    or AskIfLoadLastFailingProject then begin
+      // protocol that the IDE is trying to load the last project and did not
+      // yet succeed
+      IDEProtocolOpts.LastProjectLoadingCrashed := True;
+      IDEProtocolOpts.Save;
+      // try loading the project
+      ProjectLoaded:=
+        (DoOpenProjectFile(EnvironmentOptions.LastSavedProjectFile,[])=mrOk);
+      // protocol that the IDE was able to open the project without crashing
+      IDEProtocolOpts.LastProjectLoadingCrashed := false;
+      IDEProtocolOpts.Save;
+      if ProjectLoaded then
+      begin
+        PkgOpenFlags:=[pofAddToRecent];
+        for I := 0 to EnvironmentOptions.LastOpenPackages.Count-1 do
         begin
-          PkgOpenFlags:=[pofAddToRecent];
-          for I := 0 to EnvironmentOptions.LastOpenPackages.Count-1 do
-          begin
-            AFilename:=EnvironmentOptions.LastOpenPackages[I];
-            if AFilename='' then
-              continue;
-            if i<EnvironmentOptions.LastOpenPackages.Count-1 then
-              Include(PkgOpenFlags,pofMultiOpen)
-            else
-              Exclude(PkgOpenFlags,pofMultiOpen);
-            if PkgBoss.DoOpenPackageFile(AFilename,PkgOpenFlags,true)=mrAbort then begin
-              break;
-            end;
+          AFilename:=EnvironmentOptions.LastOpenPackages[I];
+          if AFilename='' then
+            continue;
+          if i<EnvironmentOptions.LastOpenPackages.Count-1 then
+            Include(PkgOpenFlags,pofMultiOpen)
+          else
+            Exclude(PkgOpenFlags,pofMultiOpen);
+          if PkgBoss.DoOpenPackageFile(AFilename,PkgOpenFlags,true)=mrAbort then begin
+            break;
           end;
-        end else
-        begin
-          DoCloseProject;
         end;
+      end else
+      begin
+        DoCloseProject;
       end;
     end;
-    {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject B');{$ENDIF}
+  end;
+  {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject B');{$ENDIF}
 
-    if (not ProjectLoaded) then
-    begin
-      if EnvironmentOptions.OpenLastProjectAtStart
-      and (EnvironmentOptions.LastSavedProjectFile=RestoreProjectClosed) then begin
-        // IDE was closed without a project => restore that state
-      end else begin
-        // create new project
-        DoNewProject(ProjectDescriptorApplication);
-      end;
+  if (not ProjectLoaded) then
+  begin
+    if EnvironmentOptions.OpenLastProjectAtStart
+    and (EnvironmentOptions.LastSavedProjectFile=RestoreProjectClosed) then begin
+      // IDE was closed without a project => restore that state
+    end else begin
+      // create new project
+      DoNewProject(ProjectDescriptorApplication);
     end;
+  end;
 
-    // load the cmd line files
-    if CmdLineFiles<>nil then begin
-      for i:=0 to CmdLineFiles.Count-1 do
-      Begin
-        AFilename:=CleanAndExpandFilename(CmdLineFiles.Strings[i]);
-        if not FileExistsCached(AFilename) then begin
-          debugln(['WARNING: command line file not found: "',AFilename,'"']);
-          continue;
+  // load the cmd line files
+  if CmdLineFiles<>nil then begin
+    for i:=0 to CmdLineFiles.Count-1 do
+    Begin
+      AFilename:=CleanAndExpandFilename(CmdLineFiles.Strings[i]);
+      if not FileExistsCached(AFilename) then begin
+        debugln(['WARNING: command line file not found: "',AFilename,'"']);
+        continue;
+      end;
+      if Project1=nil then begin
+        // to open a file a project is needed
+        // => create a project
+        DoNewProject(ProjectDescriptorEmptyProject);
+      end;
+      if CompareFileExt(AFilename,'.lpk',false)=0 then begin
+        if PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent,pofMultiOpen],true)=mrAbort
+        then
+          break;
+      end else begin
+        OpenFlags:=[ofAddToRecent,ofRegularFile];
+        if i<CmdLineFiles.Count then
+          Include(OpenFlags,ofMultiOpen);
+        if DoOpenEditorFile(AFilename,-1,-1,OpenFlags)=mrAbort then begin
+          break;
         end;
-        if Project1=nil then begin
-          // to open a file a project is needed
-          // => create a project
-          DoNewProject(ProjectDescriptorEmptyProject);
-        end;
-        if CompareFileExt(AFilename,'.lpk',false)=0 then begin
-          if PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent,pofMultiOpen],true)=mrAbort
-          then
-            break;
-        end else begin
-          OpenFlags:=[ofAddToRecent,ofRegularFile];
-          if i<CmdLineFiles.Count then
-            Include(OpenFlags,ofMultiOpen);
-          if DoOpenEditorFile(AFilename,-1,-1,OpenFlags)=mrAbort then begin
-            break;
-          end;
-        end;
       end;
     end;
+  end;
 
-    if Project1=nil then
-      DoNoProjectWizard(nil);
+  if Project1=nil then
+    DoNoProjectWizard(nil);
 
-    {$IFDEF IDE_DEBUG}
-    debugln('TMainIDE.Create B');
-    {$ENDIF}
-    {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject C');{$ENDIF}
-  finally
-    CmdLineFiles.Free;
-  end;
+  {$IFDEF IDE_DEBUG}
+  debugln('TMainIDE.Create B');
+  {$ENDIF}
+  {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SetupStartProject C');{$ENDIF}
 end;
 
 procedure TMainIDE.SetupRemoteControl;
@@ -5387,6 +5398,27 @@
   SetRecentFilesMenu;
 end;
 
+procedure TMainIDE.DoDropFilesAsync(Data: PtrInt);
+var
+  xParams: TDoDropFilesAsyncParams;
+begin
+  xParams := TDoDropFilesAsyncParams(Data);
+  try
+    if Length(xParams.FileNames) > 0 then
+      DoDropFiles(Self, xParams.FileNames, xParams.WindowIndex);
+    if xParams.BringToFront then
+    begin
+      if Application.MainForm.WindowState = wsMinimized then
+        UnhideIDE;
+      Application.BringToFront;
+      if SourceEditorManager.ActiveSourceWindow <> nil then
+        SourceEditorManager.ActiveSourceWindow.BringToFront;
+    end;
+  finally
+    xParams.Free;
+  end;
+end;
+
 function TMainIDE.DoSelectFrame: TComponentClass;
 var
   UnitList: TStringList;
@@ -9112,6 +9144,51 @@
   Result:=false;
 end;
 
+procedure TMainIDE.LazInstancesStartNewInstance(const aFiles: TStrings;
+  var Result: TStartNewInstanceResult);
+var
+  xParams: TDoDropFilesAsyncParams;
+  I: Integer;
+begin
+  if aFiles.Count > 0 then
+  begin
+    //there are files to open
+    if EnvironmentOptions.MultipleInstances = mioAlwaysStartNew then
+    begin//the user wants to open them in new instance!
+      Result := ofrStartNewInstance;
+    end else
+    if (not IsWindowEnabled(Application.MainForm.Handle) or//check that main is active
+       (Application.ModalLevel > 0))//check that no modal window is opened
+    then
+    begin
+      if EnvironmentOptions.MultipleInstances = mioForceSingleInstance then
+        Result := ofrForceSingleInstanceModalError
+      else
+        Result := ofrModalError;
+    end else
+      Result := ofrDoNotStart;
+  end else
+  begin
+    //no files to open
+    if EnvironmentOptions.MultipleInstances = mioForceSingleInstance then
+      Result := ofrDoNotStart
+    else
+      Result := ofrStartNewInstance;
+  end;
+
+  if Result in [ofrStartNewInstance, ofrModalError, ofrForceSingleInstanceModalError]  then
+    Exit;
+
+  //show up the current IDE and open files (if there are any)
+  xParams := TDoDropFilesAsyncParams.Create(Self);//we need direct response, do not wait to get the files opened!
+  SetLength(xParams.FileNames, aFiles.Count);
+  for I := 0 to aFiles.Count-1 do
+    xParams.FileNames[I] := aFiles[I];
+  xParams.WindowIndex := -1;
+  xParams.BringToFront := True;
+  Application.QueueAsyncCall(@DoDropFilesAsync, PtrInt(xParams));
+end;
+
 procedure TMainIDE.ApplyCodeToolChanges;
 begin
   // all changes were handled automatically by events, just clear the logs
Index: ide/startlazarus.lpi
===================================================================
--- ide/startlazarus.lpi	(revision 49901)
+++ ide/startlazarus.lpi	(working copy)
@@ -8,6 +8,7 @@
       </Flags>
       <SessionStorage Value="InIDEConfig"/>
       <MainUnit Value="0"/>
+      <UseXPManifest Value="True"/>
       <Icon Value="0"/>
     </General>
     <BuildModes Count="1">
Index: ide/startlazarus.lpr
===================================================================
--- ide/startlazarus.lpr	(revision 49901)
+++ ide/startlazarus.lpr	(working copy)
@@ -34,6 +34,7 @@
   redirect_stderr,
   Interfaces, SysUtils,
   Forms,
+  IDEInstances,
   LazarusManager;
   
 {$R *.res}
@@ -44,6 +45,9 @@
 begin
   redirect_stderr.DoShowWindow := False;
   Application.Initialize;
+  LazIDEInstances.PerformCheck;
+  if not LazIDEInstances.StartIDE then
+    Exit;
   ALazarusManager := TLazarusManager.Create(nil);
   ALazarusManager.Initialize;
   ALazarusManager.Run;
ide-multipleinstances-4.patch (75,120 bytes)   

Ondrej Pokorny

2015-09-30 19:47

developer   ~0086188

ide-multipleinstances-4.patch: New patch with the latest advancedipc.pp unit I sent to fpc-devel mailing list.

Juha Manninen

2015-10-02 12:14

developer   ~0086209

Applied, thanks.
I changed the default setting to mioOpenFilesInRunning because it feels the most intuitive.

Ondrej, there is a problem when starting Lazarus from cmd line. It is caused by this single instance change.
If I do :
 $ cd converter/
 $ ../lazarus convertertypes.pas &
then Lazarus does not start, even if there is no instance already open.
I remember there was a similar error earlier which you fixed.

If I do :
 $ ../lazarus &
without parameters then it starts OK.

Ondrej Pokorny

2015-10-02 15:01

developer   ~0086219

Thanks Juha for the report. I can't reproduce it on MS Windows (Lazarus starts and opens the file normally) - I'll check Linux in the evening.

Ondrej Pokorny

2015-10-03 01:25

developer   ~0086229

It was a bug in advancedipc.pp. Patch "ide-advancedipc-1.patch" attached. Furthermore, the ideinstances.pas content was twice in the source file, I don't know how it happened. I deleted the duplicate after the first "end.".

Ondrej Pokorny

2015-10-03 01:25

developer  

ide-advancedipc-1.patch (26,471 bytes)   
Index: ide/advancedipc.pp
===================================================================
--- ide/advancedipc.pp	(revision 49912)
+++ ide/advancedipc.pp	(working copy)
@@ -218,8 +218,8 @@
 begin
   Randomize;
   repeat
-    //if Randomize/Random is started from 2 processes at exactly same moment, it returns the same number! -> prevent duplicates by adding GetCurrentThreadId
-    Result := Integer(Int64(Random(High(Integer)))+GetCurrentThreadId);
+    //if Randomize/Random is started from 2 processes at exactly same moment, it returns the same number! -> prevent duplicates by xor GetCurrentThreadId
+    Result := Integer((Int64(Random($7FFFFFFF)) xor GetCurrentThreadId) and $7FFFFFFF);//the result must be of range 0..$7FFFFFFF (High(Integer))
     outFileName := GetRequestFileName(Result);
   until not RequestExists(outFileName);
 end;
@@ -516,19 +516,15 @@
 function TIPCServer.FindHighestPendingRequestId: Integer;
 var
   xRec: TRawByteSearchRec;
-  xRequestID, xHighestId: LongInt;
+  xRequestID: LongInt;
 begin
-  xHighestId := -1;
   Result := -1;
   if FindFirst(GetRequestPrefix+'*', faAnyFile, xRec) = 0 then
   begin
     repeat
       xRequestID := RequestFileNameToID(xRec.Name);
-      if xRequestID > xHighestId then
-      begin
-        xHighestId := xRequestID;
+      if xRequestID > Result then
         Result := xRequestID;
-      end;
     until FindNext(xRec) <> 0;
   end;
   FindClose(xRec);
Index: ide/ideinstances.pas
===================================================================
--- ide/ideinstances.pas	(revision 49912)
+++ ide/ideinstances.pas	(working copy)
@@ -735,786 +735,3 @@
   FreeAndNil(FLazIDEInstances);
 
 end.
-{
- /***************************************************************************
-                              ideinstances.pas
-                              ----------------
-
- ***************************************************************************/
-
- ***************************************************************************
- *                                                                         *
- *   This source is free software; you can redistribute it and/or modify   *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This code is distributed in the hope that it will be useful, but      *
- *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
- *   General Public License for more details.                              *
- *                                                                         *
- *   A copy of the GNU General Public License is available on the World    *
- *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
- *   obtain it by writing to the Free Software Foundation,                 *
- *   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
- *                                                                         *
- ***************************************************************************
-
-  Author: Ondrej Pokorny
-
-  Abstract:
-    This unit handles one/multiple Lazarus IDE instances.
-
-}
-unit IDEInstances;
-
-{$mode objfpc}{$H+}
-
-interface
-
-uses
-  sysutils, Interfaces, Classes, Controls, Forms, Dialogs, ExtCtrls,
-  LCLProc, LCLIntf, LCLType, AdvancedIPC,
-  LazFileUtils, LazUTF8, Laz2_DOM, laz2_XMLRead, laz2_XMLWrite,
-  LazarusIDEStrConsts, IDECmdLine;
-
-type
-  TIDEStartWithFilesAction = (isaStartNewInstance, isaOpenFilesInRunningInstance, isaPrompt, isaModalError);
-  TChooseActionEvent = procedure(
-    const aFiles: TStrings;
-    var Result: TIDEStartWithFilesAction;
-    var outPromptMessage: string;
-    var outOpenNewInstanceMessage: string) of object;//we want to receive translated outPromptMessage and outOpenNewInstanceMessage -> do not read them directly from LazarusIDEStrConsts here
-  TOpenFilesResult = (ofrStartNewInstance, ofrDoNotStart, ofrModalError);
-  TOpenFilesEvent = procedure(const aFiles: TStrings;
-    var outResult: TOpenFilesResult; var outHandleBringToFront: HWND) of object;
-
-  TMessageParam = record
-    Name: string;
-    Value: string;
-  end;
-  TMessageParams = array of TMessageParam;
-
-  TUniqueServer = class(TIPCServer)
-  public
-    procedure StartUnique(const aServerPrefix: string);
-  end;
-
-  TMainServer = class(TUniqueServer)
-  private
-    FOpenFilesEvent: TOpenFilesEvent;
-    FChooseActionEvent: TChooseActionEvent;
-    FTimer: TTimer;
-    FMsgStream: TMemoryStream;
-
-    procedure DoChooseActionEvent(const aMsgID: string; const aInParams: TMessageParams);
-    procedure DoOpenFiles(const aMsgID: string; const aInParams: TMessageParams);
-
-    procedure SimpleResponse(
-      const aResponseToMsgID, aResponseType: string;
-      const aParams: array of TMessageParam);
-
-    procedure DoCheckMessages;
-    procedure CheckMessagesOnTimer(Sender: TObject);
-
-  public
-    constructor Create; override;
-    destructor Destroy; override;
-  end;
-
-  TResponseClient = class(TIPCClient)
-  public
-    function SendFilesToRunningInstance(
-      const aFiles: TStrings; var outOpenNewInstanceMessage: string;
-      var outHandleBringToFront: HWND): TOpenFilesResult;
-  end;
-
-  TIDEInstances = class
-  private
-    FMainServer: TMainServer;//running IDE
-    FStartIDE: Boolean;// = True;
-    FForceNewInstance: Boolean;
-    FAllowOpenLastProject: Boolean;// = True;
-    FFilesToOpen: TStrings;
-
-    class procedure AddFilesToParams(const aFiles: TStrings;
-      var ioParams: TMessageParams); static;
-    class procedure AddFilesFromParams(const aInParams: TMessageParams;
-      const aFiles: TStrings); static;
-    class procedure BuildMessage(const aMessageType: string;
-      const aParams: array of TMessageParam; const aStream: TStream); static;
-    class function MessageParam(const aName, aValue: string): TMessageParam; static;
-    class function ParseMessage(const aStream: TStream; out outMessageType: string;
-      out outParams: TMessageParams): Boolean; static;
-    class function GetMessageParam(const aParams: array of TMessageParam;
-      const aParamName: string): string; static;
-
-    function CheckParamsForForceNewInstanceOpt: Boolean;
-
-    procedure CollectFiles(out
-      outFilesWereSentToCollectingServer: Boolean);
-
-    function SendFilesToRunningInstance(const aFiles: TStrings;
-      var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND): TOpenFilesResult;
-    procedure InitIDEInstances;
-  public
-    constructor Create;
-    destructor Destroy; override;
-  public
-    procedure PerformCheck;//call PerformCheck after Application.Initialize - it can open dialogs!
-
-    procedure StartListening(const aOpenFilesEvent: TOpenFilesEvent;
-      const aChooseActionEvent: TChooseActionEvent);
-    procedure StopListening;
-    function StartIDE: Boolean;//can the IDE be started?
-    function AllowOpenLastProject: Boolean;//if a secondary IDE is starting, do NOT reopen last project!
-    function FilesToOpen: TStrings;
-    procedure DeleteCollectMessageFiles;
-  end;
-
-function LazIDEInstances: TIDEInstances;
-
-implementation
-
-const
-  SERVERPREFIX_MAIN = 'LazarusMain';
-  SERVERNAME_COLLECT = 'LazarusCollect';
-  MESSAGETYPE_XML = 2;
-  ELEMENT_ROOT = 'ideinstances';
-  ATTR_VALUE = 'value';
-  ATTR_MESSAGE_TYPE = 'msgtype';
-  MESSAGE_CHOOSEACTION = 'chooseaction';
-  RESPONSE_SHOWPROMPT = 'showprompt';
-  MESSAGE_OPENFILES = 'openfiles';
-  MESSAGE_COLLECTFILES = 'collectfiles';
-  RESPONSE_STARTNEWINSTANCE = 'startnewinstance';
-  PARAM_FILE = 'file';
-  PARAM_RESULT = 'result';
-  PARAM_HANDLEBRINGTOFRONT = 'handlebringtofront';
-  PARAM_PROMPTMESSAGE = 'promptmessage';
-  PARAM_OPENNEWINSTANCEMESSAGE = 'opennewinstancemessage';
-  MAX_CHECK_INSTANCES = 10;//check maximum count of continuously started instances
-  TIMEOUT_COLLECTFILES = 100;
-  TIMEOUT_SHOWPROMPT = 500;//first timeout is 1/2 second, we should get an answer fast
-  TIMEOUT_STARTNEWINSTANCE = 3000;//we know that Lazarus is already running and responding so we get an answer, the UnHide process could take some time, let's wait for it max 3 seconds
-
-var
-  FLazIDEInstances: TIDEInstances;
-
-function LazIDEInstances: TIDEInstances;
-begin
-  Result := FLazIDEInstances;
-end;
-
-{ TIDEInstances }
-
-class function TIDEInstances.MessageParam(const aName, aValue: string): TMessageParam;
-begin
-  Result.Name := aName;
-  Result.Value := aValue;
-end;
-
-function TIDEInstances.StartIDE: Boolean;
-begin
-  Result := FStartIDE;
-end;
-
-function TIDEInstances.AllowOpenLastProject: Boolean;
-begin
-  Result := FAllowOpenLastProject;
-end;
-
-function TIDEInstances.FilesToOpen: TStrings;
-begin
-  if not Assigned(FFilesToOpen) then
-    FFilesToOpen := TStringList.Create;
-  Result := FFilesToOpen;
-end;
-
-procedure TIDEInstances.StartListening(const aOpenFilesEvent: TOpenFilesEvent;
-  const aChooseActionEvent: TChooseActionEvent);
-begin
-  Assert(Assigned(aOpenFilesEvent) and Assigned(aChooseActionEvent));
-
-  if not Assigned(FMainServer) then
-  begin
-    FMainServer := TMainServer.Create;
-    FMainServer.StartUnique(SERVERPREFIX_MAIN);
-  end;
-  FMainServer.FOpenFilesEvent := aOpenFilesEvent;
-  FMainServer.FChooseActionEvent := aChooseActionEvent;
-
-  DeleteCollectMessageFiles;
-end;
-
-procedure TIDEInstances.StopListening;
-begin
-  FreeAndNil(FMainServer);
-end;
-
-class procedure TIDEInstances.AddFilesFromParams(
-  const aInParams: TMessageParams; const aFiles: TStrings);
-var
-  I: Integer;
-begin
-  //do not clear aFiles
-  for I := Low(aInParams) to High(aInParams) do
-    if aInParams[I].Name = PARAM_FILE then
-      aFiles.Add(aInParams[I].Value);
-end;
-
-class procedure TIDEInstances.AddFilesToParams(const aFiles: TStrings;
-  var ioParams: TMessageParams);
-var
-  xStartIndex: Integer;
-  I: Integer;
-begin
-  xStartIndex := Length(ioParams);
-  SetLength(ioParams, xStartIndex+aFiles.Count);
-  for I := 0 to aFiles.Count-1 do
-    ioParams[xStartIndex+I] := MessageParam(PARAM_FILE, aFiles[I]);
-end;
-
-class function TIDEInstances.GetMessageParam(
-  const aParams: array of TMessageParam; const aParamName: string): string;
-var
-  I: Integer;
-begin
-  for I := 0 to Length(aParams) do
-  if aParams[I].Name = aParamName then
-    Exit(aParams[I].Value);
-
-  Result := '';//not found
-end;
-
-class procedure TIDEInstances.BuildMessage(const aMessageType: string;
-  const aParams: array of TMessageParam; const aStream: TStream);
-var
-  xDOM: TXMLDocument;
-  xRoot: TDOMElement;
-  xParam: TDOMElement;
-  I: Integer;
-begin
-  xDOM := TXMLDocument.Create;
-  try
-    xRoot := xDOM.CreateElement(ELEMENT_ROOT);
-    xRoot.AttribStrings[ATTR_MESSAGE_TYPE] := aMessageType;
-    xDOM.AppendChild(xRoot);
-
-    for I := Low(aParams) to High(aParams) do
-    begin
-      xParam := xDOM.CreateElement(aParams[I].Name);
-      xRoot.AppendChild(xParam);
-      xParam.AttribStrings[ATTR_VALUE] := aParams[I].Value;
-    end;
-
-    WriteXMLFile(xDOM, aStream);
-  finally
-    xDOM.Free;
-  end;
-end;
-
-class function TIDEInstances.ParseMessage(const aStream: TStream; out
-  outMessageType: string; out outParams: TMessageParams): Boolean;
-var
-  xDOM: TXMLDocument;
-  xChildList: TDOMNodeList;
-  I, J: Integer;
-begin
-  Result := False;
-
-  outMessageType := '';
-  SetLength(outParams, 0);
-  try
-    ReadXMLFile(xDOM, aStream, []);
-  except
-    on EXMLReadError do
-      Exit;//eat XML exceptions
-  end;
-  try
-    if (xDOM = nil) or (xDOM.DocumentElement = nil) or (xDOM.DocumentElement.NodeName <> ELEMENT_ROOT) then
-      Exit;
-
-    outMessageType := xDOM.DocumentElement.AttribStrings[ATTR_MESSAGE_TYPE];
-
-    xChildList := xDOM.DocumentElement.ChildNodes;
-    SetLength(outParams, xChildList.Count);
-    J := 0;
-    for I := 0 to xChildList.Count-1 do
-    if xChildList[I] is TDOMElement then
-    begin
-      outParams[J].Name := xChildList[I].NodeName;
-      outParams[J].Value := TDOMElement(xChildList[I]).AttribStrings[ATTR_VALUE];
-      Inc(J);
-    end;
-    SetLength(outParams, J);
-    Result := True;
-  finally
-    xDOM.Free;
-  end;
-end;
-
-function TIDEInstances.SendFilesToRunningInstance(const aFiles: TStrings;
-  var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND
-  ): TOpenFilesResult;
-var
-  xStartClient: TResponseClient;
-  I: Integer;
-begin
-  Result := ofrStartNewInstance;
-  xStartClient := TResponseClient.Create;
-  try
-    for I := 1 to MAX_CHECK_INSTANCES do//check for multiple instances
-    begin
-      xStartClient.ServerName := SERVERPREFIX_MAIN+IntToStr(I);
-      if xStartClient.ServerRunning then
-      begin
-        //there are open Lazarus instances, do not reopen last project!
-        FAllowOpenLastProject := False;
-        Result := xStartClient.SendFilesToRunningInstance(aFiles, outOpenNewInstanceMessage, outHandleBringToFront);
-        if Result <> ofrModalError then//if the current IDE is modal, try another one
-          Exit;//handle only one running Lazarus IDE
-      end;
-    end;
-  finally
-    xStartClient.Free;
-  end;
-end;
-
-function TIDEInstances.CheckParamsForForceNewInstanceOpt: Boolean;
-var
-  I: Integer;
-begin
-  Result := False;
-  for I := 1 to ParamsAndCfgCount do
-    if ParamIsOption(i, ForceNewInstanceOpt) then//ignore the settings and start new Lazarus IDE instance
-      Result := True;
-end;
-
-procedure TIDEInstances.DeleteCollectMessageFiles;
-var
-  xServer: TIPCServer;
-begin
-  xServer := TIPCServer.Create;
-  try
-    xServer.ServerName := SERVERNAME_COLLECT;
-    xServer.DeletePendingRequests;
-  finally
-    xServer.Free;
-  end;
-end;
-
-procedure TIDEInstances.PerformCheck;
-var
-  xResult: TOpenFilesResult;
-  xOpenNewInstanceMessage: string = '';
-  xHandleBringToFront: HWND = 0;
-begin
-  if not FStartIDE then//InitIDEInstances->CollectOtherOpeningFiles decided not to start the IDE
-    Exit;
-
-  if not FForceNewInstance and (FilesToOpen.Count > 0) then
-    xResult := SendFilesToRunningInstance(FilesToOpen, xOpenNewInstanceMessage, xHandleBringToFront)
-  else
-    xResult := ofrStartNewInstance;
-
-  if xOpenNewInstanceMessage = '' then
-    xOpenNewInstanceMessage := dlgRunningInstanceModalOpenNew;
-  FStartIDE :=
-    (xResult = ofrStartNewInstance) or
-      ((xResult = ofrModalError) and
-       (MessageDlg(lisLazarusIDE, Format(xOpenNewInstanceMessage, [FilesToOpen.Text]), mtWarning, mbYesNo, 0, mbYes) = mrYes));//user decided to open in new ide
-
-  {$IFDEF MSWINDOWS}
-  if not FStartIDE and (xHandleBringToFront <> 0) then
-  begin
-    try
-      SetForegroundWindow(xHandleBringToFront);//SetForegroundWindow works (on Windows) only if the calling process is the foreground process, therefore it must be here!
-    except
-      //eat all widget exceptions
-    end;
-  end;
-  {$ENDIF}
-end;
-
-constructor TIDEInstances.Create;
-begin
-  inherited Create;
-
-  FStartIDE := True;
-  FAllowOpenLastProject := True;
-end;
-
-destructor TIDEInstances.Destroy;
-begin
-  FreeAndNil(FMainServer);
-  FreeAndNil(FFilesToOpen);
-
-  inherited Destroy;
-end;
-
-procedure TIDEInstances.CollectFiles(out
-  outFilesWereSentToCollectingServer: Boolean);
-
-var
-  xThisClientMessageId: string;
-
-  procedure _SendToServer;
-  var
-    xClient: TIPCClient;
-    xOutParams: TMessageParams;
-    xStream: TMemoryStream;
-  begin
-    xClient := TIPCClient.Create;
-    try
-      xClient.ServerName := SERVERNAME_COLLECT;
-
-      SetLength(xOutParams, 0);
-      AddFilesToParams(FilesToOpen, xOutParams);
-
-      xStream := TMemoryStream.Create;
-      try
-        BuildMessage(MESSAGE_COLLECTFILES, xOutParams, xStream);
-        xStream.Position := 0;
-        xClient.PostRequest(MESSAGETYPE_XML, xStream, xThisClientMessageId);
-      finally
-        xStream.Free;
-      end;
-    finally
-      xClient.Free;
-    end;
-  end;
-
-  procedure _WaitForFiles;
-  var
-    xLastCount, xNewCount: Integer;
-    xServer: TIPCServer;
-  begin
-    xServer := TIPCServer.Create;
-    try
-      xServer.ServerName := SERVERNAME_COLLECT;
-      //do not start server here
-      xLastCount := -1;
-      xNewCount := xServer.GetPendingRequestCount;
-      while xLastCount <> xNewCount do
-      begin
-        xLastCount := xNewCount;
-        Sleep(TIMEOUT_COLLECTFILES);
-        xNewCount := xServer.GetPendingRequestCount;
-      end;
-    finally
-      xServer.Free;
-    end;
-  end;
-
-  function _ReceiveAsServer: Boolean;
-  var
-    xServer: TIPCServer;
-    xInParams: TMessageParams;
-    xStream: TMemoryStream;
-    xMsgType: Integer;
-    xMessageType: string;
-  begin
-    xStream := TMemoryStream.Create;
-    xServer := TIPCServer.Create;
-    try
-      xServer.ServerName := SERVERNAME_COLLECT;
-      //files have to be handled only by one instance!
-      Result := xServer.FindHighestPendingRequestId = xThisClientMessageId;
-      if Result then
-      begin
-        //we are the highest client, handle the files
-        xServer.StartServer(False);
-      end else
-      begin
-        //we are not the highest client, maybe there are pending files, check that
-        {$IFNDEF MSWINDOWS}
-        //this code is not slowing up IDE start because if there was highest client found (the normal way), we close anyway
-        Randomize;
-        Sleep(50+Random(50));//random sleep in order to prevent double file locks on linux
-        {$ENDIF}
-        if not (xServer.StartServer(False) and (xServer.GetPendingRequestCount > 0)) then
-          Exit;//server is already running or there are no pending message -> close
-        Result := True;//no one handled handled the files, do it by myself
-      end;
-
-      FilesToOpen.Clear;
-      while xServer.PeekRequest(xStream, xMsgType{%H-}) do
-      if xMsgType = MESSAGETYPE_XML then
-      begin
-        if ParseMessage(xStream, xMessageType, xInParams) and
-           (xMessageType = MESSAGE_COLLECTFILES)
-        then
-          AddFilesFromParams(xInParams, FilesToOpen);
-      end;
-    finally
-      xStream.Free;
-      xServer.Free;
-    end;
-  end;
-begin
-  //if you select more files in explorer and open them, they are not opened in one process but one process is started per file
-  // -> collect them
-
-  //first send messages to queue (there is no server, no problem, it will collect the messages when it is created)
-  _SendToServer;
-
-  //now wait until we have everything
-  _WaitForFiles;
-
-  //now send them to one instance
-  outFilesWereSentToCollectingServer := not _ReceiveAsServer;
-end;
-
-procedure TIDEInstances.InitIDEInstances;
-var
-  xFilesWereSentToCollectingServer: Boolean;
-begin
-  FForceNewInstance := CheckParamsForForceNewInstanceOpt;
-
-  //get cmd line filenames
-  FFilesToOpen := ExtractCmdLineFilenames;
-
-  if FilesToOpen.Count > 0 then//if there are file in the cmd, check for multiple starting instances
-  begin
-    CollectFiles(xFilesWereSentToCollectingServer);
-    if xFilesWereSentToCollectingServer then
-    begin
-      FilesToOpen.Clear;
-      FStartIDE := False;
-    end;
-  end;
-end;
-
-{ TUniqueServer }
-
-procedure TUniqueServer.StartUnique(const aServerPrefix: string);
-var
-  I: Integer;
-begin
-  if Active then
-    StopServer;
-
-  I := 0;
-  while not Active do
-  begin
-    Inc(I);
-    ServerName := aServerPrefix+IntToStr(I);
-    StartServer;
-  end;
-end;
-
-{ TResponseClient }
-
-function TResponseClient.SendFilesToRunningInstance(const aFiles: TStrings;
-  var outOpenNewInstanceMessage: string; var outHandleBringToFront: HWND
-  ): TOpenFilesResult;
-var
-  xStream: TMemoryStream;
-  xMsgType: Integer;
-  xResponseType: string;
-  xOutParams, xInParams: TMessageParams;
-  xChooseAction: TIDEStartWithFilesAction;
-  xPromptMessage: string;
-begin
-  Result := ofrStartNewInstance;
-  xStream := TMemoryStream.Create;
-  try
-    //ask to show prompt
-    xChooseAction := isaStartNewInstance;
-    SetLength(xOutParams, 0);
-    TIDEInstances.AddFilesToParams(aFiles, xOutParams);
-    TIDEInstances.BuildMessage(MESSAGE_CHOOSEACTION, xOutParams, xStream);
-    xStream.Position := 0;
-    PostRequest(MESSAGETYPE_XML, xStream);
-    xStream.Clear;
-    if PeekResponse(xStream, xMsgType{%H-}, TIMEOUT_SHOWPROMPT) and
-       (xMsgType = MESSAGETYPE_XML) then
-    begin
-      xStream.Position := 0;
-      if TIDEInstances.ParseMessage(xStream, xResponseType, xInParams) and
-         (xResponseType = RESPONSE_SHOWPROMPT) then
-      begin
-        xChooseAction := TIDEStartWithFilesAction(StrToIntDef(TIDEInstances.GetMessageParam(xInParams, PARAM_RESULT), 0));
-        outOpenNewInstanceMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_OPENNEWINSTANCEMESSAGE);
-        xPromptMessage := TIDEInstances.GetMessageParam(xInParams, PARAM_PROMPTMESSAGE);
-        if xChooseAction = isaModalError then
-          Exit(ofrModalError);
-      end;
-    end else//no response, the IDE is modal and cannot accept messages
-    begin
-      DeleteRequest;
-      Exit(ofrModalError);
-    end;
-
-    case xChooseAction of
-      isaPrompt:
-      begin
-        if xPromptMessage = '' then
-          xPromptMessage := dlgOpenInRunningInstance;
-        case MessageDlg(lisLazarusIDE, Format(xPromptMessage, [aFiles.Text]), mtConfirmation, mbYesNo, 0, mbYes) of
-          mrYes: begin end;//user hit "yes" -> proceed
-          mrNo: Exit(ofrStartNewInstance);//user hit "no" -> open new instance
-        else//cancel/close -> do nothing, do not open IDE
-          Exit(ofrDoNotStart);
-        end;
-      end;
-      isaStartNewInstance://settings is startnewide -> open new instance
-        Exit(ofrStartNewInstance);
-    end;
-
-    //open files in new instance
-    xStream.Clear;
-    TIDEInstances.BuildMessage(MESSAGE_OPENFILES, xOutParams, xStream);
-    xStream.Position := 0;
-    Self.PostRequest(MESSAGETYPE_XML, xStream);
-    xStream.Clear;
-    if PeekResponse(xStream, xMsgType, TIMEOUT_STARTNEWINSTANCE) and
-       (xMsgType = MESSAGETYPE_XML) then
-    begin
-      xStream.Position := 0;
-      if TIDEInstances.ParseMessage(xStream, xResponseType, xInParams) and
-         (xResponseType = RESPONSE_STARTNEWINSTANCE) then
-      begin
-        Result := TOpenFilesResult(StrToIntDef(TIDEInstances.GetMessageParam(xInParams, PARAM_RESULT), 0));
-        outHandleBringToFront := StrToInt64Def(TIDEInstances.GetMessageParam(xInParams, PARAM_HANDLEBRINGTOFRONT), 0);
-      end;
-    end else//no response, the IDE is modal and cannot accept messages
-    begin
-      DeleteRequest;
-      Exit(ofrModalError);
-    end;
-  finally
-    xStream.Free;
-  end;
-end;
-
-{ TMainServer }
-
-procedure TMainServer.CheckMessagesOnTimer(Sender: TObject);
-begin
-  DoCheckMessages;
-end;
-
-constructor TMainServer.Create;
-begin
-  inherited Create;
-
-  FMsgStream := TMemoryStream.Create;
-  FTimer := TTimer.Create(nil);
-  FTimer.OnTimer := @CheckMessagesOnTimer;
-  FTimer.Interval := 50;
-  FTimer.Enabled := True;
-end;
-
-destructor TMainServer.Destroy;
-begin
-  FMsgStream.Free;
-  FTimer.Free;//must free manually before inherited Destroy
-
-  inherited Destroy;
-end;
-
-procedure TMainServer.DoChooseActionEvent(const aMsgID: string;
-  const aInParams: TMessageParams);
-var
-  xResult: TIDEStartWithFilesAction;
-  xParams: TMessageParams;
-  xPromptMessage, xOpenNewInstanceMessage: string;
-  xFiles: TStringList;
-begin
-  xResult := isaStartNewInstance;
-  xPromptMessage := '';
-  xOpenNewInstanceMessage := '';
-  if Assigned(FChooseActionEvent) then
-  begin
-    xFiles := TStringList.Create;
-    try
-      TIDEInstances.AddFilesFromParams(aInParams, xFiles);
-      FChooseActionEvent(xFiles, xResult, xPromptMessage, xOpenNewInstanceMessage);
-    finally
-      xFiles.Free;
-    end;
-  end;
-
-  SetLength(xParams, 3);
-  xParams[0] := TIDEInstances.MessageParam(PARAM_RESULT, IntToStr(Ord(xResult)));
-  xParams[1] := TIDEInstances.MessageParam(PARAM_PROMPTMESSAGE, xPromptMessage);
-  xParams[2] := TIDEInstances.MessageParam(PARAM_OPENNEWINSTANCEMESSAGE, xOpenNewInstanceMessage);
-  SimpleResponse(aMsgID, RESPONSE_SHOWPROMPT, xParams);
-end;
-
-procedure TMainServer.DoOpenFiles(const aMsgID: string;
-  const aInParams: TMessageParams);
-var
-  xResult: TOpenFilesResult;
-  xHandleBringToFront: HWND;
-  xFiles: TStrings;
-  xParams: TMessageParams;
-begin
-  xResult := ofrStartNewInstance;
-  xHandleBringToFront := 0;
-  if Assigned(FOpenFilesEvent) then
-  begin
-    xFiles := TStringList.Create;
-    try
-      TIDEInstances.AddFilesFromParams(aInParams, xFiles);
-      FOpenFilesEvent(xFiles, xResult, xHandleBringToFront);
-    finally
-      xFiles.Free;
-    end;
-  end;
-
-  SetLength(xParams, 2);
-  xParams[0] := TIDEInstances.MessageParam(PARAM_RESULT, IntToStr(Ord(xResult)));
-  xParams[1] := TIDEInstances.MessageParam(PARAM_HANDLEBRINGTOFRONT, IntToStr(xHandleBringToFront));
-  SimpleResponse(aMsgID, RESPONSE_STARTNEWINSTANCE, xParams);
-end;
-
-procedure TMainServer.SimpleResponse(const aResponseToMsgID,
-  aResponseType: string; const aParams: array of TMessageParam);
-var
-  xStream: TMemoryStream;
-begin
-  xStream := TMemoryStream.Create;
-  try
-    TIDEInstances.BuildMessage(aResponseType, aParams, xStream);
-    xStream.Position := 0;
-    PostResponse(aResponseToMsgID, MESSAGETYPE_XML, xStream);
-  finally
-    xStream.Free;
-  end;
-end;
-
-procedure TMainServer.DoCheckMessages;
-var
-  xMessageType: string;
-  xParams: TMessageParams;
-  xMsgID: string;
-  xMsgType: Integer;
-begin
-  if Active then
-  begin
-    if PeekRequest(FMsgStream, xMsgID{%H-}, xMsgType{%H-}) and
-       (xMsgType = MESSAGETYPE_XML) then
-    begin
-      if TIDEInstances.ParseMessage(FMsgStream, xMessageType, xParams) and
-         (xMessageType = MESSAGE_CHOOSEACTION)
-      then
-        DoChooseActionEvent(xMsgID, xParams)
-      else
-      if xMessageType = MESSAGE_OPENFILES then
-        DoOpenFiles(xMsgID, xParams);
-    end;
-  end;
-end;
-
-initialization
-  FLazIDEInstances := TIDEInstances.Create;
-  FLazIDEInstances.InitIDEInstances;
-
-finalization
-  FreeAndNil(FLazIDEInstances);
-
-end.
ide-advancedipc-1.patch (26,471 bytes)   

Juha Manninen

2015-10-03 11:45

developer   ~0086231

Now it works perfectly. Earlier the problem occurred always when starting with a parameter, not only from a different directory.

Mattias fixed compilation for FPC 2.6.4.

Thanks for this feature!

Ondrej Pokorny

2015-10-03 15:55

developer   ~0086235

You are welcome!

Btw. FreeMan reported a compiler error on OSX in Lazarus mailing list. I sent a patch there. The problem is that TThreadID is a pointer on OSX (not an Integer/Int64 as on Win/Unix) therefore I hardtyped it to PtrInt to support the XOR operation. I hope it is valid for all targets Lazarus IDE should run on.

Issue History

Date Modified Username Field Change
2007-01-01 19:38 Florian New Issue
2007-01-01 19:38 Florian LazTarget => -
2007-01-01 19:38 Florian Widgetset => Win32
2007-01-02 10:42 Vincent Snijders LazTarget - => post 1.0
2007-01-02 10:42 Vincent Snijders Status new => acknowledged
2012-02-04 12:12 Zeljan Rikalo LazTarget post 1.0 => 1.2
2012-02-04 12:12 Zeljan Rikalo Note Added: 0056342
2013-05-06 11:35 Juha Manninen Relationship added has duplicate 0021647
2013-05-06 11:38 Juha Manninen Relationship added related to 0007992
2014-01-14 15:22 Martin Friebe Target Version => 1.2.0
2014-01-20 13:36 Martin Friebe Target Version 1.2.0 => 1.4
2014-01-20 13:37 Martin Friebe LazTarget 1.2 => 1.4
2014-09-10 00:26 Juha Manninen LazTarget 1.4 => -
2014-09-10 00:26 Juha Manninen Target Version 1.4 =>
2015-09-12 23:27 Ondrej Pokorny Note Added: 0085867
2015-09-12 23:27 Ondrej Pokorny File Added: ide-multiple-instances.png
2015-09-12 23:27 Ondrej Pokorny File Added: ide-one-instance-1.patch
2015-09-13 07:09 Ondrej Pokorny Note Edited: 0085867 View Revisions
2015-09-13 10:47 Juha Manninen Assigned To => Juha Manninen
2015-09-13 10:47 Juha Manninen Status acknowledged => assigned
2015-09-13 10:52 Juha Manninen Note Added: 0085871
2015-09-13 11:06 Ondrej Pokorny Note Added: 0085872
2015-09-13 14:34 Ondrej Pokorny Note Added: 0085876
2015-09-13 14:34 Ondrej Pokorny File Added: ide-one-instance-2.patch
2015-09-13 20:46 Stephano Note Added: 0085880
2015-09-13 20:47 Stephano Note Edited: 0085880 View Revisions
2015-09-14 05:13 Ondrej Pokorny Note Added: 0085881
2015-09-14 09:23 Ondrej Pokorny Note Added: 0085883
2015-09-14 10:39 Juha Manninen Note Added: 0085886
2015-09-14 10:44 Juha Manninen Note Edited: 0085886 View Revisions
2015-09-14 10:51 Juha Manninen Note Edited: 0085886 View Revisions
2015-09-14 11:07 Ondrej Pokorny Note Added: 0085887
2015-09-14 11:09 Ondrej Pokorny Note Edited: 0085887 View Revisions
2015-09-14 11:15 Ondrej Pokorny Note Edited: 0085887 View Revisions
2015-09-14 11:16 Ondrej Pokorny Note Edited: 0085887 View Revisions
2015-09-14 11:17 Ondrej Pokorny File Added: ide-multiple-instances-2.png
2015-09-14 12:23 Stephano Note Added: 0085894
2015-09-14 12:29 Ondrej Pokorny Note Added: 0085896
2015-09-14 12:31 Ondrej Pokorny File Added: ide-multipleinstances-3.patch
2015-09-14 13:03 Juha Manninen Note Added: 0085897
2015-09-14 13:05 Ondrej Pokorny Note Added: 0085898
2015-09-14 13:07 Ondrej Pokorny Note Added: 0085899
2015-09-14 13:28 Juha Manninen Note Added: 0085901
2015-09-14 16:11 Ondrej Pokorny Note Added: 0085904
2015-09-29 11:52 Juha Manninen Note Added: 0086153
2015-09-30 19:46 Ondrej Pokorny File Added: ide-multipleinstances-4.patch
2015-09-30 19:47 Ondrej Pokorny Note Added: 0086188
2015-10-02 12:14 Juha Manninen Note Added: 0086209
2015-10-02 12:14 Juha Manninen Status assigned => feedback
2015-10-02 15:01 Ondrej Pokorny Note Added: 0086219
2015-10-03 01:25 Ondrej Pokorny Note Added: 0086229
2015-10-03 01:25 Ondrej Pokorny File Added: ide-advancedipc-1.patch
2015-10-03 11:45 Juha Manninen Fixed in Revision => r49907, r49908, r49909, r49917, r49918
2015-10-03 11:45 Juha Manninen Note Added: 0086231
2015-10-03 11:45 Juha Manninen Status feedback => resolved
2015-10-03 11:45 Juha Manninen Resolution open => fixed
2015-10-03 15:55 Ondrej Pokorny Note Added: 0086235
2017-03-12 17:31 Florian Status resolved => closed