2009-12-05 8 views
1

У меня есть следующий код, и для жизни я не могу понять, почему было исключение нарушения доступа? Я даже удалил все файлы OBJ, TDS etc и поместил их в новый проект, но все же происходит нарушение доступа.Сумасшедшее нарушение доступа в C++ Builder 6

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

Примечание: код в C++ Builder 6.

Может кто-то помочь?

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

MAJOR UPDATE: Кажется, что когда UpdateTimes() вызывается с помощью таймера, «Li-> Удаление» свойство имеет значение ИСТИНА. При вызове вне таймера это ЛОЖЬ. Теперь почему «li-> Deleting» устанавливается в «true», потому что он вызывается из таймера? Если я делаю:

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

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

UPDATE: Похоже, если я вызову 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; 
} 
//--------------------------------------------------------------------------- 

UPDATE пример кода demo'ing, что список принимает элементы, как копии, а не ссылаться. (Насколько я могу видеть, пожалуйста, поправьте меня, если им сделать какую-то ошибку в коде ниже)

@Craig Young:

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

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); 
} 
+0

РЕШЕН. ОК. Я узнал, что DONT добавляет элементы в TListView в конструкторе. Как DUMB. – Liao

+0

Не совсем; ваше «решение», возможно, изменило что-то другое, но создание TListItems в конструкторе не должно по своей сути вызывать проблемы. Пожалуйста, проверьте мой подробный ответ. –

+0

Моя ошибка. Я имею тенденцию иметь больше мышления Дельфы; и без STL наш TList всегда будет хранить элементы по ссылке, если не будет сделано особых усилий. Тем не менее, я протестировал ваш оригинальный код, и он отлично работает, поэтому я думаю, что ваша ошибка в другом месте. Укажите, как вы используете фрейм. –

ответ

3

EDIT: Мой ответ неверен, я решил оставить его на месте, потому что это имеет смысл, имея в виду, что если ваша коллекция (список) имеет элементы по ссылке, это очень реальная возможность «странные нарушения прав доступа». Описанные симптомы прекрасно коррелируют, если бы не список STL, в котором хранилась копия элементов.

Привет Ляо,

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

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

EDIT: Несмотря на мой ответ ниже является некорректным, я до сих пор не согласен с Ляо «Кроваво-АДА-UPDATE». Добавление элементов в TListView в конструкторе TFrame не должно вызывать нарушения доступа. Фактически я взял оригинальный код и протестировал его в CPBB 2009, и он отлично работал. Это говорит о том, что ошибка, возможно, заключалась в том, как использовался кадр; или какой-либо другой аспект кода, который не был продемонстрирован.

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

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

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

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

LOCATIONTIMEINFORMATION* lti = new LOCATIONTIMEINFORMATION; 
lti->TimeZoneName = "UTC"; 
lti->PlaceName = "Near Greenwich, England"; 
lti->UtcOffsetMinutes = 0; 
AddTimeInformation(*lti); 
+0

+1 хороший диагноз – 2009-12-05 21:50:33

+1

Вы сказали, что «Затем вы добавляете lti в список, или, вернее, добавляете ссылку на lti в список». Но я не уверен, что это правильно .. Я добавил образец кода в конце вопроса (он не вписывался в комментарий, который не превышает 600 символов). Образец кода, похоже, показывает, что list.push_back добавляет * копию * структуры, а не ссылку. Не могли бы вы запустить это и посмотреть? – Liao

+1

-10 для неправильной диагностики. push_back() в (по крайней мере) списке STL добавляет * copy *, а не * ссылку * структуры. Только в то время как * доступ к * элементам списка STL, он возвращает ссылки. – Liao

0

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

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

+1

Выполнение ShowMessage (itr-> PlaceName); работает. Это означает, что itr-> ListItem где-то «разрушен»? – Liao

+0

Таким образом, проблема была неадекватной моей плохой. Ни в коем случае никогда не добавляйте TListItem и тому подобное в ctor. – Liao

0

Какие ценности вам есть в FTimeInformation? Например, li == NULL?


Если мы можем предположить, что нарушение прав доступа происходит в первый раз через петлю, и Ли указывает на допустимый TListItem то, возможно, мы должны разделить три заявления на линии в течение трех линий.Что-то вроде этого:

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

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

+0

Нет, li не null, он показывает действительный указатель. – Liao

+0

Не повезло. Посмотрите мое «ОБНОВЛЕНИЕ:» в вопросе. В основном, нарушение доступа происходит только при вызове UpdateTimes() из таймера. – Liao

+0

Использует ли таймер отдельный поток? Если это так, то доступ к общим значениям должен выполняться поточно-безопасным способом, поскольку в противном случае нет никакой гарантии, что изменения, сделанные в одном потоке, будут видны другому. – richj

Смежные вопросы