advancedipc.pp 24 KB

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