Browse Source

* Applied patch from Ondrej to bring it in sync with bugreport version

git-svn-id: trunk@31890 -
michael 10 years ago
parent
commit
35a46aa5be
1 changed files with 215 additions and 88 deletions
  1. 215 88
      packages/fcl-base/src/advancedipc.pp

+ 215 - 88
packages/fcl-base/src/advancedipc.pp

@@ -2,7 +2,13 @@
     This file is part of the Free Component Library (FCL)
     This file is part of the Free Component Library (FCL)
     Copyright (c) 2015 by Ondrej Pokorny
     Copyright (c) 2015 by Ondrej Pokorny
 
 
-    Unit implementing two-way (request/response) IPC between 1 server and more clients, based on files.
+    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,
     See the file COPYING.FPC, included in this distribution,
     for details about the copyright.
     for details about the copyright.
@@ -27,13 +33,14 @@ uses
   sysutils, Classes;
   sysutils, Classes;
 
 
 const
 const
-  HEADER_VERSION = 1;
+  HEADER_VERSION = 2;
 
 
 type
 type
+  TMessageType = LongInt;
   TMessageHeader = packed record
   TMessageHeader = packed record
-    HeaderVersion: Integer;
+    HeaderVersion: Byte;
     FileLock: Byte;//0 = unlocked, 1 = locked
     FileLock: Byte;//0 = unlocked, 1 = locked
-    MsgType: Integer;
+    MsgType: TMessageType;
     MsgLen: Integer;
     MsgLen: Integer;
     MsgVersion: Integer;
     MsgVersion: Integer;
   end;
   end;
@@ -45,47 +52,58 @@ type
     destructor Destroy; override;
     destructor Destroy; override;
   end;
   end;
 
 
-  TIPCBase = class
+  TIPCBase = class(TComponent)
   private
   private
     FGlobal: Boolean;
     FGlobal: Boolean;
     FFileName: string;
     FFileName: string;
-    FServerName: string;
+    FServerID: string;
     FMessageVersion: Integer;
     FMessageVersion: Integer;
   protected
   protected
-    class function ServerNameToFileName(const aServerName: string; const aGlobal: Boolean): string;
+    class function ServerIDToFileName(const aServerID: string; const aGlobal: Boolean): string;
     function GetResponseFileName(const aMsgID: Integer): string;
     function GetResponseFileName(const aMsgID: Integer): string;
     function GetResponseFileName(const aRequestFileName: string): string;
     function GetResponseFileName(const aRequestFileName: string): string;
+    function GetPeekedRequestFileName(const aMsgID: Integer): string;
+    function GetPeekedRequestFileName(const aRequestFileName: string): string;
     function GetRequestPrefix: string;
     function GetRequestPrefix: string;
     function GetRequestFileName(const aMsgID: Integer): string;
     function GetRequestFileName(const aMsgID: Integer): string;
     function RequestFileNameToMsgID(const aFileName: string): Integer;
     function RequestFileNameToMsgID(const aFileName: string): Integer;
 
 
     function GetUniqueRequest(out outFileName: string): Integer;
     function GetUniqueRequest(out outFileName: string): Integer;
-    procedure SetServerName(const aServerName: string); virtual;
+    procedure SetServerID(const aServerID: string); virtual;
     procedure SetGlobal(const aGlobal: Boolean); virtual;
     procedure SetGlobal(const aGlobal: Boolean); 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);
+    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;
     property FileName: string read FFileName;
   public
   public
-    constructor Create; virtual;
+    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
   public
-    class procedure FindRunningServers(const aServerNamePrefix: string;
-      const outServerNames: TStrings; const aGlobal: Boolean = False);
-    class function ServerIsRunning(const aServerName: string; const aGlobal: Boolean = False): Boolean;
-    property ServerName: string read FServerName write SetServerName;
+    //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;
     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;
     property MessageVersion: Integer read FMessageVersion write FMessageVersion;
   end;
   end;
 
 
   TIPCClient = class(TIPCBase)
   TIPCClient = class(TIPCBase)
-  var
+  private
     FLastMsgFileName: string;
     FLastMsgFileName: string;
   public
   public
-    function PostRequest(const aMsgType: Integer; const aStream: TStream): Integer;//returns ID
-    function PeekResponse(const aStream: TStream; var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+    //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;
     procedure DeleteRequest;
-    function ServerRunning: Boolean;
+    //check if server is running
+    function ServerRunning: Boolean; overload;
   end;
   end;
 
 
   TIPCServer = class(TIPCBase)
   TIPCServer = class(TIPCBase)
@@ -93,33 +111,55 @@ type
     FFileHandle: TFileHandle;
     FFileHandle: TFileHandle;
     FActive: Boolean;
     FActive: Boolean;
 
 
-    function FindFirstRequest(out outFileName: string; out outStream: TStream; out outMsgType, outMsgLen: Integer): Integer;
+    function FindFirstRequest(out outFileName: string; out outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer): Integer;
 
 
   protected
   protected
-    procedure SetServerName(const aServerName: string); override;
+    procedure SetServerID(const aServerID: string); override;
     procedure SetGlobal(const aGlobal: Boolean); override;
     procedure SetGlobal(const aGlobal: Boolean); override;
   public
   public
-    constructor Create; override;
+    constructor Create(aOwner: TComponent); override;
     destructor Destroy; override;
     destructor Destroy; override;
   public
   public
-    function PeekRequest(const aStream: TStream; var outMsgType: Integer): Boolean; overload;
-    function PeekRequest(const aStream: TStream; var outMsgID, outMsgType: Integer): Boolean; overload;
-    function PeekRequest(const aStream: TStream; var outMsgID, outMsgType: Integer; const aTimeOut: Integer): Boolean; overload;
-    procedure PostResponse(const aMsgID: Integer; const aMsgType: Integer; const aStream: TStream);
-
+    //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 outMsgID: Integer; out outMsgType: TMessageType): Boolean; overload;
+    function PeekRequest(const aStream: TStream; out outMsgID: 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 outMsgID: Integer; out outMsgType: TMessageType): Boolean; overload;
+    function PeekRequest(out outMsgID: Integer; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean; overload;
+    //read a peeked request (that hasn't been read yet)
+    function ReadRequest(const aMsgID: Integer; const aStream: TStream): Boolean;
+    //delete a peeked request (that hasn't been read yet)
+    procedure DeleteRequest(const aMsgID: Integer);
+
+    //post response to a request
+    procedure PostResponse(const aMsgID: Integer; const aMsgType: TMessageType; const aStream: TStream);
+
+    //find the highest request ID from all pending requests
     function FindHighestPendingRequestId: Integer;
     function FindHighestPendingRequestId: Integer;
+    //get the pending request count
     function GetPendingRequestCount: Integer;
     function GetPendingRequestCount: Integer;
 
 
-    function StartServer(const aDeletePendingRequests: Boolean = True): Boolean;//returns true if unique and started
-    function StopServer(const aDeletePendingRequests: Boolean = True): Boolean;//returns true if stopped
+    //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;
     procedure DeletePendingRequests;
-
-    property Active: Boolean read FActive;//true if started
+  public
+    //true if server runs (was started)
+    property Active: Boolean read FActive;
   end;
   end;
 
 
   EICPException = class(Exception);
   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
 implementation
 
 
 const
 const
@@ -132,7 +172,8 @@ const
 { TIPCBase }
 { TIPCBase }
 
 
 function TIPCBase.CanReadMessage(const aFileName: string; out
 function TIPCBase.CanReadMessage(const aFileName: string; out
-  outStream: TStream; out outMsgType, outMsgLen: Integer): Boolean;
+  outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer
+  ): Boolean;
 var
 var
   xFileHandle: TFileHandle;
   xFileHandle: TFileHandle;
   xHeader: TMessageHeader;
   xHeader: TMessageHeader;
@@ -172,11 +213,6 @@ begin
   outMsgLen := xHeader.MsgLen;
   outMsgLen := xHeader.MsgLen;
 end;
 end;
 
 
-constructor TIPCBase.Create;
-begin
-  inherited Create;
-end;
-
 function TIPCBase.GetUniqueRequest(out outFileName: string): Integer;
 function TIPCBase.GetUniqueRequest(out outFileName: string): Integer;
 begin
 begin
   Randomize;
   Randomize;
@@ -186,13 +222,13 @@ begin
   until not FileExists(outFileName);
   until not FileExists(outFileName);
 end;
 end;
 
 
-class function TIPCBase.ServerIsRunning(const aServerName: string;
+class function TIPCBase.ServerRunning(const aServerID: string;
   const aGlobal: Boolean): Boolean;
   const aGlobal: Boolean): Boolean;
 var
 var
   xServerFileHandle: TFileHandle;
   xServerFileHandle: TFileHandle;
   xFileName: String;
   xFileName: String;
 begin
 begin
-  xFileName := ServerNameToFileName(aServerName, aGlobal);
+  xFileName := ServerIDToFileName(aServerID, aGlobal);
   Result := FileExists(xFileName);
   Result := FileExists(xFileName);
   if Result then
   if Result then
   begin//+ check -> we should not be able to access the file
   begin//+ check -> we should not be able to access the file
@@ -203,10 +239,10 @@ begin
   end;
   end;
 end;
 end;
 
 
-class function TIPCBase.ServerNameToFileName(const aServerName: string;
+class function TIPCBase.ServerIDToFileName(const aServerID: string;
   const aGlobal: Boolean): string;
   const aGlobal: Boolean): string;
 begin
 begin
-  Result := GetTempDir(aGlobal)+aServerName;
+  Result := GetTempDir(aGlobal)+aServerID;
 end;
 end;
 
 
 procedure TIPCBase.SetGlobal(const aGlobal: Boolean);
 procedure TIPCBase.SetGlobal(const aGlobal: Boolean);
@@ -214,11 +250,11 @@ begin
   if FGlobal = aGlobal then Exit;
   if FGlobal = aGlobal then Exit;
 
 
   FGlobal := aGlobal;
   FGlobal := aGlobal;
-  FFileName := ServerNameToFileName(FServerName, FGlobal);
+  FFileName := ServerIDToFileName(FServerID, FGlobal);
 end;
 end;
 
 
 procedure TIPCBase.DoPostMessage(const aFileName: string;
 procedure TIPCBase.DoPostMessage(const aFileName: string;
-  const aMsgType: Integer; const aStream: TStream);
+  const aMsgType: TMessageType; const aStream: TStream);
 var
 var
   xHeader: TMessageHeader;
   xHeader: TMessageHeader;
   xStream: TFileStream;
   xStream: TFileStream;
@@ -226,13 +262,17 @@ begin
   xHeader.HeaderVersion := HEADER_VERSION;
   xHeader.HeaderVersion := HEADER_VERSION;
   xHeader.FileLock := 1;//locking
   xHeader.FileLock := 1;//locking
   xHeader.MsgType := aMsgType;
   xHeader.MsgType := aMsgType;
-  xHeader.MsgLen := aStream.Size-aStream.Position;
+  if Assigned(aStream) then
+    xHeader.MsgLen := aStream.Size-aStream.Position
+  else
+    xHeader.MsgLen := 0;
   xHeader.MsgVersion := MessageVersion;
   xHeader.MsgVersion := MessageVersion;
 
 
   xStream := TFileStream.Create(aFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
   xStream := TFileStream.Create(aFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
   try
   try
     xStream.WriteBuffer(xHeader, SizeOf(xHeader));
     xStream.WriteBuffer(xHeader, SizeOf(xHeader));
-    xStream.CopyFrom(aStream, 0);
+    if Assigned(aStream) then
+      xStream.CopyFrom(aStream, 0);
 
 
     xStream.Position := 0;//unlocking
     xStream.Position := 0;//unlocking
     xHeader.FileLock := 0;
     xHeader.FileLock := 0;
@@ -244,29 +284,42 @@ end;
 
 
 function TIPCBase.RequestFileNameToMsgID(const aFileName: string): Integer;
 function TIPCBase.RequestFileNameToMsgID(const aFileName: string): Integer;
 begin
 begin
-  if Length(aFileName) > 8 then
+  //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)
     Result := StrToIntDef('$'+Copy(aFileName, Length(aFileName)-7, 8), -1)
   else
   else
     Result := -1;
     Result := -1;
 end;
 end;
 
 
-class procedure TIPCBase.FindRunningServers(const aServerNamePrefix: string;
-  const outServerNames: TStrings; const aGlobal: Boolean);
+class procedure TIPCBase.FindRunningServers(const aServerIDPrefix: string;
+  const outServerIDs: TStrings; const aGlobal: Boolean);
 var
 var
   xRec: TRawByteSearchRec;
   xRec: TRawByteSearchRec;
 begin
 begin
-  if FindFirst(ServerNameToFileName(aServerNamePrefix+'*', aGlobal), faAnyFile, xRec) = 0 then
+  if FindFirst(ServerIDToFileName(aServerIDPrefix+'*', aGlobal), faAnyFile, xRec) = 0 then
   begin
   begin
     repeat
     repeat
-      if (Pos('_', xRec.Name) = 0) and//file that we found is not pending a message
-         ServerIsRunning(xRec.Name)
+      if (Pos('-', xRec.Name) = 0) and//file that we found is a pending message
+         ServerRunning(xRec.Name, aGlobal)
       then
       then
-        outServerNames.Add(xRec.Name);
+        outServerIDs.Add(xRec.Name);
     until FindNext(xRec) <> 0;
     until FindNext(xRec) <> 0;
   end;
   end;
   FindClose(xRec);
   FindClose(xRec);
 end;
 end;
 
 
+function TIPCBase.GetPeekedRequestFileName(const aMsgID: Integer): string;
+begin
+  Result := GetPeekedRequestFileName(GetRequestFileName(aMsgID));
+end;
+
+function TIPCBase.GetPeekedRequestFileName(const aRequestFileName: string
+  ): string;
+begin
+  Result := aRequestFileName+'-t';
+end;
+
 function TIPCBase.GetRequestFileName(const aMsgID: Integer): string;
 function TIPCBase.GetRequestFileName(const aMsgID: Integer): string;
 begin
 begin
   Result := GetRequestPrefix+IntToHex(aMsgID, 8);
   Result := GetRequestPrefix+IntToHex(aMsgID, 8);
@@ -274,7 +327,7 @@ end;
 
 
 function TIPCBase.GetRequestPrefix: string;
 function TIPCBase.GetRequestPrefix: string;
 begin
 begin
-  Result := FFileName+'_';
+  Result := FFileName+'-';
 end;
 end;
 
 
 function TIPCBase.GetResponseFileName(const aMsgID: Integer): string;
 function TIPCBase.GetResponseFileName(const aMsgID: Integer): string;
@@ -284,22 +337,22 @@ end;
 
 
 function TIPCBase.GetResponseFileName(const aRequestFileName: string): string;
 function TIPCBase.GetResponseFileName(const aRequestFileName: string): string;
 begin
 begin
-  Result := aRequestFileName+'_r';
+  Result := aRequestFileName+'-r';
 end;
 end;
 
 
-procedure TIPCBase.SetServerName(const aServerName: string);
+procedure TIPCBase.SetServerID(const aServerID: string);
 var
 var
   I: Integer;
   I: Integer;
 begin
 begin
-  if FServerName = aServerName then Exit;
+  if FServerID = aServerID 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.');
+  for I := 1 to Length(aServerID) do
+  if not (aServerID[I] in ['a'..'z', 'A'..'Z', '0'..'9', '_']) then
+    raise EICPException.CreateFmt(SErrInvalidServerID , [aServerID]);
 
 
-  FServerName := aServerName;
+  FServerID := aServerID;
 
 
-  FFileName := ServerNameToFileName(FServerName, FGlobal);
+  FFileName := ServerIDToFileName(FServerID, FGlobal);
 end;
 end;
 
 
 { TIPCClient }
 { TIPCClient }
@@ -310,8 +363,8 @@ begin
     FLastMsgFileName := '';
     FLastMsgFileName := '';
 end;
 end;
 
 
-function TIPCClient.PeekResponse(const aStream: TStream;
-  var outMsgType: Integer; const aTimeOut: Integer): Boolean;
+function TIPCClient.PeekResponse(const aStream: TStream; out
+  outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
 var
 var
   xStart: QWord;
   xStart: QWord;
   xStream: TStream;
   xStream: TStream;
@@ -319,7 +372,6 @@ var
   xFileResponse: string;
   xFileResponse: string;
 begin
 begin
   aStream.Size := 0;
   aStream.Size := 0;
-  outMsgType := -1;
   Result := False;
   Result := False;
   xStart := GetTickCount64;
   xStart := GetTickCount64;
   repeat
   repeat
@@ -337,17 +389,45 @@ begin
   until (GetTickCount64-xStart > aTimeOut);
   until (GetTickCount64-xStart > aTimeOut);
 end;
 end;
 
 
-function TIPCClient.PostRequest(const aMsgType: Integer; const aStream: TStream
-  ): Integer;
+function TIPCClient.PostRequest(const aMsgType: TMessageType;
+  const aStream: TStream): Integer;
 begin
 begin
   Result := GetUniqueRequest(FLastMsgFileName);
   Result := GetUniqueRequest(FLastMsgFileName);
   DeleteFile(GetResponseFileName(FLastMsgFileName));//delete old response, if there is any
   DeleteFile(GetResponseFileName(FLastMsgFileName));//delete old response, if there is any
   DoPostMessage(FLastMsgFileName, aMsgType, aStream);
   DoPostMessage(FLastMsgFileName, aMsgType, aStream);
 end;
 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;
 function TIPCClient.ServerRunning: Boolean;
 begin
 begin
-  Result := ServerIsRunning(ServerName);
+  Result := ServerRunning(ServerID, Global);
 end;
 end;
 
 
 { TReleaseHandleStream }
 { TReleaseHandleStream }
@@ -376,9 +456,14 @@ begin
   FindClose(xRec);
   FindClose(xRec);
 end;
 end;
 
 
-constructor TIPCServer.Create;
+procedure TIPCServer.DeleteRequest(const aMsgID: Integer);
 begin
 begin
-  inherited Create;
+  DeleteFile(GetPeekedRequestFileName(aMsgID));
+end;
+
+constructor TIPCServer.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
 
 
   FFileHandle := feInvalidHandle;
   FFileHandle := feInvalidHandle;
 end;
 end;
@@ -392,7 +477,8 @@ begin
 end;
 end;
 
 
 function TIPCServer.FindFirstRequest(out outFileName: string; out
 function TIPCServer.FindFirstRequest(out outFileName: string; out
-  outStream: TStream; out outMsgType, outMsgLen: Integer): Integer;
+  outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer
+  ): Integer;
 var
 var
   xRec: TRawByteSearchRec;
   xRec: TRawByteSearchRec;
 begin
 begin
@@ -452,70 +538,111 @@ begin
   FindClose(xRec);
   FindClose(xRec);
 end;
 end;
 
 
-function TIPCServer.PeekRequest(const aStream: TStream; var outMsgID,
-  outMsgType: Integer): Boolean;
+function TIPCServer.PeekRequest(out outMsgID: Integer; out
+  outMsgType: TMessageType): Boolean;
 var
 var
   xStream: TStream;
   xStream: TStream;
   xMsgLen: Integer;
   xMsgLen: Integer;
   xMsgFileName: string;
   xMsgFileName: string;
 begin
 begin
-  aStream.Size := 0;
   outMsgType := -1;
   outMsgType := -1;
   xMsgFileName := '';
   xMsgFileName := '';
   outMsgID := FindFirstRequest(xMsgFileName, xStream, outMsgType, xMsgLen);
   outMsgID := FindFirstRequest(xMsgFileName, xStream, outMsgType, xMsgLen);
   Result := outMsgID >= 0;
   Result := outMsgID >= 0;
   if Result then
   if Result then
   begin
   begin
-    aStream.CopyFrom(xStream, xMsgLen);
-    aStream.Position := 0;
     xStream.Free;
     xStream.Free;
-    DeleteFile(xMsgFileName);
+    RenameFile(xMsgFileName, GetPeekedRequestFileName(xMsgFileName));
   end;
   end;
 end;
 end;
 
 
-function TIPCServer.PeekRequest(const aStream: TStream; var outMsgID,
-  outMsgType: Integer; const aTimeOut: Integer): Boolean;
+function TIPCServer.PeekRequest(out outMsgID: Integer; out
+  outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
 var
 var
   xStart: QWord;
   xStart: QWord;
 begin
 begin
   Result := False;
   Result := False;
   xStart := GetTickCount64;
   xStart := GetTickCount64;
   repeat
   repeat
-    if PeekRequest(aStream, outMsgID, outMsgType) then
+    if PeekRequest(outMsgID, outMsgType) then
       Exit(True)
       Exit(True)
     else if aTimeOut > 20 then
     else if aTimeOut > 20 then
       Sleep(10);
       Sleep(10);
   until (GetTickCount64-xStart > aTimeOut);
   until (GetTickCount64-xStart > aTimeOut);
 end;
 end;
 
 
-function TIPCServer.PeekRequest(const aStream: TStream; var outMsgType: Integer
-  ): Boolean;
+function TIPCServer.PeekRequest(out outMsgType: TMessageType): Boolean;
+var
+  xMsgID: Integer;
+begin
+  Result := PeekRequest(xMsgID, outMsgType);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; out outMsgID: Integer;
+  out outMsgType: TMessageType): Boolean;
+begin
+  Result := PeekRequest(outMsgID, outMsgType);
+  if Result then
+    Result := ReadRequest(outMsgID, aStream);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; out outMsgID: Integer;
+  out outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
+begin
+  Result := PeekRequest(outMsgID, outMsgType, aTimeOut);
+  if Result then
+    Result := ReadRequest(outMsgID, aStream);
+end;
+
+function TIPCServer.PeekRequest(const aStream: TStream; out
+  outMsgType: TMessageType): Boolean;
 var
 var
   xMsgID: Integer;
   xMsgID: Integer;
 begin
 begin
-  Result := PeekRequest(aStream, xMsgID{%H-}, outMsgType);
+  Result := PeekRequest(aStream, xMsgID, outMsgType);
 end;
 end;
 
 
 procedure TIPCServer.PostResponse(const aMsgID: Integer;
 procedure TIPCServer.PostResponse(const aMsgID: Integer;
-  const aMsgType: Integer; const aStream: TStream);
+  const aMsgType: TMessageType; const aStream: TStream);
 begin
 begin
   DoPostMessage(GetResponseFileName(aMsgID), aMsgType, aStream);
   DoPostMessage(GetResponseFileName(aMsgID), aMsgType, aStream);
 end;
 end;
 
 
+function TIPCServer.ReadRequest(const aMsgID: Integer; const aStream: TStream
+  ): Boolean;
+var
+  xStream: TStream;
+  xMsgLen: Integer;
+  xMsgType: TMessageType;
+  xFileRequest: string;
+begin
+  aStream.Size := 0;
+  xFileRequest := GetPeekedRequestFileName(aMsgID);
+  Result := CanReadMessage(xFileRequest, xStream, xMsgType, xMsgLen);
+  if Result then
+  begin
+    aStream.CopyFrom(xStream, xMsgLen);
+    xStream.Free;
+    aStream.Position := 0;
+    DeleteFile(xFileRequest);
+    Exit(True);
+  end;
+end;
+
 procedure TIPCServer.SetGlobal(const aGlobal: Boolean);
 procedure TIPCServer.SetGlobal(const aGlobal: Boolean);
 begin
 begin
   if Active then
   if Active then
-    raise EICPException.Create('You cannot change the global property when the server is active.');
+    raise EICPException.Create(SErrSetGlobalActive);
 
 
   inherited SetGlobal(aGlobal);
   inherited SetGlobal(aGlobal);
 end;
 end;
 
 
-procedure TIPCServer.SetServerName(const aServerName: string);
+procedure TIPCServer.SetServerID(const aServerID: string);
 begin
 begin
   if Active then
   if Active then
-    raise EICPException.Create('You cannot change the server name when the server is active.');
+    raise EICPException.Create(SErrSetServerIDActive);
 
 
-  inherited SetServerName(aServerName);
+  inherited SetServerID(aServerID);
 end;
 end;
 
 
 function TIPCServer.StartServer(const aDeletePendingRequests: Boolean): Boolean;
 function TIPCServer.StartServer(const aDeletePendingRequests: Boolean): Boolean;
@@ -534,7 +661,7 @@ begin
 
 
   if FFileHandle<>feInvalidHandle then
   if FFileHandle<>feInvalidHandle then
     FileClose(FFileHandle);
     FileClose(FFileHandle);
-  DeleteFile(FFileName);
+  Result := DeleteFile(FFileName);
   FFileName := '';
   FFileName := '';
 
 
   if aDeletePendingRequests then
   if aDeletePendingRequests then