Delphi - drukarka Novitus i ActiveX OICFiscalPrinter

0

Witam,

czy ktoś korzystał może z TOICFiscalPrinter do obsługi drukarki fiskalnej Novitus Online w Delphi i spotkał się może z dziwnym błędem AV przy zamykaniu aplikacji po tym jak stworzy się obiekt:

FP:=TOICFiscalPrinter.Create(nil);

Może to coś komuś podpowie..

screenshot-20241029000525.png

0

Podejrzewam że przed zamknięciem apki musisz wywołać coś takiego jak free czy close.
Choć ze screena wygląda że problem leży zupełnie w innym miejscu.

0

dzięki ale to na pewno nie to, generalnie tutaj wskazuje na jakiś timer..ale ja nie widzę, żeby ten obiekt w ogóle był używany w jakimś timerze. Raczej liczę, że może ktoś używał tej samej biblioteki :) W świeżym projekcie utworzenie i zamknięcie nie powoduje błędu, może to kwestia jakiś ustawień kompilatora nawet :/ ciężko stwierdzić bo nie przenosi też oczywiście do fragmentu w kodzie w czasie debugowania.

1
Stl86 napisał(a):

czy ktoś korzystał może z TOICFiscalPrinter do obsługi drukarki fiskalnej Novitus Online w Delphi i spotkał się może z dziwnym błędem AV przy zamykaniu aplikacji

Ja korzystałem dla Novitus Delio Prime, aczkolwiek nie miałem błędów przy zamykaniu.

Przy zamykaniu aplikacji robiłem:

if FP <> nil then
begin
  FP.Close;
  FreeAndNil(FP);
end;
0

Nie znam tego API, ale czy parametrem konstruktora TOICFiscalPrinter.Create jest zwyczajny AOwner?

Jeśli tak, to zamiast podawać w nim nil, możesz podać jakiegokolwiek rodzica (formularz, albo i nawet Application) i taka instancja zostanie automatycznie zwolniona w destruktorze ownera, więc nie tylko zostanie wywołana raz, ale i nie będziesz musiał sam tego robić. Nie jestem zwolennikiem takich automatów (wolę sam alokować i dealokować), ale rób jak Ci wygodniej.

Przy czym zwróciłbym uwagę na to co napisał @Drunky, czyli na wywołanie metody Close przed Free. Choć obstawiam, że destruktor tej klasy sam woła Close, jeśli drukarka jest ”otwarta” w trakcie niszczenia jej instancji.

Natomiast aby znaleźć miejsce wystąpienia błędu AV, odpal aplikację pod debuggerem, do tego skorzystaj z narzędzi do analizowania pamięci, które oferuje Delphi. Gdzieś w kodzie jest odwołanie albo do znilowanego pointera, albo do nieprzydzielonej pamięci i stąd wyjątek.

0

Próbowałem z ownerem też, właśnie to jest bardzo dziwna sytuacja tym bardziej, że to się pojawia nawet jeśli drukarka nie zostanie użyta w żaden sposób. Ja mam obawy, że problem jest w czymś innym niż sam ten obiekt :/

Zabawne jest to, że gdy przechodzę metodę Closę pod debuggerem , przechodząc linijka po linijce to się zamyka bez błędu..

0

Daj cały call stack

1
Stl86 napisał(a):

Ja mam obawy, że problem jest w czymś innym niż sam ten obiekt :/

Jak widać na zrzucie, błąd występuje w metodzie TTimer.WndProc, a więc wygląda na to, że wywala się kod timera, który odpowiedzialny jest za przechwytywanie zdarzeń WM_TIMER i odpalanie zdarzenia OnTimer. Więcej informacji jest tutaj — https://docwiki.embarcadero.com/CodeExamples/Alexandria/en/TTimerFWindowHandle_(Delphi). Jest tam nawet kod, który pokazuje jak to działa (usunąłem komentarze):

procedure MyTTimer.WndProc(var Msg: TMessage);
begin
  with Msg do
    if Msg = WM_TIMER then // Check for timer messages.
      try
        Timer; // This calls the OnTimer event handler.
      except
        Application.HandleException(Self);
      end
    else
      Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);
end;

Tak więc albo wywala się kod timera, albo kod zdarzenia OnTimer.

Edit: tak szczerze pisząc, gdyby błąd dotyczył zdarzenia OnTimer, to byłoby ono widoczne w callstacku (wywołanie metody Timer też), a nie jest. Coś w tej metodzie odwołuje się do nieprzydzielonej lub już zwolnionej pamięci. Jedyne co mi przychodzi do głowy to to, że metoda WndProc lub Timer została wywołana na już zwolnionej instancji klasy TTimer. Ale w to trochę mi trudno uwierzyć, bo zniszczenie instancji timera wyrejestrowuje uchwyt okna (który timer sobie rejestruje w swoim konstruktorze), a więc po zniszczeniu timera ten uchwyt już nie istnieje (zostaje wyrejestrowany), więc system nie wyśle do niego komunikatu (czyli metoda WndProc się nie wykona).

0

@hzmzp

:01231695 TTimer.WndProc + $61
:010d57be StdWndProc + $16
:7693171b user32.AddClipboardFormatListener + 0x4b
:76927e8a ; C:\WINDOWS\SysWOW64\user32.dll
:76927a8a ; C:\WINDOWS\SysWOW64\user32.dll
:7692bf5f ; C:\WINDOWS\SysWOW64\user32.dll
:778154bd ntdll.KiUserCallbackDispatcher + 0x4d
:76925f8c user32.PostMessageW + 0x9c
:76926ba6 ; C:\WINDOWS\SysWOW64\user32.dll
:1e5a1f4a ; C:\Windows\SysWOW64\OICFiscalPrinterLib.ocx
:7693171b user32.AddClipboardFormatListener + 0x4b
:76927e8a ; C:\WINDOWS\SysWOW64\user32.dll
:76928da9 ; C:\WINDOWS\SysWOW64\user32.dll
:7692618d user32.SendMessageW + 0x4d
:1e5a7faa ; C:\Windows\SysWOW64\OICFiscalPrinterLib.ocx
:1e31abdd ; C:\Windows\SysWOW64\OICFiscalPrinterLib.ocx
:77812f96 ; 
:777ede52 ntdll.RtlActivateActivationContextUnsafeFast + 0xe2
:777fdba6 ; 
:777fda25 ntdll.RtlExitUserProcess + 0xb5
:756f6a53 KERNEL32.ExitProcess + 0x13
:00ffc8a6 @Halt0 + $116
:756efcc9 KERNEL32.BaseThreadInitThunk + 0x19
:7780809e ; 
:7780806e ntdll.RtlGetAppContainerNamedObjectPath + 0xee

0

Dzięki wszystkim za sugestie. Generalnie wyszło mi, że ten ActiveX gryzł się z jakimś timerem/wątkiem.. trudno powiedzieć bo nie udało mi się znaleźć bo sam w sobie też nasłuchiwał zdarzeń. Być może dałoby się to jakoś inaczej załatwić, gdzieś poczekać dłużej przy zamykaniu itd, ale ja zdecydowałem się na stworzenie tego obiektu w oddzielnym wątku i dać do niego dostęp z głównego i wygląda, że to załatwiło sprawę. Funkcjonalność zachowana, błędu przy zamykaniu nie ma.

1

od 15 lat korzystam z tego activeX z tym że ja nie tworzę obiektu a po prostu kładę komponent na formie.
Otwieram przez FP.Open a kończąc aplikację zamykam FP.Close.

Nie zauważyłem w tym czasie żadnych błędów a paragonów moje aplikacje wydrukowały ładnych parę milionów (a może więcej).

Według mnie drukowanie paragonów w osobnym wątku to nie jest dobry pomysł, jednak musisz mieć pełną wiedzę w jakim stanie jest urządzenie, czy na pewno paragon się wydrukował, czasami należy poczekać na założenie nowego papieru lub jakąś interakcję człowieka z drukarką itp. itd. Korzystanie z wątków może utrudniać przepływ tych informacji.

0

@robertz68 dzięki ale w tym przypadku nie ma różnicy czy to komponent położny na formę czy utworzony obiekt, problem jest taki sam. To jest akurat podstawowa obsługa tylko i na pewno nie żadna masowa produkcja paragonów. Ogólnie chodzi o użycie Novitus Online przez WiFi. Aplikacja obsługuje tez inne rodzaje drukarek, wiem, że lepiej by było używać tego w głównym wątku, w nowym projekcie oczywiście nie ma żadnych problemów z tym obiektem przy zamykaniu..ale niestety w tym z czymś się to gryzie. Nie mogę tego zlokalizować. To jest duża i wieloletnia aplikacja, gdzie nagle zaszła potrzeba obsługi tego Novitusa. Zdawałoby się, że prosta sprawa, obiekt fajnie spełnia swoje zadanie.. no ale ten błąd :/ To nawet z tym błędem (tylko przy zamykaniu aplikacji) sobie działa, drukuje..itd.
Mnie dziwi, że wystarczy utworzenie samego obiektu, nie trzeba nawet nic drukować, otwierać połączenia czy cokolwiek i już przy zamykaniu jest ten błąd. Ale jak się już debuguje zamykanie i robie step over manualnie do samego zamknięcia aplikacji to nie ma bledu..

0

@Stl86: call stack pokazuje, że problem tkwi w kodzie timera. Może zamiast skupiać się na obsłudze drukarki, skup się na tych timerach — sprawdź jakie timery używane są w tym projekcie, do czego służą, co wykonują i jak są finalizowane.

Poza tym ta wielowątkowość zdaje się być nie tylko zbędna, ale i szkodliwa. Zasada jest prosta — jeśli coś może działać w ramach głównego wątku, to powinno w nim działać, a z wątków korzysta się wyłącznie wtedy, gdy jest to absolutnie konieczne (czyli nie można się obejść bez nich).

0

@flowCRANE no tak tylko w call stacku widac tez ze jest zaangazowana akurat wtedy ta kontrolka.. w oogle to nawet nie chodzi o moment zamykania aplikacji, po prostu nawet jak
zrobię lokalna zmienna tej klasy , stworze ja i zaraz zniszczę, potem coś tam sobie porobię w programie to już wystarczy żeby przy zamknięciu wywaliło ten sam błąd co w 1 poście opisałem. Drukarka nawet nie jest podłączona.. leży w pudełku :)

1

Wiesz, tak bez kodu to sobie możemy strzelać...

0

No tak dlatego pytam czy może, ktoś się spotkał z czymś podobnym. Skoro nie;) to oznacza, że to wina leży w samej aplikacji i zapewne jakimś innym elemencie. Nie ma szans tu dać kodu. Od strony samego activeX sa 2 linijki. Wystarczy stworzyć i zniszczyć i juz.. jest problem przy zamykaniu. A reszta kodu.. to nie ma szans na wstawienie. To nie jest jakaś mała aplikacja tylko wielka , wieloletnia. Ja próbuje sobie to badać ale póki co bez jakiegoś sukcesu. Tak czy inaczej dzięki za rady.

1

Zwykle, jeśli w dużym projekcie ma się z czymś poważny problem, to się ten problem izoluje od niego i testuje, a więc… Czy próbowałeś wykonać test na pustej aplikacji? Stwórz pusty projekt, a w nim dodaj tylko ten kod tworzący i niszczący drukarkę. Jeśli problem nadal będzie istnieć, to przynajmniej będziesz miał pewność, że za to nie jest odpowiedzialny kod Twojego projektu.

0

@flowCRANE , robiłem to. W pustym projekcie nie ma problemu. Dodawałem też do pustego projektu timery i też nie ma problemu. To pewnie coś bardziej pokręconego..

0

Czyli wychodzi na to, że problem sprawia konkretnie kod Twojego projektu. Obstawiam, że jego kod jest własnościowy (zamknięto-źródłowy) i dlatego nie możesz pokazać źródeł. I tu jest problem, bo bez źródeł i konkretniejszej znajomości tego jak ten projekt działa, niewiele można Ci doradzić.

0

@flowCRANE dokładnie, ale też pokazanie kodu... to uwierz mi ze wiele godzin analiz by ktoś potrzebował. Używane tu są inne komponenty, zewnętrznych firm , rozne dllki itd itd. Pytałem w nadziei :) ze może ktoś się spotkał. Dziwi mnie to ze przechodząc debuggerem zamykanie aplikacji nie ma błędu..czyli jakby "reczne" opóźnienie całego procesu zamykania pomagało..

0

A jak to wygląda w release? Czy ten błąd powoduje np. pojawienie się systemowego okienka z błędem albo coś w tym stylu? Czy po prostu aplikacja zamyka się poprawnie, nie gubi żadnych danych itd. i nie powoduje szkód?

0

W release nie ma nic, żadnego komunikatu, nic to nie przeszkadza. Ja myślę, że to jest coś nieistotnego.. no ale to lipa taki błąd zostawić. W tym wątku przynajmniej nie widać ;P
Ale ten wątek to jest tak, że ja tam tworze tylko statyczny obiekt ( jako class var) i to wszystko, tam nic więcej się nie dzieje, a w głównym wątku sobie przypisuje go zmiennej i tyle i używam tak samo jakbym stworzył w głównym wątku.

3
Stl86 napisał(a):

@flowCRANE Dziwi mnie to ze przechodząc debuggerem zamykanie aplikacji nie ma błędu..czyli jakby "reczne" opóźnienie całego procesu zamykania pomagało..

i tu jest zapewne sedno twojego problemu. Sam piszesz że aplikacja używa dll-ek itp. Ja właśnie obstawiam na czas potrzebny na zamknięcie wszystkich otwartych elementów aplikacji. Po prostu tego czasu nie wystarcza.

Podobną sytuację miałem z aplikacją używającą sfery Subiekta. Taki program startując uruchamia w tle Subiekta (a to trwa), następnie przy kończeniu aplikacji Subiekt jest zamykany i to trwa jeszcze dłużej. Wymyśliłem sobie że z różnych względów raz na dobę będę restartował program i zrobiły się problemy. Na moim szybkim komputerze problemu nie zauważyłem ale u klienta na kilkuletnim serwerze z macierzą dysków HDD SAS wydajność tego systemu nie wystarczyła aby zamknąć Subiekta przed ponowną jego próbą uruchomienia.

Tak więc przyjrzyj się tej kwestii albo jakoś sztucznie opóźnij zamykanie aplikacji.

0

Wydaje mi się, że rozwiązałem ten problem, przejrzałem jeszcze kwestie niszczenia roznych obiektow i wydaje mi sie ze to nie bylo dopilnowane, w tym timery tworzone dynamicznie (chociaz robilem taki test na czystym projekcie i nie było błędów). W każdym razie poprawiłem to masowo wszystko w desktruktorach klas i wydaje sie ze to załatwiło sprawę. Dziękuję wszystkim za sugestie i chęć pomocy.

2

W razie czego, pamiętaj, że nawet jeśli zapomnisz zwolnić dynamicznie stworzonych obiektów, to nic się nie stanie, dlatego że system operacyjny posprząta wszystko po tym, gdy proces zakończy działanie — zwolni całą zarezerwowaną przez niego pamięć (ewentualnie łącznie z tą, która wyciekła), pozamyka niezamknięte uchwyty i zwolni wszelkie inne niesfinalizowane zasoby. Robi to i musi to robić, aby utrzymać swoją stabilność.

Tak więc brak jawnego zwolnienia dynamicznie zaalokowanych zasobów poważnym problemem nie jest, a jedynie to, że w trakcie zamykania procesu mogą próbować jeszcze robić coś, co ostatecznie doprowadza do błędu/wyjątku. Skup się na razie na tym, aby tego nie robiły (czyli wyklucz pojawianie się błędu w debugu), natomiast niezwolnione zasoby zawsze możesz łatwo namierzyć za pomocą dedykowanych narzędzi i dodać kod ich finalizacji.

0

Tak wiem ale jednak etyka pracy nie pozwala mi zostawić takiego błędu. Ale już jest OK. Obiekt tworzony w wątku głównym, drukarka drukuje, błędu przy zamykaniu nie ma. Dzięki @flowCRANE i @robertz68. Myślę, że możemy zakończyć wątek :)

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.