Artikkel innsendt av Marcus Junglas
Når du programmerer en hendelsesbehandler i Delphi (som Ved trykk hendelse av en TButton), kommer det tiden når søknaden din må være opptatt en stund, f.eks. koden må skrive en stor fil eller komprimere noen data.
Hvis du gjør det, vil du merke det søknaden din ser ut til å være låst. Skjemaet ditt kan ikke flyttes lenger, og knappene viser ingen tegn til liv. Det ser ut til å være krasjet.
Årsaken er at en Delpi-applikasjon er enkeltrådet. Koden du skriver representerer bare en haug med prosedyrer som blir kalt av Delphis hovedtråd når en hendelse skjedde. Resten av tiden behandler hovedtråden systemmeldinger og andre ting som form- og komponenthåndteringsfunksjoner.
Så hvis du ikke fullfører hendelseshåndteringen ved å gjøre noen lange arbeider, vil du forhindre at applikasjonen håndterer disse meldingene.
En vanlig løsning for slike type problemer er å kalle "Application. ProcessMessages". "Application" er et globalt objekt i TApplication-klassen.
Søknaden. Processmessages håndterer alle ventende meldinger som vindusbevegelser, knappeklikk og så videre. Det brukes ofte som en enkel løsning for å holde applikasjonen din "fungerer".
Dessverre har mekanismen bak "ProcessMessages" sine egne egenskaper, noe som kan forårsake stor forvirring!
Hva betyr ProcessMessages?
PprocessMessages håndterer alle ventende systemmeldinger i programmets meldingskø. Windows bruker meldinger for å "snakke" med alle programmer som kjører. Brukerinteraksjon blir brakt til skjemaet via meldinger og "ProcessMessages" håndterer dem.
Hvis musen for eksempel går ned på en TButton, gjør ProgressMessages alt hva som skal skje på denne hendelsen, som f.eks. male på nytt på knappen til en "presset" tilstand, og selvfølgelig en samtale til håndteringsprosedyren for OnClick () hvis du tilordnet en.
Det er problemet: ethvert anrop til ProcessMessages kan inneholde en rekursiv samtale til enhver hendelseshåndterer igjen. Her er et eksempel:
Bruk følgende kode for en knapps OnClick even handler ("arbeid"). For-uttalelsen simulerer en lang behandlingsjobb med noen anrop til ProcessMessages nå og da.
Dette er forenklet for bedre lesbarhet:
{i MyForm:}
Arbeidsnivå: heltall;
{OnCreate:}
Arbeidsnivå: = 0;
fremgangsmåte TForm1.WorkBtnClick (avsender: TObject);
Var
syklus: heltall;
begynne
inc (WorkLevel);
til syklus: = 1 til 5 gjøre
begynne
Memo1.Lines. Legg til ('- Work' + IntToStr (WorkLevel) + ', Cycle' + IntToStr (syklus);
Applikasjon. ProcessMessages;
søvn (1000); // eller noe annet arbeid
slutt;
Memo1.Lines. Legg til ('Work' + IntToStr (WorkLevel) + 'avsluttet.');
dec (WorkLevel);
slutt;
UTEN "ProcessMessages" blir følgende linjer skrevet til notatet, hvis knappen ble trykket TO ganger på kort tid:
- Arbeid 1, syklus 1
- Arbeid 1, syklus 2
- Arbeid 1, syklus 3
- Arbeid 1, syklus 4
- Arbeid 1, syklus 5
Arbeid 1 ble avsluttet.
- Arbeid 1, syklus 1
- Arbeid 1, syklus 2
- Arbeid 1, syklus 3
- Arbeid 1, syklus 4
- Arbeid 1, syklus 5
Arbeid 1 ble avsluttet.
Mens prosedyren er opptatt, viser ikke skjemaet noen reaksjon, men det andre klikket ble satt inn i meldingskøen av Windows. Rett etter at "OnClick" er ferdig, vil den bli kalt igjen.
INKLUDERT "ProcessMessages", kan resultatet være veldig forskjellig:
- Arbeid 1, syklus 1
- Arbeid 1, syklus 2
- Arbeid 1, syklus 3
- Arbeid 2, syklus 1
- Arbeid 2, syklus 2
- Arbeid 2, syklus 3
- Arbeid 2, syklus 4
- Arbeid 2, syklus 5
Arbeid 2 ble avsluttet.
- Arbeid 1, syklus 4
- Arbeid 1, syklus 5
Arbeid 1 ble avsluttet.
Denne gangen ser det ut til at skjemaet fungerer igjen og godtar alle brukerinteraksjoner. Så knappen trykkes halvveis under den første "arbeider" -funksjonen igjen, som vil bli håndtert umiddelbart. Alle innkommende hendelser håndteres som alle andre funksjonssamtaler.
I teorien kan det hende at hvert antall klikk og brukermeldinger vil skje "på plass" under hver oppfordring til "ProgressMessages".
Så vær forsiktig med koden din!
Ulik eksempel (i enkel pseudokode!):
fremgangsmåte OnClickFileWrite ();
Var myfile: = TFileStream;
begynne
myfile: = TFileStream.create ('myOutput.txt');
prøve
samtidig som BytesReady> 0 gjøre
begynne
myfile. Skriv (DataBlock);
dek (BytesReady, sizeof (DataBlock));
DataBlock [2]: = # 13; {testlinje 1}
Applikasjon. ProcessMessages;
DataBlock [2]: = # 13; {test linje 2}
slutt;
endelig
myfile.free;
slutt;
slutt;
Denne funksjonen skriver en stor datamengde og prøver å "låse opp" applikasjonen ved å bruke "ProcessMessages" hver gang en blokk med data skrives.
Hvis brukeren klikker på knappen igjen, blir den samme koden kjørt mens filen fremdeles skrives til. Så filen kan ikke åpnes andre gang, og prosedyren mislykkes.
Kanskje applikasjonen din vil gjøre noe feilgjenoppretting som å frigjøre buffere.
Som et mulig resultat blir "Datablock" frigjort, og den første koden vil "plutselig" heve en "Tilgangskrenkelse" når den får tilgang til den. I dette tilfellet: test linje 1 vil fungere, test linje 2 vil krasje.
Den bedre måten:
For å gjøre det enkelt kan du stille hele skjemaet "aktivert: = falsk", som blokkerer all brukerinput, men viser IKKE dette for brukeren (alle knappene er ikke gråtoner).
En bedre måte ville være å sette alle knappene til "deaktivert", men dette kan være sammensatt hvis du for eksempel vil beholde en "Avbryt" -knapp. Du må også gå gjennom alle komponentene for å deaktivere dem, og når de er aktivert igjen, må du sjekke om det skal være noe igjen i deaktivert tilstand.
Du kan deaktivere en beholderbarn kontrollerer når den aktiverte egenskapen endres.
Som klassens navn "TNotifyEvent" antyder, bør det bare brukes til kortsiktige reaksjoner på hendelsen. For tidkrevende kode er den beste måten IMHO å sette all den "sakte" koden i en egen tråd.
Når det gjelder problemene med "PrecessMessages" og / eller aktivering og deaktivering av komponenter, er bruken av en andre tråden ser ikke ut til å være for komplisert.
Husk at selv enkle og raske kodelinjer kan henge i sekunder, f.eks. Åpne en fil på en diskstasjon må kanskje vente til stasjonen spinner opp er ferdig. Det ser ikke veldig bra ut hvis applikasjonen din ser ut til å krasje fordi stasjonen er for treg.
Det er det. Neste gang du legger til "Søknad. ProcessMessages ", tenk to ganger;)