Componentes.Terceros.RemObj.../internal/5.0.24.615/1/RemObjects SDK for Delphi/Source/uRONamedPipeServer.pas

487 lines
14 KiB
ObjectPascal

unit uRONamedPipeServer;
{----------------------------------------------------------------------------}
{ RemObjects SDK Library - Enterprise Library }
{ }
{ compiler: Delphi 5 and up }
{ platform: Win32 }
{ }
{ (c)opyright RemObjects Software. all rights reserved. }
{ }
{ Provided by Robert Love (robert.love@teamro.remobjects.com) }
{ }
{ Using this code requires a valid license of the RemObjects SDK }
{ which can be obtained at http://www.remobjects.com. }
{----------------------------------------------------------------------------}
{$I RemObjects.inc}
//TODO: Add OnFatalException Handler to send exception to main thread to be raised.
interface
{$WARN SYMBOL_DEPRECATED OFF} // This unit is only for Win32
uses
{$IFDEF REMOBJECTS_TRIAL}uROTrial,{$ENDIF}
Sysutils,Classes, Windows, Messages, uROClientIntf, uROServer, uROClasses, uROThread;
const
PIPE_TIMEOUT = 1000;
BUFFER_SIZE = 4096;
type
TRONamedPipeServer = class;
TROPipeListenerThread = class(TThread)
private
{ Private declarations }
fPipename : string;
FList : TThreadList;
FServer : TRONamedPipeServer;
FFatalError: TObject;
hPipe : THandle;
procedure ConnectOnTerminate(Sender : TObject);
function GetPipename: String;
procedure SetPipeName(const Value: String);
procedure RaiseException;
procedure EmulateConnect;
protected
procedure Execute; override;
property PipeName : String read GetPipename write SetPipeName;
property ConnectList : TThreadList read FList;
property Server : TRONamedPipeServer read FServer Write FServer;
property FatalError : TObject read FFatalError write FFatalError;
end;
TROPipeConnectionThread = class(TROThread)
private
FPipeHandle: THandle;
fTransport : IROTransport;
FServer : TRONamedPipeServer;
FFatalError: TObject;
procedure Disconnect;
procedure RaiseException;
protected
procedure Execute; override;
property PipeHandle : THandle read FPipeHandle write FPipeHandle;
property Transport : IROTransport read FTransport write FTransport;
property Server : TRONamedPipeServer read FServer Write FServer;
property FatalError : TObject read FFatalError write FFatalError;
public
constructor Create(aCreateSuspended:Boolean);
end;
TRONamedPipeServer = class(TROServer, IROTransport)
private
fServerID: string;
fTransport : IROTransport;
FActive : Boolean;
FListener : TROPipeListenerThread;
FAllowEveryone : Boolean;
protected
{ Internals }
procedure IntSetActive(const Value: boolean); override;
function IntGetActive : boolean; override;
procedure ActivateServer; virtual;
property Listener: TROPipeListenerThread read FListener;
{ IROTransport }
function GetTransportObject : TObject;
public
constructor Create(aOwner : TComponent); override;
destructor Destroy; override;
procedure CheckProperties; override;
published
property ServerID : string read fServerID write fServerID;
property AllowEveryone : Boolean read FAllowEveryone write FAllowEveryone default false;
end;
implementation
procedure TROPipeListenerThread.ConnectOnTerminate(Sender: TObject);
var
LL : TList;
id : Integer;
begin
if Terminated then Exit;
LL := FList.LockList;
try
id := LL.IndexOf(sender);
if id >= 0 then
LL.Delete(id);
finally
FList.UnlockList;
end;
end;
procedure TROPipeListenerThread.EmulateConnect;
var
i: cardinal;
begin
if hPipe <> INVALID_HANDLE_VALUE then
CallNamedPipe(pchar(fPipeName), nil, 0, nil, 0, i, 10);
end;
{$IFDEF FPC}
const
PIPE_UNLIMITED_INSTANCES = 255;
{$ENDIF}
procedure TROPipeListenerThread.Execute;
var
fconnected : bool;
PC : TROPipeConnectionThread;
LL : TList;
I : Integer;
lSecurityDescriptor : _SECURITY_DESCRIPTOR;
lSecurityAttribute : SECURITY_ATTRIBUTES;
begin
hPipe := INVALID_HANDLE_VALUE;
try
if FServer.AllowEveryone then begin
FillChar(lSecurityAttribute, sizeof(lSecurityAttribute), 0);
InitializeSecurityDescriptor(@lSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@lSecurityDescriptor, true, nil, False);
lSecurityAttribute.nLength := sizeof(lSecurityAttribute);
lSecurityAttribute.bInheritHandle := True;
lSecurityAttribute.lpSecurityDescriptor := @lSecurityDescriptor;
end;
FList := TThreadList.Create;
try
While not Terminated do
begin
if FServer.AllowEveryone then begin
hPipe := CreateNamedPipe(
pchar(fPipeName), // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE or // message type pipe
PIPE_READMODE_BYTE or // byte-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFFER_SIZE, // output buffer size
BUFFER_SIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
@lSecurityAttribute);
end else begin
hPipe := CreateNamedPipe(
pchar(fPipeName), // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE or // message type pipe
PIPE_READMODE_BYTE or // byte-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFFER_SIZE, // output buffer size
BUFFER_SIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
nil); // no security attribute
end;
if (hPipe = INVALID_HANDLE_VALUE) then
RaiseLastOSError;
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function returns
// false, GetLastError should returns ERROR_PIPE_CONNECTED.
fConnected := ConnectNamedPipe(hPipe, nil);
if not fConnected then
fConnected := (GetLastError = ERROR_PIPE_CONNECTED);
if fConnected and not Terminated then
begin
PC := TROPipeConnectionThread.create(true);
PC.PipeHandle := hPipe;
PC.Server := FServer;
PC.FreeOnTerminate := True;
PC.Resume;
end
else
begin
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
hPipe := INVALID_HANDLE_VALUE;
end;
end;
finally
LL := FList.LockList;
try
// Terminate all client connections because if here then this thread is terminating
for I := 0 to LL.Count -1 do
TROPipeConnectionThread(LL.Items[I]).Terminate;
finally
FList.UnlockList;
end;
FList.Free;
end;
except
on e : TObject do
begin
FatalError := E;
// Make sure exception is raised in main thread.
Synchronize(RaiseException);
end;
end; // except
end;
function TROPipeListenerThread.GetPipename: String;
begin
result := string(fPipeName);
end;
procedure TROPipeListenerThread.RaiseException;
begin
raise FatalError;
end;
procedure TROPipeListenerThread.SetPipeName(const Value: String);
begin
fPipeName := Value;
end;
{ TROPipeConnectionThread }
constructor TROPipeConnectionThread.Create(aCreateSuspended:Boolean);
begin
inherited Create(aCreateSuspended,'TROPipeConnectionThread');
end;
{
function TROPipeConnectionThread.DataWaiting: Boolean;
var
fSuccess : BOOL;
BytesAvail : Integer;
begin
FSuccess := PeekNamedPipe(FPipeHandle,nil,0,nil,@BytesAvail,nil);
if not FSuccess then
RaiseLastWin32Error;
result := (BytesAvail >0);
end;
}
procedure TROPipeConnectionThread.Disconnect;
begin
// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance.
FlushFileBuffers(FPipeHandle);
DisconnectNamedPipe(FPipeHandle);
CloseHandle(FPipeHandle);
end;
procedure TROPipeConnectionThread.Execute;
var
ReadStream,
WriteStream : TMemoryStream;
fBytesRead,fBytesWritten : DWORD;
fSuccess : BOOL;
fDataSize: integer;
fBuf: PChar;
begin
GetMem(fBuf, BUFFER_SIZE);
readStream := TMemoryStream.Create;
writeStream := nil;
try
try
While Not Terminated do
begin
// Read incomming Data
fSuccess := ReadFile(fPipeHandle, fDataSize, sizeof(fDataSize), fBytesRead, nil);
if Not fSuccess then
if GetLastError = ERROR_BROKEN_PIPE then
// The pipe has been ended
break
else
RaiseLastOSError;
while fDataSize > 0 do
begin
fSuccess := ReadFile(fPipeHandle, fBuf^, BUFFER_SIZE, fBytesRead, nil);
if (not fSuccess) and (GetLastError <> ERROR_MORE_DATA) then
RaiseLastOSError;
if fBytesRead > 0 then
ReadStream.Write(fBuf^, fBytesRead);
dec(fDataSize, fBytesRead);
end;
if not FSuccess then
RaiseLastOSError;
if ReadStream.Size > 0 then
begin
writeStream := TMemoryStream.Create;
ReadStream.Position := 0;
FServer.DispatchMessage(fServer.FTransport, ReadStream, writeStream);
ReadStream.Position := 0;
ReadStream.SetSize(0);
writeStream.Position := 0;
fDataSize := writeStream.Size;
fSuccess := WriteFile(fPipeHandle, fDataSize, sizeof(fDataSize), fBytesWritten, nil);
if not fSuccess then
RaiseLastOSError;
while fDataSize > 0 do
begin
fBytesRead := writeStream.Read(fBuf^, BUFFER_SIZE);
dec(fDataSize, fBytesRead);
fSuccess := WriteFile(fPipeHandle, fBuf^, fBytesRead, fBytesWritten, nil);
if not FSuccess then
RaiseLastOSError;
end;
end;
end;
except
on e: Exception do
begin
// Only expected message is a broken pipe and it should be ignored.
if not ((e is EWin32Error)
and ((EWin32Error(E).ErrorCode = ERROR_BROKEN_PIPE)
{or (EWin32Error(E).ErrorCode = ERROR_NO_DATA)})) then
begin
FatalError := E;
// Make sure exception is raised in main thread.
Synchronize(RaiseException);
end;
end;
end;
finally
FreeMem(fBuf);
ReadStream.Free;
WriteStream.Free;
Disconnect;
if FServer.FListener <> nil then fServer.FListener.ConnectOnTerminate(self);
end;
(*
// Read incomming Data
// Empty incomming buffer into Stream
FSuccess := True;
while DataWaiting do
begin
fSuccess := ReadFile(fPipeHandle,ReadBuf,BUFFER_SIZE,fBytesRead,nil);
if (Not fSuccess) and (GetLastError <> ERROR_MORE_DATA) then
Break;
if (FBytesRead > 0) then
ReadStream.WriteBuffer(ReadBuf[0],fBytesRead);
if fSuccess then break;
end;
if Not FSuccess then
RaiseLastWIn32Error;
fIncommingData := (FSuccess and (ReadStream.Size > 0));
if FIncommingData then
begin
// Transport Data
WriteStream.Clear;
FServer.DispatchMessage(fServer.FTransport, Readstream, Writestream);
ReadStream.Clear;
// Write Contents from RO back out Pipe
fBytesRead := 1; //just to start loop
While FBytesRead > 0 do
begin
fBytesRead := WriteStream.Read(WriteBuf,BUFFER_SIZE);
if FBytesRead > 0 then
begin
fSuccess := WriteFile(FPipeHandle,WriteBuf,fBytesRead,fBytesWritten,nil);
if (not FSuccess) then
RaiseLastWin32Error;
end;
end; // while FBytesRead > 0
end; // if FIncommingData
end; // while not terminated
finally
Disconnect;
ReadStream.Free;
WriteStream.Free;
end;
except
on e: Exception do
begin
// Only expected message is a broken pipe and it should be ignored.
if not ((e is EWin32Error) and (EWin32Error(E).ErrorCode = ERROR_BROKEN_PIPE)) then
begin
FatalError := E;
// Make sure exception is raised in main thread.
Synchronize(RaiseException);
end;
end;
end; //except
*)
end;
procedure TROPipeConnectionThread.RaiseException;
begin
raise FatalError;
end;
{ TRONamedPipeServer }
procedure TRONamedPipeServer.ActivateServer;
begin
if FActive then
begin
CheckProperties;
FListener := TROPipeListenerThread.create(true);
FListener.PipeName := '\\.\pipe\'+ ServerID;
FListener.FreeOnTerminate := True;
FListener.Server := Self;
FListener.Resume;
end
else begin
FListener.Terminate;
FListener.EmulateConnect;
end;
end;
procedure TRONamedPipeServer.CheckProperties;
begin
Check(ServerID = '', Name+'.ServerID must be set.');
inherited;
end;
constructor TRONamedPipeServer.Create(aOwner: TComponent);
begin
inherited;
Supports(Self, IROTransport, fTransport);
if Assigned(Owner) then
fServerID := Owner.Name + '_RONamedPipeServer'
else
fServerID := 'RONamedPipeServer'
end;
destructor TRONamedPipeServer.Destroy;
begin
Active := False;
inherited;
end;
function TRONamedPipeServer.GetTransportObject: TObject;
begin
result := Self;
end;
function TRONamedPipeServer.IntGetActive: boolean;
begin
result := Factive;
end;
procedure TRONamedPipeServer.IntSetActive(const Value: boolean);
begin
if Value <> FActive then
begin
FActive := Value;
if not (csDesigning in ComponentState) then
ActivateServer;
end;
end;
initialization
RegisterServerClass(TRONamedPipeServer);
finalization
UnregisterServerClass(TRONamedPipeServer);
end.