advancedipc.pp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. {
  2. This file is part of the Free Component Library (Fcl)
  3. Copyright (c) 2015 by Ondrej Pokorny
  4. Unit implementing two-way (request/response) IPC between 1 server and more
  5. clients, based on files.
  6. The order of message processing is not deterministic (if there are more
  7. pending messages, the server won't process them in the order they have
  8. been sent to the server.
  9. SendRequest and PostRequest+PeekResponse sequences from 1 client are
  10. blocking and processed in correct order.
  11. See the file COPYING.FPC, included in this distribution,
  12. for details about the copyright.
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. **********************************************************************}
  17. {$IFNDEF FPC_DOTTEDUNITS}
  18. unit AdvancedIPC;
  19. {$ENDIF FPC_DOTTEDUNITS}
  20. {$mode objfpc}
  21. {$H+}
  22. interface
  23. {$IFDEF FPC_DOTTEDUNITS}
  24. uses
  25. {$IFDEF Unix}
  26. UnixApi.Base,
  27. {$endif}
  28. System.SysUtils, System.Classes, Fcl.SingleInstance;
  29. {$ELSE FPC_DOTTEDUNITS}
  30. uses
  31. {$IFDEF UNIX}
  32. baseunix,
  33. {$endif}
  34. sysutils, Classes, singleinstance;
  35. {$ENDIF FPC_DOTTEDUNITS}
  36. const
  37. HEADER_VERSION = 2;
  38. type
  39. TMessageType = LongInt;
  40. TMessageHeader = packed record
  41. HeaderVersion: Byte;
  42. FileLock: Byte;//0 = unlocked, 1 = locked
  43. MsgType: TMessageType;
  44. MsgLen: Integer;
  45. MsgVersion: Integer;
  46. end;
  47. TFileHandle = {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}Classes.THandle;
  48. TReleaseHandleStream = class(THandleStream)
  49. public
  50. destructor Destroy; override;
  51. end;
  52. TIPCBase = class(TComponent)
  53. private
  54. FGlobal: Boolean;
  55. FFileName: string;
  56. FServerID: string;
  57. FMessageVersion: Integer;
  58. protected
  59. class function ServerIDToFileName(const aServerID: string; const aGlobal: Boolean): string;
  60. function GetResponseFileName(const aRequestID: Integer): string;
  61. function GetResponseFileName(const aRequestFileName: string): string;
  62. function GetPeekedRequestFileName(const aRequestID: Integer): string;
  63. function GetPeekedRequestFileName(const aRequestFileName: string): string;
  64. function GetRequestPrefix: string;
  65. function GetRequestFileName(const aRequestID: Integer): string;
  66. function RequestFileNameToID(const aFileName: string): Integer;
  67. function RequestExists(const aRequestFileName: string): Boolean;
  68. procedure SetServerID(const aServerID: string); virtual;
  69. procedure SetGlobal(const aGlobal: Boolean); virtual;
  70. function CanReadMessage(const aFileName: string; out outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer): Boolean;
  71. procedure DoPostMessage(const aFileName: string; const aMsgType: TMessageType; const aStream: TStream); overload;
  72. procedure DoPostMessage(const aFileStream: TFileStream; const aMsgType: TMessageType; const aStream: TStream); overload;
  73. function DoReadMessage(const aFileName: string; const aStream: TStream; out outMsgType: TMessageType): Boolean;
  74. property FileName: string read FFileName;
  75. public
  76. class procedure FindRunningServers(const aServerIDPrefix: string;
  77. const outServerIDs: TStrings; const aGlobal: Boolean = False);
  78. class function ServerRunning(const aServerID: string; const aGlobal: Boolean = False): Boolean; overload;
  79. public
  80. //ServerID: name/ID of the server. Use only ['a'..'z', 'A'..'Z', '0'..'9', '_'] characters
  81. property ServerID: string read FServerID write SetServerID;
  82. //Global: if true, processes from different users can communicate; false, processes only from current user can communicate
  83. property Global: Boolean read FGlobal write SetGlobal;
  84. //MessageVersion: only messages with the same MessageVersion can be delivered between server/client
  85. property MessageVersion: Integer read FMessageVersion write FMessageVersion;
  86. end;
  87. TIPCClient = class(TIPCBase)
  88. private
  89. FLastRequestID: Integer;
  90. function CreateUniqueRequest(out outFileStream: TFileStream): Integer;
  91. function DoPeekResponse(const aResponseFileName: string; const aStream: TStream; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
  92. public
  93. constructor Create(aOwner: TComponent); override;
  94. public
  95. //post request to server, do not wait until request is peeked; returns request ID
  96. function PostRequest(const aMsgType: TMessageType; const aStream: TStream): Integer;
  97. //send request to server, wait until request is peeked; returns True if request was peeked within the aTimeOut limit
  98. function SendRequest(const aMsgType: TMessageType; const aStream: TStream; const aTimeOut: Integer): Boolean;
  99. function SendRequest(const aMsgType: TMessageType; const aStream: TStream; const aTimeOut: Integer; out outRequestID: Integer): Boolean;
  100. //peek a response from last request from this client
  101. function PeekResponse(const aStream: TStream; out outMsgType: TMessageType): Boolean; overload;
  102. function PeekResponse(const aStream: TStream; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean; overload;
  103. //peek a response from request by ID
  104. function PeekResponse(const aRequestID: Integer; const aStream: TStream; out outMsgType: TMessageType): Boolean; overload;
  105. function PeekResponse(const aRequestID: Integer; const aStream: TStream; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean; overload;
  106. //delete last request from this client, returns true if request file existed and was deleted
  107. function DeleteRequest: Boolean; overload;
  108. //delete request by ID, returns true if request existed file and was deleted
  109. function DeleteRequest(const aRequestID: Integer): Boolean; overload;
  110. //check if server is running
  111. function ServerRunning: Boolean; overload;
  112. end;
  113. TIPCServer = class(TIPCBase)
  114. private
  115. FFileHandle: TFileHandle;
  116. FActive: Boolean;
  117. function FindFirstRequest(out outFileName: string; out outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer): Integer;
  118. protected
  119. procedure SetServerID(const aServerID: string); override;
  120. procedure SetGlobal(const aGlobal: Boolean); override;
  121. public
  122. constructor Create(aOwner: TComponent); override;
  123. destructor Destroy; override;
  124. public
  125. //peek request and read the message into a stream
  126. function PeekRequest(const aStream: TStream; out outMsgType: TMessageType): Boolean; overload;
  127. function PeekRequest(const aStream: TStream; out outRequestID: Integer; out outMsgType: TMessageType): Boolean; overload;
  128. function PeekRequest(const aStream: TStream; out outRequestID: Integer; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean; overload;
  129. //only peek request, you have to read/delete the request manually with ReadRequest/DeleteRequest
  130. function PeekRequest(out outMsgType: TMessageType): Boolean; overload;
  131. function PeekRequest(out outRequestID: Integer; out outMsgType: TMessageType): Boolean; overload;
  132. function PeekRequest(out outRequestID: Integer; out outMsgType: TMessageType; const aTimeOut: Integer): Boolean; overload;
  133. //read a peeked request (that hasn't been read yet)
  134. function ReadRequest(const aRequestID: Integer; const aStream: TStream): Boolean;
  135. //delete a peeked request (that hasn't been read yet), returns true if request file existed and was deleted
  136. function DeleteRequest(const aRequestID: Integer): Boolean;
  137. //post response to a request
  138. procedure PostResponse(const aRequestID: Integer; const aMsgType: TMessageType; const aStream: TStream);
  139. //find the highest request ID from all pending requests
  140. function FindHighestPendingRequestId: Integer;
  141. //get the pending request count
  142. function GetPendingRequestCount: Integer;
  143. //start server: returns true if unique and started
  144. function StartServer(const aDeletePendingRequests: Boolean = True): Boolean;
  145. //stop server: returns true if stopped
  146. function StopServer(const aDeletePendingRequests: Boolean = True): Boolean;
  147. //delete all pending requests and responses
  148. procedure DeletePendingRequests;
  149. public
  150. //true if server runs (was started)
  151. property Active: Boolean read FActive;
  152. end;
  153. EICPException = class(Exception);
  154. resourcestring
  155. SErrInvalidServerID = 'Invalid server ID "%s". Please use only alphanumerical characters and underlines.';
  156. SErrSetGlobalActive = 'You cannot change the global property when the server is active.';
  157. SErrSetServerIDActive = 'You cannot change the server ID when the server is active.';
  158. implementation
  159. type
  160. TIPCSearchRec = TRawByteSearchRec;
  161. const
  162. {$IFDEF UNIX}
  163. GLOBAL_RIGHTS = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH or S_IWOTH;
  164. {$ELSE}
  165. GLOBAL_RIGHTS = 0;
  166. {$ENDIF}
  167. var
  168. CreateUniqueRequestCritSec: TRTLCriticalSection;
  169. { TIPCBase }
  170. function TIPCBase.CanReadMessage(const aFileName: string; out
  171. outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer
  172. ): Boolean;
  173. var
  174. xFileHandle: TFileHandle;
  175. xHeader: TMessageHeader;
  176. begin
  177. outStream := nil;
  178. outMsgType := -1;
  179. outMsgLen := 0;
  180. Result := FileExists(aFileName);
  181. if not Result then
  182. Exit;
  183. xFileHandle := FileOpen(aFileName, fmOpenRead or fmShareExclusive);
  184. Result := xFileHandle <> feInvalidHandle;
  185. if not Result then
  186. Exit;
  187. outStream := TReleaseHandleStream.Create(xFileHandle);
  188. Result := (outStream.Size >= SizeOf(xHeader));
  189. if not Result then
  190. begin
  191. FreeAndNil(outStream);
  192. Exit;
  193. end;
  194. outStream.ReadBuffer(xHeader{%H-}, SizeOf(xHeader));
  195. Result :=
  196. (xHeader.HeaderVersion = HEADER_VERSION) and (xHeader.FileLock = 0) and
  197. (xHeader.MsgVersion = MessageVersion) and
  198. (outStream.Size = Int64(SizeOf(xHeader))+Int64(xHeader.MsgLen));
  199. if not Result then
  200. begin
  201. FreeAndNil(outStream);
  202. Exit;
  203. end;
  204. outMsgType := xHeader.MsgType;
  205. outMsgLen := xHeader.MsgLen;
  206. end;
  207. function TIPCBase.DoReadMessage(const aFileName: string;
  208. const aStream: TStream; out outMsgType: TMessageType): Boolean;
  209. var
  210. xStream: TStream;
  211. xMsgLen: Integer;
  212. begin
  213. aStream.Size := 0;
  214. xStream := nil;
  215. try
  216. Result := CanReadMessage(aFileName, xStream, outMsgType, xMsgLen);
  217. if Result then
  218. begin
  219. if xMsgLen > 0 then
  220. aStream.CopyFrom(xStream, xMsgLen);
  221. FreeAndNil(xStream);
  222. aStream.Position := 0;
  223. DeleteFile(aFileName);
  224. end;
  225. finally
  226. xStream.Free;
  227. end;
  228. end;
  229. function TIPCBase.RequestExists(const aRequestFileName: string): Boolean;
  230. begin
  231. Result :=
  232. (FileExists(aRequestFileName) or
  233. FileExists(GetResponseFileName(aRequestFileName)) or
  234. FileExists(GetPeekedRequestFileName(aRequestFileName)));
  235. end;
  236. class function TIPCBase.ServerRunning(const aServerID: string;
  237. const aGlobal: Boolean): Boolean;
  238. var
  239. xServerFileHandle: TFileHandle;
  240. xFileName: String;
  241. begin
  242. xFileName := ServerIDToFileName(aServerID, aGlobal);
  243. Result := FileExists(xFileName);
  244. if Result then
  245. begin//+ check -> we should not be able to access the file
  246. xServerFileHandle := FileCreate(xFileName, fmOpenReadWrite or fmShareExclusive, GLOBAL_RIGHTS);
  247. Result := (xServerFileHandle=feInvalidHandle);
  248. if not Result then
  249. FileClose(xServerFileHandle);
  250. end;
  251. end;
  252. class function TIPCBase.ServerIDToFileName(const aServerID: string;
  253. const aGlobal: Boolean): string;
  254. begin
  255. Result := GetTempDir(aGlobal)+aServerID;
  256. end;
  257. procedure TIPCBase.SetGlobal(const aGlobal: Boolean);
  258. begin
  259. if FGlobal = aGlobal then Exit;
  260. FGlobal := aGlobal;
  261. FFileName := ServerIDToFileName(FServerID, FGlobal);
  262. end;
  263. procedure TIPCBase.DoPostMessage(const aFileName: string;
  264. const aMsgType: TMessageType; const aStream: TStream);
  265. var
  266. xStream: TFileStream;
  267. begin
  268. xStream := TFileStream.Create(aFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
  269. try
  270. DoPostMessage(xStream, aMsgType, aStream);
  271. finally
  272. xStream.Free;
  273. end;
  274. end;
  275. procedure TIPCBase.DoPostMessage(const aFileStream: TFileStream;
  276. const aMsgType: TMessageType; const aStream: TStream);
  277. var
  278. xHeader: TMessageHeader;
  279. begin
  280. xHeader.HeaderVersion := HEADER_VERSION;
  281. xHeader.FileLock := 1;//locking
  282. xHeader.MsgType := aMsgType;
  283. if Assigned(aStream) then
  284. xHeader.MsgLen := aStream.Size-aStream.Position
  285. else
  286. xHeader.MsgLen := 0;
  287. xHeader.MsgVersion := MessageVersion;
  288. aFileStream.WriteBuffer(xHeader, SizeOf(xHeader));
  289. if Assigned(aStream) and (aStream.Size-aStream.Position > 0) then
  290. aFileStream.CopyFrom(aStream, aStream.Size-aStream.Position);
  291. aFileStream.Position := 0;//unlocking
  292. xHeader.FileLock := 0;
  293. aFileStream.WriteBuffer(xHeader, SizeOf(xHeader));
  294. aFileStream.Seek(0, soEnd);
  295. end;
  296. function TIPCBase.RequestFileNameToID(const aFileName: string): Integer;
  297. begin
  298. //the function prevents all responses/temp files to be handled
  299. //only valid response files are returned
  300. if (Length(aFileName) > 9) and (aFileName[Length(aFileName)-8] = '-') then
  301. Result := StrToIntDef('$'+Copy(aFileName, Length(aFileName)-7, 8), -1)
  302. else
  303. Result := -1;
  304. end;
  305. class procedure TIPCBase.FindRunningServers(const aServerIDPrefix: string;
  306. const outServerIDs: TStrings; const aGlobal: Boolean);
  307. var
  308. xRec: TIPCSearchRec;
  309. begin
  310. if FindFirst(ServerIDToFileName(aServerIDPrefix+AllFilesMask, aGlobal), faAnyFile, xRec) = 0 then
  311. begin
  312. repeat
  313. if (Pos('-', xRec.Name) = 0) and//file that we found is a pending message
  314. ServerRunning(xRec.Name, aGlobal)
  315. then
  316. outServerIDs.Add(xRec.Name);
  317. until FindNext(xRec) <> 0;
  318. end;
  319. FindClose(xRec);
  320. end;
  321. function TIPCBase.GetPeekedRequestFileName(const aRequestID: Integer): string;
  322. begin
  323. Result := GetPeekedRequestFileName(GetRequestFileName(aRequestID));
  324. end;
  325. function TIPCBase.GetPeekedRequestFileName(const aRequestFileName: string
  326. ): string;
  327. begin
  328. Result := aRequestFileName+'-t';
  329. end;
  330. function TIPCBase.GetRequestFileName(const aRequestID: Integer): string;
  331. begin
  332. Result := GetRequestPrefix+IntToHex(aRequestID, 8);
  333. end;
  334. function TIPCBase.GetRequestPrefix: string;
  335. begin
  336. Result := FFileName+'-';
  337. end;
  338. function TIPCBase.GetResponseFileName(const aRequestID: Integer): string;
  339. begin
  340. Result := GetResponseFileName(GetRequestFileName(aRequestID));
  341. end;
  342. function TIPCBase.GetResponseFileName(const aRequestFileName: string): string;
  343. begin
  344. Result := aRequestFileName+'-r';
  345. end;
  346. procedure TIPCBase.SetServerID(const aServerID: string);
  347. var
  348. I: Integer;
  349. begin
  350. if FServerID = aServerID then Exit;
  351. for I := 1 to Length(aServerID) do
  352. if not (aServerID[I] in ['a'..'z', 'A'..'Z', '0'..'9', '_']) then
  353. raise EICPException.CreateFmt(SErrInvalidServerID , [aServerID]);
  354. FServerID := aServerID;
  355. FFileName := ServerIDToFileName(FServerID, FGlobal);
  356. end;
  357. { TIPCClient }
  358. constructor TIPCClient.Create(aOwner: TComponent);
  359. begin
  360. inherited Create(aOwner);
  361. FLastRequestID := -1;
  362. end;
  363. function TIPCClient.DeleteRequest(const aRequestID: Integer): Boolean;
  364. var
  365. xRequestFileName: string;
  366. begin
  367. xRequestFileName := GetRequestFileName(aRequestID);
  368. Result := DeleteFile(xRequestFileName);
  369. if (aRequestID = FLastRequestID) and not FileExists(xRequestFileName) then
  370. FLastRequestID := -1;
  371. end;
  372. function TIPCClient.DeleteRequest: Boolean;
  373. begin
  374. if FLastRequestID >= 0 then
  375. Result := DeleteRequest(FLastRequestID)
  376. else
  377. Result := False;
  378. end;
  379. function TIPCClient.DoPeekResponse(const aResponseFileName: string;
  380. const aStream: TStream; out outMsgType: TMessageType; const aTimeOut: Integer
  381. ): Boolean;
  382. var
  383. xStart: QWord;
  384. begin
  385. aStream.Size := 0;
  386. Result := False;
  387. xStart := GetTickCount64;
  388. repeat
  389. if DoReadMessage(aResponseFileName, aStream, outMsgType) then
  390. Exit(True)
  391. else if aTimeOut > 20 then
  392. Sleep(10);
  393. until (GetTickCount64-xStart > aTimeOut);
  394. end;
  395. function TIPCClient.CreateUniqueRequest(out outFileStream: TFileStream): Integer;
  396. var
  397. xFileName: string;
  398. begin
  399. outFileStream := nil;
  400. EnterCriticalsection(CreateUniqueRequestCritSec);
  401. try
  402. Randomize;
  403. repeat
  404. //if Randomize/Random is started from 2 processes at exactly same moment, it returns the same number! -> prevent duplicates by xor GetProcessId
  405. //the result must be of range 0..$7FFFFFFF (High(Integer))
  406. Result := Integer((PtrInt(Random($7FFFFFFF)) xor PtrInt(GetProcessID)) and $7FFFFFFF);
  407. xFileName := GetRequestFileName(Result);
  408. until not RequestExists(xFileName);
  409. outFileStream := TFileStream.Create(xFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
  410. finally
  411. LeaveCriticalsection(CreateUniqueRequestCritSec);
  412. end;
  413. end;
  414. function TIPCClient.PeekResponse(const aRequestID: Integer;
  415. const aStream: TStream; out outMsgType: TMessageType): Boolean;
  416. begin
  417. Result := DoReadMessage(GetResponseFileName(aRequestID), aStream, outMsgType);
  418. end;
  419. function TIPCClient.PeekResponse(const aRequestID: Integer;
  420. const aStream: TStream; out outMsgType: TMessageType; const aTimeOut: Integer
  421. ): Boolean;
  422. begin
  423. Result := DoPeekResponse(GetResponseFileName(aRequestID), aStream, outMsgType, aTimeOut);
  424. end;
  425. function TIPCClient.PeekResponse(const aStream: TStream; out
  426. outMsgType: TMessageType): Boolean;
  427. begin
  428. Result := DoReadMessage(GetResponseFileName(FLastRequestID), aStream, outMsgType);
  429. end;
  430. function TIPCClient.PeekResponse(const aStream: TStream; out
  431. outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
  432. begin
  433. Result := DoPeekResponse(GetResponseFileName(FLastRequestID), aStream, outMsgType, aTimeOut);
  434. end;
  435. function TIPCClient.PostRequest(const aMsgType: TMessageType;
  436. const aStream: TStream): Integer;
  437. var
  438. xRequestFileStream: TFileStream;
  439. begin
  440. xRequestFileStream := nil;
  441. try
  442. Result := CreateUniqueRequest(xRequestFileStream);
  443. DoPostMessage(xRequestFileStream, aMsgType, aStream);
  444. finally
  445. xRequestFileStream.Free;
  446. end;
  447. FLastRequestID := Result;
  448. end;
  449. function TIPCClient.SendRequest(const aMsgType: TMessageType;
  450. const aStream: TStream; const aTimeOut: Integer): Boolean;
  451. var
  452. xRequestID: Integer;
  453. begin
  454. Result := SendRequest(aMsgType, aStream, aTimeOut, xRequestID);
  455. end;
  456. function TIPCClient.SendRequest(const aMsgType: TMessageType;
  457. const aStream: TStream; const aTimeOut: Integer; out outRequestID: Integer
  458. ): Boolean;
  459. var
  460. xStart: QWord;
  461. xRequestFileName: string;
  462. begin
  463. outRequestID := PostRequest(aMsgType, aStream);
  464. Result := False;
  465. xRequestFileName := GetRequestFileName(outRequestID);
  466. xStart := GetTickCount64;
  467. repeat
  468. if not FileExists(xRequestFileName) then
  469. Exit(True)
  470. else if aTimeOut > 20 then
  471. Sleep(10);
  472. until (GetTickCount64-xStart > aTimeOut);
  473. end;
  474. function TIPCClient.ServerRunning: Boolean;
  475. begin
  476. Result := ServerRunning(ServerID, Global);
  477. end;
  478. { TReleaseHandleStream }
  479. destructor TReleaseHandleStream.Destroy;
  480. begin
  481. FileClose(Handle);
  482. inherited Destroy;
  483. end;
  484. { TIPCServer }
  485. procedure TIPCServer.DeletePendingRequests;
  486. var
  487. xRec: TIPCSearchRec;
  488. xDir: string;
  489. begin
  490. xDir := ExtractFilePath(FFileName);
  491. if FindFirst(GetRequestPrefix+AllFilesMask, faAnyFile, xRec) = 0 then
  492. begin
  493. repeat
  494. DeleteFile(xDir+xRec.Name);
  495. until FindNext(xRec) <> 0;
  496. end;
  497. FindClose(xRec);
  498. end;
  499. function TIPCServer.DeleteRequest(const aRequestID: Integer): Boolean;
  500. begin
  501. Result := DeleteFile(GetPeekedRequestFileName(aRequestID));
  502. end;
  503. constructor TIPCServer.Create(aOwner: TComponent);
  504. begin
  505. inherited Create(aOwner);
  506. FFileHandle := feInvalidHandle;
  507. end;
  508. destructor TIPCServer.Destroy;
  509. begin
  510. if Active then
  511. StopServer;
  512. inherited Destroy;
  513. end;
  514. function TIPCServer.FindFirstRequest(out outFileName: string; out
  515. outStream: TStream; out outMsgType: TMessageType; out outMsgLen: Integer
  516. ): Integer;
  517. var
  518. xRec: TIPCSearchRec;
  519. begin
  520. outFileName := '';
  521. outStream := nil;
  522. outMsgType := -1;
  523. outMsgLen := 0;
  524. Result := -1;
  525. if FindFirst(GetRequestPrefix+AllFilesMask, faAnyFile, xRec) = 0 then
  526. begin
  527. repeat
  528. Result := RequestFileNameToID(xRec.Name);
  529. if Result >= 0 then
  530. begin
  531. outFileName := GetRequestFileName(Result);
  532. if not CanReadMessage(outFileName, outStream, outMsgType, outMsgLen) then
  533. Result := -1;
  534. end;
  535. until (Result >= 0) or (FindNext(xRec) <> 0);
  536. end;
  537. FindClose(xRec);
  538. end;
  539. function TIPCServer.FindHighestPendingRequestId: Integer;
  540. var
  541. xRec: TIPCSearchRec;
  542. xRequestID: LongInt;
  543. begin
  544. Result := -1;
  545. if FindFirst(GetRequestPrefix+AllFilesMask, faAnyFile, xRec) = 0 then
  546. begin
  547. repeat
  548. xRequestID := RequestFileNameToID(xRec.Name);
  549. if xRequestID > Result then
  550. Result := xRequestID;
  551. until FindNext(xRec) <> 0;
  552. end;
  553. FindClose(xRec);
  554. end;
  555. function TIPCServer.GetPendingRequestCount: Integer;
  556. var
  557. xRec: TIPCSearchRec;
  558. begin
  559. Result := 0;
  560. if FindFirst(GetRequestPrefix+AllFilesMask, faAnyFile, xRec) = 0 then
  561. begin
  562. repeat
  563. if RequestFileNameToID(xRec.Name) >= 0 then
  564. Inc(Result);
  565. until FindNext(xRec) <> 0;
  566. end;
  567. FindClose(xRec);
  568. end;
  569. function TIPCServer.PeekRequest(out outRequestID: Integer; out
  570. outMsgType: TMessageType): Boolean;
  571. var
  572. xStream: TStream;
  573. xMsgLen: Integer;
  574. xMsgFileName: string;
  575. begin
  576. outMsgType := -1;
  577. xMsgFileName := '';
  578. xStream := nil;
  579. try
  580. outRequestID := FindFirstRequest(xMsgFileName, xStream, outMsgType, xMsgLen);
  581. Result := outRequestID >= 0;
  582. if Result then
  583. begin
  584. FreeAndNil(xStream);
  585. RenameFile(xMsgFileName, GetPeekedRequestFileName(xMsgFileName));
  586. end;
  587. finally
  588. xStream.Free;
  589. end;
  590. end;
  591. function TIPCServer.PeekRequest(out outRequestID: Integer; out
  592. outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
  593. var
  594. xStart: QWord;
  595. begin
  596. Result := False;
  597. xStart := GetTickCount64;
  598. repeat
  599. if PeekRequest(outRequestID, outMsgType) then
  600. Exit(True)
  601. else if aTimeOut > 20 then
  602. Sleep(10);
  603. until (GetTickCount64-xStart > aTimeOut);
  604. end;
  605. function TIPCServer.PeekRequest(out outMsgType: TMessageType): Boolean;
  606. var
  607. xRequestID: Integer;
  608. begin
  609. Result := PeekRequest(xRequestID, outMsgType);
  610. end;
  611. function TIPCServer.PeekRequest(const aStream: TStream; out outRequestID: Integer;
  612. out outMsgType: TMessageType): Boolean;
  613. begin
  614. Result := PeekRequest(outRequestID, outMsgType);
  615. if Result then
  616. Result := ReadRequest(outRequestID, aStream);
  617. end;
  618. function TIPCServer.PeekRequest(const aStream: TStream; out outRequestID: Integer;
  619. out outMsgType: TMessageType; const aTimeOut: Integer): Boolean;
  620. begin
  621. Result := PeekRequest(outRequestID, outMsgType, aTimeOut);
  622. if Result then
  623. Result := ReadRequest(outRequestID, aStream);
  624. end;
  625. function TIPCServer.PeekRequest(const aStream: TStream; out
  626. outMsgType: TMessageType): Boolean;
  627. var
  628. xRequestID: Integer;
  629. begin
  630. Result := PeekRequest(aStream, xRequestID, outMsgType);
  631. end;
  632. procedure TIPCServer.PostResponse(const aRequestID: Integer;
  633. const aMsgType: TMessageType; const aStream: TStream);
  634. begin
  635. DoPostMessage(GetResponseFileName(aRequestID), aMsgType, aStream);
  636. end;
  637. function TIPCServer.ReadRequest(const aRequestID: Integer; const aStream: TStream
  638. ): Boolean;
  639. var
  640. xMsgType: TMessageType;
  641. begin
  642. Result := DoReadMessage(GetPeekedRequestFileName(aRequestID), aStream, xMsgType);
  643. end;
  644. procedure TIPCServer.SetGlobal(const aGlobal: Boolean);
  645. begin
  646. if Active then
  647. raise EICPException.Create(SErrSetGlobalActive);
  648. inherited SetGlobal(aGlobal);
  649. end;
  650. procedure TIPCServer.SetServerID(const aServerID: string);
  651. begin
  652. if Active then
  653. raise EICPException.Create(SErrSetServerIDActive);
  654. inherited SetServerID(aServerID);
  655. end;
  656. function TIPCServer.StartServer(const aDeletePendingRequests: Boolean): Boolean;
  657. begin
  658. if Active then
  659. Exit(True);
  660. FFileHandle := FileCreate(FFileName, fmCreate or fmShareExclusive, GLOBAL_RIGHTS);
  661. Result := (FFileHandle<>feInvalidHandle);
  662. FActive := Result;
  663. if Result and aDeletePendingRequests then
  664. DeletePendingRequests;
  665. end;
  666. function TIPCServer.StopServer(const aDeletePendingRequests: Boolean): Boolean;
  667. begin
  668. if not Active then
  669. Exit(True);
  670. if FFileHandle<>feInvalidHandle then
  671. FileClose(FFileHandle);
  672. Result := DeleteFile(FFileName);
  673. if aDeletePendingRequests then
  674. DeletePendingRequests;
  675. FActive := False;
  676. end;
  677. initialization
  678. InitCriticalSection(CreateUniqueRequestCritSec);
  679. finalization
  680. DoneCriticalsection(CreateUniqueRequestCritSec);
  681. end.