Сумасшедшее нарушение доступа в C ++ Builder 6 - PullRequest
1 голос
/ 05 декабря 2009

У меня есть следующий код, и я не могу понять, почему возникнет исключение нарушения прав доступа? Я даже удалил все файлы OBJ, TDS и т. Д. И поместил его в новый проект, однако нарушение прав доступа происходит.

По сути, этот код отображает TListView в TFrame и должен показывать различные текущие времена по всему миру для разных часовых поясов.

Примечание. Код находится в C ++ Builder 6.

Может кто-нибудь помочь?

BLOODY-HELL-UPDATE : Решаемые. Я не должен добавлять элементы в TListView в конструкторе TFrame. DUMB DUMB DUMB.

ОСНОВНОЕ ОБНОВЛЕНИЕ : Кажется, что когда UpdateTimes () вызывается через таймер, свойство "li-> Deleting" имеет значение TRUE. Когда вызывается вне таймера, это ЛОЖЬ. Теперь, почему для "li-> Удаление" установлено значение "истина", потому что он вызывается из таймера? Если я сделаю:

if(li->Deleting == false)
{
  li->Caption = "abcd";
}

Он не вводит if (), когда UpdateTimes () вызывается из таймера ...... argggggh !!!

ОБНОВЛЕНИЕ: Похоже, если я вызываю UpdateTimes () вне TTimer, он работает нормально. Но когда вызвано от таймера, это бросает Нарушение доступа. Что дает?

Заголовочный файл:

#ifndef CurrentTimes_FrameH
#define CurrentTimes_FrameH
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
#include <ComCtrls.hpp>
#include <list>
using namespace std;
//---------------------------------------------------------------------------
struct LOCATIONTIMEINFORMATION
{
  AnsiString TimeZoneName;
  AnsiString PlaceName;
  int    UtcOffsetMinutes;
  TListItem* ListItem;
};
//---------------------------------------------------------------------------
class TCurrentTimesFrame : public TFrame
{
__published:    // IDE-managed Components
    TTimer *Timer;
    TListView *ListView;
    void __fastcall TimerTimer(TObject *Sender);
private:    // User declarations
public:     // User declarations
    __fastcall TCurrentTimesFrame(TComponent* Owner);
//---------------------------------------------------------------------------
//User Code
//---------------------------------------------------------------------------
private:
    list<LOCATIONTIMEINFORMATION>   FTimeInformation;
  typedef list<LOCATIONTIMEINFORMATION>::iterator LocationTimeInformationItr;
public:
  void AddTimeInformation(LOCATIONTIMEINFORMATION lti);
  void UpdateTimes();
};
//---------------------------------------------------------------------------
#endif

Файл CPP:

#include <vcl.h>
#pragma hdrstop
#include "CurrentTimes_Frame.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
//---------------------------------------------------------------------------
__fastcall TCurrentTimesFrame::TCurrentTimesFrame(TComponent* Owner): TFrame(Owner)
{
  Timer->Enabled = false;
  <strike>{
    LOCATIONTIMEINFORMATION lti;
    lti.TimeZoneName = "UTC";
    lti.PlaceName = "Near Greenwich, England";
    lti.UtcOffsetMinutes = 0;
    AddTimeInformation(lti);
  }</strike>
  //UPADTED: Don't add TListItem from constructor 
}
//---------------------------------------------------------------------------
void TCurrentTimesFrame::AddTimeInformation(LOCATIONTIMEINFORMATION lti)
{
  TListItem* li = ListView->Items->Add();
  li->Caption = lti.TimeZoneName;
  li->SubItems->Add(lti.PlaceName);
  li->SubItems->Add(lti.UtcOffsetMinutes);
  li->SubItems->Add("<time will come here>");
  lti.ListItem = li;
  ShowMessage(AnsiString(lti.ListItem->ClassName())); //Correctly shows "TListItem"
  FTimeInformation.push_back(lti);

  {
  LOCATIONTIMEINFORMATION temp = FTimeInformation.front();
  ShowMessage(AnsiString(temp.ListItem->ClassName())); //Correctly shows "TListItem"
  }
  Timer->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TCurrentTimesFrame::TimerTimer(TObject *Sender)
{
    UpdateTimes();
}
//---------------------------------------------------------------------------
void TCurrentTimesFrame::UpdateTimes()
{
  Timer->Enabled = false;
  TListItem* li;
  for(LocationTimeInformationItr itr=FTimeInformation.begin();itr!=FTimeInformation.end();itr++)
  {
    li = itr->ListItem;

    ShowMessage(AnsiString(li->ClassName())); //Access Violation:
    /*
    ShowMessage() above shows:

    ---------------------------
    Debugger Exception Notification
    ---------------------------
    Project XX.exe raised exception class EAccessViolation with message 'Access violation at address 4000567D in module 'rtl60.bpl'. Read of address 00000000'. Process stopped. Use Step or Run to continue.
    ---------------------------
    OK   Help
    ---------------------------
    */
  }
  Timer->Enabled = true;
}
//---------------------------------------------------------------------------

ОБНОВЛЕНИЕ Пример кода, демонстрирующий этот список, принимает элементы в качестве копии, а не ссылки. (Насколько я вижу, пожалуйста, исправьте меня, если я делаю какую-то ошибку в коде ниже)

@ Крейг Янг:

Я в замешательстве ... Я думал, что структуры будут добавлены в список как копия , а не как ссылка? Пожалуйста, посмотрите на код ниже, кажется, что копия делается? Или я что-то упускаю? Или ошибка кодирования ниже ??

void PopulateData()
{
    AnsiString DebugText;
    list<LOCATIONTIMEINFORMATION> Data;

  LOCATIONTIMEINFORMATION OnStack;

  //Prints "junk"
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);

    OnStack.TimeZoneName = "UTC";
    OnStack.PlaceName = "Near Greenwich, England";
    OnStack.UtcOffsetMinutes = 10;
    OnStack.ListItem = (TListItem*)20;

  //OnStack:
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);
  //Add data to list
    Data.push_back(OnStack);

  //Get struct from list
  LOCATIONTIMEINFORMATION InList = Data.front();

  //OnStack:
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);
  //InList:
  DebugText.sprintf("%s,%s,%d,%d",InList.TimeZoneName,InList.PlaceName,InList.UtcOffsetMinutes,(int)InList.ListItem);

  //Change OnStack
    OnStack.TimeZoneName = "NONE";
    OnStack.PlaceName = "USA";
    OnStack.UtcOffsetMinutes = 50;
    OnStack.ListItem = (TListItem*)90;

  //OnStack:
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);
  //InList:
  DebugText.sprintf("%s,%s,%d,%d",InList.TimeZoneName,InList.PlaceName,InList.UtcOffsetMinutes,(int)InList.ListItem);

  //Change InList:
    InList.TimeZoneName = "SOME";
    InList.PlaceName = "BRAZIL";
    InList.UtcOffsetMinutes = 66;
    InList.ListItem = (TListItem*)88;

  //OnStack:
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);
  //InList:
  DebugText.sprintf("%s,%s,%d,%d",InList.TimeZoneName,InList.PlaceName,InList.UtcOffsetMinutes,(int)InList.ListItem);
}

Ответы [ 3 ]

3 голосов
/ 06 декабря 2009

РЕДАКТИРОВАТЬ : Мой ответ неверный, я решил оставить его на месте, потому что стоит иметь в виду, что если ваша коллекция (список) содержит элементы по ссылке, это очень реальная возможность за «странные нарушения доступа». Описанные симптомы вполне соответствовали бы, если бы в списке STL не было копии элементов.

Привет Ляо,

Вы писали: " BLOODY-HELL-UPDATE : решено. Я не должен добавлять элементы в TListView в конструкторе TFrame."

Я собираюсь не согласиться с вами; Вы не решили это. Хотя это не обязательно хорошая идея (с точки зрения дизайна), добавление элементов в TListView в конструкторе TFrame не должно вызывать нарушения прав доступа.

РЕДАКТИРОВАТЬ : Несмотря на то, что мой ответ ниже неверен, я все еще не согласен с «BLOODY-HELL-UPDATE» Ляо. Добавление элементов в TListView в конструкторе TFrame не должно вызывать нарушения доступа. На самом деле я взял оригинальный код и протестировал его в CPBB 2009, и он отлично работал. Это говорит о том, что ошибка могла заключаться в том, как использовался кадр; или какой-то другой аспект кода, который не был продемонстрирован.

Проблема со следующей строкой в ​​конструкторе:

LOCATIONTIMEINFORMATION lti;
  • Это размещает lti в стеке.
  • Затем вы добавляете lti в список; или, более правильно: вы добавляете ссылку к lti в список .
  • Когда ваш конструктор выходит из области видимости, lti; и эта память может быть использована любой другой частью вашего приложения.
  • Позже, когда ваш таймер попытается обновить, ссылка в FTimeInformation все еще там.
  • Вы используете эту ссылку для поиска, где lti было .
  • Если этот раздел памяти был изменен какой-либо другой частью вашего приложения, то ltr->ListItem больше не ссылается на TListItem, созданный в конструкторе. Вместо этого он ссылается на какую-то другую часть памяти, которую пытается использовать, как если бы это был TListItem. Поэтому вы испытываете «странные» проблемы, такие как:
    • li-> Удаление == false
    • li-> ClassName, вызывающий нарушение прав доступа.

ПРИМЕЧАНИЕ. Независимо от того, действительно ли вы получаете нарушение доступа, обычно зависит от удачи: Считайте себя счастливчиком , если вы делаете нарушение доступа; другой вариант - обычно «необъяснимое» ошибочное поведение.

Попробуйте изменить конструктор следующим образом: он должен исправить нарушение прав доступа. ПРИМЕЧАНИЕ: теперь lti выделен динамически, вам придется решить, когда его освободить, иначе у вас будет утечка памяти;)

LOCATIONTIMEINFORMATION* lti = new LOCATIONTIMEINFORMATION;
lti->TimeZoneName = "UTC";
lti->PlaceName = "Near Greenwich, England";
lti->UtcOffsetMinutes = 0;
AddTimeInformation(*lti);
0 голосов
/ 05 декабря 2009

Какие значения у вас есть в FTimeInformation? Например, li == NULL?


Если мы можем предположить, что нарушение доступа происходит в первый раз через цикл, и li указывает на допустимый TListItem, то, возможно, нам следует разделить три оператора в строке на три строки. Примерно так:

const char* className = li->ClassName();
AnsiString  ansiString(className);
ShowMessage(ansiString);

Если нарушение прав доступа в первой строке не произойдет, это скажет нам кое-что интересное.

0 голосов
/ 05 декабря 2009

Я не вижу никаких проблем с кодом.

Попробуйте распечатать TimeZoneName или PlaceName из вашего итератора, а не li-> ClassName (), чтобы убедиться, что вы случайно не добавили что-то еще в список или что-то еще ...

...