0

プログレスバーとxmlをサーバーにアップロードするボタンを備えたフォームがあります。ボタンが押されている間、ソケットを作成する新しいスレッドが作成され、データがチャンクでサーバーに送信され、その間に進行状況バーが更新されます。ここで、アップロード ボタンをもう一度押すと、アクセス違反が発生し、デバッガーでプログレス バー オブジェクトのアドレスが NULL になります。プログレス バーが解放される理由がわかりません。

PS ターゲット OS は 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;
}

実行方法:

void __fastcall UploadRouteThread::Execute()
{
    FreeOnTerminate = true;
    tcpClient->Connect();
}

2 つの補助機能:

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;
    }
}

そして進行状況バーの Update メソッド:

void __fastcall UploadRouteThread::UpdateProgressBarInternal()
{
    if(!TabbedwithNavigationForm->ProgressBar->Visible)
    {
        TabbedwithNavigationForm->ProgressBar->Visible = true;
        TabbedwithNavigationForm->ProgressBar->Max = 100;
    }

    TabbedwithNavigationForm->ProgressBar->Value = sent;
}
4

1 に答える 1

1

このコードには、ProgressBar ポインターが になる原因となるものは何もありませんNULL。したがって、メモリを破損しているか、ここに示されていない他のコードの何かが原因です。いずれにせよ、これをトラブルシューティングするには、スレッドを初めて実行する前に、IDE デバッガーでアプリを実行し、ProgressBar 変数にデータ ブレークポイントを設定します。何かがそのポインターの値を変更すると、ブレークポイントがヒットし、コール スタックを調べて何が起こっているかを把握できます。

そうは言っても、あなたのスレッドはあまりよく整理されていません。チャンキングを処理するもっと簡単な方法があります - Indy に任せてください。OnWorkProgressBar の更新に使用できるイベントがあります。

このようなことをもっと試してください:

__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;
}
于 2016-01-13T01:14:19.727 に答える