2009-12-05 4 views
1

J'ai le code suivant, et pour la vie de moi, je ne peux pas comprendre pourquoi il y aurait une exception de violation d'accès? J'ai même supprimé tous les fichiers OBJ, TDS, etc et le mettre dans un nouveau projet, toujours la violation d'accès se produit. Essentiellement, ce code affiche un TListView dans un TFrame et doit montrer les différents temps actuels autour du monde pour différents fuseaux horaires.Violation d'accès fou dans C++ Builder 6

Note: Le code est dans le constructeur de C 6.

Quelqu'un peut-il aider?

BLOODY-HELL-UPDATE: Résolu. Je ne devrais pas ajouter d'éléments à TListView dans le constructeur TFrame. DUMB DUMB DUMB.

Mise à jour majeure: Il semble que lorsque l'on appelle le UpdateTimes() par la minuterie, la « Li-> Suppression » propriété est TRUE. Lorsqu'il est appelé en dehors de la minuterie, il est FAUX. Maintenant, pourquoi "li-> Suppression" serait-il réglé sur "vrai" parce qu'il est appelé à partir de la minuterie? Si je fais:

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

Il ne marche pas entrer dans le cas(), quand UpdateTimes() est appelée à partir de la minuterie ...... argggggh !!!

MISE À JOUR: Il semble que si j'appelle UpdateTimes() en dehors du TTimer, cela fonctionne très bien. Mais lorsqu'il est appelé à partir de la minuterie, il lance la violation d'accès. Ce qui donne?

tête de fichier:

#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 

RPC fichier:

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

MISE À JOUR Un exemple de code demo'ing cette liste prend des éléments comme la copie, pas de référence. (Pour autant que je peux voir, s'il vous plaît me corriger si im faire une erreur dans le code ci-dessous)

@Craig Young:

Je suis confus ... Je pensais que struct seraient ajoutés à la liste en tant que copie pas comme référence? S'il vous plaît jeter un oeil sur le code ci-dessous, il semble qu'une copie est en cours? Ou est-ce que je manque quelque chose de rudimentaire? Ou une erreur de codage ci-dessous ??

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

RÉSOLU. D'ACCORD. Ce que j'ai appris est que, N'ajoutez pas d'éléments à TListView dans le constructeur. Comment DUMB. – Liao

+0

Pas tout à fait; votre 'solution' peut avoir changé quelque chose d'autre, mais créer des TListItems dans le constructeur ne devrait pas causer de problèmes de manière inhérente. S'il vous plaît consulter ma réponse détaillée. –

+0

Mon erreur. J'ai tendance à avoir plus d'un état d'esprit Delphi; et sans STL, notre TList tiendra toujours des articles par référence à moins qu'un effort spécial soit fait autrement. Cependant, j'ai testé votre code original, et cela fonctionne parfaitement - donc je pense que votre erreur est ailleurs. S'il vous plaît poster comment vous utilisez le cadre. –

Répondre

3

EDIT: Ma réponse est incorrecte, j'ai décidé de le laisser en place parce qu'il est utile roulement à l'esprit que si votre collection (liste) contient des éléments de référence, ceci est une possibilité très réelle pour 'violations d'accès étranges'. Les symptômes décrits seraient corrélés parfaitement si la liste STL n'avait pas conservé une copie des éléments.

Salut Liao,

Vous avez écrit: "BLOODY-HELL-UPDATE. Résolu Je ne devrais pas ajouter des éléments à TListView dans le constructeur TFrame."

Je ne suis pas d'accord avec vous; vous avez pas résolu. Bien que ce ne soit pas nécessairement une bonne idée (en termes de conception), l'ajout d'éléments à TListView dans le constructeur TFrame ne devrait pas entraîner de violations d'accès.

EDIT: En dépit de ma réponse ci-dessous étant incorrecte, je suis en désaccord toujours avec 'BLOODY-HELL-UPDATE' Liao. L'ajout d'éléments à TListView dans le constructeur TFrame ne doit pas entraîner des violations d'accès. En fait, j'ai pris le code original et l'ai testé dans CPBB 2009, et ça a fonctionné parfaitement. Cela suggère que l'erreur peut avoir été dans la façon dont le cadre a été utilisé; ou un autre aspect du code qui n'a pas été démontré.

Le problème est lié à la ligne suivante dans le constructeur:

LOCATIONTIMEINFORMATION lti; 
  • Cette alloue lti sur la pile.
  • Vous ajoutez ensuite lti à une liste; ou plus correctement: vous ajoutez un référence à lti à la liste.
  • Lorsque votre constructeur est hors de portée, lti aussi; et cette mémoire peut être réutilisée par n'importe quelle autre partie de votre application.
  • Plus tard, lorsque votre minuteur tente la mise à jour, la référence dans FTimeInformation est toujours là.
  • Vous utilisez cette référence pour rechercher où lti était.
  • Si cette section de la mémoire a été modifiée par une autre partie de votre application, alors ltr->ListItem ne fait plus référence à TListItem qui a été créé dans le constructeur. Au lieu de cela, il fait référence à une autre partie de la mémoire qu'il essaie d'utiliser comme s'il s'agissait d'un TListItem. Par conséquent, vous rencontrez des problèmes 'étranges' tels que:
    • Li-> Suppression == false
    • Li-> ClassName provoquant une violation d'accès.

NOTE: Si oui ou non vous obtenez en fait une violation d'accès dépend généralement un peu de chance: Considérez-vous chanceux si vous faites obtenir la violation d'accès; l'autre option est généralement un comportement erratique «inexplicable». Essayez de modifier votre constructeur comme suit, il devrait corriger la violation d'accès. NOTE: lti est maintenant alloué dynamiquement, vous devrez décider quand le libérer, sinon vous aurez une fuite de mémoire;)

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

+1 bon diagnostic –

+1

Vous avez dit que "Vous ajoutez ensuite lti à une liste, ou plus correctement: vous ajoutez une référence à lti à la liste." Mais je ne suis pas sûr que c'est correct .. J'ai ajouté un échantillon de code à la fin de la question (il ne rentre pas dans le commentaire qui est au maximum de 600 caractères). L'exemple de code semble montrer que list.push_back ajoute une * copie * de la structure, pas une référence. Pourriez-vous courir cela et voir? – Liao

+1

-10 pour un diagnostic incorrect. push_back() dans (au moins) une liste STL, ajoute une * copie *, pas une * référence * de la structure. Ce n'est que lors de * l'accès * aux éléments de la liste STL que les références sont renvoyées. – Liao

0

Je ne vois aucun problème avec le code.

Essayez d'imprimer le TimeZoneName ou PlaceName de votre iterator, plutôt que Li-> ClassName(), pour vous assurer que vous avez accidentellement ajouté quelque chose d'autre à la liste ou quelque chose ...

+1

en cours ShowMessage (itr-> PlaceName); fonctionne. Cela signifie que itr-> ListItem est "ruiné" quelque part? – Liao

+0

Donc, le problème était en fait mon mauvais. Ne jamais ajouter jamais TListItem et autres dans le ctor. – Liao

0

Quelles sont les valeurs-vous avoir dans FTimeInformation? Par exemple, est li == NULL?


Si on peut supposer que la violation d'accès se produit la première fois dans la boucle, et les points de li à un TListItem valide alors nous devrions peut-être diviser les trois déclarations sur la ligne sur trois lignes.Quelque chose comme ceci:

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

Si la violation d'accès ne se produit pas sur la première ligne cela va nous dire quelque chose d'intéressant.

+0

Non, li n'est pas nul, il montre un pointeur valide. – Liao

+0

Pas de chance avec ça. Voir ma "MISE À JOUR:" dans la question. Fondamentalement, la violation d'accès ne se produit que lorsque UpdateTimes() est appelé à partir de la minuterie. – Liao

+0

La minuterie utilise-t-elle un fil séparé? Si tel est le cas, l'accès aux valeurs partagées doit être effectué de manière sûre pour les threads, car sinon il n'y a aucune garantie que les modifications apportées à un thread sont visibles à un autre. – richj

Questions connexes