De donkere kant van de applicatie. ProcessMessages

click fraud protection

Artikel ingediend door Marcus Junglas

Bij het programmeren van een gebeurtenishandler in Delphi (zoals de Bij klikken gebeurtenis van een TButton), komt er het moment dat uw toepassing een tijdje bezig moet zijn, b.v. de code moet een groot bestand schrijven of sommige gegevens comprimeren.

Als je dat doet, zul je dat merken uw toepassing lijkt te zijn vergrendeld. Uw formulier kan niet meer worden verplaatst en de knoppen vertonen geen teken van leven. Het lijkt te zijn gecrasht.

De reden is dat een Delpi-toepassing een enkele thread heeft. De code die u schrijft, vertegenwoordigt slechts een aantal procedures die door de hoofdthread van Delphi worden opgeroepen telkens wanneer zich een gebeurtenis voordoet. De rest van de tijd is de hoofdthread systeemmeldingen en andere dingen zoals vorm- en componentafhandelingsfuncties.

Dus als u de afhandeling van uw evenement niet afmaakt door wat lang werk te doen, voorkomt u dat de applicatie die berichten afhandelt.

Een gebruikelijke oplossing voor dit soort problemen is om 'Toepassing' te noemen. ProcessMessages ". "Toepassing" is een wereldwijd object van de klasse TApplication.

instagram viewer

De applicatie. Processmessages verwerkt alle wachtende berichten zoals vensterbewegingen, klikken op knoppen, enzovoort. Het wordt vaak gebruikt als een eenvoudige oplossing om uw applicatie "werkend" te houden.

Helaas heeft het mechanisme achter "ProcessMessages" zijn eigen kenmerken, wat grote verwarring kan veroorzaken!

Wat doet ProcessMessages?

PprocessMessages verwerkt alle wachtende systeemberichten in de berichtenwachtrij van de applicatie. Windows gebruikt berichten om met alle actieve toepassingen te "praten". Gebruikersinteractie wordt via berichten naar het formulier gebracht en "ProcessMessages" verwerkt ze.

Als de muis bijvoorbeeld op een TButton valt, doet ProgressMessages alles wat er op dit evenement zou moeten gebeuren, zoals de overschilder de knop naar een "ingedrukte" status en, natuurlijk, een aanroep van de OnClick () -behandelingsprocedure als u er een hebt toegewezen.

Dat is het probleem: elke aanroep naar ProcessMessages kan opnieuw een recursieve aanroep naar een gebeurtenishandler bevatten. Hier is een voorbeeld:

Gebruik de volgende code voor de OnClick even-handler van een knop ("werk"). De for-statement simuleert een lange verwerkingsopdracht met af en toe een aantal oproepen naar ProcessMessages.

Dit is vereenvoudigd voor een betere leesbaarheid:

{in MyForm:}
WorkLevel: geheel getal;
{OnCreate:}
Werkniveau: = 0;
procedure TForm1.WorkBtnClick (Sender: TObject);
var
cyclus: geheel getal;
beginnen
inc (WorkLevel);
voor cyclus: = 1 naar 5 Doen
beginnen
Memo1.Lines. Toevoegen ('- Work' + IntToStr (WorkLevel) + ', Cycle' + IntToStr (cycle);
Toepassing. ProcessMessages;
slaap (1000); // of ander werk
einde;
Memo1.Lines. Add ('Work' + IntToStr (WorkLevel) + 'ended.');
dec (WorkLevel);
einde;

ZONDER "ProcessMessages" worden de volgende regels naar het memo geschreven, als de knop in een korte tijd tweemaal werd ingedrukt:

 - Werk 1, cyclus 1
- Werk 1, cyclus 2
- Werk 1, cyclus 3
- Werk 1, cyclus 4
- Werk 1, cyclus 5
Werk 1 is beëindigd.
- Werk 1, cyclus 1
- Werk 1, cyclus 2
- Werk 1, cyclus 3
- Werk 1, cyclus 4
- Werk 1, cyclus 5
Werk 1 is beëindigd.

Terwijl de procedure bezig is, vertoont het formulier geen enkele reactie, maar de tweede klik werd door Windows in de berichtenwachtrij geplaatst. Direct nadat de "OnClick" is voltooid, wordt deze opnieuw opgeroepen.

INCLUSIEF "ProcessMessages", kan de output heel anders zijn:

 - Werk 1, cyclus 1
- Werk 1, cyclus 2
- Werk 1, cyclus 3
- Werk 2, cyclus 1
- Werk 2, cyclus 2
- Werk 2, cyclus 3
- Werk 2, cyclus 4
- Werk 2, cyclus 5
Werk 2 is beëindigd.
- Werk 1, cyclus 4
- Werk 1, cyclus 5
Werk 1 is beëindigd.

Deze keer lijkt het formulier weer te werken en accepteert het elke interactie met de gebruiker. Dus de knop wordt halverwege ingedrukt tijdens uw eerste "werk" -functie OPNIEUW, die onmiddellijk wordt behandeld. Alle binnenkomende gebeurtenissen worden afgehandeld zoals elke andere functieaanroep.

In theorie kan tijdens elke oproep aan "ProgressMessages" ELK aantal klikken en gebruikersberichten "op zijn plaats" plaatsvinden.

Dus wees voorzichtig met je code!

Ander voorbeeld (in eenvoudige pseudo-code!):

procedure OnClickFileWrite ();
var myfile: = TFileStream;
beginnen
myfile: = TFileStream.create ('myOutput.txt');
proberen
terwijl BytesReady> 0 Doen
beginnen
mijn bestand. Write (DataBlock);
dec (BytesReady, sizeof (DataBlock));
DataBlock [2]: = # 13; {testlijn 1}
Toepassing. ProcessMessages;
DataBlock [2]: = # 13; {testlijn 2}
einde;
Tenslotte
myfile.free;
einde;
einde;

Deze functie schrijft een grote hoeveelheid gegevens en probeert de toepassing te "ontgrendelen" door "ProcessMessages" te gebruiken telkens wanneer een gegevensblok wordt geschreven.

Als de gebruiker opnieuw op de knop klikt, wordt dezelfde code uitgevoerd terwijl er nog naar het bestand wordt geschreven. Het bestand kan dus geen 2e keer worden geopend en de procedure mislukt.

Misschien zal uw toepassing een foutherstel uitvoeren, zoals het vrijmaken van de buffers.

Als een mogelijk resultaat zal "Datablock" worden vrijgegeven en zal de eerste code "plotseling" een "Toegangsfout" veroorzaken wanneer deze er toegang toe heeft. In dit geval: testlijn 1 werkt, testlijn 2 crasht.

De betere manier:

Om het gemakkelijk te maken, kunt u het hele formulier "ingeschakeld: = onwaar" instellen, dat alle gebruikersinvoer blokkeert, maar dit NIET aan de gebruiker toont (alle knoppen zijn niet grijs).

Een betere manier zou zijn om alle knoppen op "uitgeschakeld" te zetten, maar dit kan complex zijn als u bijvoorbeeld één knop "Annuleren" wilt behouden. Ook moet u alle componenten doorlopen om ze uit te schakelen en wanneer ze weer worden ingeschakeld, moet u controleren of er nog enkele in de uitgeschakelde staat moeten zijn.

Jij kunt deactiveer een container kind besturingselementen wanneer de eigenschap ingeschakeld verandert.

Zoals de klassennaam "TNotifyEvent" suggereert, mag deze alleen worden gebruikt voor kortetermijnreacties op het evenement. Voor tijdrovende code is IMHO de beste manier om alle "trage" code in een eigen thread te plaatsen.

Wat betreft de problemen met "PrecessMessages" en / of het in- en uitschakelen van componenten, het gebruik van een tweede draad lijkt helemaal niet te ingewikkeld.

Vergeet niet dat zelfs eenvoudige en snelle regels code seconden kunnen blijven hangen, b.v. het openen van een bestand op een schijfstation moet mogelijk wachten tot het opstarten van het station is voltooid. Het ziet er niet erg goed uit als uw toepassing lijkt te crashen omdat de schijf te langzaam is.

Dat is het. De volgende keer dat u 'Toepassing' toevoegt. ProcessMessages ", denk twee keer na;)

instagram story viewer