{ $Project$ $Workfile$ $Revision$ $DateUTC$ $Id$ This file is part of the Indy (Internet Direct) project, and is offered under the dual-licensing agreement described on the Indy website. (http://www.indyproject.org/) Copyright: (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. } { $Log$ } { Rev 1.6 9/16/2004 8:11:40 PM JPMugaas Should compile again. Rev 1.5 6/11/2004 8:39:58 AM DSiders Added "Do not Localize" comments. Rev 1.4 2004.05.06 1:47:26 PM czhower Now uses IndexOf Rev 1.3 2004.04.13 10:37:56 PM czhower Updates Rev 1.2 2004.03.07 11:46:08 AM czhower Flushbuffer fix + other minor ones found Rev 1.1 2004.02.09 9:16:44 PM czhower Updated to compile and match lib changes. Rev 1.0 2004.02.03 12:38:56 AM czhower Move Rev 1.6 2003.10.24 10:37:38 AM czhower IdStream Rev 1.5 2003.10.19 4:38:32 PM czhower Updates Rev 1.4 2003.10.19 2:50:40 PM czhower Fiber cleanup Rev 1.3 2003.10.14 11:17:02 PM czhower Updates to match core changes. Rev 1.2 2003.10.11 5:43:30 PM czhower Chained servers now functional. Rev 1.1 2003.09.19 10:09:40 PM czhower Next stage of fiber support in servers. Rev 1.0 8/16/2003 11:09:08 AM JPMugaas Moved from Indy Core dir as part of package reorg Rev 1.49 2003.07.17 4:42:06 PM czhower More IOCP improvements. Rev 1.45 2003.07.14 11:46:46 PM czhower IOCP now passes all bubbles. Rev 1.43 2003.07.14 1:10:52 AM czhower Now passes all bubble tests for chained stack. Rev 1.41 7/7/2003 1:34:06 PM BGooijen Added WriteFile(...) Rev 1.40 7/3/2003 2:03:52 PM BGooijen IOCP works server-side now Rev 1.39 2003.06.30 5:41:54 PM czhower -Fixed AV that occurred sometimes when sockets were closed with chains -Consolidated code that was marked by a todo for merging as it no longer needed to be separate -Removed some older code that was no longer necessary Passes bubble tests. Rev 1.38 6/29/2003 10:56:26 PM BGooijen Removed .Memory from the buffer, and added some extra methods Rev 1.37 2003.06.25 4:30:02 PM czhower Temp hack fix for AV problem. Working on real solution now. Rev 1.36 6/24/2003 11:17:44 PM BGooijen change in TIdIOHandlerChain.ReadLn, LTermPos= 0 is now handled differently Rev 1.35 23/6/2003 22:33:18 GGrieve fix CheckForDataOnSource - specify timeout Rev 1.34 6/22/2003 11:22:22 PM JPMugaas Should now compile. Rev 1.33 6/4/2003 1:08:40 AM BGooijen Added CheckForDataOnSource and removed some (duplicate) code Rev 1.32 6/3/2003 8:07:20 PM BGooijen Added TIdIOHandlerChain.AllData Rev 1.31 5/11/2003 2:37:58 PM BGooijen Bindings are updated now Rev 1.30 5/11/2003 12:00:08 PM BGooijen Rev 1.29 5/11/2003 12:03:16 AM BGooijen Rev 1.28 2003.05.09 10:59:24 PM czhower Rev 1.27 2003.04.22 9:48:50 PM czhower Rev 1.25 2003.04.17 11:01:14 PM czhower Rev 1.19 2003.04.10 10:51:04 PM czhower Rev 1.18 4/2/2003 3:39:26 PM BGooijen Added Intercepts Rev 1.17 3/29/2003 5:53:52 PM BGooijen added AfterAccept Rev 1.16 3/27/2003 2:57:58 PM BGooijen Added a RawWrite for streams, implemented WriteStream, changed WriteToDestination to use TIdWorkOpUnitWriteBuffer Rev 1.15 2003.03.26 12:20:28 AM czhower Moved visibility of execute to protected. Rev 1.14 3/25/2003 11:07:58 PM BGooijen ChainEngine descends now from TIdBaseComponent Rev 1.13 3/25/2003 01:33:48 AM JPMugaas Fixed compiler warnings. Rev 1.12 3/24/2003 11:03:50 PM BGooijen Various fixes to readln: - uses connection default now - doesn't raise an exception on timeout any more Rev 1.11 2003.03.13 1:22:58 PM czhower Typo fixed. lenth --> Length Rev 1.10 3/13/2003 10:18:20 AM BGooijen Server side fibers, bug fixes Rev 1.9 3/2/2003 12:36:22 AM BGooijen Added woReadBuffer and TIdWorkOpUnitReadBuffer to read a buffer. Now ReadBuffer doesn't use ReadStream any more. TIdIOHandlerChain.ReadLn now supports MaxLineLength (splitting, and exceptions). woReadLn doesn't check the intire buffer any more, but continued where it stopped the last time. Added basic support for timeouts (probably only on read operations, and maybe connect), accuratie of timeout is currently 500msec. Rev 1.8 2/28/2003 10:15:16 PM BGooijen bugfix: changed some occurrences of FRecvBuffer to FInputBuffer Rev 1.7 2/27/2003 10:11:12 PM BGooijen Rev 1.6 2/26/2003 1:08:52 PM BGooijen Rev 1.5 2/25/2003 10:36:28 PM BGooijen Added more opcodes, methods, and moved opcodes to separate files. Rev 1.4 2003.02.25 9:02:32 PM czhower Hand off to Bas Rev 1.3 2003.02.25 1:36:04 AM czhower Rev 1.2 2002.12.11 11:00:58 AM czhower Rev 1.1 2002.12.07 12:26:06 AM czhower Rev 1.0 11/13/2002 08:45:00 AM JPMugaas } unit IdIOHandlerChain; interface uses Classes, IdBaseComponent, IdBuffer, IdGlobal, IdIOHandler, IdIOHandlerSocket, IdFiber, IdThreadSafe, IdWorkOpUnit, IdStackConsts, IdWinsock2, IdThread, IdFiberWeaver, IdStream, IdStreamVCL, Windows; type TIdConnectMode = (cmNonBlock, cmIOCP); TIdIOHandlerChain = class; TIdChainEngineThread = class; TIdChainEngine = class(TIdBaseComponent) protected FCompletionPort: THandle; FThread: TIdChainEngineThread; // procedure Execute; function GetInputBuffer(const AIOHandler: TIdIOHandler): TIdBuffer; procedure InitComponent; override; procedure SetIOHandlerOptions(AIOHandler: TIdIOHandlerChain); procedure Terminating; public procedure AddWork(AWorkOpUnit: TIdWorkOpUnit); procedure BeforeDestruction; override; destructor Destroy; override; procedure RemoveSocket(AIOHandler: TIdIOHandlerChain); procedure SocketAccepted(AIOHandler: TIdIOHandlerChain); end; TIdIOHandlerChain = class(TIdIOHandlerSocket) protected FChainEngine: TIdChainEngine; FConnectMode: TIdConnectMode; FFiber: TIdFiber; FFiberWeaver: TIdFiberWeaver; FOverlapped: PIdOverlapped; // procedure ConnectClient; override; procedure QueueAndWait( AWorkOpUnit: TIdWorkOpUnit; ATimeout: Integer = IdTimeoutDefault; AFreeWorkOpUnit: Boolean = True; AAllowGracefulException: Boolean = True ); procedure WorkOpUnitCompleted( AWorkOpUnit: TIdWorkOpUnit ); public procedure AfterAccept; override; function AllData: string; override; procedure CheckForDataOnSource( ATimeout : Integer = 0 ); override; procedure CheckForDisconnect( ARaiseExceptionIfDisconnected: Boolean = True; AIgnoreBuffer: Boolean = False ); override; constructor Create( AOwner: TComponent; AChainEngine: TIdChainEngine; AFiberWeaver: TIdFiberWeaver; AFiber: TIdFiber ); reintroduce; virtual; destructor Destroy; override; procedure Open; override; function ReadFromSource(ARaiseExceptionIfDisconnected: Boolean = True; ATimeout: Integer = IdTimeoutDefault; ARaiseExceptionOnTimeout: Boolean = True): Integer; override; procedure ReadStream(AStream: TIdStreamVCL; AByteCount: Int64; AReadUntilDisconnect: Boolean); override; // TODO: Allow ReadBuffer to by pass the internal buffer. Will it really // help? Only ReadBuffer would be able to use this optimiztion in most // cases and it is not used by many. Most calls are to stream (disk) based // or strings as ReadLn. procedure ReadBytes(var VBuffer: TIdBytes; AByteCount: Integer; AAppend: Boolean = True); override; function ReadLn( ATerminator: string = LF; ATimeout: Integer = IdTimeoutDefault; AMaxLineLength: Integer = -1 ): string; override; // function WriteFile( // AFile: string; // AEnableTransferFile: Boolean // ): Cardinal; override; function WriteFile( const AFile: String; AEnableTransferFile: Boolean): Int64; override; { procedure Write( AStream: TIdStream; ASize: Integer = 0; AWriteByteCount: Boolean = False); override; } procedure Write( AStream: TIdStreamVCL; ASize: Int64 = 0; AWriteByteCount: Boolean = False ); override; procedure WriteDirect( ABuffer: TIdBytes ); override; // property ConnectMode: TIdConnectMode read FConnectMode write FConnectMode; property Overlapped: PIdOverlapped read FOverlapped; end; TIdChainEngineThread = class(TIdThread) protected FChainEngine: TIdChainEngine; public constructor Create( AOwner: TIdChainEngine; const AName: string ); reintroduce; procedure Run; override; property Terminated; end; implementation uses IdComponent, IdException, IdExceptionCore, IdStack, IdResourceStrings, IdWorkOpUnits, IdStackWindows, SysUtils; const GCompletionKeyTerminate = $F0F0F0F0; { TIdIOHandlerChain } procedure TIdIOHandlerChain.CheckForDataOnSource(ATimeout: Integer = 0); begin // TODO: Change this so we dont have to rely on an exception trap try QueueAndWait(TIdWorkOpUnitReadAvailable.Create, ATimeout, True, False); except on E: EIdReadTimeout do begin // Nothing end else begin raise; end; end; end; procedure TIdIOHandlerChain.ConnectClient; begin // TODO: Non blocking does not support Socks Binding.OverLapped := (ConnectMode = cmIOCP); inherited; case ConnectMode of cmNonBlock: begin //TODO: Non blocking DNS resolution too? Binding.SetPeer(GWindowsStack.ResolveHost(Host), Port); GWindowsStack.SetBlocking(Binding.Handle, False); // Does not block Binding.Connect; end; cmIOCP: begin //TODO: For now we are doing blocking, just to get it to work. fix later // IOCP was not designed for connects, so we'll have to do some monkeying // maybe even create an engine thread just to watch for connect events. //TODO: Resolution too? Binding.SetPeer(GStack.ResolveHost(Host), Port); Binding.Connect; GWindowsStack.SetBlocking(Binding.Handle, False); end; else begin raise EIdException.Create('Unrecognized ConnectMode'); {do not localize} end; end; QueueAndWait(TIdWorkOpUnitWaitConnected.Create); //Update the bindings Binding.UpdateBindingLocal; //TODO: Could Peer binding ever be other than what we specified above? Need to reread it? Binding.UpdateBindingPeer; end; procedure TIdIOHandlerChain.AfterAccept; begin FChainEngine.SocketAccepted(self); end; procedure TIdIOHandlerChain.Open; begin // Things before inherited, inherited actually connects and ConnectClient // needs these things inherited; end; procedure TIdIOHandlerChain.CheckForDisconnect( ARaiseExceptionIfDisconnected: Boolean; AIgnoreBuffer: Boolean); var LDisconnected: Boolean; begin // ClosedGracefully // Server disconnected // IOHandler = nil // Client disconnected if ClosedGracefully then begin if BindingAllocated then begin Close; // Call event handlers to inform the user program that we were disconnected // DoStatus(hsDisconnected); //DoOnDisconnected; end; LDisconnected := True; end else begin LDisconnected := not BindingAllocated; end; if LDisconnected then begin // Do not raise unless all data has been read by the user if Assigned(FInputBuffer) then begin if ((FInputBuffer.Size = 0) or AIgnoreBuffer) and ARaiseExceptionIfDisconnected then begin RaiseConnClosedGracefully; end; end; end; end; function TIdIOHandlerChain.ReadFromSource( ARaiseExceptionIfDisconnected: Boolean; ATimeout: Integer; ARaiseExceptionOnTimeout: Boolean): Integer; begin Result := 0; raise EIdException.Create('Fall through error in ' + ClassName); {do not localize} end; procedure TIdIOHandlerChain.ReadStream(AStream: TIdStreamVCL; AByteCount: Int64; AReadUntilDisconnect: Boolean); begin if AReadUntilDisconnect then begin QueueAndWait(TIdWorkOpUnitReadUntilDisconnect.Create(AStream.VCLStream), -1 , True, False); end else begin QueueAndWait(TIdWorkOpUnitReadSizedStream.Create(AStream.VCLStream, AByteCount)); end; end; procedure TIdIOHandlerChain.ReadBytes(var VBuffer: TIdBytes; AByteCount: Integer; AAppend: Boolean = True); begin EIdException.IfFalse(AByteCount >= 0); if AByteCount > 0 then begin if FInputBuffer.Size < AByteCount then begin QueueAndWait(TIdWorkOpUnitReadSized.Create(AByteCount- FInputBuffer.Size)); end; Assert(FInputBuffer.Size >= AByteCount); FInputBuffer.ExtractToBytes(VBuffer, AByteCount, AAppend); end; end; function TIdIOHandlerChain.ReadLn(ATerminator: string = LF; ATimeout: Integer = IdTimeoutDefault; AMaxLineLength: Integer = -1): string; var LTermPos: Integer; begin if AMaxLineLength = -1 then begin AMaxLineLength := MaxLineLength; end; // User may pass '' if they need to pass arguments beyond the first. if ATerminator = '' then begin ATerminator := LF; end; FReadLnSplit := False; FReadLnTimedOut := False; try LTermPos := FInputBuffer.IndexOf(ATerminator) + 1; if (LTermPos = 0) and ((AMaxLineLength = 0) or (FInputBuffer.Size < AMaxLineLength)) then begin QueueAndWait(TIdWorkOpUnitReadLn.Create(ATerminator, AMaxLineLength) , ATimeout); LTermPos := FInputBuffer.IndexOf(ATerminator) + 1; end; // LTermPos cannot be 0, and the code below can't handle it properly Assert(LTermPos > 0); if (AMaxLineLength <> 0) and (LTermPos > AMaxLineLength) then begin case FMaxLineAction of // TODO: find the right exception class here maException: raise EIdException.Create('MaxLineLength exceded'); {do not localize} maSplit: Result := FInputBuffer.Extract(AMaxLineLength); end; end else begin Result := FInputBuffer.Extract(LTermPos - 1); if (ATerminator = LF) and (Copy(Result, Length(Result), 1) = CR) then begin Delete(Result, Length(Result), 1); end; FInputBuffer.Extract(Length(ATerminator));// remove the terminator end; except on E: EIdReadTimeout do FReadLnTimedOut := True; end; end; function TIdIOHandlerChain.AllData: string; var LStream: TStringStream; begin BeginWork(wmRead); try Result := ''; LStream := TStringStream.Create(''); try QueueAndWait(TIdWorkOpUnitReadUntilDisconnect.Create(LStream), -1 , True, False); Result := LStream.DataString; finally FreeAndNil(LStream); end; finally EndWork(wmRead); end; end; function TIdIOHandlerChain.WriteFile( const AFile: String; AEnableTransferFile: Boolean): Int64; var LWO:TIdWorkOpUnitWriteFile; begin //BGO: we ignore AEnableTransferFile for now Result := 0; // if not Assigned(Intercept) then begin LWO := TIdWorkOpUnitWriteFile.Create(AFile); try QueueAndWait(LWO,IdTimeoutDefault, false); finally // Result := LWO.BytesSent; FreeAndNil(LWO); end; // end else begin // inherited WriteFile(AFile, AEnableTransferFile); // end; end; procedure TIdIOHandlerChain.Write( AStream: TIdStreamVCL; ASize: Int64 = 0; AWriteByteCount: Boolean = False ); var LStart: Integer; LThisSize: Integer; begin if ASize < 0 then begin //"-1" All form current position LStart := AStream.VCLStream.Seek(0, soFromCurrent); ASize := AStream.VCLStream.Seek(0, soFromEnd) - LStart; AStream.VCLStream.Seek(LStart, soFromBeginning); end else if ASize = 0 then begin //"0" ALL LStart := 0; ASize := AStream.VCLStream.Seek(0, soFromEnd); AStream.VCLStream.Seek(0, soFromBeginning); end else begin //else ">0" ACount bytes LStart := AStream.VCLStream.Seek(0, soFromCurrent); end; if AWriteByteCount then begin Write(ASize); end; // BeginWork(wmWrite, ASize); try while ASize > 0 do begin LThisSize := Min(128 * 1024, ASize); // 128K blocks QueueAndWait(TIdWorkOpUnitWriteStream.Create(AStream.VCLStream, LStart, LThisSize , False)); Dec(ASize, LThisSize); Inc(LStart, LThisSize); end; finally // EndWork(wmWrite); end; end; procedure TIdIOHandlerChain.WriteDirect( ABuffer: TIdBytes ); begin QueueAndWait(TIdWorkOpUnitWriteBuffer.Create(@ABuffer[0], Length(ABuffer), False)); end; procedure TIdIOHandlerChain.QueueAndWait( AWorkOpUnit: TIdWorkOpUnit; ATimeout: Integer = IdTimeoutDefault; AFreeWorkOpUnit: Boolean = True; AAllowGracefulException: Boolean = True ); var LWorkOpUnit: TIdWorkOpUnit; begin try CheckForDisconnect(AAllowGracefulException); LWorkOpUnit := AWorkOpUnit; // if ATimeout = IdTimeoutInfinite then begin LWorkOpUnit.TimeOutAt := 0; end else begin if ATimeout = IdTimeoutDefault then begin if FReadTimeout <= 0 then begin LWorkOpUnit.TimeOutAt := 0; end else begin //we type cast FReadTimeOut as a cardinal to prevent the compiler from //expanding vars to an Int64 type. That can incur a performance penalty. LWorkOpUnit.TimeOutAt := GetTickCount + Cardinal(FReadTimeout); end end else begin //FReadTimeOut is typecase as a cardinal to prevent the compiler from //expanding vars to an Int64 type which can incur a performance penalty. LWorkOpUnit.TimeOutAt := GetTickCount + Cardinal(ATimeout); end end; // LWorkOpUnit.Fiber := FFiber; LWorkOpUnit.IOHandler := Self; LWorkOpUnit.OnCompleted := WorkOpUnitCompleted; LWorkOpUnit.SocketHandle := Binding.Handle; // Add to queue and wait to be rescheduled when work is completed FChainEngine.AddWork(LWorkOpUnit); // Check to see if we need to reraise an exception LWorkOpUnit.RaiseException; // Check for timeout if LWorkOpUnit.TimedOut then begin raise EIdReadTimeout.Create('Timed out'); {do not localize} end; // Check to see if it was closed during this operation CheckForDisconnect(AAllowGracefulException); finally if AFreeWorkOpUnit then begin AWorkOpUnit.Free; end; end; end; constructor TIdIOHandlerChain.Create( AOwner: TComponent; AChainEngine: TIdChainEngine; AFiberWeaver: TIdFiberWeaver; AFiber: TIdFiber ); begin inherited Create(AOwner); // EIdException.IfNotAssigned(AChainEngine, 'No chain engine specified.'); {do not localize} FChainEngine := AChainEngine; FChainEngine.SetIOHandlerOptions(Self); // EIdException.IfNotAssigned(AFiberWeaver, 'No fiber weaver specified.'); {do not localize} FFiberWeaver := AFiberWeaver; // EIdException.IfNotAssigned(AFiber, 'No fiber specified.'); {do not localize} FFiber := AFiber; // Initialize Overlapped structure New(FOverlapped); ZeroMemory(FOverlapped, SizeOf(TIdOverLapped)); New(FOverlapped.Buffer); end; procedure TIdIOHandlerChain.WorkOpUnitCompleted(AWorkOpUnit: TIdWorkOpUnit); begin FFiberWeaver.Add(AWorkOpUnit.Fiber); end; destructor TIdIOHandlerChain.Destroy; begin // Tell the chain engine that we are closing and to remove any references to // us and cease any usage. // Do not do this in close, it can cause deadlocks because the engine can // call close while in its Execute. FChainEngine.RemoveSocket(Self); Dispose(FOverlapped.Buffer); Dispose(FOverlapped); inherited; end; { TIdChainEngine } procedure TIdChainEngine.BeforeDestruction; begin if FThread <> nil then begin // Signal thread for termination FThread.Terminate; // Tell the engine we are attempting termination Terminating; // Wait for the thread to terminate FThread.WaitFor; // Free thread FreeAndNil(FThread); end; inherited; end; function TIdChainEngine.GetInputBuffer(const AIOHandler:TIdIOHandler):TidBuffer; begin Result := TIdIOHandlerChain(AIOHandler).FInputBuffer; end; procedure TIdChainEngine.SetIOHandlerOptions(AIOHandler: TIdIOHandlerChain); begin AIOHandler.ConnectMode := cmIOCP; end; procedure TIdChainEngine.SocketAccepted(AIOHandler: TIdIOHandlerChain); begin // Associate the socket with the completion port. if CreateIoCompletionPort(AIOHandler.Binding.Handle, FCompletionPort, 0, 0) = 0 then begin RaiseLastOSError; end; end; procedure TIdChainEngine.Terminating; begin if not PostQueuedCompletionStatus(FCompletionPort, 0, GCompletionKeyTerminate , nil) then begin RaiseLastOSError; end; end; procedure TIdChainEngine.Execute; var LBytesTransferred: DWord; LCompletionKey: DWord; LOverlapped: PIdOverlapped; begin // Wait forever on the completion port. If we are terminating, a terminate // signal is sent into the queue. if GetQueuedCompletionStatus(FCompletionPort, LBytesTransferred , LCompletionKey, POverLapped(LOverlapped), INFINITE) then begin if LCompletionKey <> GCompletionKeyTerminate then begin // Socket has been closed if LBytesTransferred = 0 then begin LOverlapped.WorkOpUnit.IOHandler.CloseGracefully; end; LOverlapped.WorkOpUnit.Process(LOverlapped, LBytesTransferred); end; end; end; procedure TIdChainEngine.RemoveSocket(AIOHandler: TIdIOHandlerChain); begin // raise EIdException.Create('Fall through error in ' + Self.ClassName+'.RemoveSocket'); end; procedure TIdChainEngine.AddWork(AWorkOpUnit: TIdWorkOpUnit); begin if AWorkOpUnit is TIdWorkOpUnitWaitConnected then begin // Associate the socket with the completion port. if CreateIOCompletionPort(AWorkOpUnit.SocketHandle, FCompletionPort, 0, 0) = 0 then begin RaiseLastOSError; end; AWorkOpUnit.Complete; end; AWorkOpUnit.Start; end; destructor TIdChainEngine.Destroy; begin if CloseHandle(FCompletionPort) = False then begin RaiseLastOSError; end; inherited; end; procedure TIdChainEngine.InitComponent; begin { var SysInfo: TSystemInfo; GetSystemInfo(SysInfo); SysInfo.dwNumberOfProcessors Use GetSystemInfo instead. It will return the all info on the local system's architecture and will also return a valid ActiveProcessorMask which is a DWORD to be read as a bit array of the processor on the system... CZH> And next CZH> question - any one know off hand how to set affinity? :) Use the SetProcessAffinityMask or SetThreadAffinityMask API depending on wether you want to act on the whole process or just a single thread (SetThreadIdealProcessor is another way to do it: it just gives the scheduler a hint about where to run a thread without forcing it: good for keeping two threads doing IO one with each other on the same processor). } inherited; if not (csDesigning in ComponentState) then begin // Cant use .Name, its not initialized yet in Create FThread := TIdChainEngineThread.Create(Self, 'Chain Engine'); {do not localize} end; //MS says destruction is automatic, but Google seems to say that this initial //one is not auto managed as MS says, and that CloseHandle should be called. FCompletionPort := CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); if FCompletionPort = 0 then begin RaiseLastOSError; end; end; { TIdChainEngineThread } constructor TIdChainEngineThread.Create( AOwner: TIdChainEngine; const AName: string ); begin FChainEngine := AOwner; inherited Create(False, True, AName); end; (*procedure TIdChainEngineIOCP.TransmitFileIOCP(const AWorkOpUnit:TIdWorkOpUnitWriteFile;const AFilename:string); var LPOverlapped: PIdOverlapped; LHFile:THandle; begin New(LPOverlapped); ZeroMemory(LPOverlapped,sizeof(TIdOverLapped)); New(LPOverlapped^.Buffer); LPOverlapped^.IOhandler:=TIdIOHandlerChain(AWorkOpUnit.IOhandler); LPOverlapped^.WorkOpUnit:=AWorkOpUnit; LHFile:=CreateFile(pchar(AFilename),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0); if LHFile=INVALID_HANDLE_VALUE then begin RaiseLastOSError; end; try if ServiceQueryTransmitFile(AWorkOpUnit.IOHandler.Binding.Handle,LHFile,0,0,POverlapped(LPOverlapped),nil,0) then begin AWorkOpUnit.Fiber.Relinquish; end else begin raise EIdException.Create('error in ServiceQueryTransmitFile'); end; finally CloseHandle(LHFile); end; end; *) (*procedure TIdChainEngineIOCP.TransmitFileAsStream(const AWorkOpUnit:TIdWorkOpUnitWriteFile;const AFilename:string); procedure CopyWorkUnit(ASrc,ADst: TIdWorkOpUnit); begin ADst.IOHandler := ASrc.IOHandler; ADst.Fiber := ASrc.Fiber; ADst.OnCompleted := ASrc.OnCompleted; ADst.SocketHandle:= ASrc.SocketHandle; end; var LStream:TfileStream; LWorkOpUnit : TIdWorkOpUnitWriteStream; LBuf:pointer; LBufLen:integer; begin Assert(False, 'to do'); LStream := TFileStream.Create(AFilename,fmOpenRead or fmShareDenyWrite); try LWorkOpUnit := TIdWorkOpUnitWriteStream.Create(LStream,0,LStream.size,false); try CopyWorkUnit(AWorkOpUnit,LWorkOpUnit); LBufLen:=Min(LStream.size,128*1024); getmem(LBuf,LBufLen); LWorkOpUnit.Stream.Position:=LWorkOpUnit.StartPos; LWorkOpUnit.Stream.Read(LBuf^,LBufLen); IssueWriteBuffer(LWorkOpUnit,LBuf,LBufLen); finally AWorkOpUnit.BytesSent := LStream.Size; LWorkOpUnit.free; end; finally LStream.free; end; end; *) procedure TIdChainEngineThread.Run; begin FChainEngine.Execute; end; end.