Umíme to s Delphi: 65. díl – Delphi a zprávy systému Windows, 2. část

Diskuze čtenářů k článku

Jan Fiala  |  09. 12. 2002 07:23

Opět se musím ozvat. Vzhledem k tomu, že je to kurz pro začínající, není moc dobré ukončovat aplikaci pomocí Application.Terminate tam, kde stačí obyčejné Close.
Terminate by se melo použít pouze ve vyjímečných případech, protože aplikace se ukončí okamžitě a neprovede se "uklízecí kód" aplikace. Takže se třeba neuvolní zdroje, které aplikace alokovala apod.
Ukončování pomocí Terminate by rozhodně nemělo být pravidlem.

Souhlasím  |  Nesouhlasím  |  Odpovědět
shocquer  |  09. 12. 2002 09:03

Hm, to mi pripomina kod, kde vsade pouzivali ludkovia namiesto free destroy a potom prisli same chyby - namiesto jednej rozumnej este par nechutnych a zbytocnych. uz viem kde na to chodia
*Sho*

Souhlasím  |  Nesouhlasím  |  Odpovědět
Václav Kadlec  |  09. 12. 2002 10:41

...Application.Terminate je zbytečně silným kanónem. Jeho používání není vůbec dobrým zvykem

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tytyk  |  09. 12. 2002 16:20

1. Mohu se zeptat, jaké alokované zdroje nejsou uvolněny?

2. Při ukončování aplikace pomocí Close je voláno Application.Terminate, takže jediný faktický rozdíl spočívá v tom, že Close ještě vyvolá obsluhu OnClose.

3. Pro začátečníky: Close používejte pro uzavření formuláře a nenechte se zmást - ukončení pomocí Application.Terminate je po všech stránkách zcela správné a nejde tedy o násilné přerušení aplikace (pro bližší informace si stačí přečíst nápovědu a prohlédnout pár ukázkových příkladů přímo z CD Delphi).

Souhlasím  |  Nesouhlasím  |  Odpovědět
Jan Fiala  |  10. 12. 2002 06:23

Správně. V Close se volá Application.Terminate, ale neplatí to naopak ! Takže pokud se zavolá Application.Terminate, nezavola se Close ani CloseQuery. Dokonce se nezavola ani kod, uvedeny v OnDestroy hlavniho formuláře. proto jsem mluvil o uvolňování zdrojů, protože pokud si v aplikaci něco dynamicky vytvářím, taky to po sobě uklízím a většinou v OnDestroy.
Prostě se projdou všechny procesy aplikace, ty se ukončí a ukončí se aplikace.
Pokud budete mít v OnClose ukládání parametrů nebo nastavení programu, prostě se to neprovede.

Application.Terminate slouží k nouzovému ukončení aplikace při nějaké závažné chybě, ne k obvyklému ukončení, pokud uživatel stiskne v menu Konec

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tytyk  |  10. 12. 2002 16:09

Špatně.

1. OnDestroy je voláno u každého formuláře, který je ve chvíli volání Application.Terminate v paměti, takže všechny zdroje alokované dynamicky při vytváření formulářů (v OnCreate) jsou korektně uvolněny.

2. OnClose a OnCloseQuery se samozřejmě nezavolá, protože Application.Terminate nezavírá okno, ale ukončuje aplikaci.

3. Závěrem: Souhlasím s Vámi, že tam, kde není nutné použít Application.Terminate, je ukončení pomocí Close správným řešením a nikdy bych netvrdil opak. Příspěvkem jsem chtěl poukázat na to, že začátečníci mohou s klidným svědomím pro ukončení aplikace využít Application.Terminate a všechny zdroje budou korektně uvolněny. Jde o normální ukončení programu s tím rozdílem, že není vyvolána událost OnClose (OnCloseQuery) hlavního formuláře (to jsem ale už napsal v předchozím příspěvku).

Souhlasím  |  Nesouhlasím  |  Odpovědět
Jan Fiala  |  10. 12. 2002 18:39

Dobře, zkusme jednoduchý příklad. Jeden formulář, tlačítko do jeho OnClick dáme Application.Terminate.
Do OnDestroy formuláře dejme ShowMessage('Destroy');
Zkuste si to spustit a kliknout na tlačítko. Pokud, jak píšete se volá OnDestroy, měl by se ukázat dialog. Ukáže se ? Neukáže. To znamená, že se ani nevykoná kód, který je v události. A tím pádem se neprovede ani můj uklidový kód.
Uklidí se pouze objekty, které měly jako vlastníka Form. Ale objekty, které vlastníka nemají (nevizuální objekty, jako seznamy apod.) je třeba uklízet ručně. A v případě Terminate není kde.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tytyk  |  10. 12. 2002 19:54

Špatně.

Váš jednoduchý příklad nefungoval, protože ani fungovat nemohl. Vaše poslední věty rozhodně nejsou pravdivé a mohly by začátečníka značně zmást! Usuzovat podle zobrazení/nezobrazení dialogu ShowMessage, zdali se událost provede/neprovede není příliš vhodné, zkuste aplikaci krokovat.

Zkuste dále v obsluze OnDestroy funkci Application.MessageBox. Proč "nefunguje" ShowMessage? Odpověď najdete např. po podrobném prostudování zdrojového kódu (jednotka Dialogs).

Souhlasím  |  Nesouhlasím  |  Odpovědět
Jan Fiala  |  11. 12. 2002 05:48

Mate pravdu, dival jsem se do zdroju, ten priklad byl nevhodny. OnDestroy se provede.
Kazdopadne trvam na tom, ze Application.Terminate neni kod pro obvykle ukoncovani aplikace a v dokumentaci nebo helpu + prikladech se uvadi jako reakce na vyjimku.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tytyk  |  12. 12. 2002 08:31

Nemáte pravdu!

Zřejmě jste si neprohlédl všechny vzorové příklady. Zkuste se podívat např. na Demos\Football\Main.pas (Delphi 7). Že by autoři neuměli normálně ukončit program? Nebo snad chcete tvrdit, že nejde o ZCELA NORMÁLNÍ ukončení aplikace pomocí Application.Terminate? Jistě, Close by tam být mohlo, v tom se asi shodneme. Protože ale aplikace nepotřebuje obsluhovat OnClose, je zcela normálně ukončena pomocí Application.Terminate a každý programátor (i začátečník) při krátkém pohledu na tento řádek jasně vidí, že je zde normálně ukončena aplikace a ne pouze zavřeno okno.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Jan Fiala  |  13. 12. 2002 10:22

Pokud tvrdíte, že to jak jsou napsana Dema a jak je napsano VCL je svaté a bez chyb, tak k tomu není co říct. Stačí se podívat do některých zdrojů VCL, které by kompletní přepsání zasloužily už dávno.

A to, že autoři umí programovat ? Jak kde a jak co. stačí se podívat na Database Desktop, ImageEditor a pár dalších aplikací, které jsou součástí Delphi.
Jen takový ImageEditor, který se nezměnil od verze D3 (starší jsem neviděl) by si zasloužil kompletně přepsat, aby uměl pracovat aspoň s 256 barvami.

Co se týká dem, které jsou součástí Delphi, většina z nich nemá s autory Delphi nic společného a jsou sesbírány od různých autorů.
Schválně si zkuste vyhledat ve zdrojích Application.Terminate a podívejte se na příklady a četnost použití.
To, že je to uvedeno v jednom demu neznamená, že je to správně a závazné. Ale ani to neznamená, že je to špatně. To jsem netvrdil. pouze tvrdím, že pravidelné používání Terminate místo Close hlavního formuláře může začátečníkovi způsobit více problémů než Close, až se bude divit, proč se mu nevykoná kód třeba v OnClose.

Já osobně Terminate také používám, ale ne v případě, kdy si v menu kliknu na Konec.

Pár příkladů, kde bych použil Terminate:
- v OnCreate hlavního formuláře, pokud existuje nějaký vážný problém, kvůli kterému nemůžu aplikaci spustit
- v ošetření vyjímky
- při zjišťování existence již běžící instance (přímo v DPR)
- tam, kde nemám formulář

V ostatních případech používám důsledně Close. Ale to je skutečně individuální věc každého programátora, stejně jako styl psani apod.
Při použití Terminate si musí být vědom následků.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tytyk  |  13. 12. 2002 16:01

"Pokud tvrdíte, že to jak jsou napsana Dema a jak je napsano VCL je svaté a bez chyb"  Kdo to tvrdí?

"A to, že autoři umí programovat?"  Můj názor je ten, že programovat umí. Jestli Vám z uvedených příkladů vyplývá, že tomu tak není, je to Váš názor. O tom ale diskuse není.

"To, že je to uvedeno v jednom demu neznamená, že je to správně a závazné. Ale ani to neznamená, že je to špatně. To jsem netvrdil."  Já s Vámi plně souhlasím. Pokud tedy netvrdíte, že je to špatně, pak to asi bude dobře. Podle Vás je to tedy dobře a evidentně se nejedná o "přerušení" programu, ale o jeho normální ukončení. Pak mi promiňte, ale dost silně si protiřečíte.

"pravidelné používání Terminate místo Close hlavního formuláře může začátečníkovi způsobit více problémů než Close, až se bude divit, proč se mu nevykoná kód třeba v OnClose". Až se začátečník bude při ladění divit, proč se nevykoná kód v OnClose, patrně se podívá, kde se aplikace ukončuje. Uvidí-li Application.Terminate, bude mu hned vše jasné (předpokládám, že samozřejmě ví, jak která metoda funguje, protože bez znalosti funkce použitých metod nemůže programovat). Souhlasím s Vámi, že toto by si mohl ušetřit tím, že by aplikaci uzavřel Close.

Zaujalo mě např. použití Application.Terminate tam, kde nemáte formulář. Ne, že by tyto aplikace byly časté, ale jak je potom normálně ukončujete? Zřejmě Application.Terminate. Můžete to nazývat klidně "přerušení aplikace", já to nazývám "normální ukončení". A o tom to je.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tytyk  |  13. 12. 2002 16:01

"Pokud tvrdíte, že to jak jsou napsana Dema a jak je napsano VCL je svaté a bez chyb"  Kdo to tvrdí?

"A to, že autoři umí programovat?"  Můj názor je ten, že programovat umí. Jestli Vám z uvedených příkladů vyplývá, že tomu tak není, je to Váš názor. O tom ale diskuse není.

"To, že je to uvedeno v jednom demu neznamená, že je to správně a závazné. Ale ani to neznamená, že je to špatně. To jsem netvrdil."  Já s Vámi plně souhlasím. Pokud tedy netvrdíte, že je to špatně, pak to asi bude dobře. Podle Vás je to tedy dobře a evidentně se nejedná o "přerušení" programu, ale o jeho normální ukončení. Pak mi promiňte, ale dost silně si protiřečíte.

"pravidelné používání Terminate místo Close hlavního formuláře může začátečníkovi způsobit více problémů než Close, až se bude divit, proč se mu nevykoná kód třeba v OnClose". Až se začátečník bude při ladění divit, proč se nevykoná kód v OnClose, patrně se podívá, kde se aplikace ukončuje. Uvidí-li Application.Terminate, bude mu hned vše jasné (předpokládám, že samozřejmě ví, jak která metoda funguje, protože bez znalosti funkce použitých metod nemůže programovat). Souhlasím s Vámi, že toto by si mohl ušetřit tím, že by aplikaci uzavřel Close.

Zaujalo mě např. použití Application.Terminate tam, kde nemáte formulář. Ne, že by tyto aplikace byly časté, ale jak je potom normálně ukončujete? Zřejmě Application.Terminate. Můžete to nazývat klidně "přerušení aplikace", já to nazývám "normální ukončení". A o tom to je.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Jan Fiala  |  11. 12. 2002 06:00

Takže, abychom to shrnuli a zabránili matení začínajících programátorů.

Mějme typickou aplikaci, která v:

  • OnCloseQuery zjistí změny a zobrazí dialog s dotazem na uložení změn a změny v závislosti na odpovědi z dialogu uloží.
  • OnClose bude tato aplikace ukladat nastaveni aplikace, pozici a velikost okna
  • OnDestroy provede uklízecí kód


    Já pro ukončení aplikace zavolám Close, vy zavoláte Application.Terminate, protože, jak píšete, je to obvyklý způsob ukončení aplikace (viz váš příspěvek výše)

    A co se stane ?
    Moje aplikace se zeptá na uložení změn, uloží nastavení, provede úklid.
    Vaše aplikace se ukončí a změny jsou bez dotazu ztraceny, nedošlo ani k uložení nastavení. Pouze se provedl úklidový kód.

    Souhlasím  |  Nesouhlasím  |  Odpovědět
  • Tytyk  |  12. 12. 2002 08:42

    1. Nikdy jsem netvrdil, že obvyklý způsob je používat Application.Terminate, tak to prosím nepište! Tvrdím, že autor článku použil zcela korektně normální ukončení programu voláním této metody.

    2. Osobně, pokud mohu volit mezi Close a Terminate, využiji Application.Terminate a to z toho důvodu, že je kód průhlednější. Dle mého názoru je po objektové stránce ukončení programu voláním metody Terminate globálního objektu rozhodne čistější, než vyvolaní metody Close hlavního formuláře.

    3. Ve Vašem případě bych samozřejmě použil Close, protože potřebuji provést obsluhu OnClose, na tom se teď doufám shodnem.

    4. Uvědomte si prosím, že ukončení pomocí Application.Terminate je zcela "stejně normální" jako pomocí Close.

    Souhlasím  |  Nesouhlasím  |  Odpovědět
    Jan Fiala  |  11. 12. 2002 05:51

    V nápovědě se Terminate používa jako reakce na vyjímku, tedy ne jako normální ukončení aplikace. Stačí si prohlédnout pár ukázkových příkladů...

    Souhlasím  |  Nesouhlasím  |  Odpovědět
    Tytyk  |  12. 12. 2002 08:47

    Zkuste si projít nápovědu Delphi znovu. Cituji k proceduře Halt (nechci komentovat Halt, pouze ji označím za "abnormal termination of a program"): "To perform a normal termination of a Delphi application, call the Terminate method on the global Application object."

    Souhlasím  |  Nesouhlasím  |  Odpovědět
    Jan Fiala  |  13. 12. 2002 09:54

    A zkuste se podivat přímo na nápovědu k TApplication.Terminate a na příklad. Je tam přímo příklad obvyklého použití Terminate

    Samozřejmě, že doporučují místo Halt použít Terminate, protože se navíc provede OnDestroy (měl jste pravdu). Ale obě slouží k ukončení aplikace při nějakém nepředvídaném stavu.
    Význam terminate v Delphi je spíš přerušení než ukončení a o tom je asi celá tato debata.

    Jde o to, zda chcete přerušit běh programu - v tom případě použiji Terminate nebo program ukončit - pak přichází ke slovu Close.

    Souhlasím  |  Nesouhlasím  |  Odpovědět
    Tytyk  |  13. 12. 2002 16:08

    Nemáte pravdu!

    Nebalamuťte nás, i v případě Halt se přece provede obsluha OnDestroy!

    Vy trváte na tom, že Application.Terminate je přerušení programu a že slouží k ukončení aplikace při nějakém nepředvídatelném stavu. Zároveň ale pomocí tohoto příkazu aplikaci zcela normálně ukončujete jak Vy (viz Vaše příklady), tak Borland. Pokud Vám dám jeden příklad za všechny, napíšete jinými slovy něco o tom, že VCL chce celé přepsat a že dema nemají s Borlandem nic společného. Závěrem řeknete, že netvrdíte, že by v příkladu ukončení aplikace bylo špatně a začnete nazývat Application.Terminate výrazem přerušení programu. Výsledkem je pouze to, že se díky své neznalosti dopouštíte řady jasných chyb a přitom narážíte na programátorské schopnosti ostatních autorů. Má to celé nějaký rozumný smysl?

    Souhlasím  |  Nesouhlasím  |  Odpovědět
    Jan Fiala  |  16. 12. 2002 16:31

    Nemáte pravdu.
    Vy jste uvedl jeden příklad ze všech dem, ktere tam jsou, kde se používá Terminate, na rozdíl od ostatních (je to i v dalších, ale to nejsou formulářové aplikace) a podle toho je podle vas použití běžné.
    Nemluvil jsem o přepsání celé VCL ! Stačí se podívat do toho původního příspěvku. Mluvil jsem o přepsání některých částí.

    Co se týka Halt, nahlédneme do helpu a dozvíme se:
    HALT initiates abnormal termination of a program.
    Halt se používal na ukončení v konzolových aplikacích. Rozhodně bych jej nepoužil ve "formulářové" aplikaci, i když by se asi nic nestalo.

    To že mi vyčítáte, ze někdy Terminate použiji to není moc pěkné. Vy naopak někdy použijete Close. Viz příspěvek nahoře. A viz příklady, kdy bych Terminate použil.

    Celá debata asi skutečně nemá smysl.



    Zkusme to nějak shrnout, abychom se domluvili.

    1) Terminate i Close provede regulerní ukončení aplikace.
    2) Při použití Terminate se neprovede kód v OnCloseQuery a OnClose, a toho si musí být autor programu vědom

    Bod 2) je důvod, proč celá debata začala. Uznávám, že jsem neměl v některých případech pravdu (OnDestroy apod.)

    A teď se zkuste vžít do role začátečníka, který se učí Delphi podle serálu na Živě.CZ. Co se dozví ? Uvidí, že k ukončení aplikace se používá Application.Terminate. Zafixuje si to a bude to důsledně používat.
    Pak se někde jinde dozví, že pokud potřebuje dotaz na uložení souboru při ukončení aplikace, přidá jej do OnCloseQuery. Ale nedojdou mu souvislosti, protože do toho tak moc nevidí. A aplikaci vesele ukončí pomocí Terminate.
    A pak přijde na řadu poradna. Bude se ptát, proč mu nechodí dotaz v OnCloseQuery, uvede i obsah této události, který je v pořádku. A nikdo mu nebude schopen poradit, protože nikoho nenapadne, že na uzavření aplikace použil Terminate. A tak bude nadávat, že celé slavné Delphi stojí za h...
    A proč to všechno nastalo ?
    Kvůli jednomu nevhodnému použití Application.Terminate, které si zafixuje jako ukončování aplikace.

    Souhlasím  |  Nesouhlasím  |  Odpovědět
    Zasílat názory e-mailem: Zasílat názory Můj názor