Индикатор выполнения равен NULL после выполнения потока
У меня есть форма с индикатором выполнения и кнопкой, которая загружает XML на сервер. Пока кнопка нажата, создается новый поток, который создает сокет, а затем отправляет данные на сервер порциями, а тем временем обновляет индикатор выполнения. Теперь, когда кнопка загрузки нажата во второй раз, я получаю нарушение прав доступа, и в отладчике адрес объекта Progress Bar равен NULL. Я не могу понять, почему индикатор прогресса освобождается, поэтому, если у кого-то есть идея, я был бы благодарен.
PS Целевая ОС - Windows PS2 Если тот же код выполняется в основном потоке без использования потока, то у меня, похоже, нет этой проблемы, и даже если я пропускаю использование индикатора выполнения в целом в потоке, это установите значение null снова после первого нажатия кнопки загрузки.
Конструктор потока:
__fastcall UploadRouteThread::UploadRouteThread(bool CreateSuspended) : TThread(CreateSuspended)
{
this->OnTerminate = OnTerminateHandler;
ioHandlerStack = new TIdIOHandlerStack();
tcpClient = new TIdTCPClient();
tcpClient->ReadTimeout = -1;
tcpClient->UseNagle = true;
tcpClient->IOHandler = ioHandlerStack;
tcpClient->OnConnected = OnConnectedHandler;
}
Обработчик OnTerminate:
void __fastcall UploadRouteThread::OnTerminateHandler(TObject *Sender)
{
TabbedwithNavigationForm->UploadButton->Text = "Upload";
TabbedwithNavigationForm->UploadButton->Enabled = false;
TabbedwithNavigationForm->ProgressBar->Visible = false;
tcpClient->DisconnectNotifyPeer();
ShowMessage("Data uploaded.");
delete ioHandlerStack;
delete tcpClient;
TabbedwithNavigationForm->OptionButton->Enabled = true;
TabbedwithNavigationForm->RetrieveRoutesButton->Enabled = true;
TabbedwithNavigationForm->TrackButton->Enabled = true;
TabbedwithNavigationForm->MediaButton->Enabled = true;
}
Метод Execute:
void __fastcall UploadRouteThread::Execute()
{
FreeOnTerminate = true;
tcpClient->Connect();
}
Две дополнительные функции:
void __fastcall UploadRouteThread::SetHostPort(UnicodeString host, unsigned short port)
{
tcpClient->Host = host;
tcpClient->Port = port;
}
void __fastcall UploadRouteThread::SetXML(AnsiString xmlString)
{
this->xmlString = xmlString;
}
Обработчик OnConnect:
void __fastcall UploadRouteThread::OnConnectedHandler(TObject *Sender)
{
NextPacketSize nps;
TIdBytes bytes;
int chunks;
int bytesLength;
nps.PacketID = BasicPacket::DATA_UPLOAD;
nps.size = xmlString.Length();
tcpClient->IOHandler->WriteDirect(RawToBytes(&nps, sizeof(nps)), sizeof(NextPacketSize));
bytes = RawToBytes(xmlString.c_str(), xmlString.Length());
bytesLength = bytes.get_length();
chunks = ceil(float(bytesLength) / 256.0);
int previousSizeSent(0);
for(int i = 1; i <= chunks; i++)
{
if(Terminated)
break;
int bytesToSend = 256;
TByteDynArray byteDynArray;
if((bytesToSend > bytesLength))
{
bytesToSend = bytesLength;
}
byteDynArray = bytes.CopyRange(previousSizeSent, bytesToSend);
tcpClient->IOHandler->WriteDirect(ToBytes(byteDynArray, byteDynArray.get_length(), 0),
byteDynArray.get_length());
sent = (float(i) / float(chunks)) * 100;
TThread::Synchronize(this, UpdateProgressBarInternal);
previousSizeSent += bytesToSend;
bytesLength -= bytesToSend;
}
}
И метод обновления для индикатора выполнения:
void __fastcall UploadRouteThread::UpdateProgressBarInternal()
{
if(!TabbedwithNavigationForm->ProgressBar->Visible)
{
TabbedwithNavigationForm->ProgressBar->Visible = true;
TabbedwithNavigationForm->ProgressBar->Max = 100;
}
TabbedwithNavigationForm->ProgressBar->Value = sent;
}
1 ответ
Я не вижу ничего в этом коде, что бы указатель ProgressBar стал NULL
, Так что либо вы повреждаете память, либо что-то еще в другом коде, не показанном здесь, является виновником. В любом случае, для устранения этой проблемы вы можете запустить свое приложение в отладчике IDE и установить точку останова данных в переменной ProgressBar, прежде чем запускать свой поток в первый раз. Если что-то изменит значение этого указателя, будет достигнута точка останова, и вы сможете посмотреть на стек вызовов, чтобы выяснить, что происходит.
С учетом сказанного, ваша тема не очень хорошо организована. И есть намного более простой способ справиться с порцией - пусть Indy сделает это за вас. Имеет OnWork
событие, которое вы можете использовать для ваших обновлений ProgressBar.
Попробуйте что-то вроде этого:
__fastcall UploadRouteThread::UploadRouteThread(String host, TIdPort port, AnsiString xmlString)
: TThread(false)
{
this->FreeOnTerminate = true;
this->OnTerminate = OnTerminateHandler;
this->xmlString = xmlString;
tcpClient = new TIdTCPClient();
tcpClient->Host = host;
tcpClient->Port = port;
tcpClient->UseNagle = true;
tcpClient->OnWork = OnWorkHandler;
}
__fastcall UploadRouteThread::~UploadRouteThread()
{
delete tcpClient;
}
void __fastcall UploadRouteThread::OnTerminateHandler(TObject *Sender)
{
TabbedwithNavigationForm->UploadButton->Text = "Upload";
TabbedwithNavigationForm->UploadButton->Enabled = false;
TabbedwithNavigationForm->ProgressBar->Visible = false;
if (FatalException)
ShowMessage("Data not uploaded.");
else
ShowMessage("Data uploaded.");
TabbedwithNavigationForm->OptionButton->Enabled = true;
TabbedwithNavigationForm->RetrieveRoutesButton->Enabled = true;
TabbedwithNavigationForm->TrackButton->Enabled = true;
TabbedwithNavigationForm->MediaButton->Enabled = true;
}
void __fastcall UploadRouteThread::OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)
{
if (Terminated)
Sysutils::Abort();
sent = (double(AWorkCount) * 100.0) / xmlString.Length();
// consider using TThread::Queue() instead so that you don't block
// the upload waiting for the UI to be updated...
TThread::Synchronize(this, &UpdateProgressBarInternal);
}
void __fastcall UploadRouteThread::Execute()
{
tcpClient->Connect();
try
{
NextPacketSize nps;
nps.PacketID = BasicPacket::DATA_UPLOAD;
nps.size = xmlString.Length();
tcpClient->IOHandler->Write(RawToBytes(&nps, sizeof(nps)));
tcpClient->BeginWork(wmWrite, xmlString.Length());
tcpClient->IOHandler->Write(RawToBytes(xmlString.c_str(), xmlString.Length()));
tcpClient->EndWork(wmWrite);
/* alternatively:
TIdMemoryBufferStream *strm = new TIdMemoryBufferStream(xmlString.c_str(), xmlString.Length());
try
{
// optional
tcpClient->IOHandler->SendBufferSize = 256;
// this calls (Begin|End)Work() internally...
tcpClient->IOHandler->Write(strm, 0, false);
}
__finally
{
delete strm;
}
*/
}
__finally
{
tcpClient->Disconnect();
}
}
void __fastcall UploadRouteThread::UpdateProgressBarInternal()
{
if (!TabbedwithNavigationForm->ProgressBar->Visible)
{
TabbedwithNavigationForm->ProgressBar->Visible = true;
TabbedwithNavigationForm->ProgressBar->Max = 100;
}
TabbedwithNavigationForm->ProgressBar->Value = sent;
}