I have a very particular problem that I have not been able to find on the Internet.
In my company, we have an application developed with Delphi 7 using Indy 9, but it has been decided once and for all to migrate to the Delphi 10.2 Tokyo. This has created a workload that is too high, since the program handles more than 52,000 lines of code and I have had to face issue with migrating to Unicode and Indy 10.
I need help knowing how to replace this:
Indy 9:
procedure TTraceForm.IdTCPServer1Disconnect (AThread: TIdPeerThread);
begin
try
AThread.Terminate;
if (AThread.ReturnValue >= 1) and (AThread.ReturnValue <= MaxCtrlTrns) then
try
QueueBlock.Enter;
TCPPeerThreads[AThread.ReturnValue] := Nil;
finally
QueueBlock.Leave;
end;
except
on E: Exception do
begin
WriteLogSwitch('E' , 'Error TTraceForm.IdTCPServer1Disconnect (' + E. Message + ')');
end;
end;
end;
To this in Indy 10:
procedure TTraceForm.IdTCPServer1Disconnect (AThread: TIdContext);
begin
try
AThread.Connection.Disconnect;
if (AThread.ReturnValue >= 1) and (AThread.ReturnValue <= MaxCtrlTrns) then
try
QueueBlock.Enter;
TCPPeerContext[AThread.ReturnValue] := Nil;
finally
QueueBlock.Leave;
end;
except
on E: Exception do
begin
WriteLogSwitch('E' , 'Error TTraceForm.IdTCPServer1Disconnect (' + E. Message + ')');
end;
end;
end;
In TIdContext
, there is no ReturnValue
, and I do not know how to replace it.
In Indy 9, TIdPeerThread
is a TThread
descendant. ReturnValue
is a property of TThread
.
In Indy 10, there was effort made to separate business logic from threading. As such, TIdContext
is not a TThread
descendant. But it is linked to a TThread
, via TIdYarn
. So, if you have to, you can access the underlying TThread
by type-casting the TIdContext.Yarn
property to TIdYarnOfThread
and then accessing the TIdYarnOfThread.Thread
property, eg:
procedure TTraceForm.IdTCPServer1Connect (AContext: TIdContext);
var
MyValue: Integer;
begin
...
MyValue := ...;
TIdYarnOfThread(AContext.Yarn).Thread.ReturnValue := MyValue;
if (MyValue >= 1) and (MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[MyValue] := AContext;
finally
QueueBlock.Leave;
end;
end;
...
end;
procedure TTraceForm.IdTCPServer1Disconnect (AContext: TIdContext);
var
MyValue: Integer;
begin
try
AContext.Connection.Disconnect;
MyValue := TIdYarnOfThread(AContext.Yarn).Thread.ReturnValue;
if (MyValue >= 1) and (MyValue <= MaxCtrlTrns) then
try
QueueBlock.Enter;
TCPPeerThreads[MyValue] := Nil;
finally
QueueBlock.Leave;
end;
except
on E: Exception do
begin
WriteLogSwitch('E' , 'Error TTraceForm.IdTCPServer1Disconnect (' + E. Message + ')');
end;
end;
end;
However, TThread.ReturnValue
only really has meaning to the TThread.WaitFor()
method, as it returns the ReturnValue
. And since you don't WaitFor()
the server's threads, you really shouldn't be using the ReturnValue
the way you are at all.
Indy 9's TIdPeerThread
and Indy 10's TIdContext
both have a public Data
property, you can use that instead to store user-defined values, that is what it is meant for (note: if you use Indy 10 in a Delphi ARC-enabled compiler - Android, iOS, Linux, etc - you will have to use the TIdContext.DataValue
property instead).
And FYI, there is no reason whatsoever to call AThread.Terminate
or AContext.Connection.Disconnect
in the TIdTCPServer.OnDisconnect
event. The thread that manages the socket will be stopped automatically after the event handler exits, and the socket will be closed if it isn't already closed.
Try something more like this instead:
Indy 9:
procedure TTraceForm.IdTCPServer1Connect (AThread: TIdPeerThread);
var
MyValue: Integer;
begin
...
MyValue := ...;
AThread.Data := TObject(MyValue);
if (MyValue >= 1) and (MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[MyValue] := AThread;
finally
QueueBlock.Leave;
end;
end;
...
end;
procedure TTraceForm.IdTCPServer1Disconnect (AThread: TIdPeerThread);
var
MyValue: Integer;
begin
try
MyValue := Integer(AThread.Data);
if (MyValue >= 1) and (MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[MyValue] := Nil;
finally
QueueBlock.Leave;
end;
end;
except
on E: Exception do
begin
WriteLogSwitch('E' , 'Error TTraceForm.IdTCPServer1Disconnect (' + E. Message + ')');
end;
end;
end;
Indy 10:
procedure TTraceForm.IdTCPServer1Connect (AContext: TIdContext);
var
MyValue: Integer;
begin
...
MyValue := ...;
AContext.Data := TObject(MyValue); // or 'AContext.DataValue := MyValue;' on ARC
if (MyValue >= 1) and (MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[MyValue] := AContext;
finally
QueueBlock.Leave;
end;
end;
...
end;
procedure TTraceForm.IdTCPServer1Disconnect (AContext: TIdContext);
var
MyValue: Integer;
begin
try
MyValue := Integer(AContext.Data); // or 'MyValue := AContext.DataValue;' on ARC
if (MyValue >= 1) and (MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[MyValue] := Nil;
finally
QueueBlock.Leave;
end;
end;
except
on E: Exception do
begin
WriteLogSwitch('E' , 'Error TTraceForm.IdTCPServer1Disconnect (' + E. Message + ')');
end;
end;
end;
That being said, there is an alternative solution - derive a new class from TIdPeerThread
/TIdContext
and add your own custom members to it as needed, and then assign that class to the server's ThreadClass
/ContextClass
property before activating the server. You can then type-cast the provided AThread
/AContext
object in the server events to your class when you need to access your members, eg:
Indy 9:
type
TMyPeerThread = class(TIdPeerThread)
MyValue: Integer;
end;
procedure TTraceForm.FormCreate (Sender: TObject);
begin
...
IdTCPServer1.ThreadClass := TMyPeerThread;
IdTCPServer1.Active := True;
...
end;
procedure TTraceForm.IdTCPServer1Connect (AThread: TIdPeerThread);
var
LThread: TMyPeerThread;
begin
...
LThread := TMyPeerThread(AThread);
LThread.MyValue := ...;
if (LThread.MyValue >= 1) and (LThread.MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[LThread.MyValue] := AThread;
finally
QueueBlock.Leave;
end;
end;
...
end;
procedure TTraceForm.IdTCPServer1Disconnect (AThread: TIdPeerThread);
var
LThread: TMyPeerThread;
begin
try
LThread := TMyPeerThread(AThread);
if (LThread.MyValue >= 1) and (LThread.MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[LThread.MyValue] := Nil;
finally
QueueBlock.Leave;
end;
end;
except
on E: Exception do
begin
WriteLogSwitch('E' , 'Error TTraceForm.IdTCPServer1Disconnect (' + E. Message + ')');
end;
end;
end;
Indy 10:
type
TMyContext = class(TIdServerContext)
MyValue: Integer;
end;
procedure TTraceForm.FormCreate (Sender: TObject);
begin
...
IdTCPServer1.ContextClass := TMyContext;
IdTCPServer1.Active := True;
...
end;
procedure TTraceForm.IdTCPServer1Connect (AContext: TMyContext);
var
LContext: TMyContext;
begin
...
LContext := TMyContext(AContext);
TMyContext.MyValue := ...;
if (LContext.MyValue >= 1) and (LContext.MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[LContext.MyValue] := AContext;
finally
QueueBlock.Leave;
end;
end;
...
end;
procedure TTraceForm.IdTCPServer1Disconnect (AContext: TIdContext);
var
LContext: TMyContext;
begin
try
LContext := TMyContext(AContext);
if (LContext.MyValue >= 1) and (LContext.MyValue <= MaxCtrlTrns) then
begin
QueueBlock.Enter;
try
TCPPeerThreads[LContext.MyValue] := Nil;
finally
QueueBlock.Leave;
end;
end;
except
on E: Exception do
begin
WriteLogSwitch('E' , 'Error TTraceForm.IdTCPServer1Disconnect (' + E. Message + ')');
end;
end;
end;