Индикатор выполнения равен 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;
}
Другие вопросы по тегам