I have the following code, where I can draw several rectangles and make a hole to each.
How can I send the RectList object over a socket (TServerSocket) and recover (receive in a TClientSocket) this object directly to a variable of same type (var RectList: TList<TRect>)?
type
TForm1 = class(TForm)
ComboBox1: TComboBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
Drawing: Boolean;
RectList: TList<TRect>;
Rectangle: TRect;
FormRegion, HoleRegion: HRGN;
function ClientToWindow(const P: TPoint): TPoint;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function TForm1.ClientToWindow(const P: TPoint): TPoint;
begin
Result := ClientToScreen(P);
Dec(Result.X, Left);
Dec(Result.Y, Top);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
RectList := TList<TRect>.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
RectList.Free;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Rectangle.Left := X;
Rectangle.Top := Y;
Drawing := True;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if Drawing then
begin
Rectangle.Right := X;
Rectangle.Bottom := Y;
Invalidate;
end;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
I: Integer;
begin
Drawing := false;
Rectangle.Right := X;
Rectangle.Bottom := Y;
Invalidate;
if RectList.Count < StrToInt(ComboBox1.Text) then
begin
Rectangle.NormalizeRect;
if not Rectangle.IsEmpty then
RectList.Add(Rectangle)
else
SetWindowRgn(Handle, 0, True);
end
else
begin
FormRegion := CreateRectRgn(0, 0, Width, Height);
for I := 0 to Pred(RectList.Count) do
begin
HoleRegion := CreateRectRgn(ClientToWindow(RectList.Items[I].TopLeft).X, ClientToWindow(RectList.Items[I].TopLeft).Y, ClientToWindow(RectList.Items[I].BottomRight).X, ClientToWindow(RectList.Items[I].BottomRight).Y);
CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
end;
SetWindowRgn(Handle, FormRegion, True);
RectList.Clear;
end;
end;
procedure TForm1.FormPaint(Sender: TObject);
var
R: TRect;
begin
Canvas.Brush.Style := bsClear;
Canvas.Pen.Style := psSolid;
Canvas.Pen.Color := clRed;
Canvas.Rectangle(Rectangle);
for R in RectList do
Canvas.Rectangle(R);
end;
end.
I made some code to show you how to do it.
In your code, I added a
TClientSocketon the form and assigned a few events. Also added aTButtonto send theRectListto the other side (server side) thru theTClientSocket.I designed a new simple server application having a
TServerSocketset to listen for client connection and acceptingcommandsfrom the client. I implemented two commands: rectangle and clear. Obviously clear command is implemented to clear the display on the rectangle list. The rectangle command is used to sent a rectangle (Left, top, right and bottom as coma delimited integers).Since client and server must understand each other, I designed a very simple communication protocol. Data is exchanged between client and server using ASCII lines. A line is any character collection terminated by a CRLF pair. TCP port 2500 (Almost any other would do) is used.
For example, the command
will sent a rectangle from client to server (The line above is terminated by CRLF).
If the server receive a valid command, it act on it and then send
The line above is terminated by CRLF. In case of an error, an error message is sent back to the client.
When a client establish a connection, the first thing the server does is to send a welcome banner. That is a line terminated by CRLF.
The client wait to receive the banner before sending any command. Then it send the clear command, wait for OK, then send a rectangle command with first item in
RectListand wait for OK, then loop sending all rectangle commands and waiting for OK acknowledge until allRectListhas been sent. The the client close the connection.I'm not completely correct when I say wait for. Actually the socket is event driven. That means everything is done thry events. For example, when a line comes in - sent by the other side - the socket triggers an OnRead event. In the corresponding event handler, you receive the line that is already received.
I used this line oriented protocol because it is really simple, easy to debug and cross platform. Actually, if looks much like the SMTP protocol which is used to send an email! Sending binary data is surely faster but has a lot of difficulties. Binary data format is compiler and platform specific. This result in difficulties. Binary data is diffcult to read for a human and so it is difficult to debug.
Below you'll find your enhanced source code and DFM (This is the client), then the server source code and DFM.
Client source code:
Client DFM:
Server source code:
Server DFM: