blob: b602b64c8d6617d0b6a12e2c750aaabd6979b332 [file] [log] [blame]
Roger Meier3bef8c22012-10-06 06:58:00 +00001(*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *)
19unit Thrift.Transport.Pipes;
20
21{$WARN SYMBOL_PLATFORM OFF}
Jens Geyer9f7f11e2016-04-14 21:37:11 +020022{$I Thrift.Defines.inc}
Roger Meier3bef8c22012-10-06 06:58:00 +000023
24interface
25
26uses
Jens Geyer9f7f11e2016-04-14 21:37:11 +020027 {$IFDEF OLD_UNIT_NAMES}
Jens Geyer06045cf2013-03-27 20:26:25 +020028 Windows, SysUtils, Math, AccCtrl, AclAPI, SyncObjs,
Jens Geyer9f7f11e2016-04-14 21:37:11 +020029 {$ELSE}
Nick4f5229e2016-04-14 16:43:22 +030030 Winapi.Windows, System.SysUtils, System.Math, Winapi.AccCtrl, Winapi.AclAPI, System.SyncObjs,
Jens Geyer9f7f11e2016-04-14 21:37:11 +020031 {$ENDIF}
Roger Meier3bef8c22012-10-06 06:58:00 +000032 Thrift.Transport,
Jens Geyere9651362014-03-20 22:46:17 +020033 Thrift.Utils,
Roger Meier3bef8c22012-10-06 06:58:00 +000034 Thrift.Stream;
35
36const
Jens Geyer653f0de2016-04-20 12:46:57 +020037 DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT = 10; // default: fail fast on open
Roger Meier3bef8c22012-10-06 06:58:00 +000038
Jens Geyere9651362014-03-20 22:46:17 +020039
Roger Meier3bef8c22012-10-06 06:58:00 +000040type
Roger Meier79655fb2012-10-20 20:59:41 +000041 //--- Pipe Streams ---
Roger Meier3bef8c22012-10-06 06:58:00 +000042
43
Jens Geyer06045cf2013-03-27 20:26:25 +020044 TPipeStreamBase = class( TThriftStreamImpl)
Roger Meier79655fb2012-10-20 20:59:41 +000045 strict protected
46 FPipe : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +000047 FTimeout : DWORD;
Jens Geyer653f0de2016-04-20 12:46:57 +020048 FOpenTimeOut : DWORD; // separate value to allow for fail-fast-on-open scenarios
Jens Geyere9651362014-03-20 22:46:17 +020049 FOverlapped : Boolean;
Roger Meier3bef8c22012-10-06 06:58:00 +000050
Jens Geyer17c3ad92017-09-05 20:31:27 +020051 procedure Write( const pBuf : Pointer; offset, count : Integer); override;
52 function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
Roger Meier79655fb2012-10-20 20:59:41 +000053 //procedure Open; override; - see derived classes
Roger Meier3bef8c22012-10-06 06:58:00 +000054 procedure Close; override;
55 procedure Flush; override;
Jens Geyer41f47af2019-11-09 23:24:52 +010056 procedure CheckReadBytesAvailable( const value : Integer); override;
Roger Meier3bef8c22012-10-06 06:58:00 +000057
Jens Geyer17c3ad92017-09-05 20:31:27 +020058 function ReadDirect( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload;
59 function ReadOverlapped( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; overload;
60 procedure WriteDirect( const pBuf : Pointer; offset: Integer; count: Integer); overload;
61 procedure WriteOverlapped( const pBuf : Pointer; offset: Integer; count: Integer); overload;
Jens Geyere9651362014-03-20 22:46:17 +020062
Roger Meier3bef8c22012-10-06 06:58:00 +000063 function IsOpen: Boolean; override;
64 function ToArray: TBytes; override;
65 public
Jens Geyer653f0de2016-04-20 12:46:57 +020066 constructor Create( aEnableOverlapped : Boolean;
67 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
68 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT);
Roger Meier3bef8c22012-10-06 06:58:00 +000069 destructor Destroy; override;
70 end;
71
72
Jens Geyer06045cf2013-03-27 20:26:25 +020073 TNamedPipeStreamImpl = class sealed( TPipeStreamBase)
Jens Geyere9651362014-03-20 22:46:17 +020074 strict private
Roger Meier79655fb2012-10-20 20:59:41 +000075 FPipeName : string;
76 FShareMode : DWORD;
77 FSecurityAttribs : PSecurityAttributes;
Roger Meier3bef8c22012-10-06 06:58:00 +000078
Jens Geyere9651362014-03-20 22:46:17 +020079 strict protected
Roger Meier79655fb2012-10-20 20:59:41 +000080 procedure Open; override;
81
82 public
83 constructor Create( const aPipeName : string;
Jens Geyere9651362014-03-20 22:46:17 +020084 const aEnableOverlapped : Boolean;
Roger Meier79655fb2012-10-20 20:59:41 +000085 const aShareMode: DWORD = 0;
86 const aSecurityAttributes: PSecurityAttributes = nil;
Jens Geyer653f0de2016-04-20 12:46:57 +020087 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
88 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT); overload;
Roger Meier79655fb2012-10-20 20:59:41 +000089 end;
90
91
Jens Geyer06045cf2013-03-27 20:26:25 +020092 THandlePipeStreamImpl = class sealed( TPipeStreamBase)
Jens Geyere9651362014-03-20 22:46:17 +020093 strict private
Roger Meier79655fb2012-10-20 20:59:41 +000094 FSrcHandle : THandle;
95
Jens Geyere9651362014-03-20 22:46:17 +020096 strict protected
Roger Meier79655fb2012-10-20 20:59:41 +000097 procedure Open; override;
98
99 public
Jens Geyere9651362014-03-20 22:46:17 +0200100 constructor Create( const aPipeHandle : THandle;
101 const aOwnsHandle, aEnableOverlapped : Boolean;
Jens Geyer3e8d9272014-09-14 20:10:40 +0200102 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); overload;
Roger Meier79655fb2012-10-20 20:59:41 +0000103 destructor Destroy; override;
104 end;
105
106
107 //--- Pipe Transports ---
108
109
Jens Geyer06045cf2013-03-27 20:26:25 +0200110 IPipeTransport = interface( IStreamTransport)
Roger Meier79655fb2012-10-20 20:59:41 +0000111 ['{5E05CC85-434F-428F-BFB2-856A168B5558}']
112 end;
113
114
Jens Geyer06045cf2013-03-27 20:26:25 +0200115 TPipeTransportBase = class( TStreamTransportImpl, IPipeTransport)
Roger Meier79655fb2012-10-20 20:59:41 +0000116 public
117 // ITransport
118 function GetIsOpen: Boolean; override;
119 procedure Open; override;
120 procedure Close; override;
121 end;
122
123
Jens Geyer06045cf2013-03-27 20:26:25 +0200124 TNamedPipeTransportClientEndImpl = class( TPipeTransportBase)
Roger Meier79655fb2012-10-20 20:59:41 +0000125 public
Roger Meier3bef8c22012-10-06 06:58:00 +0000126 // Named pipe constructors
Jens Geyer41f47af2019-11-09 23:24:52 +0100127 constructor Create( const aPipe : THandle;
128 const aOwnsHandle : Boolean;
129 const aTimeOut : DWORD;
130 const aTransportCtl : ITransportControl); overload;
131
Roger Meier3bef8c22012-10-06 06:58:00 +0000132 constructor Create( const aPipeName : string;
133 const aShareMode: DWORD = 0;
134 const aSecurityAttributes: PSecurityAttributes = nil;
Jens Geyer653f0de2016-04-20 12:46:57 +0200135 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
Jens Geyer41f47af2019-11-09 23:24:52 +0100136 const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT;
137 const aTransportCtl : ITransportControl = nil); overload;
Roger Meier3bef8c22012-10-06 06:58:00 +0000138 end;
139
140
Jens Geyer06045cf2013-03-27 20:26:25 +0200141 TNamedPipeTransportServerEndImpl = class( TNamedPipeTransportClientEndImpl)
Roger Meier79655fb2012-10-20 20:59:41 +0000142 strict private
143 FHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000144 public
Roger Meier79655fb2012-10-20 20:59:41 +0000145 // ITransport
146 procedure Close; override;
Jens Geyer41f47af2019-11-09 23:24:52 +0100147 constructor Create( const aPipe : THandle;
148 const aOwnsHandle : Boolean;
149 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
150 const aTransportCtl : ITransportControl = nil); reintroduce;
Roger Meier79655fb2012-10-20 20:59:41 +0000151 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000152
Roger Meier79655fb2012-10-20 20:59:41 +0000153
Jens Geyer06045cf2013-03-27 20:26:25 +0200154 TAnonymousPipeTransportImpl = class( TPipeTransportBase)
Roger Meier79655fb2012-10-20 20:59:41 +0000155 public
Roger Meier3bef8c22012-10-06 06:58:00 +0000156 // Anonymous pipe constructor
Jens Geyer41f47af2019-11-09 23:24:52 +0100157 constructor Create( const aPipeRead, aPipeWrite : THandle;
158 const aOwnsHandles : Boolean;
159 const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT;
160 const aTransportCtl : ITransportControl = nil); overload;
Roger Meier3bef8c22012-10-06 06:58:00 +0000161 end;
162
163
Roger Meier79655fb2012-10-20 20:59:41 +0000164 //--- Server Transports ---
165
166
Jens Geyer06045cf2013-03-27 20:26:25 +0200167 IAnonymousPipeServerTransport = interface( IServerTransport)
Roger Meier3bef8c22012-10-06 06:58:00 +0000168 ['{7AEE6793-47B9-4E49-981A-C39E9108E9AD}']
169 // Server side anonymous pipe ends
Roger Meier79655fb2012-10-20 20:59:41 +0000170 function ReadHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000171 function WriteHandle : THandle;
172 // Client side anonymous pipe ends
173 function ClientAnonRead : THandle;
174 function ClientAnonWrite : THandle;
175 end;
176
177
Jens Geyer06045cf2013-03-27 20:26:25 +0200178 INamedPipeServerTransport = interface( IServerTransport)
Roger Meier79655fb2012-10-20 20:59:41 +0000179 ['{9DF9EE48-D065-40AF-8F67-D33037D3D960}']
180 function Handle : THandle;
181 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000182
Roger Meier79655fb2012-10-20 20:59:41 +0000183
Jens Geyer06045cf2013-03-27 20:26:25 +0200184 TPipeServerTransportBase = class( TServerTransportImpl)
Jens Geyere9651362014-03-20 22:46:17 +0200185 strict protected
186 FStopServer : TEvent;
Jens Geyer06045cf2013-03-27 20:26:25 +0200187 procedure InternalClose; virtual; abstract;
Jens Geyere9651362014-03-20 22:46:17 +0200188 function QueryStopServer : Boolean;
Roger Meier79655fb2012-10-20 20:59:41 +0000189 public
Jens Geyere9651362014-03-20 22:46:17 +0200190 constructor Create;
191 destructor Destroy; override;
Roger Meier79655fb2012-10-20 20:59:41 +0000192 procedure Listen; override;
Jens Geyer06045cf2013-03-27 20:26:25 +0200193 procedure Close; override;
Roger Meier79655fb2012-10-20 20:59:41 +0000194 end;
195
196
Jens Geyer06045cf2013-03-27 20:26:25 +0200197 TAnonymousPipeServerTransportImpl = class( TPipeServerTransportBase, IAnonymousPipeServerTransport)
Jens Geyere9651362014-03-20 22:46:17 +0200198 strict private
Roger Meier79655fb2012-10-20 20:59:41 +0000199 FBufSize : DWORD;
200
201 // Server side anonymous pipe handles
202 FReadHandle,
Roger Meier3bef8c22012-10-06 06:58:00 +0000203 FWriteHandle : THandle;
204
205 //Client side anonymous pipe handles
206 FClientAnonRead,
207 FClientAnonWrite : THandle;
208
Jens Geyerdd074e72016-04-19 23:31:33 +0200209 FTimeOut: DWORD;
Jens Geyerfad7fd32019-11-09 23:24:52 +0100210 strict protected
Jens Geyer01640402013-09-25 21:12:21 +0200211 function Accept(const fnAccepting: TProc): ITransport; override;
Roger Meier3bef8c22012-10-06 06:58:00 +0000212
Roger Meier3bef8c22012-10-06 06:58:00 +0000213 function CreateAnonPipe : Boolean;
214
Jens Geyer06045cf2013-03-27 20:26:25 +0200215 // IAnonymousPipeServerTransport
Roger Meier79655fb2012-10-20 20:59:41 +0000216 function ReadHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000217 function WriteHandle : THandle;
218 function ClientAnonRead : THandle;
219 function ClientAnonWrite : THandle;
220
Jens Geyer06045cf2013-03-27 20:26:25 +0200221 procedure InternalClose; override;
222
Roger Meier3bef8c22012-10-06 06:58:00 +0000223 public
Jens Geyerdd074e72016-04-19 23:31:33 +0200224 constructor Create(aBufsize : Cardinal = 4096; aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT);
Roger Meier3bef8c22012-10-06 06:58:00 +0000225 end;
226
227
Jens Geyer06045cf2013-03-27 20:26:25 +0200228 TNamedPipeServerTransportImpl = class( TPipeServerTransportBase, INamedPipeServerTransport)
Jens Geyere9651362014-03-20 22:46:17 +0200229 strict private
Roger Meier79655fb2012-10-20 20:59:41 +0000230 FPipeName : string;
231 FMaxConns : DWORD;
232 FBufSize : DWORD;
Jens Geyer0b20cc82013-03-07 20:47:01 +0100233 FTimeout : DWORD;
Jens Geyer06045cf2013-03-27 20:26:25 +0200234 FHandle : THandle;
235 FConnected : Boolean;
Jens Geyer01640402013-09-25 21:12:21 +0200236
237
Jens Geyere9651362014-03-20 22:46:17 +0200238 strict protected
Jens Geyer01640402013-09-25 21:12:21 +0200239 function Accept(const fnAccepting: TProc): ITransport; override;
Jens Geyer06045cf2013-03-27 20:26:25 +0200240 function CreateNamedPipe : THandle;
241 function CreateTransportInstance : ITransport;
Roger Meier79655fb2012-10-20 20:59:41 +0000242
Jens Geyer06045cf2013-03-27 20:26:25 +0200243 // INamedPipeServerTransport
Roger Meier79655fb2012-10-20 20:59:41 +0000244 function Handle : THandle;
Jens Geyer06045cf2013-03-27 20:26:25 +0200245 procedure InternalClose; override;
Roger Meier79655fb2012-10-20 20:59:41 +0000246
247 public
248 constructor Create( aPipename : string; aBufsize : Cardinal = 4096;
Jens Geyer0b20cc82013-03-07 20:47:01 +0100249 aMaxConns : Cardinal = PIPE_UNLIMITED_INSTANCES;
Jens Geyer2ad6c302015-02-26 19:38:53 +0100250 aTimeOut : Cardinal = INFINITE);
Roger Meier79655fb2012-10-20 20:59:41 +0000251 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000252
253
254implementation
255
256
Roger Meier79655fb2012-10-20 20:59:41 +0000257procedure ClosePipeHandle( var hPipe : THandle);
Roger Meier3bef8c22012-10-06 06:58:00 +0000258begin
Roger Meier79655fb2012-10-20 20:59:41 +0000259 if hPipe <> INVALID_HANDLE_VALUE
260 then try
261 CloseHandle( hPipe);
262 finally
263 hPipe := INVALID_HANDLE_VALUE;
264 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000265end;
266
267
Roger Meier79655fb2012-10-20 20:59:41 +0000268function DuplicatePipeHandle( const hSource : THandle) : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000269begin
Roger Meier79655fb2012-10-20 20:59:41 +0000270 if not DuplicateHandle( GetCurrentProcess, hSource,
271 GetCurrentProcess, @result,
272 0, FALSE, DUPLICATE_SAME_ACCESS)
Jens Geyere0e32402016-04-20 21:50:48 +0200273 then raise TTransportExceptionNotOpen.Create('DuplicateHandle: '+SysErrorMessage(GetLastError));
Roger Meier3bef8c22012-10-06 06:58:00 +0000274end;
275
276
Roger Meier79655fb2012-10-20 20:59:41 +0000277
Jens Geyer06045cf2013-03-27 20:26:25 +0200278{ TPipeStreamBase }
Roger Meier79655fb2012-10-20 20:59:41 +0000279
280
Jens Geyere9651362014-03-20 22:46:17 +0200281constructor TPipeStreamBase.Create( aEnableOverlapped : Boolean;
Jens Geyer653f0de2016-04-20 12:46:57 +0200282 const aTimeOut, aOpenTimeOut : DWORD);
Roger Meier79655fb2012-10-20 20:59:41 +0000283begin
284 inherited Create;
Jens Geyer653f0de2016-04-20 12:46:57 +0200285 ASSERT( aTimeout > 0); // aOpenTimeout may be 0
286 FPipe := INVALID_HANDLE_VALUE;
287 FTimeout := aTimeOut;
288 FOpenTimeOut := aOpenTimeOut;
289 FOverlapped := aEnableOverlapped;
Roger Meier79655fb2012-10-20 20:59:41 +0000290end;
291
292
Jens Geyer06045cf2013-03-27 20:26:25 +0200293destructor TPipeStreamBase.Destroy;
Roger Meier3bef8c22012-10-06 06:58:00 +0000294begin
295 try
296 Close;
297 finally
298 inherited Destroy;
299 end;
300end;
301
302
Jens Geyer06045cf2013-03-27 20:26:25 +0200303procedure TPipeStreamBase.Close;
Roger Meier3bef8c22012-10-06 06:58:00 +0000304begin
Roger Meier79655fb2012-10-20 20:59:41 +0000305 ClosePipeHandle( FPipe);
Roger Meier3bef8c22012-10-06 06:58:00 +0000306end;
307
308
Jens Geyer06045cf2013-03-27 20:26:25 +0200309procedure TPipeStreamBase.Flush;
Roger Meier3bef8c22012-10-06 06:58:00 +0000310begin
Jens Geyer0d227b12015-12-02 19:50:55 +0100311 FlushFileBuffers( FPipe);
Roger Meier3bef8c22012-10-06 06:58:00 +0000312end;
313
314
Jens Geyer06045cf2013-03-27 20:26:25 +0200315function TPipeStreamBase.IsOpen: Boolean;
Roger Meier3bef8c22012-10-06 06:58:00 +0000316begin
317 result := (FPipe <> INVALID_HANDLE_VALUE);
318end;
319
320
Jens Geyer41f47af2019-11-09 23:24:52 +0100321procedure TPipeStreamBase.CheckReadBytesAvailable( const value : Integer);
322begin
323 // can't tell how much we can suck out of the pipe
324end;
325
326
Jens Geyer17c3ad92017-09-05 20:31:27 +0200327procedure TPipeStreamBase.Write( const pBuf : Pointer; offset, count : Integer);
Jens Geyere9651362014-03-20 22:46:17 +0200328begin
329 if FOverlapped
Jens Geyer17c3ad92017-09-05 20:31:27 +0200330 then WriteOverlapped( pBuf, offset, count)
331 else WriteDirect( pBuf, offset, count);
Jens Geyere9651362014-03-20 22:46:17 +0200332end;
333
334
Jens Geyer17c3ad92017-09-05 20:31:27 +0200335function TPipeStreamBase.Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
Jens Geyere9651362014-03-20 22:46:17 +0200336begin
337 if FOverlapped
Jens Geyer17c3ad92017-09-05 20:31:27 +0200338 then result := ReadOverlapped( pBuf, buflen, offset, count)
339 else result := ReadDirect( pBuf, buflen, offset, count);
Jens Geyere9651362014-03-20 22:46:17 +0200340end;
341
342
Jens Geyer17c3ad92017-09-05 20:31:27 +0200343procedure TPipeStreamBase.WriteDirect( const pBuf : Pointer; offset: Integer; count: Integer);
Jens Geyerd4df9172017-10-25 22:30:23 +0200344var cbWritten, nBytes : DWORD;
Jens Geyer85827152018-01-12 21:20:59 +0100345 pData : PByte;
Roger Meier3bef8c22012-10-06 06:58:00 +0000346begin
347 if not IsOpen
Jens Geyere0e32402016-04-20 21:50:48 +0200348 then raise TTransportExceptionNotOpen.Create('Called write on non-open pipe');
Roger Meier3bef8c22012-10-06 06:58:00 +0000349
Jens Geyerd4df9172017-10-25 22:30:23 +0200350 // if necessary, send the data in chunks
351 // there's a system limit around 0x10000 bytes that we hit otherwise
352 // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
353 nBytes := Min( 15*4096, count); // 16 would exceed the limit
Jens Geyer85827152018-01-12 21:20:59 +0100354 pData := pBuf;
355 Inc( pData, offset);
Jens Geyerd4df9172017-10-25 22:30:23 +0200356 while nBytes > 0 do begin
Jens Geyer85827152018-01-12 21:20:59 +0100357 if not WriteFile( FPipe, pData^, nBytes, cbWritten, nil)
Jens Geyerd4df9172017-10-25 22:30:23 +0200358 then raise TTransportExceptionNotOpen.Create('Write to pipe failed');
359
Jens Geyer85827152018-01-12 21:20:59 +0100360 Inc( pData, cbWritten);
Jens Geyerd4df9172017-10-25 22:30:23 +0200361 Dec( count, cbWritten);
362 nBytes := Min( nBytes, count);
363 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000364end;
365
366
Jens Geyer17c3ad92017-09-05 20:31:27 +0200367procedure TPipeStreamBase.WriteOverlapped( const pBuf : Pointer; offset: Integer; count: Integer);
Jens Geyerd4df9172017-10-25 22:30:23 +0200368var cbWritten, dwWait, dwError, nBytes : DWORD;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200369 overlapped : IOverlappedHelper;
Jens Geyer85827152018-01-12 21:20:59 +0100370 pData : PByte;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200371begin
372 if not IsOpen
373 then raise TTransportExceptionNotOpen.Create('Called write on non-open pipe');
374
Jens Geyerd4df9172017-10-25 22:30:23 +0200375 // if necessary, send the data in chunks
376 // there's a system limit around 0x10000 bytes that we hit otherwise
377 // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
378 nBytes := Min( 15*4096, count); // 16 would exceed the limit
Jens Geyer85827152018-01-12 21:20:59 +0100379 pData := pBuf;
380 Inc( pData, offset);
Jens Geyerd4df9172017-10-25 22:30:23 +0200381 while nBytes > 0 do begin
382 overlapped := TOverlappedHelperImpl.Create;
Jens Geyer85827152018-01-12 21:20:59 +0100383 if not WriteFile( FPipe, pData^, nBytes, cbWritten, overlapped.OverlappedPtr)
Jens Geyerd4df9172017-10-25 22:30:23 +0200384 then begin
385 dwError := GetLastError;
386 case dwError of
387 ERROR_IO_PENDING : begin
388 dwWait := overlapped.WaitFor(FTimeout);
Jens Geyer17c3ad92017-09-05 20:31:27 +0200389
Jens Geyer00645162018-02-01 23:38:10 +0100390 if (dwWait = WAIT_TIMEOUT) then begin
391 CancelIo( FPipe); // prevents possible AV on invalid overlapped ptr
392 raise TTransportExceptionTimedOut.Create('Pipe write timed out');
393 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200394
Jens Geyerd4df9172017-10-25 22:30:23 +0200395 if (dwWait <> WAIT_OBJECT_0)
396 or not GetOverlappedResult( FPipe, overlapped.Overlapped, cbWritten, TRUE)
397 then raise TTransportExceptionUnknown.Create('Pipe write error');
398 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200399
Jens Geyerd4df9172017-10-25 22:30:23 +0200400 else
401 raise TTransportExceptionUnknown.Create(SysErrorMessage(dwError));
Jens Geyer17c3ad92017-09-05 20:31:27 +0200402 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200403 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200404
Jens Geyerd4df9172017-10-25 22:30:23 +0200405 ASSERT( DWORD(nBytes) = cbWritten);
406
Jens Geyer85827152018-01-12 21:20:59 +0100407 Inc( pData, cbWritten);
Jens Geyerd4df9172017-10-25 22:30:23 +0200408 Dec( count, cbWritten);
409 nBytes := Min( nBytes, count);
410 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200411end;
412
413
414function TPipeStreamBase.ReadDirect( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
Jens Geyerd4df9172017-10-25 22:30:23 +0200415var cbRead, dwErr, nRemaining : DWORD;
Roger Meier3bef8c22012-10-06 06:58:00 +0000416 bytes, retries : LongInt;
417 bOk : Boolean;
Jens Geyer85827152018-01-12 21:20:59 +0100418 pData : PByte;
Roger Meier3bef8c22012-10-06 06:58:00 +0000419const INTERVAL = 10; // ms
420begin
421 if not IsOpen
Jens Geyere0e32402016-04-20 21:50:48 +0200422 then raise TTransportExceptionNotOpen.Create('Called read on non-open pipe');
Roger Meier3bef8c22012-10-06 06:58:00 +0000423
424 // MSDN: Handle can be a handle to a named pipe instance,
425 // or it can be a handle to the read end of an anonymous pipe,
426 // The handle must have GENERIC_READ access to the pipe.
427 if FTimeOut <> INFINITE then begin
428 retries := Max( 1, Round( 1.0 * FTimeOut / INTERVAL));
429 while TRUE do begin
Jens Geyer5988f482016-04-19 23:01:24 +0200430 if not PeekNamedPipe( FPipe, nil, 0, nil, @bytes, nil) then begin
431 dwErr := GetLastError;
432 if (dwErr = ERROR_INVALID_HANDLE)
433 or (dwErr = ERROR_BROKEN_PIPE)
434 or (dwErr = ERROR_PIPE_NOT_CONNECTED)
435 then begin
436 result := 0; // other side closed the pipe
437 Exit;
438 end;
439 end
440 else if bytes > 0 then begin
441 Break; // there are data
Roger Meier79655fb2012-10-20 20:59:41 +0000442 end;
443
Roger Meier3bef8c22012-10-06 06:58:00 +0000444 Dec( retries);
445 if retries > 0
446 then Sleep( INTERVAL)
Jens Geyere0e32402016-04-20 21:50:48 +0200447 else raise TTransportExceptionTimedOut.Create('Pipe read timed out');
Roger Meier3bef8c22012-10-06 06:58:00 +0000448 end;
449 end;
450
Jens Geyerd4df9172017-10-25 22:30:23 +0200451 result := 0;
452 nRemaining := count;
Jens Geyer85827152018-01-12 21:20:59 +0100453 pData := pBuf;
454 Inc( pData, offset);
Jens Geyerd4df9172017-10-25 22:30:23 +0200455 while nRemaining > 0 do begin
456 // read the data (or block INFINITE-ly)
Jens Geyer85827152018-01-12 21:20:59 +0100457 bOk := ReadFile( FPipe, pData^, nRemaining, cbRead, nil);
Jens Geyerd4df9172017-10-25 22:30:23 +0200458 if (not bOk) and (GetLastError() <> ERROR_MORE_DATA)
459 then Break; // No more data, possibly because client disconnected.
460
461 Dec( nRemaining, cbRead);
Jens Geyer85827152018-01-12 21:20:59 +0100462 Inc( pData, cbRead);
Jens Geyerd4df9172017-10-25 22:30:23 +0200463 Inc( result, cbRead);
464 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000465end;
466
467
Jens Geyer17c3ad92017-09-05 20:31:27 +0200468function TPipeStreamBase.ReadOverlapped( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer;
Jens Geyerd4df9172017-10-25 22:30:23 +0200469var cbRead, dwWait, dwError, nRemaining : DWORD;
Jens Geyere9651362014-03-20 22:46:17 +0200470 bOk : Boolean;
471 overlapped : IOverlappedHelper;
Jens Geyer85827152018-01-12 21:20:59 +0100472 pData : PByte;
Jens Geyere9651362014-03-20 22:46:17 +0200473begin
474 if not IsOpen
Jens Geyere0e32402016-04-20 21:50:48 +0200475 then raise TTransportExceptionNotOpen.Create('Called read on non-open pipe');
Jens Geyere9651362014-03-20 22:46:17 +0200476
Jens Geyerd4df9172017-10-25 22:30:23 +0200477 result := 0;
478 nRemaining := count;
Jens Geyer85827152018-01-12 21:20:59 +0100479 pData := pBuf;
480 Inc( pData, offset);
Jens Geyerd4df9172017-10-25 22:30:23 +0200481 while nRemaining > 0 do begin
482 overlapped := TOverlappedHelperImpl.Create;
Jens Geyere9651362014-03-20 22:46:17 +0200483
Jens Geyerd4df9172017-10-25 22:30:23 +0200484 // read the data
Jens Geyer85827152018-01-12 21:20:59 +0100485 bOk := ReadFile( FPipe, pData^, nRemaining, cbRead, overlapped.OverlappedPtr);
Jens Geyerd4df9172017-10-25 22:30:23 +0200486 if not bOk then begin
487 dwError := GetLastError;
488 case dwError of
489 ERROR_IO_PENDING : begin
490 dwWait := overlapped.WaitFor(FTimeout);
Jens Geyere9651362014-03-20 22:46:17 +0200491
Jens Geyer00645162018-02-01 23:38:10 +0100492 if (dwWait = WAIT_TIMEOUT) then begin
493 CancelIo( FPipe); // prevents possible AV on invalid overlapped ptr
494 raise TTransportExceptionTimedOut.Create('Pipe read timed out');
495 end;
Jens Geyere9651362014-03-20 22:46:17 +0200496
Jens Geyerd4df9172017-10-25 22:30:23 +0200497 if (dwWait <> WAIT_OBJECT_0)
498 or not GetOverlappedResult( FPipe, overlapped.Overlapped, cbRead, TRUE)
499 then raise TTransportExceptionUnknown.Create('Pipe read error');
500 end;
501
502 else
503 raise TTransportExceptionUnknown.Create(SysErrorMessage(dwError));
Jens Geyere9651362014-03-20 22:46:17 +0200504 end;
Jens Geyere9651362014-03-20 22:46:17 +0200505 end;
Jens Geyere9651362014-03-20 22:46:17 +0200506
Jens Geyerd4df9172017-10-25 22:30:23 +0200507 ASSERT( cbRead > 0); // see TTransportImpl.ReadAll()
508 ASSERT( cbRead <= DWORD(nRemaining));
509 Dec( nRemaining, cbRead);
Jens Geyer85827152018-01-12 21:20:59 +0100510 Inc( pData, cbRead);
Jens Geyerd4df9172017-10-25 22:30:23 +0200511 Inc( result, cbRead);
512 end;
Jens Geyere9651362014-03-20 22:46:17 +0200513end;
514
515
Jens Geyer06045cf2013-03-27 20:26:25 +0200516function TPipeStreamBase.ToArray: TBytes;
Roger Meier3bef8c22012-10-06 06:58:00 +0000517var bytes : LongInt;
518begin
519 SetLength( result, 0);
520 bytes := 0;
521
522 if IsOpen
523 and PeekNamedPipe( FPipe, nil, 0, nil, @bytes, nil)
524 and (bytes > 0)
525 then begin
526 SetLength( result, bytes);
527 Read( result, 0, bytes);
528 end;
529end;
530
531
Roger Meier79655fb2012-10-20 20:59:41 +0000532{ TNamedPipeStreamImpl }
Roger Meier3bef8c22012-10-06 06:58:00 +0000533
534
Jens Geyere9651362014-03-20 22:46:17 +0200535constructor TNamedPipeStreamImpl.Create( const aPipeName : string;
536 const aEnableOverlapped : Boolean;
537 const aShareMode: DWORD;
Roger Meier79655fb2012-10-20 20:59:41 +0000538 const aSecurityAttributes: PSecurityAttributes;
Jens Geyer653f0de2016-04-20 12:46:57 +0200539 const aTimeOut, aOpenTimeOut : DWORD);
Roger Meier3bef8c22012-10-06 06:58:00 +0000540begin
Jens Geyer653f0de2016-04-20 12:46:57 +0200541 inherited Create( aEnableOverlapped, aTimeout, aOpenTimeOut);
Roger Meier79655fb2012-10-20 20:59:41 +0000542
543 FPipeName := aPipeName;
544 FShareMode := aShareMode;
545 FSecurityAttribs := aSecurityAttributes;
546
547 if Copy(FPipeName,1,2) <> '\\'
548 then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost
Roger Meier3bef8c22012-10-06 06:58:00 +0000549end;
550
551
Roger Meier79655fb2012-10-20 20:59:41 +0000552procedure TNamedPipeStreamImpl.Open;
553var hPipe : THandle;
Jens Geyerb89b5b92016-04-19 23:09:41 +0200554 retries, timeout, dwErr : DWORD;
555const INTERVAL = 10; // ms
Roger Meier79655fb2012-10-20 20:59:41 +0000556begin
557 if IsOpen then Exit;
558
Jens Geyer653f0de2016-04-20 12:46:57 +0200559 retries := Max( 1, Round( 1.0 * FOpenTimeOut / INTERVAL));
560 timeout := FOpenTimeOut;
Jens Geyerb89b5b92016-04-19 23:09:41 +0200561
562 // if the server hasn't gotten to the point where the pipe has been created, at least wait the timeout
563 // According to MSDN, if no instances of the specified named pipe exist, the WaitNamedPipe function
564 // returns IMMEDIATELY, regardless of the time-out value.
Jens Geyer653f0de2016-04-20 12:46:57 +0200565 // Always use INTERVAL, since WaitNamedPipe(0) defaults to some other value
Jens Geyerb89b5b92016-04-19 23:09:41 +0200566 while not WaitNamedPipe( PChar(FPipeName), INTERVAL) do begin
567 dwErr := GetLastError;
568 if dwErr <> ERROR_FILE_NOT_FOUND
Jens Geyere0e32402016-04-20 21:50:48 +0200569 then raise TTransportExceptionNotOpen.Create('Unable to open pipe, '+SysErrorMessage(dwErr));
Jens Geyerb89b5b92016-04-19 23:09:41 +0200570
571 if timeout <> INFINITE then begin
572 if (retries > 0)
573 then Dec(retries)
Jens Geyere0e32402016-04-20 21:50:48 +0200574 else raise TTransportExceptionNotOpen.Create('Unable to open pipe, timed out');
Jens Geyerb89b5b92016-04-19 23:09:41 +0200575 end;
576
577 Sleep(INTERVAL)
578 end;
579
Roger Meier79655fb2012-10-20 20:59:41 +0000580 // open that thingy
Roger Meier79655fb2012-10-20 20:59:41 +0000581 hPipe := CreateFile( PChar( FPipeName),
582 GENERIC_READ or GENERIC_WRITE,
583 FShareMode, // sharing
584 FSecurityAttribs, // security attributes
585 OPEN_EXISTING, // opens existing pipe
Jens Geyere9651362014-03-20 22:46:17 +0200586 FILE_FLAG_OVERLAPPED or FILE_FLAG_WRITE_THROUGH, // async+fast, please
Roger Meier79655fb2012-10-20 20:59:41 +0000587 0); // no template file
588
589 if hPipe = INVALID_HANDLE_VALUE
Jens Geyere0e32402016-04-20 21:50:48 +0200590 then raise TTransportExceptionNotOpen.Create('Unable to open pipe, '+SysErrorMessage(GetLastError));
Roger Meier79655fb2012-10-20 20:59:41 +0000591
Roger Meier79655fb2012-10-20 20:59:41 +0000592 // everything fine
593 FPipe := hPipe;
594end;
595
596
597{ THandlePipeStreamImpl }
598
599
Jens Geyere9651362014-03-20 22:46:17 +0200600constructor THandlePipeStreamImpl.Create( const aPipeHandle : THandle;
601 const aOwnsHandle, aEnableOverlapped : Boolean;
602 const aTimeOut : DWORD);
Roger Meier79655fb2012-10-20 20:59:41 +0000603begin
Jens Geyere9651362014-03-20 22:46:17 +0200604 inherited Create( aEnableOverlapped, aTimeOut);
Roger Meier79655fb2012-10-20 20:59:41 +0000605
606 if aOwnsHandle
607 then FSrcHandle := aPipeHandle
608 else FSrcHandle := DuplicatePipeHandle( aPipeHandle);
609
610 Open;
611end;
612
613
614destructor THandlePipeStreamImpl.Destroy;
615begin
616 try
617 ClosePipeHandle( FSrcHandle);
618 finally
619 inherited Destroy;
620 end;
621end;
622
623
624procedure THandlePipeStreamImpl.Open;
625begin
626 if not IsOpen
627 then FPipe := DuplicatePipeHandle( FSrcHandle);
628end;
629
630
Jens Geyer06045cf2013-03-27 20:26:25 +0200631{ TPipeTransportBase }
Roger Meier79655fb2012-10-20 20:59:41 +0000632
633
Jens Geyer06045cf2013-03-27 20:26:25 +0200634function TPipeTransportBase.GetIsOpen: Boolean;
Roger Meier79655fb2012-10-20 20:59:41 +0000635begin
Jens Geyer0b20cc82013-03-07 20:47:01 +0100636 result := (FInputStream <> nil) and (FInputStream.IsOpen)
637 and (FOutputStream <> nil) and (FOutputStream.IsOpen);
Roger Meier79655fb2012-10-20 20:59:41 +0000638end;
639
640
Jens Geyer06045cf2013-03-27 20:26:25 +0200641procedure TPipeTransportBase.Open;
Roger Meier79655fb2012-10-20 20:59:41 +0000642begin
643 FInputStream.Open;
644 FOutputStream.Open;
645end;
646
647
Jens Geyer06045cf2013-03-27 20:26:25 +0200648procedure TPipeTransportBase.Close;
Roger Meier79655fb2012-10-20 20:59:41 +0000649begin
650 FInputStream.Close;
651 FOutputStream.Close;
652end;
653
654
Jens Geyer06045cf2013-03-27 20:26:25 +0200655{ TNamedPipeTransportClientEndImpl }
Roger Meier79655fb2012-10-20 20:59:41 +0000656
657
Jens Geyer06045cf2013-03-27 20:26:25 +0200658constructor TNamedPipeTransportClientEndImpl.Create( const aPipeName : string; const aShareMode: DWORD;
Jens Geyer41f47af2019-11-09 23:24:52 +0100659 const aSecurityAttributes: PSecurityAttributes;
660 const aTimeOut, aOpenTimeOut : DWORD;
661 const aTransportCtl : ITransportControl);
Roger Meier3bef8c22012-10-06 06:58:00 +0000662// Named pipe constructor
663begin
Jens Geyer41f47af2019-11-09 23:24:52 +0100664 inherited Create( nil, nil, aTransportCtl);
Jens Geyer653f0de2016-04-20 12:46:57 +0200665 FInputStream := TNamedPipeStreamImpl.Create( aPipeName, TRUE, aShareMode, aSecurityAttributes, aTimeOut, aOpenTimeOut);
Roger Meier3bef8c22012-10-06 06:58:00 +0000666 FOutputStream := FInputStream; // true for named pipes
Roger Meier3bef8c22012-10-06 06:58:00 +0000667end;
668
669
Jens Geyer41f47af2019-11-09 23:24:52 +0100670constructor TNamedPipeTransportClientEndImpl.Create( const aPipe : THandle;
671 const aOwnsHandle : Boolean;
672 const aTimeOut : DWORD;
673 const aTransportCtl : ITransportControl);
Roger Meier3bef8c22012-10-06 06:58:00 +0000674// Named pipe constructor
675begin
Jens Geyer41f47af2019-11-09 23:24:52 +0100676 inherited Create( nil, nil, aTransportCtl);
Jens Geyere9651362014-03-20 22:46:17 +0200677 FInputStream := THandlePipeStreamImpl.Create( aPipe, TRUE, aOwnsHandle, aTimeOut);
Roger Meier3bef8c22012-10-06 06:58:00 +0000678 FOutputStream := FInputStream; // true for named pipes
Roger Meier3bef8c22012-10-06 06:58:00 +0000679end;
680
681
Jens Geyer06045cf2013-03-27 20:26:25 +0200682{ TNamedPipeTransportServerEndImpl }
Roger Meier79655fb2012-10-20 20:59:41 +0000683
684
Jens Geyer41f47af2019-11-09 23:24:52 +0100685constructor TNamedPipeTransportServerEndImpl.Create( const aPipe : THandle;
686 const aOwnsHandle : Boolean;
687 const aTimeOut : DWORD;
688 const aTransportCtl : ITransportControl);
Roger Meier79655fb2012-10-20 20:59:41 +0000689// Named pipe constructor
Roger Meier3bef8c22012-10-06 06:58:00 +0000690begin
Roger Meier79655fb2012-10-20 20:59:41 +0000691 FHandle := DuplicatePipeHandle( aPipe);
Jens Geyer41f47af2019-11-09 23:24:52 +0100692 inherited Create( aPipe, aOwnsHandle, aTimeOut, aTransportCtl);
Roger Meier3bef8c22012-10-06 06:58:00 +0000693end;
694
695
Jens Geyer06045cf2013-03-27 20:26:25 +0200696procedure TNamedPipeTransportServerEndImpl.Close;
Roger Meier3bef8c22012-10-06 06:58:00 +0000697begin
Roger Meier79655fb2012-10-20 20:59:41 +0000698 FlushFileBuffers( FHandle);
699 DisconnectNamedPipe( FHandle); // force client off the pipe
700 ClosePipeHandle( FHandle);
Roger Meier3bef8c22012-10-06 06:58:00 +0000701
Roger Meier79655fb2012-10-20 20:59:41 +0000702 inherited Close;
Roger Meier3bef8c22012-10-06 06:58:00 +0000703end;
704
705
Jens Geyer06045cf2013-03-27 20:26:25 +0200706{ TAnonymousPipeTransportImpl }
Roger Meier3bef8c22012-10-06 06:58:00 +0000707
708
Jens Geyerdd074e72016-04-19 23:31:33 +0200709constructor TAnonymousPipeTransportImpl.Create( const aPipeRead, aPipeWrite : THandle;
Jens Geyer41f47af2019-11-09 23:24:52 +0100710 const aOwnsHandles : Boolean;
711 const aTimeOut : DWORD;
712 const aTransportCtl : ITransportControl);
Roger Meier3bef8c22012-10-06 06:58:00 +0000713// Anonymous pipe constructor
714begin
Jens Geyer41f47af2019-11-09 23:24:52 +0100715 inherited Create( nil, nil, aTransportCtl);
Jens Geyere9651362014-03-20 22:46:17 +0200716 // overlapped is not supported with AnonPipes, see MSDN
Jens Geyerdd074e72016-04-19 23:31:33 +0200717 FInputStream := THandlePipeStreamImpl.Create( aPipeRead, aOwnsHandles, FALSE, aTimeOut);
718 FOutputStream := THandlePipeStreamImpl.Create( aPipeWrite, aOwnsHandles, FALSE, aTimeOut);
Roger Meier3bef8c22012-10-06 06:58:00 +0000719end;
720
721
Jens Geyer06045cf2013-03-27 20:26:25 +0200722{ TPipeServerTransportBase }
Roger Meier79655fb2012-10-20 20:59:41 +0000723
724
Jens Geyere9651362014-03-20 22:46:17 +0200725constructor TPipeServerTransportBase.Create;
726begin
727 inherited Create;
728 FStopServer := TEvent.Create(nil,TRUE,FALSE,''); // manual reset
729end;
730
731
732destructor TPipeServerTransportBase.Destroy;
733begin
734 try
735 FreeAndNil( FStopServer);
736 finally
737 inherited Destroy;
738 end;
739end;
740
741
742function TPipeServerTransportBase.QueryStopServer : Boolean;
743begin
744 result := (FStopServer = nil)
745 or (FStopServer.WaitFor(0) <> wrTimeout);
746end;
747
748
Jens Geyer06045cf2013-03-27 20:26:25 +0200749procedure TPipeServerTransportBase.Listen;
Roger Meier3bef8c22012-10-06 06:58:00 +0000750begin
Jens Geyere9651362014-03-20 22:46:17 +0200751 FStopServer.ResetEvent;
Roger Meier3bef8c22012-10-06 06:58:00 +0000752end;
753
754
Jens Geyer06045cf2013-03-27 20:26:25 +0200755procedure TPipeServerTransportBase.Close;
756begin
Jens Geyere9651362014-03-20 22:46:17 +0200757 FStopServer.SetEvent;
Jens Geyer06045cf2013-03-27 20:26:25 +0200758 InternalClose;
759end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000760
761
Jens Geyer06045cf2013-03-27 20:26:25 +0200762{ TAnonymousPipeServerTransportImpl }
763
764
Jens Geyerdd074e72016-04-19 23:31:33 +0200765constructor TAnonymousPipeServerTransportImpl.Create(aBufsize : Cardinal; aTimeOut : DWORD);
Roger Meier3bef8c22012-10-06 06:58:00 +0000766// Anonymous pipe CTOR
767begin
768 inherited Create;
Roger Meier3bef8c22012-10-06 06:58:00 +0000769 FBufsize := aBufSize;
Roger Meier79655fb2012-10-20 20:59:41 +0000770 FReadHandle := INVALID_HANDLE_VALUE;
Roger Meier3bef8c22012-10-06 06:58:00 +0000771 FWriteHandle := INVALID_HANDLE_VALUE;
772 FClientAnonRead := INVALID_HANDLE_VALUE;
773 FClientAnonWrite := INVALID_HANDLE_VALUE;
Jens Geyerdd074e72016-04-19 23:31:33 +0200774 FTimeOut := aTimeOut;
Roger Meier3bef8c22012-10-06 06:58:00 +0000775
776 // The anonymous pipe needs to be created first so that the server can
777 // pass the handles on to the client before the serve (acceptImpl)
778 // blocking call.
779 if not CreateAnonPipe
Jens Geyere0e32402016-04-20 21:50:48 +0200780 then raise TTransportExceptionNotOpen.Create(ClassName+'.Create() failed');
Roger Meier3bef8c22012-10-06 06:58:00 +0000781end;
782
783
Jens Geyer01640402013-09-25 21:12:21 +0200784function TAnonymousPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITransport;
Roger Meier3bef8c22012-10-06 06:58:00 +0000785var buf : Byte;
786 br : DWORD;
Roger Meier3bef8c22012-10-06 06:58:00 +0000787begin
Jens Geyer01640402013-09-25 21:12:21 +0200788 if Assigned(fnAccepting)
789 then fnAccepting();
790
Roger Meier79655fb2012-10-20 20:59:41 +0000791 // This 0-byte read serves merely as a blocking call.
792 if not ReadFile( FReadHandle, buf, 0, br, nil)
793 and (GetLastError() <> ERROR_MORE_DATA)
Jens Geyere0e32402016-04-20 21:50:48 +0200794 then raise TTransportExceptionNotOpen.Create('TServerPipe unable to initiate pipe communication');
Jens Geyer06045cf2013-03-27 20:26:25 +0200795
796 // create the transport impl
Jens Geyerdd074e72016-04-19 23:31:33 +0200797 result := TAnonymousPipeTransportImpl.Create( FReadHandle, FWriteHandle, FALSE, FTimeOut);
Roger Meier3bef8c22012-10-06 06:58:00 +0000798end;
799
800
Jens Geyer06045cf2013-03-27 20:26:25 +0200801procedure TAnonymousPipeServerTransportImpl.InternalClose;
Roger Meier3bef8c22012-10-06 06:58:00 +0000802begin
Roger Meier79655fb2012-10-20 20:59:41 +0000803 ClosePipeHandle( FReadHandle);
804 ClosePipeHandle( FWriteHandle);
805 ClosePipeHandle( FClientAnonRead);
806 ClosePipeHandle( FClientAnonWrite);
Roger Meier3bef8c22012-10-06 06:58:00 +0000807end;
808
809
Jens Geyer06045cf2013-03-27 20:26:25 +0200810function TAnonymousPipeServerTransportImpl.ReadHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000811begin
Roger Meier79655fb2012-10-20 20:59:41 +0000812 result := FReadHandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000813end;
814
815
Jens Geyer06045cf2013-03-27 20:26:25 +0200816function TAnonymousPipeServerTransportImpl.WriteHandle : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000817begin
818 result := FWriteHandle;
819end;
820
821
Jens Geyer06045cf2013-03-27 20:26:25 +0200822function TAnonymousPipeServerTransportImpl.ClientAnonRead : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000823begin
824 result := FClientAnonRead;
825end;
826
827
Jens Geyer06045cf2013-03-27 20:26:25 +0200828function TAnonymousPipeServerTransportImpl.ClientAnonWrite : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000829begin
830 result := FClientAnonWrite;
831end;
832
833
Jens Geyer06045cf2013-03-27 20:26:25 +0200834function TAnonymousPipeServerTransportImpl.CreateAnonPipe : Boolean;
Roger Meier79655fb2012-10-20 20:59:41 +0000835var sd : PSECURITY_DESCRIPTOR;
836 sa : SECURITY_ATTRIBUTES; //TSecurityAttributes;
837 hCAR, hPipeW, hCAW, hPipe : THandle;
838begin
Roger Meier79655fb2012-10-20 20:59:41 +0000839 sd := PSECURITY_DESCRIPTOR( LocalAlloc( LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH));
Jens Geyerb64a7742013-01-23 20:58:47 +0100840 try
841 Win32Check( InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION));
842 Win32Check( SetSecurityDescriptorDacl( sd, TRUE, nil, FALSE));
Roger Meier79655fb2012-10-20 20:59:41 +0000843
Jens Geyerb64a7742013-01-23 20:58:47 +0100844 sa.nLength := sizeof( sa);
845 sa.lpSecurityDescriptor := sd;
846 sa.bInheritHandle := TRUE; //allow passing handle to child
Roger Meier79655fb2012-10-20 20:59:41 +0000847
Jens Geyer17c3ad92017-09-05 20:31:27 +0200848 Result := CreatePipe( hCAR, hPipeW, @sa, FBufSize); //create stdin pipe
849 if not Result then begin //create stdin pipe
Jens Geyere0e32402016-04-20 21:50:48 +0200850 raise TTransportExceptionNotOpen.Create('TServerPipe CreatePipe (anon) failed, '+SysErrorMessage(GetLastError));
Jens Geyerb64a7742013-01-23 20:58:47 +0100851 Exit;
852 end;
853
Jens Geyer17c3ad92017-09-05 20:31:27 +0200854 Result := CreatePipe( hPipe, hCAW, @sa, FBufSize); //create stdout pipe
855 if not Result then begin //create stdout pipe
Jens Geyerb64a7742013-01-23 20:58:47 +0100856 CloseHandle( hCAR);
857 CloseHandle( hPipeW);
Jens Geyere0e32402016-04-20 21:50:48 +0200858 raise TTransportExceptionNotOpen.Create('TServerPipe CreatePipe (anon) failed, '+SysErrorMessage(GetLastError));
Jens Geyerb64a7742013-01-23 20:58:47 +0100859 Exit;
860 end;
861
862 FClientAnonRead := hCAR;
863 FClientAnonWrite := hCAW;
864 FReadHandle := hPipe;
865 FWriteHandle := hPipeW;
Jens Geyerb64a7742013-01-23 20:58:47 +0100866 finally
867 if sd <> nil then LocalFree( Cardinal(sd));
Roger Meier79655fb2012-10-20 20:59:41 +0000868 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000869end;
870
871
Jens Geyer06045cf2013-03-27 20:26:25 +0200872{ TNamedPipeServerTransportImpl }
Roger Meier79655fb2012-10-20 20:59:41 +0000873
874
Jens Geyer06045cf2013-03-27 20:26:25 +0200875constructor TNamedPipeServerTransportImpl.Create( aPipename : string; aBufsize, aMaxConns, aTimeOut : Cardinal);
Roger Meier79655fb2012-10-20 20:59:41 +0000876// Named Pipe CTOR
877begin
878 inherited Create;
Jens Geyere9651362014-03-20 22:46:17 +0200879 ASSERT( aTimeout > 0);
Jens Geyer06045cf2013-03-27 20:26:25 +0200880 FPipeName := aPipename;
881 FBufsize := aBufSize;
882 FMaxConns := Max( 1, Min( PIPE_UNLIMITED_INSTANCES, aMaxConns));
883 FHandle := INVALID_HANDLE_VALUE;
884 FTimeout := aTimeOut;
885 FConnected := FALSE;
Roger Meier79655fb2012-10-20 20:59:41 +0000886
887 if Copy(FPipeName,1,2) <> '\\'
888 then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost
889end;
890
891
Jens Geyer01640402013-09-25 21:12:21 +0200892function TNamedPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITransport;
Jens Geyer06045cf2013-03-27 20:26:25 +0200893var dwError, dwWait, dwDummy : DWORD;
Jens Geyere9651362014-03-20 22:46:17 +0200894 overlapped : IOverlappedHelper;
895 handles : array[0..1] of THandle;
Jens Geyer01640402013-09-25 21:12:21 +0200896begin
Jens Geyere9651362014-03-20 22:46:17 +0200897 overlapped := TOverlappedHelperImpl.Create;
Jens Geyer01640402013-09-25 21:12:21 +0200898
Jens Geyere9651362014-03-20 22:46:17 +0200899 ASSERT( not FConnected);
Jens Geyer2ad6c302015-02-26 19:38:53 +0100900 CreateNamedPipe;
Jens Geyere9651362014-03-20 22:46:17 +0200901 while not FConnected do begin
Jens Geyer2ad6c302015-02-26 19:38:53 +0100902
Jens Geyer00645162018-02-01 23:38:10 +0100903 if QueryStopServer then begin
904 InternalClose;
905 Abort;
906 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000907
Jens Geyere9651362014-03-20 22:46:17 +0200908 if Assigned(fnAccepting)
909 then fnAccepting();
Jens Geyer01640402013-09-25 21:12:21 +0200910
Jens Geyere9651362014-03-20 22:46:17 +0200911 // Wait for the client to connect; if it succeeds, the
912 // function returns a nonzero value. If the function returns
913 // zero, GetLastError should return ERROR_PIPE_CONNECTED.
914 if ConnectNamedPipe( Handle, overlapped.OverlappedPtr) then begin
915 FConnected := TRUE;
916 Break;
Jens Geyer01640402013-09-25 21:12:21 +0200917 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000918
Jens Geyere9651362014-03-20 22:46:17 +0200919 // ConnectNamedPipe() returns FALSE for OverlappedIO, even if connected.
920 // We have to check GetLastError() explicitly to find out
921 dwError := GetLastError;
922 case dwError of
923 ERROR_PIPE_CONNECTED : begin
924 FConnected := not QueryStopServer; // special case: pipe immediately connected
925 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000926
Jens Geyere9651362014-03-20 22:46:17 +0200927 ERROR_IO_PENDING : begin
928 handles[0] := overlapped.WaitHandle;
929 handles[1] := FStopServer.Handle;
930 dwWait := WaitForMultipleObjects( 2, @handles, FALSE, FTimeout);
931 FConnected := (dwWait = WAIT_OBJECT_0)
932 and GetOverlappedResult( Handle, overlapped.Overlapped, dwDummy, TRUE)
933 and not QueryStopServer;
934 end;
935
936 else
937 InternalClose;
Jens Geyere0e32402016-04-20 21:50:48 +0200938 raise TTransportExceptionNotOpen.Create('Client connection failed');
Jens Geyere9651362014-03-20 22:46:17 +0200939 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000940 end;
Jens Geyere9651362014-03-20 22:46:17 +0200941
942 // create the transport impl
943 result := CreateTransportInstance;
Roger Meier79655fb2012-10-20 20:59:41 +0000944end;
945
946
Jens Geyer06045cf2013-03-27 20:26:25 +0200947function TNamedPipeServerTransportImpl.CreateTransportInstance : ITransport;
948// create the transport impl
949var hPipe : THandle;
Roger Meier79655fb2012-10-20 20:59:41 +0000950begin
Jens Geyer06045cf2013-03-27 20:26:25 +0200951 hPipe := THandle( InterlockedExchangePointer( Pointer(FHandle), Pointer(INVALID_HANDLE_VALUE)));
952 try
953 FConnected := FALSE;
Jens Geyere9651362014-03-20 22:46:17 +0200954 result := TNamedPipeTransportServerEndImpl.Create( hPipe, TRUE, FTimeout);
Jens Geyer06045cf2013-03-27 20:26:25 +0200955 except
Jens Geyer01640402013-09-25 21:12:21 +0200956 ClosePipeHandle(hPipe);
957 raise;
958 end;
Roger Meier79655fb2012-10-20 20:59:41 +0000959end;
960
961
Jens Geyer06045cf2013-03-27 20:26:25 +0200962procedure TNamedPipeServerTransportImpl.InternalClose;
963var hPipe : THandle;
964begin
965 hPipe := THandle( InterlockedExchangePointer( Pointer(FHandle), Pointer(INVALID_HANDLE_VALUE)));
966 if hPipe = INVALID_HANDLE_VALUE then Exit;
967
968 try
969 if FConnected
970 then FlushFileBuffers( hPipe)
971 else CancelIo( hPipe);
972 DisconnectNamedPipe( hPipe);
973 finally
974 ClosePipeHandle( hPipe);
975 FConnected := FALSE;
976 end;
977end;
978
979
980function TNamedPipeServerTransportImpl.Handle : THandle;
981begin
982 {$IFDEF WIN64}
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200983 result := THandle( InterlockedExchangeAdd64( Int64(FHandle), 0));
Jens Geyer06045cf2013-03-27 20:26:25 +0200984 {$ELSE}
985 result := THandle( InterlockedExchangeAdd( Integer(FHandle), 0));
986 {$ENDIF}
987end;
988
989
990function TNamedPipeServerTransportImpl.CreateNamedPipe : THandle;
Roger Meier3bef8c22012-10-06 06:58:00 +0000991var SIDAuthWorld : SID_IDENTIFIER_AUTHORITY ;
992 everyone_sid : PSID;
993 ea : EXPLICIT_ACCESS;
994 acl : PACL;
995 sd : PSECURITY_DESCRIPTOR;
996 sa : SECURITY_ATTRIBUTES;
Roger Meier3bef8c22012-10-06 06:58:00 +0000997const
998 SECURITY_WORLD_SID_AUTHORITY : TSIDIdentifierAuthority = (Value : (0,0,0,0,0,1));
999 SECURITY_WORLD_RID = $00000000;
1000begin
Jens Geyerb64a7742013-01-23 20:58:47 +01001001 sd := nil;
Roger Meier3bef8c22012-10-06 06:58:00 +00001002 everyone_sid := nil;
Jens Geyerb64a7742013-01-23 20:58:47 +01001003 try
Jens Geyer06045cf2013-03-27 20:26:25 +02001004 ASSERT( (FHandle = INVALID_HANDLE_VALUE) and not FConnected);
1005
Jens Geyerb64a7742013-01-23 20:58:47 +01001006 // Windows - set security to allow non-elevated apps
1007 // to access pipes created by elevated apps.
1008 SIDAuthWorld := SECURITY_WORLD_SID_AUTHORITY;
1009 AllocateAndInitializeSid( SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, everyone_sid);
Roger Meier3bef8c22012-10-06 06:58:00 +00001010
Jens Geyerb64a7742013-01-23 20:58:47 +01001011 ZeroMemory( @ea, SizeOf(ea));
1012 ea.grfAccessPermissions := GENERIC_ALL; //SPECIFIC_RIGHTS_ALL or STANDARD_RIGHTS_ALL;
1013 ea.grfAccessMode := SET_ACCESS;
1014 ea.grfInheritance := NO_INHERITANCE;
1015 ea.Trustee.TrusteeForm := TRUSTEE_IS_SID;
1016 ea.Trustee.TrusteeType := TRUSTEE_IS_WELL_KNOWN_GROUP;
1017 ea.Trustee.ptstrName := PChar(everyone_sid);
Roger Meier3bef8c22012-10-06 06:58:00 +00001018
Jens Geyerb64a7742013-01-23 20:58:47 +01001019 acl := nil;
1020 SetEntriesInAcl( 1, @ea, nil, acl);
Roger Meier3bef8c22012-10-06 06:58:00 +00001021
Jens Geyerb64a7742013-01-23 20:58:47 +01001022 sd := PSECURITY_DESCRIPTOR( LocalAlloc( LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH));
1023 Win32Check( InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION));
1024 Win32Check( SetSecurityDescriptorDacl( sd, TRUE, acl, FALSE));
Roger Meier3bef8c22012-10-06 06:58:00 +00001025
Jens Geyerb64a7742013-01-23 20:58:47 +01001026 sa.nLength := SizeOf(sa);
1027 sa.lpSecurityDescriptor := sd;
1028 sa.bInheritHandle := FALSE;
Roger Meier3bef8c22012-10-06 06:58:00 +00001029
Jens Geyerb64a7742013-01-23 20:58:47 +01001030 // Create an instance of the named pipe
Jens Geyer9f7f11e2016-04-14 21:37:11 +02001031 {$IFDEF OLD_UNIT_NAMES}
1032 result := Windows.CreateNamedPipe(
1033 {$ELSE}
1034 result := Winapi.Windows.CreateNamedPipe(
1035 {$ENDIF}
1036 PChar( FPipeName), // pipe name
1037 PIPE_ACCESS_DUPLEX or // read/write access
1038 FILE_FLAG_OVERLAPPED, // async mode
1039 PIPE_TYPE_BYTE or // byte type pipe
1040 PIPE_READMODE_BYTE, // byte read mode
1041 FMaxConns, // max. instances
1042 FBufSize, // output buffer size
1043 FBufSize, // input buffer size
1044 FTimeout, // time-out, see MSDN
1045 @sa // default security attribute
1046 );
Roger Meier3bef8c22012-10-06 06:58:00 +00001047
Jens Geyer06045cf2013-03-27 20:26:25 +02001048 if( result <> INVALID_HANDLE_VALUE)
1049 then InterlockedExchangePointer( Pointer(FHandle), Pointer(result))
Jens Geyere0e32402016-04-20 21:50:48 +02001050 else raise TTransportExceptionNotOpen.Create('CreateNamedPipe() failed ' + IntToStr(GetLastError));
Jens Geyerb64a7742013-01-23 20:58:47 +01001051
1052 finally
1053 if sd <> nil then LocalFree( Cardinal( sd));
1054 if acl <> nil then LocalFree( Cardinal( acl));
1055 if everyone_sid <> nil then FreeSid(everyone_sid);
Roger Meier3bef8c22012-10-06 06:58:00 +00001056 end;
Roger Meier3bef8c22012-10-06 06:58:00 +00001057end;
1058
1059
Roger Meier3bef8c22012-10-06 06:58:00 +00001060
1061end.
1062
1063
1064