Den mørke side af applikationen. ProcessMessages

click fraud protection

Artikel indsendt af Marcus Junglas

Når du programmerer en begivenhedshåndterer i Delphi (som f.eks OnClick begivenhed med en TButton), kommer det tidspunkt, hvor din ansøgning skal være optaget et stykke tid, f.eks. koden skal skrives en stor fil eller komprimere nogle data.

Hvis du gør det, vil du bemærke det din applikation ser ud til at være låst. Din form kan ikke flyttes mere, og knapperne viser intet tegn på liv. Det ser ud til at være styrtet.

Årsagen er, at en Delpi-applikation er enkelt trådet. Koden, du skriver, repræsenterer bare en masse procedurer, der kaldes af Delphis hovedtråd, hver gang en begivenhed fandt sted. Resten af ​​tiden håndterer hovedtråden systemmeddelelser og andre ting som form- og komponenthåndteringsfunktioner.

Så hvis du ikke afslutter din begivenhedshåndtering ved at udføre noget længe arbejde, forhindrer du applikationen til at håndtere disse meddelelser.

En almindelig løsning på en sådan type problemer er at kalde "Application. ProcessMessages". "Application" er et globalt objekt i TApplication-klassen.

instagram viewer

Ansøgningen. Processmessages håndterer alle ventende meddelelser som vinduesbevægelser, knapklik og så videre. Det bruges ofte som en enkel løsning for at holde din applikation "i orden".

Desværre har mekanismen bag "ProcessMessages" sine egne egenskaber, hvilket kan forårsage stor forvirring!

Hvad betyder ProcessMessages?

PprocessMessages håndterer alle ventende systemmeddelelser i applikationsmeddelelseskøen. Windows bruger meddelelser til at "tale" med alle kørende applikationer. Brugerinteraktion bringes til formularen via beskeder, og "ProcessMessages" håndterer dem.

Hvis musen f.eks. Går ned på en TButton, gør ProgressMessages alt hvad der skal ske på denne begivenhed som f.eks. Genmaling af knappen til en "presset" tilstand og selvfølgelig et opkald til håndteringsproceduren for OnClick (), hvis du tildelte en.

Det er problemet: ethvert opkald til ProcessMessages kan indeholde et rekursivt opkald til enhver begivenhedshåndterer igen. Her er et eksempel:

Brug følgende kode til en knapps OnClick jævnhåndterer ("arbejde"). For-erklæringen simulerer et langt behandlingsopgave med nogle opkald til ProcessMessages af og til.

Dette forenkles for bedre læsbarhed:

{i MyForm:}
WorkLevel: heltal;
{OnCreate:}
Arbejdsplan: = 0;
procedure TForm1.WorkBtnClick (Afsender: TObject);
Var
cyklus: heltal;
begynde
inc (WorkLevel);
til cyklus: = 1 til 5 gøre
begynde
Memo1.Lines. Tilføj ('- Work' + IntToStr (WorkLevel) + ', Cycle' + IntToStr (cyklus);
Ansøgning. ProcessMessages;
søvn (1000); // eller noget andet arbejde
ende;
Memo1.Lines. Tilføj ('Work' + IntToStr (WorkLevel) + 'sluttet.');
dec (WorkLevel);
ende;

UDEN "ProcessMessages" skrives følgende linjer til memoet, hvis knappen blev trykket TO gange på kort tid:

 - Arbejde 1, cyklus 1
- Arbejde 1, cyklus 2
- Arbejde 1, cyklus 3
- Arbejde 1, cyklus 4
- Arbejde 1, cyklus 5
Arbejde 1 sluttede.
- Arbejde 1, cyklus 1
- Arbejde 1, cyklus 2
- Arbejde 1, cyklus 3
- Arbejde 1, cyklus 4
- Arbejde 1, cyklus 5
Arbejde 1 sluttede.

Mens proceduren er optaget, viser formen ingen reaktion, men det andet klik blev sat i Windows-meddelelseskøen af ​​Windows. Lige efter "OnClick" er afsluttet, kaldes den igen.

INKLUDERET "ProcessMessages" kan output muligvis være meget forskelligt:

 - Arbejde 1, cyklus 1
- Arbejde 1, cyklus 2
- Arbejde 1, cyklus 3
- Arbejde 2, cyklus 1
- Arbejde 2, cyklus 2
- Arbejde 2, cyklus 3
- Arbejde 2, cyklus 4
- Arbejde 2, cyklus 5
Arbejde 2 sluttede.
- Arbejde 1, cyklus 4
- Arbejde 1, cyklus 5
Arbejde 1 sluttede.

Denne gang ser formen ud til at fungere igen og accepterer enhver brugerinteraktion. Så knappen trykkes halvvejs under din første "arbejder" -funktion igen, som håndteres øjeblikkeligt. Alle indkommende begivenheder håndteres som ethvert andet funktionskald.

I teorien kan der under ethvert opkald til "ProgressMessages" ALL Mængde klik og brugermeddelelser ske "på plads".

Så vær forsigtig med din kode!

Andet eksempel (i simpel pseudokode!):

procedure OnClickFileWrite ();
Var myfile: = TFileStream;
begynde
myfile: = TFileStream.create ('myOutput.txt');
prøve
mens BytesReady> 0 gøre
begynde
minfil. Skriv (DataBlock);
dec (BytesReady, sizeof (DataBlock));
DataBlock [2]: = # 13; {testlinje 1}
Ansøgning. ProcessMessages;
DataBlock [2]: = # 13; {testlinje 2}
ende;
endelig
myfile.free;
ende;
ende;

Denne funktion skriver en stor mængde data og forsøger at "låse op" applikationen ved hjælp af "ProcessMessages", hver gang en blok med data skrives.

Hvis brugeren klikker på knappen igen, udføres den samme kode, mens filen stadig skrives til. Så filen kan ikke åbnes en anden gang, og proceduren mislykkes.

Måske vil din applikation gøre noget gendannelse af fejl, som at frigøre buffere.

Som et muligt resultat frigøres "Datablock", og den første kode "pludselig" hæver en "adgangsovertrædelse", når den får adgang til den. I dette tilfælde: testlinje 1 fungerer, testlinie 2 går ned.

Den bedre måde:

For at gøre det let kunne du indstille hele formularen "aktiveret: = falsk", som blokerer al brugerinput, men IKKE viser dette for brugeren (alle knapper er ikke gråt).

En bedre måde ville være at indstille alle knapper til "deaktiveret", men dette kan være kompliceret, hvis du f.eks. Vil have en "Annuller" -knap. Du skal også gennemgå alle komponenterne for at deaktivere dem, og når de er aktiveret igen, skal du kontrollere, om der skal være nogle tilbage i den deaktiverede tilstand.

Du kunne deaktiver et containerbørn kontrollerer, når den aktiverede egenskab ændres.

Som klassens navn "TNotifyEvent" antyder, bør det kun bruges til kortsigtede reaktioner på begivenheden. For tidskrævende kode er den bedste måde IMHO at placere al den "langsomme" kode i en egen tråd.

Med hensyn til problemerne med "PrecessMessages" og / eller aktivering og deaktivering af komponenter, er brugen af ​​en anden tråd synes overhovedet ikke at være for kompliceret.

Husk, at selv enkle og hurtige kodelinjer kan hænge i sekunder, f.eks. åbning af en fil på et diskdrev skal muligvis vente, til drevets spin-up er færdig. Det ser ikke særlig godt ud, hvis din applikation ser ud til at gå ned, fordi drevet er for langsomt.

Det er det. Næste gang du tilføjer "Application. ProcessMessages ", tænk to gange;)

instagram story viewer