Однако для некоторых сообщений событие OnExecute не вызывается, если курсор не перемещается в главном окне.
TIdTCPServer
- многопоточный компонент, OnExecute
событие запускается в рабочем потоке в непрерывном цикле в течение всего времени жизни соединения с сокетом.Таким образом, ЕДИНСТВЕННЫЙ способ блокирования до тех пор, пока не будет обнаружена активность мыши, - это если ваш код OnExecute
синхронизируется с основным потоком пользовательского интерфейса, а основной поток пользовательского интерфейса блокируется до получения сообщений окна.
Вкод, который вы показали, единственные места, где ваш код OnExecute
может быть заблокирован, это вызовы ReceiveBuffer()
, mProcess()
и SendBuffer()
.Убедитесь, что они все потокобезопасны.Вы не показывали код для любого из этих методов или код для основного потока пользовательского интерфейса, но mProcess()
вызывается через TThread::Synchronize()
, поэтому начните с этого и убедитесь, что ваш основной поток пользовательского интерфейса не блокирует mProcess()
пока он пытается обработать сообщение сокета.
Кстати, вы перехватываете только исключения на основе STL (полученные из std::exception
), но вы полностью игнорируете исключения на основе RTL (полученные из System::Sysutils::Exception
),И в случае основанных на Indy исключений (которые получены из EIdException
, что само по себе происходит из System::Sysutils::Exception
), НЕ глотайте их!Если вы перехватываете исключение Indy, перезапустите его и дайте TIdTCPServer
обработать его, иначе его потоки не смогут обнаружить разъединения сокетов и очистить их должным образом (если вы вручную не вызовете AContext->Connection->Disconnect()
в своем коде).
Не знаю версию Indy, что бы ни приходило с компилятором.
Вы можете узнать версию Indy:
ищем Indy в поле «About» среды IDE
, щелкнув правой кнопкой мыши любой компонент Indy в конструкторе форм во время разработки.
чтение свойства Version
любого компонента Indy во время выполнения.
ОБНОВЛЕНИЕ: Почему вы используете критический раздел вокруг всего?Тебе это не нужно.
Вы читаете / записываете сокет клиента только из одного потока (тот, который запускает событие OnExecute
).Даже если вы читаете в одном потоке и пишете в другом, это безопасно делать с сокетами, не устанавливая замок вокруг IOHandler.Таким образом, вам вообще не нужна блокировка этих операций IOHandler.
И ваш метод mProcess()
уже сериализован TThread::Synchronize()
, поэтому он будет работать только в основном потоке пользовательского интерфейса.Если несколько потоков клиентов хотят одновременно вызывать mProcess()
, Synchronize()
гарантирует, что он запускается только по одному за раз.Так что для этого вам тоже не нужен замок.Однако использование ShowMessage()
внутри mProcess()
проблематично, поскольку он запускает вторичный цикл обработки сообщений, который позволяет выполнять ожидающие запросы Synchronize()
, пока mProcess()
все еще работает, так что вы можете получить несколько mProcess()
звонки перекрывают друг друга.Вы не должны делать ничего внутри синхронизируемого метода, который может вызвать обработку сообщений окна.Если синхронизированный метод генерирует исключение, вы не должны пытаться его перехватить.Synchronize()
ловит исключения и перебрасывает их в контексте потока, который вызвал Synchronize()
, и у вас уже есть обработчики исключений в вашем OnExecute
коде.
Единственное место, где я вижу, где вы должны использовать любыевид блокировки, если он вообще есть, будет внутри setObjectCad()
, но только если ему необходим доступ к данным, к которым могут обращаться несколько потоков одновременно.
С учетом сказанного попробуйте что-то ещекак это вместо этого:
void ReceiveBuffer(TIdTCPClient * aClient, TIdBytes & ABuffer)
{
ReceiveBuffer(aClient->IOHandler, ABuffer);
}
bool ReceiveBuffer(TIdContext * AContext, TIdBytes & ABuffer)
{
ReceiveBuffer(AContext->Connection->IOHandler, ABuffer);
}
void ReceiveBuffer(TIdIOHandler * IO, TIdBytes & ABuffer)
{
long sz = IO->ReadLongInt();
IO->ReadBytes(ABuffer, sz, false);
}
void SendBuffer(TIdIOHandler * IO, const TIdBytes & ABuffer)
{
IO->WriteBufferOpen();
try
{
IO->Write(ABuffer.Length);
IO->Write(ABuffer);
IO->WriteBufferClose();
}
catch(const Exception &)
{
IO->WriteBufferCancel();
throw;
}
}
void SendBuffer(TIdContext * AContext, const TIdBytes & ABuffer)
{
SendBuffer(AContext->Connection->IOHandler, ABuffer);
}
void SendBuffer(TIdTCPClient * aClient, const TIdBytes & aBuffer)
{
SendBuffer(aClient->IOHandler, aBuffer);
}
void __fastcall TSigToolForm::IdTCPServer1Execute(TIdContext *AContext)
{
TIdBytes buffer;
ReceiveBuffer(AContext, buffer);
try
{
msg = &buffer[0]; // msg is class member
TThread::Synchronize(0, mProcess); // Doesn't return until mProcess finishes
buffer = IPOK().toByteArray(); // Ack
SendBuffer(AContext, buffer);
}
catch (const std::exception & ex)
{
buffer = IPFailCommand(ex.what()).toByteArray();
SendBuffer(AContext, buffer);
}
catch (const Exception & ex)
{
buffer = IPFailCommand(toStdString(ex.Message)).toByteArray();
SendBuffer(AContext, buffer);
if (dynamic_cast<const EIdException *>(&ex))
throw;
}
catch (...)
{
buffer = IPFailCommand("Unknown exception").toByteArray();
SendBuffer(AContext, buffer);
}
}
void __fastcall TSigToolForm::mProcess()
{
if (msg) processMessage(msg);
}
void TSigToolForm::processMessage(byte * message)
{
IPCommand cmd(message);
switch (cmd.ID)
{
case IPCommand::SET_CAD :
{
setObjectCad(cmd);
break;
}
}
}
void TSigToolForm::setObjectCad(const IPCommand &cmd)
{
// here is where you should be using CSLock, if at all...
}
void TForm16::setPortAndConnect()
{
IdTCPClient1->Port = bdePort->IntValue();
IdTCPClient1->Host = editHost->Text;
IdTCPClient1->Connect();
}
void TForm16::sendCommandToSVST(const TIdBytes & buffer)
{
setPortAndConnect();
try
{
// Send the command
SendBuffer(IdTCPClient1, buffer);
// Read the response
TIdBytes recv;
ReceiveBuffer(IdTCPClient1, recv);
IPCommand response = IPCommand::fromByteArray(recv);
}
__finally
{
IdTCPClient1->Disconnect();
}
}