Błąd "Access Violation" podczas obsługi ComboBox

0

Witam.
W programie wybieram z ComboBox nazwę klienta, jeśli wpiszę nieistniejącą nazwę to tworzy nowy i dodaje go do bazy k1. Pola ComboBox-a to nagłówki bazy k1.
Zwykle działa dobrze, lecz zdarzają się sytuacje (raz na 100) że program wywala się z komunikatem "Access violation at address ......".
Aby sprawdzić punkt błędu dołożyłem zmienne xb1 i xb2. Na ich podstawie wiem, że warunek if nie jest spełniany, lecz n otrzymuje wartość -1. Dlaczego?
Oto fragment kodu:

   if CKlnt.Items.IndexOf(CKlnt.Text)=-1 then begin // dodanie nowego klienta
       xb2:=xb1;
       xb1:=62;
       n:=length(k1);
       SetLength(k1,n+1);
       CKlnt.Items.Add(CKlnt.Text);
   end else n:=CKlnt.ItemIndex;                 
   xb2:=xb1;
   xb1:=63;
   xb:=IntToStr(n)+' / '+IntToStr(length(k1));

Obsługa błędu zapisuje mi takie dane: "1.6.0.2>18>63|65535 / 208:Vcl.ExtCtrls.TPanel: Access violation at address 0040B876 in module 'xxx.exe'. Write of address 031A7618". Dane to wersja pliku>xb2>xb1 | xb: opis błędu.

0

Ten kod jest tak pokręcony i nieczytelny (i źle sformatowany), że należy go usunąć i napisać od nowa;

Aby sprawdzić punkt błędu dołożyłem zmienne xb1 i xb2.

Naucz się sensownie nazywać elementy programu, bo nazwa pokroju xb1 kompletnie nic nie mówi o swoim przeznaczeniu; Masz kupkę zmiennych i absolutnie żadna z nich nie jest prawidłowo nazwana;

Na ich podstawie wiem, że warunek if nie jest spełniany, lecz n otrzymuje wartość -1. Dlaczego?

A skąd my mamy to wiedzieć?

Do określenia wartości n używasz zmiennej k1, a ustalenia wartości tej zmiennej nie pokazałeś.

0

A co nazwa zmiennej ma Ci mówić?
Jaka różnica w działaniu będzie jeśli nazwę zmienne zamiast xb1, xb2 na przykład ostatni_punkt_pracy i przedostatni_punkt_pracy?
K1 jest to tablica, a n służy do odczytania/zapisania n-tego jej elementu.
To że ktoś pisze inaczej niż Ty i nie nazywa zmiennych pełnymi nazwami, to nie znaczy że kod trzeba napisać od nowa.
Nawet nie zerknąłeś o co w tym chodzi, tylko przyczepiłeś się o nazwy zmiennych itp.
a ważne są tylko 2 linijki: (wszystko z xb można usunąć, gdyż dodałem to szukając gdzie program wywala się).

 if  CKlnt.Items.IndexOf(CKlnt.Text)=-1 then {}
 else n:=CKlnt.ItemIndex; 

Według mnie po tej linii n powinien wskazywać istniejący element tabeli lub jeśli nie istnieje to stworzyć nowy i go wskazać, jednak czasem warunek if nie jest spełniony (indeks ComboBox-a ma wartość różną od -1), a wykonanie n:=CKlnt.ItemIndex powoduje że n ma wartość -1. Nie wiem dlaczego taka sytuacja powstaje. Później gdy przypisuję wartości elementowi k1[n] to oczywiście mam Access violation.

0

Skoro wiesz lepiej oraz lepiej potrafisz pisać kod i go formatować to po co zadajesz pytania na forum?

A co nazwa zmiennej ma Ci mówić?

Ma mówić o tym, jakie dane w sobie zawiera i czym te dane są;

Jaka różnica w działaniu będzie jeśli nazwę zmienne zamiast xb1, xb2 na przykład ostatni_punkt_pracy i przedostatni_punkt_pracy?

A jaka jest różnica pomiędzy nazwaniem zmiennej DefaultFormatSettings a ssdh22? Hmm?

To że ktoś pisze inaczej niż Ty i nie nazywa zmiennych pełnymi nazwami, to nie znaczy że kod trzeba napisać od nowa.

Ten kod wygląda potwornie i jest niezgodny z jakimikolwiek wytycznymi dotyczącymi pisania czytelnego kodu w Delphi (np. z tym - Object Pascal Style Guide); Im bardziej zasyfisz kod, tym trudniej będzie go analizować, więc i trudniej będzie rozwiązać Twój problem; A to Twój problem, nie nasz, więc powinieneś zwiększać szanse na rozwiązanie go przez kogoś, kto tego kodu nie pisał, a nie na odwrót;

Poza tragiczną czytelnością, kod ten jest stanowczo przekombinowany, o czym napisałem na końcu posta;

Nawet nie zerknąłeś o co w tym chodzi, tylko przyczepiłeś się o nazwy zmiennych itp.

Nie zerknąłem? A skąd wiedziałem, że zmienna n otrzymuje długość zmiennej k, która to nie wiadomo jakiego jest typu i jakie dane zawiera? Hmm?

a ważne są tylko 2 linijki: (wszystko z xb można usunąć, gdyż dodałem to szukając gdzie program wywala się).

Skoro ważne są tylko dwie linijki, to po co wstawiłeś ich dziesięć?

Według mnie po tej linii n powinien wskazywać istniejący element tabeli lub (warunek if) stworzyć nowy i go wskazać, jednak czasem ma wartość -1, i nie wiem dlaczego taka sytuacja powstaje.

Według mnie brak Ci podstawowej znajomości VCL i jakichkolwiek chęci do samodzielnego rozwiązania problemu; Wystarczyło w pustej aplikacji okienkowej postawić ComboBox i sprawdzić co zawiera jego ItemIndex w przypadku gdy wartość wpisana jest ręcznie oraz gdy wartość została wybrana z listy; To wystarczyło, aby dojść do poniższego wniosku:

if ComboBox1.ItemIndex = -1 then
  // wartość wpisana ręcznie
else
  // wartość wybrana z rozwijalnej listy

Następnym razem zastanów się nad swoim postępowaniem i nad tym jak pisać czytelny kod, zamiast miotać się o to, że ktoś zwrócił Ci uwagę - a uwaga została zwrócona jak najbardziej słusznie, co inni użytkownicy pracujący z tym językiem z całą pewnością mogą potwierdzić.

0

Nie wiem lepiej, i nie chcę się kłócić, dlatego pytam, gdyż według mnie taka sytuacja nie powinna zaistnieć, a powstaje na pewno w tym fragmencie.
Wiem jak działa ItemIndex. Zgadza się zagmatwałem z IndexOf.
Dziękuję za pomoc.

1

raz na 100 razy , czyli raz na 100 uruchomień aplikacji czy raz na 100 razy użycia tego kodu podczas jednego uruchomienia aplikacji ?
jeśli to pierwsze (100 uruchomień), to być może nie zerujesz na starcie rozmiaru tablicy k1, czyli brak setlength(k1,0). Chociaż w takiej sytuacji bardziej prawdopodobny był by wyjątek 'index out of range' . Radził bym też sprawdzić w opcjach projektu czy parametr 'Range checking' jest ustawiony na True. Bo bez tego, robiąc podstawienie pod element tablicy będący poza jej rozmiarem nie dostaniesz żadnego wyjątku, ale możesz nadpisać w pamięci inne zmienne czy obiekty i w następstwie tego próba odwołania się do takich nadpisanych obiektów może skutkować wyjątkiem "Acces violation ..."

0

raz na kilkadziesiąt razy użycia tego kodu, czasem po paru godzinach, około 80 - 100 wywołań, lecz zawsze w tym miejscu. Range checking był na false.

2
n:=CKlnt.Items.IndexOf(CKlnt.Text);
if n=-1 then 
begin
  //dopisz klienta i do n przypisz nowe id
end; 
0

Dziękuję za pomoc, na pewno Wasze przykłady tego błędu nie wygenerują.
Jednakże gdy zmieniłem Range Check na true, to uruchomienie programu przy kompilacji Release powoduje błąd

Project xxx raised exception class ERangeError with message 'Range check error'.

Przy kompilacji Debug nie mam błędu.

0

@Klakierus: podaj aktualnie testowaną wersję kodu i dokładnie wskaż linijkę, która powoduje wyjątek; Aby to określić, najlepiej będzie postawić break point przed tym warunkiem i klawiszami F7 i F8 sprawdzić kolejne linijki.

0

w jakim środowisku kompilujesz projekt

podaj cały kod

Project xxx raised exception class ERangeError with message 'Range check error'. czyli jak przypuszczałem, próbujesz odwołać się do elementów wykraczających poza rozmiar tablicy

0

Kompilacja: Win32, pracuję na Win7 64bit.
Sorki Furious programming, ale o co pytasz? Jaka wersja kodu?

Cały kod? Tego jest w głównym 2000 linii + 8 form po kilkaset linii.

0

pytam o środowisko ? Delhpi w wersja xxx , Lazarus ??

0

RAD Studio 10 Seattle
Kod jest od zmiany w Combo, a błąd powstaje już przy starcie, szukam OnCreate, dlaczego na debug jest ok?

    if CKlnt.ItemIndex=-1 then begin // dodanie nowego klienta
       xb2:=xb1;
       xb1:=62;
       n:=length(k1);
       SetLength(k1,n+1);
       CKlnt.Items.Add(CKlnt.Text);
   end else n:=CKlnt.ItemIndex;       
   xb2:=xb1;
   xb1:=63;
   xb:=IntToStr(n)+' / '+IntToStr(length(k1));
   k1[n].naz:=CKlnt.Text;
0
Klakierus:

Sorki Furious programming, ale o co pytasz? Jaka wersja kodu?

Zakładam, że po naszych sugestiach coś w swoim kodzie zmieniłeś, więc pasuje podać kod który teraz testujesz i króty nadal powoduje błąd;

Cały kod? Tego jest w głównym 2000 linii + 8 form po kilkaset linii.

Nie cały kod, bo w każdej linijce całego kodu błąd nie występuje - tylko ten fragment, który rzuca wyjątek.

0

Ok, tylko ze nie mogę "postawić break point przed tym warunkiem i klawiszami F7 i F8 sprawdzić kolejne linijki." bo w debug jest OK, wywala w Release. Szukam wyłączając fragmenty kodu.

0

W takim razie wyjątek nie pochodzi z kodu, którego o wyjątek podejrzewasz; Poza tym w dalszym ciągu nie podałeś kodu który ten rzekomy wyjątek powoduje.

0

sprawdź czy w opcjach środowiska IDE "DebuggerOptions -> Embarcadero Debuggers -> Language Exceptions " nie masz na liście "Excepions type to ignore" wyjątku ERangeError

0

Tak, już wszystko OK.
Po pierwsze chcąc robić i szybko odpowiadać na posty, zapomniałem że opcje Range checking należy oddzielnie ustawić dla debug i release. Gdy naprawiłem ten błąd, poszło z górki, i znalazłem moje błędy.
Bardzo dziękuję za Waszą pomoc, bez niej dalej bym siedział z ręką w nocniku.
Pozdrawiam i życzę miłego wieczoru.

0

Czyli podsumowując - zamiast naprawić błąd spowodowany odwoływaniem się do elementu o nieprawidłowym indeksie, Ty wyłączyłeś obsługę tego błędu, ochodząc problem na około.

Uważasz, że to prawidłowe podejście?

0

Niestety było odwrotnie. Te ustawienia były domyślne, od zainstalowania Delphi nie zmieniałem ich. Przez to szukałem błędu tam gdzie go nie było. Teraz mam Range settings na true, lecz czcionka jest pogrubiona, co jak sądzę informuje użytkownika o wprowadzonych zmianach.

2

@Klakierus: na etapie budowania i testowania aplikacji ustawienie na True parametru środowiska o nazwie "Range checking" jest konieczne. Może skutkuje to jakimś spadkiem wydajności ale pozwala wyłapać wiele błędów. Po dokładnym przetestowaniu projektu i przed udostępnieniem aplikacji userowi można tę opcję wyłączyć, ale nie polecał bym tego. Jeśli aplikacja nie jest czasowo krytyczna (mam na myśli szybkość działania) to pozostawił bym tę opcję jako True również w aplikacji finalnej (Release)

0

Dzięki Grzegorz_so, a co jeszcze należałoby przestawić w opcjach, abym w przyszłości nie miał podobnych przygód?

0

możesz jeszcze włączyć na true "I/O checking"oraz "Overflow checking"
pierwsze dotyczy błędów dostępu do urządzeń we/wy a drugie dotyczy obliczeń numerycznych

0

Dzięki za pomoc.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.