Zablokowanie uruchomienia programu po poprawnym zakończeniu kompilacji i linkowania

0

Witam

W pracy w środowisku Embarcadero C++ Builder
natknąłem się na następujący problem:
po poprawnym zakończeniu kompilacji i linkowania,
następuje zablokowanie uruchamiania programu z komunikatem:

Project nazwa_projektu.exe raised exception class $C0000090
with message 'floating point invalid operation at 0x0061169d'

Przyczyną nie jest niepoprawna operacja na liczbach rzeczywistych.
Innych błędów w programie też nie ma.

W Tools > Options > Debugger Options > Embarcadero Debuggers > Native OS Exceptions
próbowałem zmienić konfigurację dla pozycji Float Invalid Operation ($C0000090),
ale blokowanie uruchomienia programu występuje nadal.

Wygląda to na występowaniu limitu pamięci budowanej w C++ Builderze aplikacji.
Próbowałem znaleźć w opcjach C++ Buildera ustawienie takiego limitu,
ale jeszcze nie znalazłem.
Może taki limit ustawia się w inny sposób, o którym jeszcze nie wiem.
Może ma to związek z systemem Windows 10, w którym pracuję.

W Internecie znalazłem wpisy na forum w tej sprawie pod adresem:
https://forums.embarcadero.com/message.jspa?messageID=655749
ale z opisem problemu, bez jego rozwiązania.

Czy ktoś w naszym środowisku spotkał się z takim problemem ?

Pozdrawiam
Paweł Frankowski

0
  1. w trybie debug, ustaw breakpoint na startpoincie. Uruchom i zobacz czy uderza w breakpoint
  2. jezeli tak. Sprawdz gdzie dokladnie wyrzuca ten wyjatek (na ktorej linijce kodu) i wrzuc wtedy cala funkcje to ja zanalizujemy i powiemy co jest nie tak
  3. jezeli nie. To zapewne wiecej potrzebna jest informacja o projekcie
0
pawelfrankowski napisał(a):

po poprawnym zakończeniu kompilacji i linkowania,

w C/C++ poprawne skopilowanie i zlinkowanie, to strasznie mało.

Przyczyną nie jest niepoprawna operacja na liczbach rzeczywistych.
...
Wygląda to na występowaniu limitu pamięci budowanej w C++ Builderze aplikacji.
wnioskowania trochę rozbieżne

Innych błędów w programie też nie ma.

ta wiara mnie zaskakuje - patrz pierwsze zdanie

fasadin napisał(a):
  1. jezeli tak. Sprawdz gdzie dokladnie wyrzuca ten wyjatek (na ktorej linijce kodu) i wrzuc wtedy cala funkcje to ja zanalizujemy i powiemy co jest nie tak
  2. jezeli nie. To zapewne wiecej potrzebna jest informacja o projekcie

zdecydowanie bez kodu to kiepsko pomóc ...

1
  1. Program nie jest zablokowany, ale po prostu następuje Crash (poważny błąd powoduje zakończanie programu)
  2. Limit pamięci nie ma nic wspólnego z błędami obliczeń zmiennoprzecinkowych
  3. Odpal program w trybie debug z C++Builder. Program powinien zatrzymać się podczas błędu. Znajdź w C++Builder okno "Call Stack" przeklikaj jego kolejne pozycje i przeanalizuj kod do którego cię to przenosi.
0
pawelfrankowski napisał(a):

W Tools > Options > Debugger Options > Embarcadero Debuggers > Native OS Exceptions
próbowałem zmienić konfigurację dla pozycji Float Invalid Operation ($C0000090),
ale blokowanie uruchomienia programu występuje nadal.

Wybacz, ale błądzisz trochę po omacku. Opcja ta ustawia ignorowanie przez IDE (jeśli program uruchomisz spod niego w trybie debug) danych wyjątków.

W tej sytuacji jak kolega @MarekR22 powiedział. Uruchom program w trybie debug, poczekaj aż się wykrzaczy i w oknie Call Stack przejdź do swojego kodu (czasem wyjątek może być np w bibliotece kernel32.dll bądź skoczyć do źródeł VCL) i będziesz miał co wywołało błąd. Przy czym Call Stack czytamy od góry.

0

Dziękuję kolegom za udział w dyskusji nad moim problemem.

Uruchamiam program ikonką Run w pasku narzędziowym
odpowiadającą poleceniu Run z menu Run i klawiszowi F9.
Wygląda to na uruchamianie z debuggowaniem,
ponieważ w przypadku błędów C++ Builder zatrzymuje kompilację
otwiera plik żródłowy w którym jest błąd, zaznacza linię w kodzie w której jest błąd
i w oknie komunikatów Messages opisuje mniej albo bardziej dokładnie na czym ten bład polega.
W tym przypadku C++ Builder przechodzi przez kompilację
podczas której wyświetla okienko kompilacji
z informacją o kompilacji kolejnych plików źródłowych w projekcie
nie zatrzymując się na żadnym błędzie.
Po zakończeniu kompilacji C++ Builder przechodzi do linkowania
skompilowanych plików źródłowych wchodzących w skład projektu
podczas którego wyświetla okienko linkowania nie zatrzymując się na żadnym błędzie.
Po zakończeniu linkowania okienko linkowania zostaje zamknięte
i w oknie komunikatów Messages pojawia się na końcu komunikat Success.
Dopiero w po tym zamiast otwarcia zbudowanego programu pojawia się okienko z komunikatem:

Project nazwa_projektu.exe raised exception class $C0000090
with message 'floating point invalid operation at 0x0061169d'

Program jest narzędziem służącym do automatyzacji wprowadzania danych
z dokumentów księgowych do programów księgowych.
Przy kodzie z instrukcją warunkową:

       if (   (dokumenty[i]=="Faktura" && dokumenty[i+1]=="VAT:"    )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="VAT:"    )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="VAT"     )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="VAT"     )

           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="nr:"     )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="nr:"     )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="NR:"     )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="nr"      )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="nr"      )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="NR"      )

           || (dokumenty[i]=="Nr"      && dokumenty[i+1]=="faktury:")
           || (dokumenty[i]=="NR"      && dokumenty[i+1]=="FAKTURY:")
           || (dokumenty[i]=="Nr"      && dokumenty[i+1]=="faktury" )
           || (dokumenty[i]=="NR"      && dokumenty[i+1]=="FAKTURY" )

           || (dokumenty[i]=="Numer"   && dokumenty[i+1]=="faktury:")
           || (dokumenty[i]=="NUMER"   && dokumenty[i+1]=="FAKTURY:")
           || (dokumenty[i]=="Numer"   && dokumenty[i+1]=="faktury" )
           || (dokumenty[i]=="NUMER"   && dokumenty[i+1]=="FAKTURY" )

           || (dokumenty[i]=="Faktura" && dokumenty[i+1]==":"       )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]==":"       ))
          {
          Numer_dokumentu=dokumenty[i+2];
          jest_numer_dokumentu=1;
          };

program otwiera się poprawnie,
a jeśli rozbuduję tą instrukcję warunkową o kilka dodatkowych wariantów
napisu przed numerem faktury w różnych fakturach:

       if (   (dokumenty[i]=="Faktura" && dokumenty[i+1]=="VAT:"    )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="VAT:"    )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="VAT"     )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="VAT"     )

           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="numer:"  )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="Numer:"  )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="NUMER:"  )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="numer:"  )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="Numer:"  )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="NUMER:"  )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="numer"   )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="Numer"   )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="NUMER"   )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="numer"   )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="Numer"   )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="NUMER"   )

           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="nr:"     )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="nr:"     )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="NR:"     )
           || (dokumenty[i]=="Faktura" && dokumenty[i+1]=="nr"      )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="nr"      )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]=="NR"      )

           || (dokumenty[i]=="Nr"      && dokumenty[i+1]=="faktury:")
           || (dokumenty[i]=="NR"      && dokumenty[i+1]=="FAKTURY:")
           || (dokumenty[i]=="Nr"      && dokumenty[i+1]=="faktury" )
           || (dokumenty[i]=="NR"      && dokumenty[i+1]=="FAKTURY" )

           || (dokumenty[i]=="Numer"   && dokumenty[i+1]=="faktury:")
           || (dokumenty[i]=="NUMER"   && dokumenty[i+1]=="FAKTURY:")
           || (dokumenty[i]=="Numer"   && dokumenty[i+1]=="faktury" )
           || (dokumenty[i]=="NUMER"   && dokumenty[i+1]=="FAKTURY" )

           || (dokumenty[i]=="Faktura" && dokumenty[i+1]==":"       )
           || (dokumenty[i]=="FAKTURA" && dokumenty[i+1]==":"       ))
          {
          Numer_dokumentu=dokumenty[i+2];
          jest_numer_dokumentu=1;
          };

to po zakończeniu kompilacji i linkowania zamiast otwarcia programu
pojawia się okienko z komunikatem:

Project nazwa_projektu.exe raised exception class $C0000090
with message 'floating point invalid operation at 0x0061169d'

Nie widzę żadnego błędu w rozbudowie instrukcji warunkowej
o kilka dodatkowych wariantów w alternatywie logicznej,
ponadto w tym fragmencie kodu nie występują żadne działanie na liczbach rzeczywistych.

Stąd wynika moja aktualna bezradność w tej sytuacji.
podejrzewam, że ma to związek z systemem Windows.

Pozdrawiam
Paweł Frankowski

0

Hmm co prawda nie mam rozwiązania Twojego problemu, ale idziesz trochę w złą stronę. Nie mam jednak fajnego rozwiązania Twojego problemu (chodzi o wyszukiwanie fraz w tekstach). Ale zauważ, że trochę to mało eleganckie. W pierwszej kolejności użyłbym metody Upper ponieważ aż chce się ją wykorzystać aby zmniejszyć ilość przypadków.

Na chwilę obecną mogę to uprościć do takiej postaci:

  AnsiString sTemp1;
  AnsiString sTemp2;

  sTemp1 = "FakTura";
  sTemp2 = "vat";

  if (   (sTemp1=="Faktura" && sTemp2=="VAT:"    )
     || (sTemp1=="FAKTURA" && sTemp2=="VAT:"    )
     || (sTemp1=="Faktura" && sTemp2=="VAT"     )
     || (sTemp1=="FAKTURA" && sTemp2=="VAT"     )

     || (sTemp1=="Faktura" && sTemp2=="numer:"  )
     || (sTemp1=="Faktura" && sTemp2=="Numer:"  )
     || (sTemp1=="Faktura" && sTemp2=="NUMER:"  )
     || (sTemp1=="FAKTURA" && sTemp2=="numer:"  )
     || (sTemp1=="FAKTURA" && sTemp2=="Numer:"  )
     || (sTemp1=="FAKTURA" && sTemp2=="NUMER:"  )
     || (sTemp1=="Faktura" && sTemp2=="numer"   )
     || (sTemp1=="Faktura" && sTemp2=="Numer"   )
     || (sTemp1=="Faktura" && sTemp2=="NUMER"   )
     || (sTemp1=="FAKTURA" && sTemp2=="numer"   )
     || (sTemp1=="FAKTURA" && sTemp2=="Numer"   )
     || (sTemp1=="FAKTURA" && sTemp2=="NUMER"   )

     || (sTemp1=="Faktura" && sTemp2=="nr:"     )
     || (sTemp1=="FAKTURA" && sTemp2=="nr:"     )
     || (sTemp1=="FAKTURA" && sTemp2=="NR:"     )
     || (sTemp1=="Faktura" && sTemp2=="nr"      )
     || (sTemp1=="FAKTURA" && sTemp2=="nr"      )
     || (sTemp1=="FAKTURA" && sTemp2=="NR"      )

     || (sTemp1=="Nr"      && sTemp2=="faktury:")
     || (sTemp1=="NR"      && sTemp2=="FAKTURY:")
     || (sTemp1=="Nr"      && sTemp2=="faktury" )
     || (sTemp1=="NR"      && sTemp2=="FAKTURY" )

     || (sTemp1=="Numer"   && sTemp2=="faktury:")
     || (sTemp1=="NUMER"   && sTemp2=="FAKTURY:")
     || (sTemp1=="Numer"   && sTemp2=="faktury" )
     || (sTemp1=="NUMER"   && sTemp2=="FAKTURY" )

     || (sTemp1=="Faktura" && sTemp2==":"       )
     || (sTemp1=="FAKTURA" && sTemp2==":"       ))
    {
      ShowMessage("yes2");
    }

vs

  AnsiString sTemp1;
  AnsiString sTemp2;

  sTemp1 = "FakTura";
  sTemp2 = "vat";


  sTemp1 = sTemp1.UpperCase();
  sTemp2 = sTemp2.UpperCase();

  if (sTemp1=="FAKTURA" && (sTemp2=="VAT:" || sTemp2=="VAT"))
  {
    ShowMessage("yes");
  }
  if (sTemp1=="FAKTURA" && (sTemp2=="NUMER:" || sTemp2=="NUMER"))
  {
    ShowMessage("yes");
  }
  if (sTemp1=="FAKTURA" && (sTemp2=="NR:" || sTemp2=="NR"))
  {
    ShowMessage("yes");
  }
  if (sTemp1=="NR" && (sTemp2=="FAKTURY:" || sTemp2=="FAKTURY"))
  {
    ShowMessage("yes");
  }
  if (sTemp1=="NUMER" && (sTemp2=="FAKTURY:" || sTemp2=="FAKTURY"))
  {
    ShowMessage("yes");
  }
  if (sTemp1=="FAKTURA" && sTemp2==":")
  {
    ShowMessage("yes");
  }

Oczywiście jak widzisz można by było iść jeszcze dalej i pogrupować poszczególne if'y np tak:

  AnsiString sTemp1;
  AnsiString sTemp2;

  sTemp1 = "FakTura";
  sTemp2 = "vat";

  sTemp1 = sTemp1.UpperCase();
  sTemp2 = sTemp2.UpperCase();

  if (sTemp1=="FAKTURA" && (sTemp2=="VAT:" || sTemp2=="VAT" || sTemp2=="NUMER:" || sTemp2=="NUMER" || sTemp2=="NR:" || sTemp2=="NR" || sTemp2==":"))
  {
    ShowMessage("yes");
  }
  if (sTemp1=="NR" && (sTemp2=="FAKTURY:" || sTemp2=="FAKTURY"))
  {
    ShowMessage("yes");
  }
  if (sTemp1=="NUMER" && (sTemp2=="FAKTURY:" || sTemp2=="FAKTURY"))
  {
    ShowMessage("yes");
  }

Dodatkowo można by próbować to zapisać w postaci czegoś fajniejszego niż ifologia. Tylko ja nie mam pomysłu za bardzo jak.
No i warto by te sprawdzanie wydzielić do oddzielnej funkcji by zapisać to tak:

  if (jakas_funkcja(dokumenty[i],dokumenty[i+1]))
  {
    Numer_dokumentu=dokumenty[i+2];
    jest_numer_dokumentu=1;
  }

A całość sprawdzania sobie przenieść do nowej funkcji. Wtedy kod będzie bardziej prosty i czytelny.

0

słabo mi od tych ifów ;)
może w pierwszym przypadku warunek nie jest spełniony, tylko w drugim, a błąd jest dopiero w kodzie wykonywanym po spełnieniu warunku

0

Przedstawiona instrukcja warunkowa
jest częścią rozbudowanej logiki wyszukiwania numeru faktury w tekście faktury.
Z kolei logika wyszukiwania numeru faktury
jest częścią całej logiki wyszukiwania wszystkich parametrów księgowych w tekście faktury.
Dlatego przedstawiona instrukcja warunkowa
zawiera tylko część wariantów napisu występującego przed numerem faktury w różnych wzorcach faktur
już uwzględnionych w programie,
ale warianty już uwzględnione w programie nadal nie wyczerpują wszystkich możliwości.
Dlatego program jest ciągle rozbudowywany o kolejne spotykane w fakturach warianty
dotyczące wszystkich parametrów księgowych, nie tylko numeru faktury.
W pewnym momencie tej rozbudowy,
dotyczącym akurat numeru faktury w przedstawionej instrukcji warunkowej,
C++ Builder sobie nie poradził.

Dziękuję kolegom za propozycje zastosowania bardziej zaawansowanych konstrukcji językowych.
Niemniej rozwijane od wielu lat
profesjonalne komercyjne środowisko programistyczne jakim jest C++ Builder,
powinno sobie radzić z najprostszą instrukcją sterowania którą jest instrukcja warunkowa,
i oczywiście na ogół sobie radzi.
Nie wiem co się dzieje w tym przypadku,
skąd po komunikacie Success zamiast otwarcia zbudowanego programu
pojawia się komunikat o niepoprawnej operacji zmiennoprzecinkowej.
Podejrzewam przyczynę w komunikacji na styku C++ Buildera z systemem Windows.
Program wykonywalny ma aktualnie rozmiar 3,78 MB.
Myślałem też o ustawieniu w opcjach C++ Buildera jakiegoś limitu pamięci aplikacji,
ale nie znalazłem.

Pozdrawiam
Paweł Frankowski

2
  1. trzeba było zrobić konwersje do lower case albo zadbać o to by porównanie nie było wrażliwe na wielkość liter.
  2. ta lista słów kluczowych powinna się znaleźć w jakiejś strukturze danych (np std::set std::unurdered_set), co znacznie uprości i przyspieszy kod.
  3. lista słów kluczowych nie powinna być wbudowana w program, ale powinna być opcją konfiguracyjną programu, dzięki czemu jak dojdziesz do wniosku, że jeszcze inne słowo kluczowe powinno się pojawić, to wtedy nie musisz przebudowywać całego kodu.
  4. Może warto było pomyśleć o wyrażeniach regularnych (jako opcja konfiguracyjna).

Kod się piszę dla ludzi, a nie dla kompilatora. Kompilator jest wstanie skompilować dowolne krzaki, dopóki są zgodne ze składnią, natomiast człowiek ma swoje ograniczania i nie powinien być zmuszany do czytania przez 15 minut jednego if-a, by naprawić poprawnie prostego bug-a.

2
pawelfrankowski napisał(a):

Dziękuję kolegom za propozycje zastosowania bardziej zaawansowanych konstrukcji językowych.
Niemniej rozwijane od wielu lat
profesjonalne komercyjne środowisko programistyczne jakim jest C++ Builder,
powinno sobie radzić z najprostszą instrukcją sterowania którą jest instrukcja warunkowa,
i oczywiście na ogół sobie radzi.

Powinieneś pracowac w marketingu (albo w polityce).

Wybacz sarkazm, ale ja mam mocne skojarzenie, że projekty Borlandowe są wyklikiwane, algorytmy nie są planowane, a struktury danych dla programistów tej rodziny nie istnieją. Biblioteka standardowa nieznana. . Komercyjny program na dziesiątki jednostek kompilacji po 64KB source, bo więcej się nie dawało, rozwijany na globalnych zmiennych (bo tak IDE proponuje), bez jednego słowa class za wyjątkiem automatycznego class Form, z jednym struct ale to była wklejka czegoś znalezionego.

ex Borland obecnie Embecadoro sam się od 15 lat spychał w niszę (zasłużony programista borlandowy Ci to mówi)

Program wykonywalny ma aktualnie rozmiar 3,78 MB.
Myślałem też o ustawieniu w opcjach C++ Buildera jakiegoś limitu pamięci aplikacji,
ale nie znalazłem.

3 mega z kawałkiem to jest pikuś a nie jakikolwiek limit. Usilnie chcesz udowodnić sobie błąd nie po Twojej stronie.

PS. wyrażenie warunkowe na 30 linii nie jest normalne. Sam bym to odpuścił w testach, gdybym był producentem kompilatora. BTW znane mi bdb poprzednie wersje były na żywej tkance parsera i kompilatora C++ rozpaczliwie patchowane, by zachować kompatybilność z Delphi (typy datowe na twardo wszyte w kompilator itd - czarna rozpacz przy błędzie w jednym miejscu, do końca jednostki wnętrza stringów mylone z zewnętrzem).

0

Wybacz @pawelfrankowski ale delikatne mówienie do Ciebie nie dochodzi. Zrozum, że taki wielki if nie jest elegancki. Jakbym w swoim zespole spotkał takie coś, to zakazałbym jego twórcy dożywotnio używania IDE. Dlatego poradziłem Ci wydzielić tego if'a do nowej funkcji. W takiej funkcji można to załatwić ładniej. Dodatkowo kod się upraszcza i jest bardziej opisujący sam siebie. Bo sensownie nazwaną metodą ładnie Ci pokaże co robi. A tu masz pierdyliard warunków i pytanie co ten if robi.

Poza tym naprawdę zaszywasz takie rzeczy w programie? I przy każdej nowej możliwości kompilujesz całość? Takie rzeczy robi się uniwersalnie, a wyciąga do parametrów. Niech siedzi w jakimś pliku tekstowym, czy nawet w tabeli w bazie danych, gdzie każda możliwość to nowy rekord. Tylko niech nie będzie zaszyte w programie. Świadomy klient nie kupi Ci takiego produktu, bo sam będzie chciał konfigurować automaty dekretujące czy tam rozpoznające numery dokumentów. A latanie z każdą pierdółką do producenta na kilometr leci słabą próbą uwiązania klienta do producenta, a potem ściągania $ za najdrobniejsze doróbki.

I na koniec nie zwalaj winy na kompilator, bo nawet nie wiesz dokładnie w której linii masz błąd. Pokazałeś tylko tego if'a. Zacznij debuggować, a może się okazać, że błąd leży gdzieś dalej już po wykonaniu tego if'a. Jak chcesz pomocy to pokaż minimalny kompilujący się kod (np. tak jak to zrobiłem ja w swoim poprzednim poście) wraz z zestawem parametrów wejściowych, a może coś uda się zrobić. Chociaż nie sądzę, żeby ilość warunków w instrukcji if miała cokolwiek tu do rzeczy. Ja pisząc poprzedniego posta wraz z pokazaniem jak można to trochę uczłowieczyć nie miałem żadnych problemów z takim rozbudowanym if'em. Ale rozumiem mam innego Windows'a czy tam inne parametry limitów pamięci...

0

Sprawdzę czy błąd polega na przekroczeniu limitu ilości argumentów w wyrażeniu logicznym instrukcji warunkowej.
Może być za duża ilość wariatów alternatywy logicznej w jednej instrukcji warunkowej, tak jak mi koledzy podpowiadacie.
Zwrócił mi na to uwagę pan Bogdan Polak z BSC Polska, że chodzi o przekroczenie limitu dla kompilatora.
Dziękuje za rady i dam znać gdy to sprawdzę.
Natomiast wyświetlanie nieadekwatnego komunikatu jako błąd krytyczny floating point invalid operation
jest rzeczywiście błędem kompilatora i pan Bogdan Polak się z tym zgadza.

Pozdrawiam
Paweł Frankowski

1 użytkowników online, w tym zalogowanych: 0, gości: 1